diff --git a/.gitmodules b/.gitmodules index fdc9de9..c830bb4d 100644 --- a/.gitmodules +++ b/.gitmodules
@@ -488,7 +488,7 @@ url = https://chromium.googlesource.com/external/github.com/protocolbuffers/protobuf-javascript [submodule "third_party/pthreadpool/src"] path = third_party/pthreadpool/src - url = https://chromium.googlesource.com/external/github.com/Maratyszcza/pthreadpool + url = https://chromium.googlesource.com/external/github.com/google/pthreadpool [submodule "third_party/pyelftools"] path = third_party/pyelftools url = https://chromium.googlesource.com/chromiumos/third_party/pyelftools
diff --git a/DEPS b/DEPS index 862fec78..b68e5a61 100644 --- a/DEPS +++ b/DEPS
@@ -253,7 +253,7 @@ 'screen_ai_windows_386': 'version:127.9', # siso CIPD package version. - 'siso_version': 'git_revision:f9bfa081826a4ffb6f2a96b640d009cef900e69f', + 'siso_version': 'git_revision:67a8780a72f407b3dffe9e338c368f0b799d004b', # download libaom test data 'download_libaom_testdata': False, @@ -280,15 +280,15 @@ # 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': '539c311a3a7fa76bf50a9c83822815abdb825a18', + 'skia_revision': 'c24b41eebcc69f4f8156716a411de04a80b6c3bc', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'fc91f65bbd2f5dfa85e65a007ec01a71a10a45b4', + 'v8_revision': '897d8f21c242c88045314d105862e4c88e78b859', # 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': '4c60a308592af56dc745d8d1ba994e89ebb053db', + 'angle_revision': 'b3af2e86f4b75b3c8f8053175fe97c0372926675', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -300,7 +300,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. - 'boringssl_revision': '7db3433bd4466b20ade77494cd3bb03396441aef', + 'boringssl_revision': 'bf4cf6938a77f1aca83ef529dce96681efd1e6c5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. @@ -348,7 +348,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '580dbb72d8984e972a02874d06ace7b13295798a', + 'catapult_revision': 'd25caed4b98fea8a30ecf85a9c2b056cb139809e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling chromium_variations # and whatever else without interference from each other. @@ -364,7 +364,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling fuzztest # and whatever else without interference from each other. - 'fuzztest_revision': 'ae6208fc45a09da94d9c0925e26cd9bbca92154b', + 'fuzztest_revision': 'b86e98ff1149313e43333d1017c3a87baa6721c5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling domato # and whatever else without interference from each other. @@ -396,7 +396,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': '041b072241d75850d18f441b53bacf2d317e9818', + 'dawn_revision': '60a78b1ae59d6724d658e1eb3d8e2813afb29cb9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -468,7 +468,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. - 'libcxxabi_revision': '7681005c6233e8a21b97e24c1a3c5c6979927d5a', + 'libcxxabi_revision': 'cbada99a33f015cb8333d63a88ff0c10cbbc6f38', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1439,12 +1439,12 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - 'ff16da349347e90984ba1bc206cafccad280e3c6', + '13571b2b78ee3c8179518064da9ed1a3f5da47ec', 'condition': 'checkout_android and checkout_src_internal', }, 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + 'fb143e8ce76fcf5f67792fbbd6c219c5d03ec2d9', + 'url': Var('chromium_git') + '/website.git' + '@' + '54467c5b22ad5d81a5468007092db3e5d33d7f5b', }, 'src/ios/third_party/earl_grey2/src': { @@ -1598,7 +1598,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'fo4-wfWpq9bmst8F64v9PNRzBj6NBcDIlW9IT490PPkC', + 'version': 'wo7iIVgF-B5pVgthj7EDcFUn84oMIQo4Z1ZNtYHK-QIC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1842,7 +1842,7 @@ }, 'src/third_party/barhopper': { - 'url': Var('chrome_git') + '/chrome/deps/barhopper.git' + '@' + '865bd06ef4a839b0a15d17e38e25f8911e4cdf9f', + 'url': Var('chrome_git') + '/chrome/deps/barhopper.git' + '@' + '9230af4dc38c6d2cc9c0841692267762ebfca991', 'condition': 'checkout_src_internal and checkout_chromeos', }, @@ -2436,7 +2436,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '38f6220c882b08ed8bd8cbb47cff50cfa790e41b', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '2eab41ff134151a618b4f54d4b8913d770c4ac22', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2591,7 +2591,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/qemu/linux-arm64', - 'version': 'hOpuGIMj1FAtBWGDlXARkCm2srxY4enn8iI3AgrDna4C' + 'version': 'MDf3sCxn9kct3Tg1oVRHch1hkzO6-9qZZFBRPT6jDuoC' }, ], # TODO(b/351926334): Do not add `non_git_source` to this condition until the bug is fixed. @@ -2752,16 +2752,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@ee1a15d510c031acaff02138a922ccdb6f85c2a7', - 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@e43514866f7e0f8265c677039d2fe773c892d44b', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5b43f9496300bdac77c67501dc3b3738cb3d21c5', + 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@c16e6a34b72de51d07c121a1c202806bac0be9dc', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@a380cd25433092dbce9a455a3feb1242138febee', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@9064fe8637daf9f694c8a2e035a34da94022f2be', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@995922d48149384766cc646159a9e28701f01f0c', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@d4a196d8c84e032d27f999adcea3075517c1c97f', 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@369f59ad598b60d6ed9f553af651c5cccd20234c', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@315964ad5aabd5b148a484e5fbea8a365c8d1eb3', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@5a88b6042edb8f03eefc8de73bd73a899989373f', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@5cceb78082833556789a64f3237b04df7a826d93', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@cddf2371ee3ef9c31deea06ce14df558c20ece04', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21', @@ -2806,7 +2806,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'da8a535ad4e41fbb587b38346d324a2892ba4183', + Var('webrtc_git') + '/src.git' + '@' + '2911627ab3cb5e2549d7e65d176a01ed398b874a', # 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. @@ -4568,7 +4568,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - 'c37133f0c4465e77570c88c93940ae58a3d35819', + 'd2dce193b98bc200e7b9d6c6894fe09fdcb16b39', 'condition': 'checkout_src_internal', }, @@ -4634,7 +4634,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - '53efd8418bb70bf06616f417a7090f9d3b659343', + 'a48abcfc293d8e695d52adafe5a76954066a0ab8', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index d1877fb..e44039c 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc
@@ -1263,8 +1263,7 @@ bool AwContentBrowserClient::SuppressDifferentOriginSubframeJSDialogs( content::BrowserContext* browser_context) { - return base::FeatureList::IsEnabled( - features::kWebViewSuppressDifferentOriginSubframeJSDialogs); + return false; } bool AwContentBrowserClient::ShouldPreconnectNavigation(
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc index d9ae230..50d97c69 100644 --- a/android_webview/common/aw_features.cc +++ b/android_webview/common/aw_features.cc
@@ -118,12 +118,6 @@ "WebViewSupervisedUserSiteBlock", base::FEATURE_ENABLED_BY_DEFAULT); -// Disallows window.{alert, prompt, confirm} if triggered inside a subframe that -// is not same origin with the main frame. -BASE_FEATURE(kWebViewSuppressDifferentOriginSubframeJSDialogs, - "WebViewSuppressDifferentOriginSubframeJSDialogs", - base::FEATURE_DISABLED_BY_DEFAULT); - // A Feature used for WebView variations tests. Not used in production. Please // do not clean up this stale feature: we intentionally keep this feature flag // around for testing purposes.
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h index b4815c3..9c9f0ae 100644 --- a/android_webview/common/aw_features.h +++ b/android_webview/common/aw_features.h
@@ -35,7 +35,6 @@ BASE_DECLARE_FEATURE(kWebViewRestrictSensitiveContent); BASE_DECLARE_FEATURE(kWebViewSupervisedUserSiteDetection); BASE_DECLARE_FEATURE(kWebViewSupervisedUserSiteBlock); -BASE_DECLARE_FEATURE(kWebViewSuppressDifferentOriginSubframeJSDialogs); BASE_DECLARE_FEATURE(kWebViewTestFeature); BASE_DECLARE_FEATURE(kWebViewUseMetricsUploadService); BASE_DECLARE_FEATURE(kWebViewUseMetricsUploadServiceOnlySdkRuntime);
diff --git a/ash/capture_mode/capture_mode_menu_group.cc b/ash/capture_mode/capture_mode_menu_group.cc index bea4c64..e03c464 100644 --- a/ash/capture_mode/capture_mode_menu_group.cc +++ b/ash/capture_mode/capture_mode_menu_group.cc
@@ -305,7 +305,7 @@ const auto checked_icon_enabled_color = color_provider->GetColor(kColorAshButtonLabelColorBlue); - checked_icon_view_->SetImage(gfx::CreateVectorIcon( + checked_icon_view_->SetImage(ui::ImageModel::FromVectorIcon( kHollowCheckCircleIcon, is_disabled ? ColorUtil::GetDisabledColor(checked_icon_enabled_color) : checked_icon_enabled_color));
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc index 95dc7cf..1ee168b 100644 --- a/ash/capture_mode/capture_mode_unittests.cc +++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -5,6 +5,7 @@ #include <memory> #include <optional> #include <string> +#include <tuple> #include <vector> #include "ash/accessibility/magnifier/docked_magnifier_controller.h" @@ -35,6 +36,7 @@ #include "ash/capture_mode/test_capture_mode_delegate.h" #include "ash/capture_mode/user_nudge_controller.h" #include "ash/capture_mode/video_recording_watcher.h" +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/display/cursor_window_controller.h" #include "ash/display/output_protection_delegate.h" @@ -78,9 +80,11 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" +#include "base/strings/strcat.h" #include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "chromeos/ash/services/recording/recording_service_test_api.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power_manager/suspend.pb.h" @@ -92,6 +96,7 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/capture_client_observer.h" @@ -203,14 +208,14 @@ } // namespace -class CaptureModeTest : public AshTestBase { +class CaptureModeTestBase : public AshTestBase { public: - CaptureModeTest() = default; - explicit CaptureModeTest(base::test::TaskEnvironment::TimeSource time) + CaptureModeTestBase() = default; + explicit CaptureModeTestBase(base::test::TaskEnvironment::TimeSource time) : AshTestBase(time) {} - CaptureModeTest(const CaptureModeTest&) = delete; - CaptureModeTest& operator=(const CaptureModeTest&) = delete; - ~CaptureModeTest() override = default; + CaptureModeTestBase(const CaptureModeTestBase&) = delete; + CaptureModeTestBase& operator=(const CaptureModeTestBase&) = delete; + ~CaptureModeTestBase() override = default; // AshTestBase: void SetUp() override { @@ -362,8 +367,58 @@ return controller->video_recording_watcher_for_testing() ->window_being_recorded(); } + + protected: + // Used in other fixtures to more easily initialise Sunfish and Scanner + // features. + void InitFeatures(bool sunfish_enabled, bool scanner_enabled) { + std::vector<base::test::FeatureRef> enabled_features; + std::vector<base::test::FeatureRef> disabled_features; + + (sunfish_enabled ? enabled_features : disabled_features) + .push_back(features::kSunfishFeature); + (scanner_enabled ? enabled_features : disabled_features) + .push_back(features::kScannerUpdate); + (scanner_enabled ? enabled_features : disabled_features) + .push_back(features::kScannerDogfood); + + feature_list_.InitWithFeatures(enabled_features, disabled_features); + } + + base::test::ScopedFeatureList feature_list_; }; +std::string SunfishScannerTestName(bool sunfish_enabled, bool scanner_enabled) { + return base::StrCat({"Sunfish", sunfish_enabled ? "Enabled" : "Disabled", + "Scanner", scanner_enabled ? "Enabled" : "Disabled"}); +} + +class CaptureModeTest + : public CaptureModeTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { + public: + CaptureModeTest() = default; + explicit CaptureModeTest(base::test::TaskEnvironment::TimeSource time) + : CaptureModeTestBase(time) {} + + // CaptureModeTestBase: + void SetUp() override { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + InitFeatures(sunfish_enabled, scanner_enabled); + CaptureModeTestBase::SetUp(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + , + CaptureModeTest, + testing::Combine(testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<CaptureModeTest::ParamType>& info) { + bool sunfish_enabled = std::get<0>(info.param); + bool scanner_enabled = std::get<1>(info.param); + return SunfishScannerTestName(sunfish_enabled, scanner_enabled); + }); + class CaptureSessionWidgetClosed { public: explicit CaptureSessionWidgetClosed(views::Widget* widget) { @@ -381,7 +436,7 @@ base::WeakPtr<views::Widget> widget_; }; -TEST_F(CaptureModeTest, StartStop) { +TEST_P(CaptureModeTest, StartStop) { auto* controller = CaptureModeController::Get(); controller->Start(CaptureModeEntryType::kQuickSettings); EXPECT_TRUE(controller->IsActive()); @@ -398,7 +453,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, CheckCursorVisibility) { +TEST_P(CaptureModeTest, CheckCursorVisibility) { // Hide cursor before entering capture mode. auto* cursor_manager = Shell::Get()->cursor_manager(); cursor_manager->SetCursor(CursorType::kPointer); @@ -423,7 +478,7 @@ EXPECT_TRUE(cursor_manager->IsCursorVisible()); } -TEST_F(CaptureModeTest, CheckCursorVisibilityOnTabletMode) { +TEST_P(CaptureModeTest, CheckCursorVisibilityOnTabletMode) { auto* cursor_manager = Shell::Get()->cursor_manager(); // Enter tablet mode. @@ -443,13 +498,13 @@ } // Regression test for https://crbug.com/1172425. -TEST_F(CaptureModeTest, NoCrashOnClearingCapture) { +TEST_P(CaptureModeTest, NoCrashOnClearingCapture) { TestCaptureClientObserver observer(CreateTestWindow(gfx::Rect(200, 200))); auto* controller = StartImageRegionCapture(); EXPECT_TRUE(controller->IsActive()); } -TEST_F(CaptureModeTest, CheckWidgetClosed) { +TEST_P(CaptureModeTest, CheckWidgetClosed) { auto* controller = CaptureModeController::Get(); controller->Start(CaptureModeEntryType::kQuickSettings); EXPECT_TRUE(controller->IsActive()); @@ -463,7 +518,7 @@ EXPECT_TRUE(observer.GetWidgetClosed()); } -TEST_F(CaptureModeTest, StartWithMostRecentTypeAndSource) { +TEST_P(CaptureModeTest, StartWithMostRecentTypeAndSource) { auto* controller = CaptureModeController::Get(); controller->SetSource(CaptureModeSource::kFullscreen); controller->SetType(CaptureModeType::kVideo); @@ -480,7 +535,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, AccessibleCheckedState) { +TEST_P(CaptureModeTest, AccessibleCheckedState) { auto* controller = CaptureModeController::Get(); controller->Start(CaptureModeEntryType::kQuickSettings); ui::AXNodeData data; @@ -494,7 +549,7 @@ EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kFalse); } -TEST_F(CaptureModeTest, ChangeTypeAndSourceFromUI) { +TEST_P(CaptureModeTest, ChangeTypeAndSourceFromUI) { auto* controller = CaptureModeController::Get(); controller->Start(CaptureModeEntryType::kQuickSettings); EXPECT_TRUE(controller->IsActive()); @@ -520,7 +575,7 @@ EXPECT_EQ(controller->source(), CaptureModeSource::kFullscreen); } -TEST_F(CaptureModeTest, VideoRecordingUiBehavior) { +TEST_P(CaptureModeTest, VideoRecordingUiBehavior) { // Start Capture Mode in a fullscreen video recording mode. CaptureModeController* controller = StartCaptureSession( CaptureModeSource::kFullscreen, CaptureModeType::kVideo); @@ -557,7 +612,7 @@ EndRecordingReason::kStopRecordingButton, 1); } -TEST_F(CaptureModeTest, NoCrashOnMultipleClicksOnStopRecordingButton) { +TEST_P(CaptureModeTest, NoCrashOnMultipleClicksOnStopRecordingButton) { ash::CaptureModeTestApi test_api; test_api.StartForFullscreen(/*for_video=*/true); test_api.PerformCapture(); @@ -581,7 +636,7 @@ } // Tests the behavior of repositioning a region with capture mode. -TEST_F(CaptureModeTest, CaptureRegionRepositionBehavior) { +TEST_P(CaptureModeTest, CaptureRegionRepositionBehavior) { // Use a set display size as we will be choosing points in this test. UpdateDisplay("800x700"); @@ -610,7 +665,7 @@ // Tests the behavior of resizing a region with capture mode using the corner // drag affordances. -TEST_F(CaptureModeTest, CaptureRegionCornerResizeBehavior) { +TEST_P(CaptureModeTest, CaptureRegionCornerResizeBehavior) { // Use a set display size as we will be choosing points in this test. UpdateDisplay("800x700"); @@ -673,7 +728,7 @@ // Tests the behavior of resizing a region with capture mode using the edge drag // affordances. -TEST_F(CaptureModeTest, CaptureRegionEdgeResizeBehavior) { +TEST_P(CaptureModeTest, CaptureRegionEdgeResizeBehavior) { // Use a set display size as we will be choosing points in this test. UpdateDisplay("800x700"); @@ -755,7 +810,7 @@ // Tests that the capture region persists after exiting and reentering capture // mode. -TEST_F(CaptureModeTest, CaptureRegionPersistsAfterExit) { +TEST_P(CaptureModeTest, CaptureRegionPersistsAfterExit) { auto* controller = StartImageRegionCapture(); const gfx::Rect region(100, 100, 200, 200); SelectRegion(region); @@ -767,7 +822,7 @@ // Tests that the capture region resets when clicking outside the current // capture regions bounds. -TEST_F(CaptureModeTest, CaptureRegionResetsOnClickOutside) { +TEST_P(CaptureModeTest, CaptureRegionResetsOnClickOutside) { auto* controller = StartImageRegionCapture(); SelectRegion(gfx::Rect(100, 100, 200, 200)); @@ -781,7 +836,14 @@ // Tests that buttons on the capture mode bar still work when a region is // "covering" them. -TEST_F(CaptureModeTest, CaptureRegionCoversCaptureModeBar) { +TEST_P(CaptureModeTest, CaptureRegionCoversCaptureModeBar) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + UpdateDisplay("800x700"); auto* controller = StartImageRegionCapture(); @@ -809,7 +871,7 @@ // Tests that the magnifying glass appears while fine tuning the capture region, // and that the cursor is hidden if the magnifying glass is present. -TEST_F(CaptureModeTest, CaptureRegionMagnifierWhenFineTuning) { +TEST_P(CaptureModeTest, CaptureRegionMagnifierWhenFineTuning) { const gfx::Vector2d kDragDelta(50, 50); UpdateDisplay("800x700"); @@ -887,7 +949,7 @@ } // Tests that the dimensions label properly renders for capture regions. -TEST_F(CaptureModeTest, CaptureRegionDimensionsLabelLocation) { +TEST_P(CaptureModeTest, CaptureRegionDimensionsLabelLocation) { UpdateDisplay("900x800"); // Start Capture Mode in a region in image mode. @@ -946,7 +1008,7 @@ EXPECT_EQ(nullptr, GetDimensionsLabelWindow()); } -TEST_F(CaptureModeTest, CaptureRegionCaptureButtonLocation) { +TEST_P(CaptureModeTest, CaptureRegionCaptureButtonLocation) { UpdateDisplay("900x800"); auto* controller = StartImageRegionCapture(); @@ -986,7 +1048,7 @@ // Tests some edge cases to ensure the capture button does not intersect the // capture bar and end up unclickable since it is stacked below the capture bar. // Regression test for https://crbug.com/1186462. -TEST_F(CaptureModeTest, CaptureRegionCaptureButtonDoesNotIntersectCaptureBar) { +TEST_P(CaptureModeTest, CaptureRegionCaptureButtonDoesNotIntersectCaptureBar) { UpdateDisplay("800x700"); StartImageRegionCapture(); @@ -1036,7 +1098,7 @@ // Tests that pressing on the capture bar and releasing the press outside of the // capture bar, the capture region could still be draggable and set. Regression // test for https://crbug.com/1325028. -TEST_F(CaptureModeTest, SetCaptureRegionAfterPressOnCaptureBar) { +TEST_P(CaptureModeTest, SetCaptureRegionAfterPressOnCaptureBar) { UpdateDisplay("800x600"); auto* controller = @@ -1058,7 +1120,7 @@ EXPECT_EQ(controller->user_capture_region(), region_bounds); } -TEST_F(CaptureModeTest, WindowCapture) { +TEST_P(CaptureModeTest, WindowCapture) { // Create 2 windows that overlap with each other. const gfx::Rect bounds1(0, 0, 200, 200); std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds1)); @@ -1096,7 +1158,7 @@ controller->Stop(); } -TEST_F(CaptureModeTest, WindowCaptureConfineBoundsDoNotOverlapWindowCaption) { +TEST_P(CaptureModeTest, WindowCaptureConfineBoundsDoNotOverlapWindowCaption) { std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(200, 200))); auto* controller = StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); @@ -1122,7 +1184,7 @@ // Tests that the capture bar is located on the root with the cursor when // starting capture mode. -TEST_F(CaptureModeTest, MultiDisplayCaptureBarInitialLocation) { +TEST_P(CaptureModeTest, MultiDisplayCaptureBarInitialLocation) { UpdateDisplay("800x700,801+0-800x700"); auto* event_generator = GetEventGenerator(); @@ -1140,7 +1202,7 @@ } // Tests behavior of a capture mode session if the active display is removed. -TEST_F(CaptureModeTest, DisplayRemoval) { +TEST_P(CaptureModeTest, DisplayRemoval) { UpdateDisplay("1200x700,1201+0-800x700"); // Start capture mode on the secondary display. @@ -1166,7 +1228,7 @@ // Tests behavior of a capture mode session if the active display is removed // and countdown running. -TEST_F(CaptureModeTest, DisplayRemovalWithCountdownVisible) { +TEST_P(CaptureModeTest, DisplayRemovalWithCountdownVisible) { UpdateDisplay("800x700,801+0-800x700"); // Start capture mode on the secondary display. @@ -1186,7 +1248,7 @@ // Tests behavior of a capture mode session if the active display is removed, // countdown running, fullscreen window, and in overview mode. -TEST_F(CaptureModeTest, +TEST_P(CaptureModeTest, DisplayRemovalWithCountdownVisibleFullscreenWindowAndInOverview) { UpdateDisplay("800x700,801+0-800x700"); @@ -1213,7 +1275,7 @@ // Tests that using fullscreen or window source, moving the mouse across // displays will change the root window of the capture session. -TEST_F(CaptureModeTest, MultiDisplayFullscreenOrWindowSourceRootWindow) { +TEST_P(CaptureModeTest, MultiDisplayFullscreenOrWindowSourceRootWindow) { UpdateDisplay("800x700,801+0-800x700"); ASSERT_EQ(2u, Shell::GetAllRootWindows().size()); @@ -1241,7 +1303,7 @@ // Tests that in region mode, moving the mouse across displays will not change // the root window of the capture session, but clicking on a new display will. -TEST_F(CaptureModeTest, MultiDisplayRegionSourceRootWindow) { +TEST_P(CaptureModeTest, MultiDisplayRegionSourceRootWindow) { UpdateDisplay("800x700,801+0-800x700"); ASSERT_EQ(2u, Shell::GetAllRootWindows().size()); @@ -1272,7 +1334,7 @@ // Tests that using touch on multi display setups works as intended. Regression // test for https://crbug.com/1159512. -TEST_F(CaptureModeTest, MultiDisplayTouch) { +TEST_P(CaptureModeTest, MultiDisplayTouch) { UpdateDisplay("800x700,801+0-800x700"); ASSERT_EQ(2u, Shell::GetAllRootWindows().size()); @@ -1290,7 +1352,14 @@ EXPECT_EQ(gfx::Size(200, 200), controller->user_capture_region().size()); } -TEST_F(CaptureModeTest, RegionCursorStates) { +TEST_P(CaptureModeTest, RegionCursorStates) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + UpdateDisplay("800x700,801+0-800x700"); auto* cursor_manager = Shell::Get()->cursor_manager(); @@ -1424,7 +1493,7 @@ } // Regression testing for https://crbug.com/1334824. -TEST_F(CaptureModeTest, CursorShouldNotChangeWhileAdjustingRegion) { +TEST_P(CaptureModeTest, CursorShouldNotChangeWhileAdjustingRegion) { UpdateDisplay("800x600"); auto* cursor_manager = Shell::Get()->cursor_manager(); @@ -1446,7 +1515,7 @@ EXPECT_EQ(CursorType::kSouthEastResize, cursor_manager->GetCursor().type()); } -TEST_F(CaptureModeTest, FullscreenCursorStates) { +TEST_P(CaptureModeTest, FullscreenCursorStates) { auto* cursor_manager = Shell::Get()->cursor_manager(); CursorType original_cursor_type = cursor_manager->GetCursor().type(); EXPECT_FALSE(cursor_manager->IsCursorLocked()); @@ -1518,7 +1587,7 @@ EXPECT_TRUE(cursor_manager->IsCursorVisible()); } -TEST_F(CaptureModeTest, WindowCursorStates) { +TEST_P(CaptureModeTest, WindowCursorStates) { std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(200, 200))); auto* cursor_manager = Shell::Get()->cursor_manager(); @@ -1596,7 +1665,7 @@ } // Tests that nothing crashes when windows are destroyed while being observed. -TEST_F(CaptureModeTest, WindowDestruction) { +TEST_P(CaptureModeTest, WindowDestruction) { // Create 2 windows that overlap with each other. const gfx::Rect bounds1(0, 0, 200, 200); const gfx::Rect bounds2(150, 150, 200, 200); @@ -1671,7 +1740,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, CursorUpdatedOnDisplayRotation) { +TEST_P(CaptureModeTest, CursorUpdatedOnDisplayRotation) { UpdateDisplay("600x400"); const int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); @@ -1703,7 +1772,7 @@ // Tests that in Region mode, cursor compositing is used instead of the system // cursor when the cursor is being dragged. -TEST_F(CaptureModeTest, RegionDragCursorCompositing) { +TEST_P(CaptureModeTest, RegionDragCursorCompositing) { auto* event_generator = GetEventGenerator(); auto* session = StartImageRegionCapture()->capture_mode_session(); auto* cursor_manager = Shell::Get()->cursor_manager(); @@ -1749,7 +1818,7 @@ // Test that during countdown, capture mode session should not handle any // incoming input events. -TEST_F(CaptureModeTest, DoNotHandleEventDuringCountDown) { +TEST_P(CaptureModeTest, DoNotHandleEventDuringCountDown) { // We need a non-zero duration to avoid infinite loop on countdown. ui::ScopedAnimationDurationScaleMode animation_scale( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); @@ -1783,7 +1852,7 @@ } // Test that during countdown, window changes or crashes are handled. -TEST_F(CaptureModeTest, WindowChangesDuringCountdown) { +TEST_P(CaptureModeTest, WindowChangesDuringCountdown) { // We need a non-zero duration to avoid infinite loop on countdown. ui::ScopedAnimationDurationScaleMode animation_scale( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); @@ -1831,7 +1900,7 @@ // Verifies that the video notification will show the same thumbnail image as // sent by recording service. -TEST_F(CaptureModeTest, VideoNotificationThumbnail) { +TEST_P(CaptureModeTest, VideoNotificationThumbnail) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); StartVideoRecordingImmediately(); @@ -1863,7 +1932,7 @@ gfx::test::AreBitmapsEqual(notification_thumbnail, service_thumbnail)); } -TEST_F(CaptureModeTest, LowDriveFsSpace) { +TEST_P(CaptureModeTest, LowDriveFsSpace) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); const base::FilePath drive_fs_folder = CreateFolderOnDriveFS("test"); @@ -1887,7 +1956,7 @@ EndRecordingReason::kLowDriveFsQuota, 1); } -TEST_F(CaptureModeTest, WindowRecordingCaptureId) { +TEST_P(CaptureModeTest, WindowRecordingCaptureId) { auto window = CreateTestWindow(gfx::Rect(200, 200)); StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); @@ -1906,7 +1975,7 @@ EXPECT_FALSE(window->subtree_capture_id().is_valid()); } -TEST_F(CaptureModeTest, ClosingDimmedWidgetAboveRecordedWindow) { +TEST_P(CaptureModeTest, ClosingDimmedWidgetAboveRecordedWindow) { views::Widget* widget = TestWidgetBuilder().BuildOwnedByNativeWidget(); auto* window = widget->GetNativeWindow(); auto recorded_window = CreateTestWindow(gfx::Rect(200, 200)); @@ -1925,7 +1994,7 @@ widget->Close(); } -TEST_F(CaptureModeTest, DimmingOfUnRecordedWindows) { +TEST_P(CaptureModeTest, DimmingOfUnRecordedWindows) { auto win1 = CreateTestWindow(gfx::Rect(200, 200)); auto win2 = CreateTestWindow(gfx::Rect(200, 200)); auto recorded_window = CreateTestWindow(gfx::Rect(200, 200)); @@ -1982,7 +2051,7 @@ EXPECT_FALSE(recording_watcher->IsWindowDimmedForTesting(win2.get())); } -TEST_F(CaptureModeTest, DimmingWithDesks) { +TEST_P(CaptureModeTest, DimmingWithDesks) { auto recorded_window = CreateAppWindow(gfx::Rect(250, 100)); auto* controller = StartSessionAndRecordWindow(recorded_window.get()); auto* recording_watcher = controller->video_recording_watcher_for_testing(); @@ -2016,7 +2085,7 @@ EXPECT_FALSE(recording_watcher->IsWindowDimmedForTesting(win1.get())); } -TEST_F(CaptureModeTest, DimmingWithDisplays) { +TEST_P(CaptureModeTest, DimmingWithDisplays) { UpdateDisplay("500x400,401+0-800x700"); auto recorded_window = CreateAppWindow(gfx::Rect(250, 100)); auto* controller = StartSessionAndRecordWindow(recorded_window.get()); @@ -2041,7 +2110,7 @@ EXPECT_FALSE(recording_watcher->IsWindowDimmedForTesting(window.get())); } -TEST_F(CaptureModeTest, MultiDisplayWindowRecording) { +TEST_P(CaptureModeTest, MultiDisplayWindowRecording) { UpdateDisplay("500x400,401+0-800x700"); auto roots = Shell::GetAllRootWindows(); ASSERT_EQ(2u, roots.size()); @@ -2093,7 +2162,7 @@ } // Flaky especially on MSan: https://crbug.com/1293188 -TEST_F(CaptureModeTest, DISABLED_WindowResizing) { +TEST_P(CaptureModeTest, DISABLED_WindowResizing) { UpdateDisplay("700x600"); auto window = CreateTestWindow(gfx::Rect(200, 200)); auto* controller = @@ -2145,7 +2214,7 @@ EXPECT_EQ(window->bounds().size(), test_delegate->GetCurrentVideoSize()); } -TEST_F(CaptureModeTest, RotateDisplayWhileRecording) { +TEST_P(CaptureModeTest, RotateDisplayWhileRecording) { UpdateDisplay("600x800"); auto* controller = @@ -2177,7 +2246,7 @@ // Regression test for https://crbug.com/1331095. // This is disabled due to flakiness: b/318349807 -TEST_F(CaptureModeTest, DISABLED_CornerRegionWithScreenRotation) { +TEST_P(CaptureModeTest, DISABLED_CornerRegionWithScreenRotation) { UpdateDisplay("800x600"); // Pick a region at the bottom right corner of the landscape screen, so that @@ -2234,7 +2303,7 @@ // This is a regression test for https://crbug.com/1214023. // // TODO(crbug.com/1439950): This test is flaky. -TEST_F(CaptureModeTest, DISABLED_VerifyWindowRecordingVideoFrames) { +TEST_P(CaptureModeTest, DISABLED_VerifyWindowRecordingVideoFrames) { auto window = CreateTestWindow(gfx::Rect(100, 50, 200, 200)); StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); @@ -2332,7 +2401,7 @@ // Tests that the focus should be on the `Settings` button after closing the // settings menu. -TEST_F(CaptureModeTest, ReturnFocusToSettingsButtonAfterSettingsMenuIsClosed) { +TEST_P(CaptureModeTest, ReturnFocusToSettingsButtonAfterSettingsMenuIsClosed) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kImage); auto* capture_mode_session = @@ -2385,7 +2454,7 @@ // Tests that minimized window(s) will be ignored whereas four corners occluded // but overall partially occluded window will be focusable while tabbing through // in `kWindow` mode. -TEST_F(CaptureModeTest, IgnoreMinimizeWindowsInKWindow) { +TEST_P(CaptureModeTest, IgnoreMinimizeWindowsInKWindow) { // Layout of three windows: four corners of `window3` are occluded by // `window1` and `window2`. // @@ -2441,7 +2510,7 @@ // Tests that partially occluded window(s) will be focusable even when four // edges are occluded by other windows while tabbing through in `kWindow` mode. -TEST_F(CaptureModeTest, PartiallyOccludedWindowIsFocusableInKWindow) { +TEST_P(CaptureModeTest, PartiallyOccludedWindowIsFocusableInKWindow) { // Layout of five windows: four edges of `window3` is occluded by `window1`, // `window2`, `window4` and `window5` respectively, but the middle part is not // occluded. @@ -2503,7 +2572,7 @@ // Tests that fully occluded window(s) will be ignored while tabbing in // `kWindow`. -TEST_F(CaptureModeTest, IgnoreFullyOccludedWindowWhileTabbingInKWindow) { +TEST_P(CaptureModeTest, IgnoreFullyOccludedWindowWhileTabbingInKWindow) { // Layout of six windows: `window3` is fully occluded by `window1`, `window2`, // `window4`, `window5` and `window6`. // +-----------+ @@ -2567,7 +2636,7 @@ // Tests that only Tab and Shift + Tab events advance/reverse focus and stop // event propagation. Other events like Alt + Tab should still behave as // intended. -TEST_F(CaptureModeTest, OnlyAdvanceFocusWhenTabShiftPressed) { +TEST_P(CaptureModeTest, OnlyAdvanceFocusWhenTabShiftPressed) { auto window1 = CreateTestWindow(); auto window2 = CreateTestWindow(); @@ -2631,7 +2700,7 @@ // Tests that the capture region will be refreshed if in overview to reflect the // bounds of the overview item for this window in `kWindow` mode. -TEST_F(CaptureModeTest, RefreshCaptureRegionInOverviewForKWindow) { +TEST_P(CaptureModeTest, RefreshCaptureRegionInOverviewForKWindow) { auto window = CreateAppWindow(gfx::Rect(100, 50, 200, 200)); auto* controller = StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage); @@ -2658,7 +2727,7 @@ } class CaptureModeSaveFileTest - : public CaptureModeTest, + : public CaptureModeTestBase, public testing::WithParamInterface<CaptureModeType> { public: CaptureModeSaveFileTest() = default; @@ -2849,13 +2918,24 @@ std::unique_ptr<aura::Window> window_; }; +INSTANTIATE_TEST_SUITE_P( + , + CaptureModeRecordingSizeTest, + testing::Combine(testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<CaptureModeRecordingSizeTest::ParamType>& + info) { + bool sunfish_enabled = std::get<0>(info.param); + bool scanner_enabled = std::get<1>(info.param); + return SunfishScannerTestName(sunfish_enabled, scanner_enabled); + }); + // TODO(crbug.com/1291073): Flaky on ChromeOS. #if BUILDFLAG(IS_CHROMEOS) #define MAYBE_CaptureAtPixelsFullscreen DISABLED_CaptureAtPixelsFullscreen #else #define MAYBE_CaptureAtPixelsFullscreen CaptureAtPixelsFullscreen #endif -TEST_F(CaptureModeRecordingSizeTest, MAYBE_CaptureAtPixelsFullscreen) { +TEST_P(CaptureModeRecordingSizeTest, MAYBE_CaptureAtPixelsFullscreen) { float dsf = 1.6f; SetDeviceScaleFactor(dsf); EXPECT_EQ(dsf, window_->GetHost()->device_scale_factor()); @@ -2914,7 +2994,7 @@ } // The test is flaky. https://crbug.com/1287724. -TEST_F(CaptureModeRecordingSizeTest, DISABLED_CaptureAtPixelsRegion) { +TEST_P(CaptureModeRecordingSizeTest, DISABLED_CaptureAtPixelsRegion) { float dsf = 1.6f; SetDeviceScaleFactor(dsf); EXPECT_EQ(dsf, window_->GetHost()->device_scale_factor()); @@ -2962,7 +3042,7 @@ } // The test is flaky. https://crbug.com/1287724. -TEST_F(CaptureModeRecordingSizeTest, DISABLED_CaptureAtPixelsWindow) { +TEST_P(CaptureModeRecordingSizeTest, DISABLED_CaptureAtPixelsWindow) { float dsf = 1.6f; SetDeviceScaleFactor(dsf); EXPECT_EQ(dsf, window_->GetHost()->device_scale_factor()); @@ -3015,15 +3095,15 @@ // content on the screen in all capture mode sources (fullscreen, region, and // window) depending on the test param. class CaptureModeHdcpTest - : public CaptureModeTest, + : public CaptureModeTestBase, public ::testing::WithParamInterface<CaptureModeSource> { public: CaptureModeHdcpTest() = default; ~CaptureModeHdcpTest() override = default; - // CaptureModeTest: + // CaptureModeTestBase: void SetUp() override { - CaptureModeTest::SetUp(); + CaptureModeTestBase::SetUp(); window_ = CreateTestWindow(gfx::Rect(200, 200)); // Create a child window with protected content. This simulates the real // behavior of a browser window hosting a page with protected content, where @@ -3041,7 +3121,7 @@ protection_delegate_.reset(); protected_content_window_.reset(); window_.reset(); - CaptureModeTest::TearDown(); + CaptureModeTestBase::TearDown(); } // Enters the capture mode session. @@ -3096,7 +3176,7 @@ EndRecordingReason::kHdcpInterruption, 1); } -TEST_F(CaptureModeHdcpTest, ProtectedTabBecomesActiveAfterRecordingStarts) { +TEST_P(CaptureModeHdcpTest, ProtectedTabBecomesActiveAfterRecordingStarts) { // Simulate protected content being on an inactive tab. protection_delegate_->SetProtection(display::CONTENT_PROTECTION_METHOD_HDCP, base::DoNothing()); @@ -3213,7 +3293,7 @@ CaptureModeSource::kRegion, CaptureModeSource::kWindow)); -TEST_F(CaptureModeTest, ClosingWindowBeingRecorded) { +TEST_P(CaptureModeTest, ClosingWindowBeingRecorded) { auto window = CreateTestWindow(gfx::Rect(200, 200)); StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); @@ -3248,7 +3328,7 @@ EndRecordingReason::kDisplayOrWindowClosing, 1); } -TEST_F(CaptureModeTest, DetachDisplayWhileWindowRecording) { +TEST_P(CaptureModeTest, DetachDisplayWhileWindowRecording) { UpdateDisplay("500x400,401+0-500x400"); // Create a window on the second display. auto window = CreateTestWindow(gfx::Rect(450, 20, 200, 200)); @@ -3282,7 +3362,7 @@ EXPECT_TRUE(stop_recording_button->visible_preferred()); } -TEST_F(CaptureModeTest, SuspendWhileSessionIsActive) { +TEST_P(CaptureModeTest, SuspendWhileSessionIsActive) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); EXPECT_TRUE(controller->IsActive()); @@ -3291,7 +3371,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, SuspendAfterCountdownStarts) { +TEST_P(CaptureModeTest, SuspendAfterCountdownStarts) { // User NORMAL_DURATION for the countdown animation so we can have predictable // timings. ui::ScopedAnimationDurationScaleMode animation_scale( @@ -3308,7 +3388,7 @@ EXPECT_FALSE(controller->is_recording_in_progress()); } -TEST_F(CaptureModeTest, SuspendAfterRecordingStarts) { +TEST_P(CaptureModeTest, SuspendAfterRecordingStarts) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); StartVideoRecordingImmediately(); @@ -3322,7 +3402,7 @@ EndRecordingReason::kImminentSuspend, 1); } -TEST_F(CaptureModeTest, SwitchUsersWhileRecording) { +TEST_P(CaptureModeTest, SwitchUsersWhileRecording) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); StartVideoRecordingImmediately(); @@ -3335,7 +3415,7 @@ EndRecordingReason::kActiveUserChange, 1); } -TEST_F(CaptureModeTest, SwitchUsersAfterCountdownStarts) { +TEST_P(CaptureModeTest, SwitchUsersAfterCountdownStarts) { // User NORMAL_DURATION for the countdown animation so we can have predictable // timings. ui::ScopedAnimationDurationScaleMode animation_scale( @@ -3351,7 +3431,7 @@ EXPECT_FALSE(controller->is_recording_in_progress()); } -TEST_F(CaptureModeTest, ClosingDisplayBeingFullscreenRecorded) { +TEST_P(CaptureModeTest, ClosingDisplayBeingFullscreenRecorded) { UpdateDisplay("500x400,401+0-500x400"); auto roots = Shell::GetAllRootWindows(); ASSERT_EQ(2u, roots.size()); @@ -3385,7 +3465,7 @@ EndRecordingReason::kDisplayOrWindowClosing, 1); } -TEST_F(CaptureModeTest, ShuttingDownWhileRecording) { +TEST_P(CaptureModeTest, ShuttingDownWhileRecording) { StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); auto* controller = CaptureModeController::Get(); @@ -3398,7 +3478,7 @@ } // Tests that metrics are recorded properly for capture mode bar buttons. -TEST_F(CaptureModeTest, CaptureModeBarButtonTypeHistograms) { +TEST_P(CaptureModeTest, CaptureModeBarButtonTypeHistograms) { constexpr char kClamshellHistogram[] = "Ash.CaptureModeController.BarButtons.ClamshellMode"; constexpr char kTabletHistogram[] = @@ -3454,7 +3534,14 @@ CaptureModeBarButtonType::kWindow, 1); } -TEST_F(CaptureModeTest, CaptureSessionSwitchedModeMetric) { +TEST_P(CaptureModeTest, CaptureSessionSwitchedModeMetric) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + constexpr char kHistogramName[] = "Ash.CaptureModeController.SwitchesFromInitialCaptureMode"; base::HistogramTester histogram_tester; @@ -3488,7 +3575,7 @@ } // Test that cancel recording during countdown won't cause crash. -TEST_F(CaptureModeTest, CancelCaptureDuringCountDown) { +TEST_P(CaptureModeTest, CancelCaptureDuringCountDown) { ui::ScopedAnimationDurationScaleMode animation_scale( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); @@ -3505,7 +3592,7 @@ EXPECT_FALSE(test_api.IsVideoRecordingInProgress()); } -TEST_F(CaptureModeTest, EscDuringCountDownWhileSettingsOpen) { +TEST_P(CaptureModeTest, EscDuringCountDownWhileSettingsOpen) { ui::ScopedAnimationDurationScaleMode animation_scale( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); @@ -3526,7 +3613,7 @@ } // Tests that metrics are recorded properly for capture region adjustments. -TEST_F(CaptureModeTest, NumberOfCaptureRegionAdjustmentsHistogram) { +TEST_P(CaptureModeTest, NumberOfCaptureRegionAdjustmentsHistogram) { constexpr char kClamshellHistogram[] = "Ash.CaptureModeController.CaptureRegionAdjusted.ClamshellMode"; constexpr char kTabletHistogram[] = @@ -3607,7 +3694,7 @@ histogram_tester.ExpectBucketCount(kTabletHistogram, 0, 1); } -TEST_F(CaptureModeTest, ResizeRegionBoundedByDisplay) { +TEST_P(CaptureModeTest, ResizeRegionBoundedByDisplay) { UpdateDisplay("800x700"); auto* controller = StartImageRegionCapture(); @@ -3641,7 +3728,7 @@ EXPECT_EQ(gfx::Rect(0, 0, 800, 700), controller->user_capture_region()); } -TEST_F(CaptureModeTest, FullscreenCapture) { +TEST_P(CaptureModeTest, FullscreenCapture) { ui::ScopedAnimationDurationScaleMode animation_scale( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); CaptureModeController* controller = StartCaptureSession( @@ -3663,7 +3750,7 @@ // Tests that there is no crash when touching the capture label widget in tablet // mode when capturing a window. Regression test for https://crbug.com/1152938. -TEST_F(CaptureModeTest, TabletTouchCaptureLabelWidgetWindowMode) { +TEST_P(CaptureModeTest, TabletTouchCaptureLabelWidgetWindowMode) { SwitchToTabletMode(); // Enter capture window mode. @@ -3687,7 +3774,14 @@ // Tests that after rotating a display, the capture session widgets are updated // and the capture region is reset. -TEST_F(CaptureModeTest, DisplayRotation) { +TEST_P(CaptureModeTest, DisplayRotation) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test crashes when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + UpdateDisplay("1200x600"); auto* controller = StartImageRegionCapture(); @@ -3718,7 +3812,7 @@ capture_label_widget->GetWindowBoundsInScreen().CenterPoint()); } -TEST_F(CaptureModeTest, DisplayBoundsChange) { +TEST_P(CaptureModeTest, DisplayBoundsChange) { UpdateDisplay("1200x600"); auto* controller = StartImageRegionCapture(); @@ -3732,7 +3826,7 @@ GetCaptureModeBarView()->GetBoundsInScreen().CenterPoint().x()); } -TEST_F(CaptureModeTest, ReenterOnSmallerDisplay) { +TEST_P(CaptureModeTest, ReenterOnSmallerDisplay) { UpdateDisplay("1200x600,1201+0-700x600"); // Start off with the primary display as the targeted display. Create a region @@ -3753,7 +3847,7 @@ } // Tests tabbing when in capture window mode. -TEST_F(CaptureModeTest, KeyboardNavigationBasic) { +TEST_P(CaptureModeTest, KeyboardNavigationBasic) { auto* controller = StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage); @@ -3798,7 +3892,7 @@ // Tests tabbing through windows on multiple displays when in capture window // mode. -TEST_F(CaptureModeTest, KeyboardNavigationTabThroughWindowsOnMultipleDisplays) { +TEST_P(CaptureModeTest, KeyboardNavigationTabThroughWindowsOnMultipleDisplays) { UpdateDisplay("800x700,801+0-800x700"); std::vector<raw_ptr<aura::Window, VectorExperimental>> root_windows = Shell::GetAllRootWindows(); @@ -3909,7 +4003,7 @@ } // Tests that a click will remove focus. -TEST_F(CaptureModeTest, KeyboardNavigationClicksRemoveFocus) { +TEST_P(CaptureModeTest, KeyboardNavigationClicksRemoveFocus) { auto* controller = StartImageRegionCapture(); auto* event_generator = GetEventGenerator(); @@ -3922,7 +4016,14 @@ } // Tests that pressing space on a focused button will activate it. -TEST_F(CaptureModeTest, KeyboardNavigationSpaceToActivateButton) { +TEST_P(CaptureModeTest, KeyboardNavigationSpaceToActivateButton) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + auto* controller = StartImageRegionCapture(); SelectRegion(gfx::Rect(200, 200)); @@ -3957,7 +4058,7 @@ // Tests that functionality to create and adjust a region with keyboard // shortcuts works as intended. -TEST_F(CaptureModeTest, KeyboardNavigationSelectRegion) { +TEST_P(CaptureModeTest, KeyboardNavigationSelectRegion) { auto* controller = StartImageRegionCapture(); auto* event_generator = GetEventGenerator(); ASSERT_TRUE(controller->user_capture_region().IsEmpty()); @@ -4020,7 +4121,14 @@ } // Tests behavior regarding the default region when using keyboard navigation. -TEST_F(CaptureModeTest, KeyboardNavigationDefaultRegion) { +TEST_P(CaptureModeTest, KeyboardNavigationDefaultRegion) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + auto* controller = StartImageRegionCapture(); auto* event_generator = GetEventGenerator(); ASSERT_TRUE(controller->user_capture_region().IsEmpty()); @@ -4070,7 +4178,7 @@ EXPECT_EQ(gfx::Rect(), controller->user_capture_region()); } -TEST_F(CaptureModeTest, A11yEnterWithNoFocus) { +TEST_P(CaptureModeTest, A11yEnterWithNoFocus) { auto* controller = StartImageRegionCapture(); SelectRegion(gfx::Rect(20, 20, 200, 200)); CaptureModeSessionTestApi test_api(controller->capture_mode_session()); @@ -4083,7 +4191,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, A11yEnterWithFocusOnRegionKnob) { +TEST_P(CaptureModeTest, A11yEnterWithFocusOnRegionKnob) { auto* controller = StartImageRegionCapture(); SelectRegion(gfx::Rect(20, 20, 200, 200)); CaptureModeSessionTestApi test_api(controller->capture_mode_session()); @@ -4105,7 +4213,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, A11yEnterWithFocusOnWindow) { +TEST_P(CaptureModeTest, A11yEnterWithFocusOnWindow) { auto window = CreateTestWindow(gfx::Rect(200, 200)); auto* controller = StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage); @@ -4124,7 +4232,7 @@ EXPECT_FALSE(controller->IsActive()); } -TEST_F(CaptureModeTest, A11yEnterWithFocusOnFullscreenButton) { +TEST_P(CaptureModeTest, A11yEnterWithFocusOnFullscreenButton) { auto* controller = StartImageRegionCapture(); EXPECT_EQ(controller->source(), CaptureModeSource::kRegion); CaptureModeSessionTestApi test_api(controller->capture_mode_session()); @@ -4160,7 +4268,19 @@ // Tests that the UAF issue caused by `NotifyAccessibilityEvent` after the // button been destroyed has been handled without leading to a crash. -TEST_F(CaptureModeTest, KeyboardNavigationButtonDestroyedAfterBeenActivated) { +TEST_P(CaptureModeTest, KeyboardNavigationButtonDestroyedAfterBeenActivated) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled) { + // This test crashes if Sunfish is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + if (scanner_enabled) { + // This test fails if Sunfish is disabled and Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + auto* controller = StartImageRegionCapture(); SelectRegion(gfx::Rect(200, 300)); @@ -4191,7 +4311,14 @@ // Tests that accessibility overrides are set as expected on capture mode // widgets. -TEST_F(CaptureModeTest, AccessibilityFocusAnnotator) { +TEST_P(CaptureModeTest, AccessibilityFocusAnnotator) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + StartImageRegionCapture(); // Helper that takes in a current widget and checks if the accessibility next @@ -4230,7 +4357,7 @@ } // Tests that a captured image is written to the clipboard. -TEST_F(CaptureModeTest, ClipboardWrite) { +TEST_P(CaptureModeTest, ClipboardWrite) { auto* clipboard = ui::Clipboard::GetForCurrentThread(); ASSERT_NE(clipboard, nullptr); @@ -4248,7 +4375,7 @@ } // Tests the reverse tabbing behavior of the keyboard navigation. -TEST_F(CaptureModeTest, ReverseTabbingTest) { +TEST_P(CaptureModeTest, ReverseTabbingTest) { using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; auto* event_generator = GetEventGenerator(); for (CaptureModeSource source : @@ -4274,7 +4401,7 @@ // calling `Close()` or `CloseNow()` on the widget, we get a UAF. This can // happen when all the windows in the window tree hierarchy gets deleted e.g. // when shutting down. -TEST_F(CaptureModeTest, SettingsMenuWidgetDestruction) { +TEST_P(CaptureModeTest, SettingsMenuWidgetDestruction) { CaptureModeTestApi().StartForFullscreen(true); ClickOnView(GetSettingsButton(), GetEventGenerator()); auto* widget = GetCaptureModeSettingsWidget(); @@ -4292,8 +4419,18 @@ ~CaptureModeMockTimeTest() override = default; }; +INSTANTIATE_TEST_SUITE_P( + , + CaptureModeMockTimeTest, + testing::Combine(testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<CaptureModeMockTimeTest::ParamType>& info) { + bool sunfish_enabled = std::get<0>(info.param); + bool scanner_enabled = std::get<1>(info.param); + return SunfishScannerTestName(sunfish_enabled, scanner_enabled); + }); + // Tests that the consecutive screenshots histogram is recorded properly. -TEST_F(CaptureModeMockTimeTest, ConsecutiveScreenshotsHistograms) { +TEST_P(CaptureModeMockTimeTest, ConsecutiveScreenshotsHistograms) { constexpr char kConsecutiveScreenshotsHistogram[] = "Ash.CaptureModeController.ConsecutiveScreenshots"; base::HistogramTester histogram_tester; @@ -4332,7 +4469,7 @@ } // Tests that the user capture region will be cleared up after a period of time. -TEST_F(CaptureModeMockTimeTest, ClearUserCaptureRegionBetweenSessions) { +TEST_P(CaptureModeMockTimeTest, ClearUserCaptureRegionBetweenSessions) { UpdateDisplay("900x800"); auto* controller = StartImageRegionCapture(); EXPECT_EQ(gfx::Rect(), controller->user_capture_region()); @@ -4361,7 +4498,14 @@ } // Tests that in Region mode, the capture bar hides and shows itself correctly. -TEST_F(CaptureModeTest, CaptureBarOpacity) { +TEST_P(CaptureModeTest, CaptureBarOpacity) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test fails when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + UpdateDisplay("800x700"); auto* event_generator = GetEventGenerator(); @@ -4420,7 +4564,7 @@ EXPECT_EQ(1.f, capture_bar_layer->GetTargetOpacity()); } -TEST_F(CaptureModeTest, CaptureBarOpacityOnHoveringOnCaptureLabel) { +TEST_P(CaptureModeTest, CaptureBarOpacityOnHoveringOnCaptureLabel) { UpdateDisplay("800x700"); auto* event_generator = GetEventGenerator(); @@ -4445,7 +4589,7 @@ } // Tests that the quick action histogram is recorded properly. -TEST_F(CaptureModeTest, QuickActionHistograms) { +TEST_P(CaptureModeTest, QuickActionHistograms) { constexpr char kQuickActionHistogramName[] = "Ash.CaptureModeController.QuickAction"; base::HistogramTester histogram_tester; @@ -4497,7 +4641,7 @@ CaptureQuickAction::kBacklight, 1); } -TEST_F(CaptureModeTest, NotificationButtonOfVideoRecording) { +TEST_P(CaptureModeTest, NotificationButtonOfVideoRecording) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); StartVideoRecordingImmediately(); @@ -4518,7 +4662,7 @@ EXPECT_FALSE(GetPreviewNotification()); } -TEST_F(CaptureModeTest, CannotDoMultipleRecordings) { +TEST_P(CaptureModeTest, CannotDoMultipleRecordings) { StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); auto* controller = CaptureModeController::Get(); @@ -4558,7 +4702,7 @@ } // Tests the basic settings menu functionality. -TEST_F(CaptureModeTest, SettingsMenuVisibilityBasic) { +TEST_P(CaptureModeTest, SettingsMenuVisibilityBasic) { auto* event_generator = GetEventGenerator(); auto* controller = StartImageRegionCapture(); EXPECT_TRUE(controller->IsActive()); @@ -4579,7 +4723,7 @@ // Tests how interacting with the rest of the screen (i.e. clicking outside of // the bar/menu, on other buttons) affects whether the settings menu should // close or not. -TEST_F(CaptureModeTest, SettingsMenuVisibilityClicking) { +TEST_P(CaptureModeTest, SettingsMenuVisibilityClicking) { UpdateDisplay("800x700"); auto* event_generator = GetEventGenerator(); @@ -4633,7 +4777,14 @@ // Tests capture bar and settings menu visibility / opacity when capture region // is being or after drawn. -TEST_F(CaptureModeTest, CaptureBarAndSettingsMenuVisibilityDrawingRegion) { +TEST_P(CaptureModeTest, CaptureBarAndSettingsMenuVisibilityDrawingRegion) { + auto [sunfish_enabled, scanner_enabled] = GetParam(); + if (sunfish_enabled || scanner_enabled) { + // This test crashes when either Sunfish or Scanner is enabled. + // TODO: b/381965299 - Fix these test failures. + GTEST_SKIP(); + } + UpdateDisplay("800x700"); auto* event_generator = GetEventGenerator(); @@ -4734,7 +4885,7 @@ capture_bar_layer->GetTargetOpacity()); } -TEST_F(CaptureModeTest, CaptureFolderSetting) { +TEST_P(CaptureModeTest, CaptureFolderSetting) { auto* controller = CaptureModeController::Get(); auto* test_delegate = controller->delegate_for_testing(); const auto default_downloads_folder = @@ -4752,7 +4903,7 @@ EXPECT_FALSE(capture_folder.is_default_downloads_folder); } -TEST_F(CaptureModeTest, CaptureFolderSetToDefaultDownloads) { +TEST_P(CaptureModeTest, CaptureFolderSetToDefaultDownloads) { auto* controller = CaptureModeController::Get(); auto* test_delegate = controller->delegate_for_testing(); @@ -4772,7 +4923,7 @@ EXPECT_TRUE(capture_folder.is_default_downloads_folder); } -TEST_F(CaptureModeTest, UsesDefaultFolderWithCustomFolderSet) { +TEST_P(CaptureModeTest, UsesDefaultFolderWithCustomFolderSet) { auto* controller = CaptureModeController::Get(); auto* test_delegate = controller->delegate_for_testing(); @@ -4800,7 +4951,7 @@ EXPECT_FALSE(capture_folder.is_default_downloads_folder); } -TEST_F(CaptureModeTest, CaptureFolderSetToEmptyPath) { +TEST_P(CaptureModeTest, CaptureFolderSetToEmptyPath) { auto* controller = CaptureModeController::Get(); auto* test_delegate = controller->delegate_for_testing(); @@ -4820,7 +4971,7 @@ EXPECT_TRUE(capture_folder.is_default_downloads_folder); } -TEST_F(CaptureModeTest, SimulateUserCancelingDlpWarningDialog) { +TEST_P(CaptureModeTest, SimulateUserCancelingDlpWarningDialog) { auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); StartVideoRecordingImmediately(); @@ -4846,7 +4997,7 @@ // Tests that `CaptureScreenshotOfGivenWindow` can take window screenshot // successfully and that the image size matches the window size. -TEST_F(CaptureModeTest, InstantScreenshotForkWindow) { +TEST_P(CaptureModeTest, InstantScreenshotForkWindow) { const gfx::Rect window_bounds(10, 20, 700, 500); std::unique_ptr<aura::Window> window(CreateTestWindow(window_bounds)); CaptureModeController::Get()->CaptureScreenshotOfGivenWindow(window.get()); @@ -4857,7 +5008,7 @@ // Tests the capture mode behavior in the default capture mode session and // during video recording. -TEST_F(CaptureModeTest, CaptureModeDefaultBehavior) { +TEST_P(CaptureModeTest, CaptureModeDefaultBehavior) { CaptureModeController* controller = StartCaptureSession( CaptureModeSource::kFullscreen, CaptureModeType::kVideo); ASSERT_TRUE(controller->IsActive()); @@ -4909,7 +5060,7 @@ // 'Ctrl + Shift + Overview' with `kImage` as the default type and `kRegion` as // the default source. And the screen recording can be ended with the keyboard // shortcut 'Search + Shift + X'. -TEST_F(CaptureModeTest, KeyboardShortcutTest) { +TEST_P(CaptureModeTest, KeyboardShortcutTest) { base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount( kEndRecordingReasonInClamshellHistogramName, @@ -4936,7 +5087,7 @@ EndRecordingReason::kKeyboardShortcut, 1); } -TEST_F(CaptureModeTest, StopRecordingButtonTrayAccessibleName) { +TEST_P(CaptureModeTest, StopRecordingButtonTrayAccessibleName) { ash::CaptureModeTestApi test_api; test_api.StartForFullscreen(/*for_video=*/true); test_api.PerformCapture(); @@ -5085,9 +5236,20 @@ std::unique_ptr<TestVideoCaptureOverlay> fake_overlay_; }; +INSTANTIATE_TEST_SUITE_P( + , + CaptureModeCursorOverlayTest, + testing::Combine(testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<CaptureModeCursorOverlayTest::ParamType>& + info) { + bool sunfish_enabled = std::get<0>(info.param); + bool scanner_enabled = std::get<1>(info.param); + return SunfishScannerTestName(sunfish_enabled, scanner_enabled); + }); + } // namespace -TEST_F(CaptureModeCursorOverlayTest, TabletModeHidesCursorOverlay) { +TEST_P(CaptureModeCursorOverlayTest, TabletModeHidesCursorOverlay) { StartRecordingAndSetupFakeOverlay(CaptureModeSource::kFullscreen); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5104,7 +5266,7 @@ // Tests that the cursor is hidden while taking a screenshot in tablet mode and // remains hidden afterward. -TEST_F(CaptureModeCursorOverlayTest, TabletModeHidesCursor) { +TEST_P(CaptureModeCursorOverlayTest, TabletModeHidesCursor) { // Enter tablet mode. SwitchToTabletMode(); @@ -5129,7 +5291,7 @@ // Tests that a cursor is hidden while taking a fullscreen screenshot // (crbug.com/1186652). -TEST_F(CaptureModeCursorOverlayTest, CursorInFullscreenScreenshot) { +TEST_P(CaptureModeCursorOverlayTest, CursorInFullscreenScreenshot) { auto* cursor_manager = Shell::Get()->cursor_manager(); EXPECT_FALSE(cursor_manager->IsCursorLocked()); CaptureModeController* controller = StartCaptureSession( @@ -5150,7 +5312,7 @@ // Tests that a cursor is hidden while taking a region screenshot // (crbug.com/1186652). -TEST_F(CaptureModeCursorOverlayTest, CursorInPartialRegionScreenshot) { +TEST_P(CaptureModeCursorOverlayTest, CursorInPartialRegionScreenshot) { // Use a set display size as we will be choosing points in this test. UpdateDisplay("800x700"); @@ -5174,7 +5336,7 @@ CaptureScreenshotAndCheckCursorVisibility(controller); } -TEST_F(CaptureModeCursorOverlayTest, SoftwareCursorInitiallyEnabled) { +TEST_P(CaptureModeCursorOverlayTest, SoftwareCursorInitiallyEnabled) { // The software cursor is enabled before recording starts. SetDockedMagnifierEnabled(true); EXPECT_TRUE(IsCursorCompositingEnabled()); @@ -5184,7 +5346,7 @@ EXPECT_TRUE(fake_overlay()->IsHidden()); } -TEST_F(CaptureModeCursorOverlayTest, SoftwareCursorInFullscreenRecording) { +TEST_P(CaptureModeCursorOverlayTest, SoftwareCursorInFullscreenRecording) { StartRecordingAndSetupFakeOverlay(CaptureModeSource::kFullscreen); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5201,7 +5363,7 @@ EXPECT_FALSE(fake_overlay()->IsHidden()); } -TEST_F(CaptureModeCursorOverlayTest, SoftwareCursorInPartialRegionRecording) { +TEST_P(CaptureModeCursorOverlayTest, SoftwareCursorInPartialRegionRecording) { CaptureModeController::Get()->SetUserCaptureRegion(gfx::Rect(20, 20), /*by_user=*/true); StartRecordingAndSetupFakeOverlay(CaptureModeSource::kRegion); @@ -5214,7 +5376,7 @@ EXPECT_TRUE(fake_overlay()->IsHidden()); } -TEST_F(CaptureModeCursorOverlayTest, SoftwareCursorInWindowRecording) { +TEST_P(CaptureModeCursorOverlayTest, SoftwareCursorInWindowRecording) { StartRecordingAndSetupFakeOverlay(CaptureModeSource::kWindow); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5228,7 +5390,7 @@ EXPECT_FALSE(fake_overlay()->IsHidden()); } -TEST_F(CaptureModeCursorOverlayTest, OverlayHidesWhenOutOfBounds) { +TEST_P(CaptureModeCursorOverlayTest, OverlayHidesWhenOutOfBounds) { StartRecordingAndSetupFakeOverlay(CaptureModeSource::kWindow); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5261,7 +5423,7 @@ } // namespace -TEST_F(CaptureModeCursorOverlayTest, OverlayWhenCursorIsHiddenOrFails) { +TEST_P(CaptureModeCursorOverlayTest, OverlayWhenCursorIsHiddenOrFails) { StartRecordingAndSetupFakeOverlay(CaptureModeSource::kWindow); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5314,7 +5476,7 @@ // Verifies that the cursor overlay bounds calculation takes into account the // cursor image scale factor. https://crbug.com/1222494. -TEST_F(CaptureModeCursorOverlayTest, OverlayBoundsAccountForCursorScaleFactor) { +TEST_P(CaptureModeCursorOverlayTest, OverlayBoundsAccountForCursorScaleFactor) { UpdateDisplay("500x400"); StartRecordingAndSetupFakeOverlay(CaptureModeSource::kFullscreen); EXPECT_FALSE(fake_overlay()->IsHidden()); @@ -5388,7 +5550,7 @@ } // namespace -class ProjectorCaptureModeIntegrationTests : public CaptureModeTest { +class ProjectorCaptureModeIntegrationTests : public CaptureModeTestBase { public: ProjectorCaptureModeIntegrationTests() = default; ~ProjectorCaptureModeIntegrationTests() override = default; @@ -5400,9 +5562,9 @@ } aura::Window* window() const { return window_.get(); } - // CaptureModeTest: + // CaptureModeTestBase: void SetUp() override { - CaptureModeTest::SetUp(); + CaptureModeTestBase::SetUp(); projector_helper_.SetUp(); window_ = CreateTestWindow(gfx::Rect(20, 30, 200, 200)); CaptureModeController::Get()->SetUserCaptureRegion(kUserRegion, @@ -5411,7 +5573,7 @@ void TearDown() override { window_.reset(); - CaptureModeTest::TearDown(); + CaptureModeTestBase::TearDown(); } void StartProjectorModeSession() { @@ -6183,9 +6345,7 @@ CaptureModeSource::kRegion, CaptureModeSource::kWindow)); -class AnnotatorCaptureModeIntegrationTests - : public CaptureModeTest, - public ::testing::WithParamInterface<CaptureModeSource> { +class AnnotatorCaptureModeIntegrationTests : public CaptureModeTestBase { public: AnnotatorCaptureModeIntegrationTests() = default; ~AnnotatorCaptureModeIntegrationTests() override = default; @@ -6194,9 +6354,9 @@ aura::Window* window() const { return window_.get(); } - // CaptureModeTest: + // CaptureModeTestBase: void SetUp() override { - CaptureModeTest::SetUp(); + CaptureModeTestBase::SetUp(); annotator_helper_.SetUp(); window_ = CreateTestWindow(gfx::Rect(20, 30, 200, 200)); CaptureModeController::Get()->SetUserCaptureRegion(kUserRegion, @@ -6205,7 +6365,7 @@ void TearDown() override { window_.reset(); - CaptureModeTest::TearDown(); + CaptureModeTestBase::TearDown(); } void StartRecordingFromSource(CaptureModeSource source) { @@ -6235,6 +6395,10 @@ base::HistogramTester histogram_tester_; }; +class AnnotatorCaptureModeIntegrationTestsWithSource + : public AnnotatorCaptureModeIntegrationTests, + public ::testing::WithParamInterface<CaptureModeSource> {}; + TEST_F(AnnotatorCaptureModeIntegrationTests, AnnotationsOverlayWidget) { StartRecordingFromSource(CaptureModeSource::kFullscreen); @@ -6443,7 +6607,8 @@ } } -TEST_P(AnnotatorCaptureModeIntegrationTests, AnnotationsOverlayWidgetBounds) { +TEST_P(AnnotatorCaptureModeIntegrationTestsWithSource, + AnnotationsOverlayWidgetBounds) { const auto capture_source = GetParam(); StartRecordingFromSource(capture_source); CaptureModeTestApi test_api; @@ -6454,7 +6619,7 @@ VerifyOverlayWindow(overlay_window, capture_source, kUserRegion); } -TEST_P(AnnotatorCaptureModeIntegrationTests, +TEST_P(AnnotatorCaptureModeIntegrationTestsWithSource, AnnotationsOverlayWidgetBoundsSecondDisplay) { UpdateDisplay("800x700,801+0-800x700"); const gfx::Point point_in_second_display = gfx::Point(1000, 500); @@ -6479,7 +6644,7 @@ } INSTANTIATE_TEST_SUITE_P(All, - AnnotatorCaptureModeIntegrationTests, + AnnotatorCaptureModeIntegrationTestsWithSource, testing::Values(CaptureModeSource::kFullscreen, CaptureModeSource::kRegion, CaptureModeSource::kWindow)); @@ -6488,20 +6653,20 @@ // CaptureModeSettingsTest: // Test fixture for CaptureMode settings view. -class CaptureModeSettingsTest : public CaptureModeTest { +class CaptureModeSettingsTest : public CaptureModeTestBase { public: CaptureModeSettingsTest() = default; ~CaptureModeSettingsTest() override = default; - // CaptureModeTest: + // CaptureModeTestBase: void SetUp() override { - CaptureModeTest::SetUp(); + CaptureModeTestBase::SetUp(); FakeFolderSelectionDialogFactory::Start(); } void TearDown() override { FakeFolderSelectionDialogFactory::Stop(); - CaptureModeTest::TearDown(); + CaptureModeTestBase::TearDown(); } CaptureModeSettingsView* GetCaptureModeSettingsView() const {
diff --git a/ash/capture_mode/search_results_panel.cc b/ash/capture_mode/search_results_panel.cc index 2e8b139..ae50964 100644 --- a/ash/capture_mode/search_results_panel.cc +++ b/ash/capture_mode/search_results_panel.cc
@@ -126,7 +126,7 @@ // Resize the image to fit in the searchbox, keeping the same aspect ratio. const int target_height = height(); const int target_width = (image.width() * target_height) / image.height(); - image_view_->SetImage(image); + image_view_->SetImage(ui::ImageModel::FromImageSkia(image)); image_view_->SetImageSize(gfx::Size(target_width, target_height)); }
diff --git a/ash/capture_mode/stop_recording_button_tray.cc b/ash/capture_mode/stop_recording_button_tray.cc index 5386d87..b008135 100644 --- a/ash/capture_mode/stop_recording_button_tray.cc +++ b/ash/capture_mode/stop_recording_button_tray.cc
@@ -41,17 +41,12 @@ image_view_->SetHorizontalAlignment(views::ImageView::Alignment::kCenter); image_view_->SetVerticalAlignment(views::ImageView::Alignment::kCenter); image_view_->SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize)); + image_view_->SetImage(ui::ImageModel::FromVectorIcon( + kCaptureModeCircleStopIcon, kColorAshIconColorAlert)); } StopRecordingButtonTray::~StopRecordingButtonTray() = default; -void StopRecordingButtonTray::OnThemeChanged() { - TrayBackgroundView::OnThemeChanged(); - image_view_->SetImage(gfx::CreateVectorIcon( - kCaptureModeCircleStopIcon, - GetColorProvider()->GetColor(kColorAshIconColorAlert))); -} - BEGIN_METADATA(StopRecordingButtonTray) END_METADATA
diff --git a/ash/capture_mode/stop_recording_button_tray.h b/ash/capture_mode/stop_recording_button_tray.h index 0dc7d14b..bce04d96 100644 --- a/ash/capture_mode/stop_recording_button_tray.h +++ b/ash/capture_mode/stop_recording_button_tray.h
@@ -41,7 +41,6 @@ void UpdateTrayItemColor(bool is_active) override {} void HandleLocaleChange() override {} void HideBubbleWithView(const TrayBubbleView* bubble_view) override {} - void OnThemeChanged() override; void HideBubble(const TrayBubbleView* bubble_view) override {} // Image view of the stop recording icon.
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index d3369c30..7ae7a78 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -2218,12 +2218,6 @@ "OsFeedbackDialog", base::FEATURE_ENABLED_BY_DEFAULT); -// Whether the DNS dialog in should be deprecated in Security and Privacy -// Settings page when the user toggles off the DNS button. -BASE_FEATURE(kOsSettingsDeprecateDnsDialog, - "OsSettingsDeprecateDnsDialog", - base::FEATURE_ENABLED_BY_DEFAULT); - // Enables Jelly colors and components to appear in the Parent Access Widget // if jelly-colors is also enabled. BASE_FEATURE(kParentAccessJelly, @@ -4165,10 +4159,6 @@ return base::FeatureList::IsEnabled(kOsFeedbackDialog); } -bool IsOsSettingsDeprecateDnsDialogEnabled() { - return base::FeatureList::IsEnabled(kOsSettingsDeprecateDnsDialog); -} - bool IsOsSyncConsentRevampEnabled() { return base::FeatureList::IsEnabled(kOsSyncConsentRevamp); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index baf594dd..9a96268 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -722,7 +722,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kOsFeedbackDialog); COMPONENT_EXPORT(ASH_CONSTANTS) -BASE_DECLARE_FEATURE(kOsSettingsDeprecateDnsDialog); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kOsSyncConsentRevamp); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kParentAccessJelly); COMPONENT_EXPORT(ASH_CONSTANTS) @@ -1257,7 +1256,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeDisplaySizeEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeSplitModifierKeyboardInfoEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeInputMethodsEnabled(); -COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOsSettingsDeprecateDnsDialogEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOsSyncConsentRevampEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsParentAccessJellyEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsPasswordlessGaiaEnabledForConsumers();
diff --git a/ash/display/display_alignment_indicator.cc b/ash/display/display_alignment_indicator.cc index 66a5926..2532aab 100644 --- a/ash/display/display_alignment_indicator.cc +++ b/ash/display/display_alignment_indicator.cc
@@ -242,7 +242,7 @@ text_label_->SetText(text); - icon_->SetImage(arrow_image_); + icon_->SetImage(ui::ImageModel::FromImageSkia(arrow_image_)); } IndicatorPillView(const IndicatorPillView&) = delete; @@ -303,20 +303,23 @@ switch (position) { case IndicatorPosition::kLeft: - icon_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage( - arrow_image_, SkBitmapOperations::ROTATION_180_CW)); + icon_->SetImage(ui::ImageModel::FromImageSkia( + gfx::ImageSkiaOperations::CreateRotatedImage( + arrow_image_, SkBitmapOperations::ROTATION_180_CW))); return; case IndicatorPosition::kRight: // |arrow_image_| points to right by default; no rotation required. - icon_->SetImage(arrow_image_); + icon_->SetImage(ui::ImageModel::FromImageSkia(arrow_image_)); return; case IndicatorPosition::kTop: - icon_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage( - arrow_image_, SkBitmapOperations::ROTATION_270_CW)); + icon_->SetImage(ui::ImageModel::FromImageSkia( + gfx::ImageSkiaOperations::CreateRotatedImage( + arrow_image_, SkBitmapOperations::ROTATION_270_CW))); return; case IndicatorPosition::kBottom: - icon_->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage( - arrow_image_, SkBitmapOperations::ROTATION_90_CW)); + icon_->SetImage(ui::ImageModel::FromImageSkia( + gfx::ImageSkiaOperations::CreateRotatedImage( + arrow_image_, SkBitmapOperations::ROTATION_90_CW))); return; } }
diff --git a/ash/lobster/lobster_controller.cc b/ash/lobster/lobster_controller.cc index a2e2b90..221ec7bc5 100644 --- a/ash/lobster/lobster_controller.cc +++ b/ash/lobster/lobster_controller.cc
@@ -21,11 +21,13 @@ LobsterController::Trigger::Trigger(std::unique_ptr<LobsterClient> client, LobsterEntryPoint entry_point, - LobsterMode mode) + LobsterMode mode, + const gfx::Rect& caret_bounds) : client_(std::move(client)), state_(State::kReady), entry_point_(entry_point), - mode_(mode) {} + mode_(mode), + caret_bounds_(caret_bounds) {} LobsterController::Trigger::~Trigger() = default; @@ -47,7 +49,7 @@ } controller->StartSession(std::move(client_), std::move(query), entry_point_, - mode_); + mode_, caret_bounds_); } LobsterController::LobsterController() = default; @@ -60,7 +62,8 @@ std::unique_ptr<LobsterController::Trigger> LobsterController::CreateTrigger( LobsterEntryPoint entry_point, - bool support_image_insertion) { + bool support_image_insertion, + const gfx::Rect& caret_bounds) { if (client_factory_ == nullptr) { return nullptr; } @@ -74,14 +77,16 @@ ? std::make_unique<Trigger>(std::move(client), entry_point, support_image_insertion ? LobsterMode::kInsert - : LobsterMode::kDownload) + : LobsterMode::kDownload, + caret_bounds) : nullptr; } void LobsterController::StartSession(std::unique_ptr<LobsterClient> client, std::optional<std::string> query, LobsterEntryPoint entry_point, - LobsterMode mode) { + LobsterMode mode, + const gfx::Rect& caret_bounds) { // Before creating a new session, we need to inform the lobster client and // lobster session to clear their pointer to the session that is about to be // destroyed. This is to prevent them from holding a dangling pointer to the @@ -93,7 +98,7 @@ active_session_ = std::make_unique<LobsterSessionImpl>(std::move(client), entry_point); lobster_client_ptr->SetActiveSession(active_session_.get()); - active_session_->LoadUI(query, mode); + active_session_->LoadUI(query, mode, caret_bounds); } } // namespace ash
diff --git a/ash/lobster/lobster_controller.h b/ash/lobster/lobster_controller.h index e8dae62..62a8acd3 100644 --- a/ash/lobster/lobster_controller.h +++ b/ash/lobster/lobster_controller.h
@@ -27,7 +27,8 @@ public: explicit Trigger(std::unique_ptr<LobsterClient> client, LobsterEntryPoint entry_point, - LobsterMode mode); + LobsterMode mode, + const gfx::Rect& caret_bounds); ~Trigger(); void Fire(std::optional<std::string> query); @@ -46,6 +47,8 @@ LobsterEntryPoint entry_point_; LobsterMode mode_; + + gfx::Rect caret_bounds_; }; LobsterController(); @@ -54,7 +57,8 @@ void SetClientFactory(LobsterClientFactory* client_factory); std::unique_ptr<Trigger> CreateTrigger(LobsterEntryPoint entry_point, - bool support_image_insertion); + bool support_image_insertion, + const gfx::Rect& caret_bounds); private: friend class Trigger; @@ -62,7 +66,8 @@ void StartSession(std::unique_ptr<LobsterClient> client, std::optional<std::string> query, LobsterEntryPoint entry_point, - LobsterMode mode); + LobsterMode mode, + const gfx::Rect& caret_bounds); // Not owned by this class. raw_ptr<LobsterClientFactory> client_factory_;
diff --git a/ash/lobster/lobster_session_impl.cc b/ash/lobster/lobster_session_impl.cc index 74ec157..e21ea65 100644 --- a/ash/lobster/lobster_session_impl.cc +++ b/ash/lobster/lobster_session_impl.cc
@@ -265,8 +265,9 @@ } void LobsterSessionImpl::LoadUI(std::optional<std::string> query, - LobsterMode mode) { - client_->LoadUI(query, mode); + LobsterMode mode, + const gfx::Rect& caret_bounds) { + client_->LoadUI(query, mode, caret_bounds); } void LobsterSessionImpl::ShowUI() {
diff --git a/ash/lobster/lobster_session_impl.h b/ash/lobster/lobster_session_impl.h index c993fa3..507490f 100644 --- a/ash/lobster/lobster_session_impl.h +++ b/ash/lobster/lobster_session_impl.h
@@ -54,7 +54,9 @@ LobsterPreviewFeedbackCallback) override; bool SubmitFeedback(int candidate_id, const std::string& description) override; - void LoadUI(std::optional<std::string> query, LobsterMode mode) override; + void LoadUI(std::optional<std::string> query, + LobsterMode mode, + const gfx::Rect& caret_bounds) override; void ShowUI() override; void CloseUI() override; void RecordWebUIMetricEvent(ash::LobsterMetricState metric_event) override;
diff --git a/ash/lobster/lobster_session_impl_unittest.cc b/ash/lobster/lobster_session_impl_unittest.cc index a0d978c..81f50d9 100644 --- a/ash/lobster/lobster_session_impl_unittest.cc +++ b/ash/lobster/lobster_session_impl_unittest.cc
@@ -77,7 +77,9 @@ (override)); MOCK_METHOD(void, LoadUI, - (std::optional<std::string> query, LobsterMode mode), + (std::optional<std::string> query, + LobsterMode mode, + const gfx::Rect& caret_bounds), (override)); MOCK_METHOD(void, ShowUI, (), (override)); MOCK_METHOD(void, CloseUI, (), (override));
diff --git a/ash/public/cpp/lobster/lobster_client.h b/ash/public/cpp/lobster/lobster_client.h index dbe568a6..95f89af5 100644 --- a/ash/public/cpp/lobster/lobster_client.h +++ b/ash/public/cpp/lobster/lobster_client.h
@@ -35,7 +35,9 @@ const std::string& model_version, const std::string& description, const std::string& image_bytes) = 0; - virtual void LoadUI(std::optional<std::string> query, LobsterMode mode) = 0; + virtual void LoadUI(std::optional<std::string> query, + LobsterMode mode, + const gfx::Rect& caret_bounds) = 0; virtual void ShowUI() = 0; virtual void CloseUI() = 0; };
diff --git a/ash/public/cpp/lobster/lobster_session.h b/ash/public/cpp/lobster/lobster_session.h index e7b22974..655000def 100644 --- a/ash/public/cpp/lobster/lobster_session.h +++ b/ash/public/cpp/lobster/lobster_session.h
@@ -17,6 +17,10 @@ #include "base/functional/callback.h" #include "url/gurl.h" +namespace gfx { +class Rect; +} + namespace ash { class ASH_PUBLIC_EXPORT LobsterSession { @@ -42,7 +46,9 @@ virtual bool SubmitFeedback(int candidate_id, const std::string& description) = 0; - virtual void LoadUI(std::optional<std::string> query, LobsterMode mode) = 0; + virtual void LoadUI(std::optional<std::string> query, + LobsterMode mode, + const gfx::Rect& caret_bounds) = 0; virtual void ShowUI() = 0; virtual void CloseUI() = 0; virtual void RecordWebUIMetricEvent(LobsterMetricState metric_state) = 0;
diff --git a/ash/quick_insert/mock_quick_insert_client.h b/ash/quick_insert/mock_quick_insert_client.h index 941adf15..af23c9c 100644 --- a/ash/quick_insert/mock_quick_insert_client.h +++ b/ash/quick_insert/mock_quick_insert_client.h
@@ -39,7 +39,7 @@ MOCK_METHOD(ShowEditorCallback, CacheEditorContext, (), (override)); MOCK_METHOD(ShowLobsterCallback, CacheLobsterContext, - (bool support_image_insertion), + (bool support_image_insertion, const gfx::Rect& caret_bounds), (override)); MOCK_METHOD(void, GetSuggestedEditorResults,
diff --git a/ash/quick_insert/quick_insert_client.h b/ash/quick_insert/quick_insert_client.h index 40527190..d58893c 100644 --- a/ash/quick_insert/quick_insert_client.h +++ b/ash/quick_insert/quick_insert_client.h
@@ -28,6 +28,7 @@ } namespace gfx { +class Rect; class Size; } @@ -82,7 +83,8 @@ virtual ShowEditorCallback CacheEditorContext() = 0; virtual ShowLobsterCallback CacheLobsterContext( - bool support_image_insertion) = 0; + bool support_image_insertion, + const gfx::Rect& caret_bounds) = 0; virtual void GetSuggestedEditorResults( SuggestedEditorResultsCallback callback) = 0;
diff --git a/ash/quick_insert/quick_insert_controller.cc b/ash/quick_insert/quick_insert_controller.cc index 438a3034..340100e 100644 --- a/ash/quick_insert/quick_insert_controller.cc +++ b/ash/quick_insert/quick_insert_controller.cc
@@ -623,7 +623,8 @@ show_editor_callback_ = client_->CacheEditorContext(); show_lobster_callback_ = client_->CacheLobsterContext( /*support_image_insertion=*/focused_text_input_client && - focused_text_input_client->CanInsertImage()); + focused_text_input_client->CanInsertImage(), + /*caret_bounds=*/GetCaretBounds()); input_method::ImeKeyboard& keyboard = GetImeKeyboard(); if (focused_text_input_client &&
diff --git a/ash/system/video_conference/bubble/title_view.cc b/ash/system/video_conference/bubble/title_view.cc index 5a71b2f..21f25ec 100644 --- a/ash/system/video_conference/bubble/title_view.cc +++ b/ash/system/video_conference/bubble/title_view.cc
@@ -108,8 +108,9 @@ auto* background_layer = background_view_->layer(); background_layer->SetRoundedCornerRadius(gfx::RoundedCornersF(16)); - AddChildView(std::make_unique<MicTestButtonContainer>(base::BindRepeating( - &MicTestButton::OnMicTestButtonClicked, base::Unretained(this)))); + button_container_ = + AddChildView(std::make_unique<MicTestButtonContainer>(base::BindRepeating( + &MicTestButton::OnMicTestButtonClicked, base::Unretained(this)))); } void MicTestButton::OnThemeChanged() { @@ -120,6 +121,7 @@ ? cros_tokens::kCrosSysSystemPrimaryContainer : cros_tokens::kCrosSysSystemOnBase); background_view_->layer()->SetColor(color); + button_container_->OnThemeChanged(); } void MicTestButton::OnMicTestButtonClicked(const ui::Event& event) { @@ -220,6 +222,7 @@ : cros_tokens::kCrosSysOnSurface; sidetone_icon_->SetImage( ui::ImageModel::FromVectorIcon(kVideoConferenceSidetoneIcon, color_id)); + mic_indicator_->OnThemeChanged(); } MicTestButtonContainer::~MicTestButtonContainer() = default;
diff --git a/ash/system/video_conference/bubble/title_view.h b/ash/system/video_conference/bubble/title_view.h index 8a77a75..ba9c351 100644 --- a/ash/system/video_conference/bubble/title_view.h +++ b/ash/system/video_conference/bubble/title_view.h
@@ -62,10 +62,12 @@ void OnMicTestButtonClicked(const ui::Event& event); void CloseSidetoneBubble(); void ShowSidetoneBubble(const bool supported); + // views::View void OnThemeChanged() override; raw_ptr<views::View> background_view_ = nullptr; + raw_ptr<MicTestButtonContainer> button_container_ = nullptr; }; } // namespace ash::video_conference
diff --git a/base/compiler_specific.h b/base/compiler_specific.h index 2e2c21f..f484a2f 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h
@@ -59,7 +59,9 @@ // // This body will not be inlined into callers. // } // ``` -#if __has_cpp_attribute(gnu::noinline) +#if __has_cpp_attribute(clang::noinline) +#define NOINLINE [[clang::noinline]] +#elif __has_cpp_attribute(gnu::noinline) #define NOINLINE [[gnu::noinline]] #elif __has_cpp_attribute(msvc::noinline) #define NOINLINE [[msvc::noinline]] @@ -67,6 +69,24 @@ #define NOINLINE #endif +// Annotates a call site indicating that the callee should not be inlined. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#noinline +// +// Usage: +// ``` +// void Func() { +// // This specific call to `DoSomething` should not be inlined. +// NOINLINE_CALL DoSomething(); +// } +// ``` +#if __has_cpp_attribute(clang::noinline) +#define NOINLINE_CALL [[clang::noinline]] +#else +#define NOINLINE_CALL +#endif + // Annotates a function indicating it should not be optimized. // // See also: @@ -102,7 +122,9 @@ // Since `ALWAYS_INLINE` is performance-oriented but can hamper debugging, // ignore it in debug mode. #if defined(NDEBUG) -#if __has_cpp_attribute(gnu::always_inline) +#if __has_cpp_attribute(clang::always_inline) +#define ALWAYS_INLINE [[clang::always_inline]] inline +#elif __has_cpp_attribute(gnu::always_inline) #define ALWAYS_INLINE [[gnu::always_inline]] inline #elif defined(COMPILER_MSVC) #define ALWAYS_INLINE __forceinline @@ -112,6 +134,30 @@ #define ALWAYS_INLINE inline #endif +// Annotates a call site indicating the calee should always be inlined. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#always-inline-force-inline +// +// Usage: +// ``` +// void Func() { +// // This specific call will be inlined if possible. +// ALWAYS_INLINE_CALL DoSomething(); +// } +// ``` +// +// Since `ALWAYS_INLINE_CALL` is performance-oriented but can hamper debugging, +// ignore it in debug mode. +#if defined(NDEBUG) +#if __has_cpp_attribute(clang::always_inline) +#define ALWAYS_INLINE_CALL [[clang::always_inline]] +#endif +#endif +#if !defined(ALWAYS_INLINE_CALL) +#define ALWAYS_INLINE_CALL +#endif + // Annotates a function indicating it should never be tail called. Useful to // make sure callers of the annotated function are never omitted from call // stacks. Often useful with `NOINLINE` to make sure the function itself is also
diff --git a/base/tracing/stdlib/chrome/chrome_scrolls.sql b/base/tracing/stdlib/chrome/chrome_scrolls.sql index 58f1b11..2fd228c0 100644 --- a/base/tracing/stdlib/chrome/chrome_scrolls.sql +++ b/base/tracing/stdlib/chrome/chrome_scrolls.sql
@@ -218,6 +218,16 @@ -- Timestamps generation_ts, touch_move_received_ts, + -- TODO(b:385160424): this is a workaround for cases when + -- generation time is later than the input time. + MAX( + IIF( + is_inertial AND touch_move_received_ts IS NULL, + scroll_update_created_ts, + touch_move_received_ts + ), + generation_ts) + AS browser_main_received_ts, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Ids scroll_update_created_slice_id, @@ -254,16 +264,12 @@ generation_ts, -- Flings don't have a touch move event so make GenerationToBrowserMain span -- all the way to the creation of the gesture scroll update. - IIF( - is_inertial AND touch_move_received_ts IS NULL, - scroll_update_created_ts, - touch_move_received_ts - ) - generation_ts AS generation_to_browser_main_dur, + browser_main_received_ts - generation_ts AS generation_to_browser_main_dur, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- browser_utid, touch_move_received_slice_id, touch_move_received_ts, - scroll_update_created_ts - touch_move_received_ts + scroll_update_created_ts - MAX(touch_move_received_ts, generation_ts) AS touch_move_processing_dur, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- On `browser_utid`. @@ -275,8 +281,11 @@ -- No applicable utid (duration between two threads). -- No applicable slice id (duration between two threads). scroll_update_created_end_ts, - -- TODO(b:380868337): This is sometimes negative; check/fix this. - compositor_dispatch_ts - scroll_update_created_end_ts + -- TODO(b:385161677): use the start + -- of the STEP_SEND_DISPATCH_EVENT_MOJO_MESSAGE step + -- instead of scroll_update_created_end_ts. + MAX(compositor_dispatch_ts, scroll_update_created_end_ts) + - scroll_update_created_end_ts AS browser_to_compositor_delay_dur, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- compositor_utid, @@ -317,6 +326,13 @@ AS compositor_resample_task_ts, compositor_resample_step.ts AS compositor_resample_ts, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + compositor_receive_begin_frame_step.id + AS compositor_receive_begin_frame_slice_id, + compositor_receive_begin_frame_step.task_start_time_ts + AS compositor_receive_begin_frame_task_ts, + compositor_receive_begin_frame_step.ts + AS compositor_receive_begin_frame_ts, + -- compositor_generate_compositor_frame_step.id AS compositor_generate_compositor_frame_slice_id, compositor_generate_compositor_frame_step.task_start_time_ts @@ -375,6 +391,15 @@ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- LEFT JOIN chrome_graphics_pipeline_surface_frame_steps + compositor_receive_begin_frame_step + ON + compositor_receive_begin_frame_step.surface_frame_trace_id + = refs.surface_frame_id + AND compositor_receive_begin_frame_step.step + = 'STEP_RECEIVE_BEGIN_FRAME' +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +LEFT JOIN + chrome_graphics_pipeline_surface_frame_steps compositor_generate_compositor_frame_step ON compositor_generate_compositor_frame_step.surface_frame_trace_id @@ -431,6 +456,9 @@ compositor_resample_slice_id LONG, -- Timestamp for the `STEP_RESAMPLE_SCROLL_EVENTS` slice. compositor_resample_ts TIMESTAMP, + -- Timestamp for the `STEP_RECEIVE_BEGIN_FRAME` slice or the + -- containing task (if available). + compositor_receive_begin_frame_ts TIMESTAMP, -- Slice id for the `STEP_GENERATE_COMPOSITOR_FRAME` slice. compositor_generate_compositor_frame_slice_id LONG, -- Timestamp for the `STEP_GENERATE_COMPOSITOR_FRAME` slice or the @@ -510,6 +538,14 @@ compositor_resample_ts) AS compositor_resample_ts, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Ids + compositor_receive_begin_frame_slice_id, + -- Timestamps + COALESCE( + compositor_receive_begin_frame_task_ts, + compositor_receive_begin_frame_ts) + AS compositor_receive_begin_frame_ts, + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + -- Ids compositor_generate_compositor_frame_slice_id, -- Timestamps COALESCE( @@ -566,6 +602,9 @@ compositor_resample_ts, -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- On `compositor_utid`. + compositor_receive_begin_frame_ts, + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + -- On `compositor_utid`. compositor_generate_compositor_frame_slice_id, -- TODO(b:380868337): This is sometimes unexpectedly null; check/fix this. compositor_generate_compositor_frame_ts, @@ -887,6 +926,8 @@ -- No applicable slice id (duration between two slices). input.compositor_dispatch_end_ts, -- TODO(b:380868337): This is sometimes negative; check/fix this. + -- TODO(b:381273884): use frame.compositor_receive_begin_frame_ts instead of + -- input.compositor_dispatch_end_ts. COALESCE( frame.compositor_resample_ts, input.compositor_coalesced_input_handled_ts
diff --git a/build/config/unsafe_buffers_paths.txt b/build/config/unsafe_buffers_paths.txt index 208a10c..d8e1f94 100644 --- a/build/config/unsafe_buffers_paths.txt +++ b/build/config/unsafe_buffers_paths.txt
@@ -57,7 +57,6 @@ -chrome/chrome_elf/third_party_dlls/ -chrome/elevation_service/internal/ -chromecast/ --clank/ -components/optimization_guide/internal/ -ios/ -ios_internal/
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc index 1469003..70137d4 100644 --- a/cc/paint/oop_pixeltest.cc +++ b/cc/paint/oop_pixeltest.cc
@@ -76,6 +76,11 @@ namespace cc { namespace { + +SkV4 SkColorToSkV4(SkColor4f color) { + return SkV4{color.fR, color.fG, color.fB, color.fA}; +} + scoped_refptr<DisplayItemList> MakeNoopDisplayItemList() { auto display_item_list = base::MakeRefCounted<DisplayItemList>(); display_item_list->StartPaint(); @@ -2915,31 +2920,40 @@ INSTANTIATE_TEST_SUITE_P(P, OopPathPixelTest, ::testing::Bool()); TEST_F(OopPixelTest, SkSLCommandShader) { + // Draws a red square. + const std::string_view kDrawRedRect(R"( + uniform float u_border_alpha; + uniform float2 u_top_left; + uniform float2 u_btm_right; + uniform vec4 u_border_color; + uniform vec4 u_center_color; + + vec4 main(float2 coord) { + if (all(greaterThanEqual(coord, u_top_left)) && + all(lessThan(coord, u_btm_right))) { + return u_center_color; + } else { + return vec4(u_border_color.xyz, u_border_alpha); + } + } + )"); + auto shader = PaintShader::MakeSkSLCommand( + kDrawRedRect, + /*float_uniforms=*/{{.name = SkString("u_border_alpha"), .value = 0.5f}}, + /*float2_uniforms=*/ + {{.name = SkString("u_top_left"), .value = SkV2{25.f, 25.f}}, + {.name = SkString("u_btm_right"), .value = SkV2{75.f, 75.f}}}, + /*float4_uniforms=*/ + {{.name = SkString("u_border_color"), + .value = SkColorToSkV4(SkColors::kRed)}, + {.name = SkString("u_center_color"), + .value = SkColorToSkV4(SkColors::kGreen)}}); + ASSERT_TRUE(shader); + const gfx::Size rect(100, 100); RasterOptions options(rect); options.preclear = true; options.preclear_color = SkColors::kWhite; - - // Draws a red square. - const std::string_view kDrawRedRect(R"( - const half4 color = half4(1.0, 0.0, 0.0, 1.0); // <R, G, B, A> - const vec2 top_left = vec2(25, 25); // <X, Y> - const vec2 btm_right = vec2(75, 75); // <X+W, Y+H> - - half4 main(float2 coord) { - if (all(greaterThanEqual(coord, top_left)) && - all(lessThan(coord, btm_right))) { - // Red if inside the rect. - return color; - } else { - // Transparent elsewhere. - return half4(0.0); - } - } - )"); - auto shader = PaintShader::MakeSkSLCommand(kDrawRedRect); - ASSERT_TRUE(shader); - PaintFlags flags; flags.setShader(std::move(shader)); auto display_item_list = base::MakeRefCounted<DisplayItemList>(); @@ -2955,9 +2969,14 @@ SkCanvas canvas(expected, SkSurfaceProps{}); SkPaint red; red.setColor(SkColors::kRed); - canvas.drawRect(SkRect::MakeXYWH(25, 25, 50, 50), red); + red.setAlphaf(0.5f); + canvas.drawRect(SkRect::MakeXYWH(0, 0, 100, 100), red); + SkPaint green; + green.setColor(SkColors::kGreen); + canvas.drawRect(SkRect::MakeXYWH(25, 25, 50, 50), green); - EXPECT_EQ(GetPNGDataUrl(actual), GetPNGDataUrl(expected)); + EXPECT_TRUE(MatchesBitmap(actual, expected, + ManhattanDistancePixelComparator(/*tolerance=*/5))); } } // namespace
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc index fca1e94..20087d6 100644 --- a/cc/paint/paint_op_buffer_unittest.cc +++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -3545,7 +3545,15 @@ "half4 main(float2 coord) { return half4(0.5); }"; PaintFlags flags; - flags.setShader(PaintShader::MakeSkSLCommand(kCommand)); + std::vector<PaintShader::FloatUniform> scalar_uniforms = { + {.name = SkString("u_scalar"), .value = 42.f}}; + std::vector<PaintShader::Float2Uniform> float2_uniforms = { + {.name = SkString("u_vec2"), .value = SkV2{42.f, 21.f}}}; + std::vector<PaintShader::Float4Uniform> float4_uniforms = { + {.name = SkString("u_vec4"), .value = SkV4{42.f, 21.f, 10.f, 5.f}}}; + flags.setShader(PaintShader::MakeSkSLCommand( + kCommand, std::move(scalar_uniforms), std::move(float2_uniforms), + std::move(float4_uniforms))); PaintOpBuffer buffer; buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 2, 3, 4), flags);
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc index 7a35d45..6d7f17d 100644 --- a/cc/paint/paint_op_reader.cc +++ b/cc/paint/paint_op_reader.cc
@@ -61,6 +61,8 @@ namespace cc { namespace { +static_assert(std::is_same_v<unsigned char, uint8_t>); + bool IsValidPaintShaderType(PaintShader::Type type) { return static_cast<uint8_t>(type) < static_cast<uint8_t>(PaintShader::Type::kShaderCount); @@ -73,6 +75,34 @@ } // namespace +// Being a friend to `PaintOpReader`, this cannot be in the anonymous namespace. +template <typename ValueType> +void ReadSimpleValueUniformsHelper( + PaintOpReader& reader, + std::vector<PaintShader::Uniform<ValueType>>* output_uniforms) { + size_t count = 0u; + reader.ReadSize(&count); + if (count == 0) { + return; + } + output_uniforms->reserve(count); + for (size_t i = 0; i < count; ++i) { + SkString name; + reader.Read(&name); + if (!reader.valid()) { + return; + } + CHECK(!name.isEmpty()); + ValueType value; + reader.ReadSimple(&value); + if (!reader.valid()) { + return; + } + output_uniforms->push_back( + {.name = std::move(name), .value = std::move(value)}); + } +} + PaintOpReader::PaintOpReader(const volatile void* memory, size_t size, const PaintOp::DeserializeOptions& options, @@ -746,6 +776,9 @@ ReadVectorContent(positions_size, ref.positions_); Read(&ref.sksl_command_); + Read(&ref.scalar_uniforms_); + Read(&ref.float2_uniforms_); + Read(&ref.float4_uniforms_); // We don't write the cached shader, so don't attempt to read it either. @@ -919,7 +952,6 @@ } void PaintOpReader::Read(SkString* sk_string) { - static_assert(std::is_same_v<unsigned char, uint8_t>); size_t size = 0; // We always serialize the empty string's size (0u). ReadSize(&size); @@ -934,6 +966,18 @@ DidRead(size); } +void PaintOpReader::Read(std::vector<PaintShader::FloatUniform>* uniforms) { + ReadSimpleValueUniformsHelper<SkScalar>(*this, uniforms); +} + +void PaintOpReader::Read(std::vector<PaintShader::Float2Uniform>* uniforms) { + ReadSimpleValueUniformsHelper<SkV2>(*this, uniforms); +} + +void PaintOpReader::Read(std::vector<PaintShader::Float4Uniform>* uniforms) { + ReadSimpleValueUniformsHelper<SkV4>(*this, uniforms); +} + void PaintOpReader::AlignMemory(size_t alignment) { DCHECK_GE(alignment, PaintOpWriter::kDefaultAlignment); DCHECK_LE(alignment, BufferAlignment());
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h index 9c299762..4c66ac1 100644 --- a/cc/paint/paint_op_reader.h +++ b/cc/paint/paint_op_reader.h
@@ -109,6 +109,9 @@ void Read(SkGradientShader::Interpolation* interpolation); void Read(scoped_refptr<SkottieWrapper>* skottie); void Read(SkString* sk_string); + void Read(std::vector<PaintShader::FloatUniform>* uniforms); + void Read(std::vector<PaintShader::Float2Uniform>* uniforms); + void Read(std::vector<PaintShader::Float4Uniform>* uniforms); void Read(SkClipOp* op) { ReadEnum<SkClipOp, SkClipOp::kMax_EnumValue>(op); } void Read(PaintCanvas::AnnotationType* type) { @@ -182,6 +185,11 @@ } private: + template <typename ValueType> + friend void ReadSimpleValueUniformsHelper( + PaintOpReader&, + std::vector<PaintShader::Uniform<ValueType>>*); + enum class DeserializationError { // Enum values must remain synchronized with PaintOpDeserializationError // in tools/metrics/histograms/metadata/gpu/enums.xml.
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc index 4263f1f..b9b7bc0 100644 --- a/cc/paint/paint_op_writer.cc +++ b/cc/paint/paint_op_writer.cc
@@ -55,6 +55,8 @@ namespace cc { namespace { +static_assert(std::is_same_v<unsigned char, uint8_t>); + SkIRect MakeSrcRect(const PaintImage& image) { if (!image) { return SkIRect::MakeEmpty(); @@ -67,8 +69,38 @@ static_cast<uint32_t*>(memory)[0] = type | serialized_size << 8; } +template <typename ValueType> +size_t CountNonEmptyUniforms( + const std::vector<PaintShader::Uniform<ValueType>>& uniforms) { + return std::count_if(uniforms.begin(), uniforms.end(), + [](const PaintShader::Uniform<ValueType>& u) { + return !u.name.isEmpty(); + }); +} + } // namespace +// Friend to `PaintOpWriter`. Can't be nested in the anonymous namespace. +template <typename ValueType> +size_t SerializeSizeSimpleValueUniforms( + const std::vector<PaintShader::Uniform<ValueType>>& uniforms) { + const size_t count = uniforms.size(); + size_t name_sizes = 0u; + for (const auto& [name, value] : uniforms) { + if (name.isEmpty()) { + continue; + } + name_sizes += PaintOpWriter::SerializedSize(name); + } + // [ size_t [ [size_t data] data [size_t data] data ] ] + // 2u key0 val0 key1 val1 + return (PaintOpWriter::SerializedSize<size_t>() + + base::CheckedNumeric<size_t>(name_sizes) + + base::CheckedNumeric<size_t>(count) * + PaintOpWriter::SerializedSizeSimple<ValueType>()) + .ValueOrDie(); +} + // static size_t PaintOpWriter::SerializedSize(const PaintImage& image) { // Image Serialization type. @@ -130,6 +162,24 @@ return SerializedSizeOfBytes(sk_string.size()); } +// static: +size_t PaintOpWriter::SerializedSize( + const std::vector<PaintShader::FloatUniform>& uniforms) { + return SerializeSizeSimpleValueUniforms<SkScalar>(uniforms); +} + +// static: +size_t PaintOpWriter::SerializedSize( + const std::vector<PaintShader::Float2Uniform>& uniforms) { + return SerializeSizeSimpleValueUniforms<SkV2>(uniforms); +} + +// static: +size_t PaintOpWriter::SerializedSize( + const std::vector<PaintShader::Float4Uniform>& uniforms) { + return SerializeSizeSimpleValueUniforms<SkV4>(uniforms); +} + // static size_t PaintOpWriter::SerializedSize(const ColorFilter* filter) { if (!filter) { @@ -497,13 +547,51 @@ } void PaintOpWriter::Write(const SkString& sk_string) { - static_assert(std::is_same_v<unsigned char, uint8_t>); size_t num_bytes = sk_string.size(); WriteSize(num_bytes); WriteData(base::span<const uint8_t>( reinterpret_cast<const uint8_t*>(sk_string.data()), num_bytes)); } +void PaintOpWriter::Write( + const std::vector<PaintShader::FloatUniform>& uniforms) { + const size_t count = CountNonEmptyUniforms(uniforms); + WriteSize(count); + for (const auto& [name, value] : uniforms) { + if (name.isEmpty()) { + continue; + } + Write(name); + WriteSimple(value); + } +} + +void PaintOpWriter::Write( + const std::vector<PaintShader::Float2Uniform>& uniforms) { + const size_t count = CountNonEmptyUniforms(uniforms); + WriteSize(count); + for (const auto& [name, value] : uniforms) { + if (name.isEmpty()) { + continue; + } + Write(name); + WriteSimpleMultiple(value.x, value.y); + } +} + +void PaintOpWriter::Write( + const std::vector<PaintShader::Float4Uniform>& uniforms) { + const size_t count = CountNonEmptyUniforms(uniforms); + WriteSize(count); + for (const auto& [name, value] : uniforms) { + if (name.isEmpty()) { + continue; + } + Write(name); + WriteSimpleMultiple(value.x, value.y, value.z, value.w); + } +} + void PaintOpWriter::Write(const SkGainmapInfo& gainmap_info) { Write(gainmap_info.fGainmapRatioMin); Write(gainmap_info.fGainmapRatioMax); @@ -684,6 +772,9 @@ // using other fields. Write(shader->sksl_command_); + Write(shader->scalar_uniforms_); + Write(shader->float2_uniforms_); + Write(shader->float4_uniforms_); } void PaintOpWriter::Write(SkYUVColorSpace yuv_color_space) {
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h index 269307f..fefec18a 100644 --- a/cc/paint/paint_op_writer.h +++ b/cc/paint/paint_op_writer.h
@@ -140,6 +140,10 @@ static constexpr size_t kDefaultAlignment = alignof(uint32_t); private: + template <typename ValueType> + friend size_t SerializeSizeSimpleValueUniforms( + const std::vector<PaintShader::Uniform<ValueType>>&); + template <typename T> static constexpr size_t SerializedSizeSimple(); @@ -159,6 +163,12 @@ static size_t SerializedSize(const PaintRecord& record); static size_t SerializedSize(const SkHighContrastConfig& config); static size_t SerializedSize(const SkString& sk_string); + static size_t SerializedSize( + const std::vector<PaintShader::FloatUniform>& scalar_map); + static size_t SerializedSize( + const std::vector<PaintShader::Float2Uniform>& float2_map); + static size_t SerializedSize( + const std::vector<PaintShader::Float4Uniform>& float4_map); // Serialization of raw/smart pointers is not supported by default. template <typename T> @@ -277,6 +287,9 @@ void Write(const PathEffect* effect); void Write(const gfx::HDRMetadata& hdr_metadata); void Write(const SkString& sk_string); + void Write(const std::vector<PaintShader::FloatUniform>& scalar_uniforms); + void Write(const std::vector<PaintShader::Float2Uniform>& float2_uniforms); + void Write(const std::vector<PaintShader::Float4Uniform>& float4_uniforms); void Write(SkClipOp op) { WriteEnum(op); } void Write(PaintCanvas::AnnotationType type) { WriteEnum(type); }
diff --git a/cc/paint/paint_op_writer_reader_unittest.cc b/cc/paint/paint_op_writer_reader_unittest.cc index c1734fba6..16de65f 100644 --- a/cc/paint/paint_op_writer_reader_unittest.cc +++ b/cc/paint/paint_op_writer_reader_unittest.cc
@@ -6,7 +6,6 @@ #include "cc/paint/paint_op_reader.h" #include "cc/paint/paint_op_writer.h" - #include "cc/test/test_options_provider.h" #include "testing/gtest/include/gtest/gtest.h" @@ -121,4 +120,97 @@ EXPECT_EQ(deseralized, original); } +namespace { +struct UniformTestCase { + std::vector<PaintShader::FloatUniform> scalars; + std::vector<PaintShader::Float2Uniform> float2s; + std::vector<PaintShader::Float4Uniform> float4s; + size_t expected_size; +}; + +using PaintOpWriterReaderUniformTest = testing::TestWithParam<UniformTestCase>; +} // namespace + +TEST_P(PaintOpWriterReaderUniformTest, Uniforms) { + char buffer[128]; + TestOptionsProvider options_provider; + memset(buffer, 0xa5, std::size(buffer)); + PaintOpWriter writer(buffer, std::size(buffer), + options_provider.serialize_options(), + /*enable_security_constraints=*/true); + const auto& scalars = GetParam().scalars; + const auto& float2s = GetParam().float2s; + const auto& float4s = GetParam().float4s; + if (!scalars.empty()) { + writer.Write(scalars); + } else if (!float2s.empty()) { + writer.Write(float2s); + } else if (!float4s.empty()) { + writer.Write(float4s); + } else { + ASSERT_TRUE(false); + } + + EXPECT_EQ(writer.size(), GetParam().expected_size); + + PaintOpReader reader(buffer, writer.size(), + options_provider.deserialize_options(), + /*enable_security_constraints=*/true); + + if (!scalars.empty()) { + std::vector<PaintShader::FloatUniform> deseralized; + reader.Read(&deseralized); + EXPECT_THAT(deseralized, ::testing::UnorderedElementsAreArray(scalars)); + } else if (!float2s.empty()) { + std::vector<PaintShader::Float2Uniform> deseralized; + reader.Read(&deseralized); + EXPECT_THAT(deseralized, ::testing::UnorderedElementsAreArray(float2s)); + } else if (!float4s.empty()) { + std::vector<PaintShader::Float4Uniform> deseralized; + reader.Read(&deseralized); + EXPECT_THAT(deseralized, ::testing::UnorderedElementsAreArray(float4s)); + } else { + ASSERT_TRUE(false); + } +} + +INSTANTIATE_TEST_SUITE_P( + /*no prefix*/, + PaintOpWriterReaderUniformTest, + testing::ValuesIn<UniformTestCase>( + {UniformTestCase{ + .scalars = {{.name = SkString("var1"), .value = 1.f}, + {.name = SkString("variable2"), .value = 2.f}}, + // count = 8, + // "var1" = 8+4, 1.f = 4, + // "variable2" = 8+12 (9 aligned up to 12), 2.f = 4 + .expected_size = 48u}, + UniformTestCase{ + .float2s = {{.name = SkString("var1"), .value = SkV2{1.f, 2.f}}, + {.name = SkString("variable2"), + .value = SkV2{3.f, 4.f}}}, + // count = 8, + // "var1" = 8+4, value = 8, + // "variable2" = 8+12 (9 aligned up to 12), value = 8 + .expected_size = 56u}, + UniformTestCase{.float4s = {{.name = SkString("var1"), + .value = SkV4{1.f, 2.f, 3.f, 4.f}}, + {.name = SkString("variable2"), + .value = SkV4{5.f, 6.f, 7.f, 8.f}}}, + // count = 8, + // "var1" = 8+4, value = 16, + // "variable2" = 8+12 (9 aligned up to 12), value = 16 + .expected_size = 72u}}), + [](const ::testing::TestParamInfo<UniformTestCase> info) { + if (!info.param.scalars.empty()) { + return "Scalar"; + } else if (!info.param.float2s.empty()) { + return "SkV2"; + } else if (!info.param.float4s.empty()) { + return "SkV4"; + } else { + NOTREACHED(); + } + }); + } // namespace cc
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc index fbef187..938aeed 100644 --- a/cc/paint/paint_shader.cc +++ b/cc/paint/paint_shader.cc
@@ -228,7 +228,11 @@ } // static: -sk_sp<PaintShader> PaintShader::MakeSkSLCommand(std::string_view sksl) { +sk_sp<PaintShader> PaintShader::MakeSkSLCommand( + std::string_view sksl, + std::vector<FloatUniform> float_uniforms, + std::vector<Float2Uniform> float2_uniforms, + std::vector<Float4Uniform> float4_uniforms) { SkString cmd(sksl); auto [effect, error] = SkRuntimeEffect::MakeForShader(cmd); if (!effect) { @@ -236,7 +240,10 @@ return nullptr; } sk_sp<PaintShader> shader(new PaintShader(Type::kSkSLCommand)); - shader->sksl_command_ = cmd; + shader->sksl_command_ = std::move(cmd); + shader->scalar_uniforms_ = std::move(float_uniforms); + shader->float2_uniforms_ = std::move(float2_uniforms); + shader->float4_uniforms_ = std::move(float4_uniforms); return shader; } @@ -270,7 +277,10 @@ shader->colors_.size()) + PaintOpWriter::SerializedSizeOfElements(shader->positions_.data(), shader->positions_.size()) + - PaintOpWriter::SerializedSize(shader->sksl_command_)) + PaintOpWriter::SerializedSize(shader->sksl_command_) + + PaintOpWriter::SerializedSize(shader->scalar_uniforms_) + + PaintOpWriter::SerializedSize(shader->float2_uniforms_) + + PaintOpWriter::SerializedSize(shader->float4_uniforms_)) .ValueOrDie(); } @@ -528,7 +538,17 @@ // Fallback the the color shader. break; } - return effect->makeShader(/*uniforms=*/nullptr, /*children=*/{}); + SkRuntimeShaderBuilder builder(effect); + for (const auto& [name, value] : scalar_uniforms_) { + builder.uniform(name.c_str()) = value; + } + for (const auto& [name, value] : float2_uniforms_) { + builder.uniform(name.c_str()) = value; + } + for (const auto& [name, value] : float4_uniforms_) { + builder.uniform(name.c_str()) = value; + } + return builder.makeShader(); } case Type::kShaderCount: NOTREACHED();
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h index 272d26e..02ffcad 100644 --- a/cc/paint/paint_shader.h +++ b/cc/paint/paint_shader.h
@@ -126,9 +126,27 @@ // Returns null if the `sksl` command is invalid. // - // *NOTE*: This is only intended for trusted shader (e.g., shaders that are - // part of the Chromium binary). - static sk_sp<PaintShader> MakeSkSLCommand(std::string_view sksl); + // NOTE: + // - This is only intended for trusted shader (e.g., shaders that are part of + // the Chromium binary). + // - Not using flat_map because SkString does not have built-in comparator. + template <typename ValueType> + struct Uniform { + SkString name; + ValueType value; + + bool operator==(const Uniform& other) const { + return name == other.name && value == other.value; + } + }; + using FloatUniform = Uniform<SkScalar>; + using Float2Uniform = Uniform<SkV2>; + using Float4Uniform = Uniform<SkV4>; + static sk_sp<PaintShader> MakeSkSLCommand( + std::string_view sksl, + std::vector<FloatUniform> float_uniforms, + std::vector<Float2Uniform> float2_uniforms, + std::vector<Float4Uniform> float4_uniforms); static size_t GetSerializedSize(const PaintShader* shader); @@ -301,9 +319,13 @@ // // TODO(https://crbug.com/384532231): Consider cashing the Skia shader for // performance. - // - // TODO(https://crbug.com/384075578): Add support to shader uniforms. SkString sksl_command_; + + // Uniforms for `sksl_command_`. The keys of the map are the variable name of + // the uniform. + std::vector<FloatUniform> scalar_uniforms_; + std::vector<Float2Uniform> float2_uniforms_; + std::vector<Float4Uniform> float4_uniforms_; }; } // namespace cc
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java index 5683cda..dc5de93 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -965,7 +965,7 @@ /* didCloseCallback= */ null); } else if (menuId == R.id.delete_shared_group) { RecordUserAction.record("TabGridDialogMenu.DeleteShared"); - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( mActivity, mCurrentTabGroupModelFilterSupplier.get(), mActionConfirmationManager, @@ -973,7 +973,7 @@ tabId); } else if (menuId == R.id.leave_group) { RecordUserAction.record("TabGridDialogMenu.LeaveShared"); - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( mActivity, mCurrentTabGroupModelFilterSupplier.get(), mActionConfirmationManager,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java index b767663..c1d42652 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
@@ -653,8 +653,7 @@ .onResult(ActionConfirmationResult.CONFIRMATION_POSITIVE); verify(mDataSharingService) - .removeMember( - eq(COLLABORATION_ID1), eq(EMAIL2), mActionOutcomeCallbackCaptor.capture()); + .leaveGroup(eq(COLLABORATION_ID1), mActionOutcomeCallbackCaptor.capture()); mActionOutcomeCallbackCaptor .getValue() .onResult(PeopleGroupActionOutcome.TRANSIENT_FAILURE);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java index a7690d6..9a9efa8 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java
@@ -18,7 +18,6 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.LazyOneshotSupplier; -import org.chromium.chrome.R; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesColor; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesType; @@ -33,7 +32,6 @@ import org.chromium.components.data_sharing.DataSharingService; import org.chromium.components.data_sharing.DataSharingService.GroupDataOrFailureOutcome; import org.chromium.components.data_sharing.GroupData; -import org.chromium.components.data_sharing.PeopleGroupActionOutcome; import org.chromium.components.data_sharing.member_role.MemberRole; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.base.GaiaId; @@ -42,7 +40,6 @@ import org.chromium.components.tab_group_sync.TabGroupSyncService; import org.chromium.components.tab_group_sync.TabGroupUiActionHandler; import org.chromium.ui.modaldialog.ModalDialogManager; -import org.chromium.ui.modaldialog.ModalDialogUtils; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.url.GURL; @@ -170,22 +167,18 @@ GaiaId gaiaId = mCoreAccountInfoSupplier.get().getGaiaId(); @MemberRole int memberRole = TabShareUtils.getSelfMemberRole(groupData, gaiaId); + String groupTitle = groupData.displayName; + String collaborationId = groupData.groupToken.collaborationId; if (memberRole == MemberRole.OWNER) { mPropertyModel.set( - DELETE_RUNNABLE, - () -> - processDeleteSharedGroup( - groupData.displayName, groupData.groupToken.collaborationId)); + DELETE_RUNNABLE, () -> processDeleteSharedGroup(groupTitle, collaborationId)); mPropertyModel.set(LEAVE_RUNNABLE, null); } else { // TODO(crbug.com/365852281): Leave action should look like a delete if there are no // other users. mPropertyModel.set(DELETE_RUNNABLE, null); mPropertyModel.set( - LEAVE_RUNNABLE, - () -> - processLeaveGroup( - groupData.displayName, groupData.groupToken.collaborationId)); + LEAVE_RUNNABLE, () -> processLeaveGroup(groupTitle, collaborationId)); } if (sharedState == GroupSharedState.COLLABORATION_ONLY) { @@ -268,45 +261,40 @@ } } - private void processDeleteSharedGroup(String groupTitle, String groupId) { + private void processDeleteSharedGroup(String groupTitle, String collaborationId) { // TODO(crbug.com/365852281): Confirmation should look like a non-shared delete if there are // no other users. mActionConfirmationManager.processDeleteSharedGroupAttempt( groupTitle, (@ActionConfirmationResult Integer result) -> { if (result != ActionConfirmationResult.CONFIRMATION_NEGATIVE) { - mDataSharingService.deleteGroup(groupId, this::onLeaveOrDeleteGroup); + TabUiUtils.exitCollaborationWithoutWarning( + mContext, + mModalDialogManager, + mDataSharingService, + collaborationId, + MemberRole.OWNER); } }); } - private void processLeaveGroup(String groupTitle, String groupId) { + private void processLeaveGroup(String groupTitle, String collaborationId) { // TODO(crbug.com/365852281): Confirmation should look like a non-shared delete if there are // no other users. mActionConfirmationManager.processLeaveGroupAttempt( groupTitle, (@ActionConfirmationResult Integer result) -> { if (result != ActionConfirmationResult.CONFIRMATION_NEGATIVE) { - String memberEmail = mCoreAccountInfoSupplier.get().getEmail(); - mDataSharingService.removeMember( - groupId, memberEmail, this::onLeaveOrDeleteGroup); + TabUiUtils.exitCollaborationWithoutWarning( + mContext, + mModalDialogManager, + mDataSharingService, + collaborationId, + MemberRole.MEMBER); } }); } - private void onLeaveOrDeleteGroup(@PeopleGroupActionOutcome int outcome) { - if (outcome == PeopleGroupActionOutcome.SUCCESS) { - // TODO(crbug.com/345854578): Do we need to actively remove things from the UI? - } else { - ModalDialogUtils.showOneButtonConfirmation( - mModalDialogManager, - mContext.getResources(), - R.string.data_sharing_generic_failure_title, - R.string.data_sharing_generic_failure_description, - R.string.data_sharing_invitation_failure_button); - } - } - private void deleteGroup(boolean allowDialog) { @GroupWindowState int state = mFetchGroupState.get(); if (state == GroupWindowState.IN_ANOTHER) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java index 0960543..f4546806 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -2587,7 +2587,7 @@ TabUiUtils.ungroupTabGroup(mCurrentTabGroupModelFilterSupplier.get(), tabId); } else if (menuId == R.id.delete_shared_group) { RecordUserAction.record("TabGroupItemMenu.DeleteShared"); - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( mActivity, mCurrentTabGroupModelFilterSupplier.get(), mActionConfirmationManager, @@ -2595,7 +2595,7 @@ tabId); } else if (menuId == R.id.leave_group) { RecordUserAction.record("TabGroupItemMenu.LeaveShared"); - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( mActivity, mCurrentTabGroupModelFilterSupplier.get(), mActionConfirmationManager,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java index 40bb4c5..9be03f3 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
@@ -16,6 +16,7 @@ import org.chromium.base.Callback; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator; @@ -31,11 +32,13 @@ import org.chromium.chrome.browser.tabmodel.TabList; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelActionListener; -import org.chromium.chrome.browser.tabmodel.TabModelActionListener.DialogType; import org.chromium.chrome.tab_ui.R; import org.chromium.components.browser_ui.widget.ActionConfirmationResult; +import org.chromium.components.collaboration.CollaborationService; import org.chromium.components.data_sharing.DataSharingService; +import org.chromium.components.data_sharing.GroupData; import org.chromium.components.data_sharing.PeopleGroupActionOutcome; +import org.chromium.components.data_sharing.member_role.MemberRole; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.identitymanager.IdentityManager; @@ -159,46 +162,7 @@ } /** - * Deletes a shared tab group, prompting to user to verify first. - * - * @param context Used to load resources. - * @param filter Used to pull dependencies from. - * @param actionConfirmationManager Used to show a confirmation dialog. - * @param modalDialogManager Used to show error dialogs. - * @param tabId The local id of the tab being deleted. - */ - public static void deleteSharedTabGroup( - Context context, - TabGroupModelFilter filter, - ActionConfirmationManager actionConfirmationManager, - ModalDialogManager modalDialogManager, - int tabId) { - assert ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING); - TabModel tabModel = filter.getTabModel(); - Profile profile = tabModel.getProfile(); - TabGroupSyncService tabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(profile); - DataSharingService dataSharingService = DataSharingServiceFactory.getForProfile(profile); - - @Nullable - SavedTabGroup savedTabGroup = - TabGroupSyncUtils.getSavedTabGroupFromTabId(tabId, tabModel, tabGroupSyncService); - if (savedTabGroup == null || TextUtils.isEmpty(savedTabGroup.collaborationId)) return; - - assert actionConfirmationManager != null; - - actionConfirmationManager.processDeleteSharedGroupAttempt( - savedTabGroup.title, - (@ActionConfirmationResult Integer result) -> { - if (result != ActionConfirmationResult.CONFIRMATION_NEGATIVE) { - dataSharingService.deleteGroup( - savedTabGroup.collaborationId, - bindOnLeaveOrDeleteGroup(context, modalDialogManager)); - } - }); - } - - /** - * Leaves a shared tab group, prompting to user to verify first. + * Leave or deletes a shared tab group, prompting to user to verify first. * * @param context Used to load resources. * @param filter Used to pull dependencies from. @@ -206,40 +170,90 @@ * @param modalDialogManager Used to show error dialogs. * @param tabId The local id of the tab being left. */ - public static void leaveTabGroup( + public static void exitSharedTabGroupWithDialog( Context context, TabGroupModelFilter filter, ActionConfirmationManager actionConfirmationManager, ModalDialogManager modalDialogManager, int tabId) { assert ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING); + assert actionConfirmationManager != null; + TabModel tabModel = filter.getTabModel(); Profile profile = tabModel.getProfile(); TabGroupSyncService tabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(profile); DataSharingService dataSharingService = DataSharingServiceFactory.getForProfile(profile); IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(profile); + CollaborationService collaborationService = + CollaborationServiceFactory.getForProfile(profile); @Nullable SavedTabGroup savedTabGroup = TabGroupSyncUtils.getSavedTabGroupFromTabId(tabId, tabModel, tabGroupSyncService); - if (savedTabGroup == null || TextUtils.isEmpty(savedTabGroup.collaborationId)) return; @Nullable CoreAccountInfo account = identityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN); - if (account == null) return; + if (savedTabGroup == null + || TextUtils.isEmpty(savedTabGroup.collaborationId) + || account == null) { + showGenericErrorDialog(context, modalDialogManager); + return; + } - assert actionConfirmationManager != null; + String collaborationId = savedTabGroup.collaborationId; + @Nullable GroupData shareGroup = collaborationService.getGroupData(collaborationId); + if (shareGroup == null) { + showGenericErrorDialog(context, modalDialogManager); + return; + } - actionConfirmationManager.processLeaveGroupAttempt( - savedTabGroup.title, + @MemberRole + int memberRole = TabShareUtils.getSelfMemberRole(shareGroup, account.getGaiaId()); + Callback<Integer> onActionConfirmation = (@ActionConfirmationResult Integer result) -> { if (result != ActionConfirmationResult.CONFIRMATION_NEGATIVE) { - dataSharingService.removeMember( - savedTabGroup.collaborationId, - account.getEmail(), - bindOnLeaveOrDeleteGroup(context, modalDialogManager)); + exitCollaborationWithoutWarning( + context, + modalDialogManager, + dataSharingService, + collaborationId, + memberRole); } - }); + }; + if (memberRole == MemberRole.OWNER) { + actionConfirmationManager.processDeleteSharedGroupAttempt( + savedTabGroup.title, onActionConfirmation); + } else if (memberRole == MemberRole.MEMBER) { + actionConfirmationManager.processLeaveGroupAttempt( + savedTabGroup.title, onActionConfirmation); + } else { + showGenericErrorDialog(context, modalDialogManager); + } + } + + /** + * Leaves or deletes a given collaboration. + * + * @param context Used to load resources. + * @param modalDialogManager Used to show error dialogs. + * @param dataSharingService Called to do the actual leave or delete action. + * @param collaborationId Used to identify the collaboration. + * @param memberRole Used to decide which way to exit the group. + */ + public static void exitCollaborationWithoutWarning( + Context context, + ModalDialogManager modalDialogManager, + DataSharingService dataSharingService, + String collaborationId, + @MemberRole int memberRole) { + Callback<Integer> callback = bindOnLeaveOrDeleteGroup(context, modalDialogManager); + if (memberRole == MemberRole.OWNER) { + dataSharingService.deleteGroup(collaborationId, callback); + } else if (memberRole == MemberRole.MEMBER) { + dataSharingService.leaveGroup(collaborationId, callback); + } else { + showGenericErrorDialog(context, modalDialogManager); + } } /** @@ -293,20 +307,29 @@ private static Callback<Integer> bindOnLeaveOrDeleteGroup( Context context, ModalDialogManager modalDialogManager) { return (@PeopleGroupActionOutcome Integer outcome) -> { - if (outcome == PeopleGroupActionOutcome.SUCCESS) { - // TODO(crbug.com/345854578): Do we need to actively remove things from the UI? - } else { - ModalDialogUtils.showOneButtonConfirmation( - modalDialogManager, - context.getResources(), - R.string.data_sharing_generic_failure_title, - R.string.data_sharing_generic_failure_description, - R.string.data_sharing_invitation_failure_button); + if (outcome != PeopleGroupActionOutcome.SUCCESS) { + showGenericErrorDialog(context, modalDialogManager); } }; } /** + * Shows a generic error when a data sharing action fails. + * + * @param context Used to load resources. + * @param modalDialogManager Used to show the dialog. + */ + public static void showGenericErrorDialog( + Context context, ModalDialogManager modalDialogManager) { + ModalDialogUtils.showOneButtonConfirmation( + modalDialogManager, + context.getResources(), + R.string.data_sharing_generic_failure_title, + R.string.data_sharing_generic_failure_description, + R.string.data_sharing_invitation_failure_button); + } + + /** * Mark the tab switcher view as sensitive if at least one of the tabs in {@param tabList} has * sensitive content. Note that if all sensitive tabs are removed from the tab switcher, the tab * switcher will have to be closed and opened again to become not sensitive.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java index 760f06d..dbb2b99 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
@@ -12,6 +12,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.COLLABORATION_ID1; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.EMAIL1; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.EMAIL2; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.GAIA_ID1; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.GAIA_ID2; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER1; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER2; +import static org.chromium.components.tab_group_sync.SyncedGroupTestHelper.SYNC_GROUP_ID1; import static org.chromium.ui.test.util.MockitoHelper.runWithValue; import androidx.test.core.app.ApplicationProvider; @@ -31,6 +39,7 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.base.test.util.HistogramWatcher; +import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.profiles.Profile; @@ -44,13 +53,18 @@ import org.chromium.chrome.browser.tabmodel.TabModelActionListener.DialogType; import org.chromium.chrome.browser.tabmodel.TabRemover; import org.chromium.components.browser_ui.widget.ActionConfirmationResult; +import org.chromium.components.collaboration.CollaborationService; import org.chromium.components.data_sharing.DataSharingService; +import org.chromium.components.data_sharing.GroupData; +import org.chromium.components.data_sharing.GroupMember; import org.chromium.components.data_sharing.PeopleGroupActionOutcome; +import org.chromium.components.data_sharing.SharedGroupTestHelper; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.base.GaiaId; import org.chromium.components.signin.identitymanager.IdentityManager; import org.chromium.components.tab_group_sync.LocalTabGroupId; import org.chromium.components.tab_group_sync.SavedTabGroup; +import org.chromium.components.tab_group_sync.SyncedGroupTestHelper; import org.chromium.components.tab_group_sync.TabGroupSyncService; import org.chromium.ui.modaldialog.ModalDialogManager; @@ -63,10 +77,7 @@ private static final int TAB_ID = 123; private static final int ROOT_ID = TAB_ID; private static final String GROUP_TITLE = "My Group"; - private static final String COLLABORATION_ID1 = "A"; - private static final GaiaId GAIA_ID = new GaiaId("Z"); - private static final String EMAIL = "fake@gmail.com"; - private static final Token TAB_GROUP_TOKEN = Token.createRandom(); + private static final Token TAB_GROUP_ID = new Token(1L, 2L); @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -81,6 +92,7 @@ @Mock private IdentityManager mIdentityManager; @Mock private TabGroupSyncService mTabGroupSyncService; @Mock private DataSharingService mDataSharingService; + @Mock private CollaborationService mCollaborationService; @Mock private Callback<Boolean> mDidCloseTabsCallback; @Mock private Callback<Boolean> mContentSensitivitySetter; @@ -88,10 +100,13 @@ @Captor private ArgumentCaptor<Callback<Integer>> mOutcomeCaptor; private List<Tab> mTabsToClose; + private SyncedGroupTestHelper mSyncedGroupTestHelper; @Before public void setUp() { mTabsToClose = List.of(mTab); + mSyncedGroupTestHelper = new SyncedGroupTestHelper(mTabGroupSyncService); + when(mTabModel.getTabRemover()).thenReturn(mTabRemover); when(mFilter.getTabModel()).thenReturn(mTabModel); when(mFilter.isIncognitoBranded()).thenReturn(false); @@ -101,12 +116,13 @@ when(mTabModel.getTabById(TAB_ID)).thenReturn(mTab); when(mTab.isClosing()).thenReturn(false); when(mTab.getId()).thenReturn(TAB_ID); - when(mTab.getTabGroupId()).thenReturn(TAB_GROUP_TOKEN); + when(mTab.getTabGroupId()).thenReturn(TAB_GROUP_ID); when(mTabModel.getProfile()).thenReturn(mProfile); IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider); when(mIdentityServicesProvider.getIdentityManager(any())).thenReturn(mIdentityManager); TabGroupSyncServiceFactory.setForTesting(mTabGroupSyncService); DataSharingServiceFactory.setForTesting(mDataSharingService); + CollaborationServiceFactory.setForTesting(mCollaborationService); } @Test @@ -182,13 +198,11 @@ runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processDeleteSharedGroupAttempt(any(), any()); + mockIdentity(EMAIL1, GAIA_ID1); + createSyncGroup(COLLABORATION_ID1); + createSharedGroup(GROUP_MEMBER1, GROUP_MEMBER2); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -206,13 +220,11 @@ runWithValue(1, ActionConfirmationResult.CONFIRMATION_NEGATIVE) .when(mActionConfirmationManager) .processDeleteSharedGroupAttempt(any(), any()); + mockIdentity(EMAIL1, GAIA_ID1); + createSyncGroup(COLLABORATION_ID1); + createSharedGroup(GROUP_MEMBER1, GROUP_MEMBER2); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -229,12 +241,9 @@ .processDeleteSharedGroupAttempt(any(), any()); when(mTabModel.getTabById(anyInt())).thenReturn(null); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); + createSyncGroup(COLLABORATION_ID1); - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -248,14 +257,10 @@ runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processDeleteSharedGroupAttempt(any(), any()); - when(mTab.getTabGroupId()).thenReturn(null); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); + createSyncGroup(COLLABORATION_ID1); - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -270,7 +275,7 @@ .when(mActionConfirmationManager) .processDeleteSharedGroupAttempt(any(), any()); - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -284,13 +289,9 @@ runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processDeleteSharedGroupAttempt(any(), any()); + createSyncGroup(/* collaborationId= */ null); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = null; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -300,46 +301,37 @@ } @Test - public void testLeaveTabGroup_Positive() { + public void testLeaveSharedTabGroup_Positive() { runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processLeaveGroupAttempt(any(), any()); + mockIdentity(EMAIL2, GAIA_ID2); + createSyncGroup(COLLABORATION_ID1); + createSharedGroup(GROUP_MEMBER1, GROUP_MEMBER2); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - CoreAccountInfo coreAccountInfo = CoreAccountInfo.createFromEmailAndGaiaId(EMAIL, GAIA_ID); - when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); - - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, mModalDialogManager, TAB_ID); verify(mActionConfirmationManager).processLeaveGroupAttempt(eq(GROUP_TITLE), any()); - verify(mDataSharingService) - .removeMember(eq(COLLABORATION_ID1), eq(EMAIL), mOutcomeCaptor.capture()); + verify(mDataSharingService).leaveGroup(eq(COLLABORATION_ID1), mOutcomeCaptor.capture()); mOutcomeCaptor.getValue().onResult(PeopleGroupActionOutcome.TRANSIENT_FAILURE); verify(mModalDialogManager).showDialog(any(), anyInt()); } @Test - public void testLeaveTabGroup_Negative() { + public void testLeaveSharedTabGroup_Negative() { runWithValue(1, ActionConfirmationResult.CONFIRMATION_NEGATIVE) .when(mActionConfirmationManager) .processLeaveGroupAttempt(any(), any()); + mockIdentity(EMAIL2, GAIA_ID2); + createSyncGroup(COLLABORATION_ID1); + createSharedGroup(GROUP_MEMBER1, GROUP_MEMBER2); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - CoreAccountInfo coreAccountInfo = CoreAccountInfo.createFromEmailAndGaiaId(EMAIL, GAIA_ID); - when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); - - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -350,20 +342,15 @@ } @Test - public void testLeaveTabGroup_NullTab() { + public void testLeaveSharedTabGroup_NullTab() { runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processLeaveGroupAttempt(any(), any()); - when(mTabModel.getTabById(anyInt())).thenReturn(null); - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); - CoreAccountInfo coreAccountInfo = CoreAccountInfo.createFromEmailAndGaiaId(EMAIL, GAIA_ID); - when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); + mockIdentity(EMAIL1, GAIA_ID1); + createSyncGroup(COLLABORATION_ID1); - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -373,16 +360,14 @@ } @Test - public void testLeaveTabGroup_NullSavedTabGroup() { + public void testLeaveSharedTabGroup_NullSavedTabGroup() { runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processLeaveGroupAttempt(any(), any()); - + mockIdentity(EMAIL1, GAIA_ID1); when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(null); - CoreAccountInfo coreAccountInfo = CoreAccountInfo.createFromEmailAndGaiaId(EMAIL, GAIA_ID); - when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -392,18 +377,14 @@ } @Test - public void testLeaveTabGroup_NullCoreAccountInfo() { + public void testLeaveSharedTabGroup_NullCoreAccountInfo() { runWithValue(1, ActionConfirmationResult.CONFIRMATION_POSITIVE) .when(mActionConfirmationManager) .processLeaveGroupAttempt(any(), any()); - - SavedTabGroup savedTabGroup = new SavedTabGroup(); - savedTabGroup.title = GROUP_TITLE; - savedTabGroup.collaborationId = COLLABORATION_ID1; - when(mTabGroupSyncService.getGroup(any(LocalTabGroupId.class))).thenReturn(savedTabGroup); + createSyncGroup(COLLABORATION_ID1); when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(null); - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( ApplicationProvider.getApplicationContext(), mFilter, mActionConfirmationManager, @@ -457,4 +438,22 @@ verify(mContentSensitivitySetter).onResult(false); histogramWatcherForFalseBucket.assertExpected(); } + + private SavedTabGroup createSyncGroup(String collaborationId) { + SavedTabGroup syncGroup = mSyncedGroupTestHelper.newTabGroup(SYNC_GROUP_ID1, TAB_GROUP_ID); + syncGroup.title = GROUP_TITLE; + syncGroup.collaborationId = collaborationId; + return syncGroup; + } + + private GroupData createSharedGroup(GroupMember... members) { + GroupData sharedGroup = SharedGroupTestHelper.newGroupData(COLLABORATION_ID1, members); + when(mCollaborationService.getGroupData(eq(COLLABORATION_ID1))).thenReturn(sharedGroup); + return sharedGroup; + } + + private void mockIdentity(String email, GaiaId gaiaId) { + CoreAccountInfo coreAccountInfo = CoreAccountInfo.createFromEmailAndGaiaId(email, gaiaId); + when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); + } }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java index b5207894..0ef65cb 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -35,7 +35,9 @@ import static org.chromium.chrome.browser.tasks.tab_management.MessageCardViewProperties.MESSAGE_SERVICE_ACTION_PROVIDER; import static org.chromium.chrome.browser.tasks.tab_management.MessageCardViewProperties.MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER; import static org.chromium.components.data_sharing.SharedGroupTestHelper.COLLABORATION_ID1; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.EMAIL1; import static org.chromium.components.data_sharing.SharedGroupTestHelper.EMAIL2; +import static org.chromium.components.data_sharing.SharedGroupTestHelper.GAIA_ID1; import static org.chromium.components.data_sharing.SharedGroupTestHelper.GAIA_ID2; import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER1; import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER2; @@ -1452,6 +1454,10 @@ public void testDialogToolbarMenu_DeleteSharedGroup() { resetForDataSharing(/* isShared= */ true, GROUP_MEMBER1); + CoreAccountInfo coreAccountInfo = + CoreAccountInfo.createFromEmailAndGaiaId(EMAIL1, GAIA_ID1); + when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo); + mMediator.onToolbarMenuItemClick(R.id.delete_shared_group, TAB1_ID, COLLABORATION_ID1); verify(mActionConfirmationManager).processDeleteSharedGroupAttempt(eq(GROUP_TITLE), any()); assertEquals(1, mActionTester.getActionCount("TabGridDialogMenu.DeleteShared"));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java index d554c7a..9f5ba96 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -174,28 +174,13 @@ /** * Creates a {@link LayoutTab}. - * @param id The id of the reference {@link Tab} in the {@link TabModel}. - * @param isIncognito Whether the new tab is incognito. - * @return The newly created {@link LayoutTab}. + * + * @param id The id of the reference {@link Tab} in the {@link TabModel}. + * @param isIncognito Whether the new tab is incognito. + * @return The newly created {@link LayoutTab}. */ public LayoutTab createLayoutTab(int id, boolean isIncognito) { - return createLayoutTab(id, isIncognito, -1.f, -1.f); - } - - /** - * Creates a {@link LayoutTab}. - * @param id The id of the reference {@link Tab} in the {@link TabModel}. - * @param isIncognito Whether the new tab is incognito. - * @param maxContentWidth The max content width of the tab. Negative numbers will use the - * original content width. - * @param maxContentHeight The max content height of the tab. Negative numbers will use the - * original content height. - * @return The newly created {@link LayoutTab}. - */ - public LayoutTab createLayoutTab( - int id, boolean isIncognito, float maxContentWidth, float maxContentHeight) { - LayoutTab layoutTab = - mUpdateHost.createLayoutTab(id, isIncognito, maxContentWidth, maxContentHeight); + LayoutTab layoutTab = mUpdateHost.createLayoutTab(id, isIncognito); initLayoutTabFromHost(layoutTab); return layoutTab; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java index 5155c39f..6b8cdb45 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -1007,8 +1007,7 @@ } @Override - public LayoutTab createLayoutTab( - int id, boolean incognito, float maxContentWidth, float maxContentHeight) { + public LayoutTab createLayoutTab(int id, boolean incognito) { LayoutTab tab = mTabCache.get(id); if (tab == null) { tab = new LayoutTab(id, incognito, mHost.getWidth(), mHost.getHeight()); @@ -1016,9 +1015,6 @@ } else { tab.init(mHost.getWidth(), mHost.getHeight()); } - if (maxContentWidth > 0.f) tab.setMaxContentWidth(maxContentWidth); - if (maxContentHeight > 0.f) tab.setMaxContentHeight(maxContentHeight); - return tab; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java index e77170ad..8a45f6d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java
@@ -64,23 +64,18 @@ /** * Creates or recycles a {@Link LayoutTab}. * - * @param id The id of the reference tab in the - * {@link org.chromium.chrome.browser.tabmodel.TabModel}. - * @param incognito Whether the new tab is incognito. - * @param maxContentWidth The maximum layout width this tab can be. Negative numbers will use - * the original content width. - * @param maxContentHeight The maximum layout height this tab can be. Negative numbers will use - * the original content height. - * @return The created or recycled {@link LayoutTab}. + * @param id The id of the reference tab in the {@link + * org.chromium.chrome.browser.tabmodel.TabModel}. + * @param incognito Whether the new tab is incognito. + * @return The created or recycled {@link LayoutTab}. */ - LayoutTab createLayoutTab( - int id, boolean incognito, float maxContentWidth, float maxContentHeight); + LayoutTab createLayoutTab(int id, boolean incognito); /** * Notifies the host that the {@link LayoutTab} is no longer needed by the layout. * - * @param id The id of the reference tab in the - * {@link org.chromium.chrome.browser.tabmodel.TabModel}. + * @param id The id of the reference tab in the {@link + * org.chromium.chrome.browser.tabmodel.TabModel}. */ void releaseTabLayout(int id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/LayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/LayoutTab.java index 2ae8706..b57f2ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/LayoutTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/components/LayoutTab.java
@@ -318,13 +318,6 @@ } /** - * @return The original unclamped width (not scaled) of the tab contents texture. - */ - public float getUnclampedOriginalContentHeight() { - return get(ORIGINAL_CONTENT_HEIGHT_IN_DP); - } - - /** * @return The width of the drawn content (clipped and scaled). */ public float getFinalContentWidth() { @@ -332,27 +325,6 @@ } /** - * @return The maximum height the content can be. - */ - public float getMaxContentHeight() { - return get(MAX_CONTENT_HEIGHT); - } - - /** - * @param width The maximum width the content can be. - */ - public void setMaxContentWidth(float width) { - set(MAX_CONTENT_WIDTH, width); - } - - /** - * @param height The maximum height the content can be. - */ - public void setMaxContentHeight(float height) { - set(MAX_CONTENT_HEIGHT, height); - } - - /** * @return The id of the tab, same as the id from the Tab in TabModel. */ public int getId() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java index 6025c7f..a096327 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java
@@ -11,7 +11,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doAnswer; @@ -128,8 +127,7 @@ when(mNewTab.getId()).thenReturn(NEW_TAB_ID); when(mLayoutTab.isInitFromHostNeeded()).thenReturn(true); - when(mUpdateHost.createLayoutTab(anyInt(), anyBoolean(), anyFloat(), anyFloat())) - .thenReturn(mLayoutTab); + when(mUpdateHost.createLayoutTab(anyInt(), anyBoolean())).thenReturn(mLayoutTab); mActivityScenarioRule.getScenario().onActivity(this::onActivity); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java index dbd51b5..43d214e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -219,7 +219,7 @@ dataSharingTabManager.showRecentActivity(activity, collaborationId); recordUserAction("RecentActivity"); } else if (menuId == R.id.delete_shared_group) { - TabUiUtils.deleteSharedTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( activity, tabGroupModelFilter, actionConfirmationManager, @@ -227,7 +227,7 @@ tabId); recordUserAction("DeleteSharedGroup"); } else if (menuId == R.id.leave_group) { - TabUiUtils.leaveTabGroup( + TabUiUtils.exitSharedTabGroupWithDialog( activity, tabGroupModelFilter, actionConfirmationManager,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java index 5a23955..a78ad860 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
@@ -49,6 +49,7 @@ import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.PageTransition; +import org.chromium.ui.widget.Toast; import org.chromium.url.GURL; import org.chromium.url.Origin; @@ -358,6 +359,11 @@ mActivity.startActivity(intent, startActivityOptions); finish(FinishReason.OPEN_IN_BROWSER); } else { + Toast.makeText( + mActivity, + R.string.custom_tab_cant_perform_action_toast, + Toast.LENGTH_LONG) + .show(); // Silently crash to investigate https://crbug.com/384992232 boolean isPdf = tab.isNativePage() && tab.getNativePage().isPdf(); String logMessage =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java index 950c259..a0322d9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -1557,7 +1557,7 @@ if (mState == STATE_TITLE_ONLY || mCurrentlyShowingBranding) return; int securityIconResource = 0; - if (!shouldNestSecurityIcon() || !isSecureLevel()) { + if (!shouldNestSecurityIcon() || !isSecureOrNeutralLevel()) { securityIconResource = mLocationBarDataProvider.getSecurityIconResource( DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())); @@ -1579,10 +1579,11 @@ } /** Returns whether the current security level is considered secure. */ - private boolean isSecureLevel() { + private boolean isSecureOrNeutralLevel() { @ConnectionSecurityLevel int securityLevel = mLocationBarDataProvider.getSecurityLevel(); - return securityLevel == ConnectionSecurityLevel.SECURE + return securityLevel == ConnectionSecurityLevel.NONE + || securityLevel == ConnectionSecurityLevel.SECURE || securityLevel == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java index 65353ff..a02cf8a3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutUnitTest.java
@@ -13,7 +13,6 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -255,14 +254,10 @@ doAnswer( invocation -> { var args = invocation.getArguments(); - return new LayoutTab( - (Integer) args[0], - (Boolean) args[1], - ((Float) args[2]).intValue(), - ((Float) args[3]).intValue()); + return new LayoutTab((Integer) args[0], (Boolean) args[1], -1, -1); }) .when(mUpdateHost) - .createLayoutTab(anyInt(), anyBoolean(), anyFloat(), anyFloat()); + .createLayoutTab(anyInt(), anyBoolean()); when(mTab.getId()).thenReturn(TAB_ID); when(mTab.isNativePage()).thenReturn(false); when(mTabModelSelector.getCurrentTab()).thenReturn(mTab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java index 438b497..ded0b645 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
@@ -13,10 +13,12 @@ import org.chromium.base.ObserverList; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.chrome.browser.browser_controls.BottomControlsStacker; +import org.chromium.chrome.browser.browser_controls.BottomControlsStacker.LayerType; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelStateProvider; import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.keyboard_accessory.AccessorySheetVisualStateProvider; import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator; import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsVisualState; @@ -31,7 +33,7 @@ /** * An observer class that listens for changes in UI components that are attached to the bottom of - * the screen, bordering the OS navigation bar. This class then aggregates that information and + * the screen, bordering the navigation bar area. This class then aggregates that information and * notifies its own observers of properties of the UI currently bordering ("attached to") the * navigation bar. */ @@ -74,6 +76,7 @@ private final BrowserControlsStateProvider mBrowserControlsStateProvider; private int mBottomControlsHeight; + private int mBottomControlsMinHeight; private @Nullable @ColorInt Integer mBottomControlsColor; private boolean mUseBottomControlsColor; @@ -320,28 +323,58 @@ boolean bottomControlsMinHeightChanged, boolean requestNewFrame, boolean isVisibilityForced) { - updateBrowserControlsVisibility( + boolean hasOtherVisibleBottomControls = // MiniPlayerMediator#shrinkBottomControls() sets the height to 1 and minHeight to 0 // when hiding, instead of setting the height to 0. // TODO(b/320750931): Clean up once the MiniPlayerMediator has been improved. mBottomControlsHeight > 1 - && bottomOffset < mBottomControlsHeight && mBottomControlsStacker.hasVisibleLayersOtherThan( - BottomControlsStacker.LayerType.BOTTOM_CHIN)); + BottomControlsStacker.LayerType.BOTTOM_CHIN); + + if (!hasOtherVisibleBottomControls) { + updateUseBottomControlsColor(false); + return; + } + + boolean useBrowserControlsColor = bottomOffset < mBottomControlsHeight; + + // When bottom chin constraint exists, the chin will have the same coloring mechanism as + // the OS navigation bar as if E2E is disabled. + if (EdgeToEdgeUtils.isEdgeToEdgeBottomChinEnabled() + && ChromeFeatureList.isEnabled( + ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT)) { + boolean hasScrollablePortion = + bottomOffset < mBottomControlsHeight - mBottomControlsMinHeight; + boolean chinNotScrollable = + mBottomControlsStacker.isLayerNonScrollable(LayerType.BOTTOM_CHIN); + boolean hasOtherNonScrollableLayer = + mBottomControlsStacker.hasMultipleNonScrollableLayer(); + boolean hasFixedBrowserControlsAttached = + chinNotScrollable && hasOtherNonScrollableLayer; + + useBrowserControlsColor = hasScrollablePortion || hasFixedBrowserControlsAttached; + } + + updateUseBottomControlsColor(useBrowserControlsColor); } @Override public void onBottomControlsHeightChanged( int bottomControlsHeight, int bottomControlsMinHeight) { mBottomControlsHeight = bottomControlsHeight; + mBottomControlsMinHeight = bottomControlsMinHeight; // MiniPlayerMediator#shrinkBottomControls() sets the height to 1 and minHeight to 0 when // hiding, instead of setting the height to 0. // TODO(b/320750931): Clean up once the MiniPlayerMediator has been improved. - updateBrowserControlsVisibility( + updateUseBottomControlsColor( mBottomControlsHeight > 1 && mBottomControlsStacker.hasVisibleLayersOtherThan( BottomControlsStacker.LayerType.BOTTOM_CHIN)); + + // BottomChin constraint does not impact this method, since when control's height changes, + // #hasVisibleLayersOtherThan(BOTTOM_CHIN) already covers whether bottom chin will have + // a colored layer attached. } @Override @@ -350,7 +383,7 @@ updateBottomAttachedColor(); } - private void updateBrowserControlsVisibility(boolean useBottomControlsColor) { + private void updateUseBottomControlsColor(boolean useBottomControlsColor) { if (useBottomControlsColor == mUseBottomControlsColor) { return; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemover.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemover.java index 2a18140..9d9f2c0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemover.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemover.java
@@ -15,31 +15,25 @@ import org.chromium.base.Callback; import org.chromium.base.supplier.Supplier; -import org.chromium.chrome.R; import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingTabGroupUtils; import org.chromium.chrome.browser.data_sharing.DataSharingTabGroupUtils.GroupsPendingDestroy; import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabSelectionType; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory; import org.chromium.chrome.browser.tasks.tab_management.ActionConfirmationManager; import org.chromium.chrome.browser.tasks.tab_management.TabShareUtils; +import org.chromium.chrome.browser.tasks.tab_management.TabUiUtils; import org.chromium.components.browser_ui.widget.ActionConfirmationResult; import org.chromium.components.collaboration.CollaborationService; import org.chromium.components.data_sharing.DataSharingService; -import org.chromium.components.data_sharing.PeopleGroupActionOutcome; import org.chromium.components.data_sharing.member_role.MemberRole; -import org.chromium.components.signin.base.CoreAccountInfo; -import org.chromium.components.signin.identitymanager.ConsentLevel; -import org.chromium.components.signin.identitymanager.IdentityManager; import org.chromium.components.tab_group_sync.LocalTabGroupId; import org.chromium.components.tab_group_sync.SavedTabGroup; import org.chromium.components.tab_group_sync.TabGroupSyncService; import org.chromium.ui.modaldialog.ModalDialogManager; -import org.chromium.ui.modaldialog.ModalDialogUtils; import java.util.List; @@ -220,54 +214,21 @@ private void leaveOrDeleteCollaboration(@NonNull CollaborationInfo collaborationInfo) { assert collaborationInfo.isValid(); + // TODO(crbug.com/376907248): Remove DataSharingService from here once these operations // are supported by CollaborationService. + + String collaborationId = collaborationInfo.collaborationId; + @MemberRole int memberRole = collaborationInfo.memberRole; @Nullable DataSharingService dataSharingService = getDataSharingService(); if (dataSharingService == null) { - showGenericErrorDialog(mContext, mModalDialogManager); - return; - } - if (collaborationInfo.memberRole == MemberRole.OWNER) { - dataSharingService.deleteGroup( - collaborationInfo.collaborationId, - bindOnLeaveOrDeleteGroup(mContext, mModalDialogManager)); - } else if (collaborationInfo.memberRole == MemberRole.MEMBER) { - IdentityManager identityManager = - IdentityServicesProvider.get().getIdentityManager(getProfile()); - @Nullable - CoreAccountInfo account = identityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN); - if (account == null) { - showGenericErrorDialog(mContext, mModalDialogManager); - return; - } - dataSharingService.removeMember( - collaborationInfo.collaborationId, - account.getEmail(), - bindOnLeaveOrDeleteGroup(mContext, mModalDialogManager)); + TabUiUtils.showGenericErrorDialog(mContext, mModalDialogManager); } else { - showGenericErrorDialog(mContext, mModalDialogManager); + TabUiUtils.exitCollaborationWithoutWarning( + mContext, mModalDialogManager, dataSharingService, collaborationId, memberRole); } } - private static Callback<Integer> bindOnLeaveOrDeleteGroup( - Context context, ModalDialogManager modalDialogManager) { - return (@PeopleGroupActionOutcome Integer outcome) -> { - if (outcome != PeopleGroupActionOutcome.SUCCESS) { - showGenericErrorDialog(context, modalDialogManager); - } - }; - } - - private static void showGenericErrorDialog( - Context context, ModalDialogManager modalDialogManager) { - ModalDialogUtils.showOneButtonConfirmation( - modalDialogManager, - context.getResources(), - R.string.data_sharing_generic_failure_title, - R.string.data_sharing_generic_failure_description, - R.string.data_sharing_invitation_failure_button); - } - /** Contains info about a collaboration. */ private static class CollaborationInfo { public final @MemberRole int memberRole;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java index d104833..e7a9107 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java
@@ -277,9 +277,7 @@ mOnResultCaptor.getValue().onResult(ActionConfirmationResult.CONFIRMATION_NEGATIVE); verify(mTabModel).commitAllTabClosures(); - - verify(mDataSharingService) - .removeMember(eq(COLLABORATION_ID), eq(EMAIL), mOnResultCaptor.capture()); + verify(mDataSharingService).leaveGroup(eq(COLLABORATION_ID), mOnResultCaptor.capture()); mOnResultCaptor.getValue().onResult(PeopleGroupActionOutcome.PERSISTENT_FAILURE); verify(mModalDialogManager).showDialog(any(), anyInt());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java index 6573bd57..26667b6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
@@ -408,6 +408,29 @@ activity.getActivityTab().getWebContents(), "document.querySelector('input').blur()"); + // Verify that the root view bottom padding uses the nav bar bottom inset. + CriteriaHelper.pollUiThread( + () -> { + var navBarBottomInset = + insetObserver + .getLastRawWindowInsets() + .getInsets(WindowInsetsCompat.Type.navigationBars()) + .bottom; + Criteria.checkThat(rootView.getPaddingBottom(), Matchers.is(navBarBottomInset)); + }); + + // Dispatch window insets to simulate no overlap of the app window with the nav bar. + ThreadUtils.runOnUiThreadBlocking( + () -> { + insetObserver.onApplyWindowInsets( + rootView, + new WindowInsetsCompat.Builder() + .setInsets( + WindowInsetsCompat.Type.navigationBars(), + Insets.of(0, 0, 0, 0)) + .build()); + }); + // Verify that the root view bottom padding is reset. CriteriaHelper.pollUiThread( () -> Criteria.checkThat(rootView.getPaddingBottom(), Matchers.is(0)));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java index ce97d71..4ff9e94 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -606,12 +606,12 @@ }) public void testSecurityIconShown() { when(mLocationBarModel.getSecurityIconResource(anyBoolean())) - .thenReturn(R.drawable.omnibox_info); - when(mLocationBarModel.getSecurityLevel()).thenReturn(ConnectionSecurityLevel.NONE); + .thenReturn(R.drawable.omnibox_not_secure_warning); + when(mLocationBarModel.getSecurityLevel()).thenReturn(ConnectionSecurityLevel.WARNING); mLocationBar.onSecurityStateChanged(); - verify(mAnimationDelegate).updateSecurityButton(R.drawable.omnibox_info); + verify(mAnimationDelegate).updateSecurityButton(R.drawable.omnibox_not_secure_warning); } private void assertUrlAndTitleVisible(boolean titleVisible, boolean urlVisible) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java index d68bfb5..cfb9074 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
@@ -29,6 +29,8 @@ import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features; +import org.chromium.base.test.util.Features.DisableFeatures; +import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.browser_controls.BottomControlsStacker; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; @@ -49,6 +51,7 @@ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final int BOTTOM_CONTROLS_HEIGHT = 100; + private static final int BOTTOM_CONTROLS_MIN_HEIGHT_MULTIPLE_LAYER = 80; private static final int BOTTOM_CHIN_HEIGHT = 60; private static final int BROWSER_CONTROLS_COLOR = Color.RED; private static final int SNACKBAR_COLOR = Color.GREEN; @@ -141,6 +144,7 @@ } @Test + @DisableFeatures(ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT) public void testAdaptsColorToBrowserControls() { mColorChangeObserver.assertState(null, false, false); when(mBottomControlsStacker.hasVisibleLayersOtherThan( @@ -185,6 +189,133 @@ } @Test + @EnableFeatures({ + ChromeFeatureList.EDGE_TO_EDGE_BOTTOM_CHIN, + ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT + }) + public void testAdaptsColorToBrowserControls_bottomChinConstraint_bottomChinNonScrollable() { + mColorChangeObserver.assertState(null, false, false); + when(mBottomControlsStacker.hasVisibleLayersOtherThan( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + + when(mBottomControlsStacker.isLayerNonScrollable( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + when(mBottomControlsStacker.hasMultipleNonScrollableLayer()).thenReturn(false); + + // Show bottom controls, bottom chin is non-scrollable. + mBottomAttachedUiObserver.onBottomControlsBackgroundColorChanged(BROWSER_CONTROLS_COLOR); + mBottomAttachedUiObserver.onBottomControlsHeightChanged( + BOTTOM_CONTROLS_HEIGHT, BOTTOM_CHIN_HEIGHT); + mColorChangeObserver.assertState(BROWSER_CONTROLS_COLOR, false, false); + + // Scroll off bottom controls fully. Browser controls should no longer be used. + mBottomAttachedUiObserver.onControlsOffsetChanged( + 0, + 0, + false, + BOTTOM_CONTROLS_HEIGHT - BOTTOM_CHIN_HEIGHT, + BOTTOM_CHIN_HEIGHT, + false, + false, + false); + mColorChangeObserver.assertState(null, false, false); + } + + @Test + @EnableFeatures({ + ChromeFeatureList.EDGE_TO_EDGE_BOTTOM_CHIN, + ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT + }) + public void testAdaptsColorToBrowserControls_bottomChinConstraint_multipleNonScrollableLayer() { + mColorChangeObserver.assertState(null, false, false); + when(mBottomControlsStacker.hasVisibleLayersOtherThan( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + + when(mBottomControlsStacker.isLayerNonScrollable( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + when(mBottomControlsStacker.hasMultipleNonScrollableLayer()).thenReturn(true); + + // Show bottom controls, but only with the bottom chin. Color should be null. + mBottomAttachedUiObserver.onBottomControlsBackgroundColorChanged(BROWSER_CONTROLS_COLOR); + mBottomAttachedUiObserver.onBottomControlsHeightChanged( + BOTTOM_CONTROLS_HEIGHT, BOTTOM_CONTROLS_MIN_HEIGHT_MULTIPLE_LAYER); + mColorChangeObserver.assertState(BROWSER_CONTROLS_COLOR, false, false); + + // Scroll off bottom controls fully. Browser controls should still be used. + mBottomAttachedUiObserver.onControlsOffsetChanged( + 0, + 0, + false, + BOTTOM_CONTROLS_HEIGHT - BOTTOM_CONTROLS_MIN_HEIGHT_MULTIPLE_LAYER, + BOTTOM_CONTROLS_MIN_HEIGHT_MULTIPLE_LAYER, + false, + false, + false); + mColorChangeObserver.assertState(BROWSER_CONTROLS_COLOR, false, false); + } + + @Test + @EnableFeatures({ + ChromeFeatureList.EDGE_TO_EDGE_BOTTOM_CHIN, + ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT + }) + public void testAdaptsColorToBrowserControls_bottomChinConstraint_bottomChinScrollable() { + mColorChangeObserver.assertState(null, false, false); + when(mBottomControlsStacker.hasVisibleLayersOtherThan( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + + when(mBottomControlsStacker.isLayerNonScrollable( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(false); + when(mBottomControlsStacker.hasMultipleNonScrollableLayer()).thenReturn(false); + + // Show bottom controls. Color should be BROWSER_CONTROLS_COLOR. + mBottomAttachedUiObserver.onBottomControlsBackgroundColorChanged(BROWSER_CONTROLS_COLOR); + mBottomAttachedUiObserver.onBottomControlsHeightChanged(BOTTOM_CONTROLS_HEIGHT, 0); + mColorChangeObserver.assertState(BROWSER_CONTROLS_COLOR, false, false); + + // Scroll off bottom controls fully. Browser controls should no longer be used. + mBottomAttachedUiObserver.onControlsOffsetChanged( + 0, 0, false, BOTTOM_CONTROLS_HEIGHT, 0, false, false, false); + mColorChangeObserver.assertState(null, false, false); + } + + @Test + @EnableFeatures({ + ChromeFeatureList.EDGE_TO_EDGE_BOTTOM_CHIN, + ChromeFeatureList.EDGE_TO_EDGE_SAFE_AREA_CONSTRAINT + }) + public void testAdaptsColorToBrowserControls_bottomChinConstraint_bottomChinOnly() { + mColorChangeObserver.assertState(null, false, false); + when(mBottomControlsStacker.isLayerNonScrollable( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + when(mBottomControlsStacker.hasMultipleNonScrollableLayer()).thenReturn(false); + + // Assume some other browser controls were visible, but then is removed. + when(mBottomControlsStacker.hasVisibleLayersOtherThan( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(true); + mBottomAttachedUiObserver.onBottomControlsBackgroundColorChanged(BROWSER_CONTROLS_COLOR); + mBottomAttachedUiObserver.onBottomControlsHeightChanged( + BOTTOM_CONTROLS_HEIGHT, BOTTOM_CHIN_HEIGHT); + mColorChangeObserver.assertState(BROWSER_CONTROLS_COLOR, false, false); + + // Then the control is removed, the chin is set as the only layer + when(mBottomControlsStacker.hasVisibleLayersOtherThan( + eq(BottomControlsStacker.LayerType.BOTTOM_CHIN))) + .thenReturn(false); + mBottomAttachedUiObserver.onBottomControlsHeightChanged( + BOTTOM_CHIN_HEIGHT, BOTTOM_CHIN_HEIGHT); + mColorChangeObserver.assertState(null, false, false); + } + + @Test public void testAdaptsColorToSnackbars() { mColorChangeObserver.assertState(null, false, false);
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index 165e346..6bf71a5 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -7104,18 +7104,6 @@ <message name="IDS_OS_SETTINGS_SECURE_DNS_DESCRIPTION" desc="Description of secure DNS in Privacy options"> Make it harder for people with access to your internet traffic to see which sites you visit. <ph name="PRODUCT_NAME">$1<ex>ChromeOS</ex></ph> uses a secure connection to look up a site's IP address in the DNS (Domain Name System). </message> - <message name="IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TITLE" desc="Text for secure DNS dialog title in Privacy options for ChromeOS"> - Turn off URL encryption? - </message> - <message name="IDS_OS_SETTINGS_SECURE_DNS_DIALOG_BODY" desc="Text for secure DNS dialog body in Privacy options for ChromeOS"> - Those with access to your internet traffic can see what websites you visit - </message> - <message name="IDS_OS_SETTINGS_SECURE_DNS_DIALOG_CANCEL" desc="Text for secure DNS dialog to cancel action in Privacy options for ChromeOS"> - Cancel - </message> - <message name="IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TURN_OFF" desc="Text for secure DNS dialog to turn off DNS in Privacy options for ChromeOS"> - Turn off - </message> <message name="IDS_OS_SETTINGS_SECURE_DNS_AUTOMATIC_MODE_DESCRIPTION" desc="Text of the select option that puts secure DNS in Network Default mode"> Network default </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_BODY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_BODY.png.sha1 deleted file mode 100644 index 51d7148..0000000 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_BODY.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -8e19c797d6306ed565077f8699074c07b84f3161 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_CANCEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_CANCEL.png.sha1 deleted file mode 100644 index fed53dd..0000000 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_CANCEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -b6b011b608a63740d7f09c6c4080194de1c468af \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TITLE.png.sha1 deleted file mode 100644 index 3b1f7987..0000000 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TITLE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -857a0d191e88ff0fb09bb06c393b15a9c388dbf5 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TURN_OFF.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TURN_OFF.png.sha1 deleted file mode 100644 index 436db7e8..0000000 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TURN_OFF.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -e7a8e01efb3035dbac92bfd70c4349e59e7c4ebb \ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index bdfa88d..3cf950f 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1993,6 +1993,9 @@ kNtpMostRelevantTabResumptionModuleRemoteTabData[] = { {ntp_features::kNtpMostRelevantTabResumptionModuleDataParam, "2"}}; const FeatureEntry::FeatureParam + kNtpMostRelevantTabResumptionModuleRemoteVisitsData[] = { + {ntp_features::kNtpMostRelevantTabResumptionModuleDataParam, "2,4"}}; +const FeatureEntry::FeatureParam kNtpMostRelevantTabResumptionModuleAllHistoryRemoteTabData[] = { {ntp_features::kNtpMostRelevantTabResumptionModuleDataParam, "2,3,4"}}; const FeatureEntry::FeatureParam @@ -2020,6 +2023,9 @@ std::size(kNtpMostRelevantTabResumptionModuleTabData), nullptr}, {"- Remote Tabs Only", kNtpMostRelevantTabResumptionModuleRemoteTabData, std::size(kNtpMostRelevantTabResumptionModuleRemoteTabData), nullptr}, + {"- Remote Visits", kNtpMostRelevantTabResumptionModuleRemoteVisitsData, + std::size(kNtpMostRelevantTabResumptionModuleRemoteVisitsData), + nullptr}, {"- All History, Remote Tabs", kNtpMostRelevantTabResumptionModuleAllHistoryRemoteTabData, std::size(kNtpMostRelevantTabResumptionModuleAllHistoryRemoteTabData), @@ -9557,6 +9563,11 @@ flag_descriptions::kTabStripGroupCollapseAndroidDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kTabStripGroupCollapseAndroid)}, + {"tab-strip-group-drag-drop-android", + flag_descriptions::kTabStripGroupDragDropAndroidName, + flag_descriptions::kTabStripGroupDragDropAndroidDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kTabStripGroupDragDropAndroid)}, + {"tab-strip-group-reorder-android", flag_descriptions::kTabStripGroupReorderAndroidName, flag_descriptions::kTabStripGroupReorderAndroidDescription, kOsAndroid,
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index b7fd754..ae2762e93 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2139,8 +2139,6 @@ } IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, Shim_TestHiddenBeforeNavigation) { - SKIP_FOR_MPARCH(); // TODO(crbug.com/40202416): Enable test for MPArch. - TestHelper("testHiddenBeforeNavigation", "web_view/shim", NO_TEST_SERVER); }
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc index f5b089ab7..08518dd 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -85,82 +85,6 @@ } // namespace -// ----------------------------------------------------------------------------- -// DisplayOverlayController::FocusCycler: - -class DisplayOverlayController::FocusCycler { - public: - FocusCycler() = default; - ~FocusCycler() = default; - - // Adds `widget` to `widget_list_` if `widget` is visible and not in - // `widget_list_`. Otherwise, removes `widget` from `widget_list_`. - void RefreshWidget(views::Widget* widget) { - if (widget->IsVisible()) { - AddWidget(widget); - } else { - RemoveWidget(widget); - } - } - - void MaybeChangeFocusWidget(ui::KeyEvent& event) { - // Only tab pressed is checked because the focus change is triggered by the - // tab pressed event. No need to change widget focus again on tab key - // released event. - if (event.type() == ui::EventType::kKeyReleased || - !views::FocusManager::IsTabTraversalKeyEvent(event)) { - return; - } - - const bool reverse = event.IsShiftDown(); - auto* target_widget = views::Widget::GetWidgetForNativeWindow( - static_cast<aura::Window*>(event.target())); - auto* focus_manager = target_widget->GetFocusManager(); - - // Once there is next focusable view (dont_loop==true), it means the current - // focus is not the first or the last focusable view, so it doesn't need to - // change focus to the next widget. - if (focus_manager->GetNextFocusableView( - /*starting_view=*/focus_manager->GetFocusedView(), - /*starting_widget=*/target_widget, /*reverse=*/reverse, - /*dont_loop=*/true)) { - return; - } - - // Change focus to the next widget. - if (auto* next_widget = - GetNextWidgetToFocus(widget_list_, target_widget, reverse)) { - next_widget->GetFocusManager()->AdvanceFocus(reverse); - // Change the event target. - ui::Event::DispatcherApi(&event).set_target( - next_widget->GetNativeWindow()); - } - } - - private: - void AddWidget(views::Widget* widget) { - if (auto it = std::find(widget_list_.begin(), widget_list_.end(), widget); - it == widget_list_.end()) { - widget_list_.emplace_back(widget); - UpdateAccessibilityTree(widget_list_); - } - } - - void RemoveWidget(views::Widget* widget) { - if (auto it = std::find(widget_list_.begin(), widget_list_.end(), widget); - it != widget_list_.end()) { - widget_list_.erase(it); - UpdateAccessibilityTree(widget_list_); - } - } - - // Only contains visible and unique widgets. - std::vector<views::Widget*> widget_list_; -}; - -// ----------------------------------------------------------------------------- -// DisplayOverlayController: - DisplayOverlayController::DisplayOverlayController( TouchInjector* touch_injector) : touch_injector_(touch_injector) { @@ -176,7 +100,6 @@ DisplayOverlayController::~DisplayOverlayController() { touch_injector_->set_display_overlay_controller(nullptr); - widget_observations_.RemoveAllObservations(); touch_injector_->window()->RemoveObserver(this); RemoveAllWidgets(); @@ -215,7 +138,6 @@ RemoveActionHighlightWidget(); RemoveDeleteEditShortcutWidget(); RemoveEditingListWidget(); - RemoveFocusCycler(); if (GetActiveActionsSize() == 0u) { // If there is no active action in `kView` mode, it doesn't create // `input_mapping_widget_` to save resources. When switching from @@ -247,16 +169,11 @@ AddInputMappingWidget(); } - AddFocusCycler(); - // No matter if the mapping hint is hidden, `input_mapping_widget_` needs // to show up in `kEdit` mode. SetInputMappingVisible(/*visible=*/true); - // Since `focus_cycler_` was added in `kEdit` mode after - // `input_mapping_widget_` in general. Refresh `input_mapping_widget_` to - // make sure it is added in `focus_cycler_`. - focus_cycler_->RefreshWidget(input_mapping_widget_.get()); + UpdateAccessibilityTree(GetTraversableWidgets()); if (auto* input_mapping = GetInputMapping()) { input_mapping->SetDisplayMode(mode); @@ -409,7 +326,6 @@ CreateTransientWidget(input_mapping_widget_->GetNativeWindow(), /*widget_name=*/kButtonOptionsMenu, /*accept_events=*/true); - widget_observations_.AddObservation(button_options_widget_.get()); button_options_widget_->SetContentsView( std::make_unique<ButtonOptionsMenu>(this, action)); UpdateButtonOptionsMenuWidgetBounds(); @@ -420,6 +336,7 @@ SetEditingListVisibility(/*visible=*/false); button_options_widget_->Show(); + UpdateAccessibilityTree(GetTraversableWidgets()); } void DisplayOverlayController::RemoveButtonOptionsMenuWidget() { @@ -435,6 +352,7 @@ button_options_widget_->Close(); button_options_widget_.reset(); + UpdateAccessibilityTree(GetTraversableWidgets()); } void DisplayOverlayController::SetButtonOptionsMenuWidgetVisibility( @@ -451,6 +369,7 @@ } else { button_options_widget_->Hide(); } + UpdateAccessibilityTree(GetTraversableWidgets()); } void DisplayOverlayController::AddDeleteEditShortcutWidget( @@ -459,7 +378,6 @@ delete_edit_shortcut_widget_ = base::WrapUnique(views::BubbleDialogDelegateView::CreateBubble( std::make_unique<DeleteEditShortcut>(this, anchor_view))); - widget_observations_.AddObservation(delete_edit_shortcut_widget_.get()); } if (auto* shortcut = GetDeleteEditShortcut(); @@ -468,12 +386,14 @@ } delete_edit_shortcut_widget_->Show(); + UpdateAccessibilityTree(GetTraversableWidgets()); } void DisplayOverlayController::RemoveDeleteEditShortcutWidget() { if (delete_edit_shortcut_widget_) { delete_edit_shortcut_widget_->Close(); delete_edit_shortcut_widget_.reset(); + UpdateAccessibilityTree(GetTraversableWidgets()); } } @@ -561,8 +481,8 @@ } void DisplayOverlayController::OnKeyEvent(ui::KeyEvent* event) { - if (focus_cycler_) { - focus_cycler_->MaybeChangeFocusWidget(*event); + if (display_mode_ == DisplayMode::kEdit) { + MaybeChangeFocusWidget(*event); } } @@ -643,17 +563,6 @@ } } -void DisplayOverlayController::OnWidgetVisibilityChanged(views::Widget* widget, - bool visible) { - if (focus_cycler_) { - focus_cycler_->RefreshWidget(widget); - } -} - -void DisplayOverlayController::OnWidgetDestroying(views::Widget* widget) { - widget_observations_.RemoveObservation(widget); -} - void DisplayOverlayController::SetInputMappingVisible( bool visible, bool store_visible_state) { @@ -761,7 +670,6 @@ input_mapping_widget_ = CreateTransientWidget(touch_injector_->window(), /*widget_name=*/kInputMapping, /*accept_events=*/false); - widget_observations_.AddObservation(input_mapping_widget_.get()); input_mapping_widget_->SetContentsView( std::make_unique<InputMappingView>(this)); UpdateInputMappingWidgetBounds(); @@ -805,7 +713,6 @@ editing_list_widget_ = CreateTransientWidget( input_mapping_widget_->GetNativeWindow(), /*widget_name=*/kEditingList, /*accept_events=*/true); - widget_observations_.AddObservation(editing_list_widget_.get()); editing_list_widget_->SetContentsView(std::make_unique<EditingList>(this)); // Avoid active conflict with the game dashboard main menu. @@ -813,13 +720,14 @@ UpdateEditingListWidgetBounds(); editing_list_widget_->widget_delegate()->SetAccessibleTitle( l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_EDITING_LIST_A11Y_LABEL)); + UpdateAccessibilityTree(GetTraversableWidgets()); } void DisplayOverlayController::RemoveEditingListWidget() { if (editing_list_widget_) { editing_list_widget_->Close(); editing_list_widget_.reset(); - + UpdateAccessibilityTree(GetTraversableWidgets()); UpdateFlagAndProperty(touch_injector_->window(), ash::ArcGameControlsFlag::kEdit, /*turn_on=*/false); @@ -837,6 +745,7 @@ } else { editing_list_widget_->Hide(); } + UpdateAccessibilityTree(GetTraversableWidgets()); } EditingList* DisplayOverlayController::GetEditingList() { @@ -857,18 +766,6 @@ button_options_widget_->GetContentsView()); } -void DisplayOverlayController::AddFocusCycler() { - if (!focus_cycler_) { - focus_cycler_ = std::make_unique<FocusCycler>(); - } -} - -void DisplayOverlayController::RemoveFocusCycler() { - if (focus_cycler_) { - focus_cycler_.reset(); - } -} - void DisplayOverlayController::EnterButtonPlaceMode(ActionType action_type) { RemoveDeleteEditShortcutWidget(); SetEditingListVisibility(/*visible=*/false); @@ -1011,4 +908,56 @@ !IsFlagSet(flags, ash::ArcGameControlsFlag::kEdit)); } +std::vector<views::Widget*> DisplayOverlayController::GetTraversableWidgets() + const { + std::vector<views::Widget*> widget_list; + if (input_mapping_widget_ && input_mapping_widget_->IsVisible()) { + widget_list.emplace_back(input_mapping_widget_.get()); + } + if (editing_list_widget_ && editing_list_widget_->IsVisible()) { + widget_list.emplace_back(editing_list_widget_.get()); + } + if (button_options_widget_ && button_options_widget_->IsVisible()) { + widget_list.emplace_back(button_options_widget_.get()); + } + if (delete_edit_shortcut_widget_ && + delete_edit_shortcut_widget_->IsVisible()) { + widget_list.emplace_back(delete_edit_shortcut_widget_.get()); + } + return widget_list; +} + +void DisplayOverlayController::MaybeChangeFocusWidget(ui::KeyEvent& event) { + // Only tab pressed is checked because the focus change is triggered by the + // tab pressed event. No need to change widget focus again on tab key + // released event. + if (event.type() == ui::EventType::kKeyReleased || + !views::FocusManager::IsTabTraversalKeyEvent(event)) { + return; + } + + const bool reverse = event.IsShiftDown(); + auto* target_widget = views::Widget::GetWidgetForNativeWindow( + static_cast<aura::Window*>(event.target())); + auto* focus_manager = target_widget->GetFocusManager(); + + // Once there is next focusable view (dont_loop==true), it means the current + // focus is not the first or the last focusable view, so it doesn't need to + // change focus to the next widget. + if (focus_manager->GetNextFocusableView( + /*starting_view=*/focus_manager->GetFocusedView(), + /*starting_widget=*/target_widget, /*reverse=*/reverse, + /*dont_loop=*/true)) { + return; + } + + // Change focus to the next widget. + if (auto* next_widget = GetNextWidgetToFocus(GetTraversableWidgets(), + target_widget, reverse)) { + next_widget->GetFocusManager()->AdvanceFocus(reverse); + // Change the event target. + ui::Event::DispatcherApi(&event).set_target(next_widget->GetNativeWindow()); + } +} + } // namespace arc::input_overlay
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h index 9c338d8f..4341e86 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -11,7 +11,6 @@ #include "ash/public/cpp/arc_game_controls_flag.h" #include "ash/public/cpp/window_properties.h" #include "base/memory/raw_ptr.h" -#include "base/scoped_multi_source_observation.h" #include "chrome/browser/ash/arc/input_overlay/actions/input_element.h" #include "chrome/browser/ash/arc/input_overlay/arc_input_overlay_metrics.h" #include "ui/aura/window_observer.h" @@ -21,7 +20,6 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/views/widget/unique_widget_ptr.h" -#include "ui/views/widget/widget_observer.h" namespace views { class View; @@ -46,8 +44,7 @@ // menu, and educational dialog. It also handles the visibility of the // `ActionEditMenu` and `MessageView` by listening to the `LocatedEvent`. class DisplayOverlayController : public ui::EventHandler, - public aura::WindowObserver, - public views::WidgetObserver { + public aura::WindowObserver { public: explicit DisplayOverlayController(TouchInjector* touch_injector); DisplayOverlayController(const DisplayOverlayController&) = delete; @@ -138,10 +135,6 @@ const void* key, intptr_t old) override; - // views::WidgetObserver: - void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override; - void OnWidgetDestroying(views::Widget* widget) override; - const TouchInjector* touch_injector() const { return touch_injector_; } private: @@ -158,8 +151,6 @@ friend class OverlayViewTestBase; friend class RichNudgeTest; - class FocusCycler; - // If `on_overlay` is true, set event target on overlay layer. Otherwise, set // event target on the layer underneath the overlay layer. void SetEventTarget(views::Widget* overlay_widget, bool on_overlay); @@ -207,10 +198,6 @@ ButtonOptionsMenu* GetButtonOptionsMenu(); - // Focus cycler operations. - void AddFocusCycler(); - void RemoveFocusCycler(); - // Shows or removes target view when in or out button place mode. void AddTargetWidget(ActionType action_type); void RemoveTargetWidget(); @@ -235,10 +222,14 @@ // events. void UpdateEventRewriteCapability(); - const raw_ptr<TouchInjector> touch_injector_; + // Returns a list of visible widgets that are available to be traversed in the + // edit mode. + std::vector<views::Widget*> GetTraversableWidgets() const; - base::ScopedMultiSourceObservation<views::Widget, views::WidgetObserver> - widget_observations_{this}; + void MaybeChangeFocusWidget(ui::KeyEvent& event); + + // Owned by `ArcInputOverlayManager`. + const raw_ptr<TouchInjector> touch_injector_; DisplayMode display_mode_ = DisplayMode::kNone; @@ -250,8 +241,6 @@ std::unique_ptr<views::Widget> action_highlight_widget_; views::UniqueWidgetPtr delete_edit_shortcut_widget_; views::UniqueWidgetPtr rich_nudge_widget_; - - std::unique_ptr<FocusCycler> focus_cycler_; }; } // namespace arc::input_overlay
diff --git a/chrome/browser/ash/arc/session/BUILD.gn b/chrome/browser/ash/arc/session/BUILD.gn index 135fd95..7b72ba2 100644 --- a/chrome/browser/ash/arc/session/BUILD.gn +++ b/chrome/browser/ash/arc/session/BUILD.gn
@@ -83,7 +83,6 @@ "//chrome/browser/ash/arc/tracing", "//chrome/browser/ash/arc/tts", "//chrome/browser/ash/arc/user_session", - "//chrome/browser/ash/arc/video", "//chrome/browser/ash/arc/vmm", "//chrome/browser/ash/arc/wallpaper", "//chrome/browser/ash/crostini", @@ -118,6 +117,7 @@ "//chromeos/ash/experiences/arc/intent_helper", "//chromeos/ash/experiences/arc/media_session", "//chromeos/ash/experiences/arc/oemcrypto", + "//chromeos/ash/experiences/arc/video", "//chromeos/ash/services/cros_healthd/public/cpp", "//chromeos/ash/services/cros_healthd/public/mojom", "//chromeos/dbus/tpm_manager",
diff --git a/chrome/browser/ash/arc/session/arc_service_launcher.cc b/chrome/browser/ash/arc/session/arc_service_launcher.cc index 22dff282..0943443 100644 --- a/chrome/browser/ash/arc/session/arc_service_launcher.cc +++ b/chrome/browser/ash/arc/session/arc_service_launcher.cc
@@ -70,7 +70,6 @@ #include "chrome/browser/ash/arc/tracing/arc_tracing_bridge.h" #include "chrome/browser/ash/arc/tts/arc_tts_service.h" #include "chrome/browser/ash/arc/user_session/arc_user_session_service.h" -#include "chrome/browser/ash/arc/video/gpu_arc_video_service_host.h" #include "chrome/browser/ash/arc/vmm/arc_system_state_bridge.h" #include "chrome/browser/ash/arc/vmm/arc_vmm_manager.h" #include "chrome/browser/ash/arc/wallpaper/arc_wallpaper_service.h" @@ -107,6 +106,7 @@ #include "chromeos/ash/experiences/arc/session/arc_session.h" #include "chromeos/ash/experiences/arc/session/arc_session_runner.h" #include "chromeos/ash/experiences/arc/system_ui/arc_system_ui_bridge.h" +#include "chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h" #include "components/prefs/pref_member.h" #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/chrome/browser/ash/arc/video/DEPS b/chrome/browser/ash/arc/video/DEPS deleted file mode 100644 index 8230f44..0000000 --- a/chrome/browser/ash/arc/video/DEPS +++ /dev/null
@@ -1,20 +0,0 @@ -include_rules = [ - # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for - # details. - "-chrome", - - # This directory is in //chrome, which violates the rule above. Allow this - # directory to #include its own files. - "+chrome/browser/ash/arc/video", - - # Existing dependencies within //chrome. There is an active effort to - # refactor //chrome/browser/ash to break these dependencies; see b/332804822. - # Whenever possible, avoid adding new //chrome dependencies to this list. - # - # Files residing in certain directories (e.g., //chrome/browser) are listed - # individually. Other dependencies within //chrome are listed on a per- - # directory basis. See //tools/chromeos/gen_deps.sh for details. - - # Dependencies outside of //chrome: - "+mojo/core", -]
diff --git a/chrome/browser/ash/dbus/BUILD.gn b/chrome/browser/ash/dbus/BUILD.gn index 8268cd8..47e34d5d 100644 --- a/chrome/browser/ash/dbus/BUILD.gn +++ b/chrome/browser/ash/dbus/BUILD.gn
@@ -79,7 +79,6 @@ "//chrome/browser/ash/arc/fileapi", "//chrome/browser/ash/arc/session", "//chrome/browser/ash/arc/tracing", - "//chrome/browser/ash/arc/video", "//chrome/browser/ash/crostini", "//chrome/browser/ash/login/lock", "//chrome/browser/ash/net", @@ -155,6 +154,7 @@ "//chromeos/ash/components/language_packs", "//chromeos/ash/components/settings", "//chromeos/ash/experiences/arc/session", + "//chromeos/ash/experiences/arc/video", "//chromeos/ash/services/rollback_network_config/public/mojom", "//chromeos/components/mojo_bootstrap", "//chromeos/dbus/constants",
diff --git a/chrome/browser/ash/dbus/DEPS b/chrome/browser/ash/dbus/DEPS index f310476..eb6d6087 100644 --- a/chrome/browser/ash/dbus/DEPS +++ b/chrome/browser/ash/dbus/DEPS
@@ -19,7 +19,7 @@ "+chrome/browser/ash/arc/session", "+chrome/browser/ash/arc/test", "+chrome/browser/ash/arc/tracing", - "+chrome/browser/ash/arc/video", + "+chromeos/ash/experiences/arc/video", "+chrome/browser/ash/borealis", "+chrome/browser/ash/crostini", "+chrome/browser/ash/exo",
diff --git a/chrome/browser/ash/dbus/libvda_service_provider.cc b/chrome/browser/ash/dbus/libvda_service_provider.cc index aa3e588..8251ec9 100644 --- a/chrome/browser/ash/dbus/libvda_service_provider.cc +++ b/chrome/browser/ash/dbus/libvda_service_provider.cc
@@ -8,7 +8,7 @@ #include <utility> #include "base/functional/bind.h" -#include "chrome/browser/ash/arc/video/gpu_arc_video_service_host.h" +#include "chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h" #include "dbus/bus.h" #include "dbus/message.h" #include "mojo/public/cpp/system/platform_handle.h"
diff --git a/chrome/browser/ash/floating_workspace/DEPS b/chrome/browser/ash/floating_workspace/DEPS index 154fc24e..ad24539b 100644 --- a/chrome/browser/ash/floating_workspace/DEPS +++ b/chrome/browser/ash/floating_workspace/DEPS
@@ -32,3 +32,9 @@ "+chrome/grit", "+chrome/test/base", ] + +specific_include_rules = { + "floating_workspace_service_unittest\.cc": [ + "+chrome/browser/ash/settings/scoped_cros_settings_test_helper.h", + ] +}
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 abf0ac2..8e64e93 100644 --- a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc +++ b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
@@ -23,6 +23,7 @@ #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" @@ -405,6 +406,8 @@ void SetUp() override { chromeos::PowerManagerClient::InitializeFake(); + cros_settings_test_helper_ = + std::make_unique<ScopedCrosSettingsTestHelper>(); ash::AshTestHelper::InitParams params; ash_test_helper_.SetUp(std::move(params)); profile_manager_ = std::make_unique<TestingProfileManager>( @@ -468,7 +471,9 @@ profile_ = nullptr; profile_manager_ = nullptr; mock_desks_client_ = nullptr; + fake_user_manager_.Reset(); ash_test_helper_.TearDown(); + cros_settings_test_helper_.reset(); chromeos::PowerManagerClient::Shutdown(); } @@ -484,6 +489,7 @@ std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_; std::unique_ptr<apps::AppRegistryCache> cache_; AccountId account_id_; + std::unique_ptr<ScopedCrosSettingsTestHelper> cros_settings_test_helper_; AshTestHelper ash_test_helper_; std::unique_ptr<TestingProfileManager> profile_manager_; std::unique_ptr<MockDesksClient> mock_desks_client_;
diff --git a/chrome/browser/ash/input_method/BUILD.gn b/chrome/browser/ash/input_method/BUILD.gn index 39d45f5b..08e85a4 100644 --- a/chrome/browser/ash/input_method/BUILD.gn +++ b/chrome/browser/ash/input_method/BUILD.gn
@@ -195,7 +195,6 @@ "//chrome/app:generated_resources", "//chrome/browser:browser_process", "//chrome/browser:resources", - "//chrome/browser/ash/crosapi:browser_util", "//chrome/browser/ash/file_manager", "//chrome/browser/ash/input_method/japanese", "//chrome/browser/ash/lobster", @@ -346,7 +345,6 @@ "//base/test:test_support", "//chrome/app:generated_resources", "//chrome/browser", - "//chrome/browser/ash/crosapi:browser_util", "//chrome/browser/ash/input_method:test_support", "//chrome/browser/ash/login/users:test_support", "//chrome/browser/ash/settings:test_support",
diff --git a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc index 441b29dc..ebe08267 100644 --- a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc +++ b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
@@ -16,7 +16,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "base/time/time.h" -#include "chrome/browser/ash/crosapi/browser_util.h" #include "chrome/browser/ash/input_method/assistive_suggester_client_filter.h" #include "chrome/browser/ash/input_method/assistive_suggester_switch.h" #include "chrome/browser/ash/input_method/fake_suggestion_handler.h"
diff --git a/chrome/browser/ash/input_method/autocorrect_manager.cc b/chrome/browser/ash/input_method/autocorrect_manager.cc index 0141014..bcdd275 100644 --- a/chrome/browser/ash/input_method/autocorrect_manager.cc +++ b/chrome/browser/ash/input_method/autocorrect_manager.cc
@@ -15,7 +15,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" -#include "chrome/browser/ash/crosapi/browser_util.h" #include "chrome/browser/ash/input_method/assistive_prefs.h" #include "chrome/browser/ash/input_method/assistive_window_properties.h" #include "chrome/browser/ash/input_method/autocorrect_enums.h" @@ -702,8 +701,7 @@ auto autocorrect_pref = GetPhysicalKeyboardAutocorrectPref(*pref_service, engine_id); - if (!crosapi::browser_util::IsLacrosEnabled() && - base::FeatureList::IsEnabled(features::kAutocorrectByDefault) && + if (base::FeatureList::IsEnabled(features::kAutocorrectByDefault) && autocorrect_pref == AutocorrectPreference::kDefault && IsUsEnglishId(engine_id) && // This class is instantiated with NativeInputMethodEngineObserver, which @@ -859,10 +857,6 @@ // If cursor is inside autocorrect range (inclusive), show undo window and // record relevant metrics. - // On Lacros, the async behaviors accidentally delay the update of autocorrect - // range after the onSurroundingTextChanged. When users delete a few - // characters of the suggested words, this code still uses the outdated range, - // hence allowing the undo window to show. // TODO(b/278616918): Consider remove the // IsAutocorrectSuggestionInSurroundingText logic once async behaviors are // corrected. @@ -984,8 +978,7 @@ autocorrect_range.end() - surrounding_text.selection_range.end(); if (base::FeatureList::IsEnabled( - features::kAutocorrectUseReplaceSurroundingText) && - !crosapi::browser_util::IsLacrosEnabled()) { + features::kAutocorrectUseReplaceSurroundingText)) { input_context->ReplaceSurroundingText( before, after, pending_autocorrect_->original_text); } else {
diff --git a/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc b/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc index f75160bc..f0645070 100644 --- a/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc +++ b/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc
@@ -26,7 +26,8 @@ void LobsterBubbleCoordinator::LoadUI(Profile* profile, std::optional<std::string_view> query, - LobsterMode mode) { + LobsterMode mode, + const gfx::Rect& caret_bounds) { if (IsShowingUI()) { contents_wrapper_->CloseUI(); } @@ -53,7 +54,7 @@ /*esc_closes_ui=*/false); std::unique_ptr<LobsterView> lobster_view = - std::make_unique<LobsterView>(contents_wrapper_.get(), gfx::Rect()); + std::make_unique<LobsterView>(contents_wrapper_.get(), caret_bounds); auto bubble = lobster_view->GetWeakPtr(); views::BubbleDialogDelegateView::CreateBubble(std::move(lobster_view));
diff --git a/chrome/browser/ash/lobster/lobster_bubble_coordinator.h b/chrome/browser/ash/lobster/lobster_bubble_coordinator.h index 08fa417..3bf3ef8 100644 --- a/chrome/browser/ash/lobster/lobster_bubble_coordinator.h +++ b/chrome/browser/ash/lobster/lobster_bubble_coordinator.h
@@ -28,7 +28,8 @@ void LoadUI(Profile* profile, std::optional<std::string_view> query, - LobsterMode mode); + LobsterMode mode, + const gfx::Rect& caret_bounds); void ShowUI(); void CloseUI();
diff --git a/chrome/browser/ash/lobster/lobster_client_impl.cc b/chrome/browser/ash/lobster/lobster_client_impl.cc index 1301f8e..efd4a7db 100644 --- a/chrome/browser/ash/lobster/lobster_client_impl.cc +++ b/chrome/browser/ash/lobster/lobster_client_impl.cc
@@ -47,8 +47,9 @@ } void LobsterClientImpl::LoadUI(std::optional<std::string> query, - ash::LobsterMode mode) { - service_->LoadUI(query, mode); + ash::LobsterMode mode, + const gfx::Rect& caret_bounds) { + service_->LoadUI(query, mode, caret_bounds); } void LobsterClientImpl::ShowUI() {
diff --git a/chrome/browser/ash/lobster/lobster_client_impl.h b/chrome/browser/ash/lobster/lobster_client_impl.h index 544154f8..8ed5960 100644 --- a/chrome/browser/ash/lobster/lobster_client_impl.h +++ b/chrome/browser/ash/lobster/lobster_client_impl.h
@@ -39,7 +39,9 @@ const std::string& image_bytes) override; void QueueInsertion(const std::string& image_bytes, StatusCallback insert_status_callback) override; - void LoadUI(std::optional<std::string> query, ash::LobsterMode mode) override; + void LoadUI(std::optional<std::string> query, + ash::LobsterMode mode, + const gfx::Rect& caret_bounds) override; void ShowUI() override; void CloseUI() override;
diff --git a/chrome/browser/ash/lobster/lobster_service.cc b/chrome/browser/ash/lobster/lobster_service.cc index 3029588a..35952a1 100644 --- a/chrome/browser/ash/lobster/lobster_service.cc +++ b/chrome/browser/ash/lobster/lobster_service.cc
@@ -68,8 +68,9 @@ } void LobsterService::LoadUI(std::optional<std::string> query, - ash::LobsterMode mode) { - bubble_coordinator_.LoadUI(profile_, query, mode); + ash::LobsterMode mode, + const gfx::Rect& caret_bounds) { + bubble_coordinator_.LoadUI(profile_, query, mode, caret_bounds); } void LobsterService::ShowUI() {
diff --git a/chrome/browser/ash/lobster/lobster_service.h b/chrome/browser/ash/lobster/lobster_service.h index 2ff4b0fb..9ca206f4 100644 --- a/chrome/browser/ash/lobster/lobster_service.h +++ b/chrome/browser/ash/lobster/lobster_service.h
@@ -53,7 +53,9 @@ const std::string& description, const std::string& image_bytes); - void LoadUI(std::optional<std::string> query, ash::LobsterMode mode); + void LoadUI(std::optional<std::string> query, + ash::LobsterMode mode, + const gfx::Rect& caret_bounds); void ShowUI();
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java index 2bd32089..50aba3c 100644 --- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java +++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java
@@ -118,6 +118,11 @@ // The heights of each layer at their fully shown positions. private final SparseIntArray mLayerRestingOffsets = new SparseIntArray(); + // Whether layer is contributing to the minHeight. This is calculated during height calculation, + // and won't update when the layers are being repositioned during scroll. + private final SparseBooleanArray mLayerHasMinHeight = new SparseBooleanArray(); + private boolean mHasMoreThanOneNonScrollableLayer; + private final BrowserControlsSizer mBrowserControlsSizer; private int mTotalHeight = INVALID_HEIGHT; @@ -184,6 +189,23 @@ return mTotalMinHeight; } + /** + * Whether the layer with {@link type} is not scrollable. To other words, return true iff the + * layer is contributing to the bottom control's minHeight. + */ + public boolean isLayerNonScrollable(int type) { + return mLayers.get(type) != null && mLayerHasMinHeight.get(type); + } + + /** + * Whether there are more than one layer that returns true with {@link #isLayerNonScrollable}. + * To other words, returns true when more than one layer is contributing to browser control's + * minHeight. + */ + public boolean hasMultipleNonScrollableLayer() { + return mHasMoreThanOneNonScrollableLayer; + } + /** Returns if viz is able to move the browser controls now. */ public boolean isMoveableByViz() { return ChromeFeatureList.sBcivBottomControls.isEnabled() @@ -537,6 +559,10 @@ : "A scroll-off layer under a NEVER_SCROLL_OFF layer is not supported. Layer: " + layer.getType(); + // When min height exists before processing the current layer's height, it means more + // than one non-scrollable layer exists. + mHasMoreThanOneNonScrollableLayer = minHeight != 0; + if (ChromeFeatureList.sBcivBottomControls.isEnabled()) { if (shouldScrollOff) { if (mOffsetTagsInfo != null) { @@ -549,6 +575,7 @@ height += layer.getHeight(); minHeight += shouldScrollOff ? 0 : layer.getHeight(); + mLayerHasMinHeight.put(type, !shouldScrollOff); } mTotalHeight = height;
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStackerUnitTest.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStackerUnitTest.java index 43c582c..a283861 100644 --- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStackerUnitTest.java +++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStackerUnitTest.java
@@ -279,6 +279,8 @@ mBottomControlsStacker.addLayer(layer); mBottomControlsStacker.requestLayerUpdate(false); verify(mBrowserControlsSizer).setBottomControlsHeight(100, 0); + assertLayerNonScrollable(TOP_LAYER, false); + assertHasMultipleNonScrollableLayer(false); } @Test @@ -292,6 +294,8 @@ mBottomControlsStacker.addLayer(layer); mBottomControlsStacker.requestLayerUpdate(false); verify(mBrowserControlsSizer).setBottomControlsHeight(100, 100); + assertLayerNonScrollable(TOP_LAYER, true); + assertHasMultipleNonScrollableLayer(false); } @Test @@ -306,6 +310,8 @@ mBottomControlsStacker.addLayer(layer); mBottomControlsStacker.requestLayerUpdate(false); verify(mBrowserControlsSizer).setBottomControlsHeight(0, 0); + assertLayerNonScrollable(TOP_LAYER, false); + assertHasMultipleNonScrollableLayer(false); } @Test @@ -328,6 +334,10 @@ verify(mBrowserControlsSizer).setBottomControlsHeight(110, 0); verify(mBrowserControlsSizer).setAnimateBrowserControlsHeightChanges(true); + + assertLayerNonScrollable(TOP_LAYER, false); + assertLayerNonScrollable(BOTTOM_LAYER, false); + assertHasMultipleNonScrollableLayer(false); } @Test @@ -350,6 +360,10 @@ verify(mBrowserControlsSizer).setBottomControlsHeight(110, 110); verify(mBrowserControlsSizer).setAnimateBrowserControlsHeightChanges(true); + + assertLayerNonScrollable(TOP_LAYER, true); + assertLayerNonScrollable(BOTTOM_LAYER, true); + assertHasMultipleNonScrollableLayer(true); } @Test @@ -372,6 +386,10 @@ verify(mBrowserControlsSizer).setBottomControlsHeight(110, 10); verify(mBrowserControlsSizer).setAnimateBrowserControlsHeightChanges(true); + + assertLayerNonScrollable(TOP_LAYER, false); + assertLayerNonScrollable(BOTTOM_LAYER, true); + assertHasMultipleNonScrollableLayer(false); } @Test @@ -401,6 +419,11 @@ verify(mBrowserControlsSizer).setBottomControlsHeight(180, 80); verify(mBrowserControlsSizer).setAnimateBrowserControlsHeightChanges(true); + + assertLayerNonScrollable(TOP_LAYER, false); + assertLayerNonScrollable(MID_LAYER, true); + assertLayerNonScrollable(BOTTOM_LAYER, true); + assertHasMultipleNonScrollableLayer(true); } @Test @@ -423,6 +446,10 @@ verify(mBrowserControlsSizer).setBottomControlsHeight(120, 0); verify(mBrowserControlsSizer).setAnimateBrowserControlsHeightChanges(true); + + assertLayerNonScrollable(TOP_LAYER, false); + assertLayerNonScrollable(BOTTOM_LAYER, false); + assertHasMultipleNonScrollableLayer(false); } @Test(expected = AssertionError.class) @@ -1582,6 +1609,20 @@ assertEquals("Different yOffset observed.", expectedOffset, layer.mYOffset); } + private void assertLayerNonScrollable(@LayerType int type, boolean nonScrollable) { + assertEquals( + "isLayerNonScrollable(" + type + ") is unexpected.", + nonScrollable, + mBottomControlsStacker.isLayerNonScrollable(type)); + } + + private void assertHasMultipleNonScrollableLayer(boolean hasOtherLayers) { + assertEquals( + "hasMultipleNonScrollableLayer() is unexpected.", + hasOtherLayers, + mBottomControlsStacker.hasMultipleNonScrollableLayer()); + } + private static class TestLayer implements BottomControlsLayer { private final @LayerType int mType; private final @LayerScrollBehavior int mScrollBehavior;
diff --git a/chrome/browser/devtools/protocol/target_handler.cc b/chrome/browser/devtools/protocol/target_handler.cc index 7ba4064..dfd7a6f7 100644 --- a/chrome/browser/devtools/protocol/target_handler.cc +++ b/chrome/browser/devtools/protocol/target_handler.cc
@@ -4,10 +4,6 @@ #include "chrome/browser/devtools/protocol/target_handler.h" -#include <ranges> -#include <string_view> - -#include "base/notreached.h" #include "chrome/browser/devtools/chrome_devtools_manager_delegate.h" #include "chrome/browser/devtools/devtools_browser_context_manager.h" #include "chrome/browser/profiles/profile_manager.h" @@ -15,8 +11,6 @@ #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" -#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" #include "chrome/common/webui_url_constants.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/web_contents.h" @@ -85,7 +79,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> browser_context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window, @@ -148,35 +141,13 @@ "Target position can only be set for new windows"); } - static std::string_view kActionableWindowStates[] = { - protocol::Target::WindowStateEnum::Minimized, - protocol::Target::WindowStateEnum::Maximized, - protocol::Target::WindowStateEnum::Fullscreen, - }; - - bool set_window_state = !!window_state; - if (set_window_state) { - if (!create_new_window) { - return protocol::Response::ServerError( - "Target window state can only be set for new windows"); - } - if (*window_state == protocol::Target::WindowStateEnum::Normal) { - set_window_state = false; - } else if (std::ranges::find(kActionableWindowStates, *window_state) == - std::end(kActionableWindowStates)) { - return protocol::Response::ServerError("Invalid target window state: " + - *window_state); - } - } - NavigateParams params = CreateNavigateParams( profile, gurl, ui::PAGE_TRANSITION_AUTO_TOPLEVEL, create_new_window, create_in_background, target_browser); Navigate(¶ms); - if (!params.navigated_or_inserted_contents) { + if (!params.navigated_or_inserted_contents) return protocol::Response::ServerError("Failed to open a new tab"); - } if (set_window_position) { BrowserWindow* browser_window = params.browser->window(); @@ -197,20 +168,6 @@ browser_window->SetBounds(bounds); } - if (set_window_state) { - if (*window_state == protocol::Target::WindowStateEnum::Minimized) { - params.browser->window()->Minimize(); - } else if (*window_state == protocol::Target::WindowStateEnum::Maximized) { - params.browser->window()->Maximize(); - } else if (*window_state == protocol::Target::WindowStateEnum::Fullscreen) { - params.browser->exclusive_access_manager() - ->fullscreen_controller() - ->ToggleBrowserFullscreenMode(/*user_initiated=*/false); - } else { - NOTREACHED(); - } - } - if (!create_in_background) { params.navigated_or_inserted_contents->Focus(); }
diff --git a/chrome/browser/devtools/protocol/target_handler.h b/chrome/browser/devtools/protocol/target_handler.h index 67cddb8..05332ea2 100644 --- a/chrome/browser/devtools/protocol/target_handler.h +++ b/chrome/browser/devtools/protocol/target_handler.h
@@ -35,7 +35,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> browser_context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window,
diff --git a/chrome/browser/extensions/account_extension_tracker.cc b/chrome/browser/extensions/account_extension_tracker.cc index 77ca943..b2ea29c 100644 --- a/chrome/browser/extensions/account_extension_tracker.cc +++ b/chrome/browser/extensions/account_extension_tracker.cc
@@ -206,11 +206,7 @@ // and thus don't need to be set here. AccountExtensionType type = GetAccountExtensionType(extension_id); if (type == AccountExtensionType::kLocal) { - SetAccountExtensionType(extension_id, - AccountExtensionType::kAccountInstalledLocally); - for (auto& observer : observers_) { - observer.OnExtensionUploadabilityChanged(extension_id); - } + PromoteLocalToAccountExtension(extension_id); } } @@ -271,6 +267,15 @@ sync_util::ShouldSync(profile_, &extension); } +void AccountExtensionTracker::OnAccountUploadInitiatedForExtension( + const ExtensionId& extension_id) { + PromoteLocalToAccountExtension(extension_id); + + // The "local state" of the uploaded extension will be pushed to sync soon, + // so set NeedsSync to false. + ExtensionPrefs::Get(profile_)->SetNeedsSync(extension_id, false); +} + void AccountExtensionTracker::SetAccountExtensionTypeForTesting( const ExtensionId& extension_id, AccountExtensionType type) { @@ -299,6 +304,25 @@ [&extension_id](const ExtensionId& id) { return extension_id == id; }); } +void AccountExtensionTracker::PromoteLocalToAccountExtension( + const ExtensionId& extension_id) { + // Make sure we're actually promoting a local extension! + DCHECK_EQ(GetAccountExtensionType(extension_id), + AccountExtensionType::kLocal); + + // The previous `AccountExtensionType::kLocal` state implies the extension is + // still associated with the current device hence the promotion to + // `AccountExtensionType::kAccountInstalledLocally`. + SetAccountExtensionType(extension_id, + AccountExtensionType::kAccountInstalledLocally); + + // The extension's uploadability may change when its AccountExtensionType + // changes. + for (auto& observer : observers_) { + observer.OnExtensionUploadabilityChanged(extension_id); + } +} + void AccountExtensionTracker::NotifyOnExtensionsUploadabilityChanged() { for (auto& observer : observers_) { observer.OnExtensionsUploadabilityChanged();
diff --git a/chrome/browser/extensions/account_extension_tracker.h b/chrome/browser/extensions/account_extension_tracker.h index ce4c295..cc04b42 100644 --- a/chrome/browser/extensions/account_extension_tracker.h +++ b/chrome/browser/extensions/account_extension_tracker.h
@@ -106,6 +106,10 @@ // current signed in user. bool CanUploadAsAccountExtension(const Extension& extension) const; + // Called when the user initiates an upload for the given `extension_id` to + // their account. + void OnAccountUploadInitiatedForExtension(const ExtensionId& extension_id); + void SetAccountExtensionTypeForTesting(const ExtensionId& extension_id, AccountExtensionType type); @@ -122,6 +126,9 @@ // Removes `extension_id` in `extensions_installed_with_signin_promo_`. void RemoveExpiredExtension(const ExtensionId& extension_id); + // Promotes `extension_id` from a local to an account extension. + void PromoteLocalToAccountExtension(const ExtensionId& extension_id); + // Notifies observers that the eligibility of multiple extensions to be // uploaded to the user's account may have changed. void NotifyOnExtensionsUploadabilityChanged();
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index dada213..f476b38 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -27,6 +27,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/task/thread_pool.h" +#include "base/types/expected.h" #include "base/uuid.h" #include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/devtools/devtools_window.h" @@ -39,6 +40,7 @@ #include "chrome/browser/extensions/extension_commands_global_registry.h" #include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_sync_service.h" #include "chrome/browser/extensions/extension_sync_util.h" #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/extension_tab_util.h" @@ -2967,25 +2969,13 @@ EXTENSION_FUNCTION_VALIDATE(params); extension_id_ = std::move(params->extension_id); + profile_ = Profile::FromBrowserContext(browser_context()); - const Extension* extension = - ExtensionRegistry::Get(browser_context()) - ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING); - if (!extension) { - return RespondNow(Error( - ErrorUtils::FormatErrorMessage(kNoExtensionError, extension_id_))); + auto result = VerifyExtensionAndSigninState(); + if (!result.has_value()) { + return RespondNow(Error(result.error())); } - - Profile* profile = Profile::FromBrowserContext(browser_context()); - - // Return an error if there is no signed in user. - signin::IdentityManager* identity_manager = - IdentityManagerFactory::GetForProfile(profile); - AccountInfo account_info = identity_manager->FindExtendedAccountInfo( - identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)); - if (account_info.IsEmpty()) { - return RespondNow(Error(kUserNotSignedIn)); - } + const Extension* extension = *result; // Return an error if the extension cannot be uploaded for reasons such as: // - syncing extensions in transport mode (signed in but not full sync) is @@ -2993,7 +2983,7 @@ // - the extension is already associated with the signed in user's account. // - the extension is not syncable (for example, if it's unpacked). if (!sync_util::IsExtensionsExplicitSigninEnabled() || - !AccountExtensionTracker::Get(profile)->CanUploadAsAccountExtension( + !AccountExtensionTracker::Get(profile_)->CanUploadAsAccountExtension( *extension)) { return RespondNow(Error(ErrorUtils::FormatErrorMessage( kCannotUploadExtensionToAccount, extension_id_))); @@ -3030,6 +3020,37 @@ return RespondLater(); } +base::expected<const Extension*, std::string> +DeveloperPrivateUploadExtensionToAccountFunction:: + VerifyExtensionAndSigninState() { + const Extension* extension = + ExtensionRegistry::Get(browser_context()) + ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING); + if (!extension) { + return base::unexpected( + ErrorUtils::FormatErrorMessage(kNoExtensionError, extension_id_)); + } + + // Return an error if there is no signed in user. + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(profile_); + AccountInfo account_info = identity_manager->FindExtendedAccountInfo( + identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)); + if (account_info.IsEmpty()) { + return base::unexpected(kUserNotSignedIn); + } + + return base::ok(extension); +} + +void DeveloperPrivateUploadExtensionToAccountFunction::UploadExtensionToAccount( + const Extension& extension) { + AccountExtensionTracker::Get(browser_context()) + ->OnAccountUploadInitiatedForExtension(extension.id()); + ExtensionSyncService::Get(browser_context()) + ->SyncExtensionChangeIfNeeded(extension); +} + void DeveloperPrivateUploadExtensionToAccountFunction::OnDialogAccepted() { // We cannot proceed if the `browser_context` is not valid as the relevant // classes needed to upload the extension will not exist. @@ -3037,8 +3058,14 @@ return; } - // TODO(crbug.com/381127648): Upload the associated `extension_id_` to the - // user's account once the dialog is accepted. + auto result = VerifyExtensionAndSigninState(); + if (!result.has_value()) { + Respond(Error(result.error())); + return; + } + const Extension* extension = *result; + + UploadExtensionToAccount(*extension); Respond(NoArguments()); }
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h index 8211ded..2d199cdd 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -1128,6 +1128,13 @@ ResponseAction Run() override; + // Verify that the extension to be uploaded exists and that there's a signed + // in user. Returns the extension if successful, otherwise returns an error. + base::expected<const Extension*, std::string> VerifyExtensionAndSigninState(); + + // Uploads the given `extension` to the user's account. + void UploadExtensionToAccount(const Extension& extension); + // A callback function to run when the user accepts the action dialog. void OnDialogAccepted(); @@ -1137,6 +1144,8 @@ // The ID of the extension to be uploaded. ExtensionId extension_id_; + raw_ptr<Profile> profile_; + // If true, immediately accepts the keep dialog by running the callback. std::optional<bool> accept_bubble_for_testing_; };
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc index 29047e1..fd5a44d 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -3473,6 +3473,12 @@ return identity_test_env_profile_adaptor_->identity_test_env(); } + AccountExtensionTracker::AccountExtensionType GetAccountExtensionType( + const ExtensionId& extension_id) { + return AccountExtensionTracker::Get(profile())->GetAccountExtensionType( + extension_id); + } + bool CanUploadToAccount(const Extension& extension) { return AccountExtensionTracker::Get(profile())->CanUploadAsAccountExtension( extension); @@ -3586,7 +3592,8 @@ unsyncable_extension->id())); } -TEST_F(DeveloperPrivateApiTransportModeUnitTest, UploadExtensionToAccount) { +TEST_F(DeveloperPrivateApiTransportModeUnitTest, + UploadExtensionToAccount_Cancelled) { // Add a syncable extension. auto syncable_extension = LoadSyncableExtension("ext"); @@ -3625,6 +3632,76 @@ syncable_extension->id())); } +TEST_F(DeveloperPrivateApiTransportModeUnitTest, + UploadExtensionToAccount_Accepted) { + // Add a syncable extension. + auto extension = LoadSyncableExtension("ext"); + ItemStatePrefsChangedObserver test_observer = + StartListeningForEvent(extension->id()); + + // Sign the user in without full sync. + SimulateExplicitSignIn(); + + // Now simulate an initial sync with no extensions in the user's account. This + // is needed to spin up the sync service so uploaded extensions actually get + // synced. + SimulateInitialSync({}); + + // Wait for the associated prefs changed event from the initial sync so the + // event that gets emitted later from an extension upload can be properly + // picked up. + test_observer.WaitForEvent(); + + // The syncable extension can be uploaded and should be a local extension. + EXPECT_TRUE(CanUploadToAccount(*extension)); + EXPECT_EQ(AccountExtensionTracker::AccountExtensionType::kLocal, + GetAccountExtensionType(extension->id())); + + // On this machine, there should be no extensions syncing. + { + syncer::SyncDataList list = + ExtensionSyncService::Get(profile())->GetAllSyncDataForTesting( + syncer::EXTENSIONS); + EXPECT_TRUE(list.empty()); + } + + // Now upload the extension and accept the dialog to proceed with the upload. + base::Value::List args; + args.Append(extension->id()); + auto upload_function = base::MakeRefCounted< + api::DeveloperPrivateUploadExtensionToAccountFunction>(); + upload_function->set_source_context_type(mojom::ContextType::kWebUi); + upload_function->accept_bubble_for_testing(true); + + test_observer.Reset(); + EXPECT_TRUE(RunFunction(upload_function, args)); + + // Wait for the prefs changed update and verify that the extension is no + // longer uploadable after being uploaded. + test_observer.WaitForEvent(); + auto info = test_observer.event_info(); + EXPECT_FALSE(info.can_upload_as_account_extension); + EXPECT_FALSE(CanUploadToAccount(*extension)); + + // Double check that the extension is now an account extension. + EXPECT_EQ( + AccountExtensionTracker::AccountExtensionType::kAccountInstalledLocally, + GetAccountExtensionType(extension->id())); + + // Verify that the extension is now syncing from the sync service. + { + syncer::SyncDataList list = + ExtensionSyncService::Get(profile())->GetAllSyncDataForTesting( + syncer::EXTENSIONS); + ASSERT_EQ(1u, list.size()); + std::unique_ptr<ExtensionSyncData> data = + ExtensionSyncData::CreateFromSyncData(list[0]); + ASSERT_TRUE(data.get()); + EXPECT_EQ(extension->id(), data->id()); + EXPECT_TRUE(data->enabled()); + } +} + // Test that an extension is uploadable when the user signs into transport mode // and the extension is not in the user's sync data. TEST_F(DeveloperPrivateApiTransportModeUnitTest, ExtensionUploadableOnSignIn) {
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc index 6cb10f3..202e6ba 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -913,7 +913,7 @@ ExtensionInfoList list = std::move(list_); list_.clear(); std::move(callback_).Run(std::move(list)); - // WARNING: |this| is possibly deleted after this line! + // WARNING: `this` is possibly deleted after this line! } }
diff --git a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc index 6fa7c54..bc1538b 100644 --- a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc +++ b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc
@@ -3,11 +3,7 @@ // found in the LICENSE file. #include "base/files/file_path.h" -#include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" @@ -26,6 +22,15 @@ #include "net/dns/mock_host_resolver.h" #include "testing/gmock/include/gmock/gmock.h" +#if BUILDFLAG(IS_ANDROID) +#include "chrome/browser/extensions/extension_platform_browsertest.h" +#else +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/ui_test_utils.h" +#endif + namespace extensions { namespace { @@ -37,7 +42,13 @@ content::RenderProcessHost::SetMaxRendererProcessCount(1); } -class CrossOriginIsolationTest : public ExtensionBrowserTest { +#if BUILDFLAG(IS_ANDROID) +using CrossOriginIsolationTestBase = ExtensionPlatformBrowserTest; +#else +using CrossOriginIsolationTestBase = ExtensionBrowserTest; +#endif + +class CrossOriginIsolationTest : public CrossOriginIsolationTestBase { public: CrossOriginIsolationTest() = default; ~CrossOriginIsolationTest() override = default; @@ -45,7 +56,7 @@ CrossOriginIsolationTest& operator=(const CrossOriginIsolationTest&) = delete; void SetUpOnMainThread() override { - ExtensionBrowserTest::SetUpOnMainThread(); + CrossOriginIsolationTestBase::SetUpOnMainThread(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(embedded_test_server()->Start()); } @@ -58,12 +69,16 @@ const char* test_js = ""; bool is_platform_app = false; }; + using CrossOriginIsolationTestBase::LoadExtension; const Extension* LoadExtension(TestExtensionDir& dir, const Options& options) { CHECK(options.coep_value); CHECK(options.coop_value); CHECK(!options.is_platform_app || !options.use_service_worker) << "Platform apps cannot use 'service_worker' key."; +#if BUILDFLAG(IS_ANDROID) + CHECK(!options.is_platform_app) << "Android does not support platform apps"; +#endif static constexpr char kManifestTemplate[] = R"( { @@ -122,7 +137,7 @@ dir.WriteFile(FILE_PATH_LITERAL("test.html"), "<script src='test.js'></script>"); dir.WriteFile(FILE_PATH_LITERAL("test.js"), options.test_js); - return ExtensionBrowserTest::LoadExtension(dir.UnpackedPath()); + return LoadExtension(dir.UnpackedPath()); } bool IsCrossOriginIsolated(content::RenderFrameHost* host) { @@ -232,7 +247,9 @@ image_url_without_host_permissions)); } +#if !BUILDFLAG(IS_ANDROID) // Tests that platform apps can opt into cross origin isolation. +// Not run on desktop Android because Android does not support platform apps. IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, CrossOriginIsolation_PlatformApps) { RestrictProcessCount(); @@ -267,6 +284,9 @@ // Tests that a web accessible frame from a cross origin isolated extension is // not cross origin isolated. +// TODO(https://crbug.com/388110291): Port to desktop Android. The URL of the +// extension_iframe is "about:blank#blocked" for unclear reasons. Also, the +// chrome.browserAction API is not yet supported. IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, WebAccessibleFrame) { RestrictProcessCount(); @@ -281,16 +301,17 @@ EXPECT_TRUE(IsCrossOriginIsolated(coi_background_render_frame_host)); GURL extension_test_url = coi_extension->GetResourceURL("test.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), extension_test_url)); - content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE( + content::NavigateToURL(GetActiveWebContents(), extension_test_url)); + content::WebContents* web_contents = GetActiveWebContents(); EXPECT_TRUE(IsCrossOriginIsolated(web_contents->GetPrimaryMainFrame())); EXPECT_EQ(web_contents->GetPrimaryMainFrame()->GetProcess(), coi_background_render_frame_host->GetProcess()); // Load test.html as a web accessible resource inside a web frame. - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL("/iframe_blank.html"))); + ASSERT_TRUE(content::NavigateToURL( + GetActiveWebContents(), + embedded_test_server()->GetURL("/iframe_blank.html"))); ASSERT_TRUE( content::NavigateIframeToURL(web_contents, "test", extension_test_url)); @@ -402,6 +423,7 @@ } } } +#endif // !BUILDFLAG(IS_ANDROID) // Test that an extension service worker for a cross origin isolated extension // is not cross origin isolated. See crbug.com/1131404. @@ -425,8 +447,10 @@ EXPECT_TRUE(ready_listener.WaitUntilSatisfied()); GURL extension_test_url = coi_extension->GetResourceURL("test.html"); + ASSERT_TRUE( + content::NavigateToURL(GetActiveWebContents(), extension_test_url)); content::RenderFrameHost* extension_tab = - ui_test_utils::NavigateToURL(browser(), extension_test_url); + content::ConvertToRenderFrameHost(GetActiveWebContents()); ASSERT_TRUE(extension_tab); // The service worker should be active since it's waiting for a response to @@ -462,6 +486,7 @@ coi_extension, service_worker_process->GetDeprecatedID(), url)); } +#if !BUILDFLAG(IS_ANDROID) // Tests certain extension APIs which retrieve in-process extension windows. // Test these for a cross origin isolated extension with non-cross origin // isolated contexts. @@ -544,6 +569,8 @@ // Tests extension messaging between cross origin isolated and // non-cross-origin-isolated frames of an extension. +// TODO(https://crbug.com/388110291): Port to desktop Android when +// chrome.runtime.sendMessage() works there. IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, ExtensionMessaging_Frames) { RestrictProcessCount(); @@ -633,6 +660,8 @@ // Tests extension messaging between a cross origin isolated extension frame and // the extension service worker which is not cross origin isolated (and hence in // a different process). +// TODO(https://crbug.com/388110291): Port to desktop Android when +// chrome.runtime.sendMessage() works there. IN_PROC_BROWSER_TEST_F(CrossOriginIsolationTest, ExtensionMessaging_ServiceWorker) { RestrictProcessCount(); @@ -710,6 +739,8 @@ // Verify extension resource access if it's in an iframe. Regression test for // crbug.com/1343610. +// TODO(https://crbug.com/388110291): Port to desktop Android when we have a +// cross-platform replacement for NavigateParams. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ExtensionResourceInIframe) { EXPECT_TRUE(embedded_test_server()->Start()); @@ -809,6 +840,7 @@ EXPECT_EQ(target, iframe->GetLastCommittedURL()); } } +#endif // !BUILDFLAG(IS_ANDROID) } // namespace } // namespace extensions
diff --git a/chrome/browser/extensions/extension_platform_browsertest.cc b/chrome/browser/extensions/extension_platform_browsertest.cc index e395bc0e..c02b52e8 100644 --- a/chrome/browser/extensions/extension_platform_browsertest.cc +++ b/chrome/browser/extensions/extension_platform_browsertest.cc
@@ -21,7 +21,12 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/ui_test_utils.h" #else +#include "base/check.h" +#include "chrome/browser/android/tab_android.h" #include "chrome/browser/extensions/desktop_android/desktop_android_extension_system.h" #include "chrome/browser/extensions/platform_test_extension_loader.h" #include "chrome/browser/ui/android/tab_model/tab_model.h" @@ -332,6 +337,37 @@ #endif } +content::RenderFrameHost* ExtensionPlatformBrowserTest::NavigateToURLInNewTab( + const GURL& url) { +#if !BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS) + return ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); +#else + content::WebContents* active_web_contents = GetActiveWebContents(); + TabModel* tab_model = + TabModelList::GetTabModelForWebContents(active_web_contents); + TabAndroid* parent = TabAndroid::FromWebContents(active_web_contents); + std::unique_ptr<content::WebContents> contents = content::WebContents::Create( + content::WebContents::CreateParams(profile())); + content::WebContents* new_web_contents = contents.release(); + tab_model->CreateTab(parent, new_web_contents, /*select=*/true); + // Navigate and block until navigation finishes. + CHECK(content::NavigateToURL(new_web_contents, url)); + return content::ConvertToRenderFrameHost(new_web_contents); +#endif +} + +int ExtensionPlatformBrowserTest::GetTabCount() { +#if !BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS) + return browser()->tab_strip_model()->count(); +#else + TabModel* tab_model = + TabModelList::GetTabModelForWebContents(GetActiveWebContents()); + return tab_model->GetTabCount(); +#endif +} + void ExtensionPlatformBrowserTest::SetUpTestProtocolHandler() { test_protocol_handler_ = base::BindRepeating( &ExtensionProtocolTestResourcesHandler, GetTestResourcesParentDir());
diff --git a/chrome/browser/extensions/extension_platform_browsertest.h b/chrome/browser/extensions/extension_platform_browsertest.h index 030ae9b..cd85f1bb 100644 --- a/chrome/browser/extensions/extension_platform_browsertest.h +++ b/chrome/browser/extensions/extension_platform_browsertest.h
@@ -16,6 +16,7 @@ class Profile; namespace content { +class RenderFrameHost; class WebContents; } @@ -70,6 +71,12 @@ // `profile`, blocking until the navigation finishes. void PlatformOpenURLOffTheRecord(Profile* profile, const GURL& url); + // Opens `url` in a new tab, blocking until the navigation finishes. + content::RenderFrameHost* NavigateToURLInNewTab(const GURL& url); + + // Returns the number of tabs in the current window. + int GetTabCount(); + // Sets up `test_protocol_handler_` so that // chrome-extensions://<extension_id>/_test_resources/foo maps to // chrome/test/data/extensions/foo.
diff --git a/chrome/browser/extensions/extension_platform_browsertest_browsertest.cc b/chrome/browser/extensions/extension_platform_browsertest_browsertest.cc new file mode 100644 index 0000000..b0da154 --- /dev/null +++ b/chrome/browser/extensions/extension_platform_browsertest_browsertest.cc
@@ -0,0 +1,19 @@ +// 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/extensions/extension_platform_browsertest.h" + +#include "content/public/test/browser_test.h" + +namespace extensions { +namespace { + +IN_PROC_BROWSER_TEST_F(ExtensionPlatformBrowserTest, NavigateToURLInNewTab) { + ASSERT_EQ(GetTabCount(), 1); + EXPECT_TRUE(NavigateToURLInNewTab(GURL("about:blank"))); + EXPECT_EQ(GetTabCount(), 2); +} + +} // namespace +} // namespace extensions
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 392218b2..007ed46 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc
@@ -226,18 +226,14 @@ void ExtensionService::BlocklistExtensionForTest( const std::string& extension_id) { - blocklist_prefs::SetSafeBrowsingExtensionBlocklistState( - extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, - extension_prefs_); - OnBlocklistStateAdded(extension_id); + extension_registrar_.BlocklistExtensionForTest(extension_id); // IN-TEST } void ExtensionService::GreylistExtensionForTest( const std::string& extension_id, const BitMapBlocklistState& state) { - blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(extension_id, state, - extension_prefs_); - OnGreylistStateAdded(extension_id, state); + extension_registrar_.GreylistExtensionForTest(extension_id, + state); // IN-TEST } bool ExtensionService::OnExternalExtensionUpdateUrlFound( @@ -958,86 +954,21 @@ } void ExtensionService::OnGreylistStateRemoved(const std::string& extension_id) { - bool is_on_sb_list = (blocklist_prefs::GetSafeBrowsingExtensionBlocklistState( - extension_id, extension_prefs_) != - BitMapBlocklistState::NOT_BLOCKLISTED); - bool is_on_omaha_list = - blocklist_prefs::HasAnyOmahaGreylistState(extension_id, extension_prefs_); - if (is_on_sb_list || is_on_omaha_list) { - return; - } - // Clear all acknowledged states so the extension will still get disabled if - // it is added to the greylist again. - blocklist_prefs::ClearAcknowledgedGreylistStates(extension_id, - extension_prefs_); - RemoveDisableReasonAndMaybeEnable(extension_id, - disable_reason::DISABLE_GREYLIST); + extension_registrar_.OnGreylistStateRemoved(extension_id); } void ExtensionService::OnGreylistStateAdded(const std::string& extension_id, BitMapBlocklistState new_state) { -#if DCHECK_IS_ON() - bool has_new_state_on_sb_list = - (blocklist_prefs::GetSafeBrowsingExtensionBlocklistState( - extension_id, extension_prefs_) == new_state); - bool has_new_state_on_omaha_list = blocklist_prefs::HasOmahaBlocklistState( - extension_id, new_state, extension_prefs_); - DCHECK(has_new_state_on_sb_list || has_new_state_on_omaha_list); -#endif - if (blocklist_prefs::HasAcknowledgedBlocklistState(extension_id, new_state, - extension_prefs_)) { - // If the extension is already acknowledged, don't disable it again - // because it can be already re-enabled by the user. This could happen if - // the extension is added to the SafeBrowsing blocklist, and then - // subsequently marked by Omaha. In this case, we don't want to disable the - // extension twice. - return; - } - - // Set the current greylist states to acknowledge immediately because the - // extension is disabled silently. Clear the other acknowledged state because - // when the state changes to another greylist state in the future, we'd like - // to disable the extension again. - blocklist_prefs::UpdateCurrentGreylistStatesAsAcknowledged(extension_id, - extension_prefs_); - DisableExtension(extension_id, disable_reason::DISABLE_GREYLIST); + extension_registrar_.OnGreylistStateAdded(extension_id, new_state); } void ExtensionService::OnBlocklistStateRemoved( const std::string& extension_id) { - if (blocklist_prefs::IsExtensionBlocklisted(extension_id, extension_prefs_)) { - return; - } - - // Clear acknowledged state. - blocklist_prefs::RemoveAcknowledgedBlocklistState( - extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, - extension_prefs_); - - scoped_refptr<const Extension> extension = - registry_->blocklisted_extensions().GetByID(extension_id); - DCHECK(extension); - registry_->RemoveBlocklisted(extension_id); - AddExtension(extension.get()); + extension_registrar_.OnBlocklistStateRemoved(extension_id); } void ExtensionService::OnBlocklistStateAdded(const std::string& extension_id) { - DCHECK( - blocklist_prefs::IsExtensionBlocklisted(extension_id, extension_prefs_)); - // The extension was already acknowledged by the user, it should already be in - // the unloaded state. - if (blocklist_prefs::HasAcknowledgedBlocklistState( - extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, - extension_prefs_)) { - DCHECK(base::Contains(registry_->blocklisted_extensions().GetIDs(), - extension_id)); - return; - } - - scoped_refptr<const Extension> extension = - registry_->GetInstalledExtension(extension_id); - registry_->AddBlocklisted(extension); - UnloadExtension(extension_id, UnloadedExtensionReason::BLOCKLIST); + extension_registrar_.OnBlocklistStateAdded(extension_id); } void ExtensionService::RemoveDisableReasonAndMaybeEnable( @@ -1054,7 +985,6 @@ void ExtensionService::DisableExtension(const std::string& extension_id, int disable_reasons) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); extension_registrar_.DisableExtension(extension_id, disable_reasons); } @@ -1062,7 +992,6 @@ const Extension* source_extension, const std::string& extension_id, disable_reason::DisableReason disable_reasons) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); extension_registrar_.DisableExtensionWithSource( source_extension, extension_id, disable_reasons); }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index c3c46dd..394555c 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -8459,6 +8459,11 @@ "expiry_milestone": 140 }, { + "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 + }, + { "name": "tab-strip-group-reorder-android", "owners": [ "skavuluru@google.com", "zheliooo@google.com", "nemco@google.com", "clank-large-form-factors@google.com" ], "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index dc6c3d8..d2af346 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -4813,6 +4813,14 @@ "Collapsed groups can be clicked again to expand. Synced tab groups will " "immediately be collapsed."; +const char kTabStripGroupDragDropAndroidName[] = + "Tab Strip Group Drag Drop Android"; +const char kTabStripGroupDragDropAndroidDescription[] = + "Enables long-pressing on tab strip tab group indicators to start " + "drag-and-drop. Users can drag the tab group off the tab strip and drop it " + "into another window in split-screen mode or create a new window by " + "dropping it on the edge of Chrome."; + const char kTabStripGroupReorderAndroidName[] = "Tab Strip Group Reorder"; const char kTabStripGroupReorderAndroidDescription[] = "Enables long-pressing on tab strip tab group indicators to enter reorder "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 401fd4d9..d2734b8 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2823,6 +2823,9 @@ extern const char kTabStripGroupCollapseAndroidName[]; extern const char kTabStripGroupCollapseAndroidDescription[]; +extern const char kTabStripGroupDragDropAndroidName[]; +extern const char kTabStripGroupDragDropAndroidDescription[]; + extern const char kTabStripGroupReorderAndroidName[]; extern const char kTabStripGroupReorderAndroidDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 5d4724c2..4005b7b 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -1041,6 +1041,10 @@ "TabStripGroupCollapseAndroid", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kTabStripGroupDragDropAndroid, + "TabStripGroupDragDropAndroid", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kTabStripGroupReorderAndroid, "TabStripGroupReorderAndroid", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 34aceabc..e69c711 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -177,6 +177,7 @@ BASE_DECLARE_FEATURE(kTabGroupCreationDialogAndroid); BASE_DECLARE_FEATURE(kTabStateFlatBuffer); BASE_DECLARE_FEATURE(kTabStripGroupCollapseAndroid); +BASE_DECLARE_FEATURE(kTabStripGroupDragDropAndroid); BASE_DECLARE_FEATURE(kTabStripGroupReorderAndroid); BASE_DECLARE_FEATURE(kTabStripIncognitoMigration); BASE_DECLARE_FEATURE(kTabStripLayoutOptimization);
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 4e732d3..9ce634f4 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
@@ -530,6 +530,7 @@ "TabGroupSyncAutoOpenKillSwitch"; public static final String TAB_RESUMPTION_MODULE_ANDROID = "TabResumptionModuleAndroid"; public static final String TAB_STRIP_GROUP_COLLAPSE = "TabStripGroupCollapseAndroid"; + 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"; public static final String TAB_STRIP_LAYOUT_OPTIMIZATION = "TabStripLayoutOptimization";
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn index 8381ba1..5af8369fe 100644 --- a/chrome/browser/glic/BUILD.gn +++ b/chrome/browser/glic/BUILD.gn
@@ -60,6 +60,8 @@ "glic_ui.cc", "glic_view.cc", "glic_window_controller.cc", + "glic_window_resize_animation.cc", + "glic_window_resize_animation.h", "guest_util.cc", ] public_deps = [ "//chrome/browser:browser_public_dependencies" ] @@ -141,7 +143,6 @@ testonly = true defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] sources = [ - "border_view_browsertest.cc", "glic_browsertest.cc", "glic_policy_browsertest.cc", "guest_util_browsertest.cc", @@ -167,7 +168,10 @@ source_set("interactive_ui_tests") { testonly = true defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] - sources = [ "glic_window_controller_ui_test.cc" ] + sources = [ + "border_view_browsertest.cc", + "glic_window_controller_ui_test.cc", + ] deps = [ ":glic", "//chrome/browser",
diff --git a/chrome/browser/glic/border_view.cc b/chrome/browser/glic/border_view.cc index 4784045f..e8c83c5 100644 --- a/chrome/browser/glic/border_view.cc +++ b/chrome/browser/glic/border_view.cc
@@ -47,18 +47,11 @@ } // static. -void BorderView::CancelAllAnimationsForProfile(Profile* profile) { - std::vector<Browser*> browsers = chrome::FindAllBrowsersWithProfile(profile); - for (auto* browser : browsers) { - if (!browser || !browser->window()) { - // Unittests, or the View tree is torn down. - continue; - } - // Border is null if the feature is disabled for `profile`. - if (auto* border = browser->GetBrowserView().glic_border()) { - border->CancelAnimation(); - } - } +void BorderView::CancelAnimation(BrowserWindowInterface* browser_interface) { + BorderView* glic_border = + static_cast<Browser*>(browser_interface)->GetBrowserView().glic_border(); + CHECK(glic_border); + glic_border->CancelAnimation(); } BorderView::BorderView() = default; @@ -136,6 +129,8 @@ compositor_->RemoveAnimationObserver(this); compositor_ = nullptr; + progress_ = 0.f; + first_frame_time_ = base::TimeTicks(); // `DestroyLayer()` schedules another paint to repaint the affected area by // the destroyed layer.
diff --git a/chrome/browser/glic/border_view.h b/chrome/browser/glic/border_view.h index 4714478..32b12fe 100644 --- a/chrome/browser/glic/border_view.h +++ b/chrome/browser/glic/border_view.h
@@ -10,7 +10,7 @@ #include "ui/views/metadata/view_factory.h" #include "ui/views/view.h" -class Profile; +class BrowserWindowInterface; namespace content { class WebContents; @@ -32,7 +32,7 @@ static BorderView* FindBorderForWebContents( const content::WebContents* web_contents); - static void CancelAllAnimationsForProfile(Profile* profile); + static void CancelAnimation(BrowserWindowInterface* browser_interface); BorderView(); BorderView(const BorderView&) = delete; @@ -50,6 +50,8 @@ void CancelAnimation(); + ui::Compositor* compositor_for_testing() const { return compositor_; } + private: raw_ptr<ui::Compositor> compositor_ = nullptr;
diff --git a/chrome/browser/glic/border_view_browsertest.cc b/chrome/browser/glic/border_view_browsertest.cc index f727ca8b..748cebe 100644 --- a/chrome/browser/glic/border_view_browsertest.cc +++ b/chrome/browser/glic/border_view_browsertest.cc
@@ -9,11 +9,15 @@ #include <math.h> #include "cc/test/pixel_test_utils.h" +#include "chrome/browser/glic/glic_keyed_service_factory.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/tab_strip_region_view.h" +#include "chrome/browser/ui/views/tabs/glic_button.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" -#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/interaction/interactive_browser_test.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -25,7 +29,7 @@ namespace glic { namespace { -class BorderViewBrowserTest : public InProcessBrowserTest { +class BorderViewBrowserTest : public InteractiveBrowserTest { public: BorderViewBrowserTest() { scoped_feature_list_.InitWithFeatures( @@ -67,6 +71,18 @@ return tab_strip_model->GetTabAtIndex(0)->GetContents()->GetViewBounds(); } + // Appends a new tab with `url` to the end of the tabstrip. + void AppendTab(Browser* browser, const GURL& url) { + chrome::AddTabAt(browser, url, /*index=*/-1, /*foreground=*/true); + } + + GlicButton* GetGlicButton(Browser* browser) { + TabStripRegionView* tab_strip_view = + browser->GetBrowserView().tab_strip_region_view(); + EXPECT_TRUE(tab_strip_view); + return tab_strip_view->GetGlicButton(); + } + protected: base::test::ScopedFeatureList scoped_feature_list_; }; @@ -135,7 +151,7 @@ base::TimeTicks timestamp = base::TimeTicks::Now(); - // Note: the following is based on having the animation duration = 2 seconds. + // Note: the following is based on having animation duration = 2 seconds. // timestamp = 0 (now). { border->OnAnimationStep(timestamp); @@ -228,6 +244,131 @@ border->CancelAnimation(); } +// Ensures that the border animation state is reset after canceling the +// animation. +IN_PROC_BROWSER_TEST_F(BorderViewBrowserTest, AnimationStateReset) { + auto* border = browser()->window()->AsBrowserView()->glic_border(); + ASSERT_TRUE(border); + + border->StartAnimation(); + border->OnAnimationStep(base::TimeTicks::Now()); + border->CancelAnimation(); + + ASSERT_FALSE(border->compositor_for_testing()); +} + +// Ensures that the border animation is restarted when tab focus changes. +IN_PROC_BROWSER_TEST_F(BorderViewBrowserTest, FocusedTabChange) { + auto* border = browser()->window()->AsBrowserView()->glic_border(); + ASSERT_TRUE(border); + gfx::Rect capture_rect = GetContentsRectForWindow(browser()); + SkColor background_color = SkColors::kBlack.toSkColor(); + SkColor border_color = + browser()->GetBrowserView().GetColorProvider()->GetColor( + ui::kColorSysPrimary); + + // Mimicking the user journey by clicking the button and having the WebApp set + // the context access indicator status. + auto* const glic_keyed_service = + glic::GlicKeyedServiceFactory::GetGlicKeyedService( + browser()->GetProfile()); + GetGlicButton(browser())->LaunchUI(); + glic_keyed_service->SetContextAccessIndicator(true); + + base::TimeTicks timestamp = base::TimeTicks::Now(); + + // Note: the following is based on having animation duration = 2 seconds. + // timestamp = 0 (now). + { + border->OnAnimationStep(timestamp); + gfx::Canvas canvas(capture_rect.size(), /*image_scale=*/1.0f, + /*is_opaque=*/true); + canvas.DrawColor(background_color); + border->OnPaint(&canvas); + SkBitmap actual_bitmap = canvas.GetBitmap(); + + SkBitmap expected_bitmap = ConstructExpectedBitmap( + capture_rect.size(), + /*border_color=*/border_color, + /*center_color=*/background_color, /*border_width=*/2, /*alpha=*/0); + + EXPECT_TRUE(cc::MatchesBitmap(actual_bitmap, expected_bitmap, + cc::ExactPixelComparator())); + } + + // timestamp = 0.5 seconds. + { + timestamp += base::Seconds(0.5); + border->OnAnimationStep(timestamp); + gfx::Canvas canvas(capture_rect.size(), /*image_scale=*/1.0f, + /*is_opaque=*/true); + canvas.DrawColor(background_color); + border->OnPaint(&canvas); + SkBitmap actual_bitmap = canvas.GetBitmap(); + + float progress = sin(0.125 * M_PI); + float border_width = 2 + (8 * progress); + SkBitmap expected_bitmap = ConstructExpectedBitmap( + capture_rect.size(), + /*border_color=*/border_color, + /*center_color=*/background_color, /*border_width=*/border_width, + /*alpha=*/progress); + + EXPECT_TRUE(cc::MatchesBitmap(actual_bitmap, expected_bitmap, + cc::ExactPixelComparator())); + } + + // Changing the active tab. + AppendTab(browser(), GURL(chrome::kChromeUINewTabURL)); + ASSERT_EQ(browser()->tab_strip_model()->active_index(), 1); + + // Since the active tab has changed, the animation should start from the + // beginning. + // timestamp = 6 seconds; second 0 in the current animation. + { + timestamp += base::Seconds(5.5); + border->OnAnimationStep(timestamp); + gfx::Canvas canvas(capture_rect.size(), /*image_scale=*/1.0f, + /*is_opaque=*/true); + canvas.DrawColor(background_color); + border->OnPaint(&canvas); + SkBitmap actual_bitmap = canvas.GetBitmap(); + + SkBitmap expected_bitmap = ConstructExpectedBitmap( + capture_rect.size(), + /*border_color=*/border_color, + /*center_color=*/background_color, /*border_width=*/2, + /*alpha=*/0); + + EXPECT_TRUE(cc::MatchesBitmap(actual_bitmap, expected_bitmap, + cc::ExactPixelComparator())); + } + + // timestamp = 6.5 seconds; second 0.5 in the current animation. + { + timestamp += base::Seconds(0.5); + border->OnAnimationStep(timestamp); + gfx::Canvas canvas(capture_rect.size(), /*image_scale=*/1.0f, + /*is_opaque=*/true); + canvas.DrawColor(background_color); + border->OnPaint(&canvas); + SkBitmap actual_bitmap = canvas.GetBitmap(); + + float progress = sin(0.125 * M_PI); + float border_width = 2 + (8 * progress); + SkBitmap expected_bitmap = ConstructExpectedBitmap( + capture_rect.size(), + /*border_color=*/border_color, + /*center_color=*/background_color, /*border_width=*/border_width, + /*alpha=*/progress); + + EXPECT_TRUE(cc::MatchesBitmap(actual_bitmap, expected_bitmap, + cc::ExactPixelComparator())); + } + + border->CancelAnimation(); +} + namespace { class BorderViewFeatureDisabledBrowserTest : public BorderViewBrowserTest { public:
diff --git a/chrome/browser/glic/glic_border_view_manager.cc b/chrome/browser/glic/glic_border_view_manager.cc index f951e7482..3e16452e 100644 --- a/chrome/browser/glic/glic_border_view_manager.cc +++ b/chrome/browser/glic/glic_border_view_manager.cc
@@ -42,8 +42,7 @@ GlicBorderViewManager::~GlicBorderViewManager() = default; void GlicBorderViewManager::UpdateBorderView() { - BorderView::CancelAllAnimationsForProfile( - Profile::FromBrowserContext(browser_->GetProfile())); + BorderView::CancelAnimation(browser_.get()); auto* const model = browser_->GetTabStripModel(); CHECK(model); const int index = model->GetIndexOfWebContents(focused_tab_.get());
diff --git a/chrome/browser/glic/glic_window_controller.cc b/chrome/browser/glic/glic_window_controller.cc index e056b21..779ac0f 100644 --- a/chrome/browser/glic/glic_window_controller.cc +++ b/chrome/browser/glic/glic_window_controller.cc
@@ -7,6 +7,7 @@ #include "base/check.h" #include "chrome/browser/glic/glic.mojom.h" #include "chrome/browser/glic/glic_view.h" +#include "chrome/browser/glic/glic_window_resize_animation.h" #include "chrome/browser/media/audio_ducker.h" #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" @@ -318,6 +319,10 @@ Close(); } +void GlicWindowController::ResizeFinished() { + window_resize_animation_.reset(); +} + void GlicWindowController::Attach() { // TODO (crbug.com/388917542) Determine which browser to attach to. Currently // attaches to the last focused glic-compatible browser. @@ -376,8 +381,11 @@ return false; } - glic_window_widget_->SetSize(size); - GetGlicView()->web_view()->SetSize(size); + window_resize_animation_.reset(); + window_resize_animation_ = std::make_unique<GlicWindowResizeAnimation>( + glic_window_widget_.get(), GetGlicView(), size, + /*duration=*/base::Milliseconds(0), + base::BindOnce(&GlicWindowController::ResizeFinished, GetWeakPtr())); return true; } @@ -403,8 +411,7 @@ if (!glic_window_widget_) { return; } - glic_window_widget_->CloseWithReason( - views::Widget::ClosedReason::kCloseButtonClicked); + window_resize_animation_.reset(); glic_widget_observer_.reset(); window_event_observer_.reset(); browser_close_subscription_.reset();
diff --git a/chrome/browser/glic/glic_window_controller.h b/chrome/browser/glic/glic_window_controller.h index cf1c3a7..03f7b8e 100644 --- a/chrome/browser/glic/glic_window_controller.h +++ b/chrome/browser/glic/glic_window_controller.h
@@ -31,6 +31,7 @@ } // namespace class GlicView; +class GlicWindowResizeAnimation; // Class for Glic window controller. Owned by the Glic profile keyed-service. // This gets created when the Glic window needs to be shown and it owns the Glic @@ -191,6 +192,9 @@ // When the attached browser is closed, this is invoked so we can clean up. void AttachedBrowserDidClose(BrowserWindowInterface* browser); + // Called when the programmatic resize has finished. + void ResizeFinished(); + AttachedTargetWidgetObserver attached_target_widget_observer_{this}; base::WeakPtr<Browser> attached_browser_; @@ -212,6 +216,7 @@ std::unique_ptr<ContentsAndProfileKeepAlive> contents_; std::unique_ptr<views::Widget> glic_window_widget_; + std::unique_ptr<GlicWindowResizeAnimation> window_resize_animation_; bool glic_window_widget_visible_ = false; // Indicates `Show()` has been called, but not `FinishShow()`.
diff --git a/chrome/browser/glic/glic_window_resize_animation.cc b/chrome/browser/glic/glic_window_resize_animation.cc new file mode 100644 index 0000000..6056334 --- /dev/null +++ b/chrome/browser/glic/glic_window_resize_animation.cc
@@ -0,0 +1,35 @@ +// 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/glic/glic_window_resize_animation.h" + +#include "base/task/sequenced_task_runner.h" +#include "chrome/browser/glic/glic_view.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/widget/widget.h" + +namespace glic { + +GlicWindowResizeAnimation::GlicWindowResizeAnimation( + views::Widget* widget, + GlicView* view, + gfx::Size new_size, + base::TimeDelta duration, + FinishedCallback finished_callback) + : finished_callback_(std::move(finished_callback)) { + widget->SetSize(new_size); + view->web_view()->SetSize(new_size); + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&GlicWindowResizeAnimation::Finished, + weak_ptr_factory_.GetWeakPtr())); +} + +GlicWindowResizeAnimation::~GlicWindowResizeAnimation() = default; + +void GlicWindowResizeAnimation::Finished() { + // Destroys `this`. + std::move(finished_callback_).Run(); +} + +} // namespace glic
diff --git a/chrome/browser/glic/glic_window_resize_animation.h b/chrome/browser/glic/glic_window_resize_animation.h new file mode 100644 index 0000000..8a75fe9 --- /dev/null +++ b/chrome/browser/glic/glic_window_resize_animation.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_GLIC_GLIC_WINDOW_RESIZE_ANIMATION_H_ +#define CHROME_BROWSER_GLIC_GLIC_WINDOW_RESIZE_ANIMATION_H_ + +#include "base/callback_list.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace views { +class Widget; +} // namespace views + +namespace glic { + +class GlicView; + +// This class controls the animation of the glic window from one size to +// another. It has the following constraints that the caller must enforce: +// * The glic window and glic view must outlive instances of this class. +// * There can be at most 1 animation at any point in time. +// This class will generally override any other changes to window size. +class GlicWindowResizeAnimation { + public: + // The caller is expected to destroy GlicWindowResizeAnimation upon receiving + // FinishedCallback. FinishedCallback is always invoked asynchronously. + using FinishedCallback = base::OnceClosure; + GlicWindowResizeAnimation(views::Widget* widget, + GlicView* view, + gfx::Size new_size, + base::TimeDelta duration, + FinishedCallback finished_callback); + ~GlicWindowResizeAnimation(); + + private: + // Ensures that `finished_callback_` is not called if the instance is + // destroyed after posting the task but before the task is run. + void Finished(); + + FinishedCallback finished_callback_; + base::WeakPtrFactory<GlicWindowResizeAnimation> weak_ptr_factory_{this}; +}; + +} // namespace glic + +#endif // CHROME_BROWSER_GLIC_GLIC_WINDOW_RESIZE_ANIMATION_H_
diff --git a/chrome/browser/headless/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/headless_mode_protocol_browsertest.cc index 8468de2..310c77af 100644 --- a/chrome/browser/headless/headless_mode_protocol_browsertest.cc +++ b/chrome/browser/headless/headless_mode_protocol_browsertest.cc
@@ -310,7 +310,4 @@ HEADLESS_MODE_PROTOCOL_TEST(CreateTargetPosition, "sanity/create-target-position.js") -HEADLESS_MODE_PROTOCOL_TEST(CreateTargetWindowState, - "sanity/create-target-window-state.js") - } // namespace headless
diff --git a/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state-expected.txt b/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state-expected.txt deleted file mode 100644 index f096d356..0000000 --- a/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -Tests Target.createTarget() window state handling. -Expected: normal, actual: normal -Expected: maximized, actual: maximized -Expected: minimized, actual: minimized -Expected: fullscreen, actual: fullscreen \ No newline at end of file
diff --git a/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state.js b/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state.js deleted file mode 100644 index 18babd46..0000000 --- a/chrome/browser/headless/test/data/protocol/sanity/create-target-window-state.js +++ /dev/null
@@ -1,24 +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. -(async function(testRunner) { - const {session, dp} = await testRunner.startBlank( - `Tests Target.createTarget() window state handling.`); - - async function tryCreateTarget(windowState) { - const {targetId} = - (await session.protocol.Target.createTarget( - {'url': 'about:blank', windowState, 'newWindow': true})) - .result; - - const {bounds} = (await dp.Browser.getWindowForTarget({targetId})).result; - testRunner.log(`Expected: ${windowState}, actual: ${bounds.windowState}`); - } - - await tryCreateTarget('normal'); - await tryCreateTarget('maximized'); - await tryCreateTarget('minimized'); - await tryCreateTarget('fullscreen'); - - testRunner.completeTest(); -})
diff --git a/chrome/browser/resources/ash/settings/BUILD.gn b/chrome/browser/resources/ash/settings/BUILD.gn index 56866b9..e9f2dd5e 100644 --- a/chrome/browser/resources/ash/settings/BUILD.gn +++ b/chrome/browser/resources/ash/settings/BUILD.gn
@@ -330,7 +330,6 @@ "os_privacy_page/privacy_hub_subpage.ts", "os_privacy_page/privacy_hub_system_service_row.ts", "os_privacy_page/secure_dns.ts", - "os_privacy_page/secure_dns_dialog.ts", "os_privacy_page/secure_dns_input.ts", "os_privacy_page/smart_privacy_subpage.ts", "os_reset_page/os_powerwash_dialog.ts",
diff --git a/chrome/browser/resources/ash/settings/lazy_load.ts b/chrome/browser/resources/ash/settings/lazy_load.ts index 7aeb8b2..d9fa1c0 100644 --- a/chrome/browser/resources/ash/settings/lazy_load.ts +++ b/chrome/browser/resources/ash/settings/lazy_load.ts
@@ -327,7 +327,6 @@ export {SettingsPrivacyHubMicrophoneSubpage} from './os_privacy_page/privacy_hub_microphone_subpage.js'; export {SettingsPrivacyHubSubpage} from './os_privacy_page/privacy_hub_subpage.js'; export {SecureDnsResolverType, SettingsSecureDnsElement} from './os_privacy_page/secure_dns.js'; -export {SettingsSecureDnsDialogElement} from './os_privacy_page/secure_dns_dialog.js'; export {SecureDnsInputElement} from './os_privacy_page/secure_dns_input.js'; export {SettingsSmartPrivacySubpage} from './os_privacy_page/smart_privacy_subpage.js'; export {OsSettingsPowerwashDialogElement} from './os_reset_page/os_powerwash_dialog.js';
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.html b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.html index 992d4d7..7a04e2b8 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.html +++ b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.html
@@ -27,30 +27,15 @@ } </style> - <template is="dom-if" if="[[isDeprecateDnsDialogEnabled_]]"> - <settings-toggle-button - id="secureDnsToggle" - class="hr" - icon="os-settings:privacy-secure-dns" - pref="{{secureDnsToggle_}}" - label="$i18n{secureDnsOsSettingsTitle}" - sub-label="[[secureDnsDescription_]]" - on-change="onToggleChanged_"> - </settings-toggle-button> - </template> - - <template is="dom-if" if="[[!isDeprecateDnsDialogEnabled_]]"> - <settings-toggle-button - id="secureDnsToggle" - class="hr" - icon="os-settings:privacy-secure-dns" - pref="[[secureDnsToggle_]]" - label="$i18n{secureDnsOsSettingsTitle}" - sub-label="[[secureDnsDescription_]]" - on-settings-boolean-control-change="onDnsToggleClick_" - no-set-pref> - </settings-toggle-button> - </template> + <settings-toggle-button + id="secureDnsToggle" + class="hr" + icon="os-settings:privacy-secure-dns" + pref="{{secureDnsToggle_}}" + label="$i18n{secureDnsOsSettingsTitle}" + sub-label="[[secureDnsDescription_]]" + on-change="onToggleChanged_"> + </settings-toggle-button> <div id="resolverOptions" hidden="[[!showSecureDnsOptions_]]"> <div class="cr-row continuation"> @@ -98,9 +83,3 @@ </secure-dns-input> </div> </div> - - <template is="dom-if" if="[[showDisableDnsDialog_]]" restamp> - <settings-secure-dns-dialog id="warningDialog" - on-close="onDisableDnsDialogClosed_" prefs="{{prefs}}"> - </settings-secure-dns-dialog> - </template>
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.ts b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.ts index 2cff92b..2c09276 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.ts +++ b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns.ts
@@ -21,7 +21,6 @@ import 'chrome://resources/ash/common/cr_elements/md_select.css.js'; import '../controls/settings_toggle_button.js'; import './secure_dns_input.js'; -import './secure_dns_dialog.js'; import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js'; import type {PrivacyPageBrowserProxy, ResolverOption, SecureDnsSetting} from '/shared/settings/privacy_page/privacy_page_browser_proxy.js'; @@ -32,8 +31,6 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js'; - import {getTemplate} from './secure_dns.html.js'; import type {SecureDnsInputElement} from './secure_dns_input.js'; @@ -118,19 +115,6 @@ */ secureDnsInputValue_: String, - showDisableDnsDialog_: { - type: Boolean, - value: false, - }, - - isDeprecateDnsDialogEnabled_: { - type: Boolean, - value() { - return loadTimeData.getBoolean('isDeprecateDnsDialogEnabled'); - }, - readOnly: true, - }, - /** * Boolean to make network default description visible if user selects * Automatic option in DNS dropdown. @@ -164,8 +148,6 @@ private secureDnsInputValue_: string; private browserProxy_: PrivacyPageBrowserProxy = PrivacyPageBrowserProxyImpl.getInstance(); - private showDisableDnsDialog_: boolean; - private readonly isDeprecateDnsDialogEnabled_: boolean; private showNetworkDefaultDescription_: boolean; private showPrivacyPolicyDescription_: boolean; private networkDefaultAriaDescribedBy_: string|null; @@ -189,15 +171,6 @@ 'secure-dns-setting-changed', (setting: SecureDnsSetting) => this.onSecureDnsPrefsChanged_(setting)); - - // This event will only get dispatched from the DNS dialog. If the flag - // kOsSettingsDeprecateDnsDialog is enabled, we don't have to listen for - // the event. - if (!this.isDeprecateDnsDialogEnabled_) { - this.addEventListener( - 'dns-settings-invalid-custom-to-off-mode', - () => this.onSecureDnsPrefChangedToFalse_()); - } }); } @@ -503,34 +476,6 @@ } return undefined; } - - private onDnsToggleClick_(): void { - const secureDnsToggle = - this.shadowRoot!.querySelector<SettingsToggleButtonElement>( - '#secureDnsToggle'); - assert(secureDnsToggle); - if (secureDnsToggle.checked) { - // Always allow turning on the toggle. - this.turnOnDnsToggle_(); - return; - } - - // Do not update the underlying pref value to false. Instead if the user is - // attempting to turn off the toggle, present the warning dialog. - this.showDisableDnsDialog_ = true; - return; - } - - private onDisableDnsDialogClosed_(): void { - // Sync the toggle's value to its pref value. - const secureDnsToggle = - this.shadowRoot!.querySelector<SettingsToggleButtonElement>( - '#secureDnsToggle'); - assert(secureDnsToggle); - secureDnsToggle.resetToPrefValue(); - - this.showDisableDnsDialog_ = false; - } } declare global {
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.html b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.html deleted file mode 100644 index b7b97aa..0000000 --- a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.html +++ /dev/null
@@ -1,30 +0,0 @@ -<style include="settings-shared"> - #secureDnsDialogDescription { - padding-top: 10px; - } - - cr-dialog::part(dialog) { - width: 370px; - } -</style> - -<cr-dialog id="dialog" show-on-attach> - <div slot="title"> - $i18n{secureDnsDialogTitle} - </div> - <div slot="body"> - <div id="secureDnsDialogDescription"> - $i18n{secureDnsDialogBody} - </div> - </div> - <div slot="button-container"> - <cr-button id="cancelButton" class="cancel-button" - on-click="onCancelButtonClicked_"> - $i18n{secureDnsDialogCancel} - </cr-button> - <cr-button id="disableButton" class="action-button" - on-click="onDisableClicked_"> - $i18n{secureDnsDialogTurnOff} - </cr-button> - </div> -</cr-dialog>
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.ts b/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.ts deleted file mode 100644 index 7d25c97..0000000 --- a/chrome/browser/resources/ash/settings/os_privacy_page/secure_dns_dialog.ts +++ /dev/null
@@ -1,80 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview This dialog explains and warns users of the expected outcome - * when turning off secure DNS - */ - -import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js'; -import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js'; - -import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js'; -import {SecureDnsMode} from '/shared/settings/privacy_page/privacy_page_browser_proxy.js'; -import type {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js'; -import type {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js'; -import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; - -import {getTemplate} from './secure_dns_dialog.html.js'; - -export interface SettingsSecureDnsDialogElement { - $: { - dialog: CrDialogElement, - cancelButton: CrButtonElement, - disableButton: CrButtonElement, - }; -} - -const SettingsSecureDnsDialogElementBase = PrefsMixin(PolymerElement); - -export class SettingsSecureDnsDialogElement extends - SettingsSecureDnsDialogElementBase { - static get is() { - return 'settings-secure-dns-dialog' as const; - } - - static get template() { - return getTemplate(); - } - - /** - * Sets the pref's mode to false which will turn off the toggle, and closes - * the dialog. - */ - private onDisableClicked_(): void { - // If the user tries to use their own Secure Custom DNS but enters an - // invalid DNS configuration, the DNS value will not be saved. So in the - // scenario where the user switches from Secure Custom with invalid config - // -> OFF -> Secure Custom with invalid config, the underlying pref value - // will remain OFF. If the user wants to turn DNS to OFF again, the - // secure-dns-setting-changed WebUI event does not get fired if the mode is - // OFF -> OFF, so we have to manually sync the toggle state through a new - // event. the underlying pref's value remains OFF until the DNS config is - // valid. - if (this.getPref('dns_over_https.mode').value === SecureDnsMode.OFF) { - this.dispatchEvent( - new CustomEvent('dns-settings-invalid-custom-to-off-mode', { - bubbles: true, - composed: true, - })); - } else { - this.setPrefValue('dns_over_https.mode', SecureDnsMode.OFF); - } - - this.$.dialog.close(); - } - - private onCancelButtonClicked_(): void { - this.$.dialog.close(); - } -} - -declare global { - interface HTMLElementTagNameMap { - [SettingsSecureDnsDialogElement.is]: SettingsSecureDnsDialogElement; - } -} - -customElements.define( - SettingsSecureDnsDialogElement.is, SettingsSecureDnsDialogElement);
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn index 6483acd2..ef812d81 100644 --- a/chrome/browser/resources/extensions/BUILD.gn +++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -72,9 +72,6 @@ "runtime_hosts_dialog.html.ts", "runtime_hosts_dialog.ts", "service.ts", - "shortcut_input.html.ts", - "shortcut_input.ts", - "shortcut_util.ts", "sidebar.html.ts", "sidebar.ts", "site_permissions/site_permissions.html.ts", @@ -126,7 +123,6 @@ "runtime_host_permissions.css", "shared_style.css", "shared_vars.css", - "shortcut_input.css", "sidebar.css", "site_permissions/site_permissions.css", "site_permissions/site_permissions_by_site.css", @@ -152,6 +148,7 @@ ts_deps = [ "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_components/cr_shortcut_input:build_ts", "//ui/webui/resources/cr_components/managed_footnote:build_ts", "//ui/webui/resources/cr_elements:build_ts", "//ui/webui/resources/js:build_ts",
diff --git a/chrome/browser/resources/extensions/extensions.ts b/chrome/browser/resources/extensions/extensions.ts index ea5661d..937e905c 100644 --- a/chrome/browser/resources/extensions/extensions.ts +++ b/chrome/browser/resources/extensions/extensions.ts
@@ -36,8 +36,6 @@ export {ExtensionsRuntimeHostPermissionsElement} from './runtime_host_permissions.js'; export {ExtensionsRuntimeHostsDialogElement, getMatchingUserSpecifiedSites, getPatternFromSite} from './runtime_hosts_dialog.js'; export {Service, ServiceInterface} from './service.js'; -export {ExtensionsShortcutInputElement} from './shortcut_input.js'; -export {isValidKeyCode, Key, keystrokeToString} from './shortcut_util.js'; export {ExtensionsSidebarElement} from './sidebar.js'; export {ExtensionsSitePermissionsElement} from './site_permissions/site_permissions.js'; export {ExtensionsSitePermissionsBySiteElement} from './site_permissions/site_permissions_by_site.js';
diff --git a/chrome/browser/resources/extensions/keyboard_shortcuts.html.ts b/chrome/browser/resources/extensions/keyboard_shortcuts.html.ts index 3e9d479..39fded7 100644 --- a/chrome/browser/resources/extensions/keyboard_shortcuts.html.ts +++ b/chrome/browser/resources/extensions/keyboard_shortcuts.html.ts
@@ -19,10 +19,15 @@ ${item.commands.map(command => html` <div class="command-entry"> <span class="command-name">${command.description}</span> - <extensions-shortcut-input .delegate="${this.delegate}" - .item="${item}" .shortcut="${command.keybinding}" - .command="${command}"> - </extensions-shortcut-input> + <cr-shortcut-input .shortcut="${command.keybinding}" + input-aria-label="${this.i18n('editShortcutInputLabel', + command.description, item.name)}" + edit-button-aria-label="${this.i18n('editShortcutButtonLabel', + command.description, item.name)}" + @input-capture-change="${this.onInputCaptureChange_}" + @shortcut-updated="${this.onShortcutUpdated_.bind(this, item.id, + command.name)}"> + </cr-shortcut-input> <select class="md-select" @change="${this.onScopeChanged_}" data-extension-id="${item.id}" data-command-name="${command.name}"
diff --git a/chrome/browser/resources/extensions/keyboard_shortcuts.ts b/chrome/browser/resources/extensions/keyboard_shortcuts.ts index 5fe2ec7..1c42c6d 100644 --- a/chrome/browser/resources/extensions/keyboard_shortcuts.ts +++ b/chrome/browser/resources/extensions/keyboard_shortcuts.ts
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import './shortcut_input.js'; +import 'chrome://resources/cr_components/cr_shortcut_input/cr_shortcut_input.js'; import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -43,6 +43,16 @@ this.addEventListener('view-enter-start', this.onViewEnter_); } + protected onInputCaptureChange_(event: CustomEvent<boolean>) { + this.delegate.setShortcutHandlingSuspended(event.detail); + } + + protected onShortcutUpdated_( + itemId: string, commandName: string, event: CustomEvent<string>) { + this.delegate.updateExtensionCommandKeybinding( + itemId, commandName, event.detail); + } + private onViewEnter_() { chrome.metricsPrivate.recordUserAction('Options_ExtensionCommands'); }
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java index f9331a0..8657473 100644 --- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java +++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinator.java
@@ -79,6 +79,7 @@ DesktopWindowHeuristicResult.UNKNOWN; private @WindowingMode int mWindowingMode = WindowingMode.UNKNOWN; private int mKeyboardInset; + private int mNavBarInset; /** * Instantiate the coordinator to handle drawing the tab strip into the captionBar area. @@ -110,7 +111,7 @@ mRootView = rootView; mBrowserControlsVisibilityDelegate = browserControlsVisibilityDelegate; mInsetObserver = insetObserver; - mInsetObserver.addInsetsConsumer(this, InsetConsumerSource.APP_HEADER_COORDINATOR_IME); + mInsetObserver.addInsetsConsumer(this, InsetConsumerSource.APP_HEADER_COORDINATOR_BOTTOM); mInsetsController = mRootView.getWindowInsetsController(); mActivityLifecycleDispatcher = activityLifecycleDispatcher; mActivityLifecycleDispatcher.register(this); @@ -313,7 +314,10 @@ private boolean maybeUpdateRootViewBottomPadding() { int rootViewBottomPadding = mRootView.getPaddingBottom(); // Pad the root view with IME bottom insets only if E2E is active. - int bottomInset = FALSE.equals(mEdgeToEdgeStateProvider.get()) ? 0 : mKeyboardInset; + int bottomInset = + FALSE.equals(mEdgeToEdgeStateProvider.get()) + ? 0 + : Math.max(mKeyboardInset, mNavBarInset); // If the root view is padded as needed already, return early. if (rootViewBottomPadding == bottomInset) return bottomInset != 0; @@ -358,12 +362,15 @@ public WindowInsetsCompat onApplyWindowInsets( @NonNull View view, @NonNull WindowInsetsCompat windowInsetsCompat) { mKeyboardInset = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom; + mNavBarInset = + windowInsetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom; boolean resizedRootView = maybeUpdateRootViewBottomPadding(); if (!resizedRootView) return windowInsetsCompat; // Consume IME insets if the root view has been adjusted. return new WindowInsetsCompat.Builder(windowInsetsCompat) .setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE) + .setInsets(WindowInsetsCompat.Type.navigationBars(), Insets.NONE) .build(); } }
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java index 15a86dc..abe720c7 100644 --- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java +++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/AppHeaderCoordinatorUnitTest.java
@@ -72,6 +72,8 @@ private static final Rect WIDEST_UNOCCLUDED_RECT = new Rect(LEFT_BLOCK, 0, WINDOW_WIDTH - RIGHT_BLOCK, HEADER_HEIGHT); private static final int KEYBOARD_INSET = 736; + private static final int NAV_BAR_INSET = 128; + private static final int UNSPECIFIED_INSET = -1; private static final int APPEARANCE_LIGHT_CAPTION_BARS = 1 << 8; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -345,7 +347,7 @@ } @Test - public void noImeInsets() { + public void noImeOrNavBarInsets() { // Simulate switching to desktop windowing mode, without any bottom insets. setupWithLeftAndRightBoundingRect(); notifyInsetsRectObserver(); @@ -358,7 +360,7 @@ /* error= */ "DesktopWindowing should exit when no insets is supplied."); // Simulate overlapping keyboard. - var insets = applyWindowInsets(KEYBOARD_INSET); + var insets = applyWindowInsets(KEYBOARD_INSET, UNSPECIFIED_INSET); assertNotEquals( "Ime insets should not be consumed when root view is not adjusted.", Insets.NONE, @@ -368,7 +370,7 @@ // Simulate switching to desktop windowing mode. setupWithLeftAndRightBoundingRect(); notifyInsetsRectObserver(); - insets = applyWindowInsets(KEYBOARD_INSET); + insets = applyWindowInsets(KEYBOARD_INSET, UNSPECIFIED_INSET); assertEquals( "Ime insets should be consumed when root view is bottom-padded.", Insets.NONE, @@ -382,7 +384,7 @@ // Simulate switching out of desktop windowing mode. setupWithNoCaptionInsets(); notifyInsetsRectObserver(); - insets = applyWindowInsets(KEYBOARD_INSET); + insets = applyWindowInsets(KEYBOARD_INSET, UNSPECIFIED_INSET); assertNotEquals( "Ime insets should not be consumed when root view is not adjusted.", Insets.NONE, @@ -398,7 +400,7 @@ notifyInsetsRectObserver(); // Simulate overlapping keyboard. - var insets = applyWindowInsets(KEYBOARD_INSET); + var insets = applyWindowInsets(KEYBOARD_INSET, UNSPECIFIED_INSET); assertEquals( "Ime insets should be consumed when root view is adjusted.", Insets.NONE, @@ -409,7 +411,7 @@ mSpyRootView.getPaddingBottom()); // Simulate moving a desktop window that causes the keyboard inset to be updated. - insets = applyWindowInsets(KEYBOARD_INSET + 100); + insets = applyWindowInsets(KEYBOARD_INSET + 100, UNSPECIFIED_INSET); assertEquals( "Ime insets should be consumed when root view is adjusted.", Insets.NONE, @@ -421,6 +423,84 @@ } @Test + public void overlappingNavBar_SwitchToAndFromDesktopWindowingMode() { + verifyDesktopWindowingDisabled( + /* error= */ "Desktop windowing mode should be disabled initially."); + + // Simulate overlapping nav bar bottom inset. + var insets = applyWindowInsets(UNSPECIFIED_INSET, NAV_BAR_INSET); + assertNotEquals( + "Nav bar insets should not be consumed when root view is not adjusted.", + Insets.NONE, + insets.getInsets(WindowInsetsCompat.Type.navigationBars())); + assertEquals("Root view bottom should not be padded.", 0, mSpyRootView.getPaddingBottom()); + + // Simulate switching to desktop windowing mode. + setupWithLeftAndRightBoundingRect(); + notifyInsetsRectObserver(); + insets = applyWindowInsets(UNSPECIFIED_INSET, NAV_BAR_INSET); + assertEquals( + "Nav bar insets should be consumed when root view is bottom-padded.", + Insets.NONE, + insets.getInsets(WindowInsetsCompat.Type.ime())); + verifyDesktopWindowingEnabled(); + assertEquals( + "Root view bottom padding should be updated.", + NAV_BAR_INSET, + mSpyRootView.getPaddingBottom()); + + // Simulate switching out of desktop windowing mode. + setupWithNoCaptionInsets(); + notifyInsetsRectObserver(); + insets = applyWindowInsets(UNSPECIFIED_INSET, NAV_BAR_INSET); + assertNotEquals( + "Nav bar insets should not be consumed when root view is not adjusted.", + Insets.NONE, + insets.getInsets(WindowInsetsCompat.Type.navigationBars())); + assertEquals( + "Root view bottom padding should be reset.", 0, mSpyRootView.getPaddingBottom()); + } + + @Test + public void overlappingNavBar_MoveDesktopWindow() { + // Simulate switching to desktop windowing mode. + setupWithLeftAndRightBoundingRect(); + notifyInsetsRectObserver(); + + // Simulate overlapping nav bar bottom inset. + var insets = applyWindowInsets(UNSPECIFIED_INSET, NAV_BAR_INSET); + assertEquals( + "Nav bar insets should be consumed when root view is adjusted.", + Insets.NONE, + insets.getInsets(WindowInsetsCompat.Type.navigationBars())); + + // Simulate moving a desktop window that causes the nav bar inset to be updated. + insets = applyWindowInsets(UNSPECIFIED_INSET, NAV_BAR_INSET - 10); + assertEquals( + "Nav bar insets should be consumed when root view is adjusted.", + Insets.NONE, + insets.getInsets(WindowInsetsCompat.Type.navigationBars())); + assertEquals( + "Root view bottom padding should be updated.", + NAV_BAR_INSET - 10, + mSpyRootView.getPaddingBottom()); + } + + @Test + public void overlappingKeyboardAndNavBar() { + // Simulate switching to desktop windowing mode. + setupWithLeftAndRightBoundingRect(); + notifyInsetsRectObserver(); + + // Simulate overlapping keyboard and nav bar bottom insets. + var insets = applyWindowInsets(KEYBOARD_INSET, NAV_BAR_INSET); + assertEquals( + "Root view bottom padding should be updated.", + KEYBOARD_INSET, + mSpyRootView.getPaddingBottom()); + } + + @Test public void windowingModeHistogram_EnterFullScreen() { // Simulate starting in desktop windowing mode for an initial state. setupWithLeftAndRightBoundingRect(); @@ -594,10 +674,16 @@ assertFalse("Edge to edge should not be active.", mEdgeToEdgeStateProvider.get()); } - private WindowInsetsCompat applyWindowInsets(int keyboardInset) { + private WindowInsetsCompat applyWindowInsets(int keyboardInset, int navBarInset) { var windowInsetsBuilder = new WindowInsetsCompat.Builder(); - windowInsetsBuilder.setInsets( - WindowInsetsCompat.Type.ime(), Insets.of(0, 0, 0, keyboardInset)); + if (keyboardInset != UNSPECIFIED_INSET) { + windowInsetsBuilder.setInsets( + WindowInsetsCompat.Type.ime(), Insets.of(0, 0, 0, keyboardInset)); + } + if (navBarInset != UNSPECIFIED_INSET) { + windowInsetsBuilder.setInsets( + WindowInsetsCompat.Type.navigationBars(), Insets.of(0, 0, 0, navBarInset)); + } return mAppHeaderCoordinator.onApplyWindowInsets(mSpyRootView, windowInsetsBuilder.build()); } }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java index b67779cec..9ed4f4fe 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java
@@ -135,10 +135,18 @@ } // Zero out (consume) the ime insets; we're applying them ourselves so no one else needs - // to consume them. - return new WindowInsetsCompat.Builder(windowInsetsCompat) - .setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE) - .build(); + // to consume them. Additionally, we will also consume nav bar insets because we have at + // least one other inset consumer that might otherwise use the nav bar inset incorrectly + // when the ime is visible and only the ime insets are consumed here. This is based on the + // assumption that both the ime and nav bar are present at the bottom of the app window. + // TODO (crbug.com/388037271): Remove nav bar inset consumption. + var builder = + new WindowInsetsCompat.Builder(windowInsetsCompat) + .setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE); + if (imeInsets.bottom > 0) { + builder.setInsets(WindowInsetsCompat.Type.navigationBars(), Insets.NONE); + } + return builder.build(); } private void commitKeyboardHeight(int newKeyboardHeight) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallbackTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallbackTest.java index 23255cc..2dc422c8 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallbackTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallbackTest.java
@@ -79,6 +79,8 @@ WindowInsetsCompat modifiedInsets = mCallback.onApplyWindowInsets(mView, windowInsets); assertEquals(Insets.NONE, modifiedInsets.getInsets(WindowInsetsCompat.Type.ime())); + assertEquals( + Insets.NONE, modifiedInsets.getInsets(WindowInsetsCompat.Type.navigationBars())); verify(mUpdateRunnable, never()).run(); mCallback.onEnd(mAnimation);
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd index de8c25c..e4f83df5 100644 --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -6183,6 +6183,9 @@ <message name="IDS_CUSTOM_TABS_SIGNED_OUT_MESSAGE_TITLE" desc="This string is the title of a message promotion about signing in to Chrome with the user's Google Account."> Sign in to Chrome </message> + <message name="IDS_CUSTOM_TAB_CANT_PERFORM_ACTION_TOAST" desc="Toast displayed when the URL currently being displayed in the Custom Tab cannot be opened in the regular browser."> + Something went wrong. Try again. + </message> <message name="IDS_ACCOUNT_SELECTION_CONTINUE" desc="Title of the button that continues filling with the only available set of credentials."> Continue as <ph name="NAME">%1$s<ex>Albus (or Albus Dumbledore)</ex></ph>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CUSTOM_TAB_CANT_PERFORM_ACTION_TOAST.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CUSTOM_TAB_CANT_PERFORM_ACTION_TOAST.png.sha1 new file mode 100644 index 0000000..f5750a2 --- /dev/null +++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CUSTOM_TAB_CANT_PERFORM_ACTION_TOAST.png.sha1
@@ -0,0 +1 @@ +bf54d3b35d3f41e90359d1ea1433c72eeaea6126 \ No newline at end of file
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc index 0bd370a..fe69e5f8 100644 --- a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc +++ b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
@@ -35,6 +35,7 @@ #include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h" #include "chromeos/constants/chromeos_features.h" #include "content/public/browser/browser_context.h" +#include "ui/base/ime/ash/ime_bridge.h" #include "ui/gfx/geometry/rect.h" #include "ui/views/view_utils.h" #include "ui/views/widget/widget.h" @@ -47,6 +48,15 @@ namespace { +gfx::Rect CalculateCaretBounds() { + const ui::InputMethod* input_method = + ash::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod(); + if (input_method && input_method->GetTextInputClient()) { + return input_method->GetTextInputClient()->GetCaretBounds(); + } + return gfx::Rect(); +} + std::unique_ptr<LobsterManager> CreateLobsterManager() { ash::LobsterController* lobster_controller = ash::Shell::Get()->lobster_controller(); @@ -57,7 +67,7 @@ std::unique_ptr<ash::LobsterController::Trigger> lobster_trigger = lobster_controller->CreateTrigger(ash::LobsterEntryPoint::kRightClickMenu, - true); + true, CalculateCaretBounds()); if (!lobster_trigger) { return nullptr;
diff --git a/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.cc b/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.cc index cb761dc..122120b 100644 --- a/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.cc +++ b/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.cc
@@ -390,7 +390,8 @@ } QuickInsertClientImpl::ShowLobsterCallback -QuickInsertClientImpl::CacheLobsterContext(bool support_image_insertion) { +QuickInsertClientImpl::CacheLobsterContext(bool support_image_insertion, + const gfx::Rect& caret_bounds) { if (!ash::features::IsLobsterEnabled()) { return base::NullCallback(); } @@ -404,8 +405,9 @@ return base::NullCallback(); } - lobster_trigger_ = lobster_controller->CreateTrigger( - ash::LobsterEntryPoint::kQuickInsert, support_image_insertion); + lobster_trigger_ = + lobster_controller->CreateTrigger(ash::LobsterEntryPoint::kQuickInsert, + support_image_insertion, caret_bounds); if (!lobster_trigger_) { return base::NullCallback();
diff --git a/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.h b/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.h index c9bdb9c..e22dd63c 100644 --- a/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.h +++ b/chrome/browser/ui/ash/quick_insert/quick_insert_client_impl.h
@@ -66,7 +66,8 @@ bool IsEligibleForEditor() override; ShowEditorCallback CacheEditorContext() override; ShowLobsterCallback CacheLobsterContext( - bool support_image_insertion) override; + bool support_image_insertion, + const gfx::Rect& caret_bounds) override; void GetSuggestedEditorResults( SuggestedEditorResultsCallback callback) override; void GetRecentLocalFileResults(size_t max_files,
diff --git a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc index 62d7c6f..d078cb49 100644 --- a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc
@@ -22,6 +22,7 @@ #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h" #include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h" #include "chrome/browser/ui/webui/ntp/new_tab_ui.h" +#include "chrome/grit/branded_strings.h" #include "components/lens/lens_features.h" #include "components/lens/lens_overlay_permission_utils.h" #include "components/omnibox/browser/omnibox_prefs.h" @@ -251,7 +252,8 @@ } if (!features::IsOmniboxEntrypointAlwaysVisible() && - !location_bar_->HasFocus()) { + !location_bar_->Contains( + location_bar_->GetFocusManager()->GetFocusedView())) { page_action_controller->Hide(page_action_id); return; } @@ -264,6 +266,11 @@ return; } + // No-ops if the overriding string is the same. + page_action_controller->OverrideText( + page_action_id, + l10n_util::GetStringUTF16(IDS_CONTENT_LENS_OVERLAY_ENTRYPOINT_LABEL)); + // TODO(crbug.com/376283383): We should always use the chip state once that's // implemented. page_action_controller->Show(page_action_id);
diff --git a/chrome/browser/ui/tabs/tab_strip_model_observer.cc b/chrome/browser/ui/tabs/tab_strip_model_observer.cc index 706e087f..4fdc3f2 100644 --- a/chrome/browser/ui/tabs/tab_strip_model_observer.cc +++ b/chrome/browser/ui/tabs/tab_strip_model_observer.cc
@@ -48,45 +48,40 @@ TabStripModelChange::TabStripModelChange() = default; TabStripModelChange::TabStripModelChange(Insert delta) - : TabStripModelChange(Type::kInserted, - std::make_unique<Insert>(std::move(delta))) {} + : TabStripModelChange(Type::kInserted, std::move(delta)) {} TabStripModelChange::TabStripModelChange(Remove delta) - : TabStripModelChange(Type::kRemoved, - std::make_unique<Remove>(std::move(delta))) {} + : TabStripModelChange(Type::kRemoved, std::move(delta)) {} TabStripModelChange::TabStripModelChange(Move delta) - : TabStripModelChange(Type::kMoved, - std::make_unique<Move>(std::move(delta))) {} + : TabStripModelChange(Type::kMoved, std::move(delta)) {} TabStripModelChange::TabStripModelChange(Replace delta) - : TabStripModelChange(Type::kReplaced, - std::make_unique<Replace>(std::move(delta))) {} + : TabStripModelChange(Type::kReplaced, std::move(delta)) {} TabStripModelChange::~TabStripModelChange() = default; const TabStripModelChange::Insert* TabStripModelChange::GetInsert() const { - DCHECK_EQ(type_, Type::kInserted); - return static_cast<const Insert*>(delta_.get()); + CHECK_EQ(type_, Type::kInserted); + return &std::get<Insert>(delta_); } const TabStripModelChange::Remove* TabStripModelChange::GetRemove() const { - DCHECK_EQ(type_, Type::kRemoved); - return static_cast<const Remove*>(delta_.get()); + CHECK_EQ(type_, Type::kRemoved); + return &std::get<Remove>(delta_); } const TabStripModelChange::Move* TabStripModelChange::GetMove() const { - DCHECK_EQ(type_, Type::kMoved); - return static_cast<const Move*>(delta_.get()); + CHECK_EQ(type_, Type::kMoved); + return &std::get<Move>(delta_); } const TabStripModelChange::Replace* TabStripModelChange::GetReplace() const { - DCHECK_EQ(type_, Type::kReplaced); - return static_cast<const Replace*>(delta_.get()); + CHECK_EQ(type_, Type::kReplaced); + return &std::get<Replace>(delta_); } -TabStripModelChange::TabStripModelChange(Type type, - std::unique_ptr<Delta> delta) +TabStripModelChange::TabStripModelChange(Type type, Delta delta) : type_(type), delta_(std::move(delta)) {} void TabStripModelChange::RemovedTab::WriteIntoTrace( @@ -130,7 +125,7 @@ void TabStripModelChange::WriteIntoTrace(perfetto::TracedValue context) const { auto dict = std::move(context).WriteDictionary(); dict.Add("type", type_); - dict.Add("delta", delta_); + std::visit([&dict](auto&& delta) { dict.Add("delta", delta); }, delta_); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/tabs/tab_strip_model_observer.h b/chrome/browser/ui/tabs/tab_strip_model_observer.h index c0f7bd0..e3243f2 100644 --- a/chrome/browser/ui/tabs/tab_strip_model_observer.h +++ b/chrome/browser/ui/tabs/tab_strip_model_observer.h
@@ -58,15 +58,6 @@ kInsertedIntoOtherTabStrip }; - // Base class for all changes. - // TODO(dfried): would love to change this whole thing into a std::variant, - // but C++17 features are not yet approved for use in chromium. - struct Delta { - virtual ~Delta() = default; - - virtual void WriteIntoTrace(perfetto::TracedValue context) const = 0; - }; - struct RemovedTab { RemovedTab(tabs::TabInterface* tab, int index, @@ -96,9 +87,9 @@ // WebContents were inserted. This implicitly changes the existing selection // model by calling IncrementFrom(index) on each index in |contents[i].index|. - struct Insert : public Delta { + struct Insert { Insert(); - ~Insert() override; + ~Insert(); Insert(Insert&& other); Insert& operator=(Insert&& other); @@ -126,14 +117,14 @@ // after processing all of `contents`. std::vector<ContentsWithIndex> contents; - void WriteIntoTrace(perfetto::TracedValue context) const override; + void WriteIntoTrace(perfetto::TracedValue context) const; }; // WebContents were removed at |indices_before_removal|. This implicitly // changes the existing selection model by calling DecrementFrom(index). - struct Remove : public Delta { + struct Remove { Remove(); - ~Remove() override; + ~Remove(); Remove(Remove&& other); Remove& operator=(Remove&& other); @@ -162,30 +153,30 @@ // after processing all of `contents`. std::vector<RemovedTab> contents; - void WriteIntoTrace(perfetto::TracedValue context) const override; + void WriteIntoTrace(perfetto::TracedValue context) const; }; // A tab was moved from `from_index` to `to_index`. This implicitly changes // the existing selection model by calling Move(from_index, to_index, 1). - struct Move : public Delta { + struct Move { raw_ptr<tabs::TabInterface> tab = nullptr; raw_ptr<content::WebContents> contents = nullptr; int from_index; int to_index; - void WriteIntoTrace(perfetto::TracedValue context) const override; + void WriteIntoTrace(perfetto::TracedValue context) const; }; // The tab was replaced at the specified index. This is invoked when // prerendering swaps in a prerendered WebContents or when a tab's WebContents // is discarded to save memory. - struct Replace : public Delta { + struct Replace { raw_ptr<tabs::TabInterface> tab = nullptr; raw_ptr<content::WebContents> old_contents = nullptr; raw_ptr<content::WebContents> new_contents = nullptr; int index; - void WriteIntoTrace(perfetto::TracedValue context) const override; + void WriteIntoTrace(perfetto::TracedValue context) const; }; TabStripModelChange(); @@ -206,10 +197,13 @@ void WriteIntoTrace(perfetto::TracedValue context) const; private: - TabStripModelChange(Type type, std::unique_ptr<Delta> delta); + using Delta = std::variant<Insert, Remove, Move, Replace>; + + TabStripModelChange(Type type, Delta delta); const Type type_ = kSelectionOnly; - std::unique_ptr<Delta> delta_; + + Delta delta_; }; // Struct to carry changes on selection/activation.
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc index 9a1ac05..5f641cb 100644 --- a/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc +++ b/chrome/browser/ui/views/download/bubble/download_bubble_interactive_uitest.cc
@@ -46,7 +46,6 @@ namespace { -#if !BUILDFLAG(IS_MAC) // This waits for the download bubble widget to be shown. views::NamedWidgetShownWaiter CreateDownloadBubbleDialogWaiter() { return views::NamedWidgetShownWaiter{views::test::AnyWidgetTestPasskey{}, @@ -64,7 +63,6 @@ bool is_hiding = bubble->animation_for_test()->IsClosing(); return bubble->IsShowing() || (bubble->IsVisible() && !is_hiding); } -#endif // TODO(chlily): Deduplicate this helper class into a test utils file. class TestDownloadManagerDelegate : public ChromeDownloadManagerDelegate { @@ -103,6 +101,7 @@ : InteractiveFeaturePromoTestT(UseDefaultTrackerAllowingPromos( {feature_engagement::kIPHDownloadEsbPromoFeature})) { #if BUILDFLAG(IS_MAC) + // TODO(chlily): Add test coverage for immersive fullscreen disabled on Mac. test_features_.InitWithFeatures({features::kImmersiveFullscreen}, {}); #endif // BUILDFLAG(IS_MAC) } @@ -219,7 +218,6 @@ }); } -#if !BUILDFLAG(IS_MAC) // Check for whether the exclusive access bubble is shown ("Press Esc to // exit fullscreen" or other similar message). auto IsExclusiveAccessBubbleDisplayed(bool displayed) { @@ -243,7 +241,6 @@ : false); }); } -#endif #if BUILDFLAG(IS_MAC) auto EnterImmersiveFullscreen() { @@ -417,27 +414,37 @@ } #endif // BUILDFLAG(IS_MAC) -// This test is only for platforms where fullscreen is not immersive. -// TODO(chlily): Add test coverage for Mac. -#if !BUILDFLAG(IS_MAC) // Test that downloading a file in tab fullscreen (not browser fullscreen) // results in an exclusive access bubble, and the partial view, if enabled, is // displayed after the tab exits fullscreen. IN_PROC_BROWSER_TEST_F( DownloadBubbleInteractiveUiTest, ExclusiveAccessBubbleShownForTabFullscreenDownloadThenPartialView) { + using ui_test_utils::FullscreenWaiter; + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId); // Grab the fullscreen accelerator, which is used to exit fullscreen in the // test. For some reason, exiting tab fullscreen via JavaScript doesn't work // (times out). ui::Accelerator fullscreen_accelerator; +#if BUILDFLAG(IS_MAC) + // SendAccelerator or ui_controls::SendKeyPress doesn't support fn key on + // Mac, that the default fullscreen hotkey wouldn't work. + // TODO: When SendAccelerator fixed on mac, remove this hard coded key. + fullscreen_accelerator = + ui::Accelerator(ui::VKEY_F, ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN); +#else chrome::AcceleratorProviderForBrowser(browser())->GetAcceleratorForCommandId( IDC_FULLSCREEN, &fullscreen_accelerator); +#endif views::NamedWidgetShownWaiter dialog_waiter = CreateDownloadBubbleDialogWaiter(); + auto tab_fullscreen_waiter = std::make_unique<FullscreenWaiter>( + browser(), FullscreenWaiter::Expectation{.tab_fullscreen = true}); + RunTestSequenceInContext( browser()->window()->GetElementContext(), InstrumentTab(kWebContentsElementId), @@ -447,7 +454,14 @@ InParallel( ExecuteJs(kWebContentsElementId, "() => document.documentElement.requestFullscreen()"), - InAnyContext(WaitForShow(kExclusiveAccessBubbleViewElementId))), + InAnyContext(WaitForShow(kExclusiveAccessBubbleViewElementId)), + Do([&]() { + tab_fullscreen_waiter->Wait(); + // Reset the fullscreen waiter to wait for exiting fullscreen next + // time. + tab_fullscreen_waiter = std::make_unique<FullscreenWaiter>( + browser(), FullscreenWaiter::kNoFullscreen); + })), // The exclusive access bubble should notify about the fullscreen change. Check(IsExclusiveAccessBubbleDisplayed(true), "Exclusive access bubble is displayed upon entering fullscreen"), @@ -464,8 +478,8 @@ "Exclusive access bubble is for a download"), // Now exit fullscreen, and the partial view, if enabled, should be shown. - SendAccelerator(kBrowserViewElementId, fullscreen_accelerator), - + InParallel(SendAccelerator(kBrowserViewElementId, fullscreen_accelerator), + Do([&]() { tab_fullscreen_waiter->Wait(); })), If([&]() { return IsPartialViewEnabled(); }, Steps(Do(WaitForDownloadBubbleShow(dialog_waiter)), Check(DownloadBubbleIsShowingDetails(true), @@ -476,7 +490,6 @@ Do(ChangeBubbleVisibility(false)), Do(ChangeButtonVisibility(false)), WaitForHide(kToolbarDownloadButtonElementId)); } -#endif // Tests that the partial view does not steal focus from the web contents, and // that the partial view is still closable when clicking outside of it, and that
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc index 2df9dfff..0370ec1 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -413,6 +413,13 @@ if (!browser_view) { return false; } +#if BUILDFLAG(IS_MAC) + // In content fullscreen, we do not show the download bubble and the toolbar + // is not visible. Therefore, we must show the ExclusiveAccessBubble notice. + if (fullscreen_utils::IsInContentFullscreen(browser_)) { + return true; + } +#endif #if BUILDFLAG(IS_CHROMEOS) if (chromeos::IsKioskSession()) { return false;
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc index 0c5cfbb..1196c86 100644 --- a/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc +++ b/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc
@@ -231,6 +231,13 @@ if (!browser_view_) { return false; } +#if BUILDFLAG(IS_MAC) + // In content fullscreen, we do not show the download bubble and the toolbar + // is not visible. Therefore, we must show the ExclusiveAccessBubble notice. + if (fullscreen_utils::IsInContentFullscreen(browser_view_->browser())) { + return true; + } +#endif #if BUILDFLAG(IS_CHROMEOS) if (chromeos::IsKioskSession()) { return false;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 7dfc7e36..3975be5 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -321,6 +321,7 @@ #if BUILDFLAG(GOOGLE_CHROME_BRANDING) #include "chrome/browser/ui/views/promos/ios_promo_password_bubble.h" +#include "components/segmentation_platform/public/result.h" #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) #if defined(USE_AURA)
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index a72ecf8..d98bcf5 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -43,7 +43,6 @@ #include "chrome/common/buildflags.h" #include "components/enterprise/buildflags/buildflags.h" #include "components/infobars/core/infobar_container.h" -#include "components/segmentation_platform/public/result.h" #include "components/user_education/common/feature_promo/feature_promo_controller.h" #include "components/user_education/common/feature_promo/feature_promo_handle.h" #include "components/webapps/browser/banners/app_banner_manager.h" @@ -123,6 +122,10 @@ class BorderView; } // namespace glic +namespace segmentation_platform { +struct ClassificationResult; +} + /////////////////////////////////////////////////////////////////////////////// // BrowserView //
diff --git a/chrome/browser/ui/views/mahi/mahi_menu_view.cc b/chrome/browser/ui/views/mahi/mahi_menu_view.cc index 318ca5d..3771d26 100644 --- a/chrome/browser/ui/views/mahi/mahi_menu_view.cc +++ b/chrome/browser/ui/views/mahi/mahi_menu_view.cc
@@ -94,9 +94,15 @@ button->SetImageModel(views::Button::ButtonState::STATE_NORMAL, ui::ImageModel::FromVectorIcon( icon, ui::kColorSysOnSurface, kButtonHeight)); + button->SetImageModel(views::Button::ButtonState::STATE_DISABLED, + ui::ImageModel::FromVectorIcon( + icon, ui::kColorSysStateDisabled, kButtonHeight)); button->SetTextColorId(views::LabelButton::ButtonState::STATE_NORMAL, ui::kColorSysOnSurface); + button->SetTextColorId(views::LabelButton::ButtonState::STATE_DISABLED, + ui::kColorSysStateDisabled); button->SetImageLabelSpacing(kButtonImageLabelSpacing); + auto color_id = button->GetEnabled() ? ui::kColorSysTonalOutline : ui::kColorButtonBorderDisabled; button->SetBorder(views::CreatePaddedBorder(
diff --git a/chrome/browser/ui/views/page_action/page_action_controller.cc b/chrome/browser/ui/views/page_action/page_action_controller.cc index 55295d4e..89a42bf 100644 --- a/chrome/browser/ui/views/page_action/page_action_controller.cc +++ b/chrome/browser/ui/views/page_action/page_action_controller.cc
@@ -63,6 +63,17 @@ model.SetImage(action_item->GetImage()); } +void PageActionController::OverrideText(actions::ActionId action_id, + const std::u16string& override_text) { + FindPageActionModel(action_id).SetOverrideText( + base::PassKey<PageActionController>(), override_text); +} + +void PageActionController::ClearOverrideText(actions::ActionId action_id) { + FindPageActionModel(action_id).SetOverrideText( + base::PassKey<PageActionController>(), /*override_text=*/std::nullopt); +} + void PageActionController::AddObserver( actions::ActionId action_id, base::ScopedObservation<PageActionModel, PageActionModelObserver>&
diff --git a/chrome/browser/ui/views/page_action/page_action_controller.h b/chrome/browser/ui/views/page_action/page_action_controller.h index c8776f9..afd202829 100644 --- a/chrome/browser/ui/views/page_action/page_action_controller.h +++ b/chrome/browser/ui/views/page_action/page_action_controller.h
@@ -8,6 +8,7 @@ #include <map> #include <memory> #include <set> +#include <string> #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" @@ -48,6 +49,14 @@ // Returns true if the page action will be shown and false otherwise. bool ShowIfNotPinned(actions::ActionId action_id); + // By default, in suggestion chip mode, the ActionItem text will be used as + // the control label. However, features can provide a custom text to use + // as the label. In that case, the custom text will take precedence over + // the ActionItem text. + void OverrideText(actions::ActionId action_id, + const std::u16string& override_text); + void ClearOverrideText(actions::ActionId action_id); + // Manages observers for the page action's underlying `PageActionModel`. void AddObserver( actions::ActionId action_id,
diff --git a/chrome/browser/ui/views/page_action/page_action_controller_unittest.cc b/chrome/browser/ui/views/page_action/page_action_controller_unittest.cc index 51581ca2..609e2fb 100644 --- a/chrome/browser/ui/views/page_action/page_action_controller_unittest.cc +++ b/chrome/browser/ui/views/page_action/page_action_controller_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/page_action/page_action_controller.h" #include <memory> +#include <string> #include "base/scoped_observation.h" #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h" @@ -18,6 +19,15 @@ namespace page_actions { namespace { +const std::u16string kText = u"Text"; +const std::u16string kOverrideText = u"Override Text"; +const std::u16string kOverrideOne = u"Override One"; +const std::u16string kOverrideTwo = u"Override Two"; +const std::u16string kOverrideThree = u"Override Three"; +const std::u16string kAnotherNewText = u"Another New Text"; + +const std::u16string kTooltip = u"Tooltip"; + using ::actions::ActionItem; using TestPageActionModelObservation = @@ -177,11 +187,11 @@ controller()->CreateActionItemSubscription(action_item.get()); controller()->AddObserver(0, observation); - action_item->SetText(u"Text"); - action_item->SetTooltipText(u"Tooltip"); + action_item->SetText(kText); + action_item->SetTooltipText(kTooltip); - EXPECT_EQ(observer.text(), u"Text"); - EXPECT_EQ(observer.tooltip_text(), u"Tooltip"); + EXPECT_EQ(kText, observer.text()); + EXPECT_EQ(kTooltip, observer.tooltip_text()); } TEST_F(PageActionControllerTest, ShowIfNotPinned) { @@ -211,5 +221,80 @@ actions::ActionManager::Get().ResetForTesting(); } +TEST_F(PageActionControllerTest, OverrideText) { + auto observer = PageActionTestObserver(); + TestPageActionModelObservation observation(&observer); + controller()->Register(0); + auto action_item = BuildActionItem(0); + base::CallbackListSubscription subscription = + controller()->CreateActionItemSubscription(action_item.get()); + controller()->AddObserver(0, observation); + + action_item->SetText(kText); + + controller()->OverrideText(0, kOverrideText); + EXPECT_EQ(kOverrideText, observer.text()); +} + +TEST_F(PageActionControllerTest, UpdateActionItemTextWithOverrideText) { + auto observer = PageActionTestObserver(); + TestPageActionModelObservation observation(&observer); + controller()->Register(0); + auto action_item = BuildActionItem(0); + base::CallbackListSubscription subscription = + controller()->CreateActionItemSubscription(action_item.get()); + controller()->AddObserver(0, observation); + + action_item->SetText(kText); + + controller()->OverrideText(0, kOverrideText); + EXPECT_EQ(kOverrideText, observer.text()); + + action_item->SetText(kAnotherNewText); + // The override text should still take precedence. + EXPECT_EQ(kOverrideText, observer.text()); +} + +TEST_F(PageActionControllerTest, ClearOverrideText) { + auto observer = PageActionTestObserver(); + TestPageActionModelObservation observation(&observer); + controller()->Register(0); + auto action_item = BuildActionItem(0); + base::CallbackListSubscription subscription = + controller()->CreateActionItemSubscription(action_item.get()); + controller()->AddObserver(0, observation); + + action_item->SetText(kText); + controller()->OverrideText(0, kOverrideText); + controller()->ClearOverrideText(0); + + // We should revert to the ActionItem text. + EXPECT_EQ(kText, observer.text()); +} + +TEST_F(PageActionControllerTest, MultipleTextOverrides) { + auto observer = PageActionTestObserver(); + TestPageActionModelObservation observation(&observer); + controller()->Register(0); + auto action_item = BuildActionItem(0); + auto subscription = + controller()->CreateActionItemSubscription(action_item.get()); + controller()->AddObserver(0, observation); + + action_item->SetText(kText); + + controller()->OverrideText(0, kOverrideOne); + EXPECT_EQ(kOverrideOne, observer.text()); + + controller()->OverrideText(0, kOverrideTwo); + EXPECT_EQ(kOverrideTwo, observer.text()); + + controller()->OverrideText(0, kOverrideThree); + EXPECT_EQ(kOverrideThree, observer.text()); + + controller()->ClearOverrideText(0); + EXPECT_EQ(kText, observer.text()); +} + } // namespace } // namespace page_actions
diff --git a/chrome/browser/ui/views/page_action/page_action_model.cc b/chrome/browser/ui/views/page_action/page_action_model.cc index 9b6ff32e..712d170 100644 --- a/chrome/browser/ui/views/page_action/page_action_model.cc +++ b/chrome/browser/ui/views/page_action/page_action_model.cc
@@ -65,7 +65,7 @@ NotifyChange(); } const std::u16string PageActionModel::GetText() const { - return text_; + return override_text_.value_or(text_); } void PageActionModel::SetTooltipText(const std::u16string& tooltip) { @@ -79,6 +79,16 @@ return tooltip_; } +void PageActionModel::SetOverrideText( + base::PassKey<PageActionController>, + const std::optional<std::u16string>& override_text) { + if (override_text_ == override_text) { + return; + } + override_text_ = override_text; + NotifyChange(); +} + void PageActionModel::AddObserver(PageActionModelObserver* observer) { observer_list_.AddObserver(observer); }
diff --git a/chrome/browser/ui/views/page_action/page_action_model.h b/chrome/browser/ui/views/page_action/page_action_model.h index b7caed5..3564ffe 100644 --- a/chrome/browser/ui/views/page_action/page_action_model.h +++ b/chrome/browser/ui/views/page_action/page_action_model.h
@@ -30,6 +30,8 @@ void SetShowRequested(base::PassKey<PageActionController>, bool requested); void SetActionItemEnabled(base::PassKey<PageActionController>, bool enabled); void SetActionItemVisible(base::PassKey<PageActionController>, bool visible); + void SetOverrideText(base::PassKey<PageActionController>, + const std::optional<std::u16string>& override_text); // The model distills all visibility properties into a single result. bool GetVisible() const; @@ -53,6 +55,8 @@ bool action_item_enabled_ = false; bool action_item_visible_ = false; std::u16string text_; + // When set, it will always take precedence over `text_`. + std::optional<std::u16string> override_text_; std::u16string tooltip_; ui::ImageModel action_item_image_;
diff --git a/chrome/browser/ui/views/page_action/page_action_view_unittest.cc b/chrome/browser/ui/views/page_action/page_action_view_unittest.cc index 6d79f37a..1c7ede5 100644 --- a/chrome/browser/ui/views/page_action/page_action_view_unittest.cc +++ b/chrome/browser/ui/views/page_action/page_action_view_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/page_action/page_action_view.h" #include <memory> +#include <string> #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h" @@ -245,5 +246,33 @@ EXPECT_NE(updated_insets_true, updated_insets_false); } +// Test that once a PageActionController is active, overriding the text updates +// the view, and that the override persists until the controller is removed. +TEST_F(PageActionViewTest, OverrideText) { + const std::u16string kInitialText = u"Initial Text"; + actions::ActionItem* item = action_item(); + item->SetEnabled(true); + item->SetVisible(true); + item->SetText(kInitialText); + + PageActionView* view = page_action_view(); + EXPECT_FALSE(view->GetVisible()); + EXPECT_EQ(u"", view->GetText()); + + auto controller = NewPageActionController(); + view->OnNewActiveController(controller.get()); + EXPECT_EQ(kInitialText, view->GetText()); + + const std::u16string kOverrideText = u"Override Text"; + controller->Show(0); + controller->OverrideText(0, kOverrideText); + EXPECT_TRUE(view->GetVisible()); + EXPECT_EQ(kOverrideText, view->GetText()); + + view->OnNewActiveController(nullptr); + EXPECT_FALSE(view->GetVisible()); + EXPECT_EQ(kOverrideText, view->GetText()); +} + } // namespace } // namespace page_actions
diff --git a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_pixel_browsertest.cc b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_pixel_browsertest.cc index 952b5a4..3f4f7dc 100644 --- a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_pixel_browsertest.cc +++ b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_pixel_browsertest.cc
@@ -5,6 +5,7 @@ #include <string> #include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/enterprise/browser_management/management_service_factory.h" #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" #include "chrome/browser/profiles/profile_attributes_entry.h" @@ -389,6 +390,13 @@ IN_PROC_BROWSER_TEST_P(DiceWebSigninInterceptionBubblePixelTest, InvokeUi_default) { +#if BUILDFLAG(IS_WIN) + if (GetParam().test_suffix == "EnterpriseManagedIntercepted") { + // TODO(crbug.com/389737045): Enable for this variation once pixel tests + // are corrected. + GTEST_SKIP(); + } +#endif ShowAndVerifyUi(); }
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc index cdada453..0d95e25 100644 --- a/chrome/browser/ui/views/task_manager_view.cc +++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -623,6 +623,9 @@ tab_table->SetModel(table_model_.get()); tab_table->SetGrouper(this); tab_table->SetSortOnPaint(true); + if (table_config_.layout_refresh) { + tab_table->SetMouseHoveringEnabled(true); + } tab_table->set_observer(this); tab_table->set_context_menu_controller(this); set_context_menu_controller(this);
diff --git a/chrome/browser/ui/webauthn/passkey_upgrade_request_controller.cc b/chrome/browser/ui/webauthn/passkey_upgrade_request_controller.cc index 985d800..8860684 100644 --- a/chrome/browser/ui/webauthn/passkey_upgrade_request_controller.cc +++ b/chrome/browser/ui/webauthn/passkey_upgrade_request_controller.cc
@@ -7,14 +7,18 @@ #include "chrome/browser/password_manager/account_password_store_factory.h" #include "chrome/browser/password_manager/profile_password_store_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/passwords/passwords_client_ui_delegate.h" #include "chrome/browser/webauthn/enclave_manager_factory.h" #include "chrome/browser/webauthn/gpm_enclave_controller.h" #include "chrome/browser/webauthn/passkey_model_factory.h" #include "components/device_event_log/device_event_log.h" +#include "components/password_manager/core/browser/features/password_manager_features_util.h" #include "components/password_manager/core/browser/form_parsing/form_data_parser.h" #include "components/password_manager/core/browser/password_store/password_store.h" #include "components/password_manager/core/browser/password_store/password_store_util.h" +#include "components/password_manager/core/browser/password_sync_util.h" +#include "components/sync/service/sync_service.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/document_user_data.h" #include "content/public/browser/render_frame_host.h" @@ -67,13 +71,34 @@ void PasskeyUpgradeRequestController::ContinuePendingUpgradeRequest() { CHECK_EQ(enclave_state_, EnclaveState::kReady); CHECK(pending_upgrade_request_); + CHECK(pending_callback_); + pending_upgrade_request_ = false; - // TODO(crbug.com/377758786): The profile password store is probably wrong in - // some cases. Find out how to query GPM specifically. - scoped_refptr<password_manager::PasswordStoreInterface> password_store = - ProfilePasswordStoreFactory::GetForProfile( - profile(), ServiceAccessType::EXPLICIT_ACCESS); + // When looking for passwords that might be eligible to be upgraded, only + // consider passwords stored in GPM. + syncer::SyncService* sync_service = + SyncServiceFactory::GetForProfile(profile()); + password_manager::PasswordStoreInterface* password_store = nullptr; + if (password_manager::features_util::IsOptedInForAccountStorage( + profile()->GetPrefs(), sync_service)) { + password_store = AccountPasswordStoreFactory::GetForProfile( + profile(), ServiceAccessType::EXPLICIT_ACCESS) + .get(); + } else if (password_manager::sync_util:: + IsSyncFeatureEnabledIncludingPasswords(sync_service)) { + password_store = ProfilePasswordStoreFactory::GetForProfile( + profile(), ServiceAccessType::EXPLICIT_ACCESS) + .get(); + } + + if (!password_store) { + FIDO_LOG(EVENT) + << "Passkey upgrade failed without available password store"; + std::move(pending_callback_).Run(false); + return; + } + GURL url = origin().GetURL(); password_manager::PasswordFormDigest form_digest( password_manager::PasswordForm::Scheme::kHtml,
diff --git a/chrome/browser/ui/webui/ash/lobster/lobster_page_handler_unittest.cc b/chrome/browser/ui/webui/ash/lobster/lobster_page_handler_unittest.cc index 6088060c..28de48d 100644 --- a/chrome/browser/ui/webui/ash/lobster/lobster_page_handler_unittest.cc +++ b/chrome/browser/ui/webui/ash/lobster/lobster_page_handler_unittest.cc
@@ -64,7 +64,9 @@ const std::string& description) override { return feedback_submission_status_; } - void LoadUI(std::optional<std::string> query, LobsterMode mode) override {} + void LoadUI(std::optional<std::string> query, + LobsterMode mode, + const gfx::Rect& caret_bounds) override {} void ShowUI() override {} void CloseUI() override {} void RecordWebUIMetricEvent(LobsterMetricState metric_state) override {
diff --git a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc index 0607d8d..7d242edb4 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc +++ b/chrome/browser/ui/webui/ash/settings/pages/privacy/privacy_section.cc
@@ -307,10 +307,6 @@ IDS_SETTINGS_SECURE_DNS_WITH_IDENTIFIERS_DESCRIPTION}, {"secureDnsWithIdentifiersAndDomainConfigDescription", IDS_OS_SETTINGS_SECURE_DNS_WITH_IDENTIFIERS_AND_DOMAIN_CONFIG_DESCRIPTION}, - {"secureDnsDialogTitle", IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TITLE}, - {"secureDnsDialogBody", IDS_OS_SETTINGS_SECURE_DNS_DIALOG_BODY}, - {"secureDnsDialogCancel", IDS_OS_SETTINGS_SECURE_DNS_DIALOG_CANCEL}, - {"secureDnsDialogTurnOff", IDS_OS_SETTINGS_SECURE_DNS_DIALOG_TURN_OFF}, {"secureDnsAutomaticModeDescription", IDS_OS_SETTINGS_SECURE_DNS_AUTOMATIC_MODE_DESCRIPTION}, {"secureDnsSecureDropdownModeNetworkDefaultDescription", @@ -665,9 +661,6 @@ html_source->AddBoolean("showSecureDnsSetting", true); html_source->AddBoolean("showSecureDnsOsSettingLink", false); - html_source->AddBoolean( - "isDeprecateDnsDialogEnabled", - ash::features::IsOsSettingsDeprecateDnsDialogEnabled()); ::settings::AddSecureDnsStrings(html_source); AddChromeOsSecureDnsStrings(html_source);
diff --git a/chrome/browser/ui/webui/ash/settings/pages/search/search_section_unittest.cc b/chrome/browser/ui/webui/ash/settings/pages/search/search_section_unittest.cc index fa125c5..a026220d 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/search/search_section_unittest.cc +++ b/chrome/browser/ui/webui/ash/settings/pages/search/search_section_unittest.cc
@@ -76,7 +76,15 @@ .value()); } -TEST_F(SearchSectionTest, DoesNotIncludeSunfishSettingsByDefault) { +TEST_F(SearchSectionTest, + DoesNotIncludeSunfishSettingsWhenSunfishFeaturesDisabled) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures(/*enabled_features=*/{}, /*disabled_features=*/ + { + features::kSunfishFeature, + features::kScannerUpdate, + features::kScannerDogfood, + }); search_section_ = std::make_unique<SearchSection>(profile(), search_tag_registry()); std::unique_ptr<content::TestWebUIDataSource> html_source =
diff --git a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc index 510d513..269b8b13 100644 --- a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc +++ b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
@@ -42,6 +42,7 @@ #include "base/thread_annotations.h" #include "base/time/time.h" #include "build/build_config.h" +#include "chrome/browser/password_manager/account_password_store_factory.h" #include "chrome/browser/password_manager/profile_password_store_factory.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" @@ -887,7 +888,12 @@ sync_harness_ = SyncServiceImplHarness::Create( browser()->profile(), kEmail, "password", SyncServiceImplHarness::SigninType::FAKE_SIGNIN); - ASSERT_TRUE(sync_harness_->SetupSync()); + if (sync_feature_enabled_) { + ASSERT_TRUE(sync_harness_->SetupSync()); + } else { + // Sign in without full sync consent, opt into using account passwords. + ASSERT_TRUE(sync_harness_->SignInPrimaryAccount()); + } sync_service->GetUserSettings()->SetSelectedTypes( /*sync_everything=*/false, /*types=*/{syncer::UserSelectableType::kPasswords}); @@ -1098,6 +1104,7 @@ crypto::ScopedFailingUserVerifyingKeyProvider> fake_uv_provider_; logging::ScopedVmoduleSwitches scoped_vmodule_; + bool sync_feature_enabled_ = true; }; class EnclaveAuthenticatorWithPinBrowserTest @@ -4142,14 +4149,29 @@ } class EnclaveAuthenticatorConditionalCreateBrowserTest - : public EnclaveAuthenticatorWithPinBrowserTest { + : public EnclaveAuthenticatorWithPinBrowserTest, + public testing::WithParamInterface<bool> { protected: EnclaveAuthenticatorConditionalCreateBrowserTest() { + sync_feature_enabled_ = GetParam(); + scoped_feature_list_.InitAndEnableFeature(device::kWebAuthnPasskeyUpgrade); CHECK(base::FeatureList::IsEnabled(device::kWebAuthnPasskeyUpgrade)); CHECK(base::FeatureList::IsEnabled(device::kWebAuthnEnclaveAuthenticator)); } + bool use_account_password_store() { return !sync_feature_enabled_; } + + password_manager::PasswordStoreInterface* password_store() { + return use_account_password_store() + ? AccountPasswordStoreFactory::GetForProfile( + browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS) + .get() + : ProfilePasswordStoreFactory::GetForProfile( + browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS) + .get(); + } + // Creates a credential to ensure the enclave authenticator is in a usable // state prior to making a conditional create request. void BootstrapEnclave() { @@ -4183,10 +4205,6 @@ } void InjectPassword(base::Time last_used) { - password_manager::PasswordStoreInterface* password_store = - ProfilePasswordStoreFactory::GetForProfile( - browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS) - .get(); password_manager::PasswordForm saved_form; saved_form.signon_realm = https_server_.GetURL("example.com", "/").spec(); saved_form.url = https_server_.GetURL("example.com", @@ -4194,13 +4212,17 @@ saved_form.username_value = u"bar@example.com"; saved_form.password_value = u"hunter1"; saved_form.date_last_used = last_used; - password_store->AddLogin(saved_form); + password_store()->AddLogin(saved_form); } base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(EnclaveAuthenticatorConditionalCreateBrowserTest, +INSTANTIATE_TEST_SUITE_P(WithSyncFeatureEnabled, + EnclaveAuthenticatorConditionalCreateBrowserTest, + testing::Bool()); + +IN_PROC_BROWSER_TEST_P(EnclaveAuthenticatorConditionalCreateBrowserTest, ConditionalCreate) { ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), https_server_.GetURL("www.example.com", "/title1.html")));
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 55beb6d..6f77ab3 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1736791001-f5a46224bb83ceb9e6b2cc5ccb8546d988065895-10021c5159e4121954dcc274c5eb9646ee6dad38.profdata +chrome-android32-main-1736812635-cb2e1de919905e7391bc2854b6dee56823f3fbfe-7d3b695c550d443ed19e2b8a0598214c48556024.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index b9008e5b..8c7fabf 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1736794606-3b5562cc78b8a8c80dd894f57f809a19850c456e-ad66aa0ac8ad7552127ac34c2c812efe194edc69.profdata +chrome-android64-main-1736812923-b91e22bd048e50ca156190c5f174498d600f0a08-9644bcf5a9588d562fa12611cda3252d26ee5760.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 90c46af..4dfa5e2 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1736798394-2eb45a99e528bce7e950ac6b6ad2a9c82ac72a2c-b80efb0bc63804804321a2b178d9a39df90c4d87.profdata +chrome-mac-arm-main-1736805507-0b04020f195564cdf12834686b7ac6106027e38e-1fab80dad9a80b75211023046cb8d045515e945c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index a4b18f9..1f5466b 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1736780267-6e6643332bea5129499835f448ff5a8f433c3030-121cfb01b818f003b8fd5ec34f347b123fa610a8.profdata +chrome-win32-main-1736791001-caed23642957c755d4788f9416472485b042435f-10021c5159e4121954dcc274c5eb9646ee6dad38.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 96e1c84..f0d8c13 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1736769296-8be801aea43e420f465ae089961d0021065f5f4c-03b712dbd057f11864ee41f655aaa4cfdb1bf517.profdata +chrome-win64-main-1736791001-766f078d3f5fb2b574e78bb139dc2e77dc5b8bc6-10021c5159e4121954dcc274c5eb9646ee6dad38.profdata
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc index 82c1ce33..36f846b 100644 --- a/chrome/common/crash_keys.cc +++ b/chrome/common/crash_keys.cc
@@ -206,6 +206,9 @@ string_annotations = base::StrCat( {string_annotations, crash_key.Name(), "=", crash_key.Value()}); } + if (string_annotations.empty()) { + return; + } command_line->AppendSwitchASCII(kStringAnnotationsSwitch, string_annotations); }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 78f92f6..0694f63 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1797,7 +1797,9 @@ "../browser/extensions/api/runtime/runtime_apitest.cc", "../browser/extensions/api/web_request/web_request_apitest.cc", "../browser/extensions/background_script_executor_browsertest.cc", + "../browser/extensions/cross_origin_isolation_browsertest.cc", "../browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc", + "../browser/extensions/extension_platform_browsertest_browsertest.cc", "../browser/extensions/system_cpu_apitest.cc", "../browser/extensions/system_memory_apitest.cc", "../browser/extensions/test_resources_browsertest.cc", @@ -4220,6 +4222,7 @@ "../browser/extensions/extension_loading_browsertest.cc", "../browser/extensions/extension_modules_apitest.cc", "../browser/extensions/extension_override_apitest.cc", + "../browser/extensions/extension_platform_browsertest_browsertest.cc", "../browser/extensions/extension_resource_request_policy_apitest.cc", "../browser/extensions/extension_security_exploit_browsertest.cc", "../browser/extensions/extension_shared_array_buffer_browsertest.cc",
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index c276be57..9453c3da 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -428,6 +428,7 @@ "commerce/product_specifications:build_grdp", "commerce_internals:build_grdp", "cr_components:build_grdp", + "cr_components/cr_shortcut_input:build_grdp", "cr_components/help_bubble:build_grdp", "cr_components/history_clusters:build_grdp", "cr_components/history_embeddings:build_grdp", @@ -482,6 +483,7 @@ "$target_gen_dir/commerce/product_specifications/resources.grdp", "$target_gen_dir/commerce_internals/resources.grdp", "$target_gen_dir/cr_components/resources.grdp", + "$target_gen_dir/cr_components/cr_shortcut_input/resources.grdp", "$target_gen_dir/cr_components/help_bubble/resources.grdp", "$target_gen_dir/cr_components/history_clusters/resources.grdp", "$target_gen_dir/cr_components/history_embeddings/resources.grdp",
diff --git a/chrome/test/data/webui/chromeos/settings/os_privacy_page/secure_dns_test.ts b/chrome/test/data/webui/chromeos/settings/os_privacy_page/secure_dns_test.ts index c94d76d1..8030d4c 100644 --- a/chrome/test/data/webui/chromeos/settings/os_privacy_page/secure_dns_test.ts +++ b/chrome/test/data/webui/chromeos/settings/os_privacy_page/secure_dns_test.ts
@@ -13,12 +13,11 @@ import {webUIListenerCallback} from 'chrome://resources/js/cr.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -import {SecureDnsInputElement, SettingsSecureDnsDialogElement, SettingsSecureDnsElement, SecureDnsResolverType} from 'chrome://os-settings/lazy_load.js'; +import {SecureDnsInputElement, SettingsSecureDnsElement, SecureDnsResolverType} from 'chrome://os-settings/lazy_load.js'; import {PrivacyPageBrowserProxyImpl, ResolverOption, SecureDnsMode, LocalizedLinkElement, SecureDnsUiManagementMode, SettingsToggleButtonElement} from 'chrome://os-settings/os_settings.js'; -import {assertEquals, assertNull, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {flushTasks} from 'chrome://webui-test/polymer_test_util.js'; import {isVisible} from 'chrome://webui-test/test_util.js'; -import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; import {TestPrivacyPageBrowserProxy} from './test_privacy_page_browser_proxy.js'; @@ -480,198 +479,3 @@ }); }); }); - -suite('SecureDnsDialog', () => { - let testBrowserProxy: TestPrivacyPageBrowserProxy; - let testElement: SettingsSecureDnsElement; - let secureDnsToggle: SettingsToggleButtonElement; - let secureDnsToggleDialog: SettingsSecureDnsDialogElement; - const isDeprecateDnsDialogEnabled = - loadTimeData.getBoolean('isDeprecateDnsDialogEnabled'); - - /** - * Checks that the select menu is shown and the toggle is properly - * configured for showing the configuration options. - */ - function assertResolverSelectShown() { - assertTrue(secureDnsToggle.checked); - assertFalse(testElement.$.resolverSelect.hidden); - } - - function setAndAssertSecureDnsDialog() { - secureDnsToggleDialog = - testElement.shadowRoot!.querySelector('#warningDialog')!; - assertTrue(!!secureDnsToggleDialog); - assertTrue(!!secureDnsToggleDialog.$.dialog); - assertTrue(isVisible(secureDnsToggleDialog.$.cancelButton)); - assertTrue(isVisible(secureDnsToggleDialog.$.disableButton)); - } - - function getResolverOptions(): HTMLElement { - const options = - testElement.shadowRoot!.querySelector<HTMLElement>('#resolverOptions'); - assertTrue(!!options); - return options; - } - - suiteSetup(function() { - loadTimeData.overrideValues({ - showSecureDnsSetting: true, - }); - }); - - setup(async function() { - clearBody(); - - testBrowserProxy = new TestPrivacyPageBrowserProxy(); - PrivacyPageBrowserProxyImpl.setInstance(testBrowserProxy); - testElement = document.createElement('settings-secure-dns'); - testElement.prefs = { - 'dns_over_https': { - 'mode': {'value': SecureDnsMode.AUTOMATIC}, - 'templates': {'value': ''}, - }, - }; - document.body.appendChild(testElement); - - await testBrowserProxy.whenCalled('getSecureDnsSetting'); - await flushTasks(); - - secureDnsToggle = - testElement.shadowRoot!.querySelector('#secureDnsToggle')!; - assertTrue(isVisible(secureDnsToggle)); - - assertResolverSelectShown(); - assertEquals( - SecureDnsResolverType.AUTOMATIC, testElement.$.resolverSelect.value); - }); - - if (isDeprecateDnsDialogEnabled) { - test( - 'No warning dialog appears when secure DNS is toggled off', - async () => { - // Initiate a toggle change from on to off. - secureDnsToggle.click(); - await flushTasks(); - - secureDnsToggleDialog = - testElement.shadowRoot!.querySelector('#warningDialog')!; - assertNull(secureDnsToggleDialog); - assertFalse(secureDnsToggle.checked); - assertTrue(getResolverOptions().hidden); - }); - } else { - test('SecureDnsDialogSanityCheck', () => { - // Initiate a toggle change from on to off, opens the warning dialog. - secureDnsToggle.click(); - flush(); - - setAndAssertSecureDnsDialog(); - }); - - test('SecureDnsDialogCancel', async () => { - // Initiate a toggle change from on to off, opens the warning dialog. - secureDnsToggle.click(); - flush(); - setAndAssertSecureDnsDialog(); - - secureDnsToggleDialog.$.cancelButton.click(); - flush(); - - // Wait for onDisableDnsDialogClosed_ to finish. - await flushTasks(); - await waitAfterNextRender(secureDnsToggle); - - assertFalse(secureDnsToggleDialog.$.dialog.open); - assertTrue(secureDnsToggle.checked); - assertResolverSelectShown(); - assertEquals( - SecureDnsResolverType.AUTOMATIC, testElement.$.resolverSelect.value); - }); - - test('SecureDnsDialogOnToOff', () => { - // Initiate a toggle change from on to off, opens the warning dialog. - secureDnsToggle.click(); - flush(); - setAndAssertSecureDnsDialog(); - - // Turn off the toggle - secureDnsToggleDialog.$.disableButton.click(); - webUIListenerCallback('secure-dns-setting-changed', { - mode: SecureDnsMode.OFF, - config: '', - osMode: SecureDnsMode.OFF, - osConfig: '', - managementMode: SecureDnsUiManagementMode.NO_OVERRIDE, - }); - flush(); - - assertFalse(secureDnsToggle.checked); - assertTrue(getResolverOptions().hidden); - }); - - test('SecureDnsDialogSecureOffToOn', () => { - // If the user selects Custom Secure mode with an invalid input, we will - // not register that the user wants to use secure mode (see the comment on - // secure_dns_dialog.ts), however, when toggled off and on, we will still - // show that the user had selected Custom before. - - // Select Secure in the menu button with no input and config. - webUIListenerCallback('secure-dns-setting-changed', { - mode: SecureDnsMode.SECURE, - config: '', - osMode: SecureDnsMode.SECURE, - osConfig: '', - managementMode: SecureDnsUiManagementMode.NO_OVERRIDE, - }); - testElement.prefs = { - 'dns_over_https': { - 'mode': {'value': SecureDnsMode.SECURE}, - 'templates': {'value': ''}, - }, - }; - - // Simulate that the toggle is off. - webUIListenerCallback('secure-dns-setting-changed', { - mode: SecureDnsMode.OFF, - config: '', - osMode: SecureDnsMode.OFF, - osConfig: '', - managementMode: SecureDnsUiManagementMode.NO_OVERRIDE, - }); - testElement.prefs = { - 'dns_over_https': - {'mode': {'value': SecureDnsMode.OFF}, 'templates': {'value': ''}}, - }; - - // Turn on the toggle - secureDnsToggle.click(); - flush(); - assertTrue(secureDnsToggle.checked); - assertResolverSelectShown(); - assertEquals( - SecureDnsResolverType.CUSTOM, testElement.$.resolverSelect.value); - - // Turn off the toggle, this will dispatch an event from the dialog since - // the invalid Custom secure mode was not registered to the pref. For more - // info, see the comment in secure_dns_dialog.ts. - secureDnsToggle.click(); - flush(); - setAndAssertSecureDnsDialog(); - - secureDnsToggleDialog.$.disableButton.click(); - flush(); - assertFalse(secureDnsToggle.checked); - assertTrue(getResolverOptions().hidden); - - // Turn on the toggle. The selected menu option should still be secure - // mode. - secureDnsToggle.click(); - flush(); - assertTrue(secureDnsToggle.checked); - assertResolverSelectShown(); - assertEquals( - SecureDnsResolverType.CUSTOM, testElement.$.resolverSelect.value); - }); - } -});
diff --git a/chrome/test/data/webui/chromeos/settings/os_settings_browsertest.cc b/chrome/test/data/webui/chromeos/settings/os_settings_browsertest.cc index 976805e..2ba98d8 100644 --- a/chrome/test/data/webui/chromeos/settings/os_settings_browsertest.cc +++ b/chrome/test/data/webui/chromeos/settings/os_settings_browsertest.cc
@@ -293,13 +293,6 @@ ash::features::kCrosPrivacyHub}; }; -class OSSettingsPrivacyTestDeprecateDnsDialogEnabled - : public OSSettingsMochaTest { - private: - base::test::ScopedFeatureList scoped_feature_list_{ - ash::features::kOsSettingsDeprecateDnsDialog}; -}; - using OSSettingsPrivacyPageTestPrivacyHubSubpage = OSSettingsMochaTest; class OSSettingsResetTestSanitizeEnabled : public OSSettingsMochaTest { @@ -1467,17 +1460,6 @@ "runMochaSuite('SettingsSecureDns')"); } -IN_PROC_BROWSER_TEST_F(OSSettingsPrivacyTestDeprecateDnsDialogEnabled, - OsPrivacyPageDeprecateDnsDialog) { - RunSettingsTest("os_privacy_page/secure_dns_test.js", - "runMochaSuite('SecureDnsDialog')"); -} - -IN_PROC_BROWSER_TEST_F(OSSettingsMochaTest, OsPrivacyPageSecureDnsDialog) { - RunSettingsTest("os_privacy_page/secure_dns_test.js", - "runMochaSuite('SecureDnsDialog')"); -} - IN_PROC_BROWSER_TEST_F(OSSettingsMochaTest, OsPrivacyPageSmartPrivacySubpage) { RunSettingsTest("os_privacy_page/smart_privacy_subpage_test.js"); }
diff --git a/chrome/test/data/webui/cr_components/cr_components_interactive_test.cc b/chrome/test/data/webui/cr_components/cr_components_interactive_test.cc index 8f53a0e..3393511 100644 --- a/chrome/test/data/webui/cr_components/cr_components_interactive_test.cc +++ b/chrome/test/data/webui/cr_components/cr_components_interactive_test.cc
@@ -14,6 +14,11 @@ RunTest("cr_components/most_visited_focus_test.js", "mocha.run()"); } +IN_PROC_BROWSER_TEST_F(CrComponentsFocusTest, CrShortcutInput) { + RunTest("cr_components/cr_shortcut_input/cr_shortcut_input_test.js", + "mocha.run()"); +} + class CrComponentsHistoryClustersFocusTest : public WebUIMochaFocusTest { protected: CrComponentsHistoryClustersFocusTest() {
diff --git a/chrome/test/data/webui/cr_components/cr_shortcut_input/BUILD.gn b/chrome/test/data/webui/cr_components/cr_shortcut_input/BUILD.gn new file mode 100644 index 0000000..740747d --- /dev/null +++ b/chrome/test/data/webui/cr_components/cr_shortcut_input/BUILD.gn
@@ -0,0 +1,17 @@ +# 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("//chrome/test/data/webui/build_webui_tests.gni") + +assert(!is_android && !is_ios) + +build_webui_tests("build") { + files = [ "cr_shortcut_input_test.ts" ] + + ts_deps = [ + "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_components/cr_shortcut_input:build_ts", + "//ui/webui/resources/js:build_ts", + ] +}
diff --git a/chrome/test/data/webui/cr_components/cr_shortcut_input/cr_shortcut_input_test.ts b/chrome/test/data/webui/cr_components/cr_shortcut_input/cr_shortcut_input_test.ts new file mode 100644 index 0000000..83fda88 --- /dev/null +++ b/chrome/test/data/webui/cr_components/cr_shortcut_input/cr_shortcut_input_test.ts
@@ -0,0 +1,135 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Suite of tests for cr-shortcut-input. */ + +import 'chrome://resources/cr_components/cr_shortcut_input/cr_shortcut_input.js'; + +import type {CrShortcutInputElement} from 'chrome://resources/cr_components/cr_shortcut_input/cr_shortcut_input.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import type {Modifier} from 'chrome://webui-test/keyboard_mock_interactions.js'; +import {keyDownOn, keyUpOn} from 'chrome://webui-test/keyboard_mock_interactions.js'; +import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; + +suite('CrShortcutInputTest', function() { + let input: CrShortcutInputElement; + + setup(function() { + loadTimeData.resetForTesting({ + shortcutSet: 'set', + shortcutNotSet: 'not set', + shortcutTypeAShortcut: 'type', + shortcutIncludeStartModifier: 'include modifier', + shortcutTooManyModifiers: 'too many modifier', + shortcutNeedCharacter: 'need character', + }); + document.body.innerHTML = window.trustedTypes!.emptyHTML; + input = document.createElement('cr-shortcut-input'); + document.body.appendChild(input); + }); + + async function assertError( + isKeyDown: boolean, keyCode: number, modifiers: Modifier[], + expectedErrorStringId: string) { + const field = input.$.input; + if (isKeyDown) { + keyDownOn(field, keyCode, modifiers); + } else { + keyUpOn(field, keyCode, modifiers); + } + await microtasksFinished(); + assertEquals('', field.value); + assertEquals( + loadTimeData.getString(expectedErrorStringId), field.errorMessage); + } + + test('Basic', async function() { + const field = input.$.input; + assertEquals('', field.value); + + // Click the edit button. Capture should start. + const whenInputCaptureChange = + eventToPromise('input-capture-change', input); + input.$.edit.click(); + let event = await whenInputCaptureChange; + assertTrue(event.detail); + await microtasksFinished(); + assertEquals('', field.value); + + // Press character. + await assertError(true, 65, [], 'shortcutIncludeStartModifier'); + // Add shift to character. + await assertError(true, 65, ['shift'], 'shortcutIncludeStartModifier'); + // Press ctrl. + await assertError(true, 17, ['ctrl'], 'shortcutNeedCharacter'); + // Add shift. + await assertError(true, 17, ['ctrl', 'shift'], 'shortcutNeedCharacter'); + // Remove shift. + await assertError(false, 17, ['ctrl'], 'shortcutNeedCharacter'); + // Add alt (ctrl + alt is invalid). + await assertError(true, 17, ['ctrl', 'alt'], 'shortcutTooManyModifiers'); + // Remove alt. + await assertError(false, 17, ['ctrl'], 'shortcutNeedCharacter'); + + // Add 'A'. Once a valid shortcut is typed (like Ctrl + A), it is + // committed. + const whenShortcutUpdate = eventToPromise('shortcut-updated', input); + keyDownOn(field, 65, ['ctrl']); + event = await whenShortcutUpdate; + assertEquals('Ctrl+A', event.detail); + + await microtasksFinished(); + assertEquals('Ctrl + A', field.value); + assertEquals('Ctrl+A', input.shortcut); + + // Test clearing the shortcut. + const clearShortcutPromise = eventToPromise('shortcut-updated', input); + input.$.edit.click(); + assertEquals(input.$.input, input.shadowRoot!.activeElement); + event = await clearShortcutPromise; + await microtasksFinished(); + assertEquals('', event.detail); + field.blur(); + assertEquals('', input.shortcut); + + // The `input-capture-change` event should happen twice when the edit button + // is clicked. The first event is triggered when the mouse down happens and + // the input capture should stop. The second event occurs during mouse up + // which triggers the button to start the input capture again. + const inputCaptureChangeResults: boolean[] = []; + input.addEventListener('input-capture-change', (e) => { + inputCaptureChangeResults.push((e as CustomEvent<boolean>).detail); + }); + + input.$.edit.click(); + await microtasksFinished(); + assertEquals(2, inputCaptureChangeResults.length); + assertFalse(inputCaptureChangeResults[0]!); + assertTrue(inputCaptureChangeResults[1]!); + + // Test ending capture using the escape key. + const stopInputCapturePromise = + eventToPromise('input-capture-change', input); + input.$.edit.click(); + keyDownOn(field, 27); // Escape key. + event = await stopInputCapturePromise; + assertFalse(event.detail); + }); + + test('AriaLabelUpdates', async function() { + // Verify that the aria labels are initially empty + assertEquals('', input.$.input.ariaLabel); + assertEquals('', input.$.edit.ariaLabel); + + // Update the input and edit button aria labels + const inputAriaLabel = 'input'; + const editButtonAriaLabel = 'edit'; + input.inputAriaLabel = inputAriaLabel; + input.editButtonAriaLabel = editButtonAriaLabel; + await microtasksFinished(); + assertEquals(inputAriaLabel, input.$.input.ariaLabel); + assertEquals(editButtonAriaLabel, input.$.edit.ariaLabel); + }); +});
diff --git a/chrome/test/data/webui/extensions/BUILD.gn b/chrome/test/data/webui/extensions/BUILD.gn index 12dd6f0..284012a0 100644 --- a/chrome/test/data/webui/extensions/BUILD.gn +++ b/chrome/test/data/webui/extensions/BUILD.gn
@@ -40,7 +40,6 @@ "review_panel_test.ts", "runtime_host_permissions_test.ts", "runtime_hosts_dialog_test.ts", - "shortcut_input_test.ts", "sidebar_test.ts", "site_permissions_by_site_test.ts", "site_permissions_edit_permissions_dialog_test.ts", @@ -62,6 +61,7 @@ ts_deps = [ "//chrome/browser/resources/extensions:build_ts", "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_components/cr_shortcut_input:build_ts", "//ui/webui/resources/cr_elements:build_ts", "//ui/webui/resources/js:build_ts", ]
diff --git a/chrome/test/data/webui/extensions/extensions_browsertest.cc b/chrome/test/data/webui/extensions/extensions_browsertest.cc index e68ae1f..cc60afe 100644 --- a/chrome/test/data/webui/extensions/extensions_browsertest.cc +++ b/chrome/test/data/webui/extensions/extensions_browsertest.cc
@@ -719,14 +719,6 @@ RunTestCase("Layout"); } -IN_PROC_BROWSER_TEST_F(CrExtensionsShortcutTest, IsValidKeyCode) { - RunTestCase("IsValidKeyCode"); -} - -IN_PROC_BROWSER_TEST_F(CrExtensionsShortcutTest, KeyStrokeToString) { - RunTestCase("KeyStrokeToString"); -} - IN_PROC_BROWSER_TEST_F(CrExtensionsShortcutTest, ScopeChange) { RunTestCase("ScopeChange"); }
diff --git a/chrome/test/data/webui/extensions/extensions_focus_test.cc b/chrome/test/data/webui/extensions/extensions_focus_test.cc index b521d2b..1b9361cb 100644 --- a/chrome/test/data/webui/extensions/extensions_focus_test.cc +++ b/chrome/test/data/webui/extensions/extensions_focus_test.cc
@@ -8,13 +8,6 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" -using CrExtensionsShortcutInputTest = WebUIMochaFocusTest; -IN_PROC_BROWSER_TEST_F(CrExtensionsShortcutInputTest, Basic) { - set_test_loader_host(chrome::kChromeUIExtensionsHost); - RunTest("extensions/shortcut_input_test.js", - "runMochaTest('ExtensionShortcutInputTest', 'Basic')"); -} - class CrExtensionsFocusTest : public WebUIMochaFocusTest { protected: CrExtensionsFocusTest() { @@ -27,6 +20,11 @@ "runMochaTest('ExtensionManagerUnitTest', 'UninstallFocus')"); } +IN_PROC_BROWSER_TEST_F(CrExtensionsFocusTest, UpdateShortcut) { + RunTest("extensions/keyboard_shortcuts_test.js", + "runMochaTest('ExtensionShortcutTest', 'UpdateShortcut')"); +} + class CrExtensionsOptionsPageTest : public ExtensionSettingsTestBase { protected: void OnWebContentsAvailable(content::WebContents* web_contents) override {
diff --git a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts index 0c0beb07..8929990 100644 --- a/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts +++ b/chrome/test/data/webui/extensions/keyboard_shortcuts_test.ts
@@ -4,9 +4,11 @@ /** @fileoverview Suite of tests for extension-keyboard-shortcuts. */ +import 'chrome://extensions/extensions.js'; + import type {ExtensionsKeyboardShortcutsElement} from 'chrome://extensions/extensions.js'; -import {isValidKeyCode, Key, keystrokeToString} from 'chrome://extensions/extensions.js'; -import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {keyDownOn, keyUpOn} from 'chrome://webui-test/keyboard_mock_interactions.js'; import {isChildVisible, microtasksFinished} from 'chrome://webui-test/test_util.js'; import {TestService} from './test_service.js'; @@ -88,34 +90,6 @@ assertEquals(2, commands.length); }); - test('IsValidKeyCode', function() { - assertTrue(isValidKeyCode('A'.charCodeAt(0))); - assertTrue(isValidKeyCode('F'.charCodeAt(0))); - assertTrue(isValidKeyCode('Z'.charCodeAt(0))); - assertTrue(isValidKeyCode('4'.charCodeAt(0))); - assertTrue(isValidKeyCode(Key.PAGE_UP)); - assertTrue(isValidKeyCode(Key.MEDIA_PLAY_PAUSE)); - assertTrue(isValidKeyCode(Key.DOWN)); - assertFalse(isValidKeyCode(16)); // Shift - assertFalse(isValidKeyCode(17)); // Ctrl - assertFalse(isValidKeyCode(18)); // Alt - assertFalse(isValidKeyCode(113)); // F2 - assertFalse(isValidKeyCode(144)); // Num Lock - assertFalse(isValidKeyCode(43)); // + - assertFalse(isValidKeyCode(27)); // Escape - }); - - test('KeyStrokeToString', function() { - const charCodeA = 'A'.charCodeAt(0); - let e = new KeyboardEvent('keydown', {keyCode: charCodeA}); - assertEquals('A', keystrokeToString(e)); - e = new KeyboardEvent('keydown', {keyCode: charCodeA, ctrlKey: true}); - assertEquals('Ctrl+A', keystrokeToString(e)); - e = new KeyboardEvent( - 'keydown', {keyCode: charCodeA, ctrlKey: true, shiftKey: true}); - assertEquals('Ctrl+Shift+A', keystrokeToString(e)); - }); - test('ScopeChange', async function() { const selectElement = keyboardShortcuts.shadowRoot!.querySelector('select'); assertTrue(!!selectElement); @@ -128,4 +102,100 @@ await microtasksFinished(); assertEquals(selectElement.value, params[2]); }); + + + test('UpdateShortcut', async function() { + const shortcutInput = + keyboardShortcuts.shadowRoot!.querySelector('cr-shortcut-input'); + assertTrue(!!shortcutInput); + const field = shortcutInput.$.input; + assertEquals('Ctrl + W', field.value); + + // Click the edit button. Capture should start. + shortcutInput.$.edit.click(); + let arg = await testDelegate.whenCalled('setShortcutHandlingSuspended'); + + assertTrue(arg); + testDelegate.reset(); + await microtasksFinished(); + assertEquals('', field.value); + + // Press character. + keyDownOn(field, 65, []); + await microtasksFinished(); + assertEquals('', field.value); + assertTrue(field.errorMessage!.startsWith('Include')); + // Add shift to character. + keyDownOn(field, 65, ['shift']); + await microtasksFinished(); + assertEquals('', field.value); + assertTrue(field.errorMessage!.startsWith('Include')); + // Press ctrl. + keyDownOn(field, 17, ['ctrl']); + await microtasksFinished(); + assertEquals('', field.value); + assertEquals('Type a letter', field.errorMessage); + // Add shift. + keyDownOn(field, 16, ['ctrl', 'shift']); + await microtasksFinished(); + assertEquals('', field.value); + assertEquals('Type a letter', field.errorMessage); + // Remove shift. + keyUpOn(field, 16, ['ctrl']); + await microtasksFinished(); + assertEquals('', field.value); + assertEquals('Type a letter', field.errorMessage); + // Add alt (ctrl + alt is invalid). + keyDownOn(field, 18, ['ctrl', 'alt']); + await microtasksFinished(); + assertEquals('', field.value); + // Remove alt. + keyUpOn(field, 18, ['ctrl']); + await microtasksFinished(); + assertEquals('', field.value); + assertEquals('Type a letter', field.errorMessage); + + // Add 'A'. Once a valid shortcut is typed (like Ctrl + A), it is + // committed. + keyDownOn(field, 65, ['ctrl']); + arg = await testDelegate.whenCalled('updateExtensionCommandKeybinding'); + testDelegate.reset(); + + assertDeepEquals( + [oneCommand.id, oneCommand.commands[0]!.name, 'Ctrl+A'], arg); + await microtasksFinished(); + assertEquals('Ctrl + A', field.value); + assertEquals('Ctrl+A', shortcutInput.shortcut); + + // Test clearing the shortcut. + shortcutInput.$.edit.click(); + assertEquals( + shortcutInput.$.input, shortcutInput.shadowRoot!.activeElement); + arg = await testDelegate.whenCalled('updateExtensionCommandKeybinding'); + await microtasksFinished(); + + field.blur(); + testDelegate.reset(); + assertDeepEquals([oneCommand.id, oneCommand.commands[0]!.name, ''], arg); + assertEquals('', shortcutInput.shortcut); + + // The click event causes the input element to lose focus on mouse down + // but regains focus on mouse up after triggering the edit button on mouse + // up. This should ultimately result in shortcuts being suspended. + shortcutInput.$.edit.click(); + await testDelegate.whenCalled('setShortcutHandlingSuspended'); + await microtasksFinished(); + const shortcutSuspendedArgs = + testDelegate.getArgs('setShortcutHandlingSuspended'); + assertEquals(2, testDelegate.getCallCount('setShortcutHandlingSuspended')); + assertFalse(shortcutSuspendedArgs[0]); + assertTrue(shortcutSuspendedArgs[1]); + testDelegate.reset(); + + // Test ending capture using the escape key. + shortcutInput.$.edit.click(); + keyDownOn(field, 27); // Escape key. + arg = await testDelegate.whenCalled('setShortcutHandlingSuspended'); + assertFalse(arg); + }); });
diff --git a/chrome/test/data/webui/extensions/shortcut_input_test.ts b/chrome/test/data/webui/extensions/shortcut_input_test.ts deleted file mode 100644 index 353758d..0000000 --- a/chrome/test/data/webui/extensions/shortcut_input_test.ts +++ /dev/null
@@ -1,124 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** @fileoverview Suite of tests for extension-shortcut-input. */ - -import 'chrome://extensions/extensions.js'; - -import type {ExtensionsShortcutInputElement} from 'chrome://extensions/extensions.js'; -import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; -import {keyDownOn, keyUpOn} from 'chrome://webui-test/keyboard_mock_interactions.js'; -import {microtasksFinished} from 'chrome://webui-test/test_util.js'; - -import {TestService} from './test_service.js'; -import {createExtensionInfo} from './test_util.js'; - -suite('ExtensionShortcutInputTest', function() { - let input: ExtensionsShortcutInputElement; - let testService: TestService; - - setup(function() { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - input = document.createElement('extensions-shortcut-input'); - testService = new TestService(); - input.delegate = testService; - input.command = { - description: 'Command description', - keybinding: 'Ctrl+W', - name: 'Command', - isActive: true, - scope: chrome.developerPrivate.CommandScope.CHROME, - isExtensionAction: true, - }; - input.item = createExtensionInfo({id: 'itemid'}); - document.body.appendChild(input); - }); - - test('Basic', async function() { - const field = input.$.input; - assertEquals('', field.value); - - // Click the edit button. Capture should start. - input.$.edit.click(); - let arg = await testService.whenCalled('setShortcutHandlingSuspended'); - - assertTrue(arg); - testService.reset(); - await microtasksFinished(); - assertEquals('', field.value); - - // Press character. - keyDownOn(field, 65, []); - await microtasksFinished(); - assertEquals('', field.value); - assertTrue(field.errorMessage!.startsWith('Include')); - // Add shift to character. - keyDownOn(field, 65, ['shift']); - await microtasksFinished(); - assertEquals('', field.value); - assertTrue(field.errorMessage!.startsWith('Include')); - // Press ctrl. - keyDownOn(field, 17, ['ctrl']); - await microtasksFinished(); - assertEquals('', field.value); - assertEquals('Type a letter', field.errorMessage); - // Add shift. - keyDownOn(field, 16, ['ctrl', 'shift']); - await microtasksFinished(); - assertEquals('', field.value); - assertEquals('Type a letter', field.errorMessage); - // Remove shift. - keyUpOn(field, 16, ['ctrl']); - await microtasksFinished(); - assertEquals('', field.value); - assertEquals('Type a letter', field.errorMessage); - // Add alt (ctrl + alt is invalid). - keyDownOn(field, 18, ['ctrl', 'alt']); - await microtasksFinished(); - assertEquals('', field.value); - // Remove alt. - keyUpOn(field, 18, ['ctrl']); - await microtasksFinished(); - assertEquals('', field.value); - assertEquals('Type a letter', field.errorMessage); - - // Add 'A'. Once a valid shortcut is typed (like Ctrl + A), it is - // committed. - keyDownOn(field, 65, ['ctrl']); - arg = await testService.whenCalled('updateExtensionCommandKeybinding'); - - testService.reset(); - assertDeepEquals(['itemid', 'Command', 'Ctrl+A'], arg); - await microtasksFinished(); - assertEquals('Ctrl + A', field.value); - assertEquals('Ctrl+A', input.shortcut); - - // Test clearing the shortcut. - input.$.edit.click(); - assertEquals(input.$.input, input.shadowRoot!.activeElement); - arg = await testService.whenCalled('updateExtensionCommandKeybinding'); - await microtasksFinished(); - - field.blur(); - testService.reset(); - assertDeepEquals(['itemid', 'Command', ''], arg); - assertEquals('', input.shortcut); - - input.$.edit.click(); - arg = await testService.whenCalled('setShortcutHandlingSuspended'); - await microtasksFinished(); - testService.reset(); - assertTrue(arg); - - // Test ending capture using the escape key. - input.$.edit.click(); - keyDownOn(field, 27); // Escape key. - arg = await testService.whenCalled('setShortcutHandlingSuspended'); - assertFalse(arg); - }); -});
diff --git a/chrome/browser/ash/arc/video/BUILD.gn b/chromeos/ash/experiences/arc/video/BUILD.gn similarity index 100% rename from chrome/browser/ash/arc/video/BUILD.gn rename to chromeos/ash/experiences/arc/video/BUILD.gn
diff --git a/chromeos/ash/experiences/arc/video/DEPS b/chromeos/ash/experiences/arc/video/DEPS new file mode 100644 index 0000000..d6e2030 --- /dev/null +++ b/chromeos/ash/experiences/arc/video/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+content/public/common/content_switches.h", +]
diff --git a/chrome/browser/ash/arc/video/OWNERS b/chromeos/ash/experiences/arc/video/OWNERS similarity index 100% rename from chrome/browser/ash/arc/video/OWNERS rename to chromeos/ash/experiences/arc/video/OWNERS
diff --git a/chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc b/chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc similarity index 99% rename from chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc rename to chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc index 3149de6..711c73b 100644 --- a/chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc +++ b/chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ash/arc/video/gpu_arc_video_service_host.h" +#include "chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h" #include <memory> #include <string>
diff --git a/chrome/browser/ash/arc/video/gpu_arc_video_service_host.h b/chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h similarity index 90% rename from chrome/browser/ash/arc/video/gpu_arc_video_service_host.h rename to chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h index a105912..5cb9be2d 100644 --- a/chrome/browser/ash/arc/video/gpu_arc_video_service_host.h +++ b/chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_ASH_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_ -#define CHROME_BROWSER_ASH_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_ +#ifndef CHROMEOS_ASH_EXPERIENCES_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_ +#define CHROMEOS_ASH_EXPERIENCES_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_ #include "ash/components/arc/mojom/video.mojom.h" #include "base/memory/raw_ptr.h" @@ -70,4 +70,4 @@ } // namespace arc -#endif // CHROME_BROWSER_ASH_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_ +#endif // CHROMEOS_ASH_EXPERIENCES_ARC_VIDEO_GPU_ARC_VIDEO_SERVICE_HOST_H_
diff --git a/clank b/clank index ff16da3..13571b2 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit ff16da349347e90984ba1bc206cafccad280e3c6 +Subproject commit 13571b2b78ee3c8179518064da9ed1a3f5da47ec
diff --git a/components/autofill/android/java/strings/autofill_strings.grd b/components/autofill/android/java/strings/autofill_strings.grd index 1c7b7eccb..7b6333f 100644 --- a/components/autofill/android/java/strings/autofill_strings.grd +++ b/components/autofill/android/java/strings/autofill_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="values-af/autofill_strings.xml" lang="af" type="android" /> <output filename="values-am/autofill_strings.xml" lang="am" type="android" />
diff --git a/components/autofill/core/browser/geo/autofill_address_rewriter_resources.grd b/components/autofill/core/browser/geo/autofill_address_rewriter_resources.grd index 1db1460..51329f19 100644 --- a/components/autofill/core/browser/geo/autofill_address_rewriter_resources.grd +++ b/components/autofill/core/browser/geo/autofill_address_rewriter_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> - <grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/autofill_address_rewriter_resources.h" type="rc_header"> <emit emit_type="prepend">
diff --git a/components/components_chromium_strings.grd b/components/components_chromium_strings.grd index 87c402d..a5a2c3bed 100644 --- a/components/components_chromium_strings.grd +++ b/components/components_chromium_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/components_branded_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/components_google_chrome_strings.grd b/components/components_google_chrome_strings.grd index e237c2d..f248690 100644 --- a/components/components_google_chrome_strings.grd +++ b/components/components_google_chrome_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/components_branded_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/components_locale_settings.grd b/components/components_locale_settings.grd index 8a13525..4adcb82 100644 --- a/components/components_locale_settings.grd +++ b/components/components_locale_settings.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/components_locale_settings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/components_strings.grd b/components/components_strings.grd index 3f6b44c..a9a79dc 100644 --- a/components/components_strings.grd +++ b/components/components_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/components_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd b/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd index aa1a7d7..2a9e9b7 100644 --- a/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd +++ b/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="values-af/web_contents_delegate_android_strings.xml" lang="af" type="android" /> <output filename="values-am/web_contents_delegate_android_strings.xml" lang="am" type="android" />
diff --git a/components/headless/command_handler/headless_command.grd b/components/headless/command_handler/headless_command.grd index 7c1277e..6b5f538 100644 --- a/components/headless/command_handler/headless_command.grd +++ b/components/headless/command_handler/headless_command.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/headless_command_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/javascript_dialogs/android/javascript_dialogs_android_strings.grd b/components/javascript_dialogs/android/javascript_dialogs_android_strings.grd index 3ad3c461..b8d0435 100644 --- a/components/javascript_dialogs/android/javascript_dialogs_android_strings.grd +++ b/components/javascript_dialogs/android/javascript_dialogs_android_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="values-af/javascript_dialogs_android_strings.xml" lang="af" type="android" /> <output filename="values-am/javascript_dialogs_android_strings.xml" lang="am" type="android" />
diff --git a/components/media_router/browser/android/java/strings/android_chrome_media_router_strings.grd b/components/media_router/browser/android/java/strings/android_chrome_media_router_strings.grd index 00bc45f..65193ae 100644 --- a/components/media_router/browser/android/java/strings/android_chrome_media_router_strings.grd +++ b/components/media_router/browser/android/java/strings/android_chrome_media_router_strings.grd
@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- android_chrome_strings.grd contains strings for VR DFM of Chrome for Android. --> -<grit current_release="1" latest_public_release="0" output_all_resource_defines="false"> +<grit current_release="1" latest_public_release="0"> <outputs> <output filename="values-af/android_chrome_media_router_strings.xml" lang="af" type="android" /> <output filename="values-am/android_chrome_media_router_strings.xml" lang="am" type="android" />
diff --git a/components/metrics/server_urls.grd b/components/metrics/server_urls.grd index 21c7785a..c92980f 100644 --- a/components/metrics/server_urls.grd +++ b/components/metrics/server_urls.grd
@@ -10,7 +10,7 @@ rather than the code. --> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/metrics_server_urls.h" type="rc_header"> <emit emit_type="prepend"></emit>
diff --git a/components/omnibox/resources/omnibox_pedal_synonyms.grd b/components/omnibox/resources/omnibox_pedal_synonyms.grd index 2d363bc..f0534099 100644 --- a/components/omnibox/resources/omnibox_pedal_synonyms.grd +++ b/components/omnibox/resources/omnibox_pedal_synonyms.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/omnibox_pedal_synonyms.h" type="rc_header">
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index c37133f..d2dce19 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit c37133f0c4465e77570c88c93940ae58a3d35819 +Subproject commit d2dce193b98bc200e7b9d6c6894fe09fdcb16b39
diff --git a/components/optimization_guide/proto/model_quality_service.proto b/components/optimization_guide/proto/model_quality_service.proto index 1d73200..9c376d31 100644 --- a/components/optimization_guide/proto/model_quality_service.proto +++ b/components/optimization_guide/proto/model_quality_service.proto
@@ -22,6 +22,7 @@ import "components/optimization_guide/proto/features/model_prototyping.proto"; import "components/optimization_guide/proto/features/password_change_submission.proto"; import "components/optimization_guide/proto/features/product_specifications.proto"; +import "components/optimization_guide/proto/features/scam_detection.proto"; import "components/optimization_guide/proto/features/tab_organization.proto"; import "components/optimization_guide/proto/features/wallpaper_search.proto"; import "components/optimization_guide/proto/features/bling_prototyping.proto"; @@ -61,6 +62,8 @@ PasswordChangeSubmissionLoggingData password_change_submission = 16; + ScamDetectionLoggingData scam_detection = 17; + DefaultLoggingData default = 1000; } }
diff --git a/components/permissions/android/permissions_android_strings.grd b/components/permissions/android/permissions_android_strings.grd index 4abcbed..ec8aaa6a 100644 --- a/components/permissions/android/permissions_android_strings.grd +++ b/components/permissions/android/permissions_android_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="values-af/permissions_android_strings.xml" lang="af" type="android" /> <output filename="values-am/permissions_android_strings.xml" lang="am" type="android" />
diff --git a/components/policy/resources/policy_templates.build.grd b/components/policy/resources/policy_templates.build.grd index c11f282..be5d20c 100644 --- a/components/policy/resources/policy_templates.build.grd +++ b/components/policy/resources/policy_templates.build.grd
@@ -11,7 +11,7 @@ --> <grit base_dir="." latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="app/policy/translations/policy_templates_de-DE.json" type="policy_templates" lang="de" /> <output filename="app/policy/translations/policy_templates_en-US.json" type="policy_templates" lang="en" />
diff --git a/components/policy/resources/policy_templates.grd b/components/policy/resources/policy_templates.grd index 50ac319..05d6572 100644 --- a/components/policy/resources/policy_templates.grd +++ b/components/policy/resources/policy_templates.grd
@@ -15,7 +15,7 @@ --> <grit base_dir="." latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="app/policy/translations/policy_templates_de-DE.json" type="policy_templates" lang="de" /> <output filename="app/policy/translations/policy_templates_en-US.json" type="policy_templates" lang="en" />
diff --git a/components/privacy_sandbox/android/BUILD.gn b/components/privacy_sandbox/android/BUILD.gn index 8a62fde..e2fa9ef 100644 --- a/components/privacy_sandbox/android/BUILD.gn +++ b/components/privacy_sandbox/android/BUILD.gn
@@ -51,6 +51,7 @@ ":java", "//base:base_java", "//base:base_java_test_support", + "//components/browser_ui/settings/android:java", "//components/browser_ui/settings/android:test_support_java", "//components/browser_ui/site_settings/android:java", "//components/content_settings/android:content_settings_enums_java", @@ -62,6 +63,7 @@ "//third_party/androidx:androidx_appcompat_appcompat_resources_java", "//third_party/androidx:androidx_preference_preference_java", "//third_party/androidx:androidx_recyclerview_recyclerview_java", + "//third_party/androidx:androidx_test_core_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/hamcrest:hamcrest_java", "//third_party/hamcrest:hamcrest_library_java",
diff --git a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettingsTest.java b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettingsTest.java index 66a5aee..4b09689 100644 --- a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettingsTest.java +++ b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettingsTest.java
@@ -5,18 +5,22 @@ package org.chromium.components.privacy_sandbox; import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.chromium.ui.test.util.ViewUtils.clickOnClickableSpan; + import android.content.Context; +import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.contrib.RecyclerViewActions; import androidx.test.filters.SmallTest; @@ -26,6 +30,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.chromium.base.library_loader.LibraryLoader; @@ -33,6 +38,7 @@ import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.Batch; import org.chromium.components.browser_ui.settings.BlankUiTestActivitySettingsTestRule; +import org.chromium.components.browser_ui.settings.SettingsCustomTabLauncher; import org.chromium.components.browser_ui.site_settings.SiteSettingsDelegate; import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni; @@ -54,6 +60,8 @@ @Mock private SiteSettingsDelegate mSiteSettingsDelegate; + @Mock private SettingsCustomTabLauncher mCustomTabLauncher; + private TrackingProtectionSettings mFragment; @BeforeClass @@ -79,13 +87,15 @@ (fragment) -> { ((TrackingProtectionSettings) fragment) .setTrackingProtectionDelegate(mDelegate); + ((TrackingProtectionSettings) fragment) + .setCustomTabLauncher(mCustomTabLauncher); }); mFragment = (TrackingProtectionSettings) mSettingsRule.getPreferenceFragment(); } @Test @SmallTest - public void testShowTrackingProtectionRewindUi() { + public void launchTrackingProtectionPage() { when(mDelegate.isBlockAll3pcEnabled()).thenReturn(true); when(mDelegate.isDoNotTrackEnabled()).thenReturn(true); @@ -97,7 +107,7 @@ @Test @SmallTest - public void testIpFpProtectionsDisplayedInLaunchUi() { + public void launchWithIpAndFpProtection_extraContentDisplayed() { when(mDelegate.isBlockAll3pcEnabled()).thenReturn(true); when(mDelegate.isDoNotTrackEnabled()).thenReturn(true); when(mDelegate.shouldDisplayIpProtection()).thenReturn(true); @@ -105,12 +115,66 @@ launchTrackingProtectionSettings(); + Context context = ApplicationProvider.getApplicationContext(); + String fp_protection = + context.getString(R.string.tracking_protection_fingerprinting_protection_learn_more) + .replaceAll("<link>|</link>", ""); + String ip_protection = + context.getString(R.string.tracking_protection_ip_protection_learn_more) + .replaceAll("<link>|</link>", ""); + onView(withId(R.id.recycler_view)) + .perform(RecyclerViewActions.scrollTo(hasDescendant(withText(fp_protection)))); + onView(withText(ip_protection)).check(matches(isDisplayed())); + onView(withText(fp_protection)).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void changeToggleValues_propagatedToBackend() { + when(mDelegate.isBlockAll3pcEnabled()).thenReturn(true); + when(mDelegate.isDoNotTrackEnabled()).thenReturn(true); + when(mDelegate.shouldDisplayIpProtection()).thenReturn(true); + when(mDelegate.shouldDisplayFingerprintingProtection()).thenReturn(true); + when(mDelegate.isIpProtectionEnabled()).thenReturn(false); + when(mDelegate.isFingerprintingProtectionEnabled()).thenReturn(false); + + launchTrackingProtectionSettings(); + + onView(withText(R.string.tracking_protection_block_cookies_toggle_title)).perform(click()); + verify(mDelegate).setBlockAll3pc(/* enabled= */ Mockito.eq(false)); + onView(withId(R.id.recycler_view)) .perform( RecyclerViewActions.scrollTo( - hasDescendant(withText(containsString("Learn how limiting"))))); - onView(withText(containsString("Learn how IP protection"))).check(matches(isDisplayed())); - onView(withText(containsString("Learn how limiting digital"))) - .check(matches(isDisplayed())); + hasDescendant( + withText( + R.string + .tracking_protection_fingerprinting_protection_title)))); + + onView(withText(R.string.tracking_protection_ip_protection_toggle_title)).perform(click()); + verify(mDelegate).setIpProtection(/* enabled= */ Mockito.eq(true)); + onView(withText(R.string.tracking_protection_fingerprinting_protection_title)) + .perform(click()); + verify(mDelegate).setFingerprintingProtection(/* enabled= */ Mockito.eq(true)); + } + + @Test + @SmallTest + public void clickOnLearnMore_cctIsOpened() { + when(mDelegate.isBlockAll3pcEnabled()).thenReturn(true); + when(mDelegate.isDoNotTrackEnabled()).thenReturn(true); + + launchTrackingProtectionSettings(); + + Context context = ApplicationProvider.getApplicationContext(); + String tp_learn_more = + context.getString( + R.string.privacy_sandbox_tracking_protection_bullet_two_description) + .replaceAll("<link>|</link>", ""); + onView(withText(tp_learn_more)).perform(clickOnClickableSpan(/* spanIndex= */ 0)); + verify(mCustomTabLauncher) + .openUrlInCct( + /* context= */ Mockito.any(), + /* url= */ Mockito.eq(TrackingProtectionSettings.LEARN_MORE_URL)); } }
diff --git a/components/privacy_sandbox_strings.grd b/components/privacy_sandbox_strings.grd index 7ae46df..57849ce 100644 --- a/components/privacy_sandbox_strings.grd +++ b/components/privacy_sandbox_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/privacy_sandbox_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/search_engine_descriptions_strings.grd b/components/search_engine_descriptions_strings.grd index 8c18b5b..b1568f7 100644 --- a/components/search_engine_descriptions_strings.grd +++ b/components/search_engine_descriptions_strings.grd
@@ -3,7 +3,7 @@ locale. The strings in the file are not translated by the Chrome translation process but are generated using a script that pulls them from outside the repo.--> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/search_engine_descriptions_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc index f6a06a8..6b69163 100644 --- a/components/user_manager/fake_user_manager.cc +++ b/components/user_manager/fake_user_manager.cc
@@ -59,28 +59,6 @@ return user; } -UserList FakeUserManager::GetUsersAllowedForMultiUserSignIn() const { - UserList result; - for (UserList::const_iterator it = users_.begin(); it != users_.end(); ++it) { - if ((*it)->GetType() == UserType::kRegular && !(*it)->is_logged_in()) { - result.push_back(*it); - } - } - return result; -} - -void FakeUserManager::UpdateUserAccountData( - const AccountId& account_id, - const UserAccountData& account_data) { - for (User* user : users_) { - if (user->GetAccountId() == account_id) { - user->set_display_name(account_data.display_name()); - user->set_given_name(account_data.given_name()); - return; - } - } -} - void FakeUserManager::LogoutAllUsers() { primary_user_ = nullptr; active_user_ = nullptr; @@ -154,28 +132,6 @@ } } -void FakeUserManager::SaveUserDisplayName(const AccountId& account_id, - const std::u16string& display_name) { - for (UserList::iterator it = users_.begin(); it != users_.end(); ++it) { - if ((*it)->GetAccountId() == account_id) { - (*it)->set_display_name(display_name); - return; - } - } -} - -const UserList& FakeUserManager::GetLRULoggedInUsers() const { - return users_; -} - -UserList FakeUserManager::GetUnlockUsers() const { - return users_; -} - -bool FakeUserManager::IsKnownUser(const AccountId& account_id) const { - return true; -} - bool FakeUserManager::IsUserNonCryptohomeDataEphemeral( const AccountId& account_id) const { return base::Contains(accounts_with_ephemeral_non_cryptohome_data_,
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h index df6ccb4..4ca5ab40 100644 --- a/components/user_manager/fake_user_manager.h +++ b/components/user_manager/fake_user_manager.h
@@ -54,33 +54,11 @@ bool is_ephemeral); // UserManager overrides. - UserList GetUsersAllowedForMultiUserSignIn() const override; - void UpdateUserAccountData(const AccountId& account_id, - const UserAccountData& account_data) override; - - // Set the user as logged in. void UserLoggedIn(const AccountId& account_id, const std::string& username_hash, bool browser_restart, bool is_child) override; - void SwitchActiveUser(const AccountId& account_id) override; - void SaveUserDisplayName(const AccountId& account_id, - const std::u16string& display_name) override; - - // Not implemented. - void Shutdown() override {} - const UserList& GetLRULoggedInUsers() const override; - UserList GetUnlockUsers() const override; - void OnSessionStarted() override {} - bool IsKnownUser(const AccountId& account_id) const override; - void SaveUserOAuthStatus(const AccountId& account_id, - User::OAuthTokenStatus oauth_token_status) override { - } - void SaveForceOnlineSignin(const AccountId& account_id, - bool force_online_signin) override {} - void SaveUserDisplayEmail(const AccountId& account_id, - const std::string& display_email) override {} bool IsUserNonCryptohomeDataEphemeral( const AccountId& account_id) const override; bool IsUserCryptohomeDataEphemeral(
diff --git a/components/webapps/browser/android/android_webapps_strings.grd b/components/webapps/browser/android/android_webapps_strings.grd index 47a0e3e71..b0cca84 100644 --- a/components/webapps/browser/android/android_webapps_strings.grd +++ b/components/webapps/browser/android/android_webapps_strings.grd
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="values-af/android_webapps_strings.xml" lang="af" type="android" /> <output filename="values-am/android_webapps_strings.xml" lang="am" type="android" />
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc index 0631d42..02677b3 100644 --- a/content/browser/devtools/protocol/target_handler.cc +++ b/content/browser/devtools/protocol/target_handler.cc
@@ -1191,7 +1191,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window,
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h index 5521f28..6715e2f4 100644 --- a/content/browser/devtools/protocol/target_handler.h +++ b/content/browser/devtools/protocol/target_handler.h
@@ -119,7 +119,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window,
diff --git a/content/browser/fenced_frame/fenced_frame.cc b/content/browser/fenced_frame/fenced_frame.cc index c6cdb479..3901d22 100644 --- a/content/browser/fenced_frame/fenced_frame.cc +++ b/content/browser/fenced_frame/fenced_frame.cc
@@ -218,6 +218,12 @@ return nullptr; } +bool FencedFrame::OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) { + return false; +} + FrameTree* FencedFrame::GetPictureInPictureOpenerFrameTree() { return nullptr; }
diff --git a/content/browser/fenced_frame/fenced_frame.h b/content/browser/fenced_frame/fenced_frame.h index 66cfce7..0ea90b6 100644 --- a/content/browser/fenced_frame/fenced_frame.h +++ b/content/browser/fenced_frame/fenced_frame.h
@@ -70,6 +70,9 @@ void SetFocusedFrame(FrameTreeNode* node, SiteInstanceGroup* source) override; FrameTree* GetOwnedPictureInPictureFrameTree() override; FrameTree* GetPictureInPictureOpenerFrameTree() override; + bool OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) override; // Returns the devtools frame token of the fenced frame's inner FrameTree's // main frame.
diff --git a/content/browser/guest_page_holder_impl.cc b/content/browser/guest_page_holder_impl.cc index 30fd66d3..6c8c488 100644 --- a/content/browser/guest_page_holder_impl.cc +++ b/content/browser/guest_page_holder_impl.cc
@@ -5,6 +5,7 @@ #include "content/browser/guest_page_holder_impl.h" #include "base/notimplemented.h" +#include "content/browser/renderer_host/cross_process_frame_connector.h" #include "content/browser/renderer_host/frame_tree.h" #include "content/browser/site_instance_impl.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -301,4 +302,25 @@ return &guest_page->frame_tree(); } +bool GuestPageHolderImpl::OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) { + CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch)); + + if (render_frame_proxy_host->frame_tree_node() != frame_tree_.root()) { + return false; + } + const bool hidden_with_parent_state = + render_frame_proxy_host->cross_process_frame_connector()->IsHidden() || + render_frame_proxy_host->cross_process_frame_connector() + ->EmbedderVisibility() != Visibility::VISIBLE; + frame_tree_.ForEachRenderViewHost([hidden_with_parent_state]( + RenderViewHostImpl* rvh) { + rvh->SetFrameTreeVisibility( + hidden_with_parent_state ? blink::mojom::PageVisibilityState::kHidden + : blink::mojom::PageVisibilityState::kVisible); + }); + return false; +} + } // namespace content
diff --git a/content/browser/guest_page_holder_impl.h b/content/browser/guest_page_holder_impl.h index e168b05..68c5c56 100644 --- a/content/browser/guest_page_holder_impl.h +++ b/content/browser/guest_page_holder_impl.h
@@ -58,6 +58,9 @@ void SetFocusedFrame(FrameTreeNode* node, SiteInstanceGroup* source) override; FrameTree* GetOwnedPictureInPictureFrameTree() override; FrameTree* GetPictureInPictureOpenerFrameTree() override; + bool OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) override; // NavigationControllerDelegate implementation. void NotifyNavigationStateChangedFromController(
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc index 68225dae..bb604ae7 100644 --- a/content/browser/preloading/prerender/prerender_host.cc +++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -368,6 +368,12 @@ return nullptr; } +bool PrerenderHost::OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) { + return false; +} + FrameTreeNodeId PrerenderHost::GetOuterDelegateFrameTreeNodeId() { // A prerendered FrameTree is not "inner to" or "nested inside" another // FrameTree; it exists in parallel to the primary FrameTree of the current
diff --git a/content/browser/preloading/prerender/prerender_host.h b/content/browser/preloading/prerender/prerender_host.h index 5e22941..11547c6 100644 --- a/content/browser/preloading/prerender/prerender_host.h +++ b/content/browser/preloading/prerender/prerender_host.h
@@ -208,6 +208,9 @@ void SetFocusedFrame(FrameTreeNode* node, SiteInstanceGroup* source) override; FrameTree* GetOwnedPictureInPictureFrameTree() override; FrameTree* GetPictureInPictureOpenerFrameTree() override; + bool OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) override; // NavigationControllerDelegate void NotifyNavigationStateChangedFromController(
diff --git a/content/browser/renderer_host/cross_process_frame_connector.cc b/content/browser/renderer_host/cross_process_frame_connector.cc index adf0e25..b19e810 100644 --- a/content/browser/renderer_host/cross_process_frame_connector.cc +++ b/content/browser/renderer_host/cross_process_frame_connector.cc
@@ -380,8 +380,11 @@ // the visibility. The Show/Hide methods will not be called if an inner // WebContents exists since the corresponding WebContents will itself call // Show/Hide on all the RenderWidgetHostViews (including this) one. - if (view_->host()->delegate()->OnRenderFrameProxyVisibilityChanged( - frame_proxy_in_parent_renderer_, visibility_)) { + if (view_->host() + ->frame_tree() + ->delegate() + ->OnRenderFrameProxyVisibilityChanged(frame_proxy_in_parent_renderer_, + visibility_)) { return; } @@ -602,22 +605,26 @@ } bool CrossProcessFrameConnector::IsVisible() { - if (visibility_ == blink::mojom::FrameVisibility::kNotRendered) + if (visibility_ == blink::mojom::FrameVisibility::kNotRendered || + intersection_state().viewport_intersection.IsEmpty()) { return false; - if (intersection_state().viewport_intersection.IsEmpty()) - return false; + } - if (!current_child_frame_host()) + if (!current_child_frame_host()) { return true; + } - Visibility embedder_visibility = - current_child_frame_host()->delegate()->GetVisibility(); - if (embedder_visibility != Visibility::VISIBLE) + if (EmbedderVisibility() != Visibility::VISIBLE) { return false; + } return true; } +Visibility CrossProcessFrameConnector::EmbedderVisibility() { + return current_child_frame_host()->delegate()->GetVisibility(); +} + RenderFrameHostImpl* CrossProcessFrameConnector::current_child_frame_host() const { return frame_proxy_in_parent_renderer_
diff --git a/content/browser/renderer_host/cross_process_frame_connector.h b/content/browser/renderer_host/cross_process_frame_connector.h index 4ab5c6c..264397ff 100644 --- a/content/browser/renderer_host/cross_process_frame_connector.h +++ b/content/browser/renderer_host/cross_process_frame_connector.h
@@ -15,6 +15,7 @@ #include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/common/surfaces/surface_id.h" #include "content/common/content_export.h" +#include "content/public/browser/visibility.h" #include "third_party/blink/public/common/frame/frame_visual_properties.h" #include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom-forward.h" #include "third_party/blink/public/mojom/frame/lifecycle.mojom.h" @@ -323,6 +324,9 @@ child_frame_crash_shown_closure_for_testing_ = std::move(closure); } + // Returns the embedder's visibility. + Visibility EmbedderVisibility(); + protected: friend class MockCrossProcessFrameConnector; friend class SitePerProcessBrowserTestBase;
diff --git a/content/browser/renderer_host/frame_tree.h b/content/browser/renderer_host/frame_tree.h index 0765e42..d14928c 100644 --- a/content/browser/renderer_host/frame_tree.h +++ b/content/browser/renderer_host/frame_tree.h
@@ -47,6 +47,7 @@ class BrowserContext; class PageDelegate; class RenderFrameHostDelegate; +class RenderFrameProxyHost; class RenderViewHostDelegate; class RenderViewHostImpl; class RenderFrameHostManager; @@ -199,6 +200,16 @@ // Returns this FrameTree's opener if this FrameTree represents a // picture-in-picture window. virtual FrameTree* GetPictureInPictureOpenerFrameTree() = 0; + + // Called when the visibility of the RenderFrameProxyHost changes. + // This method should only handle visibility for inner WebContents and + // will eventually notify all the RenderWidgetHostViews belonging to that + // WebContents. If this is not an inner WebContents or the inner WebContents + // FrameTree root does not match `render_frame_proxy_host` FrameTreeNode it + // should return false. + virtual bool OnRenderFrameProxyVisibilityChanged( + RenderFrameProxyHost* render_frame_proxy_host, + blink::mojom::FrameVisibility visibility) = 0; }; // Type of FrameTree instance.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index ac8f86a0..429e12a 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -3978,10 +3978,11 @@ if (previous_rfh) { // When migrating a frame to a new/different render process, use the frame // size we already have from the existing RenderFrameHost. - if (params->widget_params->visual_properties.new_size_device_px - .IsZero()) { - params->widget_params->visual_properties.new_size_device_px = - previous_rfh->GetFrameSize().value_or(gfx::Size()); + if (params->widget_params->visual_properties.new_size.IsZero()) { + float dsf = rwh->GetScreenInfo().device_scale_factor; + params->widget_params->visual_properties.new_size = + gfx::ScaleToRoundedSize( + previous_rfh->GetFrameSize().value_or(gfx::Size()), 1.f / dsf); } params->widget_params->reuse_compositor =
diff --git a/content/browser/renderer_host/render_widget_host_browsertest.cc b/content/browser/renderer_host/render_widget_host_browsertest.cc index 3e45ef21..c147ddf 100644 --- a/content/browser/renderer_host/render_widget_host_browsertest.cc +++ b/content/browser/renderer_host/render_widget_host_browsertest.cc
@@ -912,12 +912,9 @@ WaitForVisualPropertiesAck(); EXPECT_EQ(base::NumberToString(offset) + "px", EvalJs(shell(), "getComputedStyle(video).width").ExtractString()); - // Rounding of GetVisibleViewportSize in the presence of a non-integer - // devicePixelRatio device can make this off by one vs the video height. - EXPECT_NEAR( + EXPECT_EQ( root_view_size.height(), - EvalJs(shell(), "parseInt(getComputedStyle(video).height)").ExtractInt(), - 1); + EvalJs(shell(), "parseInt(getComputedStyle(video).height)").ExtractInt()); emulated_display_feature.orientation = DisplayFeature::Orientation::kHorizontal; @@ -928,26 +925,21 @@ WaitForVisualPropertiesAck(); EXPECT_EQ(base::NumberToString(offset) + "px", EvalJs(shell(), "getComputedStyle(video).height").ExtractString()); - EXPECT_NEAR( + EXPECT_EQ( root_view_size.width(), - EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt(), - 1); + EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt()); // No display feature/viewport segments are set, the video should go // fullscreen. view()->SetDisplayFeatureForTesting(nullptr); host()->SynchronizeVisualProperties(); WaitForVisualPropertiesAck(); - // Rounding of GetVisibleViewportSize in the presence of a non-integer - // devicePixelRatio device can make this off by one vs the video height. - EXPECT_NEAR( + EXPECT_EQ( root_view_size.height(), - EvalJs(shell(), "parseInt(getComputedStyle(video).height)").ExtractInt(), - 1); - EXPECT_NEAR( + EvalJs(shell(), "parseInt(getComputedStyle(video).height)").ExtractInt()); + EXPECT_EQ( root_view_size.width(), - EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt(), - 1); + EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt()); constexpr char kExitFullscreenScript[] = R"JS( document.exitFullscreen().then(() => { @@ -964,10 +956,9 @@ WaitForVisualPropertiesAck(); EXPECT_EQ(base::NumberToString(offset) + "px", EvalJs(shell(), "getComputedStyle(video).height").ExtractString()); - EXPECT_NEAR( + EXPECT_EQ( root_view_size.width(), - EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt(), - 1); + EvalJs(shell(), "parseInt(getComputedStyle(video).width)").ExtractInt()); } // Tests that the renderer receives the root widget's viewport segments and
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index d0135cf..da659cff 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1051,8 +1051,7 @@ visual_properties.min_size_for_auto_resize = min_size_for_auto_resize_; visual_properties.max_size_for_auto_resize = max_size_for_auto_resize_; - visual_properties.new_size_device_px = - view_->GetRequestedRendererSizeDevicePx(); + visual_properties.new_size = view_->GetRequestedRendererSize(); // This widget is for a frame that is the main frame of the outermost frame // tree. That makes it the top-most frame. OR this is a non-frame widget. @@ -1266,9 +1265,9 @@ blink_widget_->UpdateVisualProperties(*visual_properties); } - bool width_changed = !old_visual_properties_ || - old_visual_properties_->new_size_device_px.width() != - visual_properties->new_size_device_px.width(); + bool width_changed = + !old_visual_properties_ || old_visual_properties_->new_size.width() != + visual_properties->new_size.width(); // WidgetBase::UpdateSurfaceAndScreenInfo uses similar logic to detect // orientation changes on the display currently showing the widget. @@ -2817,8 +2816,7 @@ old_visual_properties.max_size_for_auto_resize != new_visual_properties.max_size_for_auto_resize)) || (!old_visual_properties.auto_resize_enabled && - (old_visual_properties.new_size_device_px != - new_visual_properties.new_size_device_px || + (old_visual_properties.new_size != new_visual_properties.new_size || (old_visual_properties.compositor_viewport_pixel_rect.IsEmpty() && !new_visual_properties.compositor_viewport_pixel_rect.IsEmpty()))); } @@ -2834,7 +2832,7 @@ bool is_acking_applicable = g_check_for_pending_visual_properties_ack && !new_visual_properties.auto_resize_enabled && - !new_visual_properties.new_size_device_px.IsEmpty() && + !new_visual_properties.new_size.IsEmpty() && !new_visual_properties.compositor_viewport_pixel_rect.IsEmpty() && new_visual_properties.local_surface_id;
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 4740713..d5d80035 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -970,8 +970,7 @@ view_->SetMockCompositorViewportPixelSize(gfx::Size()); host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(original_size.size(), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); @@ -982,8 +981,7 @@ view_->ClearMockCompositorViewportPixelSize(); host_->SynchronizeVisualProperties(); EXPECT_TRUE(host_->visual_properties_ack_pending_); - EXPECT_EQ(original_size.size(), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size); cc::RenderFrameMetadata metadata; metadata.viewport_size_in_pixels = original_size.size(); metadata.local_surface_id = std::nullopt; @@ -1023,8 +1021,7 @@ static_cast<RenderFrameMetadataProvider::Observer&>(*host_) .OnLocalSurfaceIdChanged(metadata); EXPECT_TRUE(host_->visual_properties_ack_pending_); - EXPECT_EQ(third_size.size(), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); @@ -1036,8 +1033,7 @@ static_cast<RenderFrameMetadataProvider::Observer&>(*host_) .OnLocalSurfaceIdChanged(metadata); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(third_size.size(), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); @@ -1050,7 +1046,7 @@ view_->SetBounds(gfx::Rect()); host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(gfx::Size(), host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(gfx::Size(), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); @@ -1060,8 +1056,7 @@ view_->SetBounds(gfx::Rect(0, 0, 0, 30)); host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(gfx::Size(0, 30), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); @@ -1070,8 +1065,7 @@ // Set the same size again. It should not be sent again. host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(gfx::Size(0, 30), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); @@ -1081,8 +1075,7 @@ view_->SetBounds(gfx::Rect(0, 0, 0, 31)); host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(gfx::Size(0, 31), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(gfx::Size(0, 31), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); @@ -1094,8 +1087,7 @@ view_->InvalidateLocalSurfaceId(); host_->SynchronizeVisualProperties(); EXPECT_FALSE(host_->visual_properties_ack_pending_); - EXPECT_EQ(gfx::Size(25, 25), - host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(gfx::Size(25, 25), host_->old_visual_properties_->new_size); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); } @@ -1154,31 +1146,6 @@ EXPECT_FALSE(host_->visual_properties_ack_pending_); } -// Test that the reported new_size includes the scale factor. -TEST_F(RenderWidgetHostTest, NewSizeIncludesScaleFactor) { - display::ScreenInfo screen_info; - screen_info.rect = gfx::Rect(0, 0, 800, 600); - screen_info.available_rect = gfx::Rect(0, 0, 800, 600); - screen_info.orientation_type = - display::mojom::ScreenOrientation::kPortraitPrimary; - screen_info.device_scale_factor = 2.f; - - ClearVisualProperties(); - view_->SetScreenInfo(screen_info); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); - gfx::Rect original_size(0, 0, 101, 100); - view_->SetBounds(original_size); - EXPECT_TRUE(host_->SynchronizeVisualProperties()); - // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(1u, widget_.ReceivedVisualProperties().size()); - auto new_size = widget_.ReceivedVisualProperties()[0].new_size_device_px; - EXPECT_EQ(202, new_size.width()); - EXPECT_EQ(200, new_size.height()); - EXPECT_TRUE(host_->visual_properties_ack_pending_); -} - // Ensure VisualProperties continues reporting the size of the current screen, // not the viewport, when the frame is fullscreen. See crbug.com/1367416. TEST_F(RenderWidgetHostTest, ScreenSizeInFullscreen) { @@ -1204,7 +1171,7 @@ blink::VisualProperties props = widget_.ReceivedVisualProperties().at(0); EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); - EXPECT_EQ(kViewBounds.size(), props.new_size_device_px); + EXPECT_EQ(kViewBounds.size(), props.new_size); // Enter fullscreen and do another VisualProperties sync. delegate_->set_is_fullscreen(true); @@ -1215,7 +1182,7 @@ props = widget_.ReceivedVisualProperties().at(1); EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); - EXPECT_EQ(kViewBounds.size(), props.new_size_device_px); + EXPECT_EQ(kViewBounds.size(), props.new_size); // Exit fullscreen and do another VisualProperties sync. delegate_->set_is_fullscreen(false); @@ -1226,7 +1193,7 @@ props = widget_.ReceivedVisualProperties().at(2); EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); - EXPECT_EQ(kViewBounds.size(), props.new_size_device_px); + EXPECT_EQ(kViewBounds.size(), props.new_size); } TEST_F(RenderWidgetHostTest, RootViewportSegments) { @@ -2182,7 +2149,7 @@ compositor_viewport_pixel_rect.size()); blink::VisualProperties visual_properties = host_->GetVisualProperties(); - EXPECT_EQ(bounds.size(), visual_properties.new_size_device_px); + EXPECT_EQ(bounds.size(), visual_properties.new_size); EXPECT_EQ(compositor_viewport_pixel_rect, visual_properties.compositor_viewport_pixel_rect); } @@ -2273,7 +2240,7 @@ // SynchronizeVisualProperties calls should not result in new IPC (unless the // size has actually changed). EXPECT_FALSE(host_->SynchronizeVisualProperties()); - EXPECT_EQ(initial_size_, host_->old_visual_properties_->new_size_device_px); + EXPECT_EQ(initial_size_, host_->old_visual_properties_->new_size); EXPECT_TRUE(host_->visual_properties_ack_pending_); } @@ -2287,7 +2254,7 @@ { // Size sent to the renderer. EXPECT_EQ(gfx::Size(100, 100), - widget_.ReceivedVisualProperties().at(0).new_size_device_px); + widget_.ReceivedVisualProperties().at(0).new_size); } // An ack is pending, throttling further updates. EXPECT_TRUE(host_->visual_properties_ack_pending_);
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index dd0c7c97..d72c1f4f 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -353,7 +353,7 @@ void RenderWidgetHostViewAndroid::ScreenStateChangeHandler:: BeginScreenStateChange() { - current_screen_state_.visible_viewport_size = rwhva_->view_.GetSizeDIPs(); + current_screen_state_.visible_viewport_size = rwhva_->view_.GetSize(); current_screen_state_.physical_backing_size = rwhva_->view_.GetPhysicalBackingSize(); auto screen_info = rwhva_->GetScreenInfo(); @@ -1185,24 +1185,11 @@ if (!view_.parent()) return default_bounds_dip_; - gfx::Size size(view_.GetSizeDIPs()); + gfx::Size size(view_.GetSize()); + return gfx::Rect(size); } -gfx::Size RenderWidgetHostViewAndroid::GetRequestedRendererSizeDevicePx() { - if (!view_.parent()) { - if (default_bounds_dip_.IsEmpty()) { - return gfx::Size(); - } - - const float scale_factor = GetDeviceScaleFactor(); - return gfx::Size(default_bounds_dip_.width() * scale_factor, - default_bounds_dip_.height() * scale_factor); - } - - return view_.GetSizeDevicePx(); -} - gfx::Size RenderWidgetHostViewAndroid::GetVisibleViewportSize() { int pinned_bottom_adjust_dps = std::max(0, (int)(view_.GetViewportInsetBottom() / view_.GetDipScale())); @@ -2484,8 +2471,8 @@ // TODO(yusufo) : Get rid of the below conditions and have a better handling // for resizing after crbug.com/628302 is handled. bool is_size_initialized = !will_build_tree || - view_.GetSizeDIPs().width() != 0 || - view_.GetSizeDIPs().height() != 0; + view_.GetSize().width() != 0 || + view_.GetSize().height() != 0; if (has_view_tree || is_size_initialized) resize = true; has_view_tree = will_build_tree; @@ -2609,8 +2596,7 @@ } void RenderWidgetHostViewAndroid::OnSizeChanged() { - screen_state_change_handler_.OnVisibleViewportSizeChanged( - view_.GetSizeDIPs()); + screen_state_change_handler_.OnVisibleViewportSizeChanged(view_.GetSize()); // The display feature depends on the view size so we need to recompute it. ComputeDisplayFeature(); } @@ -2960,7 +2946,7 @@ } display_feature_ = std::nullopt; - gfx::Size view_size(view_.GetSizeDIPs()); + gfx::Size view_size(view_.GetSize()); // On some devices like the Galaxy Fold the display feature has a size of // 0 (width or height depending on the orientation). IsEmpty() will fail here. if (display_feature_bounds_.size().IsZero() || view_size.IsEmpty()) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h index f74e5e4..ef7bbda 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.h +++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -155,7 +155,6 @@ void Hide() override; bool IsShowing() override; gfx::Rect GetViewBounds() override; - gfx::Size GetRequestedRendererSizeDevicePx() override; gfx::Size GetVisibleViewportSize() override; void SetInsets(const gfx::Insets& insets) override; gfx::Size GetCompositorViewportPixelSize() override;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index d695762a..bdb00be 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -2616,7 +2616,7 @@ view_->SetSize(gfx::Size(100, 100)); - // Device pixel size. + // Physical pixel size. EXPECT_EQ(gfx::Size(100, 100), view_->GetCompositorViewportPixelSize()); // Update to the renderer. base::RunLoop().RunUntilIdle(); @@ -2624,9 +2624,9 @@ { blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); - // Device pixel size. - EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size_device_px); - // Device pixel size. + // DIP size. + EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size); + // Physical pixel size. EXPECT_EQ(gfx::Size(100, 100), visual_properties.compositor_viewport_pixel_rect.size()); } @@ -2641,11 +2641,11 @@ sink_->ClearMessages(); widget_host_->ClearVisualProperties(); - // Device scale factor changes to 2, so the device pixel sizes should + // Device scale factor changes to 2, so the physical pixel sizes should // change, while the DIP sizes do not. aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(2.0f); - // Device pixel size. + // Physical pixel size. EXPECT_EQ(gfx::Size(200, 200), view_->GetCompositorViewportPixelSize()); // Update to the renderer. base::RunLoop().RunUntilIdle(); @@ -2653,9 +2653,9 @@ { blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); - // Device pixel size. - EXPECT_EQ(gfx::Size(200, 200), visual_properties.new_size_device_px); - // Device pixel size. + // DIP size. + EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size); + // Physical pixel size. EXPECT_EQ(gfx::Size(200, 200), visual_properties.compositor_viewport_pixel_rect.size()); } @@ -2671,7 +2671,7 @@ aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(1.0f); - // Device pixel size. + // Physical pixel size. EXPECT_EQ(gfx::Size(100, 100), view_->GetCompositorViewportPixelSize()); // Update to the renderer. base::RunLoop().RunUntilIdle(); @@ -2680,8 +2680,8 @@ blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); // DIP size. - EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size_device_px); - // Device pixel size. + EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size); + // Physical pixel size. EXPECT_EQ(gfx::Size(100, 100), visual_properties.compositor_viewport_pixel_rect.size()); } @@ -2780,7 +2780,7 @@ EXPECT_EQ(1u, widget_host_->visual_properties().size()); const auto& received_property = widget_host_->visual_properties()[0]; EXPECT_EQ(false, received_property.auto_resize_enabled); - EXPECT_EQ(size_after_disabling, received_property.new_size_device_px); + EXPECT_EQ(size_after_disabling, received_property.new_size); } // This test verifies that in AutoResize mode a new @@ -2837,7 +2837,7 @@ // Auto-resizve limits sent to the renderer. EXPECT_EQ(gfx::Size(50, 50), visual_properties.min_size_for_auto_resize); EXPECT_EQ(gfx::Size(100, 100), visual_properties.max_size_for_auto_resize); - EXPECT_EQ(gfx::Size(120, 120), visual_properties.new_size_device_px); + EXPECT_EQ(gfx::Size(120, 120), visual_properties.new_size); EXPECT_EQ(1, visual_properties.screen_infos.current().device_scale_factor); // A newly generated LocalSurfaceId is sent. EXPECT_TRUE(visual_properties.local_surface_id.has_value()); @@ -3098,7 +3098,7 @@ blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); // Empty size is sent. - EXPECT_EQ(gfx::Size(), visual_properties.new_size_device_px); + EXPECT_EQ(gfx::Size(), visual_properties.new_size); // A LocalSurfaceId is sent too. ASSERT_TRUE(visual_properties.local_surface_id.has_value()); EXPECT_TRUE(visual_properties.local_surface_id->is_valid()); @@ -3175,7 +3175,7 @@ EXPECT_TRUE(widget_host_->visual_properties_ack_pending_for_testing()); base::RunLoop().RunUntilIdle(); ASSERT_EQ(1u, widget_host_->visual_properties().size()); - EXPECT_EQ(size2, widget_host_->visual_properties().at(0).new_size_device_px); + EXPECT_EQ(size2, widget_host_->visual_properties().at(0).new_size); // Render should send back RenderFrameMetadata with new size. { cc::RenderFrameMetadata metadata; @@ -3433,7 +3433,7 @@ { blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); - EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size_device_px); + EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size); EXPECT_EQ(gfx::Size(100, 100), visual_properties.visible_viewport_size); } @@ -3455,7 +3455,7 @@ { blink::VisualProperties visual_properties = widget_host_->visual_properties().at(0); - EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size_device_px); + EXPECT_EQ(gfx::Size(100, 100), visual_properties.new_size); EXPECT_EQ(gfx::Size(100, 60), visual_properties.visible_viewport_size); } }
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc index 51f8361..2fb637bb 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -140,11 +140,6 @@ return GetViewBounds().size(); } -gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSizeDevicePx() { - return gfx::ScaleToCeiledSize(GetRequestedRendererSize(), - GetDeviceScaleFactor()); -} - uint32_t RenderWidgetHostViewBase::GetCaptureSequenceNumber() const { // TODO(vmpstr): Implement this for overrides other than aura and child frame. NOTIMPLEMENTED_LOG_ONCE(); @@ -569,7 +564,7 @@ void RenderWidgetHostViewBase::ResetGestureDetection() {} float RenderWidgetHostViewBase::GetDeviceScaleFactor() const { - return GetScreenInfos().current().device_scale_factor; + return screen_infos_.current().device_scale_factor; } base::WeakPtr<input::RenderWidgetHostViewInput>
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h index 2a2edc60..8377a897 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -236,7 +236,6 @@ // The requested size of the renderer. May differ from GetViewBounds().size() // when the view requires additional throttling. virtual gfx::Size GetRequestedRendererSize(); - virtual gfx::Size GetRequestedRendererSizeDevicePx(); // Returns the current capture sequence number. virtual uint32_t GetCaptureSequenceNumber() const;
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc index da43859..2c733a7 100644 --- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -324,8 +324,7 @@ EXPECT_EQ(compositor_viewport_pixel_rect, sent_visual_properties.compositor_viewport_pixel_rect); - EXPECT_EQ(rect_in_local_root.size(), - sent_visual_properties.new_size_device_px); + EXPECT_EQ(rect_in_local_root.size(), sent_visual_properties.new_size); EXPECT_EQ(local_surface_id, sent_visual_properties.local_surface_id); EXPECT_EQ(123u, sent_visual_properties.capture_sequence_number); EXPECT_EQ(1u, sent_visual_properties.root_widget_viewport_segments.size());
diff --git a/content/browser/renderer_host/scroll_into_view_browsertest.cc b/content/browser/renderer_host/scroll_into_view_browsertest.cc index b9bb46d..51ab2bf 100644 --- a/content/browser/renderer_host/scroll_into_view_browsertest.cc +++ b/content/browser/renderer_host/scroll_into_view_browsertest.cc
@@ -796,7 +796,7 @@ RunTest(); // width=device-width must prevent the zooming behavior. - EXPECT_LE(kMobileMinimumScale, GetVisualViewport().scale); + EXPECT_EQ(kMobileMinimumScale, GetVisualViewport().scale); } // Similar to above, an input in a touch-action region that disables pinch-zoom
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 54f24878..47afc760 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -9427,7 +9427,7 @@ // Avoid having the root try to handle the following event. root_view->set_event_handler(nullptr); - auto size = root_view->GetSizeDIPs(); + auto size = root_view->GetSize(); float x = size.width() / 2; float y = size.height() / 2; ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc index 2776494c..fc486e03 100644 --- a/content/browser/web_contents/web_contents_android.cc +++ b/content/browser/web_contents/web_contents_android.cc
@@ -826,11 +826,11 @@ } int WebContentsAndroid::GetWidth(JNIEnv* env) { - return web_contents_->GetNativeView()->GetSizeDIPs().width(); + return web_contents_->GetNativeView()->GetSize().width(); } int WebContentsAndroid::GetHeight(JNIEnv* env) { - return web_contents_->GetNativeView()->GetSizeDIPs().height(); + return web_contents_->GetNativeView()->GetSize().height(); } ScopedJavaLocalRef<jobject> WebContentsAndroid::GetOrCreateEventForwarder(
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 27c675d..f462c6c 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4503,6 +4503,18 @@ base::BindRepeating( [](PageVisibilityState page_visibility, RenderViewHostImpl* render_view_host) { + // If we are a guest frame tree, allow the visibility (ie + // display:none) to override the page visibility. + if (render_view_host->frame_tree()->is_guest()) { + RenderFrameProxyHost* proxy = render_view_host->frame_tree() + ->root() + ->render_manager() + ->GetProxyToOuterDelegate(); + if (proxy && proxy->cross_process_frame_connector()->IsHidden()) { + page_visibility = PageVisibilityState::kHidden; + } + } + render_view_host->SetFrameTreeVisibility(page_visibility); }, page_visibility); @@ -10218,7 +10230,7 @@ return window->bounds().size(); #elif BUILDFLAG(IS_ANDROID) ui::ViewAndroid* view_android = GetNativeView(); - return view_android->GetSizeDIPs(); + return view_android->GetSize(); #elif BUILDFLAG(IS_IOS) // TODO(crbug.com/40254930): Implement me. NOTREACHED();
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc index 37b434da..b088103 100644 --- a/content/browser/web_contents/web_contents_view_android.cc +++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -248,7 +248,7 @@ } gfx::Rect WebContentsViewAndroid::GetViewBounds() const { - return gfx::Rect(view_.GetSizeDIPs()); + return gfx::Rect(view_.GetSize()); } void WebContentsViewAndroid::CreateView(gfx::NativeView context) {}
diff --git a/content/browser/webui/url_data_manager.h b/content/browser/webui/url_data_manager.h index 6f84431..8b250ca 100644 --- a/content/browser/webui/url_data_manager.h +++ b/content/browser/webui/url_data_manager.h
@@ -73,9 +73,10 @@ friend struct DeleteURLDataSource; typedef std::vector<const URLDataSourceImpl*> URLDataSources; - // If invoked on the UI thread the DataSource is deleted immediatlye, + // If invoked on the UI thread the DataSource is deleted immediately, // otherwise it is added to |data_sources_| and a task is scheduled to handle - // deletion on the UI thread. See note abouve DeleteDataSource for more info. + // deletion on the UI thread. See note above the |DeleteURLDataSource| struct + // in url_data_source_impl.h for more info. static void DeleteDataSource(const URLDataSourceImpl* data_source); // Returns true if |data_source| is scheduled for deletion (|DeleteDataSource|
diff --git a/content/public/android/java/strings/android_content_strings.grd b/content/public/android/java/strings/android_content_strings.grd index f7997a2..927fe832 100644 --- a/content/public/android/java/strings/android_content_strings.grd +++ b/content/public/android/java/strings/android_content_strings.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="values-af/android_content_strings.xml" lang="af" type="android" /> <output filename="values-am/android_content_strings.xml" lang="am" type="android" />
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index 85ce29e..16f4458a 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc
@@ -771,7 +771,7 @@ void RenderViewTest::Resize(gfx::Size new_size, bool is_fullscreen_granted) { blink::VisualProperties visual_properties; visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); - visual_properties.new_size_device_px = new_size; + visual_properties.new_size = new_size; visual_properties.compositor_viewport_pixel_rect = gfx::Rect(new_size); visual_properties.is_fullscreen_granted = is_fullscreen_granted; visual_properties.display_mode = blink::mojom::DisplayMode::kBrowser; @@ -880,7 +880,7 @@ initial_visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); // Ensure the view has some size so tests involving scrolling bounds work. - initial_visual_properties.new_size_device_px = gfx::Size(400, 300); + initial_visual_properties.new_size = gfx::Size(400, 300); initial_visual_properties.visible_viewport_size = gfx::Size(400, 300); return initial_visual_properties; }
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 4e886e04..9c31268 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -72,6 +72,8 @@ "gpu_benchmarking_extension.h", "in_process_renderer_thread.cc", "in_process_renderer_thread.h", + "local_resource_url_loader_factory.cc", + "local_resource_url_loader_factory.h", "media/audio_decoder.cc", "media/audio_decoder.h", "media/batching_media_log.cc",
diff --git a/content/renderer/DEPS b/content/renderer/DEPS index a172720b..8248c11 100644 --- a/content/renderer/DEPS +++ b/content/renderer/DEPS
@@ -48,6 +48,9 @@ "render_thread_impl_browsertest\.cc": [ "+content/app/mojo/mojo_init.h", ], + "local_resource_url_loader_factory_unittest\.cc": [ + "+ui/base/resource", + ], "render_thread_impl_discardable_memory_browsertest\.cc": [ "+components/discardable_memory/service", "+content/browser/browser_main_loop.h",
diff --git a/content/renderer/local_resource_url_loader_factory.cc b/content/renderer/local_resource_url_loader_factory.cc new file mode 100644 index 0000000..191140b --- /dev/null +++ b/content/renderer/local_resource_url_loader_factory.cc
@@ -0,0 +1,231 @@ +// 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/renderer/local_resource_url_loader_factory.h" + +#include <cstdint> +#include <memory> +#include <utility> + +#include "base/check.h" +#include "base/containers/span.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/notimplemented.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "base/types/expected.h" +#include "content/common/web_ui_loading_util.h" +#include "content/public/common/content_client.h" +#include "content/public/common/url_constants.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "net/base/mime_util.h" +#include "net/socket/socket.h" +#include "services/network/public/cpp/parsed_headers.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/mojom/loader/local_resource_loader_config.mojom.h" +#include "ui/base/template_expressions.h" +#include "url/origin.h" + +namespace content { + +namespace { + +std::map<url::Origin, LocalResourceURLLoaderFactory::Source> +ConvertConfigToSourcesMap( + const blink::mojom::LocalResourceLoaderConfigPtr& config) { + std::map<url::Origin, LocalResourceURLLoaderFactory::Source> sources; + // TODO(https://crbug.com/384765582) This manual copy is only necessary + // because ui::ReplaceTemplateExpressions uses an unconventional map type. + // Remove this when that is fixed. + for (const auto& source : config->sources) { + const url::Origin origin = source.first; + const blink::mojom::LocalResourceSourcePtr& mojo_source = source.second; + const std::map<const std::string, std::string> replacement_strings( + mojo_source->replacement_strings.begin(), + mojo_source->replacement_strings.end()); + LocalResourceURLLoaderFactory::Source local_source( + mojo_source.Clone(), std::move(replacement_strings)); + sources.insert({std::move(origin), std::move(local_source)}); + } + return sources; +} + +} // namespace + +LocalResourceURLLoaderFactory::Source::Source( + blink::mojom::LocalResourceSourcePtr source, + std::map<const std::string, std::string> replacement_strings) + : source(std::move(source)), + replacement_strings(std::move(replacement_strings)) {} + +LocalResourceURLLoaderFactory::Source::Source(Source&& other) = default; +LocalResourceURLLoaderFactory::Source& +LocalResourceURLLoaderFactory::Source::operator=(Source&& other) = default; + +LocalResourceURLLoaderFactory::Source::~Source() = default; + +LocalResourceURLLoaderFactory::LocalResourceURLLoaderFactory( + const blink::mojom::LocalResourceLoaderConfigPtr& config, + mojo::PendingRemote<network::mojom::URLLoaderFactory> fallback) + : sources_(base::MakeRefCounted< + base::RefCountedData<std::map<url::Origin, Source>>>( + ConvertConfigToSourcesMap(config))), + fallback_(std::move(fallback)) {} + +LocalResourceURLLoaderFactory::~LocalResourceURLLoaderFactory() = default; + +bool LocalResourceURLLoaderFactory::CanServe( + const network::ResourceRequest& request) const { + const url::Origin origin = url::Origin::Create(request.url); + auto it = sources_->data.find(origin); + // The renderer process may not have metadata for the data source. This can + // happen if the data source isn't a WebUIDataSource, in which case the + // browser process doesn't send metadata for it. + // Example: chrome://theme/colors.css + if (it == sources_->data.end()) { + return false; + } + + // Get the resource ID corresponding to the URL path. + const blink::mojom::LocalResourceSourcePtr& source = it->second.source; + std::string_view path = request.url.path_piece().substr(1); + auto resource_it = source->path_to_resource_id_map.find(path); + // The path-to-ID map may not have an entry for the given path. This can + // happen for resources that are generated on-the-fly in the browser process. + // Example: chrome://my-webui/strings.m.js + if (resource_it == source->path_to_resource_id_map.end()) { + return false; + } + int resource_id = resource_it->second; + + // Return true if the in-process ResourceBundle has the resource for this ID. + return GetContentClient()->HasDataResource(resource_id); +} + +void LocalResourceURLLoaderFactory::CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + if (!CanServe(request)) { + fallback_->CreateLoaderAndStart(std::move(loader), request_id, options, + request, std::move(client), + traffic_annotation); + return; + } + // Only the "chrome" scheme is supported. + CHECK(request.url.scheme() == kChromeUIScheme); + // Parallelize calls to GetResourceAndRespond across multiple threads. + // Needs to be posted to a SequencedTaskRunner as Mojo requires a + // SequencedTaskRunner::CurrentDefaultHandle in scope. + base::ThreadPool::CreateSequencedTaskRunner( + {base::TaskPriority::USER_BLOCKING, base::MayBlock(), + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}) + ->PostTask(FROM_HERE, base::BindOnce(GetResourceAndRespond, sources_, + request, std::move(client))); +} + +void LocalResourceURLLoaderFactory::Clone( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { + receivers_.Add(this, std::move(receiver)); +} + +// static +void LocalResourceURLLoaderFactory::GetResourceAndRespond( + const scoped_refptr<base::RefCountedData<std::map<url::Origin, Source>>> + sources, + const network::ResourceRequest& request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client) { + const url::Origin origin = url::Origin::Create(request.url); + auto it = sources->data.find(origin); + // CanServe should have been called before this point, which would have + // confirmed that there exists a source corresponding to the URL origin. + CHECK(it != sources->data.end()); + + const blink::mojom::LocalResourceSourcePtr& source = it->second.source; + const std::map<const std::string, std::string>& replacement_strings = + it->second.replacement_strings; + + // Get resource id. + std::string_view path = request.url.path_piece().substr(1); + auto resource_it = source->path_to_resource_id_map.find(path); + // CanServe should have been called before this point, which would have + // confirmed that there exists a resource ID corresponding to the URL path. + CHECK(resource_it != source->path_to_resource_id_map.end()); + int resource_id = resource_it->second; + + // Load bytes. + scoped_refptr<base::RefCountedMemory> raw_bytes = + GetContentClient()->GetDataResourceBytes(resource_id); + // CanServe should have been called before this point, which would have + // confirmed that the ResourceBundle will return non-null for the given + // resource ID. + CHECK(raw_bytes); + std::string_view bytes(base::as_string_view(*raw_bytes)); + + auto url_response_head = network::mojom::URLResponseHead::New(); + + // Mime type. + std::string mime_type; + if (net::GetMimeTypeFromFile( + base::FilePath::FromASCII(request.url.ExtractFileName()), + &mime_type)) { + url_response_head->mime_type = mime_type; + } else { + url_response_head->mime_type = "text/html"; + } + + scoped_refptr<base::RefCountedMemory> bytes_after_replacement = raw_bytes; + if (source->replacement_strings.size() > 0 && + (url_response_head->mime_type == "text/html" || + url_response_head->mime_type == "text/css" || + (source->should_replace_i18n_in_js && + url_response_head->mime_type == "text/javascript"))) { + std::string replaced_string; + if (url_response_head->mime_type == "text/javascript") { + CHECK(ui::ReplaceTemplateExpressionsInJS(bytes, replacement_strings, + &replaced_string)); + } else { + replaced_string = + ui::ReplaceTemplateExpressions(bytes, replacement_strings); + } + bytes_after_replacement = base::MakeRefCounted<base::RefCountedString>( + std::move(replaced_string)); + } + + // Other headers. + scoped_refptr<net::HttpResponseHeaders> headers = + base::MakeRefCounted<net::HttpResponseHeaders>(source->headers); + headers->SetHeader(net::HttpRequestHeaders::kContentType, mime_type); + url_response_head->headers = headers; + url_response_head->parsed_headers = network::PopulateParsedHeaders( + url_response_head->headers.get(), request.url); + + // Handle Range header if request. + base::expected<net::HttpByteRange, webui::GetRequestedRangeError> + range_or_error = webui::GetRequestedRange(request.headers); + // Errors (aside from 'no Range header') should be surfaced to the client. + if (!range_or_error.has_value() && + range_or_error.error() != webui::GetRequestedRangeError::kNoRanges) { + webui::CallOnError(std::move(client), + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); + return; + } + std::optional<net::HttpByteRange> maybe_range = + range_or_error.has_value() ? std::make_optional(range_or_error.value()) + : std::nullopt; + + webui::SendData(std::move(url_response_head), std::move(client), maybe_range, + bytes_after_replacement); +} + +} // namespace content
diff --git a/content/renderer/local_resource_url_loader_factory.h b/content/renderer/local_resource_url_loader_factory.h new file mode 100644 index 0000000..674cfe9 --- /dev/null +++ b/content/renderer/local_resource_url_loader_factory.h
@@ -0,0 +1,113 @@ +// 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 CONTENT_RENDERER_LOCAL_RESOURCE_URL_LOADER_FACTORY_H_ +#define CONTENT_RENDERER_LOCAL_RESOURCE_URL_LOADER_FACTORY_H_ + +#include <cstdint> +#include <memory> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" +#include "base/task/sequenced_task_runner.h" +#include "base/threading/sequence_bound.h" +#include "content/common/content_export.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/socket/socket.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "third_party/blink/public/mojom/loader/local_resource_loader_config.mojom.h" +#include "url/origin.h" + +namespace content { + +// LocalResourceURLLoaderFactory is a URLLoaderFactory that lives in the +// renderer process and fetches resources directly from the ResourceBundle, +// enabling renderers to load bundled resources entirely in-process. This can +// significantly reduce IPC overhead for WebUIs, whose resources come almost +// exclusively from the ResourceBundle. +class CONTENT_EXPORT LocalResourceURLLoaderFactory + : public network::mojom::URLLoaderFactory { + public: + struct Source { + Source(blink::mojom::LocalResourceSourcePtr source, + std::map<const std::string, std::string> replacement_strings); + Source(Source&& other); + Source& operator=(Source&& other); + ~Source(); + blink::mojom::LocalResourceSourcePtr source; + std::map<const std::string, std::string> replacement_strings; + }; + + LocalResourceURLLoaderFactory( + const blink::mojom::LocalResourceLoaderConfigPtr& config, + mojo::PendingRemote<network::mojom::URLLoaderFactory> fallback); + ~LocalResourceURLLoaderFactory() override; + + // network::mojom::URLLoaderFactory implementation. + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override; + + private: + // CanServe returns true if and only if all of the following are true: + // + // 1. The LocalResourceURLLoaderFactory has a resource ID for the given URL + // (which depends on the resource metadata received from the browser + // process), and + // 2. The resource ID corresponds to a resource that exists in + // 'resources.pak'. + // + // It should return false in the following cases: + // + // 1. For dynamic resources that are generated on-the-fly in the browser + // process (e.g. strings.m.js, chrome://theme/colors.css). + // 2. For static resources that reside in a pak file that is not + // memory-mapped in the renderer process (e.g. browser_tests.pak). + // + // CanServe is called internally for every resource request to decide whether + // the request can be serviced in-process or if it has to be serviced + // out-of-process. + bool CanServe(const network::ResourceRequest& request) const; + + // Fetches the resource from the ResourceBundle and sends it to the + // URLLoaderClient. This is static because it is posted as a task which may + // outlive |this|. + static void GetResourceAndRespond( + const scoped_refptr<base::RefCountedData<std::map<url::Origin, Source>>> + sources, + const network::ResourceRequest& request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client); + + // Map for resolving origins to their respective source metadata + // (path-to-resource-ID mappings and string replacement maps). + // It is ref-counted because it needs to be accessed in an async call to + // GetResourceAndRespond, which may outlive |this|. + const scoped_refptr<base::RefCountedData<std::map<url::Origin, Source>>> + sources_; + + // Pipe to fallback factory, which should be the WebUIURLLoaderFactory in the + // browser process. This is required because there are certain resources that + // cannot be serviced in-process and must be serviced by the browser process. + // See the CanServe method for more details. + const mojo::Remote<network::mojom::URLLoaderFactory> fallback_; + + // Pipes to callers of the Clone method. + mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_LOCAL_RESOURCE_URL_LOADER_FACTORY_H_
diff --git a/content/renderer/local_resource_url_loader_factory_unittest.cc b/content/renderer/local_resource_url_loader_factory_unittest.cc new file mode 100644 index 0000000..a76571c --- /dev/null +++ b/content/renderer/local_resource_url_loader_factory_unittest.cc
@@ -0,0 +1,351 @@ +// 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/renderer/local_resource_url_loader_factory.h" + +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +#include "base/check.h" +#include "base/containers/flat_map.h" +#include "base/containers/span.h" +#include "base/functional/callback_forward.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/notimplemented.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/thread_pool.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "base/threading/sequence_bound.h" +#include "content/common/web_ui_loading_util.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/system/data_pipe_utils.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_version.h" +#include "net/socket/socket.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "services/network/test/test_url_loader_client.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/loader/local_resource_loader_config.mojom.h" +#include "ui/base/resource/mock_resource_bundle_delegate.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/resource/resource_scale_factor.h" +#include "url/origin.h" +#include "url/url_util.h" + +namespace { + +// A URLLoaderFactory that always sends the string "out-of-process resource" to +// the client. +class FakeURLLoaderFactory : public network::mojom::URLLoaderFactory { + public: + FakeURLLoaderFactory() : receiver_(this) {} + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + auto headers = network::mojom::URLResponseHead::New(); + auto bytes = + base::MakeRefCounted<base::RefCountedString>("out-of-process resource"); + content::webui::SendData(std::move(headers), std::move(client), + std::nullopt, std::move(bytes)); + } + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override { + // Supports only one receiver at a time. + receiver_.reset(); + receiver_.Bind(std::move(receiver)); + } + + private: + mojo::Receiver<network::mojom::URLLoaderFactory> receiver_; +}; + +class LocalResourceURLLoaderFactoryTest : public ::testing::Test { + public: + void SetUp() override { + // Swap in mock ResourceBundle. + original_resource_bundle_ = + ui::ResourceBundle::SwapSharedInstanceForTesting(nullptr); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", &resource_bundle_delegate_, + ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); + + source_ = blink::mojom::LocalResourceSource::New(); + source_->headers = + net::HttpResponseHeaders::Builder(net::HttpVersion(1, 1), "200 OK") + .Build() + ->raw_headers(); + + UpdateLoaderFactory(); + } + + void TearDown() override { + ui::ResourceBundle::CleanupSharedInstance(); + ui::ResourceBundle::SwapSharedInstanceForTesting(original_resource_bundle_); + } + + protected: + // Used to control the behavior of the mock ResourceBundle. + // Marked non-private so that tests can call EXPECT_CALL on it. + testing::NiceMock<ui::MockResourceBundleDelegate> resource_bundle_delegate_; + + content::LocalResourceURLLoaderFactory* loader_factory() { + return loader_factory_.get(); + } + + void SetShouldReplaceI18nInJs(bool value) { + source_->should_replace_i18n_in_js = value; + UpdateLoaderFactory(); + } + + void AddReplacementString(const std::string& key, const std::string& value) { + source_->replacement_strings[key] = value; + UpdateLoaderFactory(); + } + + void AddResourceID(const std::string& path, int id) { + source_->path_to_resource_id_map[path] = id; + UpdateLoaderFactory(); + } + + std::string ReadAllData(network::TestURLLoaderClient& client) { + std::string result; + CHECK(mojo::BlockingCopyToString(client.response_body_release(), &result)); + return result; + } + + private: + // Create a config with a |source_| as the single hardcoded entry in the map. + void UpdateLoaderFactory() { + const url::Origin origin = url::Origin::Create(GURL("chrome://sourcename")); + auto config = blink::mojom::LocalResourceLoaderConfig::New(); + config->sources[origin] = source_.Clone(); + // Create a pipe and pass receiving end to |fake_fallback_factory_|. + mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote; + fake_fallback_factory_.Clone( + pending_remote.InitWithNewPipeAndPassReceiver()); + // Pass other end to |loader_factory_|. + loader_factory_ = std::make_unique<content::LocalResourceURLLoaderFactory>( + config, std::move(pending_remote)); + } + + std::unique_ptr<content::LocalResourceURLLoaderFactory> loader_factory_; + + FakeURLLoaderFactory fake_fallback_factory_; + + // Intermediate state that is updated by the test and eventually used to + // update the loader factory state. + blink::mojom::LocalResourceSourcePtr source_; + + // Temporary storage of original ResourceBundle while we swap in the test + // mock. + raw_ptr<ui::ResourceBundle> original_resource_bundle_; + + // For CreateLoaderAndStart, which posts a task. + base::test::TaskEnvironment task_environment_; +}; + +struct CanServeTestCase { + std::optional<int> resource_id; + bool has_resource; + bool can_serve; +}; + +struct ServeTestCase { + std::string path; + std::string mime_type; + std::string resource_data; + std::string response_body; +}; + +struct RequestRangeTestCase { + std::string request_range; + int error_code; + std::string resource_data; + std::string response_body; +}; + +class LocalResourceURLLoaderFactoryCanServeTest + : public LocalResourceURLLoaderFactoryTest, + public ::testing::WithParamInterface<CanServeTestCase> {}; +class LocalResourceURLLoaderFactoryServeTest + : public LocalResourceURLLoaderFactoryTest, + public ::testing::WithParamInterface<ServeTestCase> {}; +class LocalResourceURLLoaderFactoryRequestRangeTest + : public LocalResourceURLLoaderFactoryTest, + public ::testing::WithParamInterface<RequestRangeTestCase> {}; + +} // namespace + +// Check if loader factory can service a particular request. +TEST_P(LocalResourceURLLoaderFactoryCanServeTest, CanServe) { + const std::string path = "path/to/resource"; + if (GetParam().resource_id) { + AddResourceID(path, *GetParam().resource_id); + } + const scoped_refptr<base::RefCountedString> resource_data = + base::MakeRefCounted<base::RefCountedString>("in-process resource"); + ON_CALL(resource_bundle_delegate_, HasDataResource) + .WillByDefault(testing::Return(GetParam().has_resource)); + ON_CALL(resource_bundle_delegate_, LoadDataResourceBytes) + .WillByDefault(testing::Return(resource_data.get())); + + network::TestURLLoaderClient client; + network::ResourceRequest request; + // The test fixture hardcodes the origin to 'chrome://sourcename'. + request.url = GURL("chrome://sourcename/" + path); + mojo::PendingRemote<network::mojom::URLLoader> loader; + loader_factory()->CreateLoaderAndStart( + loader.InitWithNewPipeAndPassReceiver(), 0, 0, request, + client.CreateRemote(), net::MutableNetworkTrafficAnnotationTag()); + client.RunUntilComplete(); + + ASSERT_EQ(net::OK, client.completion_status().error_code); + ASSERT_TRUE(client.response_body().is_valid()); + std::string response_body = ReadAllData(client); + if (GetParam().can_serve) { + EXPECT_EQ(response_body, "in-process resource"); + } else { + EXPECT_EQ(response_body, "out-of-process resource"); + } +} + +INSTANTIATE_TEST_SUITE_P( + LocalResourceURLLoaderFactoryCanServeTest, + LocalResourceURLLoaderFactoryCanServeTest, + ::testing::Values( + // Resource ID exists and ResourceBundle has the resource. + CanServeTestCase(std::make_optional(1), true, true), + // Resource ID exists but ResourceBundle does not have the resource. + CanServeTestCase(std::make_optional(1), false, false), + // Resource ID does not exist in mapping. + CanServeTestCase(std::nullopt, false, false))); + +// Create loader, read bytes from the ResourceBundle and send them to the +// client. +TEST_P(LocalResourceURLLoaderFactoryServeTest, Serve) { + const int resource_id = 1; + const scoped_refptr<base::RefCountedString> resource_data = + base::MakeRefCounted<base::RefCountedString>(GetParam().resource_data); + AddResourceID(GetParam().path, resource_id); + AddReplacementString("foo", "bar"); + SetShouldReplaceI18nInJs(true); + // Bypass the fallback by making CanServe return true. + ON_CALL(resource_bundle_delegate_, HasDataResource) + .WillByDefault(testing::Return(true)); + EXPECT_CALL(resource_bundle_delegate_, + LoadDataResourceBytes(resource_id, + ui::ResourceScaleFactor::kScaleFactorNone)) + .WillOnce(testing::Return(resource_data.get())); + + network::TestURLLoaderClient client; + network::ResourceRequest request; + // The test fixture hardcodes the origin to 'chrome://sourcename'. + request.url = GURL("chrome://sourcename/" + GetParam().path); + mojo::PendingRemote<network::mojom::URLLoader> loader; + loader_factory()->CreateLoaderAndStart( + loader.InitWithNewPipeAndPassReceiver(), 0, 0, request, + client.CreateRemote(), net::MutableNetworkTrafficAnnotationTag()); + client.RunUntilComplete(); + + ASSERT_EQ(net::OK, client.completion_status().error_code); + EXPECT_EQ(GetParam().mime_type, client.response_head()->mime_type); + ASSERT_TRUE(client.response_body().is_valid()); + std::string response_body = ReadAllData(client); + EXPECT_EQ(GetParam().response_body, response_body); +} + +INSTANTIATE_TEST_SUITE_P( + LocalResourceURLLoaderFactoryServeTest, + LocalResourceURLLoaderFactoryServeTest, + ::testing::Values( + // MIME type is assumed to be text/html. String replacement occurs. + ServeTestCase("path/to/resource", + "text/html", + "this is $i18n{foo}", + "this is bar"), + // MIME type is text/html. String replacement occurs. + ServeTestCase("path/to/resource.html", + "text/html", + "this is $i18n{foo}", + "this is bar"), + // MIME type is text/css. String replacement occurs. + ServeTestCase("path/to/resource.css", + "text/css", + "this is $i18n{foo}", + "this is bar"), + // MIME type is text/javascript. String replacement only occurs within + // HTML template section. + ServeTestCase("path/to/resource.js", + "text/javascript", + "this is $i18n{foo}", + "this is $i18n{foo}"))); + +// Request various byte ranges which may or may not be valid. +TEST_P(LocalResourceURLLoaderFactoryRequestRangeTest, RequestRange) { + const std::string path = "path/to/resource"; + const int resource_id = 1; + const scoped_refptr<base::RefCountedString> resource_data = + base::MakeRefCounted<base::RefCountedString>(GetParam().resource_data); + AddResourceID(path, resource_id); + // Bypass the fallback by making CanServe return true. + ON_CALL(resource_bundle_delegate_, HasDataResource) + .WillByDefault(testing::Return(true)); + EXPECT_CALL(resource_bundle_delegate_, + LoadDataResourceBytes(resource_id, + ui::ResourceScaleFactor::kScaleFactorNone)) + .WillOnce(testing::Return(resource_data.get())); + + network::TestURLLoaderClient client; + network::ResourceRequest request; + // The test fixture hardcodes the origin to 'chrome://sourcename'. + request.url = GURL("chrome://sourcename/" + path); + request.headers.SetHeader(net::HttpRequestHeaders::kRange, + GetParam().request_range); + mojo::PendingRemote<network::mojom::URLLoader> loader; + loader_factory()->CreateLoaderAndStart( + loader.InitWithNewPipeAndPassReceiver(), 0, 0, request, + client.CreateRemote(), net::MutableNetworkTrafficAnnotationTag()); + client.RunUntilComplete(); + + EXPECT_EQ(GetParam().error_code, client.completion_status().error_code); + if (GetParam().error_code != net::OK) { + return; + } + ASSERT_TRUE(client.response_body().is_valid()); + std::string response_body = ReadAllData(client); + EXPECT_EQ(GetParam().response_body, response_body); +} + +INSTANTIATE_TEST_SUITE_P( + LocalResourceURLLoaderFactoryRequestRangeTest, + LocalResourceURLLoaderFactoryRequestRangeTest, + ::testing::Values( + // Valid range. + RequestRangeTestCase("bytes=3-10", + net::OK, + "resource data", + "ource da"), + // Valid range, but starting byte is greater than resource size. + // Error expected. + RequestRangeTestCase("bytes=100-101", + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)));
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc index 0531485d..d0d72072 100644 --- a/content/renderer/render_frame_impl_browsertest.cc +++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -122,7 +122,7 @@ mojom::CreateFrameWidgetParamsPtr widget_params = mojom::CreateFrameWidgetParams::New(); widget_params->routing_id = kSubframeWidgetRouteId; - widget_params->visual_properties.new_size_device_px = gfx::Size(100, 100); + widget_params->visual_properties.new_size = gfx::Size(100, 100); widget_params->visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); @@ -286,7 +286,7 @@ visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); gfx::Size widget_size(400, 200); gfx::Size visible_size(350, 170); - visual_properties.new_size_device_px = widget_size; + visual_properties.new_size = widget_size; visual_properties.compositor_viewport_pixel_rect = gfx::Rect(widget_size); visual_properties.visible_viewport_size = visible_size;
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc index 140ec647..17bbb44 100644 --- a/content/renderer/render_view_browsertest.cc +++ b/content/renderer/render_view_browsertest.cc
@@ -548,11 +548,9 @@ visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); visual_properties.screen_infos.mutable_current().device_scale_factor = dsf; - visual_properties.new_size_device_px = - gfx::ScaleToCeiledSize(gfx::Size(100, 100), dsf); + visual_properties.new_size = gfx::Size(100, 100); visual_properties.compositor_viewport_pixel_rect = gfx::Rect(200, 200); - visual_properties.visible_viewport_size = - visual_properties.new_size_device_px; + visual_properties.visible_viewport_size = visual_properties.new_size; visual_properties.auto_resize_enabled = web_view_->AutoResizeMode(); visual_properties.min_size_for_auto_resize = min_size_for_autoresize_; visual_properties.max_size_for_auto_resize = max_size_for_autoresize_;
diff --git a/content/renderer/render_widget_browsertest.cc b/content/renderer/render_widget_browsertest.cc index 89b9fde..4874ea3f 100644 --- a/content/renderer/render_widget_browsertest.cc +++ b/content/renderer/render_widget_browsertest.cc
@@ -68,7 +68,7 @@ protected: blink::VisualProperties InitialVisualProperties() override { blink::VisualProperties initial_visual_properties; - initial_visual_properties.new_size_device_px = initial_size_; + initial_visual_properties.new_size = initial_size_; initial_visual_properties.compositor_viewport_pixel_rect = gfx::Rect(initial_size_); initial_visual_properties.local_surface_id =
diff --git a/content/shell/shell_resources.grd b/content/shell/shell_resources.grd index 8b48f8bb..f2337e8 100644 --- a/content/shell/shell_resources.grd +++ b/content/shell/shell_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/shell_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index e9478ce..89d48cc 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -2900,6 +2900,7 @@ "../public/test/test_utils_unittest.cc", "../renderer/accessibility/annotations/ax_image_stopwords_unittest.cc", "../renderer/content_security_policy_util_unittest.cc", + "../renderer/local_resource_url_loader_factory_unittest.cc", "../renderer/media/batching_media_log_unittest.cc", "../renderer/media/inspector_media_event_handler_unittest.cc", "../renderer/media/renderer_webaudiodevice_impl_unittest.cc",
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py index 5f82b32..5a9eb2c 100644 --- a/content/test/gpu/gpu_tests/gpu_integration_test.py +++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -505,7 +505,9 @@ cls._EnsureScreenOn() cls._CheckBrowserVersion() cls._VerifyBrowserFeaturesMatchExpectedValues() - cls._RetrieveAboutGpu() + # TODO(crbug.com/376498163): Re-enable this once the impact on Windows + # builds has been reduced. + # cls._RetrieveAboutGpu() return except Exception as e: # pylint: disable=broad-except last_exception = e @@ -864,7 +866,9 @@ self._HandlePass(test_name, expected_crashes, expected_results) finally: self.additionalTags[TEST_WAS_SLOW] = json.dumps(self._TestWasSlow()) - self._ReportAboutGpu(test_name) + # TODO(crbug.com/376498163): Re-enable this once the impact on Windows + # builds has been reduced. + # self._ReportAboutGpu(test_name) self._OnAfterTest(args) def _OnAfterTest(self, args: ct.TestArgs) -> None:
diff --git a/content/test/test_content_client.cc b/content/test/test_content_client.cc index 99f5e34..fc3c8d65 100644 --- a/content/test/test_content_client.cc +++ b/content/test/test_content_client.cc
@@ -14,6 +14,10 @@ TestContentClient::~TestContentClient() = default; +bool TestContentClient::HasDataResource(int resource_id) const { + return ui::ResourceBundle::GetSharedInstance().HasDataResource(resource_id); +} + std::string_view TestContentClient::GetDataResource( int resource_id, ui::ResourceScaleFactor scale_factor) {
diff --git a/content/test/test_content_client.h b/content/test/test_content_client.h index 6778f07..636ad7e 100644 --- a/content/test/test_content_client.h +++ b/content/test/test_content_client.h
@@ -21,6 +21,7 @@ ~TestContentClient() override; // ContentClient: + bool HasDataResource(int resource_id) const override; std::string_view GetDataResource( int resource_id, ui::ResourceScaleFactor scale_factor) override;
diff --git a/content/test/web_ui_mojo_test_resources.grd b/content/test/web_ui_mojo_test_resources.grd index 9adc490..0686f4e 100644 --- a/content/test/web_ui_mojo_test_resources.grd +++ b/content/test/web_ui_mojo_test_resources.grd
@@ -2,7 +2,7 @@ <!-- This file specifies resources for content_browsertests. --> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/web_ui_mojo_test_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/docs/website b/docs/website index fb143e8..54467c5 160000 --- a/docs/website +++ b/docs/website
@@ -1 +1 @@ -Subproject commit fb143e8ce76fcf5f67792fbbd6c219c5d03ec2d9 +Subproject commit 54467c5b22ad5d81a5468007092db3e5d33d7f5b
diff --git a/extensions/browser/extension_registrar.cc b/extensions/browser/extension_registrar.cc index db1b24e..dde230b 100644 --- a/extensions/browser/extension_registrar.cc +++ b/extensions/browser/extension_registrar.cc
@@ -331,6 +331,7 @@ const Extension* source_extension, const std::string& extension_id, disable_reason::DisableReason disable_reasons) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(disable_reasons == disable_reason::DISABLE_USER_ACTION || disable_reasons == disable_reason::DISABLE_BLOCKED_BY_POLICY); if (disable_reasons == disable_reason::DISABLE_BLOCKED_BY_POLICY) { @@ -505,6 +506,107 @@ } } +void ExtensionRegistrar::OnBlocklistStateRemoved( + const std::string& extension_id) { + if (blocklist_prefs::IsExtensionBlocklisted(extension_id, extension_prefs_)) { + return; + } + + // Clear acknowledged state. + blocklist_prefs::RemoveAcknowledgedBlocklistState( + extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, + extension_prefs_); + + scoped_refptr<const Extension> extension = + registry_->blocklisted_extensions().GetByID(extension_id); + DCHECK(extension); + registry_->RemoveBlocklisted(extension_id); + AddExtension(extension.get()); +} + +void ExtensionRegistrar::OnBlocklistStateAdded( + const std::string& extension_id) { + DCHECK( + blocklist_prefs::IsExtensionBlocklisted(extension_id, extension_prefs_)); + // The extension was already acknowledged by the user, it should already be in + // the unloaded state. + if (blocklist_prefs::HasAcknowledgedBlocklistState( + extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, + extension_prefs_)) { + DCHECK(base::Contains(registry_->blocklisted_extensions().GetIDs(), + extension_id)); + return; + } + + scoped_refptr<const Extension> extension = + registry_->GetInstalledExtension(extension_id); + registry_->AddBlocklisted(extension); + RemoveExtension(extension_id, UnloadedExtensionReason::BLOCKLIST); +} + +void ExtensionRegistrar::OnGreylistStateRemoved( + const std::string& extension_id) { + bool is_on_sb_list = (blocklist_prefs::GetSafeBrowsingExtensionBlocklistState( + extension_id, extension_prefs_) != + BitMapBlocklistState::NOT_BLOCKLISTED); + bool is_on_omaha_list = + blocklist_prefs::HasAnyOmahaGreylistState(extension_id, extension_prefs_); + if (is_on_sb_list || is_on_omaha_list) { + return; + } + // Clear all acknowledged states so the extension will still get disabled if + // it is added to the greylist again. + blocklist_prefs::ClearAcknowledgedGreylistStates(extension_id, + extension_prefs_); + RemoveDisableReasonAndMaybeEnable(extension_id, + disable_reason::DISABLE_GREYLIST); +} + +void ExtensionRegistrar::OnGreylistStateAdded(const std::string& extension_id, + BitMapBlocklistState new_state) { +#if DCHECK_IS_ON() + bool has_new_state_on_sb_list = + (blocklist_prefs::GetSafeBrowsingExtensionBlocklistState( + extension_id, extension_prefs_) == new_state); + bool has_new_state_on_omaha_list = blocklist_prefs::HasOmahaBlocklistState( + extension_id, new_state, extension_prefs_); + DCHECK(has_new_state_on_sb_list || has_new_state_on_omaha_list); +#endif + if (blocklist_prefs::HasAcknowledgedBlocklistState(extension_id, new_state, + extension_prefs_)) { + // If the extension is already acknowledged, don't disable it again + // because it can be already re-enabled by the user. This could happen if + // the extension is added to the SafeBrowsing blocklist, and then + // subsequently marked by Omaha. In this case, we don't want to disable the + // extension twice. + return; + } + + // Set the current greylist states to acknowledge immediately because the + // extension is disabled silently. Clear the other acknowledged state because + // when the state changes to another greylist state in the future, we'd like + // to disable the extension again. + blocklist_prefs::UpdateCurrentGreylistStatesAsAcknowledged(extension_id, + extension_prefs_); + DisableExtension(extension_id, disable_reason::DISABLE_GREYLIST); +} + +void ExtensionRegistrar::BlocklistExtensionForTest( + const std::string& extension_id) { + blocklist_prefs::SetSafeBrowsingExtensionBlocklistState( + extension_id, BitMapBlocklistState::BLOCKLISTED_MALWARE, + extension_prefs_); + OnBlocklistStateAdded(extension_id); +} + +void ExtensionRegistrar::GreylistExtensionForTest( + const std::string& extension_id, + const BitMapBlocklistState& state) { + blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(extension_id, state, + extension_prefs_); + OnGreylistStateAdded(extension_id, state); +} + void ExtensionRegistrar::OnUnpackedExtensionReloadFailed( const base::FilePath& path) { failed_to_reload_unpacked_extensions_.insert(path);
diff --git a/extensions/browser/extension_registrar.h b/extensions/browser/extension_registrar.h index 151665e7..fc66656 100644 --- a/extensions/browser/extension_registrar.h +++ b/extensions/browser/extension_registrar.h
@@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" +#include "extensions/browser/blocklist_state.h" #include "extensions/browser/disable_reason.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/process_manager_observer.h" @@ -174,6 +175,38 @@ // reloaded. Newly added extensions are no longer automatically blocked. void UnblockAllExtensions(); + // Takes Safe Browsing and Omaha malware blocklist states into account and + // decides whether to remove the extension from the blocklist and reload it. + // Called when a blocklisted extension is removed from the Safe Browsing + // malware blocklist or Omaha malware blocklist. Also clears the acknowledged + // state if the extension is reloaded. + void OnBlocklistStateRemoved(const std::string& extension_id); + + // Takes acknowledged malware blocklist state into account and decides whether + // to add the extension to the blocklist and unload it. Called when the + // extension is added to the Safe Browsing malware blocklist or the Omaha + // malware blocklist. + void OnBlocklistStateAdded(const std::string& extension_id); + + // Takes Safe Browsing and Omaha blocklist states into account and decides + // whether to remove greylist disabled reason. Called when a greylisted + // state is removed from the Safe Browsing blocklist or Omaha blocklist. Also + // clears all acknowledged states if the greylist disabled reason is removed. + void OnGreylistStateRemoved(const std::string& extension_id); + + // Takes acknowledged blocklist states into account and decides whether to + // disable the greylisted extension. Called when a new greylisted state is + // added to the Safe Browsing blocklist or Omaha blocklist. + void OnGreylistStateAdded(const std::string& extension_id, + BitMapBlocklistState new_state); + + // Simulates an extension being blocklisted for tests. + void BlocklistExtensionForTest(const std::string& extension_id); + + // Simulates an extension being greylisted for tests. + void GreylistExtensionForTest(const std::string& extension_id, + const BitMapBlocklistState& state); + // Deactivates the extension, adding its id to the list of terminated // extensions. void TerminateExtension(const ExtensionId& extension_id);
diff --git a/extensions/browser/resources/extensions_browser_resources.grd b/extensions/browser/resources/extensions_browser_resources.grd index 1c6fa9c..5243a8f 100644 --- a/extensions/browser/resources/extensions_browser_resources.grd +++ b/extensions/browser/resources/extensions_browser_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/extensions_browser_resources.h" type="rc_header" context="default_100_percent"> <emit emit_type='prepend'></emit>
diff --git a/extensions/extensions_resources.grd b/extensions/extensions_resources.grd index 6c9a07c..ea85963 100644 --- a/extensions/extensions_resources.grd +++ b/extensions/extensions_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/extensions_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd index 54507f9..5981f69e 100644 --- a/extensions/renderer/resources/extensions_renderer_resources.grd +++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/extensions_renderer_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/extensions/shell/app_shell_resources.grd b/extensions/shell/app_shell_resources.grd index 75bd1c8..857ac480 100644 --- a/extensions/shell/app_shell_resources.grd +++ b/extensions/shell/app_shell_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/app_shell_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd index f7e67d7..6a97381 100644 --- a/extensions/strings/extensions_strings.grd +++ b/extensions/strings/extensions_strings.grd
@@ -7,7 +7,7 @@ --> <grit latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/extensions_strings.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/gpu/command_buffer/client/DEPS b/gpu/command_buffer/client/DEPS index df80219..0e93d2b2 100644 --- a/gpu/command_buffer/client/DEPS +++ b/gpu/command_buffer/client/DEPS
@@ -6,6 +6,7 @@ "+components/miracle_parameter", "+components/viz/common/resources/shared_image_format.h", "+components/viz/common/resources/shared_image_format_utils.h", + "+mojo/public/cpp/bindings/pending_remote.h", ] specific_include_rules = {
diff --git a/gpu/command_buffer/client/shared_image_interface.cc b/gpu/command_buffer/client/shared_image_interface.cc index 8cd91d29..977a241 100644 --- a/gpu/command_buffer/client/shared_image_interface.cc +++ b/gpu/command_buffer/client/shared_image_interface.cc
@@ -180,6 +180,17 @@ } #endif // BUILDFLAG(IS_WIN) +void SharedImageInterface::CreateSharedImagePool( + const SharedImagePoolId& pool_id, + mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote) { + NOTREACHED(); +} + +void SharedImageInterface::DestroySharedImagePool( + const SharedImagePoolId& pool_id) { + NOTREACHED(); +} + SharedImageInterface::SharedImageMapping::SharedImageMapping() = default; SharedImageInterface::SharedImageMapping::SharedImageMapping( SharedImageInterface::SharedImageMapping&& mapped) = default;
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h index 32e1aa0..5a3f12a 100644 --- a/gpu/command_buffer/client/shared_image_interface.h +++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -18,7 +18,9 @@ #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/common/sync_token.h" #include "gpu/gpu_export.h" +#include "gpu/ipc/common/shared_image_pool_client_interface.mojom.h" #include "gpu/ipc/common/surface_handle.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/gpu/ganesh/GrTypes.h" #include "ui/gfx/buffer_types.h" @@ -411,6 +413,18 @@ void Release() const; + // Used by client side shared image pool aka SharedImagePool to + // create a service side pool. It also creates a new mojo IPC connection + // between the client and the service side pool so that service side pool + // can communicate with client side pool when needed. + virtual void CreateSharedImagePool( + const SharedImagePoolId& pool_id, + mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote); + + // Called when client side SharedImagePool is destroyed. It will + // in turn destroy the corresponding GPU service side SharedImagePool. + virtual void DestroySharedImagePool(const SharedImagePoolId& pool_id); + protected: friend class base::RefCountedThreadSafe<SharedImageInterface>; virtual ~SharedImageInterface();
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn index d3f2d2d..2a1f8b0 100644 --- a/gpu/ipc/common/BUILD.gn +++ b/gpu/ipc/common/BUILD.gn
@@ -125,6 +125,7 @@ ":gpu_peak_memory", ":interfaces_cpp_sources", ":memory_stats_sources", + ":shared_image_pool_client_interface", ":surface_handle_type", ":vulkan_ycbcr_info", "//ipc", @@ -306,6 +307,19 @@ ] } +mojom("shared_image_pool_client_interface") { + generate_java = true + visibility = [ + "//gpu/*", + "//services/*", + ] + sources = [ "shared_image_pool_client_interface.mojom" ] + deps = [ "//mojo/public/mojom/base" ] + export_class_attribute = "GPU_EXPORT" + export_header = "gpu/gpu_export.h" + cpp_configs = [ "//gpu:gpu_implementation" ] +} + mojom("surface_handle") { generate_java = true sources = [ "surface_handle.mojom" ]
diff --git a/gpu/ipc/common/shared_image_pool_client_interface.mojom b/gpu/ipc/common/shared_image_pool_client_interface.mojom new file mode 100644 index 0000000..efbd87c3 --- /dev/null +++ b/gpu/ipc/common/shared_image_pool_client_interface.mojom
@@ -0,0 +1,12 @@ +// 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. + +module gpu.mojom; + +// Interface used by the GPU process to notify the client/renderer side +// SharedImagePool instance to clear/purge the pool. +interface SharedImagePoolClientInterface { + // Currently empty and methods will be added in future CLs along with its + // implementations. +};
diff --git a/headless/lib/browser/protocol/target_handler.cc b/headless/lib/browser/protocol/target_handler.cc index b19f063..a5f67a8 100644 --- a/headless/lib/browser/protocol/target_handler.cc +++ b/headless/lib/browser/protocol/target_handler.cc
@@ -32,7 +32,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window,
diff --git a/headless/lib/browser/protocol/target_handler.h b/headless/lib/browser/protocol/target_handler.h index e162d88..33204fb 100644 --- a/headless/lib/browser/protocol/target_handler.h +++ b/headless/lib/browser/protocol/target_handler.h
@@ -33,7 +33,6 @@ std::optional<int> top, std::optional<int> width, std::optional<int> height, - std::optional<std::string> window_state, std::optional<std::string> context_id, std::optional<bool> enable_begin_frame_control, std::optional<bool> new_window,
diff --git a/ios_internal b/ios_internal index 53efd84..a48abcf 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit 53efd8418bb70bf06616f417a7090f9d3b659343 +Subproject commit a48abcfc293d8e695d52adafe5a76954066a0ab8
diff --git a/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc b/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc index 170568a..1500055 100644 --- a/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc +++ b/media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.cc
@@ -24,10 +24,10 @@ // to exist only in those configurations so that the presandbox hook is only // compiled in those scenarios. As it is now, kHardwareVideoDecoding exists for // all ash-chrome builds because -// chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc depends on it and -// that file is built for ash-chrome regardless of VA-API/V4L2. That means that -// bots like linux-chromeos-rel end up compiling this presandbox hook (thus the -// NOTREACHED()s in some places here). +// chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc depends on +// it and that file is built for ash-chrome regardless of VA-API/V4L2. That +// means that bots like linux-chromeos-rel end up compiling this presandbox hook +// (thus the NOTREACHED()s in some places here). namespace media { namespace {
diff --git a/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc b/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc index 2154e19..97269b9 100644 --- a/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc +++ b/media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.cc
@@ -107,10 +107,10 @@ // sandbox type to exist only in those configurations so that the presandbox // hook is only reached in those scenarios. As it is now, // kHardwareVideoEncoding exists for all ash-chrome builds because - // chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc is expected to - // depend on it eventually and that file is built for ash-chrome regardless - // of VA-API/V4L2. That means that bots like linux-chromeos-rel would end up - // reaching this presandbox hook. + // chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc is + // expected to depend on it eventually and that file is built for ash-chrome + // regardless of VA-API/V4L2. That means that bots like linux-chromeos-rel + // would end up reaching this presandbox hook. #if BUILDFLAG(USE_VAAPI) VaapiWrapper::PreSandboxInitialization(/*allow_disabling_global_lock=*/true);
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc index 7f03ff6..5a2318b 100644 --- a/net/cookies/cookie_monster.cc +++ b/net/cookies/cookie_monster.cc
@@ -64,6 +64,7 @@ #include "base/metrics/field_trial.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/rand_util.h" #include "base/ranges/algorithm.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" @@ -71,6 +72,7 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" +#include "base/timer/elapsed_timer.h" #include "net/base/isolation_info.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/schemeful_site.h" @@ -722,6 +724,11 @@ GetCookieListCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + std::optional<base::ElapsedTimer> timer; + if (get_cookie_list_timing_subsampler_.ShouldSample(0.001)) { + timer.emplace(); + } + CookieAccessResultList included_cookies; CookieAccessResultList excluded_cookies; if (HasCookieableScheme(url)) { @@ -767,6 +774,12 @@ MaybeRunCookieCallback(std::move(callback), included_cookies, excluded_cookies); + + if (timer) { + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Cookie.GetCookieListWithOptions.Duration", timer->Elapsed(), + base::Microseconds(1), base::Milliseconds(128), 100); + } } void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h index 5a81a20..54530a6 100644 --- a/net/cookies/cookie_monster.h +++ b/net/cookies/cookie_monster.h
@@ -24,6 +24,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/rand_util.h" #include "base/thread_annotations.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" @@ -808,6 +809,8 @@ bool persist_session_cookies_ = false; + base::MetricsSubSampler get_cookie_list_timing_subsampler_; + THREAD_CHECKER(thread_checker_); base::WeakPtrFactory<CookieMonster> weak_ptr_factory_{this};
diff --git a/remoting/proto/control.proto b/remoting/proto/control.proto index e1223fa..242fcd4 100644 --- a/remoting/proto/control.proto +++ b/remoting/proto/control.proto
@@ -143,6 +143,9 @@ optional int32 x_dpi = 6; optional int32 y_dpi = 7; + // Display name. This is displayed in the client UI and is informational only. + optional string display_name = 9; + // TODO(yuweih): Add bpp to the message. }
diff --git a/sandbox/policy/linux/bpf_hardware_video_decoding_policy_linux.cc b/sandbox/policy/linux/bpf_hardware_video_decoding_policy_linux.cc index 8eab2981..e15be33 100644 --- a/sandbox/policy/linux/bpf_hardware_video_decoding_policy_linux.cc +++ b/sandbox/policy/linux/bpf_hardware_video_decoding_policy_linux.cc
@@ -44,9 +44,9 @@ // sandbox type to exist only in those configurations so that the // HardwareVideoDecodingProcessPolicy is only compiled in those scenarios. As // it is now, kHardwareVideoDecoding exists for all ash-chrome builds because - // chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc depends on it - // and that file is built for ash-chrome regardless of VA-API/V4L2. That means - // that bots like linux-chromeos-rel end up compiling this policy. + // chromeos/ash/experiences/arc/video/gpu_arc_video_service_host.cc depends on + // it and that file is built for ash-chrome regardless of VA-API/V4L2. That + // means that bots like linux-chromeos-rel end up compiling this policy. NOTREACHED(); #endif }
diff --git a/storage/browser/file_system/file_system_quota_client.cc b/storage/browser/file_system/file_system_quota_client.cc index d9a8e6de..fa61ce1 100644 --- a/storage/browser/file_system/file_system_quota_client.cc +++ b/storage/browser/file_system/file_system_quota_client.cc
@@ -58,7 +58,8 @@ merged.insert(merged.end(), ts.begin(), ts.end()); } base::ranges::sort(merged); - merged.erase(base::ranges::unique(merged), merged.end()); + auto repeated = std::ranges::unique(merged); + merged.erase(repeated.begin(), repeated.end()); return merged; }
diff --git a/third_party/angle b/third_party/angle index 4c60a30..b3af2e8 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 4c60a308592af56dc745d8d1ba994e89ebb053db +Subproject commit b3af2e86f4b75b3c8f8053175fe97c0372926675
diff --git a/third_party/barhopper b/third_party/barhopper index 865bd06..9230af4 160000 --- a/third_party/barhopper +++ b/third_party/barhopper
@@ -1 +1 @@ -Subproject commit 865bd06ef4a839b0a15d17e38e25f8911e4cdf9f +Subproject commit 9230af4dc38c6d2cc9c0841692267762ebfca991
diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc index b87ffec..9d4153f 100644 --- a/third_party/blink/common/origin_trials/trial_token.cc +++ b/third_party/blink/common/origin_trials/trial_token.cc
@@ -192,14 +192,14 @@ return nullptr; } - std::optional<base::Value> data = base::JSONReader::Read(token_payload); - if (!data || !data->is_dict()) { + std::optional<base::Value::Dict> data = + base::JSONReader::ReadDict(token_payload); + if (!data) { return nullptr; } - base::Value::Dict& datadict = data->GetDict(); // Ensure that the origin is a valid (non-opaque) origin URL. - std::string* origin_string = datadict.FindString("origin"); + std::string* origin_string = data->FindString("origin"); if (!origin_string) { return nullptr; } @@ -210,7 +210,7 @@ // The |isSubdomain| flag is optional. If found, ensure it is a valid boolean. bool is_subdomain = false; - base::Value* is_subdomain_value = datadict.Find("isSubdomain"); + base::Value* is_subdomain_value = data->Find("isSubdomain"); if (is_subdomain_value) { if (!is_subdomain_value->is_bool()) { return nullptr; @@ -219,13 +219,13 @@ } // Ensure that the feature name is a valid string. - std::string* feature_name = datadict.FindString("feature"); + std::string* feature_name = data->FindString("feature"); if (!feature_name || feature_name->empty()) { return nullptr; } // Ensure that the expiry timestamp is a valid (positive) integer. - int expiry_timestamp = datadict.FindInt("expiry").value_or(0); + int expiry_timestamp = data->FindInt("expiry").value_or(0); if (expiry_timestamp <= 0) { return nullptr; } @@ -237,7 +237,7 @@ if (version == kVersion3) { // The |isThirdParty| flag is optional. If found, ensure it is a valid // boolean. - base::Value* is_third_party_value = datadict.Find("isThirdParty"); + base::Value* is_third_party_value = data->Find("isThirdParty"); if (is_third_party_value) { if (!is_third_party_value->is_bool()) { return nullptr; @@ -247,7 +247,7 @@ // The |usage| field is optional. If found, ensure its value is either empty // or "subset". - std::string* usage_value = datadict.FindString("usage"); + std::string* usage_value = data->FindString("usage"); if (usage_value) { if (usage_value->empty()) { usage = UsageRestriction::kNone;
diff --git a/third_party/blink/common/widget/visual_properties.cc b/third_party/blink/common/widget/visual_properties.cc index c9cf3aa1..135a690a 100644 --- a/third_party/blink/common/widget/visual_properties.cc +++ b/third_party/blink/common/widget/visual_properties.cc
@@ -19,7 +19,7 @@ auto_resize_enabled == other.auto_resize_enabled && min_size_for_auto_resize == other.min_size_for_auto_resize && max_size_for_auto_resize == other.max_size_for_auto_resize && - new_size_device_px == other.new_size_device_px && + new_size == other.new_size && visible_viewport_size == other.visible_viewport_size && compositor_viewport_pixel_rect == other.compositor_viewport_pixel_rect &&
diff --git a/third_party/blink/common/widget/visual_properties_mojom_traits.cc b/third_party/blink/common/widget/visual_properties_mojom_traits.cc index 79854c13..5a47c97 100644 --- a/third_party/blink/common/widget/visual_properties_mojom_traits.cc +++ b/third_party/blink/common/widget/visual_properties_mojom_traits.cc
@@ -18,7 +18,7 @@ if (!data.ReadScreenInfos(&out->screen_infos) || !data.ReadMinSizeForAutoResize(&out->min_size_for_auto_resize) || !data.ReadMaxSizeForAutoResize(&out->max_size_for_auto_resize) || - !data.ReadNewSize(&out->new_size_device_px) || + !data.ReadNewSize(&out->new_size) || !data.ReadVisibleViewportSize(&out->visible_viewport_size) || !data.ReadCompositorViewportPixelRect( &out->compositor_viewport_pixel_rect) ||
diff --git a/third_party/blink/public/common/widget/visual_properties.h b/third_party/blink/public/common/widget/visual_properties.h index a020546..628ce647 100644 --- a/third_party/blink/public/common/widget/visual_properties.h +++ b/third_party/blink/public/common/widget/visual_properties.h
@@ -64,8 +64,8 @@ // The maximum size for Blink if auto-resize is enabled. gfx::Size max_size_for_auto_resize; - // The size for the widget, in device pixels. - gfx::Size new_size_device_px; + // The size for the widget in DIPs. + gfx::Size new_size; // The size of the area of the widget that is visible to the user, in DIPs. // The visible area may be empty if the visible area does not intersect with @@ -75,17 +75,15 @@ // as with an on-screen keyboard. gfx::Size visible_viewport_size; - // The rect of compositor's viewport in device pixels. Note that for top level - // widgets this is the same as |new_size| (when UseDevicePixelsForWidgetSizing - // is on; otherwise different by device pixel ratio) except that on Android - // it includes the size of the browser controls. For child frame widgets it - // is a pixel-perfect bounds of the visible region of the widget. The size - // would be similar to visible_viewport_size, but in device pixels and - // computed via very different means. TODO(danakj): It would be super nice to - // remove one of |new_size|, |visible_viewport_size| and - // |compositor_viewport_pixel_rect|. Their values overlap in purpose, - // creating a very confusing situation about which to use for what, and how - // they should relate or not. + // The rect of compositor's viewport in pixels. Note that for top level + // widgets this is roughly the DSF scaled new_size put into a rect. For child + // frame widgets it is a pixel-perfect bounds of the visible region of the + // widget. The size would be similar to visible_viewport_size, but in physical + // pixels and computed via very different means. + // TODO(danakj): It would be super nice to remove one of |new_size|, + // |visible_viewport_size| and |compositor_viewport_pixel_rect|. Their values + // overlap in purpose, creating a very confusing situation about which to use + // for what, and how they should relate or not. gfx::Rect compositor_viewport_pixel_rect; // Browser controls params such as top and bottom controls heights, whether @@ -93,9 +91,8 @@ cc::BrowserControlsParams browser_controls_params; // If shown and resizing the renderer, returns the height of the virtual - // keyboard in device pixels. Otherwise, returns 0. Always 0 in a + // keyboard in physical pixels. Otherwise, returns 0. Always 0 in a // non-outermost main frame. - // TODO(chrishtr): rename to virtual_keyboard_resize_height_device_px. int virtual_keyboard_resize_height_physical_px = 0; // Whether or not the focused node should be scrolled into view after the
diff --git a/third_party/blink/public/common/widget/visual_properties_mojom_traits.h b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h index 030373a4..aedd7b0 100644 --- a/third_party/blink/public/common/widget/visual_properties_mojom_traits.h +++ b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
@@ -42,7 +42,7 @@ } static const gfx::Size& new_size(const blink::VisualProperties& r) { - return r.new_size_device_px; + return r.new_size; } static const gfx::Size& visible_viewport_size(
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl index e10e5495..583cc9fe 100644 --- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl +++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -11174,14 +11174,6 @@ string host integer port - # The state of the target window. - experimental type WindowState extends string - enum - normal - minimized - maximized - fullscreen - # Activates (focuses) the target. command activateTarget parameters @@ -11263,9 +11255,6 @@ optional integer width # Frame height in DIP (requires newWindow to be true or headless shell). optional integer height - # Frame window state (requires newWindow to be true or headless shell). - # Default is normal. - optional WindowState windowState # The browser context to create the page in. experimental optional Browser.BrowserContextID browserContextId # Whether BeginFrames for this target will be controlled via DevTools (headless shell only,
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h index c96abe1..b0104dff 100644 --- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h +++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -56,6 +56,7 @@ class HTMLOptionElement; class HTMLFrameOwnerElement; class HTMLSelectElement; +class LayoutBlockFlow; struct PhysicalRect; class WebPluginContainer; @@ -266,6 +267,9 @@ // Determine if a serialization is in the process or not. virtual bool IsSerializationInFlight() const = 0; + // Clears the cached fragment data associated with `block_flow` if it exists. + virtual void ClearBlockFlowCachedData(const LayoutBlockFlow* block_flow) = 0; + protected: friend class ScopedBlinkAXEventIntent; FRIEND_TEST_ALL_PREFIXES(ScopedBlinkAXEventIntentTest, SingleIntent);
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc index a2608eb..038e786 100644 --- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc +++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -926,7 +926,7 @@ widget_base_->LayerTreeHost()->SetExternalPageScaleFactor( combined_scale_factor, visual_properties.is_pinch_gesture_active); - Resize(visual_properties.new_size_device_px); + Resize(widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size)); } gfx::Rect WebPagePopupImpl::ViewportVisibleRect() {
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index 59722f2..9b2d6b0f 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -6298,7 +6298,7 @@ blink::VisualProperties visual_properties; visual_properties.screen_infos = display::ScreenInfos(display::ScreenInfo()); - visual_properties.new_size_device_px = gfx::Size(400, 300); + visual_properties.new_size = gfx::Size(400, 300); visual_properties.visible_viewport_size = gfx::Size(400, 300); visual_properties.screen_infos.mutable_current().rect = gfx::Rect(800, 600);
diff --git a/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc b/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc index 7077759..31625e4 100644 --- a/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc +++ b/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc
@@ -178,7 +178,7 @@ DCHECK(!frame_widget_->AutoResizeMode()); original_screen_infos_ = visual_properties.screen_infos; - original_widget_size_ = visual_properties.new_size_device_px; + original_widget_size_ = visual_properties.new_size; original_visible_viewport_size_ = visual_properties.visible_viewport_size; original_root_viewport_segments_ = visual_properties.root_widget_viewport_segments;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index 4edbb7e..abc5319 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1976,20 +1976,9 @@ const VisualProperties& visual_properties) { gfx::Rect new_compositor_viewport_pixel_rect = visual_properties.compositor_viewport_pixel_rect; - gfx::Size new_size; - new_size = visual_properties.new_size_device_px; - if (device_scale_factor_for_testing_) { - DCHECK(non_testing_device_scale_factor_); - new_size = - gfx::ScaleToFlooredSize(new_size, device_scale_factor_for_testing_ / - non_testing_device_scale_factor_); - new_compositor_viewport_pixel_rect = gfx::ScaleToEnclosedRect( - new_compositor_viewport_pixel_rect, - device_scale_factor_for_testing_ / non_testing_device_scale_factor_); - } - if (ForMainFrame()) { - if (size_ != new_size) { + if (size_ != + widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size)) { // Only hide popups when the size changes. Eg https://crbug.com/761908. View()->CancelPagePopup(); } @@ -2024,7 +2013,7 @@ if (ForMainFrame()) { if (!AutoResizeMode()) { - size_ = new_size; + size_ = widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size); View()->ResizeWithBrowserControls( size_.value(), @@ -2041,18 +2030,17 @@ } else { // Widgets in a WebView's frame tree without a local main frame // set the size of the WebView to be the |visible_viewport_size|, in order - // to limit compositing in (out of process) child frames to what is - // visible. + // to limit compositing in (out of process) child frames to what is visible. // // Note that child frames in the same process/WebView frame tree as the - // main frame do not do this in order to not clobber the source of truth - // in the main frame. + // main frame do not do this in order to not clobber the source of truth in + // the main frame. if (!View()->MainFrameImpl()) { View()->Resize(widget_base_->DIPsToCeiledBlinkSpace( widget_base_->VisibleViewportSizeInDIPs())); } - Resize(new_size); + Resize(widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size)); } } @@ -5111,11 +5099,6 @@ DCHECK(ForMainFrame()); DCHECK_GE(factor, 0.f); - if (!device_scale_factor_for_testing_) { - non_testing_device_scale_factor_ = - widget_base_->GetOriginalDeviceScaleFactor(); - } - // Stash the window size before we adjust the scale factor, as subsequent // calls to convert will use the new scale factor. gfx::Size size_in_dips = widget_base_->BlinkSpaceToFlooredDIPs(Size()); @@ -5123,10 +5106,8 @@ // Receiving a 0 is used to reset between tests, it removes the override in // order to listen to the browser for the next test. - if (!factor) { - non_testing_device_scale_factor_ = 0; + if (!factor) return; - } // We are changing the device scale factor from the renderer, so allocate a // new viz::LocalSurfaceId to avoid surface invariants violations in tests. @@ -5140,7 +5121,7 @@ if (!AutoResizeMode()) { // This picks up the new device scale factor as // `UpdateCompositorViewportAndScreenInfo()` has applied a new value. - Resize(size_with_dsf); + Resize(widget_base_->DIPsToCeiledBlinkSpace(size_in_dips)); } }
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h index 3dd204c..69febe3 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -666,8 +666,8 @@ bool enabled, const blink::DeviceEmulationParams& params); void SetScreenInfoAndSize(const display::ScreenInfos& screen_infos, - const gfx::Size& widget_size_in_dips, - const gfx::Size& visible_viewport_size_in_dips); + const gfx::Size& widget_size, + const gfx::Size& visible_viewport_size); // Update the surface allocation information, compositor viewport rect and // screen info on the widget. @@ -1212,11 +1212,6 @@ // frame widgets. float device_scale_factor_for_testing_ = 0; - // When device_scale_factor_for_testing_ is set (i.e. nonzero), this - // stores the device scale factor before the testing override was set. - // Otherwise it is set to zero. - float non_testing_device_scale_factor_ = 0; - // This struct contains data that is only valid for main frame widgets. // You should use `main_data()` to access it. struct MainFrameData {
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 83fa3b0..2bb8c5b 100644 --- a/third_party/blink/renderer/core/html/forms/option_list.cc +++ b/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -71,7 +71,9 @@ } if (RuntimeEnabledFeatures::SelectParserRelaxationEnabled()) { - if (IsA<HTMLSelectElement>(current)) { + if (current == select_) { + current = nullptr; + } else if (IsA<HTMLSelectElement>(current)) { current = ElementTraversal::PreviousAbsoluteSibling(*next, &select_); } else { current = ElementTraversal::Previous(*current, &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 8a5110d..9289f45 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
@@ -95,4 +95,13 @@ EXPECT_EQ("gg11", Id(*iter2)) << "Nested OPTGROUP should be included."; } +TEST_F(OptionListTest, RetreatBeforeBeginning) { + Select().setInnerHTML("<button>button</button><option id=o1>option</option>"); + OptionList list = Select().GetOptionList(); + OptionList::Iterator it = list.begin(); + EXPECT_EQ("o1", Id(*it)); + --it; + EXPECT_EQ(nullptr, *it); +} + } // naemespace blink
diff --git a/third_party/blink/renderer/core/html/forms/resources/list_picker.js b/third_party/blink/renderer/core/html/forms/resources/list_picker.js index ca121f5..7824f42c 100644 --- a/third_party/blink/renderer/core/html/forms/resources/list_picker.js +++ b/third_party/blink/renderer/core/html/forms/resources/list_picker.js
@@ -334,6 +334,13 @@ this.selectElement_.style.fontVariant = this.config_.baseStyle.fontVariant; if (this.config_.baseStyle.textAlign) this.selectElement_.style.textAlign = this.config_.baseStyle.textAlign; + + // updateChildren_ takes longer when there are existing elements, so remove + // them to make it faster. + // TODO(crbug.com/388557894): Remove this after improving the performance + // of updateChildren_. + this.selectElement_.innerHTML = ''; + this.updateChildren_(this.selectElement_, this.config_); this.setMenuListOptionsBoundsInAXTree_(); } @@ -360,6 +367,8 @@ } /** + * TODO(crbug.com/388557894): Make this faster in the case that `parent` has + * a large number of children. * @param {!Element} parent Select element or optgroup element. * @param {!Object} config */
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc index 795ec912..6dffb46 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -3913,11 +3913,14 @@ StartPlaybackProgressTimer(); playing_ = true; } else { // Should not be playing right now - if (is_playing) { + // Always tell WMP about the pause since it may need to clear a pending + // automatic playback resumption. + if (web_media_player_ && ready_state_ >= kHaveMetadata) { web_media_player_->Pause(); - - if (pause_speech && ::features::IsTextBasedAudioDescriptionEnabled()) - SpeechSynthesis()->Pause(); + } + if (is_playing && pause_speech && + ::features::IsTextBasedAudioDescriptionEnabled()) { + SpeechSynthesis()->Pause(); } playback_progress_timer_.Stop();
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc index 5b56a767..ddf1a8b 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -84,6 +84,7 @@ class MockWebMediaPlayer : public EmptyWebMediaPlayer { public: + MOCK_METHOD0(Pause, void()); MOCK_METHOD0(OnTimeUpdate, void()); MOCK_CONST_METHOD0(Seekable, WebTimeRanges()); MOCK_METHOD0(OnFrozen, void()); @@ -1185,6 +1186,27 @@ Media()->Play(); } +// Test ensures that WebMediaPlayer is always told about pause events, +// even if already paused. +TEST_P(HTMLMediaElementTest, WebMediaPlayerIsPaused) { + // Prepare the player. + Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp)); + test::RunPendingTasks(); + + // Prior to HaveMetadat, Play/Pause won't be delivered to the player. + EXPECT_CALL(*MockMediaPlayer(), Pause()).Times(0); + Media()->Play(); + Media()->pause(); + testing::Mock::VerifyAndClearExpectations(MockMediaPlayer()); + + // After metadata pause should be delivered even if already paused. + SetReadyState(HTMLMediaElement::kHaveMetadata); + EXPECT_CALL(*MockMediaPlayer(), Pause()).Times(2); + Media()->pause(); + Media()->pause(); + testing::Mock::VerifyAndClearExpectations(MockMediaPlayer()); +} + TEST_P(HTMLMediaElementTest, OnTimeUpdate_ReadyState) { // Prepare the player. Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_host.cc b/third_party/blink/renderer/core/inspector/dev_tools_host.cc index b1051d2..e9ce363f 100644 --- a/third_party/blink/renderer/core/inspector/dev_tools_host.cc +++ b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
@@ -171,19 +171,18 @@ void DevToolsHost::sendMessageToEmbedder(const String& message) { if (client_) { // Strictly convert, as we expect message to be serialized JSON. - auto value = - base::JSONReader::Read(message.Utf8(WTF::Utf8ConversionMode::kStrict)); - if (!value || !value->is_dict()) { + auto value = base::JSONReader::ReadDict( + message.Utf8(WTF::Utf8ConversionMode::kStrict)); + if (!value) { ScriptState* script_state = ToScriptStateForMainWorld(frontend_frame_); if (!script_state) return; V8ThrowException::ThrowTypeError( script_state->GetIsolate(), - value ? "Message to embedder must deserialize to a dictionary value" - : "Message to embedder couldn't be JSON-deserialized"); + "Message to embedder couldn't be deserialized as a JSON object"); return; } - client_->SendMessageToEmbedder(std::move(*value).TakeDict()); + client_->SendMessageToEmbedder(std::move(*value)); } }
diff --git a/third_party/blink/renderer/core/layout/inline/fragment_items.cc b/third_party/blink/renderer/core/layout/inline/fragment_items.cc index 8e26fc7..57e011d 100644 --- a/third_party/blink/renderer/core/layout/inline/fragment_items.cc +++ b/third_party/blink/renderer/core/layout/inline/fragment_items.cc
@@ -60,7 +60,7 @@ // |FragmentItem|. if (auto* layout_text = DynamicTo<LayoutText>(other_item.GetMutableLayoutObject())) - layout_text->DetachAbstractInlineTextBoxesIfNeeded(); + layout_text->DetachAxHooksIfNeeded(); } }
diff --git a/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/third_party/blink/renderer/core/layout/layout_object_child_list.cc index 213d0b8..18927212 100644 --- a/third_party/blink/renderer/core/layout/layout_object_child_list.cc +++ b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
@@ -64,7 +64,7 @@ // be prohibited when moved to different parent as if it were destroyed. if (object->FirstInlineFragmentItemIndex()) { if (auto* text = DynamicTo<LayoutText>(object)) - text->DetachAbstractInlineTextBoxesIfNeeded(); + text->DetachAxHooksIfNeeded(); FragmentItems::LayoutObjectWillBeMoved(*object); } object->SetIsInLayoutNGInlineFormattingContext(false);
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc index 2a5f9e5..ef7fe3b 100644 --- a/third_party/blink/renderer/core/layout/layout_text.cc +++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -260,12 +260,12 @@ Parent()->DirtyLinesFromChangedChild(this); } if (FirstInlineFragmentItemIndex()) { - DetachAbstractInlineTextBoxesIfNeeded(); + DetachAxHooksIfNeeded(); FragmentItems::LayoutObjectWillBeDestroyed(*this); ClearFirstInlineFragmentItemIndex(); } } else if (FirstInlineFragmentItemIndex()) { - DetachAbstractInlineTextBoxesIfNeeded(); + DetachAxHooksIfNeeded(); ClearFirstInlineFragmentItemIndex(); } DeleteTextBoxes(); @@ -297,10 +297,10 @@ void LayoutText::DeleteTextBoxes() { NOT_DESTROYED(); - DetachAbstractInlineTextBoxesIfNeeded(); + DetachAxHooksIfNeeded(); } -void LayoutText::DetachAbstractInlineTextBoxes() { +void LayoutText::DetachAxHooks() { NOT_DESTROYED(); // TODO(layout-dev): Because We should call |WillDestroy()| once for // associated fragments, when you reuse fragments, you should construct @@ -310,14 +310,21 @@ // TODO(yosin): Make sure we call this function within valid containg block // of |this|. InlineCursor cursor; - for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) + for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { AbstractInlineTextBox::WillDestroy(cursor); + } +} + +void LayoutText::ClearBlockFlowCachedData(const LayoutBlockFlow* block_flow) { + if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) { + cache->ClearBlockFlowCachedData(FragmentItemsContainer()); + } } void LayoutText::ClearFirstInlineFragmentItemIndex() { NOT_DESTROYED(); CHECK(IsInLayoutNGInlineFormattingContext()) << *this; - DetachAbstractInlineTextBoxesIfNeeded(); + DetachAxHooksIfNeeded(); first_fragment_item_index_ = 0u; } @@ -326,7 +333,7 @@ CHECK(IsInLayoutNGInlineFormattingContext()); // TODO(yosin): Call |AbstractInlineTextBox::WillDestroy()|. DCHECK_NE(index, 0u); - DetachAbstractInlineTextBoxesIfNeeded(); + DetachAxHooksIfNeeded(); // Changing the first fragment item index causes // LayoutText::FirstAbstractInlineTextBox to return a box, // so notify the AX object for this LayoutText that it might need to
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h index 63e4aba..8db2c13 100644 --- a/third_party/blink/renderer/core/layout/layout_text.h +++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -332,7 +332,7 @@ void InvalidateSubtreeLayoutForFontUpdates() override; - void DetachAbstractInlineTextBoxesIfNeeded(); + void DetachAxHooksIfNeeded(); // Returns the logical location of the first line box, and the logical height // of the LayoutText. @@ -438,7 +438,8 @@ private: ContentCaptureManager* GetOrResetContentCaptureManager(); - void DetachAbstractInlineTextBoxes(); + void DetachAxHooks(); + void ClearBlockFlowCachedData(const LayoutBlockFlow* block_flow); virtual unsigned NonCollapsedCaretMaxOffset() const; @@ -470,10 +471,15 @@ return first_fragment_item_index_; } -inline void LayoutText::DetachAbstractInlineTextBoxesIfNeeded() { +inline void LayoutText::DetachAxHooksIfNeeded() { if (has_abstract_inline_text_box_) [[unlikely]] { - DetachAbstractInlineTextBoxes(); + DetachAxHooks(); } + if (!IsInLayoutNGInlineFormattingContext()) { + return; + } + + ClearBlockFlowCachedData(FragmentItemsContainer()); } template <>
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 21aa8f4..53258335 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -5555,7 +5555,8 @@ if (::features::IsAccessibilityBlockFlowIteratorEnabled()) { DCHECK(it.Next()) << "Failed to advance the BlockFlow Iterator while " "processing AxInlineTextBox children of " - << this; + << this << " which has layout " << GetLayoutObject() + << "\n and the AITB produced " << box->GetText(); WTF::String fragment_text = it.GetText(); WTF::String abstract_inline_text = box->GetText();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index af0f071..4da4c97a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -122,6 +122,7 @@ #include "third_party/blink/renderer/platform/graphics/dom_node_id.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/wtf/functional.h" +#include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/ax_common.h" #include "ui/accessibility/ax_enums.mojom-blink.h" #include "ui/accessibility/ax_event.h" @@ -2170,6 +2171,17 @@ MarkAXObjectDirty(ax_object); } +void AXObjectCacheImpl::ClearBlockFlowCachedData( + const LayoutBlockFlow* block_flow) { + if (!::features::IsAccessibilityBlockFlowIteratorEnabled()) { + return; + } + auto it = block_flow_data_cache_.find(block_flow); + if (it != block_flow_data_cache_.end()) { + block_flow_data_cache_.erase(it); + } +} + void AXObjectCacheImpl::CSSAnchorChanged(const LayoutObject* positioned_obj) { if (Node* node = positioned_obj->GetNode()) { DeferTreeUpdate(TreeUpdateReason::kCSSAnchorChanged, node); @@ -3416,14 +3428,19 @@ const AXBlockFlowData* AXObjectCacheImpl::GetBlockFlowData( const AXObject* object) { - // TODO: Assumption that we are only really working on one paragraph at a - // time turned out to be incorrect. Ideally, we can come up with a strategy - // to make this work in order to avoid memory bloat. For now just compute the - // AxBlockFlowData every time as depending on a cached version may cause - // problems. LayoutBlockFlow* block_flow = object->GetLayoutObject()->FragmentItemsContainer(); - return MakeGarbageCollected<AXBlockFlowData>(block_flow); + if (!block_flow) { + return nullptr; + } + auto it = block_flow_data_cache_.find(block_flow); + if (it != block_flow_data_cache_.end()) { + return it->value; + } + auto result = block_flow_data_cache_.insert( + block_flow, MakeGarbageCollected<AXBlockFlowData>(block_flow)); + CHECK(result.is_new_entry); + return result.stored_value->value; } bool AXObjectCacheImpl::IsParsingMainDocument() const { @@ -6121,6 +6138,7 @@ visitor->Trace(next_on_line_map_); visitor->Trace(processed_blocks_); visitor->Trace(previous_on_line_map_); + visitor->Trace(block_flow_data_cache_); visitor->Trace(tree_update_callback_queue_main_); visitor->Trace(tree_update_callback_queue_popup_);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h index 3d57cad..e0d2feb 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -274,6 +274,9 @@ void TextChanged(const LayoutObject*) override; void TextChangedWithCleanLayout(Node* optional_node, AXObject*); + // Called when fragments in the LayoutBlockFlow changed. + void ClearBlockFlowCachedData(const LayoutBlockFlow* block_flow) override; + void DocumentTitleChanged() override; // Returns true if we can immediately process tree updates for this node. @@ -1269,6 +1272,9 @@ previous_on_line_map_; HeapHashSet<Member<const LayoutBlockFlow>> processed_blocks_; + HeapHashMap<Member<const LayoutBlockFlow>, Member<AXBlockFlowData>> + block_flow_data_cache_; + FRIEND_TEST_ALL_PREFIXES(AccessibilityTest, PauseUpdatesAfterMaxNumberQueued); FRIEND_TEST_ALL_PREFIXES(AccessibilityTest, UpdateAXForAllDocumentsAfterPausedUpdates);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl index 4a2e4f8..95fd1660 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl +++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
@@ -34,7 +34,7 @@ // image smoothing [NoAllocDirectCall=Setter] attribute boolean imageSmoothingEnabled; // (default True) - [RuntimeEnabled=CanvasImageSmoothing, MeasureAs=Canvas2DImageSmoothingQuality2] attribute ImageSmoothingQuality imageSmoothingQuality; // (default "low") + [MeasureAs=Canvas2DImageSmoothingQuality2] attribute ImageSmoothingQuality imageSmoothingQuality; // (default "low") // colors and styles (see also the CanvasDrawingStyles interface) [HighEntropy, SetterCallWith=Isolate, RaisesException=Setter, GetterCallWith=ScriptState] attribute any strokeStyle; // (default black)
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc index e789251..2ebdb9de 100644 --- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc +++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -93,16 +93,18 @@ } std::optional<media::AudioCodec> TryGetPcmCodec(const String& codec) { - if (codec == "ulaw") { + String codecs_str = codec.LowerASCII(); + if (codecs_str == "ulaw") { return media::AudioCodec::kPCM_MULAW; } - if (codec == "alaw") { + if (codecs_str == "alaw") { return media::AudioCodec::kPCM_ALAW; } - if (codec == "pcm-u8" || codec == "pcm-s16" || codec == "pcm-s24" || - codec == "pcm-s32" || codec == "pcm-f32") { + if (codecs_str == "pcm-u8" || codecs_str == "pcm-s16" || + codecs_str == "pcm-s24" || codecs_str == "pcm-s32" || + codecs_str == "pcm-f32") { return media::AudioCodec::kPCM; } @@ -110,29 +112,33 @@ } media::SampleFormat PcmCodecToSampleFormat(const String& codec) { - CHECK(codec.StartsWith("pcm")); + String codecs_str = codec.LowerASCII(); - if (codec == "pcm-u8") { + if (codecs_str == "pcm-u8") { return media::SampleFormat::kSampleFormatU8; } - if (codec == "pcm-s16") { + if (codecs_str == "pcm-s16") { return media::SampleFormat::kSampleFormatS16; } - if (codec == "pcm-s24") { + if (codecs_str == "pcm-s24") { return media::SampleFormat::kSampleFormatS24; } - if (codec == "pcm-s32") { + if (codecs_str == "pcm-s32") { return media::SampleFormat::kSampleFormatS32; } - if (codec == "pcm-f32") { + if (codecs_str == "pcm-f32") { return media::SampleFormat::kSampleFormatF32; } - return media::SampleFormat::kUnknownSampleFormat; + // We want to default to F32Planar for unexpected states, as well as the case + // of the codec being "1", which is a valid PCM codec for WAV (seen in + // media/base/mime_util_internal.cc). This return catches both unexpected + // cases and this desired case. + return media::SampleFormat::kSampleFormatPlanarF32; } // static
diff --git a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc index 63cdab1..996ca62 100644 --- a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc +++ b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
@@ -369,8 +369,7 @@ BASE_FEATURE(kGpuMemoryBufferReadbackFromTexture, "GpuMemoryBufferReadbackFromTexture", -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || \ - BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc index c454ccb..b34ac49 100644 --- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc +++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -3833,6 +3833,9 @@ // we should plumb the pause reason from here all the way through to // `WebMediaPlayerImpl::Pause`, where the reset is done. client_->PausePlayback(pause_reason); + + // NOTE: The reason MUST be set AFTER `PausePlayback()` is called, since it + // will call `Pause()` which clears the `visibility_pause_reason_`. visibility_pause_reason_ = pause_reason; }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 1caf2ce..329afc27 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -729,10 +729,6 @@ status: "experimental", }, { - name: "CanvasImageSmoothing", - status: "experimental", - }, - { // https://crbug.com/377325952 name: "CanvasInterventions", browser_process_read_write_access: true,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index 1a89255..25ba6c2 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -2222,11 +2222,15 @@ void MainThreadSchedulerImpl::OnPageFrozen( base::MemoryReductionTaskContext called_from) { + main_thread_only().renderer_frozen_metadata.emplace( + "MainThreadSchedulerImpl.RendererFrozen", /* is_frozen */ 1, + base::SampleMetadataScope::kProcess); memory_purge_manager_.OnPageFrozen(called_from); UpdatePolicy(); } void MainThreadSchedulerImpl::OnPageResumed() { + main_thread_only().renderer_frozen_metadata.reset(); memory_purge_manager_.OnPageResumed(); UpdatePolicy(); }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 42b07c2c..3e1a1bd 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -705,6 +705,7 @@ bool renderer_hidden = false; std::optional<base::ScopedSampleMetadata> renderer_hidden_metadata; + std::optional<base::ScopedSampleMetadata> renderer_frozen_metadata; bool renderer_backgrounded = kLaunchingProcessIsBackgrounded; TraceableState<bool, TracingCategory::kDefault> blocking_input_expected_soon;
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc index a3ace153..e558cd72 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.cc +++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -504,6 +504,9 @@ // Web tests can override the device scale factor in the renderer. if (auto scale_factor = client_->GetTestingDeviceScaleFactorOverride()) { screen_info.device_scale_factor = scale_factor; + visual_properties.compositor_viewport_pixel_rect = + gfx::Rect(gfx::ScaleToCeiledSize(visual_properties.new_size, + screen_info.device_scale_factor)); } // Inform the rendering thread of the color space indicating the presence of
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h index 37bdfbd..dbd53ba9 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.h +++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -405,9 +405,6 @@ void OnDevToolsSessionConnectionChanged(bool attached); - // Helper to get the non-emulated device scale factor. - float GetOriginalDeviceScaleFactor() const; - private: static void AssertAreCompatible(const WidgetBase& a, const WidgetBase& b); @@ -434,6 +431,9 @@ // Called after the delay given in `RequestAnimationAfterDelay()`. void RequestAnimationAfterDelayTimerFired(TimerBase*); + // Helper to get the non-emulated device scale factor. + float GetOriginalDeviceScaleFactor() const; + // Finishes the call to RequestNewLayerTreeFrameSink() once the // |gpu_channel_host| is available. // TODO(crbug.com/1278147): Clean up these parameters using either a struct or
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index f131e6b..cb1f356 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2694,8 +2694,6 @@ crbug.com/388345823 [ Win ] external/wpt/fetch/api/credentials/cookies.any.sharedworker.html [ Crash ] crbug.com/388312947 [ Mac14 ] virtual/composite-clip-path-animation/external/wpt/css/css-masking/clip-path/animations/clip-path-transition.html [ Crash ] crbug.com/388312947 [ Win ] virtual/composite-clip-path-animation/external/wpt/css/css-masking/clip-path/animations/clip-path-transition.html [ Crash ] -crbug.com/388376333 [ Mac12 ] virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-inside-top-layer.tentative.html [ Crash ] -crbug.com/388376333 [ Win ] virtual/customizable-select-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-inside-top-layer.tentative.html [ Crash ] crbug.com/388326073 [ Mac14 ] virtual/fedcm-authz/external/wpt/fedcm/fedcm-authz/fedcm-disclosure-text-shown.https.html [ Crash ] crbug.com/388326073 [ Win ] virtual/fedcm-authz/external/wpt/fedcm/fedcm-authz/fedcm-disclosure-text-shown.https.html [ Crash ] [ Linux ] virtual/fenced-frame-mparch-internal/wpt_internal/fenced_frame/revoke-manual-report-event-beacons.https.html [ Crash ] @@ -9085,6 +9083,9 @@ crbug.com/366530634 external/wpt/storage-access-api/storage-access-headers.tentative.https.sub.window.html [ Failure Pass Timeout ] crbug.com/366530634 external/wpt/storage-access-api/storage-access-permission.sub.https.window.html [ Failure Pass Timeout ] +crbug.com/40653832 external/wpt/css/css-position/sticky/position-sticky-rtl.html [ Failure ] +crbug.com/40653832 virtual/fractional-scroll-offsets/external/wpt/css/css-position/sticky/position-sticky-rtl.html [ Failure ] + # Gardener 2024-12-20 crbug.com/379003901 [ Mac11 ] virtual/gpu-rasterization/external/wpt/css/css-images/object-view-box-fit-cover-video.html [ Failure Pass ] @@ -9109,3 +9110,6 @@ crbug.com/385337582 external/wpt/paint-timing/fcp-only/fcp-document-opacity-image.html [ Failure Pass ] crbug.com/383627707 [ Mac ] external/wpt/element-timing/image-carousel.html [ Failure Pass ] crbug.com/379474211 external/wpt/paint-timing/fcp-only/fcp-document-opacity-text.html [ Failure Pass ] + +crbug.com/389559091 [ Linux ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-home-end-pagedown-pageup.tentative.html [ Pass Timeout ] +crbug.com/389559091 [ Mac ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-home-end-pagedown-pageup.tentative.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html index 89854a6..abe20d5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Masonry: masonry-slack getComputedStyle()</title> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> <meta name="assert" content="masonry-slack computed value is as specified."> <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html index feb90fd..75bae04 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Masonry: masonry-slack parsing</title> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> <meta name="assert" content="masonry-slack supports only the grammar 'normal | <length-percentage>'."> <meta name="assert" content="masonry-slack rejects negative <length-percentage>.">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html index 9447b9b..55044a9 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Masonry: masonry-slack parsing</title> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> <meta name="assert" content="masonry-slack supports the full grammar 'normal | <length-percentage>'."> <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-invalid.html index df69085..7fed500c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-invalid.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <title>CSS Masonry: Parsing masonry-template-tracks with invalid values</title> <link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com"> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/#template-tracks"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3/#propdef-masonry-template-tracks"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/parsing-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-valid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-valid.html index 7397760..45550cb 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-template-tracks-valid.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <title>CSS Masonry: Parsing masonry-template-tracks with valid values</title> <link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com"> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/#template-tracks"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3/#propdef-masonry-template-tracks"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/parsing-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-computed.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-computed.html index a8239bf2..771626c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-computed.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <title>CSS Masonry: masonry-track-* getComputedStyle()</title> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/computed-testcommon.js"></script> @@ -44,4 +44,4 @@ test_computed_value("masonry-track", "span first", "span first"); test_computed_value("masonry-track", "span 2 first / auto", "span 2 first"); test_computed_value("masonry-track", "span 2 first", "span 2 first"); -</script> \ No newline at end of file +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-invalid.html index 65887008..ba03c84 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-invalid.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <title>CSS Masonry: masonry-track-* parsing</title> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/parsing-testcommon.js"></script> @@ -53,4 +53,4 @@ test_invalid_value("masonry-track", "first span 1 / last"); test_invalid_value("masonry-track", "3 first / 2 span last"); test_invalid_value("masonry-track", "3 / 1 span 2"); -</script> \ No newline at end of file +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-valid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-valid.html index 5f98422..2625c262 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-track-valid.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <title>CSS Masonry: masonry-track-* parsing</title> <link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> -<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/"> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/parsing-testcommon.js"></script> @@ -95,4 +95,4 @@ "masonry-track-start": "last", "masonry-track-end": "last" }); -</script> \ No newline at end of file +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high-ref.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high-ref.html new file mode 100644 index 0000000..4a1a62b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<canvas id="canvas" width="300" height="300"></canvas> +<script> + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('2d'); + ctx.imageSmoothingQuality = 'high'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, 300, 300); + const image = new Image(); + image.src = './resources/html5.png'; + image.onload = () => { + ctx.drawImage(image, 0, 0, 200, 200); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high.https.html new file mode 100644 index 0000000..9e4494e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.high.https.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> +<link rel="match" href="paint2d-imageSmoothingQuality.high-ref.html"> +<style> +#output { + width: 300px; + height: 300px; + background-image: paint(image); + border-image: url(./resources/html5.png); +} +</style> +<script src="/common/reftest-wait.js"></script> +<script src="/common/worklet-reftest.js"></script> +<div id="output"></div> + +<script id="code" type="text/worklet"> + registerPaint('image', class { + static get inputProperties() { return [ 'border-image-source' ]; }; + paint(ctx, geom, styleMap) { + ctx.imageSmoothingQuality = 'high'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, geom.width, geom.height); + ctx.drawImage(styleMap.get('border-image-source'), 0, 0, 200, 200); + } + }); +</script> + +<script> + importWorkletAndTerminateTestAfterAsyncPaint( + CSS.paintWorklet, document.getElementById('code').textContent); +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low-ref.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low-ref.html new file mode 100644 index 0000000..511da3f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<canvas id="canvas" width="300" height="300"></canvas> +<script> + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('2d'); + ctx.imageSmoothingQuality = 'low'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, 300, 300); + const image = new Image(); + image.src = './resources/html5.png'; + image.onload = () => { + ctx.drawImage(image, 0, 0, 200, 200); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low.https.html new file mode 100644 index 0000000..85338ae --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.low.https.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> +<link rel="match" href="paint2d-imageSmoothingQuality.low-ref.html"> +<style> +#output { + width: 300px; + height: 300px; + background-image: paint(image); + border-image: url(./resources/html5.png); +} +</style> +<script src="/common/reftest-wait.js"></script> +<script src="/common/worklet-reftest.js"></script> +<div id="output"></div> + +<script id="code" type="text/worklet"> + registerPaint('image', class { + static get inputProperties() { return [ 'border-image-source' ]; }; + paint(ctx, geom, styleMap) { + ctx.imageSmoothingQuality = 'low'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, geom.width, geom.height); + ctx.drawImage(styleMap.get('border-image-source'), 0, 0, 200, 200); + } + }); +</script> + +<script> + importWorkletAndTerminateTestAfterAsyncPaint( + CSS.paintWorklet, document.getElementById('code').textContent); +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med-ref.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med-ref.html new file mode 100644 index 0000000..801c512 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<canvas id="canvas" width="300" height="300"></canvas> +<script> + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('2d'); + ctx.imageSmoothingQuality = 'medium'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, 300, 300); + const image = new Image(); + image.src = './resources/html5.png'; + image.onload = () => { + ctx.drawImage(image, 0, 0, 200, 200); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med.https.html new file mode 100644 index 0000000..4d5008a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-imageSmoothingQuality.med.https.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> +<link rel="match" href="paint2d-imageSmoothingQuality.med-ref.html"> +<style> +#output { + width: 300px; + height: 300px; + background-image: paint(image); + border-image: url(./resources/html5.png); +} +</style> +<script src="/common/reftest-wait.js"></script> +<script src="/common/worklet-reftest.js"></script> +<div id="output"></div> + +<script id="code" type="text/worklet"> + registerPaint('image', class { + static get inputProperties() { return [ 'border-image-source' ]; }; + paint(ctx, geom, styleMap) { + ctx.imageSmoothingQuality = 'medium'; + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, geom.width, geom.height); + ctx.drawImage(styleMap.get('border-image-source'), 0, 0, 200, 200); + } + }); +</script> + +<script> + importWorkletAndTerminateTestAfterAsyncPaint( + CSS.paintWorklet, document.getElementById('code').textContent); +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl-ref.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl-ref.html new file mode 100644 index 0000000..b114a3a3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<style> +.container { + display: flex; + overflow: auto; + width: 500px; + position: relative; +} + +.container { + scrollbar-width: none; +} + +.child { + width: 1900px; + height: 100px; + flex-shrink: 0; + background: lightblue; +} +.sticky { + position: absolute; + left: 0; + width: 100px; + height: 100px; + flex-shrink: 0; + background: lightgreen; +} +</style> +<div class="container" dir="rtl"> + <div class="child">1</div> + <div class="sticky">20</div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl.html new file mode 100644 index 0000000..df1a636 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-rtl.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" /> +<meta name="assert" content="Position sticky works correctly with rtl" /> +<link rel="match" href="position-sticky-rtl-ref.html" /> +<style> +.container { + display: flex; + overflow: auto; + width: 500px; + position: relative; +} + +.container { + scrollbar-width: none; +} + +.child { + width: 1900px; + height: 100px; + flex-shrink: 0; + background: lightblue; +} +.sticky { + position: sticky; + left: 0; + width: 100px; + height: 100px; + flex-shrink: 0; + background: lightgreen; +} +</style> +<div class="container" dir="rtl"> + <div class="child">1</div> + <div class="sticky">20</div> +</div>
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt index a15ae65..4dbfd0e 100644 --- a/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt
@@ -187,6 +187,7 @@ CONSOLE MESSAGE: getter globalAlpha CONSOLE MESSAGE: getter globalCompositeOperation CONSOLE MESSAGE: getter imageSmoothingEnabled +CONSOLE MESSAGE: getter imageSmoothingQuality CONSOLE MESSAGE: getter lineCap CONSOLE MESSAGE: getter lineDashOffset CONSOLE MESSAGE: getter lineJoin @@ -239,6 +240,7 @@ CONSOLE MESSAGE: setter globalAlpha CONSOLE MESSAGE: setter globalCompositeOperation CONSOLE MESSAGE: setter imageSmoothingEnabled +CONSOLE MESSAGE: setter imageSmoothingQuality CONSOLE MESSAGE: setter lineCap CONSOLE MESSAGE: setter lineDashOffset CONSOLE MESSAGE: setter lineJoin @@ -586,6 +588,7 @@ CONSOLE MESSAGE: getter globalAlpha CONSOLE MESSAGE: getter globalCompositeOperation CONSOLE MESSAGE: getter imageSmoothingEnabled +CONSOLE MESSAGE: getter imageSmoothingQuality CONSOLE MESSAGE: getter lineCap CONSOLE MESSAGE: getter lineDashOffset CONSOLE MESSAGE: getter lineJoin @@ -638,6 +641,7 @@ CONSOLE MESSAGE: setter globalAlpha CONSOLE MESSAGE: setter globalCompositeOperation CONSOLE MESSAGE: setter imageSmoothingEnabled +CONSOLE MESSAGE: setter imageSmoothingQuality CONSOLE MESSAGE: setter lineCap CONSOLE MESSAGE: setter lineDashOffset CONSOLE MESSAGE: setter lineJoin
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index 7db3433..bf4cf69 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit 7db3433bd4466b20ade77494cd3bb03396441aef +Subproject commit bf4cf6938a77f1aca83ef529dce96681efd1e6c5
diff --git a/third_party/catapult b/third_party/catapult index 580dbb7..d25caed 160000 --- a/third_party/catapult +++ b/third_party/catapult
@@ -1 +1 @@ -Subproject commit 580dbb72d8984e972a02874d06ace7b13295798a +Subproject commit d25caed4b98fea8a30ecf85a9c2b056cb139809e
diff --git a/third_party/dawn b/third_party/dawn index 041b072..60a78b1 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit 041b072241d75850d18f441b53bacf2d317e9818 +Subproject commit 60a78b1ae59d6724d658e1eb3d8e2813afb29cb9
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src index ae6208f..b86e98f 160000 --- a/third_party/fuzztest/src +++ b/third_party/fuzztest/src
@@ -1 +1 @@ -Subproject commit ae6208fc45a09da94d9c0925e26cd9bbca92154b +Subproject commit b86e98ff1149313e43333d1017c3a87baa6721c5
diff --git a/third_party/glslang/src b/third_party/glslang/src index e435148..c16e6a3 160000 --- a/third_party/glslang/src +++ b/third_party/glslang/src
@@ -1 +1 @@ -Subproject commit e43514866f7e0f8265c677039d2fe773c892d44b +Subproject commit c16e6a34b72de51d07c121a1c202806bac0be9dc
diff --git a/third_party/libavif/LICENSE b/third_party/libavif/LICENSE deleted file mode 100644 index 5b188d7..0000000 --- a/third_party/libavif/LICENSE +++ /dev/null
@@ -1,46 +0,0 @@ -Copyright 2019 Joe Drago. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ------------------------------------------------------------------------------- - -Files: tests/cJSON.* - -Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE.
diff --git a/third_party/libavif/README.chromium b/third_party/libavif/README.chromium index a12b1e3..24344f5 100644 --- a/third_party/libavif/README.chromium +++ b/third_party/libavif/README.chromium
@@ -3,8 +3,8 @@ URL: https://github.com/AOMediaCodec/libavif Version: N/A Revision: DEPS -License: BSD-2-Clause, MIT -License File: LICENSE +License: BSD-2-Clause, IJG, Apache-2.0, BSD-3-Clause +License File: src/LICENSE Security Critical: yes Shipped: yes
diff --git a/third_party/libc++abi/src b/third_party/libc++abi/src index 7681005..cbada99 160000 --- a/third_party/libc++abi/src +++ b/third_party/libc++abi/src
@@ -1 +1 @@ -Subproject commit 7681005c6233e8a21b97e24c1a3c5c6979927d5a +Subproject commit cbada99a33f015cb8333d63a88ff0c10cbbc6f38
diff --git a/third_party/lit/v3_0/BUILD.gn b/third_party/lit/v3_0/BUILD.gn index f279486e..c7de797 100644 --- a/third_party/lit/v3_0/BUILD.gn +++ b/third_party/lit/v3_0/BUILD.gn
@@ -64,6 +64,7 @@ "//chrome/test/data/pdf:build_ts", "//chrome/test/data/webui/app_home:build_ts", "//chrome/test/data/webui/cr_components:build_ts", + "//chrome/test/data/webui/cr_components/cr_shortcut_input:build_ts", "//chrome/test/data/webui/cr_components/help_bubble:build_ts", "//chrome/test/data/webui/cr_components/history_clusters:build_ts", "//chrome/test/data/webui/cr_elements:build_ts", @@ -85,6 +86,7 @@ "//content/browser/resources/service_worker:build_ts", "//content/browser/resources/traces_internals:build_ts", "//ui/webui/resources/cr_components/certificate_manager:build_ts", + "//ui/webui/resources/cr_components/cr_shortcut_input:build_ts", "//ui/webui/resources/cr_components/customize_color_scheme_mode:build_ts", "//ui/webui/resources/cr_components/help_bubble:build_ts", "//ui/webui/resources/cr_components/history_clusters:build_ts",
diff --git a/third_party/perfetto b/third_party/perfetto index 38f6220..2eab41ff 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 38f6220c882b08ed8bd8cbb47cff50cfa790e41b +Subproject commit 2eab41ff134151a618b4f54d4b8913d770c4ac22
diff --git a/third_party/skia b/third_party/skia index 539c311..c24b41e 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 539c311a3a7fa76bf50a9c83822815abdb825a18 +Subproject commit c24b41eebcc69f4f8156716a411de04a80b6c3bc
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index 9064fe8..995922d 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit 9064fe8637daf9f694c8a2e035a34da94022f2be +Subproject commit 995922d48149384766cc646159a9e28701f01f0c
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index ee1a15d..5b43f94 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit ee1a15d510c031acaff02138a922ccdb6f85c2a7 +Subproject commit 5b43f9496300bdac77c67501dc3b3738cb3d21c5
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index 5cceb78..cddf237 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit 5cceb78082833556789a64f3237b04df7a826d93 +Subproject commit cddf2371ee3ef9c31deea06ce14df558c20ece04
diff --git a/third_party/webrtc b/third_party/webrtc index da8a535a..2911627 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit da8a535ad4e41fbb587b38346d324a2892ba4183 +Subproject commit 2911627ab3cb5e2549d7e65d176a01ed398b874a
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index f2d9c4c..a03f1c6 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -17370,6 +17370,7 @@ <int value="-1239515260" label="OmniboxDisableCGIParamMatching:disabled"/> <int value="-1239262870" label="TextFragmentAnchor:enabled"/> <int value="-1238992816" label="ShelfDimming:enabled"/> + <int value="-1238769868" label="TabStripGroupDragDropAndroid:disabled"/> <int value="-1237921078" label="SyncUSSNigori:enabled"/> <int value="-1237821073" label="SharedHighlightingUseBlocklist:enabled"/> <int value="-1237621246" label="WebXRGamepadSupport:disabled"/> @@ -22097,6 +22098,7 @@ <int value="653795860" label="HelpAppReleaseNotes:disabled"/> <int value="654199907" label="AllowSyncXHRInPageDismissal:disabled"/> <int value="654879464" label="WalletRequiresFirstSyncSetupComplete:enabled"/> + <int value="655364027" label="TabStripGroupDragDropAndroid:enabled"/> <int value="656779919" label="OsFeedbackJelly:enabled"/> <int value="656864700" label="FillOnAccountSelectHttp:disabled"/> <int value="657832926" label="SwipeToMoveCursor:enabled"/>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml index 9e489f5..6c039b6 100644 --- a/tools/metrics/histograms/metadata/apps/histograms.xml +++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -908,7 +908,7 @@ </histogram> <histogram name="Apps.AppList.NumberOfNonSystemFolders" units="count" - expires_after="2025-02-22"> + expires_after="2025-08-22"> <owner>tbarzic@chromium.org</owner> <owner>jamescook@chromium.org</owner> <owner>chromeos-launcher@google.com</owner> @@ -1951,7 +1951,7 @@ </histogram> <histogram name="Apps.AppListFolder.ShowHide.AnimationSmoothness" units="%" - expires_after="2025-02-22"> + expires_after="2025-08-22"> <owner>tbarzic@chromium.org</owner> <owner>jamescook@chromium.org</owner> <owner>chromeos-launcher@google.com</owner> @@ -2930,7 +2930,7 @@ </histogram> <histogram name="Apps.TimeBetweenAppInstallAndLaunch{TabletOrClamshell}" - units="ms" expires_after="2025-02-22"> + units="ms" expires_after="2025-08-22"> <owner>tbarzic@chromium.org</owner> <owner>jamescook@chromium.org</owner> <owner>chromeos-launcher@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml index 84c0bf9..81c7aff7 100644 --- a/tools/metrics/histograms/metadata/cookie/histograms.xml +++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -583,6 +583,18 @@ </summary> </histogram> +<histogram name="Cookie.GetCookieListWithOptions.Duration" units="microseconds" + expires_after="2025-07-13"> + <owner>anthonyvd@chromium.org</owner> + <owner>chrome-catan@google.com</owner> + <summary> + Records the time spent executing CookieMonster::GetCookieListWithOptions, + plus its callback if the callback is invoked synchronously. + + Subsampled to 1/1000. Not reported for clients with low-resolution clocks. + </summary> +</histogram> + <histogram name="Cookie.HasNonASCII.{CookieField}" enum="Boolean" expires_after="2025-05-18"> <owner>bingler@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml index b7b7bde1..ac8f2ea 100644 --- a/tools/metrics/histograms/metadata/search/histograms.xml +++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -54,7 +54,7 @@ </histogram> <histogram name="Search.AuxiliarySearch.DeleteTime{AuxiliarySearchDataType}" - units="ms" expires_after="M140"> + units="ms" expires_after="2026-02-10"> <owner>gangwu@chromium.org</owner> <owner>chrome-mobile-search@google.com</owner> <summary>Time taken for deleting contents from the auxiliary search.</summary> @@ -63,7 +63,7 @@ <histogram name="Search.AuxiliarySearch.DeletionFailure{AuxiliarySearchDataType}" - units="count" expires_after="M140"> + units="count" expires_after="2026-02-10"> <owner>gangwu@chromium.org</owner> <owner>chrome-mobile-search@google.com</owner> <summary> @@ -74,7 +74,7 @@ <histogram name="Search.AuxiliarySearch.DeletionRequestStatus{AuxiliarySearchDataType}" - enum="AuxiliarySearchRequestStatus" expires_after="M140"> + enum="AuxiliarySearchRequestStatus" expires_after="2026-02-10"> <owner>gangwu@chromium.org</owner> <owner>chrome-mobile-search@google.com</owner> <summary> @@ -94,7 +94,7 @@ <histogram name="Search.AuxiliarySearch.DonationFailure{AuxiliarySearchDataType}" - units="count" expires_after="M140"> + units="count" expires_after="2026-02-10"> <owner>gangwu@chromium.org</owner> <owner>chrome-mobile-search@google.com</owner> <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 87cc92a..c8c0a9a5 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": "81ac9b9e0950715b1cd7bfea1230606e5ef4357f", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/38f6220c882b08ed8bd8cbb47cff50cfa790e41b/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/2eab41ff134151a618b4f54d4b8913d770c4ac22/trace_processor_shell.exe" }, "linux_arm": { "hash": "a15d8362d80cfd7cd8d785cf6afc22586de688cd", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v49.0/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "24fb6186c9dcbb4c77c7200803988100907b9730", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/38f6220c882b08ed8bd8cbb47cff50cfa790e41b/trace_processor_shell" + "hash": "f2ce431c4ccb7c9de644e63d3c43bdb7e43a6e9f", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/2eab41ff134151a618b4f54d4b8913d770c4ac22/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/typescript/path_mappings.py b/tools/typescript/path_mappings.py index a18d58b..f0ea406c 100644 --- a/tools/typescript/path_mappings.py +++ b/tools/typescript/path_mappings.py
@@ -23,6 +23,7 @@ "cr_components/certificate_manager", "cr_components/color_change_listener", "cr_components/commerce", + "cr_components/cr_shortcut_input", "cr_components/customize_color_scheme_mode", "cr_components/customize_themes", "cr_components/help_bubble",
diff --git a/ui/accessibility/extensions/strings/accessibility_extensions_strings.grd b/ui/accessibility/extensions/strings/accessibility_extensions_strings.grd index 982072d..e9d975a4 100644 --- a/ui/accessibility/extensions/strings/accessibility_extensions_strings.grd +++ b/ui/accessibility/extensions/strings/accessibility_extensions_strings.grd
@@ -4,7 +4,7 @@ name of the extension as a prefix, e.g. IDS_ALT_APP_NAME. --> <grit base_dir="." latest_public_release="0" current_release="1" - output_all_resource_defines="false" enc_check="möl" source_lang_id="en"> + enc_check="möl" source_lang_id="en"> <outputs> <output filename="_locales/am/messages.json" type="chrome_messages_json" lang="am"/> <output filename="_locales/ar/messages.json" type="chrome_messages_json" lang="ar"/>
diff --git a/ui/android/event_forwarder.cc b/ui/android/event_forwarder.cc index a980682..34c80f23 100644 --- a/ui/android/event_forwarder.cc +++ b/ui/android/event_forwarder.cc
@@ -209,7 +209,7 @@ jlong time_ms, jfloat scale) { float dip_scale = view_->GetDipScale(); - auto size = view_->GetSizeDIPs(); + auto size = view_->GetSize(); float x = size.width() / 2; float y = size.height() / 2; gfx::PointF root_location = @@ -226,7 +226,7 @@ const JavaParamRef<jobject>& motion_event, jlong event_time_ns, jlong down_time_ms) { - auto size = view_->GetSizeDIPs(); + auto size = view_->GetSize(); float x = size.width() / 2; float y = size.height() / 2; ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
diff --git a/ui/android/java/src/org/chromium/ui/InsetObserver.java b/ui/android/java/src/org/chromium/ui/InsetObserver.java index cf26c5e..105b91be 100644 --- a/ui/android/java/src/org/chromium/ui/InsetObserver.java +++ b/ui/android/java/src/org/chromium/ui/InsetObserver.java
@@ -100,7 +100,7 @@ InsetConsumerSource.APP_HEADER_COORDINATOR_CAPTION, InsetConsumerSource.EDGE_TO_EDGE_CONTROLLER_IMPL, InsetConsumerSource.EDGE_TO_EDGE_LAYOUT_COORDINATOR, - InsetConsumerSource.APP_HEADER_COORDINATOR_IME, + InsetConsumerSource.APP_HEADER_COORDINATOR_BOTTOM, InsetConsumerSource.COUNT }) @Retention(RetentionPolicy.SOURCE) @@ -112,7 +112,7 @@ int APP_HEADER_COORDINATOR_CAPTION = 2; int EDGE_TO_EDGE_CONTROLLER_IMPL = 3; int EDGE_TO_EDGE_LAYOUT_COORDINATOR = 4; - int APP_HEADER_COORDINATOR_IME = 5; + int APP_HEADER_COORDINATOR_BOTTOM = 5; // Update this whenever a consumer source is added or removed. int COUNT = 6;
diff --git a/ui/android/java/strings/android_ui_strings.grd b/ui/android/java/strings/android_ui_strings.grd index 20b6af0..b664d29 100644 --- a/ui/android/java/strings/android_ui_strings.grd +++ b/ui/android/java/strings/android_ui_strings.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="values-af/android_ui_strings.xml" lang="af" type="android" /> <output filename="values-am/android_ui_strings.xml" lang="am" type="android" />
diff --git a/ui/android/junit/src/org/chromium/ui/InsetObserverTest.java b/ui/android/junit/src/org/chromium/ui/InsetObserverTest.java index eaf7eba4d..bff3a105 100644 --- a/ui/android/junit/src/org/chromium/ui/InsetObserverTest.java +++ b/ui/android/junit/src/org/chromium/ui/InsetObserverTest.java
@@ -144,7 +144,7 @@ public void applyInsets_withMultipleInsetConsumers() { // Add consumers in reverse order of priority. mInsetObserver.addInsetsConsumer( - mInsetsConsumer1, InsetConsumerSource.APP_HEADER_COORDINATOR_IME); + mInsetsConsumer1, InsetConsumerSource.APP_HEADER_COORDINATOR_BOTTOM); mInsetObserver.addInsetsConsumer( mInsetsConsumer2, InsetConsumerSource.DEFERRED_IME_WINDOW_INSET_APPLICATION_CALLBACK);
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc index a955092..ab9d83f 100644 --- a/ui/android/view_android.cc +++ b/ui/android/view_android.cc
@@ -151,9 +151,9 @@ // Empty view size also need not propagating down in order to prevent // spurious events with empty size from being sent down. - if (child->match_parent() && !bounds_device_px_.IsEmpty() && - child->GetSizeDevicePx() != bounds_device_px_.size()) { - child->OnSizeChangedInternal(bounds_device_px_.size()); + if (child->match_parent() && !bounds_.IsEmpty() && + child->GetSize() != bounds_.size()) { + child->OnSizeChangedInternal(bounds_.size()); child->DispatchOnSizeChanged(); } @@ -493,32 +493,25 @@ // Match-parent view must not receive size events. DCHECK(!match_parent()); - gfx::Size size_device_px(width, height); - - if (bounds_device_px_.size() == size_device_px) { + float scale = GetDipScale(); + gfx::Size size(std::ceil(width / scale), std::ceil(height / scale)); + if (bounds_.size() == size) return; - } - OnSizeChangedInternal(size_device_px); + OnSizeChangedInternal(size); // Signal resize event after all the views in the tree get the updated size. DispatchOnSizeChanged(); } -void ViewAndroid::OnSizeChangedInternal(const gfx::Size& size_device_px) { - if (bounds_device_px_.size() == size_device_px) { +void ViewAndroid::OnSizeChangedInternal(const gfx::Size& size) { + if (bounds_.size() == size) return; - } - bounds_device_px_.set_size(size_device_px); - - float scale = GetDipScale(); - bounds_dips_.set_size(gfx::Size(std::ceil(size_device_px.width() / scale), - std::ceil(size_device_px.height() / scale))); - + bounds_.set_size(size); for (ViewAndroid* child : children_) { if (child->match_parent()) - child->OnSizeChangedInternal(size_device_px); + child->OnSizeChangedInternal(size); } } @@ -561,12 +554,8 @@ return physical_size_; } -gfx::Size ViewAndroid::GetSizeDIPs() const { - return bounds_dips_.size(); -} - -gfx::Size ViewAndroid::GetSizeDevicePx() const { - return bounds_device_px_.size(); +gfx::Size ViewAndroid::GetSize() const { + return bounds_.size(); } bool ViewAndroid::OnDragEvent(const DragEventAndroid& event) { @@ -696,7 +685,7 @@ const E& event, const gfx::PointF& point) { if (event_handler_) { - if (bounds_dips_.origin().IsOrigin()) { // (x, y) == (0, 0) + if (bounds_.origin().IsOrigin()) { // (x, y) == (0, 0) if (handler_callback.Run(event_handler_.get(), event)) return true; } else { @@ -708,14 +697,14 @@ if (!children_.empty()) { gfx::PointF offset_point(point); - offset_point.Offset(-bounds_dips_.x(), -bounds_dips_.y()); + offset_point.Offset(-bounds_.x(), -bounds_.y()); gfx::Point int_point = gfx::ToFlooredPoint(offset_point); // Match from back to front for hit testing. for (ViewAndroid* child : base::Reversed(children_)) { bool matched = child->match_parent(); if (!matched) - matched = child->bounds_dips_.Contains(int_point); + matched = child->bounds_.Contains(int_point); if (matched && child->HitTest(handler_callback, event, offset_point)) return true; } @@ -724,8 +713,7 @@ } void ViewAndroid::SetLayoutForTesting(int x, int y, int width, int height) { - bounds_dips_.SetRect(x, y, width, height); - bounds_device_px_.SetRect(x, y, width, height); + bounds_.SetRect(x, y, width, height); } size_t ViewAndroid::GetChildrenCountForTesting() const {
diff --git a/ui/android/view_android.h b/ui/android/view_android.h index 314f89e..78876253 100644 --- a/ui/android/view_android.h +++ b/ui/android/view_android.h
@@ -168,10 +168,8 @@ jint drag_obj_rect_height); gfx::Size GetPhysicalBackingSize() const; - gfx::Size GetSizeDIPs() const; - gfx::Size GetSizeDevicePx() const; + gfx::Size GetSize() const; - // |width| and |height| are in device pixels. void OnSizeChanged(int width, int height); // |deadline_override| if not nullopt will be used as the cc::DeadlinePolicy // timeout for this resize. @@ -304,7 +302,7 @@ // each leaf of subtree. static bool SubtreeHasEventForwarder(ViewAndroid* view); - void OnSizeChangedInternal(const gfx::Size& size_device_px); + void OnSizeChangedInternal(const gfx::Size& size); void DispatchOnSizeChanged(); // Returns the Java delegate for this view. This is used to delegate work @@ -322,11 +320,7 @@ // Basic view layout information. Used to do hit testing deciding whether // the passed events should be processed by the view. Unit in DIP. - gfx::Rect bounds_dips_; - - // Same as above, but before dividing by the device scale factor. - gfx::Rect bounds_device_px_; - + gfx::Rect bounds_; const LayoutType layout_type_; // In physical pixel.
diff --git a/ui/android/view_android_unittest.cc b/ui/android/view_android_unittest.cc index e3d3202..bbe3a4e 100644 --- a/ui/android/view_android_unittest.cc +++ b/ui/android/view_android_unittest.cc
@@ -214,7 +214,7 @@ Reset(); - // Match-parent view should not receive size events in the first place. + // Match-parent view should not receivee size events in the first place. EXPECT_DCHECK_DEATH(viewm_.OnSizeChanged(100, 200)); EXPECT_FALSE(handlerm_.OnSizeCalled()); EXPECT_FALSE(handler3_.OnSizeCalled());
diff --git a/ui/base/test/ui_base_test_resources.grd b/ui/base/test/ui_base_test_resources.grd index 8d8763f..3a9f6e2b 100644 --- a/ui/base/test/ui_base_test_resources.grd +++ b/ui/base/test/ui_base_test_resources.grd
@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <grit base_dir="." latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/ui_base_test_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd index 50ff7c1a..6138436 100644 --- a/ui/resources/ui_resources.grd +++ b/ui/resources/ui_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/ui_resources.h" type="rc_header" context="default_100_percent"> <emit emit_type='prepend'></emit>
diff --git a/ui/resources/ui_unscaled_resources.grd b/ui/resources/ui_unscaled_resources.grd index e1b99717..1674c35 100644 --- a/ui/resources/ui_unscaled_resources.grd +++ b/ui/resources/ui_unscaled_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/ui_unscaled_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc index d37d41c..c8497dffd 100644 --- a/ui/views/controls/table/table_view.cc +++ b/ui/views/controls/table/table_view.cc
@@ -550,6 +550,13 @@ return ax::mojom::SortDirection::kDescending; } +void TableView::SetMouseHoveringEnabled(bool enabled) { + if (hovering_enabled_ != enabled) { + hovering_enabled_ = enabled; + SchedulePaint(); + } +} + void TableView::Layout(PassKey) { // When the scrollview's width changes we force recalculating column sizes. ScrollView* scroll_view = ScrollView::GetScrollViewForContents(this); @@ -766,7 +773,7 @@ } void TableView::OnMouseMoved(const ui::MouseEvent& event) { - if (!HasFocus()) { + if (!hovering_enabled_ || !HasFocus()) { return; } @@ -781,8 +788,10 @@ } void TableView::OnMouseExited(const ui::MouseEvent& event) { - OnHoverChanged(hovered_row_, std::nullopt); - hovered_row_ = std::nullopt; + if (hovering_enabled_) { + OnHoverChanged(hovered_row_, std::nullopt); + hovered_row_ = std::nullopt; + } } void TableView::OnGestureEvent(ui::GestureEvent* event) { @@ -1101,7 +1110,7 @@ hovered_row_.has_value() && hovered_row_.value() == i; if (is_selected) { canvas->FillRect(GetRowBounds(i), selected_bg_color); - } else if (is_hovered) { + } else if (hovering_enabled_ && is_hovered) { canvas->FillRect(GetRowBounds(i), hovered_bg_color); } else if (alternate_bg_color != default_bg_color && (i % 2)) { canvas->FillRect(GetRowBounds(i), alternate_bg_color); @@ -1456,6 +1465,10 @@ void TableView::OnHoverChanged(std::optional<size_t> previous_hovered_row, std::optional<size_t> new_hovered_row) { + if (!hovering_enabled_) { + return; + } + const auto maybe_schedule_paint = [this](std::optional<size_t> row) { if (row.has_value() && row.value() < GetRowCount()) { SchedulePaintInRect(GetRowBounds(row.value()));
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h index 5e8709f..e103346 100644 --- a/ui/views/controls/table/table_view.h +++ b/ui/views/controls/table/table_view.h
@@ -238,6 +238,10 @@ bool GetSortOnPaint() const; void SetSortOnPaint(bool sort_on_paint); + // If enabled, hovering over a row causes the row's background color to + // change. + void SetMouseHoveringEnabled(bool enabled); + // Returns the proper ax sort direction. ax::mojom::SortDirection GetFirstSortDescriptorDirection() const; @@ -592,6 +596,10 @@ // Customization for the header. Includes options such as padding. TableHeaderStyle header_style_; + // TODO(crbug.com/388086397): Enable by mouse hovering by default when color + // tokens are refined on all platforms. + bool hovering_enabled_ = false; + // Weak pointer factory, enables using PostTask safely. base::WeakPtrFactory<TableView> weak_factory_; };
diff --git a/ui/views/examples/views_examples_resources.grd b/ui/views/examples/views_examples_resources.grd index 2acaba0b..88f06dfe 100644 --- a/ui/views/examples/views_examples_resources.grd +++ b/ui/views/examples/views_examples_resources.grd
@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <grit base_dir="." latest_public_release="0" current_release="1" - output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> + source_lang_id="en" enc_check="möl"> <outputs> <output filename="grit/views_examples_resources.h" type="rc_header"> <emit emit_type='prepend'></emit>
diff --git a/ui/views/resources/views_resources.grd b/ui/views/resources/views_resources.grd index f1429ef..fb1f9e5 100644 --- a/ui/views/resources/views_resources.grd +++ b/ui/views/resources/views_resources.grd
@@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> +<grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/views_resources.h" type="rc_header" context="default_100_percent"> <emit emit_type='prepend'></emit>
diff --git a/ui/views/view.cc b/ui/views/view.cc index 6f9417d6..c31e311 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc
@@ -1791,15 +1791,13 @@ // Providing we are attached to a Widget and registered with a focus manager, // we should de-register from that focus manager now. - if (GetWidget() && accelerator_focus_manager_) { - accelerator_focus_manager_->UnregisterAccelerator(accelerator, this); + if (auto* focus_manager = GetFocusManager()) { + focus_manager->UnregisterAccelerator(accelerator, this); } } void View::ResetAccelerators() { - if (accelerators_) { - UnregisterAccelerators(false); - } + UnregisterAccelerators(false); } bool View::AcceleratorPressed(const ui::Accelerator& accelerator) { @@ -2257,15 +2255,12 @@ void View::VisibilityChanged(View* starting_from, bool is_visible) {} -void View::NativeViewHierarchyChanged() { - FocusManager* focus_manager = GetFocusManager(); - if (accelerator_focus_manager_ != focus_manager) { - UnregisterAccelerators(true); +void View::NativeViewHierarchyWillChange() { + UnregisterAccelerators(true); +} - if (focus_manager) { - RegisterPendingAccelerators(); - } - } +void View::NativeViewHierarchyChanged() { + RegisterPendingAccelerators(); } void View::AddedToWidget() {} @@ -3136,9 +3131,7 @@ // their parents. This allows children to override accelerators registered by // their parents as accelerators registered later take priority over those // registered earlier. - if (GetFocusManager()) { - RegisterPendingAccelerators(); - } + RegisterPendingAccelerators(); { internal::ScopedChildrenLock lock(this); @@ -3155,6 +3148,16 @@ } } +void View::PropagateNativeViewHierarchyWillChange() { + { + internal::ScopedChildrenLock lock(this); + for (views::View* child : children_) { + child->PropagateNativeViewHierarchyWillChange(); + } + } + NativeViewHierarchyWillChange(); +} + void View::PropagateNativeViewHierarchyChanged() { { internal::ScopedChildrenLock lock(this); @@ -3621,13 +3624,16 @@ return; } - accelerator_focus_manager_ = GetFocusManager(); - CHECK(accelerator_focus_manager_); + auto* focus_manager = GetFocusManager(); + if (!focus_manager) { + return; + } + for (std::vector<ui::Accelerator>::const_iterator i = accelerators_->begin() + static_cast<ptrdiff_t>(registered_accelerator_count_); i != accelerators_->end(); ++i) { - accelerator_focus_manager_->RegisterAccelerator( + focus_manager->RegisterAccelerator( *i, ui::AcceleratorManager::kNormalPriority, this); } registered_accelerator_count_ = accelerators_->size(); @@ -3639,9 +3645,8 @@ } if (GetWidget()) { - if (accelerator_focus_manager_) { - accelerator_focus_manager_->UnregisterAccelerators(this); - accelerator_focus_manager_ = nullptr; + if (auto* focus_manager = GetFocusManager()) { + focus_manager->UnregisterAccelerators(this); } if (!leave_data_intact) { accelerators_->clear();
diff --git a/ui/views/view.h b/ui/views/view.h index 61f50d0..c03ba19 100644 --- a/ui/views/view.h +++ b/ui/views/view.h
@@ -1783,6 +1783,12 @@ virtual void VisibilityChanged(View* starting_from, bool is_visible); // This method is invoked when the parent NativeView of the widget that the + // view is attached to is about to change, but the view hierarchy will not + // change. This is followed by `NativeViewHierarchyChanged()` below after the + // change has occurred. + virtual void NativeViewHierarchyWillChange(); + + // This method is invoked when the parent NativeView of the widget that the // view is attached to has changed and the view hierarchy has not changed. // ViewHierarchyChanged() is called when the parent NativeView of the widget // that the view is attached to is changed as a result of changing the view @@ -2058,6 +2064,10 @@ void PropagateAddNotifications(const ViewHierarchyChangedDetails& details, bool is_added_to_widget); + // Propagates NativeViewHierarchyWillChange() notification through all the + // children. + void PropagateNativeViewHierarchyWillChange(); + // Propagates NativeViewHierarchyChanged() notification through all the // children. void PropagateNativeViewHierarchyChanged(); @@ -2475,9 +2485,6 @@ // Accelerators -------------------------------------------------------------- - // Focus manager accelerators registered on. - raw_ptr<FocusManager> accelerator_focus_manager_ = nullptr; - // The list of accelerators. List elements in the range // [0, registered_accelerator_count_) are already registered to FocusManager, // and the rest are not yet.
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc index e8246ae..d26dd03b 100644 --- a/ui/views/widget/root_view.cc +++ b/ui/views/widget/root_view.cc
@@ -355,6 +355,10 @@ return children().empty() ? nullptr : children().front(); } +void RootView::NotifyNativeViewHierarchyWillChange() { + PropagateNativeViewHierarchyWillChange(); +} + void RootView::NotifyNativeViewHierarchyChanged() { PropagateNativeViewHierarchyChanged(); }
diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h index 4dc9b9e..f056dd5 100644 --- a/ui/views/widget/root_view.h +++ b/ui/views/widget/root_view.h
@@ -70,6 +70,9 @@ void SetContentsView(View* contents_view); View* GetContentsView(); + // Called when parent of the host is about to change. + void NotifyNativeViewHierarchyWillChange(); + // Called when parent of the host changed. void NotifyNativeViewHierarchyChanged();
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 3136f99..8b11480 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -673,6 +673,7 @@ // that may have been in focus. ClearFocusFromWidget(); native_widget_->OnNativeViewHierarchyWillChange(); + root_view_->NotifyNativeViewHierarchyWillChange(); } void Widget::NotifyNativeViewHierarchyChanged() {
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index 4942f3f..4572513 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn
@@ -52,6 +52,7 @@ if (!is_android && !is_ios) { public_deps += [ "cr_components/app_management:build_grdp", + "cr_components/cr_shortcut_input:build_grdp", "cr_components/customize_color_scheme_mode:build_grdp", "cr_components/help_bubble:build_grdp", "cr_components/localized_link:build_grdp", @@ -67,6 +68,7 @@ "$root_gen_dir/third_party/polymer/v3_0/polymer_3_0_resources.grdp", "$target_gen_dir/cr_components/app_management/resources.grdp", "$target_gen_dir/cr_components/theme_color_picker/resources.grdp", + "$target_gen_dir/cr_components/cr_shortcut_input/resources.grdp", "$target_gen_dir/cr_components/customize_color_scheme_mode/resources.grdp", "$target_gen_dir/cr_components/help_bubble/resources.grdp", "$target_gen_dir/cr_components/localized_link/resources.grdp",
diff --git a/ui/webui/resources/cr_components/cr_shortcut_input/BUILD.gn b/ui/webui/resources/cr_components/cr_shortcut_input/BUILD.gn new file mode 100644 index 0000000..f2d8bfd --- /dev/null +++ b/ui/webui/resources/cr_components/cr_shortcut_input/BUILD.gn
@@ -0,0 +1,32 @@ +# 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("//ui/webui/resources/tools/build_webui.gni") + +assert(!is_android && !is_ios) + +build_webui("build") { + grd_prefix = "cr_components_cr_shortcut_input" + + css_files = [ "cr_shortcut_input.css" ] + + non_web_component_files = [ + "cr_shortcut_input.ts", + "cr_shortcut_input.html.ts", + "cr_shortcut_util.ts", + ] + + ts_out_dir = + "$root_gen_dir/ui/webui/resources/tsc/cr_components/cr_shortcut_input" + ts_composite = true + generate_grdp = true + + ts_deps = [ + "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_elements:build_ts", + "//ui/webui/resources/js:build_ts", + ] + + grd_resource_path_prefix = rebase_path(".", "//ui/webui/resources") +}
diff --git a/chrome/browser/resources/extensions/shortcut_input.css b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.css similarity index 78% rename from chrome/browser/resources/extensions/shortcut_input.css rename to ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.css index 820bf65..53cdb55d 100644 --- a/chrome/browser/resources/extensions/shortcut_input.css +++ b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.css
@@ -4,8 +4,9 @@ /* #css_wrapper_metadata_start * #type=style-lit - * #import=chrome://resources/cr_elements/cr_icons_lit.css.js - * #import=chrome://resources/cr_elements/cr_hidden_style_lit.css.js + * #import=//resources/cr_elements/cr_icons_lit.css.js + * #import=//resources/cr_elements/cr_hidden_style_lit.css.js + * #scheme=relative * #include=cr-icons-lit cr-hidden-style-lit * #css_wrapper_metadata_end */
diff --git a/chrome/browser/resources/extensions/shortcut_input.html.ts b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.html.ts similarity index 76% rename from chrome/browser/resources/extensions/shortcut_input.html.ts rename to ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.html.ts index dbde97f5..52c3fa6 100644 --- a/chrome/browser/resources/extensions/shortcut_input.html.ts +++ b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.html.ts
@@ -4,20 +4,20 @@ import {html} from '//resources/lit/v3_0/lit.rollup.js'; -import type {ShortcutInputElement} from './shortcut_input.js'; +import type {CrShortcutInputElement} from './cr_shortcut_input.js'; -export function getHtml(this: ShortcutInputElement) { +export function getHtml(this: CrShortcutInputElement) { // clang-format off return html`<!--_html_template_start_--> <div id="main"> <cr-input id="input" ?readonly="${this.readonly_}" - aria-label="${this.computeInputAriaLabel_()}" + aria-label="${this.inputAriaLabel}" .placeholder="${this.computePlaceholder_()}" ?invalid="${this.getIsInvalid_()}" .errorMessage="${this.getErrorString_()}" .value="${this.computeText_()}"> <cr-icon-button id="edit" title="$i18n{edit}" - aria-label="${this.computeEditButtonAriaLabel_()}" + aria-label="${this.editButtonAriaLabel}" slot="suffix" class="icon-edit no-overlap" @click="${this.onEditClick_}"> </cr-icon-button>
diff --git a/chrome/browser/resources/extensions/shortcut_input.ts b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.ts similarity index 63% rename from chrome/browser/resources/extensions/shortcut_input.ts rename to ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.ts index 3518094..eeaeadf 100644 --- a/chrome/browser/resources/extensions/shortcut_input.ts +++ b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_input.ts
@@ -2,21 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js'; -import 'chrome://resources/cr_elements/cr_input/cr_input.js'; +import '//resources/cr_elements/cr_icon_button/cr_icon_button.js'; +import '//resources/cr_elements/cr_input/cr_input.js'; -import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js'; -import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js'; -import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js'; -import {assert} from 'chrome://resources/js/assert.js'; -import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; +import {getInstance as getAnnouncerInstance} from '//resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js'; +import type {CrInputElement} from '//resources/cr_elements/cr_input/cr_input.js'; +import {I18nMixinLit} from '//resources/cr_elements/i18n_mixin_lit.js'; +import {assert} from '//resources/js/assert.js'; +import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; -import {createDummyExtensionInfo} from './item_util.js'; -import type {KeyboardShortcutDelegate} from './keyboard_shortcut_delegate.js'; -import {createDummyKeyboardShortcutDelegate} from './keyboard_shortcut_delegate.js'; -import {getCss} from './shortcut_input.css.js'; -import {getHtml} from './shortcut_input.html.js'; -import {formatShortcutText, hasValidModifiers, isValidKeyCode, Key, keystrokeToString} from './shortcut_util.js'; +import {getCss} from './cr_shortcut_input.css.js'; +import {getHtml} from './cr_shortcut_input.html.js'; +import {formatShortcutText, hasValidModifiers, isValidKeyCode, Key, keystrokeToString} from './cr_shortcut_util.js'; enum ShortcutError { NO_ERROR = 0, @@ -25,21 +22,20 @@ NEED_CHARACTER = 3, } -// The UI to display and manage keyboard shortcuts set for extension commands. +// The UI to display and manage keyboard shortcuts. -export interface ExtensionsShortcutInputElement { +export interface CrShortcutInputElement { $: { input: CrInputElement, edit: HTMLElement, }; } -const ExtensionsShortcutInputElementBase = I18nMixinLit(CrLitElement); +const CrShortcutInputElementBase = I18nMixinLit(CrLitElement); -export class ExtensionsShortcutInputElement extends - ExtensionsShortcutInputElementBase { +export class CrShortcutInputElement extends CrShortcutInputElementBase { static get is() { - return 'extensions-shortcut-input'; + return 'cr-shortcut-input'; } static override get styles() { @@ -52,10 +48,9 @@ static override get properties() { return { - delegate: {type: Object}, - item: {type: Object}, - command: {type: Object}, shortcut: {type: String}, + inputAriaLabel: {type: String}, + editButtonAriaLabel: {type: String}, error_: {type: Number}, readonly_: { @@ -65,17 +60,9 @@ }; } - delegate: KeyboardShortcutDelegate = createDummyKeyboardShortcutDelegate(); - item: chrome.developerPrivate.ExtensionInfo = createDummyExtensionInfo(); - command: chrome.developerPrivate.Command = { - description: '', - keybinding: '', - name: '', - isActive: false, - scope: chrome.developerPrivate.CommandScope.CHROME, - isExtensionAction: false, - }; shortcut: string = ''; + inputAriaLabel: string = ''; + editButtonAriaLabel: string = ''; protected readonly_: boolean = true; private capturing_: boolean = false; private error_: ShortcutError = ShortcutError.NO_ERROR; @@ -90,15 +77,16 @@ node.addEventListener('keyup', this.onKeyUp_.bind(this)); } - private startCapture_() { + private async startCapture_() { if (this.capturing_ || this.readonly_) { return; } this.capturing_ = true; - this.delegate.setShortcutHandlingSuspended(true); + await this.updateComplete; + this.fire('input-capture-change', true); } - private endCapture_() { + private async endCapture_() { if (!this.capturing_) { return; } @@ -106,15 +94,15 @@ this.capturing_ = false; this.$.input.blur(); this.error_ = ShortcutError.NO_ERROR; - this.delegate.setShortcutHandlingSuspended(false); this.readonly_ = true; + await this.updateComplete; + this.fire('input-capture-change', false); } private clearShortcut_() { this.pendingShortcut_ = ''; this.shortcut = ''; - // We commit the empty shortcut in order to clear the current shortcut - // for the extension. + // Commit the empty shortcut in order to clear the current shortcut. this.commitPending_(); this.endCapture_(); } @@ -130,7 +118,7 @@ if (e.keyCode === Key.ESCAPE) { if (!this.capturing_) { - // If we're not currently capturing, allow escape to propagate. + // If not currently capturing, allow escape to propagate. return; } // Otherwise, escape cancels capturing. @@ -186,13 +174,13 @@ } private handleKey_(e: KeyboardEvent) { - // While capturing, we prevent all events from bubbling, to prevent + // While capturing, prevent all events from bubbling, to prevent // shortcuts lacking the right modifier (F3 for example) from activating // and ending capture prematurely. e.preventDefault(); e.stopPropagation(); - // We don't allow both Ctrl and Alt in the same keybinding. + // Don't allow both Ctrl and Alt in the same keybinding. // TODO(devlin): This really should go in hasValidModifiers, // but that requires updating the existing page as well. if (e.ctrlKey && e.altKey) { @@ -219,20 +207,10 @@ this.endCapture_(); } - private commitPending_() { + private async commitPending_() { this.shortcut = this.pendingShortcut_; - this.delegate.updateExtensionCommandKeybinding( - this.item.id, this.command.name, this.shortcut); - } - - protected computeInputAriaLabel_(): string { - return this.i18n( - 'editShortcutInputLabel', this.command.description, this.item.name); - } - - protected computeEditButtonAriaLabel_(): string { - return this.i18n( - 'editShortcutButtonLabel', this.command.description, this.item.name); + await this.updateComplete; + this.fire('shortcut-updated', this.shortcut); } protected computePlaceholder_(): string { @@ -257,22 +235,17 @@ protected onEditClick_() { // TODO(ghazale): The clearing functionality should be improved. // Instead of clicking the edit button, and then clicking elsewhere to - // commit the "empty" shortcut, we want to introduce a separate clear - // button. + // commit the "empty" shortcut, introduce a separate clear button. this.clearShortcut_(); this.readonly_ = false; this.$.input.focus(); } } -// Exported to be used in the autogenerated Lit template file -export type ShortcutInputElement = ExtensionsShortcutInputElement; - declare global { interface HTMLElementTagNameMap { - 'extensions-shortcut-input': ExtensionsShortcutInputElement; + 'cr-shortcut-input': CrShortcutInputElement; } } -customElements.define( - ExtensionsShortcutInputElement.is, ExtensionsShortcutInputElement); +customElements.define(CrShortcutInputElement.is, CrShortcutInputElement);
diff --git a/chrome/browser/resources/extensions/shortcut_util.ts b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_util.ts similarity index 92% rename from chrome/browser/resources/extensions/shortcut_util.ts rename to ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_util.ts index 74ce2bf..3c537d80 100644 --- a/chrome/browser/resources/extensions/shortcut_util.ts +++ b/ui/webui/resources/cr_components/cr_shortcut_input/cr_shortcut_util.ts
@@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertNotReached} from 'chrome://resources/js/assert.js'; -import {isChromeOS, isMac} from 'chrome://resources/js/platform.js'; - +import {assertNotReached} from '//resources/js/assert.js'; +import {isChromeOS, isMac} from '//resources/js/platform.js'; export enum Key { COMMA = 188, @@ -68,7 +67,7 @@ } /** - * Checks whether the passed in |keyCode| is a valid extension command key. + * Checks whether the passed in |keyCode| is a valid command key. * @return Whether the key is valid. */ export function isValidKeyCode(keyCode: number): boolean { @@ -85,8 +84,7 @@ } /** - * Converts a keystroke event to string form, ignoring invalid extension - * commands. + * Converts a keystroke event to string form, ignoring invalid commands. */ export function keystrokeToString(e: KeyboardEvent): string { const output = []; @@ -178,7 +176,7 @@ /** * Returns true if the event has valid modifiers. * @param e The keyboard event to consider. - * @return Wether the event is valid. + * @return Whether the event is valid. */ export function hasValidModifiers(e: KeyboardEvent): boolean { switch (getModifierPolicy(e.keyCode)) {
diff --git a/v8 b/v8 index fc91f65..897d8f2 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit fc91f65bbd2f5dfa85e65a007ec01a71a10a45b4 +Subproject commit 897d8f21c242c88045314d105862e4c88e78b859