diff --git a/DEPS b/DEPS index ef9bc69..e7e9aba 100644 --- a/DEPS +++ b/DEPS
@@ -299,15 +299,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': 'd32ef242befe8c58ee01237bcbba6b5a67b0711e', + 'skia_revision': 'eb3f6d58f591daaedbef747f8f4161d3cb669a5a', # 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': '05aca56fd2f465914c0168123499a5a2e3beee9a', + 'v8_revision': '4b1a75acff49a134aca2d2e85520cc2fff199b09', # 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': '1148a66276a924672f052d8609cf56429a538fc6', + 'angle_revision': '75153e1009ac95eb066725db8248453588755a60', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -326,7 +326,7 @@ # 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. - 'fuchsia_version': 'version:11.20230217.0.1', + 'fuchsia_version': 'version:11.20230217.2.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling google-toolbox-for-mac # and whatever else without interference from each other. @@ -370,7 +370,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': '44b7bc5487653bc9c61958ffd58851a6c369d441', + 'catapult_revision': '767feea600c0102ba7cd82cf725bb8de1fc6b1e0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -426,7 +426,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': 'ca469fec10c3b41f672ec7569a861f280990728a', + 'dawn_revision': '32cb509307806cbccef5642ac365df2d9ed229f4', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -764,7 +764,7 @@ 'src/clank': { 'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' + - 'ec0c0cc6520a8c14f821566d044979ce3cbf2fe6', + 'da576c1e69b367796b931cd0c8eb53ef331049e6', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1120,7 +1120,7 @@ Var('boringssl_git') + '/boringssl.git' + '@' + Var('boringssl_revision'), 'src/third_party/breakpad/breakpad': - Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '5687ac51caab4f7100ddcb9dea92ca6bd6be66e7', + Var('chromium_git') + '/breakpad/breakpad.git' + '@' + 'abb105db21e962eda5b7d9b7a0ac8dd701e0b987', 'src/third_party/byte_buddy': { 'packages': [ @@ -1206,13 +1206,13 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3879bd830c6c1c8d4f3460fa93fa58e891e58263', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '62c7a8bad074fdb4339e544b0053206a4f820e15', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'cce1639bd0d6fb15628558ade2dae5c7bc0d002c', + 'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'fb734201152142ab7dbbb4e8fdbc9641a0dca027', 'condition': 'checkout_src_internal', }, @@ -1540,7 +1540,7 @@ Var('chromium_git') + '/webm/libwebp.git' + '@' + '603e8d7adb0ccc35237419c2938194623b60e9be', 'src/third_party/libyuv': - Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '6e4b0acb4b3d5858c77a044aad46132998ac4a76', + Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '2bdc210be9eb11ded16bf3ef1f6cadb0d4dcb0c2', 'src/third_party/lighttpd': { 'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'), @@ -1684,7 +1684,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '58600633967db2ab113cf205eb2be6b76aef97d4', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '0d180f46481a96cbe8340734fa5cdce3bba636c8', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1869,7 +1869,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '6c8361e98f1daba65902f5e2fc1297893ac14b67', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '0277f2b4a71b83f279aebb963f054befd6cc6b1b', + Var('webrtc_git') + '/src.git' + '@' + '48d784225972b7dd0542acfad7cd2d0eed25ad1c', # 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. @@ -1939,7 +1939,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c77eb159ddb564f93ac01cbdfcefdf9861f835c7', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d3157750332d0cf8323f9a74cf699e5525c88d22', 'condition': 'checkout_src_internal', },
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc index 3e7ce6e2..3cca1c1 100644 --- a/ash/accelerators/accelerator_commands.cc +++ b/ash/accelerators/accelerator_commands.cc
@@ -62,6 +62,8 @@ #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_session.h" #include "ash/wm/screen_pinning_controller.h" +#include "ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h" +#include "ash/wm/tablet_mode/tablet_mode_window_manager.h" #include "ash/wm/window_cycle/window_cycle_controller.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -518,14 +520,17 @@ } bool CanToggleMultitaskMenu() { - if (!chromeos::wm::features::IsWindowLayoutMenuEnabled() || - Shell::Get()->tablet_mode_controller()->InTabletMode()) { + if (!chromeos::wm::features::IsWindowLayoutMenuEnabled()) { return false; } aura::Window* window = window_util::GetActiveWindow(); if (!window) { return false; } + if (Shell::Get()->tablet_mode_controller()->InTabletMode()) { + // In tablet mode, the window just has to be able to maximize. + return WindowState::Get(window)->CanMaximize(); + } // If the active window has a visible size button, the menu can be opened. if (auto* size_button = GetFrameSizeButton(window); size_button && size_button->GetVisible()) { @@ -1476,6 +1481,15 @@ DCHECK(chromeos::wm::features::IsWindowLayoutMenuEnabled()); aura::Window* window = window_util::GetActiveWindow(); DCHECK(window); + if (auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller(); + tablet_mode_controller->InTabletMode()) { + auto* tablet_mode_event_handler = + tablet_mode_controller->tablet_mode_window_manager() + ->tablet_mode_multitask_menu_event_handler(); + // Does nothing if the menu is already shown. + tablet_mode_event_handler->ShowMultitaskMenu(window); + return; + } auto* frame_view = NonClientFrameViewAsh::Get(window); if (!frame_view) { // If `window` doesn't have a frame, it must be the multitask menu and have
diff --git a/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc b/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc index d996b98..a1b22f7e 100644 --- a/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc +++ b/ash/assistant/ui/main_stage/suggestion_chip_view_unittest.cc
@@ -75,13 +75,6 @@ return canvas.GetBitmap(); } -SkBitmap GetFocusRingBitmap(views::FocusRing* focus_ring) { - gfx::Canvas canvas(focus_ring->size(), /*image_scale=*/1.0f, - /*is_opaque=*/false); - focus_ring->OnPaint(&canvas); - return canvas.GetBitmap(); -} - } // namespace // Tests ----------------------------------------------------------------------- @@ -161,18 +154,6 @@ ColorProvider::Get()->GetContentLayerColor( ColorProvider::ContentLayerType::kSeparatorColor)))); - // Focus the chip view and confirm that focus ring is rendered. - suggestion_chip_view->RequestFocus(); - views::test::RunScheduledLayout(views::FocusRing::Get(suggestion_chip_view)); - EXPECT_TRUE(cc::ExactPixelComparator().Compare( - GetFocusRingBitmap(views::FocusRing::Get(suggestion_chip_view)), - GetBitmapWithInnerRoundedRect( - kSuggestionChipViewSize, /*stroke_width=*/2, - ColorProvider::Get()->GetControlsLayerColor( - ColorProvider::ControlsLayerType::kFocusRingColor)))); - - suggestion_chip_view->GetFocusManager()->ClearFocus(); - // Switch the color mode. dark_light_mode_controller->ToggleColorMode(); ASSERT_NE(initial_dark_mode_status, @@ -187,15 +168,6 @@ kSuggestionChipViewSize, /*stroke_width=*/1, ColorProvider::Get()->GetContentLayerColor( ColorProvider::ContentLayerType::kSeparatorColor)))); - - // Focus the chip view and confirm that focus ring is rendered. - suggestion_chip_view->RequestFocus(); - EXPECT_TRUE(cc::ExactPixelComparator().Compare( - GetFocusRingBitmap(views::FocusRing::Get(suggestion_chip_view)), - GetBitmapWithInnerRoundedRect( - kSuggestionChipViewSize, /*stroke_width=*/2, - ColorProvider::Get()->GetControlsLayerColor( - ColorProvider::ControlsLayerType::kFocusRingColor)))); } TEST_F(SuggestionChipViewTest, FontWeight) {
diff --git a/ash/shell.cc b/ash/shell.cc index 74c7a0ab..250bdd8 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -219,7 +219,6 @@ #include "chromeos/ash/services/assistant/public/cpp/features.h" #include "chromeos/dbus/init/initialize_dbus_client.h" #include "chromeos/dbus/power/power_policy_controller.h" -#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" #include "chromeos/ui/wm/features.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -849,7 +848,7 @@ // destroyed first to remove the tablet mode observer. glanceables_controller_.reset(); - multitask_menu_nudge_controller_.reset(); + multitask_menu_nudge_delegate_.reset(); tablet_mode_controller_.reset(); login_screen_controller_.reset(); system_notification_controller_.reset(); @@ -1524,15 +1523,6 @@ // since it may need to add observers to root windows. window_restore_controller_ = std::make_unique<WindowRestoreController>(); - // Needs to be constructed after `WindowTreeHostManager::InitHosts()` as it - // needs to get the activation client from chromeos/. - if (chromeos::wm::features::IsWindowLayoutMenuEnabled()) { - multitask_menu_nudge_controller_ = - std::make_unique<chromeos::MultitaskMenuNudgeController>( - GetPrimaryRootWindow(), - std::make_unique<MultitaskMenuNudgeDelegateAsh>()); - } - static_cast<CursorManager*>(cursor_manager_.get())->Init(); mojo::PendingRemote<device::mojom::Fingerprint> fingerprint; @@ -1589,6 +1579,8 @@ if (chromeos::wm::features::IsWindowLayoutMenuEnabled()) { float_controller_ = std::make_unique<FloatController>(); + multitask_menu_nudge_delegate_ = + std::make_unique<MultitaskMenuNudgeDelegateAsh>(); } if (features::IsFederatedServiceEnabled()) {
diff --git a/ash/shell.h b/ash/shell.h index fc82d549..3eb57fe1 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -24,6 +24,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/scoped_observation_traits.h" +#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" #include "ui/display/screen.h" @@ -40,7 +41,6 @@ namespace chromeos { class ImmersiveContext; -class MultitaskMenuNudgeController; class SnapController; } // namespace chromeos @@ -575,9 +575,6 @@ MultiDisplayMetricsController* multi_display_metrics_controller() { return multi_display_metrics_controller_.get(); } - chromeos::MultitaskMenuNudgeController* multitask_menu_nudge_controller() { - return multitask_menu_nudge_controller_.get(); - } NearbyShareControllerImpl* nearby_share_controller() { return nearby_share_controller_.get(); } @@ -939,8 +936,8 @@ multi_display_metrics_controller_; std::unique_ptr<MultiDeviceNotificationPresenter> multidevice_notification_presenter_; - std::unique_ptr<chromeos::MultitaskMenuNudgeController> - multitask_menu_nudge_controller_; + std::unique_ptr<chromeos::MultitaskMenuNudgeController::Delegate> + multitask_menu_nudge_delegate_; std::unique_ptr<NearbyShareControllerImpl> nearby_share_controller_; std::unique_ptr<NearbyShareDelegate> nearby_share_delegate_; std::unique_ptr<ParentAccessController> parent_access_controller_;
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc index 4167970..9cbca1a 100644 --- a/ash/system/message_center/ash_notification_view.cc +++ b/ash/system/message_center/ash_notification_view.cc
@@ -292,6 +292,8 @@ END_METADATA void AshNotificationView::AddedToWidget() { + MessageView::AddedToWidget(); + // crbug/1337661: We need to abort animations in a grouped parent view when // it's widget is being destroyed. By default when a widget is destroyed, all // current animations are forced to finish. The grouped notification removal
diff --git a/ash/system/message_center/ash_notification_view_pixeltest.cc b/ash/system/message_center/ash_notification_view_pixeltest.cc index e300be3f..adaedbb 100644 --- a/ash/system/message_center/ash_notification_view_pixeltest.cc +++ b/ash/system/message_center/ash_notification_view_pixeltest.cc
@@ -5,6 +5,7 @@ #include "ash/capture_mode/capture_mode_controller.h" #include "ash/capture_mode/capture_mode_test_util.h" #include "ash/capture_mode/capture_mode_util.h" +#include "ash/system/message_center/ash_notification_view.h" #include "ash/system/message_center/message_popup_animation_waiter.h" #include "ash/system/notification_center/notification_center_test_api.h" #include "ash/system/unified/unified_system_tray.h" @@ -17,6 +18,7 @@ #include "ui/base/models/image_model.h" #include "ui/message_center/views/message_popup_view.h" #include "ui/message_center/views/message_view.h" +#include "ui/message_center/views/notification_control_buttons_view.h" namespace ash { @@ -86,6 +88,37 @@ std::unique_ptr<NotificationCenterTestApi> test_api_; }; +// Tests that a notification's close button is visible when it is focused. +TEST_F(AshNotificationViewPixelTestBase, CloseButtonFocused) { + // Create a notification and open the notification center bubble to view it. + const auto id = test_api()->AddNotification(); + test_api()->ToggleBubble(); + + // Verify that the close button is neither focused nor visible. Note that the + // close button, as a `views::ImageButton`, will actually be visible in the + // sense of `views::View::GetVisible()`, but its parent's `ui::Layer` will + // have an opacity of zero, making it visually invisible. + auto* notification_view = static_cast<AshNotificationView*>( + test_api()->GetNotificationViewForId(id)); + auto* control_buttons_layer = + notification_view->GetControlButtonsView()->layer(); + auto* close_button = + notification_view->GetControlButtonsView()->close_button(); + EXPECT_EQ(control_buttons_layer->opacity(), 0); + EXPECT_FALSE(close_button->HasFocus()); + + // Move focus to the close button. + close_button->GetWidget()->widget_delegate()->SetCanActivate(true); + close_button->RequestFocus(); + + // Verify, with both an assertion and a pixel test, that the close button has + // focus and is visible. + EXPECT_TRUE(close_button->HasFocus()); + EXPECT_EQ(control_buttons_layer->opacity(), 1); + EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen( + "close_button_focused", 0u, notification_view)); +} + class AshNotificationViewTitlePixelTest : public AshNotificationViewPixelTestBase, public testing::WithParamInterface<
diff --git a/ash/system/notification_center/notification_center_view_unittest.cc b/ash/system/notification_center/notification_center_view_unittest.cc index b07252e..164397b 100644 --- a/ash/system/notification_center/notification_center_view_unittest.cc +++ b/ash/system/notification_center/notification_center_view_unittest.cc
@@ -8,6 +8,7 @@ #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/focus_cycler.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/system/message_center/ash_message_center_lock_screen_controller.h" @@ -188,6 +189,13 @@ return focused_message_view; } + void FocusClearAllButton() { + auto* widget = GetNotificationBarClearAllButton()->GetWidget(); + widget->widget_delegate()->SetCanActivate(true); + Shell::Get()->focus_cycler()->FocusWidget(widget); + GetNotificationBarClearAllButton()->RequestFocus(); + } + void RelayoutMessageCenterViewForTest() { // Outside of tests, any changes to bubble's size as well as scrolling // through notification list will trigger TrayBubbleView's BoxLayout to @@ -725,6 +733,23 @@ GetNotificationBarClearAllButton()->height()); } +// Tests that the "Clear all" button is not focusable when it is disabled. +TEST_P(NotificationCenterViewTest, ClearAllNotFocusableWhenDisabled) { + // Add a pinned notification and toggle the bubble. + test_api()->AddPinnedNotification(); + test_api()->ToggleBubble(); + + // Verify that the "Clear all" button is visible but disabled. + ASSERT_TRUE(GetNotificationBarClearAllButton()->GetVisible()); + ASSERT_FALSE(GetNotificationBarClearAllButton()->GetEnabled()); + + // Attempt to focus the "Clear all" button. + FocusClearAllButton(); + + // Verify that the "Clear all" button did not receive focus. + EXPECT_FALSE(GetNotificationBarClearAllButton()->HasFocus()); +} + TEST_P(NotificationCenterViewTest, StackedNotificationCount) { // There should not be any stacked notifications in the expanded message // center with just one notification added.
diff --git a/ash/system/notification_center/stacked_notification_bar.cc b/ash/system/notification_center/stacked_notification_bar.cc index 2037012..a75e5d3f 100644 --- a/ash/system/notification_center/stacked_notification_bar.cc +++ b/ash/system/notification_center/stacked_notification_bar.cc
@@ -328,9 +328,7 @@ unpinned_count); clear_all_button_->SetTooltipText(tooltip); clear_all_button_->SetAccessibleName(tooltip); - clear_all_button_->SetState(unpinned_count == 0 - ? views::Button::STATE_DISABLED - : views::Button::STATE_NORMAL); + clear_all_button_->SetEnabled(unpinned_count != 0); return true; }
diff --git a/ash/system/unified/quick_settings_view.cc b/ash/system/unified/quick_settings_view.cc index f0dabf6d..585f808 100644 --- a/ash/system/unified/quick_settings_view.cc +++ b/ash/system/unified/quick_settings_view.cc
@@ -6,7 +6,6 @@ #include <numeric> -#include "ash/constants/ash_features.h" #include "ash/system/media/unified_media_controls_container.h" #include "ash/system/tray/interacted_by_tap_recorder.h" #include "ash/system/tray/tray_constants.h" @@ -25,16 +24,21 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/compositor/layer.h" +#include "ui/gfx/geometry/insets.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/flex_layout_view.h" +#include "ui/views/view_class_properties.h" namespace ash { namespace { +constexpr auto kPageIndicatorMargin = gfx::Insets::TLBR(0, 0, 8, 0); +constexpr auto kSlidersContainerMargin = gfx::Insets::TLBR(4, 0, 0, 0); + class DetailedViewContainer : public views::View { public: METADATA_HEADER(DetailedViewContainer); @@ -144,6 +148,7 @@ controller_, /*initially_expanded=*/controller_->model() ->pagination_model() ->total_pages() > 1)); + page_indicator_view_->SetProperty(views::kMarginsKey, kPageIndicatorMargin); if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) { media_controls_container_ = system_tray_container_->AddChildView( @@ -154,6 +159,7 @@ sliders_container_ = system_tray_container_->AddChildView( std::make_unique<views::FlexLayoutView>()); sliders_container_->SetOrientation(views::LayoutOrientation::kVertical); + sliders_container_->SetProperty(views::kMarginsKey, kSlidersContainerMargin); footer_ = system_tray_container_->AddChildView( std::make_unique<QuickSettingsFooter>(controller_));
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc index 4dd8fbb..ddeb579 100644 --- a/ash/wm/default_state.cc +++ b/ash/wm/default_state.cc
@@ -660,20 +660,19 @@ if (window_state->IsMinimized()) return; - if (IsMinimizedWindowStateType(previous_state_type) || - window_state->IsFullscreen() || window_state->IsPinned() || - window_state->bounds_animation_type() == - WindowState::BoundsChangeAnimationType::kNone) { + if (bool to_float = state_type_ == WindowStateType::kFloated; + to_float || previous_state_type == WindowStateType::kFloated) { + // Float and unfloat have their own animation. + window_state->SetBoundsDirectCrossFade(bounds_in_parent, to_float); + } else if (IsMinimizedWindowStateType(previous_state_type) || + window_state->IsFullscreen() || window_state->IsPinned() || + window_state->bounds_animation_type() == + WindowState::BoundsChangeAnimationType::kNone) { window_state->SetBoundsDirect(bounds_in_parent); } else if (window_state->IsMaximized() || IsMaximizedOrFullscreenOrPinnedWindowStateType( previous_state_type)) { window_state->SetBoundsDirectCrossFade(bounds_in_parent); - } else if (window_state->IsFloated() && - previous_state_type == WindowStateType::kFloated) { - // This can happen during the tablet -> clamshell transition. Use cross fade - // animation for better performance. - window_state->SetBoundsDirectCrossFade(bounds_in_parent); } else if (window_state->is_dragged()) { // SetBoundsDirectAnimated does not work when the window gets reparented. // TODO(oshima): Consider fixing it and re-enable the animation.
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc index 008b9e2..11428f9 100644 --- a/ash/wm/desks/templates/saved_desk_unittest.cc +++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -403,13 +403,6 @@ ->set_disable_app_id_check_for_saved_desks(disabled); } - SkBitmap GetFocusRingBitmap(views::FocusRing* focus_ring) { - gfx::Canvas canvas(focus_ring->size(), /*image_scale=*/1.0f, - /*is_opaque=*/false); - focus_ring->OnPaint(&canvas); - return canvas.GetBitmap(); - } - SkBitmap GetBitmapWithInnerRoundedRect(gfx::Size size, int stroke_width, SkColor color) { @@ -958,7 +951,7 @@ } // Tests that the color of save desk button focus ring is as expected. -TEST_F(SavedDeskTest, SaveDeskButtonFocusRingColor) { +TEST_F(SavedDeskTest, SaveDeskButtonHighlight) { // Create a test window in the current desk. auto test_window = CreateAppWindow(); @@ -968,45 +961,24 @@ GetSaveDeskAsTemplateButtonForRoot(root_window); auto* save_for_later_button = GetSaveDeskForLaterButtonForRoot(root_window); - // Verify the focus ring of the given button is as expected. - auto verify_button_focus_ring_color = [this](SavedDeskSaveDeskButton* button, - bool highlighted) { - EXPECT_EQ(cc::ExactPixelComparator().Compare( - GetFocusRingBitmap(views::FocusRing::Get(button)), - GetBitmapWithInnerRoundedRect( - views::FocusRing::Get(button)->size(), - /*stroke_width=*/2, - ColorProvider::Get()->GetControlsLayerColor( - ColorProvider::ControlsLayerType::kFocusRingColor))), - highlighted); - }; - // Both buttons are not highlighted. ASSERT_FALSE(save_as_template_button->IsViewHighlighted()); ASSERT_FALSE(save_for_later_button->IsViewHighlighted()); - verify_button_focus_ring_color(save_as_template_button, false); - verify_button_focus_ring_color(save_for_later_button, false); // Reverse tab, then save desk for later button is highlighted. SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); ASSERT_FALSE(save_as_template_button->IsViewHighlighted()); ASSERT_TRUE(save_for_later_button->IsViewHighlighted()); - verify_button_focus_ring_color(save_as_template_button, false); - verify_button_focus_ring_color(save_for_later_button, true); // Reverse tab, then save desk as template button is highlighted. SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); ASSERT_TRUE(save_as_template_button->IsViewHighlighted()); ASSERT_FALSE(save_for_later_button->IsViewHighlighted()); - verify_button_focus_ring_color(save_as_template_button, true); - verify_button_focus_ring_color(save_for_later_button, false); // Reverse tab, then both buttons are not highlighted. SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); ASSERT_FALSE(save_as_template_button->IsViewHighlighted()); ASSERT_FALSE(save_for_later_button->IsViewHighlighted()); - verify_button_focus_ring_color(save_as_template_button, false); - verify_button_focus_ring_color(save_for_later_button, false); } // Tests that the save desk as template button and save for later button are
diff --git a/ash/wm/multitask_menu_nudge_controller_unittest.cc b/ash/wm/multitask_menu_nudge_controller_unittest.cc index aa3d128..8343523 100644 --- a/ash/wm/multitask_menu_nudge_controller_unittest.cc +++ b/ash/wm/multitask_menu_nudge_controller_unittest.cc
@@ -13,6 +13,8 @@ #include "ash/wm/splitview/split_view_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" #include "ash/wm/tablet_mode/tablet_mode_multitask_cue.h" +#include "ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h" +#include "ash/wm/tablet_mode/tablet_mode_window_manager.h" #include "ash/wm/window_state.h" #include "ash/wm/wm_event.h" #include "base/test/scoped_feature_list.h" @@ -30,48 +32,76 @@ namespace ash { +namespace { + +// Returns the nudge controller associated with `window`. +chromeos::MultitaskMenuNudgeController* GetNudgeControllerForWindow( + aura::Window* window) { + if (Shell::Get()->tablet_mode_controller()->InTabletMode()) { + return TabletModeControllerTestApi() + .tablet_mode_window_manager() + ->tablet_mode_multitask_menu_event_handler() + ->multitask_cue_for_testing() + ->nudge_controller_for_testing(); + } + + if (auto* frame = NonClientFrameViewAsh::Get(window)) { + return chromeos::FrameCaptionButtonContainerView::TestApi( + frame->GetHeaderView()->caption_button_container()) + .nudge_controller(); + } + + return nullptr; +} + +} // namespace + class MultitaskMenuNudgeControllerTest : public AshTestBase { public: - MultitaskMenuNudgeControllerTest() = default; + MultitaskMenuNudgeControllerTest() + : scoped_feature_list_(chromeos::wm::features::kWindowLayoutMenu) {} MultitaskMenuNudgeControllerTest(const MultitaskMenuNudgeControllerTest&) = delete; MultitaskMenuNudgeControllerTest& operator=( const MultitaskMenuNudgeControllerTest&) = delete; ~MultitaskMenuNudgeControllerTest() override = default; - views::Widget* GetWidget() { return controller_->nudge_widget_.get(); } + views::Widget* GetNudgeWidgetForWindow(aura::Window* window) { + chromeos::MultitaskMenuNudgeController* controller = + GetNudgeControllerForWindow(window); + return controller ? controller->nudge_widget_.get() : nullptr; + } - void FireDismissNudgeTimer() { - controller_->clamshell_nudge_dismiss_timer_.FireNow(); + void FireDismissNudgeTimer(aura::Window* window) { + if (chromeos::MultitaskMenuNudgeController* controller = + GetNudgeControllerForWindow(window)) { + controller->clamshell_nudge_dismiss_timer_.FireNow(); + } } // AshTestBase: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - chromeos::wm::features::kWindowLayoutMenu); - AshTestBase::SetUp(); chromeos::MultitaskMenuNudgeController::SetSuppressNudgeForTesting(false); - controller_ = Shell::Get()->multitask_menu_nudge_controller(); - controller_->SetOverrideClockForTesting(&test_clock_); + chromeos::MultitaskMenuNudgeController::SetOverrideClockForTesting( + &test_clock_); // Advance the test clock so we aren't at zero time. test_clock_.Advance(base::Hours(50)); } void TearDown() override { - controller_->SetOverrideClockForTesting(nullptr); + chromeos::MultitaskMenuNudgeController::SetOverrideClockForTesting(nullptr); AshTestBase::TearDown(); } protected: - base::SimpleTestClock test_clock_; - // Tests that the tablet mode nudge bounds in screen are correct. void ExpectCorrectTabletNudgeBounds(aura::Window* window) { - const gfx::Size size = GetWidget()->GetContentsView()->GetPreferredSize(); + const gfx::Size size = + GetNudgeWidgetForWindow(window)->GetContentsView()->GetPreferredSize(); const auto window_screen_bounds = window->GetBoundsInScreen(); const int tablet_nudge_y_offset = MultitaskMenuNudgeDelegateAsh::kTabletNudgeAdditionalYOffset + @@ -82,12 +112,13 @@ window_screen_bounds.x(), tablet_nudge_y_offset + window_screen_bounds.y(), size.width(), size.height()); - EXPECT_EQ(expected_bounds, GetWidget()->GetWindowBoundsInScreen()); + EXPECT_EQ(expected_bounds, + GetNudgeWidgetForWindow(window)->GetWindowBoundsInScreen()); } - private: - chromeos::MultitaskMenuNudgeController* controller_; + base::SimpleTestClock test_clock_; + private: base::test::ScopedFeatureList scoped_feature_list_; }; @@ -95,7 +126,7 @@ // test for https://crbug.com/1341142. TEST_F(MultitaskMenuNudgeControllerTest, NoCrashAfterFullscreening) { auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); // Turn of animations for immersive mode, so we don't have to wait for the top // container to hide on fullscreen. @@ -106,7 +137,7 @@ const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN); WindowState::Get(window.get())->OnWMEvent(&event); - EXPECT_FALSE(GetWidget()); + EXPECT_FALSE(GetNudgeWidgetForWindow(window.get())); // Window needs to be immersive enabled, but not revealed for the bug to // reproduce. @@ -114,7 +145,7 @@ ASSERT_FALSE(immersive_controller->IsRevealed()); WindowState::Get(window.get())->OnWMEvent(&event); - EXPECT_FALSE(GetWidget()); + EXPECT_FALSE(GetNudgeWidgetForWindow(window.get())); } // Tests that there is no crash after floating a window via the multitask menu. @@ -122,7 +153,7 @@ TEST_F(MultitaskMenuNudgeControllerTest, NoCrashAfterFloatingFromMultitaskMenu) { auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); // Float the window from the multitask menu. Floating the window using the // accelerator does not cause the crash mentioned in the bug because the @@ -150,25 +181,15 @@ .CenterPoint()); GetEventGenerator()->ClickLeftButton(); ASSERT_TRUE(WindowState::Get(window.get())->IsFloated()); - EXPECT_TRUE(GetWidget()); + EXPECT_TRUE(GetNudgeWidgetForWindow(window.get())); } TEST_F(MultitaskMenuNudgeControllerTest, NudgeTimeout) { auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); - FireDismissNudgeTimer(); - EXPECT_FALSE(GetWidget()); -} - -// Tests that if a window gets destroyed while the nduge is showing, the nudge -// disappears and there is no crash. -TEST_F(MultitaskMenuNudgeControllerTest, WindowDestroyedWhileNudgeShown) { - auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); - - window.reset(); - EXPECT_FALSE(GetWidget()); + FireDismissNudgeTimer(window.get()); + EXPECT_FALSE(GetNudgeWidgetForWindow(window.get())); } TEST_F(MultitaskMenuNudgeControllerTest, NudgeMultiDisplay) { @@ -176,7 +197,7 @@ ASSERT_EQ(2u, Shell::GetAllRootWindows().size()); auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); // Drag from the caption the window to the other display. The nudge should be // on the other display, even though the window is not (the window stays @@ -186,55 +207,58 @@ event_generator->set_current_screen_location(gfx::Point(150, 10)); event_generator->PressLeftButton(); event_generator->MoveMouseTo(gfx::Point(900, 0)); - EXPECT_EQ(Shell::GetAllRootWindows()[1], - GetWidget()->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ(Shell::GetAllRootWindows()[1], GetNudgeWidgetForWindow(window.get()) + ->GetNativeWindow() + ->GetRootWindow()); event_generator->ReleaseLeftButton(); - EXPECT_EQ(Shell::GetAllRootWindows()[1], - GetWidget()->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ(Shell::GetAllRootWindows()[1], GetNudgeWidgetForWindow(window.get()) + ->GetNativeWindow() + ->GetRootWindow()); display_move_window_util::HandleMoveActiveWindowBetweenDisplays(); - EXPECT_EQ(Shell::GetAllRootWindows()[0], - GetWidget()->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ(Shell::GetAllRootWindows()[0], GetNudgeWidgetForWindow(window.get()) + ->GetNativeWindow() + ->GetRootWindow()); } // Tests that based on preferences (shown count, and last shown time), the nudge // may or may not be shown. TEST_F(MultitaskMenuNudgeControllerTest, NudgePreferences) { auto window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); - FireDismissNudgeTimer(); - ASSERT_FALSE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); + FireDismissNudgeTimer(window.get()); + ASSERT_FALSE(GetNudgeWidgetForWindow(window.get())); // Create the window. This does not show the nudge as 24 hours have not // elapsed since the nudge was shown. window.reset(); window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_FALSE(GetWidget()); + ASSERT_FALSE(GetNudgeWidgetForWindow(window.get())); // Create the window again after waiting 25 hours. The nudge should now show // for the second time. test_clock_.Advance(base::Hours(25)); window.reset(); window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); - FireDismissNudgeTimer(); - ASSERT_FALSE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); + FireDismissNudgeTimer(window.get()); + ASSERT_FALSE(GetNudgeWidgetForWindow(window.get())); // Show the nudge for a third time. This will be the last time it is shown. test_clock_.Advance(base::Hours(25)); window.reset(); window = CreateAppWindow(gfx::Rect(300, 300)); - ASSERT_TRUE(GetWidget()); - FireDismissNudgeTimer(); - ASSERT_FALSE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); + FireDismissNudgeTimer(window.get()); + ASSERT_FALSE(GetNudgeWidgetForWindow(window.get())); // Advance the clock and attempt to show the nudge for a forth time. Verify // that it will not show. test_clock_.Advance(base::Hours(25)); window.reset(); window = CreateAppWindow(gfx::Rect(300, 300)); - EXPECT_FALSE(GetWidget()); + EXPECT_FALSE(GetNudgeWidgetForWindow(window.get())); } // Tests that the nudge works in tablet mode, and that its bounds in screen are @@ -244,7 +268,7 @@ // The widget should appear the first time a window is activated. auto window = CreateAppWindow(); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); // Test that the widget is shown at the correct bounds when the window is // first created. @@ -257,15 +281,27 @@ // snapped in the primary position. split_view_controller->SnapWindow( window.get(), SplitViewController::SnapPosition::kPrimary); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); ExpectCorrectTabletNudgeBounds(window.get()); // Tests that the widget is shown at the correct bounds when the window is // snapped in the secondary position. split_view_controller->SnapWindow( window.get(), SplitViewController::SnapPosition::kSecondary); - ASSERT_TRUE(GetWidget()); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); ExpectCorrectTabletNudgeBounds(window.get()); } +// Tests that if a window gets destroyed while the nduge is showing in tablet +// mode, the nudge disappears and there is no crash. +TEST_F(MultitaskMenuNudgeControllerTest, TabletWindowDestroyedWhileNudgeShown) { + TabletModeControllerTestApi().EnterTabletMode(); + + auto window = CreateAppWindow(gfx::Rect(300, 300)); + ASSERT_TRUE(GetNudgeWidgetForWindow(window.get())); + + window.reset(); + EXPECT_FALSE(GetNudgeWidgetForWindow(window.get())); +} + } // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc b/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc index 84aae73d..7a4b3d5 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc +++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue.cc
@@ -8,7 +8,6 @@ #include "ash/shell.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" -#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" #include "chromeos/ui/wm/features.h" #include "ui/aura/client/aura_constants.h" #include "ui/views/animation/animation_builder.h" @@ -60,7 +59,6 @@ // dismissed before it can be shown again. If the user activates a floatable // or non-maximizable window, any existing cue should still be dismissed. DismissCue(); - Shell::Get()->multitask_menu_nudge_controller()->DismissNudge(); // Floated windows do not have the multitask menu. // TODO(hewer): Consolidate checks with ones for multitask menu in a helper. @@ -101,8 +99,7 @@ &TabletModeMultitaskCue::OnTimerFinished); // Show the education nudge a maximum of three times with 24h in between. - DCHECK(Shell::Get()->multitask_menu_nudge_controller()); - Shell::Get()->multitask_menu_nudge_controller()->MaybeShowNudge(window_); + nudge_controller_.MaybeShowNudge(window_); } void TabletModeMultitaskCue::DismissCue() { @@ -117,8 +114,7 @@ cue_layer_.reset(); // The education nudge should not appear without the cue. - DCHECK(Shell::Get()->multitask_menu_nudge_controller()); - Shell::Get()->multitask_menu_nudge_controller()->DismissNudge(); + nudge_controller_.DismissNudge(); } void TabletModeMultitaskCue::OnWindowDestroying(aura::Window* window) {
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue.h b/ash/wm/tablet_mode/tablet_mode_multitask_cue.h index 539722e..e2cd45c 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_cue.h +++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue.h
@@ -11,6 +11,7 @@ #include "ash/wm/window_state_observer.h" #include "base/scoped_observation.h" #include "base/timer/timer.h" +#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" #include "ui/aura/window_observer.h" #include "ui/compositor/layer.h" #include "ui/wm/public/activation_change_observer.h" @@ -60,11 +61,12 @@ void OnPostWindowStateTypeChange(WindowState* window_state, chromeos::WindowStateType old_type) override; + chromeos::MultitaskMenuNudgeController* nudge_controller_for_testing() { + return &nudge_controller_; + } void FireCueDismissTimerForTesting() { cue_dismiss_timer_.FireNow(); } private: - friend class TabletModeMultitaskCueTest; - // Updates the bounds of the cue relative to the window if the window is // still available. void UpdateCueBounds(); @@ -76,6 +78,9 @@ // The app window that the cue is associated with. aura::Window* window_ = nullptr; + // Handles showing the educational nudge for the tablet multitask menu. + chromeos::MultitaskMenuNudgeController nudge_controller_; + // The solid color layer that represents the cue. std::unique_ptr<ui::Layer> cue_layer_; @@ -89,4 +94,4 @@ } // namespace ash -#endif // ASH_WM_TABLET_MODE_TABLET_MODE_MULTITASK_CUE_H_ \ No newline at end of file +#endif // ASH_WM_TABLET_MODE_TABLET_MODE_MULTITASK_CUE_H_
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc b/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc index 64d114f..a9dc39f1 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_multitask_cue_unittest.cc
@@ -36,7 +36,7 @@ TabletModeMultitaskCue* GetMultitaskCue() { return TabletModeControllerTestApi() .tablet_mode_window_manager() - ->tablet_mode_multitask_menu_event_handler_for_testing() + ->tablet_mode_multitask_menu_event_handler() ->multitask_cue_for_testing(); }
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc index d15e626..b822bb0 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc +++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
@@ -36,60 +36,16 @@ Shell::Get()->RemovePreTargetHandler(this); } -void TabletModeMultitaskMenuEventHandler::MaybeCreateMultitaskMenu( - aura::Window* active_window) { - if (!multitask_menu_) { - multitask_menu_ = - std::make_unique<TabletModeMultitaskMenu>(this, active_window); - - multitask_cue_->DismissCue(); - } +void TabletModeMultitaskMenuEventHandler::ShowMultitaskMenu( + aura::Window* window) { + MaybeCreateMultitaskMenu(window); + multitask_menu_->Animate(/*show=*/true); } void TabletModeMultitaskMenuEventHandler::ResetMultitaskMenu() { multitask_menu_.reset(); } -void TabletModeMultitaskMenuEventHandler::OnMouseEvent(ui::MouseEvent* event) { - if (event->type() != ui::ET_MOUSEWHEEL) - return; - - // Note that connecting a mouse normally puts the device in clamshell mode - // unless a developer switch is enabled. - if (!debug::DeveloperAcceleratorsEnabled()) - return; - - const float y_offset = event->AsMouseWheelEvent()->y_offset(); - if (y_offset == 0.f) - return; - - aura::Window* target = static_cast<aura::Window*>(event->target()); - - // Close the multitask menu if it is the target and we have a upwards scroll. - if (y_offset > 0.f && multitask_menu_ && - target == multitask_menu_->widget()->GetNativeWindow()) { - multitask_menu_->Animate(/*show=*/false); - return; - } - - if (multitask_menu_) - return; - - aura::Window* active_window = window_util::GetActiveWindow(); - if (!active_window || !active_window->Contains(target) || - !WindowState::Get(active_window)->CanMaximize()) { - return; - } - - // Show the multitask menu if it is in the top quarter of the target and is a - // downwards scroll. - if (y_offset < 0.f && - event->location_f().y() < target->bounds().height() / 4.f) { - MaybeCreateMultitaskMenu(active_window); - multitask_menu_->Animate(/*show=*/true); - } -} - void TabletModeMultitaskMenuEventHandler::OnTouchEvent(ui::TouchEvent* event) { // The event target may be the active window, multitask menu, or multitask // cue, so convert to screen coordinates for consistency. @@ -206,4 +162,13 @@ return !window_state->IsFloated() && window_state->CanMaximize(); } +void TabletModeMultitaskMenuEventHandler::MaybeCreateMultitaskMenu( + aura::Window* active_window) { + if (!multitask_menu_) { + multitask_menu_ = + std::make_unique<TabletModeMultitaskMenu>(this, active_window); + multitask_cue_->DismissCue(); + } +} + } // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h index e1a22be..5223121 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h +++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h
@@ -26,15 +26,13 @@ const TabletModeMultitaskMenuEventHandler&) = delete; ~TabletModeMultitaskMenuEventHandler() override; - void MaybeCreateMultitaskMenu(aura::Window* active_window); + // Creates and shows the menu. + void ShowMultitaskMenu(aura::Window* window); // Destroys the multitask menu. void ResetMultitaskMenu(); // ui::EventHandler: - // TODO(crbug.com/1336836): Temporarily allow mouse wheel events to show or - // hide the multitask menu for developers. Remove this before launch. - void OnMouseEvent(ui::MouseEvent* event) override; void OnTouchEvent(ui::TouchEvent* event) override; TabletModeMultitaskMenu* multitask_menu_for_testing() { @@ -55,6 +53,8 @@ bool CanProcessEvent(aura::Window* window) const; + void MaybeCreateMultitaskMenu(aura::Window* active_window); + // Valid if we may need to handle the event. absl::optional<InitialDragData> initial_drag_data_;
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler_unittest.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler_unittest.cc index 7624ecc9..43d8532fa 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler_unittest.cc
@@ -94,13 +94,13 @@ TabletModeMultitaskMenuEventHandler* GetMultitaskMenuEventHandler() { return TabletModeControllerTestApi() .tablet_mode_window_manager() - ->tablet_mode_multitask_menu_event_handler_for_testing(); + ->tablet_mode_multitask_menu_event_handler(); } TabletModeMultitaskMenu* GetMultitaskMenu() { return TabletModeControllerTestApi() .tablet_mode_window_manager() - ->tablet_mode_multitask_menu_event_handler_for_testing() + ->tablet_mode_multitask_menu_event_handler() ->multitask_menu_for_testing(); } @@ -314,10 +314,9 @@ ShowMultitaskMenu(*window); - auto* event_handler = - TabletModeControllerTestApi() - .tablet_mode_window_manager() - ->tablet_mode_multitask_menu_event_handler_for_testing(); + auto* event_handler = TabletModeControllerTestApi() + .tablet_mode_window_manager() + ->tablet_mode_multitask_menu_event_handler(); auto* multitask_menu = event_handler->multitask_menu_for_testing(); ASSERT_TRUE(multitask_menu); ASSERT_TRUE(multitask_menu->widget()->GetContentsView()->GetVisible());
diff --git a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc index 36ebc06a..20539cb 100644 --- a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
@@ -65,11 +65,10 @@ // Swiping down on the center reveals the tablet mode multitask menu. Ensure // our swipes do not reveal it, as it may eat following gestures. - auto* multitask_menu = - TabletModeControllerTestApi() - .tablet_mode_window_manager() - ->tablet_mode_multitask_menu_event_handler_for_testing() - ->multitask_menu_for_testing(); + auto* multitask_menu = TabletModeControllerTestApi() + .tablet_mode_window_manager() + ->tablet_mode_multitask_menu_event_handler() + ->multitask_menu_for_testing(); ASSERT_FALSE(multitask_menu); }
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h index 9dd0692..98c8e815 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_manager.h +++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -53,6 +53,11 @@ ~TabletModeWindowManager() override; + TabletModeMultitaskMenuEventHandler* + tablet_mode_multitask_menu_event_handler() { + return tablet_mode_multitask_menu_event_handler_.get(); + } + void Init(); // Stops tracking windows and returns them to their clamshell mode state. Work @@ -107,11 +112,6 @@ // SessionObserver: void OnActiveUserSessionChanged(const AccountId& account_id) override; - TabletModeMultitaskMenuEventHandler* - tablet_mode_multitask_menu_event_handler_for_testing() { - return tablet_mode_multitask_menu_event_handler_.get(); - } - private: using WindowToState = std::map<aura::Window*, TabletModeWindowState*>; using WindowAndStateTypeList =
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc index 3dfd105..85fae3bb 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_state.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -304,6 +304,9 @@ return; } + const chromeos::WindowStateType previous_state_type = + window_state->GetStateType(); + switch (event->type()) { case WM_EVENT_TOGGLE_FULLSCREEN: ToggleFullScreen(window_state, window_state->delegate()); @@ -388,15 +391,19 @@ if (bounds_in_parent.IsEmpty()) return; - if (current_state_type_ == WindowStateType::kFloated || - window_util::IsDraggingTabs(window_state->window()) || - IsTabDraggingSourceWindow(window_state->window()) || - TabDragDropDelegate::IsSourceWindowForDrag(window_state->window()) || - BoundsChangeIsFromVKAndAllowed(window_state->window())) { + if (bool to_float = current_state_type_ == WindowStateType::kFloated; + to_float || previous_state_type == WindowStateType::kFloated) { // Floated windows in tablet mode are freeform, so they can placed - // anywhere, not just centered. Also, if the window is the current - // tab-dragged window or the current tab- dragged window's source - // window, we may need to update its bounds during dragging. + // anywhere, not just centered. + window_state->SetBoundsDirectCrossFade(bounds_in_parent, to_float); + } else if (window_util::IsDraggingTabs(window_state->window()) || + IsTabDraggingSourceWindow(window_state->window()) || + TabDragDropDelegate::IsSourceWindowForDrag( + window_state->window()) || + BoundsChangeIsFromVKAndAllowed(window_state->window())) { + // If the window is the current tab-dragged window or the current tab- + // dragged window's source window, we may need to update its bounds + // during dragging. window_state->SetBoundsDirect(bounds_in_parent); } else if (current_state_type_ == WindowStateType::kMaximized) { // Having a maximized window, it could have been created with an empty
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc index e98b1125..8f5feea 100644 --- a/ash/wm/window_animations.cc +++ b/ash/wm/window_animations.cc
@@ -55,6 +55,10 @@ constexpr base::TimeDelta kCrossFadeMaxDuration = base::Milliseconds(400); +// The default duration for an animation to float or unfloat a window. +static constexpr base::TimeDelta kFloatUnfloatDuration = + base::Milliseconds(400); + // Durations for the brightness/grayscale fade animation, in milliseconds. const int kBrightnessGrayscaleFadeDurationMs = 1000; @@ -628,11 +632,23 @@ void CrossFadeAnimation(aura::Window* window, std::unique_ptr<ui::LayerTreeOwner> old_layer_owner) { CrossFadeAnimationInternal( - window, std::move(old_layer_owner), /*animate_old_layer=*/true, + window, std::move(old_layer_owner), /*animate_old_layer_transform=*/true, /*duration=*/absl::nullopt, /*tween_type=*/absl::nullopt, /*histogram_name=*/absl::nullopt); } +void CrossFadeAnimationForFloatUnfloat( + aura::Window* window, + std::unique_ptr<ui::LayerTreeOwner> old_layer_owner, + bool to_float) { + CrossFadeAnimationInternal(window, std::move(old_layer_owner), + /*animate_old_layer_transform=*/true, + kFloatUnfloatDuration, + to_float ? gfx::Tween::Type::ACCEL_30_DECEL_20_85 + : gfx::Tween::Type::FAST_OUT_SLOW_IN_3, + /*histogram_name=*/absl::nullopt); +} + void CrossFadeAnimationAnimateNewLayerOnly(aura::Window* window, const gfx::Rect& target_bounds, base::TimeDelta duration,
diff --git a/ash/wm/window_animations.h b/ash/wm/window_animations.h index 05a56773..10dd9ce7 100644 --- a/ash/wm/window_animations.h +++ b/ash/wm/window_animations.h
@@ -36,12 +36,19 @@ LayerScaleAnimationDirection type); // Implementation of cross fading. Window is the window being cross faded. It -// should be at the target bounds. |old_layer_owner| contains the previous layer -// from |window|. +// should be at the target bounds. `old_layer_owner` contains the previous layer +// from `window`. ASH_EXPORT void CrossFadeAnimation( aura::Window* window, std::unique_ptr<ui::LayerTreeOwner> old_layer_owner); +// Implementation of cross fading for floating/unfloating a window. If +// `to_float` is true, animates to floated state, else animates unfloat. +ASH_EXPORT void CrossFadeAnimationForFloatUnfloat( + aura::Window* window, + std::unique_ptr<ui::LayerTreeOwner> old_layer_owner, + bool to_float); + // Implementation of cross fading which only animates the new layer. The old // layer will be owned by an observer which will update the transform as the new // layer's transform and bounds change. This is used by the
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc index 19b8515..052b363 100644 --- a/ash/wm/window_state.cc +++ b/ash/wm/window_state.cc
@@ -1037,7 +1037,8 @@ SetBoundsDirect(bounds); } -void WindowState::SetBoundsDirectCrossFade(const gfx::Rect& new_bounds) { +void WindowState::SetBoundsDirectCrossFade(const gfx::Rect& new_bounds, + absl::optional<bool> float_state) { // Some test results in invoking CrossFadeToBounds when window is not visible. // No animation is necessary in that case, thus just change the bounds and // quit. @@ -1065,6 +1066,12 @@ // Resize the window to the new size, which will force a layout and paint. SetBoundsDirect(new_bounds); + if (float_state) { + CrossFadeAnimationForFloatUnfloat(window_, std::move(old_layer_owner), + *float_state); + return; + } + CrossFadeAnimation(window_, std::move(old_layer_owner)); }
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h index 8d22f89..63b276f2 100644 --- a/ash/wm/window_state.h +++ b/ash/wm/window_state.h
@@ -536,9 +536,12 @@ base::TimeDelta duration = kBoundsChangeSlideDuration, gfx::Tween::Type animation_type = gfx::Tween::LINEAR); - // Sets the window's |bounds| and transition to the new bounds with - // a cross fade animation. - void SetBoundsDirectCrossFade(const gfx::Rect& bounds); + // Sets the window's `bounds` and transition to the new bounds with + // a cross fade animation. If `float_state` has a value, sets a custom + // float/unfloat cross fade animation. + void SetBoundsDirectCrossFade( + const gfx::Rect& bounds, + absl::optional<bool> float_state = absl::nullopt); // Called before the state change and update PIP related state, such as next // window animation type, upon state change.
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr.h b/base/allocator/partition_allocator/pointers/raw_ptr.h index 65445c70..ac926de5 100644 --- a/base/allocator/partition_allocator/pointers/raw_ptr.h +++ b/base/allocator/partition_allocator/pointers/raw_ptr.h
@@ -686,12 +686,13 @@ /*allow_dangling=*/Contains(Traits, RawPtrTraits::kMayDangle)>; #elif BUILDFLAG(USE_ASAN_UNOWNED_PTR) - using UnderlyingImpl = - std::conditional_t<Contains(Traits, RawPtrTraits::kMayDangle), - // No special bookkeeping required for this case, - // just treat these as ordinary pointers. - internal::RawPtrNoOpImpl, - internal::RawPtrAsanUnownedImpl>; + using UnderlyingImpl = std::conditional_t< + Contains(Traits, RawPtrTraits::kMayDangle), + // No special bookkeeping required for this case, + // just treat these as ordinary pointers. + internal::RawPtrNoOpImpl, + internal::RawPtrAsanUnownedImpl< + Contains(Traits, RawPtrTraits::kAllowPtrArithmetic)>>; #elif PA_CONFIG(ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) using UnderlyingImpl = std::conditional_t<Contains(Traits, RawPtrTraits::kDisableMTECheckedPtr),
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc index b372f8e..fb305931 100644 --- a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc +++ b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc
@@ -12,21 +12,34 @@ namespace base::internal { PA_NO_SANITIZE("address") -bool RawPtrAsanUnownedImpl::EndOfAliveAllocation(const volatile void* ptr) { +bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr) { uintptr_t address = reinterpret_cast<uintptr_t>(ptr); + // Normally, we probe the first byte of an object, but in cases of pointer + // arithmetic, we may be probing subsequent bytes, including the legal + // "end + 1" position. + // // Alas, ASAN will claim an unmapped page is unpoisoned, so willfully ignore // the fist address of a page, since "end + 1" of an object allocated exactly // up to a page boundary will SEGV on probe. This will cause false negatives // for pointers that happen to be page aligned, which is undesirable but // necessary for now. - // TODO(tsepez): investigate pointer tracking approaches to avoid this. - return ((address & 0x0fff) == 0 || + // + // We minimize the consequences by using the pointer arithmetic flag in + // higher levels to conditionalize this suppression. + // + // TODO(tsepez): this may still fail for a non-accessible but non-null + // return from, say, malloc(0) which happens to be page-aligned. + // + // TODO(tsepez): enforce the pointer arithmetic flag. Until then, we + // may fail here if a pointer requires the flag but is lacking it. + return is_adjustable_ptr && + ((address & 0x0fff) == 0 || __asan_region_is_poisoned(reinterpret_cast<void*>(address), 1)) && !__asan_region_is_poisoned(reinterpret_cast<void*>(address - 1), 1); } -bool RawPtrAsanUnownedImpl::LikelySmuggledScalar(const volatile void* ptr) { +bool LikelySmuggledScalar(const volatile void* ptr) { intptr_t address = reinterpret_cast<intptr_t>(ptr); return address < 0x4000; // Negative or small positive. }
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h index 4fe1884e..1fac1a9 100644 --- a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h +++ b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h
@@ -19,6 +19,10 @@ namespace base::internal { +bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr); +bool LikelySmuggledScalar(const volatile void* ptr); + +template <bool IsAdjustablePtr> struct RawPtrAsanUnownedImpl { // Wraps a pointer. template <typename T> @@ -91,14 +95,11 @@ template <typename T> static void ProbeForLowSeverityLifetimeIssue(T* wrapped_ptr) { if (wrapped_ptr && !LikelySmuggledScalar(wrapped_ptr) && - !EndOfAliveAllocation(wrapped_ptr)) { + !EndOfAliveAllocation(wrapped_ptr, IsAdjustablePtr)) { reinterpret_cast<const volatile uint8_t*>(wrapped_ptr)[0]; } } - static bool EndOfAliveAllocation(const volatile void* ptr); - static bool LikelySmuggledScalar(const volatile void* ptr); - // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. template <typename T>
diff --git a/base/allocator/partition_allocator/pointers/raw_ref.h b/base/allocator/partition_allocator/pointers/raw_ref.h index cdd5cf7..cb844a3b 100644 --- a/base/allocator/partition_allocator/pointers/raw_ref.h +++ b/base/allocator/partition_allocator/pointers/raw_ref.h
@@ -79,7 +79,8 @@ internal::MTECheckedPtrImplPartitionAllocSupport>> || #endif // PA_CONFIG(ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) #if BUILDFLAG(USE_ASAN_UNOWNED_PTR) - std::is_same_v<Impl, internal::RawPtrAsanUnownedImpl> || + std::is_same_v<Impl, internal::RawPtrAsanUnownedImpl<true>> || + std::is_same_v<Impl, internal::RawPtrAsanUnownedImpl<false>> || #endif // BUILDFLAG(USE_ASAN_UNOWNED_PTR) std::is_same_v<Impl, internal::RawPtrNoOpImpl>;
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc index e3cdc5d..2a46445 100644 --- a/base/metrics/sample_vector.cc +++ b/base/metrics/sample_vector.cc
@@ -276,6 +276,12 @@ if (sample.count == 0) return; + // Stop here if the sample bucket would be out of range for the AtomicCount + // array. + if (sample.bucket >= counts_size()) { + return; + } + // Move the value into storage. Sum and redundant-count already account // for this entry so no need to call IncreaseSumAndCount(). subtle::NoBarrier_AtomicIncrement(&counts()[sample.bucket], sample.count);
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index fc668a6a..9afb442 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -1432,22 +1432,10 @@ } config("rustc_revision") { - # Use the rustc version as an input to all rustc invovations if a custom - # toolchain is not being used (`rust_sysroot_absolute`). - if (toolchain_has_rust && rust_sysroot_absolute == "") { - if (use_chromium_rust_toolchain) { - update_rust_args = [ "--print-package-version" ] - rustc_revision = exec_script("//tools/rust/update_rust.py", - update_rust_args, - "trim string") - } else { - # Android toolchain version. - rustc_revision = - "rustc 1.64.0-dev (Android Rust Toolchain version 9099361)" - } - - # Similar to the above config, this is here so that all files get - # recompiled after a rustc roll. Nothing should ever read this cfg. + if (rustc_revision != "") { + # Similar to the above config, this is here so that all files get recompiled + # after a rustc roll. Nothing should ever read this cfg. This will not be + # set if a custom toolchain is used. rustflags = [ "--cfg", "cr_rustc_revision=\"$rustc_revision\"",
diff --git a/build/config/rust.gni b/build/config/rust.gni index cf62550..b3e501149 100644 --- a/build/config/rust.gni +++ b/build/config/rust.gni
@@ -94,6 +94,22 @@ (!use_chromium_rust_toolchain && android_toolchain_supports_platform) || (rust_sysroot_absolute != "" && custom_toolchain_supports_platform)) +# The rustc_revision is used to introduce a dependency on the toolchain version +# (so e.g. rust targets are rebuilt, and the standard library is re-copied when +# the toolchain changes). It is left empty for custom toolchains. +rustc_revision = "" +if (toolchain_has_rust && rust_sysroot_absolute == "") { + if (use_chromium_rust_toolchain) { + update_rust_args = [ "--print-package-version" ] + rustc_revision = exec_script("//tools/rust/update_rust.py", + update_rust_args, + "trim string") + } else { + # Android toolchain version. + rustc_revision = "rustc 1.64.0-dev (Android Rust Toolchain version 9099361)" + } +} + # TODO(crbug.com/1278030): To build unit tests for Android we need to build # them as a dylib and put them into an APK. We should reuse all the same logic # for gtests from the `//testing/test:test` template.
diff --git a/build/dotfile_settings.gni b/build/dotfile_settings.gni index 7caa778..94b4fb0 100644 --- a/build/dotfile_settings.gni +++ b/build/dotfile_settings.gni
@@ -23,6 +23,7 @@ "//build/config/mac/mac_sdk.gni", "//build/config/mac/rules.gni", "//build/config/posix/BUILD.gn", + "//build/config/rust.gni", "//build/config/sysroot.gni", "//build/config/win/BUILD.gn", "//build/config/win/visual_studio_version.gni",
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py index bdbd5c92..9b1349a86 100755 --- a/build/lacros/test_runner.py +++ b/build/lacros/test_runner.py
@@ -608,8 +608,6 @@ if enable_mojo_crosapi: forward_args.append(lacros_mojo_socket_arg) - forward_args.append("--ash-chrome-path=%s" % ash_chrome_file) - forward_args.append("--ash-user-data-dir=%s" % tmp_ash_data_dir_name) test_env = os.environ.copy() test_env['WAYLAND_DISPLAY'] = ash_wayland_socket_name test_env['EGL_PLATFORM'] = 'surfaceless'
diff --git a/build/lacros/test_runner_test.py b/build/lacros/test_runner_test.py index 9d5ea38..da79423 100755 --- a/build/lacros/test_runner_test.py +++ b/build/lacros/test_runner_test.py
@@ -80,10 +80,6 @@ @mock.patch.object(os.path, 'exists', return_value=True) @mock.patch.object(os.path, 'isfile', return_value=True) @mock.patch.object(os.path, 'abspath', return_value='/a/b/filter') - @mock.patch.object(os.path, 'dirname', return_value='/some/dir') - @mock.patch.object(test_runner, - '_GetAshChromeDirPath', - return_value='/some/dir') @mock.patch.object(test_runner, '_GetLatestVersionOfAshChrome', return_value='793554') @@ -101,7 +97,8 @@ self.assertEqual(2, mock_popen.call_count) ash_chrome_args = mock_popen.call_args_list[0][0][0] - self.assertTrue(ash_chrome_args[0].endswith('/some/dir/test_ash_chrome')) + self.assertTrue(ash_chrome_args[0].endswith( + 'build/lacros/prebuilt_ash_chrome/793554/test_ash_chrome')) expected_ash_chrome_args = [ '--user-data-dir=/tmp/ash-data', '--enable-wayland-server', @@ -128,8 +125,6 @@ command, '--test-launcher-filter-file=/a/b/filter', '--lacros-mojo-socket-for-testing=/tmp/ash-data/lacros.sock', - '--ash-chrome-path=/some/dir/test_ash_chrome', - '--ash-user-data-dir=/tmp/ash-data', ], test_args) else: self.assertListEqual(test_args[:len(command_parts)], command_parts)
diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn index 71a393a..b5fe564b 100644 --- a/build/rust/std/BUILD.gn +++ b/build/rust/std/BUILD.gn
@@ -123,6 +123,11 @@ # related to the Rust standard library, we ensure libstd.rlib is first. "--depfile-target", stdlib_files[0], + + # Create a dependency on the rustc version so this action is re-run when + # it changes. This argument is not actually read by the script. + "--rustc-revision", + rustc_revision, ] if (!use_unverified_rust_toolchain) {
diff --git a/build/rust/std/find_std_rlibs.py b/build/rust/std/find_std_rlibs.py index e15efb7..b91e631 100755 --- a/build/rust/std/find_std_rlibs.py +++ b/build/rust/std/find_std_rlibs.py
@@ -37,8 +37,9 @@ help="Expected list of standard library libraries") parser.add_argument("--extra-libs", help="List of extra non-libstd sysroot libraries") - parser.add_argument("--expected-rustc-version", - help="The string we expect to be reported by 'rustc -V'") + parser.add_argument("--rustc-revision", + help="Not used, just passed from GN to add a dependency" + " on the rustc version.") args = parser.parse_args() # Expected rlibs by concise name (the crate name, plus a disambiguating suffix @@ -61,19 +62,8 @@ for lib in args.extra_libs.split(','): extra_libs.add(lib) - # First, ask rustc to confirm it's the version expected. - rustc = os.path.join(args.rust_bin_dir, "rustc") - if args.expected_rustc_version: - proc = subprocess.run([rustc, "-V"], capture_output=True, text=True) - proc.check_returncode() - rustc_version = proc.stdout.rstrip() - if rustc_version != args.expected_rustc_version: - raise Exception("gn arguments state that the rustc_version is %s " - "but it was actually %s. Please adjust your " - "gn arguments to match." % - (args.expected_rustc_version, rustc_version)) - # Ask rustc where to find the stdlib for this target. + rustc = os.path.join(args.rust_bin_dir, "rustc") rustc_args = [rustc, "--print", "target-libdir"] if args.target: rustc_args.extend(["--target", args.target])
diff --git a/build/rust/std/gnrt_config.toml b/build/rust/std/gnrt_config.toml new file mode 100644 index 0000000..40fb850 --- /dev/null +++ b/build/rust/std/gnrt_config.toml
@@ -0,0 +1,31 @@ +# 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. + +[libc] +# Requires: +# * cfg(libc_align) for new enough rustc, which is normally provided by build.rs +# but we don't run build scripts for std crates. +# * cfg(libc_priv_mod_use) is required for the below to work properly. +# * cfg(libc_core_cvoid) to use the same ffi c_void definition as libcore. +# +# See https://github.com/rust-lang/libc/blob/master/build.rs +cfg = ['libc_align', 'libc_priv_mod_use', 'libc_core_cvoid'] + +[std] +# Requires: +# * cfg(backtrace_in_libstd) because it directly includes .rs files from the +# backtrace code rather than including it as a dependency. backtrace's +# implementation has special-purpose code to handle this. +# * STD_ENV_ARCH is referenced in architecture-dependent code. Note this is the +# target arch, and as such `$rust_target_arch` is passed literally to GN. This +# variable is set at build time in build/config/rust.gni +# +# See https://github.com/rust-lang/rust/blob/master/library/std/build.rs +cfg = ['backtrace_in_libstd'] +env = ['STD_ENV_ARCH=$rust_target_arch'] + +[test] +# Requires: +# * CFG_DISABLE_UNSTABLE_FEATURES=0 to match how it's built by x.py. +env = ['CFG_DISABLE_UNSTABLE_FEATURES=0']
diff --git a/build/rust/std/rules/BUILD.gn b/build/rust/std/rules/BUILD.gn index 26459d2..8bdb2ed 100644 --- a/build/rust/std/rules/BUILD.gn +++ b/build/rust/std/rules/BUILD.gn
@@ -711,6 +711,8 @@ ":proc_macro", ":std", ] + + rustenv = [ "CFG_DISABLE_UNSTABLE_FEATURES=0" ] } cargo_crate("unicode_width") { crate_type = "rlib"
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 48def44ab..e1762803 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -398,7 +398,6 @@ "trees/presentation_time_callback_buffer.h", "trees/property_animation_state.cc", "trees/property_animation_state.h", - "trees/property_ids.h", "trees/property_tree.cc", "trees/property_tree.h", "trees/property_tree_builder.cc", @@ -784,6 +783,7 @@ "resources/resource_pool_unittest.cc", "scheduler/scheduler_state_machine_unittest.cc", "scheduler/scheduler_unittest.cc", + "slim/slim_layer_tree_compositor_frame_unittest.cc", "slim/slim_layer_tree_unittest.cc", "slim/slim_layer_unittest.cc", "slim/test_frame_sink_impl.cc",
diff --git a/cc/base/rtree.h b/cc/base/rtree.h index cc94de2c..c91d226 100644 --- a/cc/base/rtree.h +++ b/cc/base/rtree.h
@@ -17,6 +17,7 @@ #include "base/check_op.h" #include "base/memory/raw_ptr.h" #include "base/numerics/clamped_math.h" +#include "base/trace_event/trace_event.h" #include "ui/gfx/geometry/rect.h" namespace cc { @@ -178,6 +179,7 @@ void RTree<T>::Build(const Container& items, const BoundsFunctor& bounds_getter, const PayloadFunctor& payload_getter) { + TRACE_EVENT1("cc", "RTree::Build", "size", items.size()); DCHECK_EQ(0u, num_data_elements_); std::vector<Branch<T>> branches; @@ -317,11 +319,14 @@ results->clear(); if (num_data_elements_ == 0) return; + + TRACE_EVENT_BEGIN1("cc", "RTree::Search", "size", num_data_elements_); if (!has_valid_bounds_) { SearchRecursiveFallback(root_.subtree.get(), query, results, rects); } else if (query.Intersects(root_.bounds)) { SearchRecursive(root_.subtree.get(), query, results, rects); } + TRACE_EVENT_END1("cc", "RTree::Search", "result_size", results->size()); } template <typename T>
diff --git a/cc/slim/frame_sink_impl.cc b/cc/slim/frame_sink_impl.cc index c83845b2..3efcf98 100644 --- a/cc/slim/frame_sink_impl.cc +++ b/cc/slim/frame_sink_impl.cc
@@ -85,7 +85,8 @@ base::BindOnce(&FrameSinkImpl::OnContextLost, base::Unretained(this))); client_receiver_.Bind(std::move(pending_client_receiver_), task_runner_); - frame_sink_remote_->InitializeCompositorFrameSinkType( + frame_sink_ = frame_sink_remote_.get(); + frame_sink_->InitializeCompositorFrameSinkType( viz::mojom::CompositorFrameSinkType::kLayerTree); #if BUILDFLAG(IS_ANDROID) @@ -94,7 +95,7 @@ if (io_thread_id_ != base::kInvalidThreadId) { thread_ids.push_back(io_thread_id_); } - frame_sink_remote_->SetThreadIds(thread_ids); + frame_sink_->SetThreadIds(thread_ids); #endif return true; } @@ -108,7 +109,7 @@ return; } needs_begin_frame_ = needs_begin_frame; - frame_sink_remote_->SetNeedsBeginFrame(needs_begin_frame); + frame_sink_->SetNeedsBeginFrame(needs_begin_frame); } void FrameSinkImpl::UploadUIResource(cc::UIResourceId resource_id, @@ -225,7 +226,7 @@ } if (!local_surface_id_.is_valid()) { - frame_sink_remote_->DidNotProduceFrame( + frame_sink_->DidNotProduceFrame( viz::BeginFrameAck(begin_frame_args, false)); return; } @@ -235,11 +236,19 @@ viz::HitTestRegionList hit_test_region_list; if (!client_->BeginFrame(begin_frame_args, frame, viz_resource_ids, hit_test_region_list)) { - frame_sink_remote_->DidNotProduceFrame( + frame_sink_->DidNotProduceFrame( viz::BeginFrameAck(begin_frame_args, false)); return; } + if (local_surface_id_ == last_submitted_local_surface_id_) { + DCHECK_EQ(last_submitted_device_scale_factor_, frame.device_scale_factor()); + DCHECK_EQ(last_submitted_size_in_pixels_.height(), + frame.size_in_pixels().height()); + DCHECK_EQ(last_submitted_size_in_pixels_.width(), + frame.size_in_pixels().width()); + } + resource_provider_.PrepareSendToParent(std::move(viz_resource_ids).extract(), &frame.resource_list, context_provider_.get()); @@ -254,7 +263,7 @@ { TRACE_EVENT0("cc", "SubmitCompositorFrame"); - frame_sink_remote_->SubmitCompositorFrame( + frame_sink_->SubmitCompositorFrame( local_surface_id_, std::move(frame), send_new_hit_test_region_list ? hit_test_region_list_ : absl::nullopt, 0);
diff --git a/cc/slim/frame_sink_impl.h b/cc/slim/frame_sink_impl.h index 61db3293..4d7c5ff 100644 --- a/cc/slim/frame_sink_impl.h +++ b/cc/slim/frame_sink_impl.h
@@ -122,6 +122,8 @@ pending_client_receiver_; mojo::AssociatedRemote<viz::mojom::CompositorFrameSink> frame_sink_remote_; + // Separate from AssociatedRemote above for testing. + viz::mojom::CompositorFrameSink* frame_sink_ = nullptr; mojo::Receiver<viz::mojom::CompositorFrameSinkClient> client_receiver_{this}; scoped_refptr<viz::ContextProvider> context_provider_; raw_ptr<FrameSinkImplClient> client_ = nullptr; @@ -133,6 +135,10 @@ absl::optional<viz::HitTestRegionList> hit_test_region_list_; base::PlatformThreadId io_thread_id_; + viz::LocalSurfaceId last_submitted_local_surface_id_; + float last_submitted_device_scale_factor_ = 1.f; + gfx::Size last_submitted_size_in_pixels_; + bool needs_begin_frame_ = false; };
diff --git a/cc/slim/layer.cc b/cc/slim/layer.cc index 4ccb227..e226867 100644 --- a/cc/slim/layer.cc +++ b/cc/slim/layer.cc
@@ -11,7 +11,6 @@ #include "base/atomic_sequence_num.h" #include "base/check.h" #include "base/containers/cxx20_erase_vector.h" -#include "base/feature_list.h" #include "base/ranges/algorithm.h" #include "cc/layers/layer.h" #include "cc/paint/filter_operation.h" @@ -20,7 +19,6 @@ #include "cc/slim/layer_tree.h" #include "cc/slim/layer_tree_impl.h" #include "components/viz/common/quads/shared_quad_state.h" -#include "components/viz/common/quads/solid_color_draw_quad.h" namespace cc::slim { @@ -58,7 +56,7 @@ Layer::Layer(scoped_refptr<cc::Layer> cc_layer) : cc_layer_(std::move(cc_layer)), - id_(g_next_id.GetNext()), + id_(g_next_id.GetNext() + 1), is_drawable_(false), contents_opaque_(false), draws_content_(false), @@ -375,6 +373,23 @@ return is_drawable_; } +gfx::Transform Layer::ComputeTransformToParent() { + // Layer transform is: + // position x transform_origin x transform x -transform_origin + gfx::Transform transform = + gfx::Transform::MakeTranslation(position_.x(), position_.y()); + transform.Translate3d(transform_origin_.x(), transform_origin_.y(), + transform_origin_.z()); + transform.PreConcat(transform_); + transform.Translate3d(-transform_origin_.x(), -transform_origin_.y(), + -transform_origin_.z()); + return transform; +} + +void Layer::AppendQuads(viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip) {} + void Layer::NotifyTreeChanged() { if (cc_layer()) { return; @@ -391,4 +406,23 @@ } } +viz::SharedQuadState* Layer::CreateAndAppendSharedQuadState( + viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip) { + viz::SharedQuadState* quad_state = + render_pass.CreateAndAppendSharedQuadState(); + const gfx::Rect rect{bounds()}; + absl::optional<gfx::Rect> clip_opt; + if (clip) { + clip_opt = *clip; + } + // TODO(crbug.com/1408128): Set visible_layer_rect properly. + quad_state->SetAll(transform, /*layer_rect=*/rect, + /*visible_layer_rect=*/rect, gfx::MaskFilterInfo(), + std::move(clip_opt), contents_opaque(), opacity(), + SkBlendMode::kSrcOver, 0); + return quad_state; +} + } // namespace cc::slim
diff --git a/cc/slim/layer.h b/cc/slim/layer.h index e0c64bc..cd9b1774 100644 --- a/cc/slim/layer.h +++ b/cc/slim/layer.h
@@ -23,10 +23,16 @@ class Layer; } +namespace viz { +class CompositorRenderPass; +class SharedQuadState; +} // namespace viz + namespace cc::slim { class LayerTree; class LayerTreeCcWrapper; +class LayerTreeImpl; // Base class for composited layers. Special layer types are derived from // this class. Each layer is an independent unit in the compositor, be that @@ -178,15 +184,24 @@ protected: friend class LayerTreeCcWrapper; + friend class LayerTreeImpl; explicit Layer(scoped_refptr<cc::Layer> cc_layer); virtual ~Layer(); // Called by LayerTree. + gfx::Transform ComputeTransformToParent(); virtual bool HasDrawableContent() const; + virtual void AppendQuads(viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip); void NotifyTreeChanged(); void NotifyPropertyChanged(); + virtual viz::SharedQuadState* CreateAndAppendSharedQuadState( + viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip); const scoped_refptr<cc::Layer> cc_layer_;
diff --git a/cc/slim/layer_tree_impl.cc b/cc/slim/layer_tree_impl.cc index b0c3821d..6a849b2 100644 --- a/cc/slim/layer_tree_impl.cc +++ b/cc/slim/layer_tree_impl.cc
@@ -6,15 +6,24 @@ #include <algorithm> #include <memory> +#include <vector> #include "base/auto_reset.h" +#include "base/containers/adapters.h" +#include "base/trace_event/trace_event.h" #include "cc/slim/frame_sink_impl.h" #include "cc/slim/layer.h" #include "cc/slim/layer_tree_client.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/hit_test/hit_test_region_list.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/quads/compositor_frame_metadata.h" +#include "components/viz/common/quads/compositor_render_pass.h" +#include "components/viz/common/quads/draw_quad.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/transform.h" namespace cc::slim { @@ -76,7 +85,7 @@ } void LayerTreeImpl::set_display_transform_hint(gfx::OverlayTransform hint) { - // TODO(crbug.com/1408128): Implement. + display_transform_hint_ = hint; } void LayerTreeImpl::RequestCopyOfOutput( @@ -182,9 +191,16 @@ // changes made by client `BeginFrame` are about to be drawn, so there is no // need for another frame. needs_draw_ = false; - // TODO(crbug.com/1408128): Implement frame production here. + + if (!root_) { + UpdateNeedsBeginFrame(); + return false; + } + + GenerateCompositorFrame(args, out_frame, out_resource_ids, + out_hit_test_region_list); UpdateNeedsBeginFrame(); - return false; + return true; } void LayerTreeImpl::DidReceiveCompositorFrameAck() { @@ -262,4 +278,88 @@ return client_needs_one_begin_frame_ || needs_draw_; } +void LayerTreeImpl::GenerateCompositorFrame( + const viz::BeginFrameArgs& args, + viz::CompositorFrame& out_frame, + base::flat_set<viz::ResourceId>& out_resource_ids, + viz::HitTestRegionList& out_hit_test_region_list) { + // TODO(crbug.com/1408128): Only has a very simple and basic compositor frame + // generation. Some missing features include: + // * Support multiple render passes (non-axis aligned clip, filters) + // * Damage tracking + // * Occlusion culling + // * Visible rect (ie clip) on quads + // * Surface embedding fields (referenced surfaces, activation dependency, + // deadline) + TRACE_EVENT0("cc", "slim::LayerTreeImpl::ProduceFrame"); + auto render_pass = viz::CompositorRenderPass::Create(); + render_pass->SetNew(viz::CompositorRenderPassId(root_->id()), + /*output_rect=*/device_viewport_rect_, + /*damage_rect=*/device_viewport_rect_, + /*transform_to_root_target=*/gfx::Transform()); + + out_frame.metadata.frame_token = ++next_frame_token_; + out_frame.metadata.begin_frame_ack = + viz::BeginFrameAck(args, /*has_damage=*/true); + out_frame.metadata.device_scale_factor = device_scale_factor_; + out_frame.metadata.root_background_color = background_color_; + out_frame.metadata.referenced_surfaces = std::vector<viz::SurfaceRange>( + referenced_surfaces_.begin(), referenced_surfaces_.end()); + out_frame.metadata.top_controls_visible_height = top_controls_visible_height_; + top_controls_visible_height_.reset(); + out_frame.metadata.display_transform_hint = display_transform_hint_; + + Draw(*root_, *render_pass, /*transform_to_target=*/gfx::Transform(), + /*clip_from_parent=*/nullptr); + + out_frame.render_pass_list.push_back(std::move(render_pass)); + + for (const auto& pass : out_frame.render_pass_list) { + for (const auto* quad : pass->quad_list) { + for (viz::ResourceId resource_id : quad->resources) { + out_resource_ids.insert(resource_id); + } + } + } +} + +void LayerTreeImpl::Draw(Layer& layer, + viz::CompositorRenderPass& parent_pass, + const gfx::Transform& transform_to_target, + const gfx::Rect* clip_from_parent) { + if (layer.hide_layer_and_subtree()) { + return; + } + + gfx::Transform transform_to_parent = layer.ComputeTransformToParent(); + + // New transform is: parent transform x layer transform. + gfx::Transform new_transform_to_target = transform_to_target; + new_transform_to_target.PreConcat(transform_to_parent); + + bool use_new_clip = false; + gfx::Rect new_clip; + // Drop non-axis aligned clip instead of using new render pass. + // TODO(crbug.com/1408128): Clip in layer space (visible rect) for clip + // that is not an exact integer. + if (layer.masks_to_bounds() && + new_transform_to_target.Preserves2dAxisAlignment()) { + new_clip.set_size(layer.bounds()); + new_clip = new_transform_to_target.MapRect(new_clip); + if (clip_from_parent) { + new_clip.Intersect(*clip_from_parent); + } + use_new_clip = true; + } + const gfx::Rect* clip = use_new_clip ? &new_clip : clip_from_parent; + + for (auto& child : base::Reversed(layer.children())) { + Draw(*child, parent_pass, new_transform_to_target, clip); + } + + if (!layer.bounds().IsEmpty() && layer.HasDrawableContent()) { + layer.AppendQuads(parent_pass, new_transform_to_target, clip); + } +} + } // namespace cc::slim
diff --git a/cc/slim/layer_tree_impl.h b/cc/slim/layer_tree_impl.h index 8c3809e..58e40a6 100644 --- a/cc/slim/layer_tree_impl.h +++ b/cc/slim/layer_tree_impl.h
@@ -32,6 +32,10 @@ class UIResourceManager; } // namespace cc +namespace viz { +class CompositorRenderPass; +} // namespace viz + namespace cc::slim { class FrameSinkImpl; @@ -104,6 +108,15 @@ // submitted in a CompositorFrame. void SetNeedsDraw(); bool NeedsBeginFrames() const; + void GenerateCompositorFrame( + const viz::BeginFrameArgs& args, + viz::CompositorFrame& out_frame, + base::flat_set<viz::ResourceId>& out_resource_ids, + viz::HitTestRegionList& out_hit_test_region_list); + void Draw(Layer& layer, + viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform_to_target, + const gfx::Rect* clip_from_parent); const raw_ptr<LayerTreeClient> client_; scoped_refptr<Layer> root_; @@ -132,6 +145,8 @@ SkColor4f background_color_ = SkColors::kWhite; absl::optional<float> top_controls_visible_height_; base::flat_set<viz::SurfaceRange> referenced_surfaces_; + viz::FrameTokenGenerator next_frame_token_; + gfx::OverlayTransform display_transform_hint_ = gfx::OVERLAY_TRANSFORM_NONE; base::WeakPtrFactory<LayerTreeImpl> weak_factory_{this}; };
diff --git a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc new file mode 100644 index 0000000..8fe76a85 --- /dev/null +++ b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
@@ -0,0 +1,310 @@ +// 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. + +#include <memory> +#include <utility> + +#include "base/memory/weak_ptr.h" +#include "base/test/scoped_feature_list.h" +#include "base/time/time.h" +#include "base/unguessable_token.h" +#include "cc/slim/features.h" +#include "cc/slim/layer.h" +#include "cc/slim/solid_color_layer.h" +#include "cc/slim/test_frame_sink_impl.h" +#include "cc/slim/test_layer_tree_client.h" +#include "cc/slim/test_layer_tree_impl.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "components/viz/test/draw_quad_matchers.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" + +namespace cc::slim { + +namespace { + +using testing::AllOf; +using testing::ElementsAre; + +class SlimLayerTreeCompositorFrameTest : public testing::Test { + public: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature(features::kSlimCompositor); + layer_tree_ = std::make_unique<TestLayerTreeImpl>(&client_); + layer_tree_->SetVisible(true); + + auto frame_sink = TestFrameSinkImpl::Create(); + frame_sink_ = frame_sink->GetWeakPtr(); + layer_tree_->SetFrameSink(std::move(frame_sink)); + + viewport_ = gfx::Rect(100, 100); + base::UnguessableToken token = base::UnguessableToken::Create(); + local_surface_id_ = viz::LocalSurfaceId(1u, 2u, token); + EXPECT_TRUE(local_surface_id_.is_valid()); + layer_tree_->SetViewportRectAndScale( + viewport_, /*device_scale_factor=*/1.0f, local_surface_id_); + } + + void IncrementLocalSurfaceId() { + DCHECK(local_surface_id_.is_valid()); + local_surface_id_ = + viz::LocalSurfaceId(local_surface_id_.parent_sequence_number(), + local_surface_id_.child_sequence_number() + 1, + local_surface_id_.embed_token()); + DCHECK(local_surface_id_.is_valid()); + } + + viz::CompositorFrame ProduceFrame() { + layer_tree_->SetNeedsRedraw(); + EXPECT_TRUE(layer_tree_->NeedsBeginFrames()); + base::TimeTicks frame_time = base::TimeTicks::Now(); + base::TimeDelta interval = viz::BeginFrameArgs::DefaultInterval(); + viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, + /*source_id=*/1, ++sequence_id_, frame_time, frame_time + interval, + interval, viz::BeginFrameArgs::NORMAL); + frame_sink_->OnBeginFrame(begin_frame_args, {}, /*frame_ack=*/false, {}); + viz::CompositorFrame frame = frame_sink_->TakeLastFrame(); + frame_sink_->DidReceiveCompositorFrameAck({}); + return frame; + } + + scoped_refptr<SolidColorLayer> CreateSolidColorLayer(const gfx::Size& bounds, + SkColor4f color) { + auto solid_color_layer = SolidColorLayer::Create(); + solid_color_layer->SetBounds(bounds); + solid_color_layer->SetBackgroundColor(color); + solid_color_layer->SetIsDrawable(true); + return solid_color_layer; + } + + protected: + base::test::ScopedFeatureList scoped_feature_list_; + TestLayerTreeClient client_; + std::unique_ptr<TestLayerTreeImpl> layer_tree_; + base::WeakPtr<TestFrameSinkImpl> frame_sink_; + + uint64_t sequence_id_ = 0; + + gfx::Rect viewport_; + viz::LocalSurfaceId local_surface_id_; +}; + +TEST_F(SlimLayerTreeCompositorFrameTest, CompositorFrameMetadataBasics) { + auto solid_color_layer = + CreateSolidColorLayer(viewport_.size(), SkColors::kGray); + layer_tree_->SetRoot(solid_color_layer); + + // TODO(crbug.com/1408128): Add tests for features once implemented: + // * reference_surfaces + // * activation_dependencies + // * deadline + uint32_t first_frame_token = 0u; + { + viz::CompositorFrame frame = ProduceFrame(); + viz::CompositorFrameMetadata& metadata = frame.metadata; + EXPECT_NE(0u, metadata.frame_token); + first_frame_token = metadata.frame_token; + EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number); + EXPECT_EQ(1.0f, metadata.device_scale_factor); + EXPECT_EQ(SkColors::kWhite, metadata.root_background_color); + EXPECT_EQ(gfx::OVERLAY_TRANSFORM_NONE, metadata.display_transform_hint); + EXPECT_EQ(absl::nullopt, metadata.top_controls_visible_height); + } + + IncrementLocalSurfaceId(); + layer_tree_->SetViewportRectAndScale(viewport_, /*device_scale_factor=*/2.0f, + local_surface_id_); + layer_tree_->set_background_color(SkColors::kBlue); + layer_tree_->set_display_transform_hint(gfx::OVERLAY_TRANSFORM_ROTATE_90); + layer_tree_->UpdateTopControlsVisibleHeight(5.0f); + { + viz::CompositorFrame frame = ProduceFrame(); + viz::CompositorFrameMetadata& metadata = frame.metadata; + EXPECT_NE(0u, metadata.frame_token); + EXPECT_NE(first_frame_token, metadata.frame_token); + EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number); + EXPECT_EQ(2.0f, metadata.device_scale_factor); + EXPECT_EQ(SkColors::kBlue, metadata.root_background_color); + EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, + metadata.display_transform_hint); + EXPECT_EQ(5.0f, metadata.top_controls_visible_height); + } +} + +TEST_F(SlimLayerTreeCompositorFrameTest, OneSolidColorQuad) { + auto solid_color_layer = + CreateSolidColorLayer(viewport_.size(), SkColors::kGray); + layer_tree_->SetRoot(solid_color_layer); + + viz::CompositorFrame frame = ProduceFrame(); + + ASSERT_EQ(frame.render_pass_list.size(), 1u); + auto& pass = frame.render_pass_list.back(); + EXPECT_EQ(pass->output_rect, viewport_); + EXPECT_EQ(pass->damage_rect, viewport_); + EXPECT_EQ(pass->transform_to_root_target, gfx::Transform()); + + ASSERT_THAT( + pass->quad_list, + ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGray), + viz::HasRect(viewport_), viz::HasVisibleRect(viewport_), + viz::HasTransform(gfx::Transform())))); + auto* quad = pass->quad_list.back(); + auto* shared_quad_state = quad->shared_quad_state; + + EXPECT_EQ(shared_quad_state->quad_layer_rect, viewport_); + EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, viewport_); + EXPECT_EQ(shared_quad_state->clip_rect, absl::nullopt); + EXPECT_EQ(shared_quad_state->are_contents_opaque, true); + EXPECT_EQ(shared_quad_state->opacity, 1.0f); + EXPECT_EQ(shared_quad_state->blend_mode, SkBlendMode::kSrcOver); +} + +TEST_F(SlimLayerTreeCompositorFrameTest, LayerTransform) { + auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); + layer_tree_->SetRoot(root_layer); + + auto child = CreateSolidColorLayer(gfx::Size(10, 20), SkColors::kGreen); + root_layer->AddChild(child); + + auto check_child_quad = [&](gfx::Rect expected_rect_in_root) { + viz::CompositorFrame frame = ProduceFrame(); + ASSERT_EQ(frame.render_pass_list.size(), 1u); + auto& pass = frame.render_pass_list.back(); + ASSERT_THAT(pass->quad_list, + ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGreen), + viz::HasRect(gfx::Rect(10, 20)), + viz::HasVisibleRect(gfx::Rect(10, 20))), + AllOf(viz::IsSolidColorQuad(SkColors::kGray), + viz::HasRect(viewport_), + viz::HasVisibleRect(viewport_)))); + + auto* quad = pass->quad_list.front(); + auto* shared_quad_state = quad->shared_quad_state; + + EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(10, 20)); + EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, gfx::Rect(10, 20)); + + gfx::Rect rect_in_root = + shared_quad_state->quad_to_target_transform.MapRect(quad->rect); + EXPECT_EQ(expected_rect_in_root, rect_in_root); + }; + + child->SetPosition(gfx::PointF(30.0f, 30.0f)); + check_child_quad(gfx::Rect(30, 30, 10, 20)); + + child->SetTransform(gfx::Transform::MakeTranslation(10.0f, 10.0f)); + check_child_quad(gfx::Rect(40, 40, 10, 20)); + + // Rotate about top left corner. + child->SetTransform(gfx::Transform::Make90degRotation()); + check_child_quad(gfx::Rect(10, 30, 20, 10)); + + // Rotate about the center. + child->SetTransformOrigin(gfx::Point3F(5.0f, 10.0f, 0.0f)); + check_child_quad(gfx::Rect(25, 35, 20, 10)); +} + +TEST_F(SlimLayerTreeCompositorFrameTest, ChildOrder) { + auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); + layer_tree_->SetRoot(root_layer); + + scoped_refptr<SolidColorLayer> children[] = { + CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kBlue), + CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kGreen), + CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kMagenta), + CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kRed), + CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kYellow)}; + + // Build tree such that quads appear in child order. + // Quads are appended post order depth first, in reverse child order. + // root <- child4 <- child3 + // <- child2 + // <- child1 <- child0 + root_layer->AddChild(children[4]); + root_layer->AddChild(children[1]); + children[4]->AddChild(children[3]); + children[4]->AddChild(children[2]); + children[1]->AddChild(children[0]); + + // Add offsets so they do not cover each other. + children[3]->SetPosition(gfx::PointF(10.0f, 10.0f)); + children[2]->SetPosition(gfx::PointF(20.0f, 20.0f)); + children[1]->SetPosition(gfx::PointF(30.0f, 30.0f)); + children[0]->SetPosition(gfx::PointF(10.0f, 10.0f)); + + gfx::Point expected_origins[] = { + gfx::Point(40.0f, 40.0f), gfx::Point(30.0f, 30.0f), + gfx::Point(20.0f, 20.0f), gfx::Point(10.0f, 10.0f), + gfx::Point(00.0f, 00.0f)}; + + viz::CompositorFrame frame = ProduceFrame(); + ASSERT_EQ(frame.render_pass_list.size(), 1u); + auto& pass = frame.render_pass_list.back(); + ASSERT_THAT(pass->quad_list, + ElementsAre(viz::IsSolidColorQuad(SkColors::kBlue), + viz::IsSolidColorQuad(SkColors::kGreen), + viz::IsSolidColorQuad(SkColors::kMagenta), + viz::IsSolidColorQuad(SkColors::kRed), + viz::IsSolidColorQuad(SkColors::kYellow), + viz::IsSolidColorQuad(SkColors::kGray))); + + for (size_t i = 0; i < std::size(expected_origins); ++i) { + auto* quad = pass->quad_list.ElementAt(i); + EXPECT_EQ(quad->shared_quad_state->quad_to_target_transform.MapPoint( + gfx::Point()), + expected_origins[i]); + } +} + +TEST_F(SlimLayerTreeCompositorFrameTest, AxisAlignedClip) { + auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); + layer_tree_->SetRoot(root_layer); + + auto clip_layer = Layer::Create(); + clip_layer->SetBounds(gfx::Size(10, 20)); + clip_layer->SetMasksToBounds(true); + + auto draw_layer = CreateSolidColorLayer(gfx::Size(30, 30), SkColors::kRed); + + root_layer->AddChild(clip_layer); + clip_layer->AddChild(draw_layer); + + { + viz::CompositorFrame frame = ProduceFrame(); + ASSERT_EQ(frame.render_pass_list.size(), 1u); + auto& pass = frame.render_pass_list.back(); + ASSERT_THAT(pass->quad_list, + ElementsAre(viz::IsSolidColorQuad(SkColors::kRed), + viz::IsSolidColorQuad(SkColors::kGray))); + + auto* quad = pass->quad_list.front(); + ASSERT_TRUE(quad->shared_quad_state->clip_rect); + EXPECT_EQ(quad->shared_quad_state->clip_rect.value(), gfx::Rect(10, 20)); + } + + clip_layer->SetPosition(gfx::PointF(5, 5)); + { + viz::CompositorFrame frame = ProduceFrame(); + ASSERT_EQ(frame.render_pass_list.size(), 1u); + auto& pass = frame.render_pass_list.back(); + ASSERT_THAT(pass->quad_list, + ElementsAre(viz::IsSolidColorQuad(SkColors::kRed), + viz::IsSolidColorQuad(SkColors::kGray))); + + auto* quad = pass->quad_list.front(); + ASSERT_TRUE(quad->shared_quad_state->clip_rect); + // Clip is in target space. + EXPECT_EQ(quad->shared_quad_state->clip_rect.value(), + gfx::Rect(5, 5, 10, 20)); + } +} + +} // namespace + +} // namespace cc::slim
diff --git a/cc/slim/solid_color_layer.cc b/cc/slim/solid_color_layer.cc index dc4e6f5..ece3586 100644 --- a/cc/slim/solid_color_layer.cc +++ b/cc/slim/solid_color_layer.cc
@@ -7,6 +7,7 @@ #include <utility> #include "cc/layers/solid_color_layer.h" +#include "cc/slim/features.h" #include "components/viz/common/quads/compositor_render_pass.h" #include "components/viz/common/quads/solid_color_draw_quad.h" @@ -15,7 +16,9 @@ // static scoped_refptr<SolidColorLayer> SolidColorLayer::Create() { scoped_refptr<cc::SolidColorLayer> cc_layer; - cc_layer = cc::SolidColorLayer::Create(); + if (!features::IsSlimCompositorEnabled()) { + cc_layer = cc::SolidColorLayer::Create(); + } return base::AdoptRef(new SolidColorLayer(std::move(cc_layer))); } @@ -29,7 +32,24 @@ } void SolidColorLayer::SetBackgroundColor(SkColor4f color) { - cc_layer()->SetBackgroundColor(color); + if (cc_layer()) { + cc_layer()->SetBackgroundColor(color); + return; + } + SetContentsOpaque(color.isOpaque()); + Layer::SetBackgroundColor(color); +} + +void SolidColorLayer::AppendQuads(viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip) { + viz::SharedQuadState* quad_state = + CreateAndAppendSharedQuadState(render_pass, transform, clip); + viz::SolidColorDrawQuad* quad = + render_pass.CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); + quad->SetNew(quad_state, quad_state->quad_layer_rect, + quad_state->visible_quad_layer_rect, background_color(), + /*anti_aliasing_off=*/true); } } // namespace cc::slim
diff --git a/cc/slim/solid_color_layer.h b/cc/slim/solid_color_layer.h index c3c91b5..abbc41d 100644 --- a/cc/slim/solid_color_layer.h +++ b/cc/slim/solid_color_layer.h
@@ -26,6 +26,10 @@ explicit SolidColorLayer(scoped_refptr<cc::SolidColorLayer> cc_layer); ~SolidColorLayer() override; + void AppendQuads(viz::CompositorRenderPass& render_pass, + const gfx::Transform& transform, + const gfx::Rect* clip) override; + cc::SolidColorLayer* cc_layer() const; };
diff --git a/cc/slim/test_frame_sink_impl.cc b/cc/slim/test_frame_sink_impl.cc index 67608ef..e764043 100644 --- a/cc/slim/test_frame_sink_impl.cc +++ b/cc/slim/test_frame_sink_impl.cc
@@ -6,8 +6,11 @@ #include <memory> #include <utility> +#include <vector> #include "base/task/single_thread_task_runner.h" +#include "build/build_config.h" +#include "components/viz/common/quads/compositor_frame.h" #include "components/viz/test/test_context_provider.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" @@ -16,6 +19,41 @@ namespace cc::slim { +class TestFrameSinkImpl::TestMojoCompositorFrameSink + : public viz::mojom::CompositorFrameSink { + public: + TestMojoCompositorFrameSink() = default; + void SetNeedsBeginFrame(bool needs_begin_frame) override {} + void SetWantsAnimateOnlyBeginFrames() override {} + void SubmitCompositorFrame( + const viz::LocalSurfaceId& local_surface_id, + viz::CompositorFrame frame, + absl::optional<::viz::HitTestRegionList> hit_test_region_list, + uint64_t submit_time) override { + last_frame_ = std::move(frame); + } + void SubmitCompositorFrameSync( + const viz::LocalSurfaceId& local_surface_id, + viz::CompositorFrame frame, + absl::optional<::viz::HitTestRegionList> hit_test_region_list, + uint64_t submit_time, + SubmitCompositorFrameSyncCallback callback) override {} + void DidNotProduceFrame(const viz::BeginFrameAck& ack) override {} + void DidAllocateSharedBitmap(base::ReadOnlySharedMemoryRegion region, + const gpu::Mailbox& id) override {} + void DidDeleteSharedBitmap(const gpu::Mailbox& id) override {} + void InitializeCompositorFrameSinkType( + viz::mojom::CompositorFrameSinkType type) override {} +#if BUILDFLAG(IS_ANDROID) + void SetThreadIds(const std::vector<int32_t>& thread_ids) override {} +#endif + + viz::CompositorFrame TakeLastFrame() { return std::move(last_frame_); } + + private: + viz::CompositorFrame last_frame_; +}; + // static std::unique_ptr<TestFrameSinkImpl> TestFrameSinkImpl::Create() { auto task_runner = base::SingleThreadTaskRunner::GetCurrentDefault(); @@ -44,13 +82,18 @@ std::move(client_receiver), std::move(context_provider), base::kInvalidThreadId), - pending_sink_receiver_(std::move(sink_receiver)) {} + mojo_sink_(std::make_unique<TestMojoCompositorFrameSink>()) {} TestFrameSinkImpl::~TestFrameSinkImpl() = default; +viz::CompositorFrame TestFrameSinkImpl::TakeLastFrame() { + return mojo_sink_->TakeLastFrame(); +} + bool TestFrameSinkImpl::BindToClient(FrameSinkImplClient* client) { DCHECK(!bind_to_client_called_); client_ = client; + frame_sink_ = mojo_sink_.get(); bind_to_client_called_ = true; return bind_to_client_result_; }
diff --git a/cc/slim/test_frame_sink_impl.h b/cc/slim/test_frame_sink_impl.h index a1a5b725..e74ac326 100644 --- a/cc/slim/test_frame_sink_impl.h +++ b/cc/slim/test_frame_sink_impl.h
@@ -24,6 +24,7 @@ DCHECK(!bind_to_client_called_); bind_to_client_result_ = result; } + viz::CompositorFrame TakeLastFrame(); bool bind_to_client_called() const { return bind_to_client_called_; } bool needs_begin_frames() const { return needs_begin_frames_; } @@ -32,6 +33,7 @@ void SetNeedsBeginFrame(bool needs_begin_frame) override; private: + class TestMojoCompositorFrameSink; TestFrameSinkImpl( scoped_refptr<base::SingleThreadTaskRunner> task_runner, mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink> @@ -42,8 +44,7 @@ mojo::PendingAssociatedReceiver<viz::mojom::CompositorFrameSink> sink_receiver); - mojo::PendingAssociatedReceiver<viz::mojom::CompositorFrameSink> - pending_sink_receiver_; + std::unique_ptr<TestMojoCompositorFrameSink> mojo_sink_; bool bind_to_client_called_ = false; bool bind_to_client_result_ = true;
diff --git a/cc/trees/clip_node.cc b/cc/trees/clip_node.cc index 7bf24fa..9318ece 100644 --- a/cc/trees/clip_node.cc +++ b/cc/trees/clip_node.cc
@@ -11,9 +11,16 @@ namespace cc { -ClipNode::ClipNode() = default; +ClipNode::ClipNode() + : id(kInvalidPropertyNodeId), + parent_id(kInvalidPropertyNodeId), + pixel_moving_filter_id(kInvalidPropertyNodeId), + transform_id(kInvalidPropertyNodeId) {} + ClipNode::ClipNode(const ClipNode& other) = default; + ClipNode& ClipNode::operator=(const ClipNode& other) = default; + ClipNode::~ClipNode() = default; bool ClipNode::AppliesLocalClip() const {
diff --git a/cc/trees/clip_node.h b/cc/trees/clip_node.h index bab772d4..0d941eaa7 100644 --- a/cc/trees/clip_node.h +++ b/cc/trees/clip_node.h
@@ -7,7 +7,6 @@ #include "base/containers/stack_container.h" #include "cc/cc_export.h" -#include "cc/trees/property_ids.h" #include "ui/gfx/geometry/rect_f.h" namespace base { @@ -41,9 +40,9 @@ bool AppliesLocalClip() const; // The node index of this node in the clip tree node vector. - int id = kInvalidPropertyNodeId; + int id; // The node index of the parent node in the clip tree node vector. - int parent_id = kInvalidPropertyNodeId; + int parent_id; // The clip rect that this node contributes, expressed in the space of its // transform node. This field is ignored if AppliesLocalClip() is false. @@ -66,10 +65,10 @@ // Instead of applying |clip|, this clip node expands the accumulated clip // to include any pixels in the contents that can affect the rendering result // with the filter. - int pixel_moving_filter_id = kInvalidPropertyNodeId; + int pixel_moving_filter_id; // The id of the transform node that defines the clip node's local space. - int transform_id = kInvalidPropertyNodeId; + int transform_id; #if DCHECK_IS_ON() bool operator==(const ClipNode& other) const;
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc index 5b048cf..9e3aabb 100644 --- a/cc/trees/effect_node.cc +++ b/cc/trees/effect_node.cc
@@ -12,12 +12,101 @@ namespace cc { -EffectNode::EffectNode() = default; +EffectNode::EffectNode() + : id(kInvalidPropertyNodeId), + parent_id(kInvalidPropertyNodeId), + stable_id(INVALID_STABLE_ID), + opacity(1.f), + screen_space_opacity(1.f), + backdrop_filter_quality(1.f), + blend_mode(SkBlendMode::kSrcOver), + cache_render_surface(false), + has_copy_request(false), + hidden_by_backface_visibility(false), + double_sided(true), + trilinear_filtering(false), + is_drawn(true), + only_draws_visible_content(true), + subtree_hidden(false), + has_potential_filter_animation(false), + has_potential_backdrop_filter_animation(false), + has_potential_opacity_animation(false), + is_currently_animating_filter(false), + is_currently_animating_backdrop_filter(false), + is_currently_animating_opacity(false), + has_masking_child(false), + effect_changed(false), + subtree_has_copy_request(false), + is_fast_rounded_corner(false), + node_or_ancestor_has_filters(false), + affected_by_backdrop_filter(false), + render_surface_reason(RenderSurfaceReason::kNone), + transform_id(0), + clip_id(0), + target_id(1), + closest_ancestor_with_cached_render_surface_id(-1), + closest_ancestor_with_copy_request_id(-1), + closest_ancestor_being_captured_id(-1), + closest_ancestor_with_shared_element_id(-1) {} + EffectNode::EffectNode(const EffectNode& other) = default; + EffectNode::~EffectNode() = default; #if DCHECK_IS_ON() -bool EffectNode::operator==(const EffectNode& other) const = default; +bool EffectNode::operator==(const EffectNode& other) const { + return id == other.id && parent_id == other.parent_id && + stable_id == other.stable_id && opacity == other.opacity && + screen_space_opacity == other.screen_space_opacity && + backdrop_filter_quality == other.backdrop_filter_quality && + subtree_capture_id == other.subtree_capture_id && + subtree_size == other.subtree_size && + cache_render_surface == other.cache_render_surface && + has_copy_request == other.has_copy_request && + filters == other.filters && + backdrop_filters == other.backdrop_filters && + backdrop_filter_bounds == other.backdrop_filter_bounds && + backdrop_mask_element_id == other.backdrop_mask_element_id && + mask_filter_info == other.mask_filter_info && + is_fast_rounded_corner == other.is_fast_rounded_corner && + node_or_ancestor_has_filters == other.node_or_ancestor_has_filters && + affected_by_backdrop_filter == other.affected_by_backdrop_filter && + // The specific reason is just for tracing/testing/debugging, so just + // check whether a render surface is needed. + HasRenderSurface() == other.HasRenderSurface() && + blend_mode == other.blend_mode && + surface_contents_scale == other.surface_contents_scale && + hidden_by_backface_visibility == other.hidden_by_backface_visibility && + double_sided == other.double_sided && + trilinear_filtering == other.trilinear_filtering && + is_drawn == other.is_drawn && + only_draws_visible_content == other.only_draws_visible_content && + subtree_hidden == other.subtree_hidden && + has_potential_filter_animation == + other.has_potential_filter_animation && + has_potential_backdrop_filter_animation == + other.has_potential_backdrop_filter_animation && + has_potential_opacity_animation == + other.has_potential_opacity_animation && + is_currently_animating_filter == other.is_currently_animating_filter && + is_currently_animating_backdrop_filter == + other.is_currently_animating_backdrop_filter && + is_currently_animating_opacity == + other.is_currently_animating_opacity && + has_masking_child == other.has_masking_child && + effect_changed == other.effect_changed && + subtree_has_copy_request == other.subtree_has_copy_request && + transform_id == other.transform_id && clip_id == other.clip_id && + target_id == other.target_id && + closest_ancestor_with_cached_render_surface_id == + other.closest_ancestor_with_cached_render_surface_id && + closest_ancestor_with_copy_request_id == + other.closest_ancestor_with_copy_request_id && + closest_ancestor_being_captured_id == + other.closest_ancestor_being_captured_id && + closest_ancestor_with_shared_element_id == + other.closest_ancestor_with_shared_element_id; +} #endif // DCHECK_IS_ON() const char* RenderSurfaceReasonToString(RenderSurfaceReason reason) {
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h index b5a2eae2..c329bea7 100644 --- a/cc/trees/effect_node.h +++ b/cc/trees/effect_node.h
@@ -8,7 +8,6 @@ #include "cc/cc_export.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/property_ids.h" #include "cc/view_transition/view_transition_element_id.h" #include "components/viz/common/surfaces/subtree_capture_id.h" #include "components/viz/common/view_transition_element_resource_id.h" @@ -65,25 +64,25 @@ EffectNode(const EffectNode& other); ~EffectNode(); - enum StableIdLabels { kInvalidStableId = 0 }; + enum StableIdLabels { INVALID_STABLE_ID = 0 }; // The node index of this node in the effect tree node vector. - int id = kInvalidPropertyNodeId; + int id; // The node index of the parent node in the effect tree node vector. - int parent_id = kInvalidPropertyNodeId; - // An opaque, unique, stable identifier for this effect that persists across + int parent_id; + // An opaque, unique, stable identifer for this effect that persists across // frame commits. This id is used only for internal implementation // details such as RenderSurface and RenderPass ids, and should not // be assumed to have semantic meaning. - uint64_t stable_id = kInvalidStableId; + uint64_t stable_id; - float opacity = 1.f; - float screen_space_opacity = 1.f; + float opacity; + float screen_space_opacity; FilterOperations filters; FilterOperations backdrop_filters; absl::optional<gfx::RRectF> backdrop_filter_bounds; - float backdrop_filter_quality = 1.f; + float backdrop_filter_quality; gfx::PointF filters_origin; // The element id corresponding to the mask to apply to the filtered backdrop @@ -95,79 +94,79 @@ // effect node. gfx::MaskFilterInfo mask_filter_info; - SkBlendMode blend_mode = SkBlendMode::kSrcOver; + SkBlendMode blend_mode; gfx::Vector2dF surface_contents_scale; viz::SubtreeCaptureId subtree_capture_id; gfx::Size subtree_size; - bool cache_render_surface : 1 = false; - bool has_copy_request : 1 = false; - bool hidden_by_backface_visibility : 1 = false; + bool cache_render_surface : 1; + bool has_copy_request : 1; + bool hidden_by_backface_visibility : 1; // Whether the contents should continue to be visible when rotated such that // its back face is facing toward the camera. It's true by default. - bool double_sided : 1 = true; - bool trilinear_filtering : 1 = false; - bool is_drawn : 1 = true; + bool double_sided : 1; + bool trilinear_filtering : 1; + bool is_drawn : 1; // In most cases we only need to draw the visible part of any content // contributing to the effect. For copy request case, we would need to copy // the entire content, and could not only draw the visible part. In the rare // case of a backdrop zoom filter we need to take into consideration the // content offscreen to make sure the backdrop zoom filter is applied with the // correct center. - bool only_draws_visible_content : 1 = true; + bool only_draws_visible_content : 1; // TODO(jaydasika) : Delete this after implementation of // SetHideLayerAndSubtree is cleaned up. (crbug.com/595843) - bool subtree_hidden : 1 = false; + bool subtree_hidden : 1; // Whether this node has a potentially running (i.e., irrespective // of exact timeline) filter animation. - bool has_potential_filter_animation : 1 = false; + bool has_potential_filter_animation : 1; // Whether this node has a potentially running (i.e., irrespective // of exact timeline) backdrop-filter animation. - bool has_potential_backdrop_filter_animation : 1 = false; + bool has_potential_backdrop_filter_animation : 1; // Whether this node has a potentially running (i.e., irrespective // of exact timeline) opacity animation. - bool has_potential_opacity_animation : 1 = false; + bool has_potential_opacity_animation : 1; // Whether this node has a currently running filter animation. - bool is_currently_animating_filter : 1 = false; + bool is_currently_animating_filter : 1; // Whether this node has a currently running backdrop-filter animation. - bool is_currently_animating_backdrop_filter : 1 = false; + bool is_currently_animating_backdrop_filter : 1; // Whether this node has a currently running opacity animation. - bool is_currently_animating_opacity : 1 = false; + bool is_currently_animating_opacity : 1; // Whether this node has a child node with kDstIn blend mode. - bool has_masking_child : 1 = false; + bool has_masking_child : 1; // Whether this node's effect has been changed since the last // frame. Needed in order to compute damage rect. - bool effect_changed : 1 = false; - bool subtree_has_copy_request : 1 = false; + bool effect_changed : 1; + bool subtree_has_copy_request : 1; // If set, the effect node tries to not trigger a render surface due to it // having a rounded corner. - bool is_fast_rounded_corner : 1 = false; + bool is_fast_rounded_corner : 1; // If the node or it's parent has the filters, it sets to true. - bool node_or_ancestor_has_filters : 1 = false; + bool node_or_ancestor_has_filters : 1; // All node in the subtree starting from the containing render surface, and // before the backdrop filter node in pre tree order. // This is set and used for the impl-side effect tree only. - bool affected_by_backdrop_filter : 1 = false; + bool affected_by_backdrop_filter: 1; // RenderSurfaceReason::kNone if this effect node should not create a render // surface, or the reason that this effect node should create one. - RenderSurfaceReason render_surface_reason = RenderSurfaceReason::kNone; + RenderSurfaceReason render_surface_reason; // The transform node index of the transform to apply to this effect // node's content when rendering to a surface. - int transform_id = kRootPropertyNodeId; + int transform_id; // The clip node index of the clip to apply to this effect node's // content when rendering to a surface. - int clip_id = kRootPropertyNodeId; + int clip_id; // This is the id of the ancestor effect node that induces a // RenderSurfaceImpl. // This is set and used for the impl-side effect tree only. - int target_id = 1; - int closest_ancestor_with_cached_render_surface_id = kInvalidPropertyNodeId; - int closest_ancestor_with_copy_request_id = kInvalidPropertyNodeId; - int closest_ancestor_being_captured_id = kInvalidPropertyNodeId; - int closest_ancestor_with_shared_element_id = kInvalidPropertyNodeId; + int target_id; + int closest_ancestor_with_cached_render_surface_id; + int closest_ancestor_with_copy_request_id; + int closest_ancestor_being_captured_id; + int closest_ancestor_with_shared_element_id; // Represents a DOM element id for the view transition API. ViewTransitionElementId view_transition_shared_element_id;
diff --git a/cc/trees/mutator_host.h b/cc/trees/mutator_host.h index e5bdf98..74fddb4 100644 --- a/cc/trees/mutator_host.h +++ b/cc/trees/mutator_host.h
@@ -26,7 +26,7 @@ // Used as the return value of GetAnimationScales() to indicate that there is // no active transform animation or the scale cannot be computed. -inline constexpr float kInvalidScale = 0.f; +constexpr float kInvalidScale = 0.f; // A MutatorHost owns all the animation and mutation effects. // There is just one MutatorHost for LayerTreeHost on main renderer thread
diff --git a/cc/trees/property_ids.h b/cc/trees/property_ids.h deleted file mode 100644 index 7a838b90..0000000 --- a/cc/trees/property_ids.h +++ /dev/null
@@ -1,21 +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. - -#ifndef CC_TREES_PROPERTY_IDS_H_ -#define CC_TREES_PROPERTY_IDS_H_ - -namespace cc { - -// Ids of property tree nodes, starting from 0. -enum { - kInvalidPropertyNodeId = -1, - kRootPropertyNodeId = 0, - kSecondaryRootPropertyNodeId = 1, - kContentsRootPropertyNodeId = kSecondaryRootPropertyNodeId, - kViewportPropertyNodeId = kSecondaryRootPropertyNodeId -}; - -} // namespace cc - -#endif // CC_TREES_PROPERTY_IDS_H_
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h index 820fd53..627a9ca 100644 --- a/cc/trees/property_tree.h +++ b/cc/trees/property_tree.h
@@ -28,7 +28,6 @@ #include "cc/trees/clip_node.h" #include "cc/trees/effect_node.h" #include "cc/trees/mutator_host.h" -#include "cc/trees/property_ids.h" #include "cc/trees/scroll_node.h" #include "cc/trees/sticky_position_constraint.h" #include "cc/trees/transform_node.h" @@ -62,6 +61,16 @@ class PropertyTrees; +// Property tree node starts from index 0. See equivalent constants in +// property_tree_manager.cc for comments. +enum { + kInvalidPropertyNodeId = -1, + kRootPropertyNodeId = 0, + kSecondaryRootPropertyNodeId = 1, + kContentsRootPropertyNodeId = kSecondaryRootPropertyNodeId, + kViewportPropertyNodeId = kSecondaryRootPropertyNodeId +}; + template <typename T> class CC_EXPORT PropertyTree { friend class PropertyTrees;
diff --git a/cc/trees/scroll_node.cc b/cc/trees/scroll_node.cc index 5588ab5c..15819b23 100644 --- a/cc/trees/scroll_node.cc +++ b/cc/trees/scroll_node.cc
@@ -5,6 +5,7 @@ #include "cc/trees/scroll_node.h" #include "cc/base/math_util.h" +#include "cc/input/main_thread_scrolling_reason.h" #include "cc/layers/layer.h" #include "cc/paint/element_id.h" #include "cc/trees/property_tree.h" @@ -13,12 +14,48 @@ namespace cc { -ScrollNode::ScrollNode() = default; +ScrollNode::ScrollNode() + : id(kInvalidPropertyNodeId), + parent_id(kInvalidPropertyNodeId), + main_thread_scrolling_reasons( + MainThreadScrollingReason::kNotScrollingOnMain), + scrollable(false), + max_scroll_offset_affected_by_page_scale(false), + scrolls_inner_viewport(false), + scrolls_outer_viewport(false), + prevent_viewport_scrolling_from_inner(false), + should_flatten(false), + user_scrollable_horizontal(false), + user_scrollable_vertical(false), + transform_id(0), + overscroll_behavior(OverscrollBehavior::Type::kAuto), + is_composited(false) {} + ScrollNode::ScrollNode(const ScrollNode& other) = default; + ScrollNode::~ScrollNode() = default; #if DCHECK_IS_ON() -bool ScrollNode::operator==(const ScrollNode& other) const = default; +bool ScrollNode::operator==(const ScrollNode& other) const { + return id == other.id && parent_id == other.parent_id && + scrollable == other.scrollable && + main_thread_scrolling_reasons == other.main_thread_scrolling_reasons && + container_bounds == other.container_bounds && bounds == other.bounds && + max_scroll_offset_affected_by_page_scale == + other.max_scroll_offset_affected_by_page_scale && + scrolls_inner_viewport == other.scrolls_inner_viewport && + prevent_viewport_scrolling_from_inner == + other.prevent_viewport_scrolling_from_inner && + scrolls_outer_viewport == other.scrolls_outer_viewport && + offset_to_transform_parent == other.offset_to_transform_parent && + should_flatten == other.should_flatten && + user_scrollable_horizontal == other.user_scrollable_horizontal && + user_scrollable_vertical == other.user_scrollable_vertical && + element_id == other.element_id && transform_id == other.transform_id && + overscroll_behavior == other.overscroll_behavior && + snap_container_data == other.snap_container_data && + is_composited == other.is_composited; +} #endif void ScrollNode::AsValueInto(base::trace_event::TracedValue* value) const {
diff --git a/cc/trees/scroll_node.h b/cc/trees/scroll_node.h index 3ecc057..c8edf41 100644 --- a/cc/trees/scroll_node.h +++ b/cc/trees/scroll_node.h
@@ -7,12 +7,10 @@ #include "cc/base/region.h" #include "cc/cc_export.h" -#include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/scroll_snap_data.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/property_ids.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/size.h" @@ -30,12 +28,11 @@ ~ScrollNode(); // The node index of this node in the scroll tree node vector. - int id = kInvalidPropertyNodeId; + int id; // The node index of the parent node in the scroll tree node vector. - int parent_id = kInvalidPropertyNodeId; + int parent_id; - uint32_t main_thread_scrolling_reasons = - MainThreadScrollingReason::kNotScrollingOnMain; + uint32_t main_thread_scrolling_reasons; // Size of the container area that the contents scrolls in, not including // non-overlay scrollbars. Overlay scrollbars do not affect these bounds. @@ -50,27 +47,28 @@ // This is used for subtrees that should not be scrolled independently. For // example, when there is a layer that is not scrollable itself but is inside // a scrolling layer. - bool scrollable : 1 = false; - bool max_scroll_offset_affected_by_page_scale : 1 = false; - bool scrolls_inner_viewport : 1 = false; - bool scrolls_outer_viewport : 1 = false; - bool prevent_viewport_scrolling_from_inner : 1 = false; - bool should_flatten : 1 = false; - bool user_scrollable_horizontal : 1 = false; - bool user_scrollable_vertical : 1 = false; - bool is_composited : 1 = false; + bool scrollable : 1; + bool max_scroll_offset_affected_by_page_scale : 1; + bool scrolls_inner_viewport : 1; + bool scrolls_outer_viewport : 1; + bool prevent_viewport_scrolling_from_inner : 1; + bool should_flatten : 1; + bool user_scrollable_horizontal : 1; + bool user_scrollable_vertical : 1; // This offset is used when |scrollable| is false and there isn't a transform // node already present that covers this offset. For layer tree mode only. gfx::Vector2dF offset_to_transform_parent; ElementId element_id; - int transform_id = kRootPropertyNodeId; + int transform_id; - OverscrollBehavior overscroll_behavior{OverscrollBehavior::Type::kAuto}; + OverscrollBehavior overscroll_behavior; absl::optional<SnapContainerData> snap_container_data; + bool is_composited : 1; + #if DCHECK_IS_ON() bool operator==(const ScrollNode& other) const; #endif
diff --git a/cc/trees/transform_node.cc b/cc/trees/transform_node.cc index 907e0d4e6..d5f1a929 100644 --- a/cc/trees/transform_node.cc +++ b/cc/trees/transform_node.cc
@@ -12,12 +12,74 @@ namespace cc { -TransformNode::TransformNode() = default; +TransformNode::TransformNode() + : id(kInvalidPropertyNodeId), + parent_id(kInvalidPropertyNodeId), + parent_frame_id(kInvalidPropertyNodeId), + sticky_position_constraint_id(-1), + anchor_scroll_containers_data_id(-1), + sorting_context_id(0), + needs_local_transform_update(true), + node_and_ancestors_are_animated_or_invertible(true), + is_invertible(true), + ancestors_are_invertible(true), + has_potential_animation(false), + is_currently_animating(false), + to_screen_is_potentially_animated(false), + flattens_inherited_transform(true), + node_and_ancestors_are_flat(true), + scrolls(false), + should_undo_overscroll(false), + should_be_snapped(false), + moved_by_outer_viewport_bounds_delta_y(false), + in_subtree_of_page_scale_layer(false), + transform_changed(false), + delegates_to_parent_for_backface(false), + will_change_transform(false), + node_or_ancestors_will_change_transform(false), + maximum_animation_scale(kInvalidScale) {} + TransformNode::TransformNode(const TransformNode&) = default; + TransformNode& TransformNode::operator=(const TransformNode&) = default; #if DCHECK_IS_ON() -bool TransformNode::operator==(const TransformNode& other) const = default; +bool TransformNode::operator==(const TransformNode& other) const { + return id == other.id && parent_id == other.parent_id && + parent_frame_id == other.parent_frame_id && + element_id == other.element_id && local == other.local && + origin == other.origin && post_translation == other.post_translation && + to_parent == other.to_parent && + sorting_context_id == other.sorting_context_id && + needs_local_transform_update == other.needs_local_transform_update && + node_and_ancestors_are_animated_or_invertible == + other.node_and_ancestors_are_animated_or_invertible && + is_invertible == other.is_invertible && + ancestors_are_invertible == other.ancestors_are_invertible && + has_potential_animation == other.has_potential_animation && + is_currently_animating == other.is_currently_animating && + to_screen_is_potentially_animated == + other.to_screen_is_potentially_animated && + flattens_inherited_transform == other.flattens_inherited_transform && + node_and_ancestors_are_flat == other.node_and_ancestors_are_flat && + scrolls == other.scrolls && + should_undo_overscroll == other.should_undo_overscroll && + should_be_snapped == other.should_be_snapped && + moved_by_outer_viewport_bounds_delta_y == + other.moved_by_outer_viewport_bounds_delta_y && + in_subtree_of_page_scale_layer == + other.in_subtree_of_page_scale_layer && + delegates_to_parent_for_backface == + other.delegates_to_parent_for_backface && + will_change_transform == other.will_change_transform && + node_or_ancestors_will_change_transform == + other.node_or_ancestors_will_change_transform && + transform_changed == other.transform_changed && + scroll_offset == other.scroll_offset && + snap_amount == other.snap_amount && + maximum_animation_scale == other.maximum_animation_scale && + visible_frame_element_id == other.visible_frame_element_id; +} #endif // DCHECK_IS_ON() void TransformNode::AsValueInto(base::trace_event::TracedValue* value) const { @@ -35,11 +97,18 @@ MathUtil::AddToTracedValue("snap_amount", snap_amount, value); } -TransformCachedNodeData::TransformCachedNodeData() = default; +TransformCachedNodeData::TransformCachedNodeData() + : is_showing_backface(false) {} + TransformCachedNodeData::TransformCachedNodeData( const TransformCachedNodeData& other) = default; + TransformCachedNodeData::~TransformCachedNodeData() = default; + bool TransformCachedNodeData::operator==( - const TransformCachedNodeData& other) const = default; + const TransformCachedNodeData& other) const { + return from_screen == other.from_screen && to_screen == other.to_screen && + is_showing_backface == other.is_showing_backface; +} } // namespace cc
diff --git a/cc/trees/transform_node.h b/cc/trees/transform_node.h index 8292af42..467f8db 100644 --- a/cc/trees/transform_node.h +++ b/cc/trees/transform_node.h
@@ -7,8 +7,6 @@ #include "cc/cc_export.h" #include "cc/paint/element_id.h" -#include "cc/trees/mutator_host.h" -#include "cc/trees/property_ids.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/transform.h" @@ -28,12 +26,12 @@ TransformNode& operator=(const TransformNode&); // The node index of this node in the transform tree node vector. - int id = kInvalidPropertyNodeId; + int id; // The node index of the parent node in the transform tree node vector. - int parent_id = kInvalidPropertyNodeId; + int parent_id; // The node index of the nearest parent frame node in the transform tree node // vector. - int parent_frame_id = kInvalidPropertyNodeId; + int parent_frame_id; ElementId element_id; @@ -50,80 +48,80 @@ gfx::Transform to_parent; // This is the node which defines the sticky position constraints for this - // transform node. - int sticky_position_constraint_id = kInvalidPropertyNodeId; + // transform node. -1 indicates there are no sticky position constraints. + int sticky_position_constraint_id; // This is the data of the scroll container of the anchor node specified by - // the `anchor-scroll` property. - int anchor_scroll_containers_data_id = kInvalidPropertyNodeId; + // the `anchor-scroll` property. -1 indicates there is no such node. + int anchor_scroll_containers_data_id; // This id determines which 3d rendering context the node is in. 0 is a // special value and indicates that the node is not in any 3d rendering // context. - int sorting_context_id = 0; + int sorting_context_id; // True if |TransformTree::UpdateLocalTransform| needs to be called which // will update |to_parent|. - bool needs_local_transform_update : 1 = true; + bool needs_local_transform_update : 1; // Whether this node or any ancestor has a potentially running // (i.e., irrespective of exact timeline) transform animation or an // invertible transform. - bool node_and_ancestors_are_animated_or_invertible : 1 = true; + bool node_and_ancestors_are_animated_or_invertible : 1; - bool is_invertible : 1 = true; + bool is_invertible : 1; // Whether the transform from this node to the screen is // invertible. - bool ancestors_are_invertible : 1 = true; + bool ancestors_are_invertible : 1; // Whether this node has a potentially running (i.e., irrespective // of exact timeline) transform animation. - bool has_potential_animation : 1 = false; + bool has_potential_animation : 1; // Whether this node has a currently running transform animation. - bool is_currently_animating : 1 = false; + bool is_currently_animating : 1; // Whether this node *or an ancestor* has a potentially running // (i.e., irrespective of exact timeline) transform // animation. - bool to_screen_is_potentially_animated : 1 = false; + bool to_screen_is_potentially_animated : 1; // Flattening, when needed, is only applied to a node's inherited transform, // never to its local transform. It's true by default. - bool flattens_inherited_transform : 1 = true; + bool flattens_inherited_transform : 1; // This is true if the to_parent transform at every node on the path to the // root is flat. - bool node_and_ancestors_are_flat : 1 = true; + bool node_and_ancestors_are_flat : 1; - bool scrolls : 1 = false; + bool scrolls : 1; - bool should_undo_overscroll : 1 = false; + bool should_undo_overscroll : 1; - bool should_be_snapped : 1 = false; + bool should_be_snapped : 1; // Used by the compositor to determine which layers need to be repositioned by // the compositor as a result of browser controls expanding/contracting the // outer viewport size before Blink repositions the fixed layers. - bool moved_by_outer_viewport_bounds_delta_y : 1 = false; + bool moved_by_outer_viewport_bounds_delta_y : 1; // Layer scale factor is used as a fallback when we either cannot adjust // raster scale or if the raster scale cannot be extracted from the screen // space transform. For layers in the subtree of the page scale layer, the // layer scale factor should include the page scale factor. - bool in_subtree_of_page_scale_layer : 1 = false; + bool in_subtree_of_page_scale_layer : 1; // We need to track changes to to_screen transform to compute the damage rect. - bool transform_changed : 1 = false; + bool transform_changed : 1; // Whether the parent transform node should be used for checking backface // visibility, not this transform one. - bool delegates_to_parent_for_backface : 1 = false; + bool delegates_to_parent_for_backface : 1; // Set to true, if the compositing reason is will-change:transform, scale, // rotate, or translate (for the CSS property that created this node). - bool will_change_transform : 1 = false; + bool will_change_transform : 1; // Set to true, if the node or it's parent |will_change_transform| is true. - bool node_or_ancestors_will_change_transform : 1 = false; + bool node_or_ancestors_will_change_transform : 1; gfx::PointF scroll_offset; @@ -135,7 +133,7 @@ // From MutatorHost::GetMaximuimAnimationScale(). Updated by // PropertyTrees::MaximumAnimationScaleChanged() and // LayerTreeImpl::UpdateTransformAnimation(). - float maximum_animation_scale = kInvalidScale; + float maximum_animation_scale; // Set to the element ID of containing document if this transform node is the // root of a visible frame subtree. @@ -162,7 +160,7 @@ gfx::Transform from_screen; gfx::Transform to_screen; - bool is_showing_backface = false; + bool is_showing_backface : 1; bool operator==(const TransformCachedNodeData& other) const; };
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 30d92f4..8c6760b 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -504,6 +504,7 @@ "java/src/org/chromium/chrome/browser/customtabs/features/sessionrestore/TabFreezer.java", "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrandingSecurityButtonAnimationDelegate.java", "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabBrowserControlsVisibilityDelegate.java", + "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabCaptureStateToken.java", "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java", "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java", "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java index 4f24c76..96dca3d1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
@@ -6,14 +6,13 @@ import android.content.Context; import android.text.TextUtils; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; @@ -26,21 +25,16 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.sync.SyncService; -import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.signin.PersonalizedSigninPromoView; import org.chromium.chrome.browser.ui.signin.SyncPromoController.SyncPromoState; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; import org.chromium.components.bookmarks.BookmarkType; -import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool; import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter; import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightParams; import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightShape; import org.chromium.components.feature_engagement.EventConstants; -import org.chromium.components.image_fetcher.ImageFetcher; -import org.chromium.components.image_fetcher.ImageFetcherConfig; -import org.chromium.components.image_fetcher.ImageFetcherFactory; import org.chromium.components.power_bookmarks.PowerBookmarkMeta; import java.util.ArrayList; @@ -54,9 +48,19 @@ private static final int MAXIMUM_NUMBER_OF_SEARCH_RESULTS = 500; private static final String EMPTY_QUERY = null; - private final ImageFetcher mImageFetcher; + /** Abstraction around how to build type specific {@link View}s. */ + interface ViewFactory { + /** + * @param parent The parent to which the new {@link View} will be added as a child. + * @param viewType The type of row being created. + * @return A new View that can be added to the view hierarchy. + */ + View buildView(@NonNull ViewGroup parent, @ViewType int viewType); + } + private final List<BookmarkId> mTopLevelFolders = new ArrayList<>(); - private final SnackbarManager mSnackbarManager; + private final Profile mProfile; + private final SyncService mSyncService; // There can only be one promo header at a time. This takes on one of the values: // ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.SYNC_PROMO, or ViewType.INVALID. @@ -64,9 +68,9 @@ private int mPromoHeaderType = ViewType.INVALID; private BookmarkDelegate mDelegate; private BookmarkPromoHeader mPromoHeaderManager; + private ViewFactory mViewFactory; private String mSearchText; private BookmarkId mCurrentFolder; - private SyncService mSyncService; // Keep track of the currently highlighted bookmark - used for "show in folder" action. private BookmarkId mHighlightedBookmark; @@ -125,16 +129,11 @@ } }; - BookmarkItemsAdapter(Context context, SnackbarManager snackbarManager) { + BookmarkItemsAdapter(Context context, Profile profile) { super(context); + mProfile = profile; mSyncService = SyncService.get(); mSyncService.addSyncStateChangedListener(this); - - mImageFetcher = - ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.IN_MEMORY_WITH_DISK_CACHE, - Profile.getLastUsedRegularProfile().getProfileKey(), - GlobalDiscardableReferencePool.getReferencePool()); - mSnackbarManager = snackbarManager; } /** @@ -210,63 +209,9 @@ return entry.getViewType(); } - private ViewHolder createViewHolderHelper(ViewGroup parent, @LayoutRes int layoutId) { - // create the row associated with this adapter - ViewGroup row = (ViewGroup) LayoutInflater.from(parent.getContext()) - .inflate(layoutId, parent, false); - - // ViewHolder is abstract and it cannot be instantiated directly. - ViewHolder holder = new ViewHolder(row) {}; - ((BookmarkRow) row).onDelegateInitialized(mDelegate); - return holder; - } - @Override public ViewHolder onCreateViewHolder(ViewGroup parent, @ViewType int viewType) { - assert mDelegate != null; - - // The shopping-specific bookmark row is only shown with the visual refresh. When there's a - // mismatch, the ViewType is downgraded to ViewType.BOOKMARK. - if (viewType == ViewType.SHOPPING_POWER_BOOKMARK - && !BookmarkFeatures.isBookmarksVisualRefreshEnabled()) { - viewType = ViewType.BOOKMARK; - } - - switch (viewType) { - case ViewType.PERSONALIZED_SIGNIN_PROMO: - // fall through - case ViewType.PERSONALIZED_SYNC_PROMO: - return mPromoHeaderManager.createPersonalizedSigninAndSyncPromoHolder(parent); - case ViewType.SYNC_PROMO: - return mPromoHeaderManager.createSyncPromoHolder(parent); - case ViewType.SECTION_HEADER: - return createSectionHeaderViewHolder(parent, viewType); - case ViewType.FOLDER: - return createViewHolderHelper(parent, R.layout.bookmark_folder_row); - case ViewType.BOOKMARK: - return createViewHolderHelper(parent, R.layout.bookmark_item_row); - case ViewType.SHOPPING_POWER_BOOKMARK: - ViewHolder vh = null; - if (BookmarkFeatures.isBookmarksVisualRefreshEnabled()) { - vh = createViewHolderHelper(parent, R.layout.power_bookmark_shopping_item_row); - ((PowerBookmarkShoppingItemRow) vh.itemView) - .init(mImageFetcher, mDelegate.getModel(), mSnackbarManager); - } else { - vh = createViewHolderHelper(parent, R.layout.bookmark_item_row); - } - return vh; - case ViewType.DIVIDER: - return new ViewHolder( - LayoutInflater.from(parent.getContext()) - .inflate(R.layout.horizontal_divider, parent, false)) {}; - case ViewType.SHOPPING_FILTER: - return new ViewHolder( - LayoutInflater.from(parent.getContext()) - .inflate(R.layout.shopping_filter_row, parent, false)) {}; - default: - assert false; - return null; - } + return new ViewHolder(mViewFactory.buildView(parent, viewType)) {}; } @Override @@ -274,8 +219,7 @@ if (holder.getItemViewType() == ViewType.PERSONALIZED_SIGNIN_PROMO || holder.getItemViewType() == ViewType.PERSONALIZED_SYNC_PROMO) { PersonalizedSigninPromoView view = - (PersonalizedSigninPromoView) holder.itemView.findViewById( - R.id.signin_promo_view_container); + holder.itemView.findViewById(R.id.signin_promo_view_container); mPromoHeaderManager.setUpSyncPromoView(view); } else if (holder.getItemViewType() == ViewType.SECTION_HEADER) { bindSectionHeaderViewHolder(holder.itemView, getItemByPosition(position)); @@ -304,19 +248,10 @@ } else if (holder.getItemViewType() == ViewType.SHOPPING_FILTER) { LinearLayout layout = ((LinearLayout) holder.itemView); layout.setClickable(true); - layout.setOnClickListener( - (view) -> { mDelegate.openFolder(BookmarkId.SHOPPING_FOLDER); }); + layout.setOnClickListener((view) -> mDelegate.openFolder(BookmarkId.SHOPPING_FOLDER)); } } - private ViewHolder createSectionHeaderViewHolder(ViewGroup parent, @ViewType int viewType) { - ViewGroup sectionHeader = (ViewGroup) LayoutInflater.from(parent.getContext()) - .inflate(R.layout.bookmark_section_header, parent, false); - - // ViewHolder is abstract and it cannot be instantiated directly. - return new ViewHolder(sectionHeader) {}; - } - private void bindSectionHeaderViewHolder(View view, BookmarkListEntry listItem) { TextView title = view.findViewById(R.id.title); title.setText(listItem.getHeaderTitle()); @@ -344,8 +279,9 @@ * Sets the delegate to use to handle UI actions related to this adapter. * * @param delegate A {@link BookmarkDelegate} instance to handle all backend interaction. + * @param viewFactory An object to create {@link View}s for each item/row. */ - void onBookmarkDelegateInitialized(BookmarkDelegate delegate) { + void onBookmarkDelegateInitialized(BookmarkDelegate delegate, ViewFactory viewFactory) { mDelegate = delegate; mDelegate.addUIObserver(this); mDelegate.getModel().addObserver(mBookmarkModelObserver); @@ -359,6 +295,8 @@ mPromoHeaderManager = new BookmarkPromoHeader(mContext, promoHeaderChangeAction); populateTopLevelFoldersList(); + mViewFactory = viewFactory; + mElements = new ArrayList<>(); setDragStateDelegate(delegate.getDragStateDelegate()); notifyDataSetChanged(); @@ -394,8 +332,8 @@ mDelegate.getSelectableListLayout().setEmptyViewText( R.string.tracked_products_empty_list_title); } else if (folder.getType() == BookmarkType.READING_LIST) { - TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile()) - .notifyEvent(EventConstants.READ_LATER_BOOKMARK_FOLDER_OPENED); + TrackerFactory.getTrackerForProfile(mProfile).notifyEvent( + EventConstants.READ_LATER_BOOKMARK_FOLDER_OPENED); mDelegate.getSelectableListLayout().setEmptyViewText( R.string.reading_list_empty_list_title); } else { @@ -610,6 +548,10 @@ return entry.getBookmarkItem().getId(); } + public BookmarkPromoHeader getPromoHeaderManager() { + return mPromoHeaderManager; + } + private boolean hasPromoHeader() { return mPromoHeaderType != ViewType.INVALID; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java index e920c07..7850f13 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java
@@ -27,7 +27,8 @@ @Retention(RetentionPolicy.SOURCE) @IntDef({ViewType.INVALID, ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.PERSONALIZED_SYNC_PROMO, ViewType.SYNC_PROMO, ViewType.FOLDER, ViewType.BOOKMARK, ViewType.DIVIDER, - ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST}) + ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST, + ViewType.SHOPPING_FILTER}) public @interface ViewType { int INVALID = -1; int PERSONALIZED_SIGNIN_PROMO = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java index ea3cc98f..1b87ac43 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -92,9 +92,10 @@ private boolean mIsIncognito; private boolean mIsDestroyed; - private BookmarkItemsAdapter mAdapter; - private BookmarkDragStateDelegate mDragStateDelegate; - private AdapterDataObserver mAdapterDataObserver; + private final BookmarkItemsAdapter mAdapter; + private final BookmarkManagerCoordinator mBookmarkManagerCoordinator; + private final BookmarkDragStateDelegate mDragStateDelegate; + private final AdapterDataObserver mAdapterDataObserver; private final BookmarkOpener mBookmarkOpener; private final ObservableSupplierImpl<Boolean> mBackPressStateSupplier = @@ -239,7 +240,8 @@ mSelectableListLayout.getHandleBackPressChangedSupplier().addObserver( (x) -> onBackPressStateChanged()); - mAdapter = new BookmarkItemsAdapter(mContext, snackbarManager); + mAdapter = new BookmarkItemsAdapter(mContext, profile); + mBookmarkManagerCoordinator = new BookmarkManagerCoordinator(profile, snackbarManager); mAdapterDataObserver = new AdapterDataObserver() { @Override @@ -270,7 +272,10 @@ if (!sPreventLoadingForTesting) { Runnable modelLoadedRunnable = () -> { mDragStateDelegate.onBookmarkDelegateInitialized(BookmarkManager.this); - mAdapter.onBookmarkDelegateInitialized(BookmarkManager.this); + mAdapter.onBookmarkDelegateInitialized( + BookmarkManager.this, mBookmarkManagerCoordinator::createView); + mBookmarkManagerCoordinator.onBookmarkDelegateInitialized( + this, mAdapter.getPromoHeaderManager()); mToolbar.onBookmarkDelegateInitialized(BookmarkManager.this); mAdapter.addDragListener(mToolbar);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java index e335f11..df8f710 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
@@ -4,5 +4,138 @@ package org.chromium.chrome.browser.bookmarks; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; +import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool; +import org.chromium.components.image_fetcher.ImageFetcher; +import org.chromium.components.image_fetcher.ImageFetcherConfig; +import org.chromium.components.image_fetcher.ImageFetcherFactory; + /** Responsible for setting up sub-components and routing incoming/outgoing signals */ -public class BookmarkManagerCoordinator {} +public class BookmarkManagerCoordinator { + private final @NonNull ImageFetcher mImageFetcher; + private final @NonNull SnackbarManager mSnackbarManager; + + private @Nullable BookmarkPromoHeader mPromoHeaderManager; + private @Nullable BookmarkDelegate mDelegate; + + /** + * Creates a partially initialized instance, needs {@link onBookmarkDelegateInitialized}. + * @param profile Profile instance corresponding to the active profile. + * @param snackbarManager Allows control over the app snackbar. + */ + public BookmarkManagerCoordinator( + @NonNull Profile profile, @NonNull SnackbarManager snackbarManager) { + mImageFetcher = + ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.IN_MEMORY_WITH_DISK_CACHE, + profile.getProfileKey(), GlobalDiscardableReferencePool.getReferencePool()); + mSnackbarManager = snackbarManager; + } + + /** + * Fully initializes, ready to be used after this is called. + * @param delegate A {@link BookmarkDelegate} instance to handle all backend interaction. + * @param bookmarkPromoHeader Used to show the signin promo header. + */ + public void onBookmarkDelegateInitialized( + @NonNull BookmarkDelegate delegate, @NonNull BookmarkPromoHeader bookmarkPromoHeader) { + mDelegate = delegate; + mPromoHeaderManager = bookmarkPromoHeader; + } + + /** + * Should only be called after fully initialized. + * @param parent The parent to which the new {@link View} will be added as a child. + * @param viewType The type of row being created. + * @return A new View that can be added to the view hierarchy. + */ + public View createView(@NonNull ViewGroup parent, @ViewType int viewType) { + assert mDelegate != null; + + // The shopping-specific bookmark row is only shown with the visual refresh. When + // there's a mismatch, the ViewType is downgraded to ViewType.BOOKMARK. + if (viewType == ViewType.SHOPPING_POWER_BOOKMARK + && !BookmarkFeatures.isBookmarksVisualRefreshEnabled()) { + viewType = ViewType.BOOKMARK; + } + + switch (viewType) { + case ViewType.PERSONALIZED_SIGNIN_PROMO: + case ViewType.PERSONALIZED_SYNC_PROMO: + return buildPersonalizedPromoView(parent); + case ViewType.SYNC_PROMO: + return buildLegacyPromoView(parent); + case ViewType.SECTION_HEADER: + return buildSectionHeaderView(parent); + case ViewType.FOLDER: + return buildBookmarkFolderView(parent); + case ViewType.BOOKMARK: + return buildBookmarkItemView(parent); + case ViewType.SHOPPING_POWER_BOOKMARK: + return buildShoppingItemView(parent); + case ViewType.DIVIDER: + return buildDividerView(parent); + case ViewType.SHOPPING_FILTER: + return buildShoppingFilterView(parent); + default: + assert false; + return null; + } + } + + private View buildPersonalizedPromoView(ViewGroup parent) { + return mPromoHeaderManager.createPersonalizedSigninAndSyncPromoHolder(parent); + } + + private View buildLegacyPromoView(ViewGroup parent) { + return mPromoHeaderManager.createSyncPromoHolder(parent); + } + + private View buildSectionHeaderView(ViewGroup parent) { + return inflate(parent, org.chromium.chrome.R.layout.bookmark_section_header); + } + + private View buildBookmarkFolderView(ViewGroup parent) { + return inflateBookmarkRow(parent, org.chromium.chrome.R.layout.bookmark_folder_row); + } + + private View buildBookmarkItemView(ViewGroup parent) { + return inflateBookmarkRow(parent, org.chromium.chrome.R.layout.bookmark_item_row); + } + + private View buildShoppingItemView(ViewGroup parent) { + PowerBookmarkShoppingItemRow row = (PowerBookmarkShoppingItemRow) inflateBookmarkRow( + parent, org.chromium.chrome.R.layout.power_bookmark_shopping_item_row); + row.init(mImageFetcher, mDelegate.getModel(), mSnackbarManager); + return row; + } + + private View buildDividerView(ViewGroup parent) { + return inflate(parent, org.chromium.chrome.R.layout.horizontal_divider); + } + + private View buildShoppingFilterView(ViewGroup parent) { + return inflate(parent, org.chromium.chrome.R.layout.shopping_filter_row); + } + + private View inflate(ViewGroup parent, @LayoutRes int layoutId) { + Context context = parent.getContext(); + return LayoutInflater.from(context).inflate(layoutId, parent, false); + } + + private View inflateBookmarkRow(ViewGroup parent, @LayoutRes int layoutId) { + BookmarkRow row = (BookmarkRow) inflate(parent, layoutId); + (row).onDelegateInitialized(mDelegate); + return row; + } +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java index b3adb6a4..692d3a8a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java
@@ -11,8 +11,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.ViewHolder; import org.chromium.chrome.R; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; @@ -108,28 +106,15 @@ return mPromoState; } - /** - * @return Personalized signin promo header {@link ViewHolder} instance that can be used with - * {@link RecyclerView}. - */ - ViewHolder createPersonalizedSigninAndSyncPromoHolder(ViewGroup parent) { - View view = LayoutInflater.from(mContext).inflate( + /** Returns personalized signin promo header {@link View}. */ + View createPersonalizedSigninAndSyncPromoHolder(ViewGroup parent) { + return LayoutInflater.from(mContext).inflate( R.layout.sync_promo_view_bookmarks, parent, false); - - // ViewHolder is abstract and it cannot be instantiated directly. - return new ViewHolder(view) {}; } - /** - * @return Sync promo header {@link ViewHolder} instance that can be used with - * {@link RecyclerView}. - */ - ViewHolder createSyncPromoHolder(ViewGroup parent) { - LegacySyncPromoView view = - LegacySyncPromoView.create(parent, SigninAccessPoint.BOOKMARK_MANAGER); - - // ViewHolder is abstract and it cannot be instantiated directly. - return new ViewHolder(view) {}; + /** Returns sync promo header {@link View}. */ + View createSyncPromoHolder(ViewGroup parent) { + return LegacySyncPromoView.create(parent, SigninAccessPoint.BOOKMARK_MANAGER); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java index 80c731e..e5afbb1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -298,7 +298,8 @@ mActivityLifecycleDispatcher, mFullscreenManager, DeviceFormFactor.isWindowOnTablet(mWindowAndroid), intentDataProvider.canInteractWithBackground(), - intentDataProvider.showSideSheetMaximizeButton()); + intentDataProvider.showSideSheetMaximizeButton(), + intentDataProvider.getActivitySideSheetDecorationType()); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java index 7f39edaa..6f7bc8b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -262,6 +262,13 @@ "androidx.browser.customtabs.extra.ACTIVITY_SIDE_SHEET_BREAKPOINT_DP"; /** + * Extra that, if set, allows you to set how you want to distinguish the PCCT side sheet from + * the rest of the display. Options include shadow, a divider line, or no decoration. + */ + public static final String EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE = + "androidx.browser.customtabs.extra.ACTIVITY_SIDE_SHEET_DECORATION_TYPE"; + + /** * Extra that, if set, makes the toolbar's top corner radii to be x pixels. This will only have * effect if the custom tab is behaving as a bottom sheet. Currently, this is capped at 16dp. * TODO(jinsukkim): Deprecate this. @@ -322,6 +329,8 @@ private List<CustomButtonParams> mToolbarButtons = new ArrayList<>(1); private List<CustomButtonParams> mBottombarButtons = new ArrayList<>(2); private RemoteViews mRemoteViews; + @SideSheetDecorationType + private int mSideSheetDecorationType; private int[] mClickableViewIds; private PendingIntent mRemoteViewsPendingIntent; private PendingIntent mSecondaryToolbarSwipeUpPendingIntent; @@ -425,6 +434,15 @@ return breakPointDp < 0 ? DEFAULT_BREAKPOINT_DP : breakPointDp; } + private static int getActivitySideSheetDecorationTypeFromIntent(Intent intent) { + int decorationType = + IntentUtils.safeGetIntExtra(intent, EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT); + return decorationType < 0 || decorationType > ACTIVITY_SIDE_SHEET_DECORATION_TYPE_MAX + ? ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT + : decorationType; + } + /** * Get the package name from {@link #getReferrerUriString(Activity)}. If the referrer format * is invalid, return an empty string. @@ -558,6 +576,7 @@ int backgroundInteractBehavior = IntentUtils.safeGetIntExtra( intent, EXTRA_ENABLE_BACKGROUND_INTERACTION, BACKGROUND_INTERACT_DEFAULT); mInteractWithBackground = backgroundInteractBehavior != BACKGROUND_INTERACT_OFF; + mSideSheetDecorationType = getActivitySideSheetDecorationTypeFromIntent(intent); logCustomTabFeatures(intent, colorScheme, usingDynamicFeatures); } @@ -839,6 +858,10 @@ intent, CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP)) { featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP); } + if (IntentUtils.safeHasExtra(intent, + CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE)) { + featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE); + } if (mEnableEmbeddedMediaExperience) { featureUsage.log(CustomTabsFeature.EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE); } @@ -1209,6 +1232,12 @@ return version; } + @SideSheetDecorationType + @Override + public int getActivitySideSheetDecorationType() { + return mSideSheetDecorationType; + } + @Override @Nullable public int[] getGsaExperimentIds() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java index 72d4a21..c1524418 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java
@@ -51,7 +51,8 @@ CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP, CustomTabsFeature.EXTRA_INITIAL_ACTIVITY_WIDTH_PX, CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION, - CustomTabsFeature.EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION, CustomTabsFeature.COUNT}) + CustomTabsFeature.EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION, + CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, CustomTabsFeature.COUNT}) @Retention(RetentionPolicy.SOURCE) public @interface CustomTabsFeature { /** Special enum for the start of a session. */ @@ -104,9 +105,10 @@ int EXTRA_INITIAL_ACTIVITY_WIDTH_PX = 46; int EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION = 47; int EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION = 48; + int EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE = 49; /** Total count of entries. */ - int COUNT = 49; + int COUNT = 50; } // Whether flag-enabled or not.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/CustomTabHeightStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/CustomTabHeightStrategy.java index 13df306..c19849e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/CustomTabHeightStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/CustomTabHeightStrategy.java
@@ -32,7 +32,8 @@ @Px int initialWidth, int breakPointDp, boolean isPartialCustomTabFixedHeight, CustomTabsConnection connection, @Nullable CustomTabsSessionToken session, ActivityLifecycleDispatcher lifecycleDispatcher, FullscreenManager fullscreenManager, - boolean isTablet, boolean interactWithBackground, boolean showMaximizeButton) { + boolean isTablet, boolean interactWithBackground, boolean showMaximizeButton, + int decorationType) { if (initialHeight <= 0 && (!ChromeFeatureList.sCctResizableSideSheet.isEnabled() || initialWidth <= 0)) { return new CustomTabHeightStrategy(); @@ -44,7 +45,7 @@ (height, width) -> connection.onResized(session, height, width), lifecycleDispatcher, fullscreenManager, isTablet, interactWithBackground, - showMaximizeButton); + showMaximizeButton, decorationType); } else { return new PartialCustomTabBottomSheetStrategy(activity, initialHeight, isPartialCustomTabFixedHeight,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManager.java index bac9839..70cc86b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManager.java
@@ -32,6 +32,7 @@ private final Activity mActivity; private final int mBreakPointDp; + private final int mDecorationType; private final @Px int mUnclampedInitialHeight; private final @Px int mUnclampedInitialWidth; private final boolean mIsFixedHeight; @@ -62,7 +63,7 @@ @Px int initialWidth, int breakPointDp, boolean isFixedHeight, OnResizedCallback onResizedCallback, ActivityLifecycleDispatcher lifecycleDispatcher, FullscreenManager fullscreenManager, boolean isTablet, boolean interactWithBackground, - boolean showMaximizeButton) { + boolean showMaximizeButton, int decorationType) { mActivity = activity; mUnclampedInitialHeight = initialHeight; mUnclampedInitialWidth = initialWidth; @@ -73,6 +74,7 @@ mIsTablet = isTablet; mInteractWithBackground = interactWithBackground; mShowMaximizeButton = showMaximizeButton; + mDecorationType = decorationType; mActivityLifecycleDispatcher = lifecycleDispatcher; lifecycleDispatcher.register(this); @@ -233,7 +235,7 @@ case PartialCustomTabType.SIDE_SHEET: { return new PartialCustomTabSideSheetStrategy(mActivity, mUnclampedInitialWidth, mOnResizedCallback, mFullscreenManager, mIsTablet, mInteractWithBackground, - mShowMaximizeButton, maximized, mHandleStrategyFactory); + mShowMaximizeButton, maximized, mHandleStrategyFactory, mDecorationType); } case PartialCustomTabType.FULL_SIZE: { return new PartialCustomTabFullSizeStrategy(mActivity, mOnResizedCallback,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java index c853149..10faad09 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
@@ -6,6 +6,9 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER; +import static org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE; + import android.animation.ValueAnimator; import android.app.Activity; import android.graphics.drawable.GradientDrawable; @@ -33,12 +36,13 @@ private final boolean mShowMaximizeButton; private boolean mIsMaximized; + private int mDecorationType; public PartialCustomTabSideSheetStrategy(Activity activity, @Px int initialWidth, CustomTabHeightStrategy.OnResizedCallback onResizedCallback, FullscreenManager fullscreenManager, boolean isTablet, boolean interactWithBackground, boolean showMaximizeButton, boolean startMaximized, - PartialCustomTabHandleStrategyFactory handleStrategyFactory) { + PartialCustomTabHandleStrategyFactory handleStrategyFactory, int decorationType) { super(activity, onResizedCallback, fullscreenManager, isTablet, interactWithBackground, handleStrategyFactory); @@ -46,6 +50,7 @@ mShowMaximizeButton = showMaximizeButton; mPositionUpdater = this::updatePosition; mIsMaximized = startMaximized; + mDecorationType = decorationType; setupAnimator(); } @@ -104,9 +109,10 @@ // For smooth animation, make the window full-width and then translate it // rather than resizing the window itself during the animation. - setWindowWidth(mVersionCompat.getDisplayWidth()); + int displayWidth = mVersionCompat.getDisplayWidth(); + setWindowWidth(displayWidth); int start = mActivity.getWindow().getAttributes().x; - int end = mIsMaximized ? 0 : mVersionCompat.getDisplayWidth() - mUnclampedInitialWidth; + int end = mIsMaximized ? 0 : displayWidth - calculateWidth(mUnclampedInitialWidth); startAnimation(start, end, this::onMaximizeProgress, this::onMaximizeEnd); return mIsMaximized; } @@ -186,7 +192,8 @@ @Override protected boolean shouldHaveNoShadowOffset() { // We remove shadow in maximized mode. - return isMaximized(); + return isMaximized() || mDecorationType == ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE + || mDecorationType == ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabCaptureStateToken.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabCaptureStateToken.java new file mode 100644 index 0000000..ac64a66 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabCaptureStateToken.java
@@ -0,0 +1,59 @@ +// 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. + +package org.chromium.chrome.browser.customtabs.features.toolbar; + +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; + +import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotDifference; + +import java.util.Objects; + +/** + * The idea of this class is to hold all of the properties that materially change the way the + * toolbar looks. If two tokens are identical (no difference is found), then there should be + * no reason to perform a bitmap capture. + */ +class CustomTabCaptureStateToken { + private final String mUrl; + private final String mTitle; + private final @ColorInt int mBackgroundColor; + private final @DrawableRes int mSecurityIconRes; + private @Nullable final Object mAnimationToken; + public CustomTabCaptureStateToken(String url, String title, @ColorInt int backgroundColor, + @DrawableRes int securityIconRes, boolean isInAnimation) { + mUrl = url; + mTitle = title; + mBackgroundColor = backgroundColor; + mSecurityIconRes = securityIconRes; + // When animations are in progress, tokens should never be equal. Object should use + // reference equality, resulting in a difference unless both are null or the objects + // are actually the same object. + mAnimationToken = isInAnimation ? new Object() : null; + } + + /** + * Compares two tokens and looks for any difference. If multiple are present only one will + * be returned. ToolbarSnapshotDifference.NONE indicates the two tokens are the same. + */ + public @ToolbarSnapshotDifference int getAnyDifference(CustomTabCaptureStateToken that) { + if (that == null) { + return ToolbarSnapshotDifference.NULL; + } else if (!Objects.equals(mUrl, that.mUrl)) { + return ToolbarSnapshotDifference.URL_TEXT; + } else if (!Objects.equals(mTitle, that.mTitle)) { + return ToolbarSnapshotDifference.TITLE_TEXT; + } else if (mBackgroundColor != that.mBackgroundColor) { + return ToolbarSnapshotDifference.TINT; + } else if (mSecurityIconRes != that.mSecurityIconRes) { + return ToolbarSnapshotDifference.SECURITY_ICON; + } else if (!Objects.equals(mAnimationToken, that.mAnimationToken)) { + return ToolbarSnapshotDifference.CCT_ANIMATION; + } else { + return ToolbarSnapshotDifference.NONE; + } + } +} \ No newline at end of file
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 26d3aa17..4ad6356 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
@@ -80,7 +80,7 @@ import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason; import org.chromium.chrome.browser.toolbar.top.ToolbarLayout; import org.chromium.chrome.browser.toolbar.top.ToolbarPhone; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; +import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotDifference; import org.chromium.chrome.browser.ui.native_page.NativePage; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.components.browser_ui.styles.ChromeColors; @@ -101,8 +101,6 @@ import org.chromium.ui.widget.Toast; import org.chromium.url.GURL; -import java.util.Objects; - /** * The Toolbar layout to be used for a custom tab. This is used for both phone and tablet UIs. */ @@ -128,7 +126,7 @@ private final CustomTabLocationBar mLocationBar = new CustomTabLocationBar(); private LocationBarModel mLocationBarModel; private BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate; - private @Nullable CaptureStateToken mLastCaptureStateToken; + private @Nullable CustomTabCaptureStateToken mLastCustomTabCaptureStateToken; /** * Whether to use the toolbar as handle to resize the Window height. @@ -724,9 +722,9 @@ if (ToolbarFeatures.shouldBlockCapturesForAblation()) { return CaptureReadinessResult.notReady(TopToolbarBlockCaptureReason.SCROLL_ABLATION); } else if (ToolbarFeatures.shouldSuppressCaptures()) { - CaptureStateToken currentToken = generateCaptureStateToken(); + CustomTabCaptureStateToken currentToken = generateCaptureStateToken(); final @ToolbarSnapshotDifference int difference = - currentToken.getAnyDifference(mLastCaptureStateToken); + currentToken.getAnyDifference(mLastCustomTabCaptureStateToken); if (difference == ToolbarSnapshotDifference.NONE) { return CaptureReadinessResult.notReady(TopToolbarBlockCaptureReason.SNAPSHOT_SAME); } else { @@ -740,61 +738,15 @@ @Override public void setTextureCaptureMode(boolean textureMode) { if (textureMode) { - mLastCaptureStateToken = generateCaptureStateToken(); + mLastCustomTabCaptureStateToken = generateCaptureStateToken(); } } - /** - * The idea of this class is to hold all of the properties that materially change the way the - * toolbar looks. If two tokens are identical (no difference is found), then there should be - * no reason to perform a bitmap capture. - */ - private static class CaptureStateToken { - private final String mUrl; - private final String mTitle; - private final @ColorInt int mBackgroundColor; - private final @DrawableRes int mSecurityIconRes; - private @Nullable final Object mAnimationToken; - public CaptureStateToken(String url, String title, @ColorInt int backgroundColor, - @DrawableRes int securityIconRes, boolean isInAnimation) { - mUrl = url; - mTitle = title; - mBackgroundColor = backgroundColor; - mSecurityIconRes = securityIconRes; - // When animations are in progress, tokens should never be equal. Object should use - // reference equality, resulting in a difference unless both are null or the objects - // are actually the same object. - mAnimationToken = isInAnimation ? new Object() : null; - } - - /** - * Compares two tokens and looks for any difference. If multiple are present only one will - * be returned. ToolbarSnapshotDifference.NONE indicates the two tokens are the same. - */ - public @ToolbarSnapshotDifference int getAnyDifference(CaptureStateToken that) { - if (that == null) { - return ToolbarSnapshotDifference.NULL; - } else if (!Objects.equals(mUrl, that.mUrl)) { - return ToolbarSnapshotDifference.URL_TEXT; - } else if (!Objects.equals(mTitle, that.mTitle)) { - return ToolbarSnapshotDifference.TITLE_TEXT; - } else if (mBackgroundColor != that.mBackgroundColor) { - return ToolbarSnapshotDifference.TINT; - } else if (mSecurityIconRes != that.mSecurityIconRes) { - return ToolbarSnapshotDifference.SECURITY_ICON; - } else if (!Objects.equals(mAnimationToken, that.mAnimationToken)) { - return ToolbarSnapshotDifference.CCT_ANIMATION; - } else { - return ToolbarSnapshotDifference.NONE; - } - } - } - - private CaptureStateToken generateCaptureStateToken() { + private CustomTabCaptureStateToken generateCaptureStateToken() { // Must convert CharSequence to String in order for equality to be clearly defined. String url = mLocationBar.mUrlBar.getText().toString(); String title = mLocationBar.mTitleBar.getText().toString(); - return new CaptureStateToken(url, title, getBackground().getColor(), + return new CustomTabCaptureStateToken(url, title, getBackground().getColor(), mLocationBar.mAnimDelegate.getSecurityIconRes(), mLocationBar.mAnimDelegate.isInAnimation()); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java index f0becac..ebe04ce5 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
@@ -640,6 +640,57 @@ assertNull(provider.getSecondaryToolbarSwipeUpPendingIntent()); } + @Test + public void testActivityDecorationType_Default() { + // Decoration not set + Intent intent = new CustomTabsIntent.Builder().build().intent; + var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT); + assertEquals("Decoration types do not match", + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT, + dataProvider.getActivitySideSheetDecorationType()); + + // Decoration set higher than max + intent.putExtra(CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_MAX + 1); + var dataProvider2 = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT); + assertEquals("Decoration types do not match", + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT, + dataProvider2.getActivitySideSheetDecorationType()); + } + + @Test + public void testActivityDecorationType_Shadow() { + Intent intent = new CustomTabsIntent.Builder().build().intent; + intent.putExtra(CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW); + var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT); + assertEquals("Decoration types do not match", + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW, + dataProvider.getActivitySideSheetDecorationType()); + } + + @Test + public void testActivityDecorationType_DividerLine() { + Intent intent = new CustomTabsIntent.Builder().build().intent; + intent.putExtra(CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER); + var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT); + assertEquals("Decoration types do not match", + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER, + dataProvider.getActivitySideSheetDecorationType()); + } + + @Test + public void testActivityDecorationType_None() { + Intent intent = new CustomTabsIntent.Builder().build().intent; + intent.putExtra(CustomTabIntentDataProvider.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE, + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE); + var dataProvider = new CustomTabIntentDataProvider(intent, mContext, COLOR_SCHEME_LIGHT); + assertEquals("Decoration types do not match", + CustomTabIntentDataProvider.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE, + dataProvider.getActivitySideSheetDecorationType()); + } + private Bundle createActionButtonInToolbarBundle() { Bundle bundle = new Bundle(); bundle.putInt(CustomTabsIntent.KEY_ID, CustomTabsIntent.TOOLBAR_ACTION_BUTTON_ID);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManagerTest.java index 3badb36b..50f5475 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManagerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabDisplayManagerTest.java
@@ -53,7 +53,7 @@ PartialCustomTabDisplayManager displayManager = new PartialCustomTabDisplayManager( mPCCTTestRule.mActivity, heightPx, widthPx, breakPointDp, false, mPCCTTestRule.mOnResizedCallback, mPCCTTestRule.mActivityLifecycleDispatcher, - mPCCTTestRule.mFullscreenManager, false, true, /*showMaximizeButton=*/true); + mPCCTTestRule.mFullscreenManager, false, true, /*showMaximizeButton=*/true, 0); var sizeStrategyCreator = displayManager.getSizeStrategyCreatorForTesting(); SizeStrategyCreator testSizeStrategyCreator = (type, maximized) -> { var strategy = sizeStrategyCreator.createForType(type, maximized);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java index eb6860b9..89eb525 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategyTest.java
@@ -58,7 +58,7 @@ PartialCustomTabSideSheetStrategy pcct = new PartialCustomTabSideSheetStrategy( mPCCTTestRule.mActivity, widthPx, mPCCTTestRule.mOnResizedCallback, mPCCTTestRule.mFullscreenManager, false, true, /*showMaximizedButton=*/true, - /*startMaximized=*/false, mPCCTTestRule.mHandleStrategyFactory); + /*startMaximized=*/false, mPCCTTestRule.mHandleStrategyFactory, 0); pcct.setMockViewForTesting(mPCCTTestRule.mCoordinatorLayout, mPCCTTestRule.mToolbarView, mPCCTTestRule.mToolbarCoordinator); return pcct;
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 b8660fc..415fc3b 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
@@ -66,7 +66,7 @@ import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator; import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult; import org.chromium.chrome.browser.toolbar.top.NavigationPopup.HistoryDelegate; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; +import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotDifference; import org.chromium.chrome.browser.toolbar.top.ToolbarTablet.OfflineDownloader; import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1e9cae3ab..a9704ad 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -14154,7 +14154,7 @@ <message name="IDS_VERIFY_SHEET_TITLE" desc="Header shown to the user while signing in happens."> Verifying… </message> - <message name="IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN" desc="Header for auto re-authentication sheet. Sheet is shown to notify the user that they are re-authenticating into a website using an account from an identity provider." translateable="false"> + <message name="IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN" desc="Header for auto re-authentication sheet. Sheet is shown to notify the user that they are re-authenticating into a website using an account from an identity provider."> Signing you in… </message> <message name="IDS_AUTO_REAUTHN_OPTOUT_CHECKBOX" desc="Allows users to opt out of FedCM auto re-authentication by unchecking the checkbox" translateable="false">
diff --git a/chrome/app/generated_resources_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1 b/chrome/app/generated_resources_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1 new file mode 100644 index 0000000..581e67f --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1
@@ -0,0 +1 @@ +b826261d4f1d3385632bca55233b43f1540abbf3 \ No newline at end of file
diff --git a/chrome/app/vector_icons/letter_spacing.icon b/chrome/app/vector_icons/letter_spacing.icon index 43ebaef4..ea5eabb 100644 --- a/chrome/app/vector_icons/letter_spacing.icon +++ b/chrome/app/vector_icons/letter_spacing.icon
@@ -6,30 +6,30 @@ MOVE_TO, 0, 13, LINE_TO, 3, 16, V_LINE_TO, 14, -H_LINE_TO, 11, +H_LINE_TO, 13, V_LINE_TO, 16, -LINE_TO, 14, 13, -LINE_TO, 11, 10, +LINE_TO, 16, 13, +LINE_TO, 13, 10, V_LINE_TO, 12, H_LINE_TO, 3, V_LINE_TO, 10, LINE_TO, 0, 13, CLOSE, -MOVE_TO, 2, 9, +MOVE_TO, 3, 9, V_LINE_TO, 0, -H_LINE_TO, 4, +H_LINE_TO, 5, V_LINE_TO, 9, -H_LINE_TO, 2, +H_LINE_TO, 3, CLOSE, -MOVE_TO, 6, 9, +MOVE_TO, 7, 9, V_LINE_TO, 0, -H_LINE_TO, 8, +H_LINE_TO, 9, V_LINE_TO, 9, -H_LINE_TO, 6, +H_LINE_TO, 7, CLOSE, -MOVE_TO, 10, 9, +MOVE_TO, 11, 9, V_LINE_TO, 0, -H_LINE_TO, 12, +H_LINE_TO, 13, V_LINE_TO, 9, -H_LINE_TO, 10, +H_LINE_TO, 11, CLOSE
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 4434338..6ad5e7c 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -4041,10 +4041,6 @@ flag_descriptions::kScreenshotsForAndroidV2Name, flag_descriptions::kScreenshotsForAndroidV2Description, kOsAndroid, FEATURE_VALUE_TYPE(share::kScreenshotsForAndroidV2)}, - {"voice-button-in-top-toolbar", - flag_descriptions::kVoiceButtonInTopToolbarName, - flag_descriptions::kVoiceButtonInTopToolbarDescription, kOsAndroid, - FEATURE_VALUE_TYPE(chrome::android::kVoiceButtonInTopToolbar)}, {"assistant-consent-simplified-text", flag_descriptions::kAssistantConsentSimplifiedTextName, flag_descriptions::kAssistantConsentSimplifiedTextDescription, kOsAndroid,
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java index 807f754..c6043ca 100644 --- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java +++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -82,6 +82,17 @@ */ public static final int ACTIVITY_HEIGHT_FIXED = 2; + @IntDef({ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT, ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW, + ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER}) + @Retention(RetentionPolicy.SOURCE) + public @interface SideSheetDecorationType {} + public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT = 0; + public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_NONE = 1; + public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW = 2; + public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER = 3; + public static final int ACTIVITY_SIDE_SHEET_DECORATION_TYPE_MAX = 3; + /** * @return The type of the Activity; */ @@ -529,6 +540,13 @@ } /** + * @return An int representing the side sheet decoration type for the Activity. + */ + public int getActivitySideSheetDecorationType() { + return ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT; + } + + /** * Returns the {@link CloseButtonPosition}. */ public @CloseButtonPosition int getCloseButtonPosition() {
diff --git a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc index 87ea9a0..923e2ec 100644 --- a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc +++ b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc
@@ -6,8 +6,10 @@ #include "base/android/jni_android.h" #include "base/android/jni_array.h" +#include "base/feature_list.h" #include "cc/slim/solid_color_layer.h" #include "chrome/browser/android/compositor/layer/toolbar_layer.h" +#include "chrome/browser/browser_features.h" #include "chrome/browser/ui/android/toolbar/jni_headers/TopToolbarSceneLayer_jni.h" #include "ui/android/resources/resource_manager_impl.h" #include "ui/gfx/android/java_bitmap.h" @@ -42,18 +44,25 @@ bool show_shadow, bool visible, bool anonymize) { + ui::ResourceManager* resource_manager = + ui::ResourceManagerImpl::FromJavaObject(jresource_manager); // If the toolbar layer has not been created yet, create it. if (!toolbar_layer_) { - ui::ResourceManager* resource_manager = - ui::ResourceManagerImpl::FromJavaObject(jresource_manager); toolbar_layer_ = ToolbarLayer::Create(resource_manager); toolbar_layer_->layer()->SetHideLayerAndSubtree(true); layer_->AddChild(toolbar_layer_->layer()); } toolbar_layer_->layer()->SetHideLayerAndSubtree(!visible); - if (!visible) + if (!visible) { + if (base::FeatureList::IsEnabled(features::kKeepToolbarTexture)) { + // Uploading the Toolbar texture on scroll is a source of jank, and the + // toolbar becomes visible frequently enough for it to be worth holding + // onto the texture even when not visible. + resource_manager->MarkTintNonDiscardable(url_bar_color); + } return; + } toolbar_layer_->PushResource(toolbar_resource_id, toolbar_background_color, anonymize, url_bar_color, url_bar_resource_id,
diff --git a/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc b/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc index e20f982..320b234b 100644 --- a/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc +++ b/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc
@@ -54,19 +54,20 @@ Invalidate(); } -void AutofillObserverImpl::OnFormSubmitted() { +void AutofillObserverImpl::OnFormSubmitted(autofill::AutofillManager&) { OnFormInteraction(); } -void AutofillObserverImpl::OnSelectControlDidChange() { +void AutofillObserverImpl::OnSelectControlDidChange( + autofill::AutofillManager&) { OnFormInteraction(); } -void AutofillObserverImpl::OnTextFieldDidChange() { +void AutofillObserverImpl::OnTextFieldDidChange(autofill::AutofillManager&) { OnFormInteraction(); } -void AutofillObserverImpl::OnTextFieldDidScroll() { +void AutofillObserverImpl::OnTextFieldDidScroll(autofill::AutofillManager&) { OnFormInteraction(); }
diff --git a/chrome/browser/android/customtabs/tab_interaction_recorder_android.h b/chrome/browser/android/customtabs/tab_interaction_recorder_android.h index cc5757e..6b667dc9 100644 --- a/chrome/browser/android/customtabs/tab_interaction_recorder_android.h +++ b/chrome/browser/android/customtabs/tab_interaction_recorder_android.h
@@ -34,10 +34,10 @@ ~AutofillObserverImpl() override; // AutofillManager::Observer: - void OnFormSubmitted() override; - void OnSelectControlDidChange() override; - void OnTextFieldDidChange() override; - void OnTextFieldDidScroll() override; + void OnFormSubmitted(autofill::AutofillManager&) override; + void OnSelectControlDidChange(autofill::AutofillManager&) override; + void OnTextFieldDidChange(autofill::AutofillManager&) override; + void OnTextFieldDidScroll(autofill::AutofillManager&) override; private: void OnFormInteraction();
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index ffdc8a1..2542872 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -3375,6 +3375,7 @@ "//chrome/browser/ash/fusebox:fusebox_staging_proto", "//chrome/browser/ash/guest_os:guest_os_diagnostics_mojom", "//chrome/browser/ash/login/oobe_quick_start", + "//chrome/browser/ash/login/oobe_quick_start/logging", "//chrome/browser/ash/power/ml/smart_dim", "//chrome/browser/ash/system_web_apps/types", "//chrome/browser/ash/video_conference",
diff --git a/chrome/browser/ash/crosapi/feedback_ash.cc b/chrome/browser/ash/crosapi/feedback_ash.cc index 4b73ef407..0b15c73 100644 --- a/chrome/browser/ash/crosapi/feedback_ash.cc +++ b/chrome/browser/ash/crosapi/feedback_ash.cc
@@ -26,6 +26,8 @@ return chrome::kFeedbackSourceChromeLabs; case mojom::LacrosFeedbackSource::kLacrosQuickAnswers: return chrome::kFeedbackSourceQuickAnswers; + case mojom::LacrosFeedbackSource::kLacrosWindowLayoutMenu: + return chrome::kFeedbackSourceWindowLayoutMenu; case mojom::LacrosFeedbackSource::kUnknown: return chrome::kFeedbackSourceUnknownLacrosSource; }
diff --git a/chrome/browser/ash/login/gaia_reauth_token_fetcher.cc b/chrome/browser/ash/login/gaia_reauth_token_fetcher.cc index b7afa64..6e9d2e6 100644 --- a/chrome/browser/ash/login/gaia_reauth_token_fetcher.cc +++ b/chrome/browser/ash/login/gaia_reauth_token_fetcher.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/net/system_network_context_manager.h" #include "chromeos/ash/components/login/auth/recovery/service_constants.h" -#include "google_apis/credentials_mode.h" #include "google_apis/google_api_keys.h" #include "net/base/load_flags.h" #include "net/base/url_util.h" @@ -60,8 +59,7 @@ resource_request->url = GetFetchReauthTokenUrl(); resource_request->load_flags = net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SAVE_COOKIES; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request->method = "GET"; // TODO(b/197615068): Update the "policy" field in the traffic
diff --git a/chrome/browser/ash/login/marketing_backend_connector.cc b/chrome/browser/ash/login/marketing_backend_connector.cc index b97cd0b..5a05a5e 100644 --- a/chrome/browser/ash/login/marketing_backend_connector.cc +++ b/chrome/browser/ash/login/marketing_backend_connector.cc
@@ -22,7 +22,6 @@ #include "components/signin/public/identity_manager/scope_set.h" #include "components/user_manager/user.h" #include "components/user_manager/user_manager.h" -#include "google_apis/credentials_mode.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" #include "net/http/http_status_code.h" @@ -66,8 +65,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = GetChromebookServiceEndpoint(); resource_request->load_flags = net::LOAD_DISABLE_CACHE; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request->method = "POST"; return resource_request; }
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc index a1a80def6..79b11c8 100644 --- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc +++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h" #include "base/check_op.h" +#include "base/command_line.h" #include "base/containers/contains.h" #include "base/functional/bind.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h" @@ -19,6 +20,15 @@ namespace { +// Passing "--quick-start-phone-instance-id" on the command line will implement +// the Unified Setup UI enhancements with the ID provided in the switch. This is +// for testing only and in the future this ID will be received during the Gaia +// credentials exchange. +// TODO(b/234655072): Delete this and get ID from the |bootstrap_controller_| +// Gaia credentials exchange instead. +constexpr char kQuickStartPhoneInstanceIDSwitch[] = + "quick-start-phone-instance-id"; + TargetDeviceBootstrapController::QRCodePixelData GenerateQRCode( std::vector<uint8_t> blob) { QRCodeGenerator qr_generator; @@ -58,6 +68,17 @@ connection_broker_->GetFeatureSupportStatusAsync(std::move(callback)); } +std::string TargetDeviceBootstrapController::GetPhoneInstanceId() { + // TODO(b/234655072): Delete kQuickStartPhoneInstanceIDSwitch and get the ID + // from the Gaia credentials exchange instead. + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kQuickStartPhoneInstanceIDSwitch)) { + return command_line->GetSwitchValueASCII(kQuickStartPhoneInstanceIDSwitch); + } + + return ""; +} + base::WeakPtr<TargetDeviceBootstrapController> TargetDeviceBootstrapController::GetAsWeakPtrForClient() { // Only one client at a time should have a pointer.
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h index 7e7903a..62dda382 100644 --- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h +++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_TARGET_DEVICE_BOOTSTRAP_CONTROLLER_H_ #include <memory> +#include <string> #include "base/memory/weak_ptr.h" #include "base/observer_list.h" @@ -36,6 +37,7 @@ ADVERTISING, QR_CODE_VERIFICATION, CONNECTED, + GAIA_CREDENTIALS, }; enum class ErrorCode { @@ -69,6 +71,10 @@ void GetFeatureSupportStatusAsync( TargetDeviceConnectionBroker::FeatureSupportStatusCallback callback); + // Returns the CryptAuth Instance ID of the connected remote device. Returns + // an empty string if no ID is available. + std::string GetPhoneInstanceId(); + // This function would crash (if DCHECKs are on) in case there are existing // valid weakptrs. base::WeakPtr<TargetDeviceBootstrapController> GetAsWeakPtrForClient();
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc index 03e10cd..bde7f7a 100644 --- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc +++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller_unittest.cc
@@ -3,8 +3,11 @@ // found in the LICENSE file. #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h" -#include <memory> +#include <memory> +#include <string> + +#include "base/command_line.h" #include "base/test/bind.h" #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.h" #include "chrome/browser/nearby_sharing/fake_nearby_connections_manager.h" @@ -186,4 +189,17 @@ ErrorCode::CONNECTION_CLOSED); } +TEST_F(TargetDeviceBootstrapControllerTest, GetPhoneInstanceId) { + // Ensure GetPhoneInstanceId() returns an empty string when no command line + // switch is set. + ASSERT_TRUE(bootstrap_controller_->GetPhoneInstanceId().empty()); + + std::string kExpectedPhoneInstanceID = "someArbitraryInstanceID"; + base::CommandLine::ForCurrentProcess()->InitFromArgv( + {"", "--quick-start-phone-instance-id=" + kExpectedPhoneInstanceID}); + + EXPECT_EQ(bootstrap_controller_->GetPhoneInstanceId(), + kExpectedPhoneInstanceID); +} + } // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc b/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc index 27c9874..70fd8ca 100644 --- a/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc +++ b/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc
@@ -24,7 +24,6 @@ #include "components/user_manager/known_user.h" #include "content/public/browser/browser_context.h" #include "content/public/common/url_constants.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -242,8 +241,7 @@ } resource_request->load_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_CACHE; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (request_type_ == RequestType::kCreateToken) { resource_request->method = net::HttpRequestHeaders::kPostMethod; } else {
diff --git a/chrome/browser/ash/login/screens/quick_start_screen.cc b/chrome/browser/ash/login/screens/quick_start_screen.cc index 889ea85..a02fc1f 100644 --- a/chrome/browser/ash/login/screens/quick_start_screen.cc +++ b/chrome/browser/ash/login/screens/quick_start_screen.cc
@@ -10,9 +10,11 @@ #include "base/notreached.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "chrome/browser/ash/login/oobe_quick_start/logging/logging.h" #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h" #include "chrome/browser/ash/login/oobe_quick_start/verification_shapes.h" #include "chrome/browser/ash/login/ui/login_display_host.h" +#include "chrome/browser/ash/login/wizard_context.h" #include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h" #include "third_party/abseil-cpp/absl/types/variant.h" @@ -79,6 +81,23 @@ view_->SetQRCode(std::move(qr_code_list)); return; } + case Step::GAIA_CREDENTIALS: { + std::string phone_instance_id = + bootstrap_controller_->GetPhoneInstanceId(); + + if (phone_instance_id.empty()) { + return; + } + + quick_start::QS_LOG(INFO) + << "Adding Phone Instance ID to Wizard Object for Unified " + "Setup UI enhancements. quick_start_phone_instance_id: " + << phone_instance_id; + LoginDisplayHost::default_host() + ->GetWizardContext() + ->quick_start_phone_instance_id = phone_instance_id; + return; + } case Step::NONE: case Step::ERROR: case Step::ADVERTISING:
diff --git a/chrome/browser/ash/login/users/avatar/user_image_loader.cc b/chrome/browser/ash/login/users/avatar/user_image_loader.cc index 4bb3eed1..5c450b1b 100644 --- a/chrome/browser/ash/login/users/avatar/user_image_loader.cc +++ b/chrome/browser/ash/login/users/avatar/user_image_loader.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/ui/ash/image_downloader_impl.h" #include "components/user_manager/user_image/user_image.h" -#include "google_apis/credentials_mode.h" #include "ipc/ipc_channel.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/data_decoder/public/cpp/data_decoder.h" @@ -439,8 +438,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = default_image_url; - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; auto loader = network::SimpleURLLoader::Create(std::move(request), kNetworkTrafficAnnotationTag);
diff --git a/chrome/browser/autofill/autofill_across_iframes_browsertest.cc b/chrome/browser/autofill/autofill_across_iframes_browsertest.cc index 4ff1bcab..b6feccb 100644 --- a/chrome/browser/autofill/autofill_across_iframes_browsertest.cc +++ b/chrome/browser/autofill/autofill_across_iframes_browsertest.cc
@@ -109,10 +109,10 @@ private: TestAutofillManagerWaiter did_autofill_{ *this, - {&AutofillManager::Observer::OnAfterDidFillAutofillFormData}}; + {AutofillManagerEvent::kDidFillAutofillFormData}}; TestAutofillManagerWaiter form_submitted_{ *this, - {&AutofillManager::Observer::OnAfterFormSubmitted}}; + {AutofillManagerEvent::kFormSubmitted}}; absl::optional<FormData> submitted_form_; };
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc index 4ff2395..22978eb4 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -130,7 +130,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; AutofillTest() = default;
diff --git a/chrome/browser/autofill/autofill_uitest_util.cc b/chrome/browser/autofill/autofill_uitest_util.cc index cb39d428..ddb6079 100644 --- a/chrome/browser/autofill/autofill_uitest_util.cc +++ b/chrome/browser/autofill/autofill_uitest_util.cc
@@ -140,8 +140,8 @@ absl::get<AutofillDriver*>(autofill_external_delegate->GetDriver())); AutofillManager* manager = driver->autofill_manager(); mojom::AutofillDriver* mojo_driver = driver; - TestAutofillManagerWaiter waiter( - *manager, {&AutofillManager::Observer::OnAfterAskForValuesToFill}); + TestAutofillManagerWaiter waiter(*manager, + {AutofillManagerEvent::kAskForValuesToFill}); mojo_driver->AskForValuesToFill(form, form.fields.front(), bounds, AutoselectFirstSuggestion(false), FormElementWasClicked(false));
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc index dfb791e..fb1ecca9 100644 --- a/chrome/browser/autofill/captured_sites_test_utils.cc +++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -124,6 +124,7 @@ $ echo where >%1$s # prints the current position $ echo show -1 >%1$s # prints last 1 actions $ echo show 1 >%1$s # prints next 1 actions + $ echo failure >%1$s # unpauses execution until failure $ echo help >%1$s # prints this text )"; LOG(INFO) << base::StringPrintf(msg, @@ -168,6 +169,7 @@ kSkipAction, kShowAction, kWhereAmI, + kRunUntilFailure }; struct ExecutionCommand { @@ -207,6 +209,11 @@ commands.push_back({ExecutionCommandType::kShowAction, GetParamOr(1)}); } else if (base::StartsWith(command, "where")) { commands.push_back({ExecutionCommandType::kWhereAmI}); + } else if (base::StartsWith(command, "failure")) { + commands.push_back({ExecutionCommandType::kRunUntilFailure}); + // also add an absolute max limit (like a" run" command). + commands.push_back({ExecutionCommandType::kAbsoluteLimit, + std::numeric_limits<int>::max()}); } else if (base::StartsWith(command, "help")) { PrintDebugInstructions(command_file_path); } @@ -222,6 +229,8 @@ int limit = std::numeric_limits<int>::max(); // The number of actions to be executed. int length = 0; + // Whether to stop at a step if a failure is detected. + bool pause_on_failure = false; }; // Blockingly reads the commands from |command_file_path| and executes them. @@ -266,6 +275,12 @@ LOG(INFO) << "Next action is at position " << execution_state.index << ", limit (excl) is at " << execution_state.limit << ", last (excl) is at " << execution_state.length; + break; + } + case ExecutionCommandType::kRunUntilFailure: { + LOG(INFO) << "Will stop when a failure is found."; + execution_state.pause_on_failure = true; + break; } } } @@ -1099,6 +1114,14 @@ } while (execution_state.index < execution_state.length) { + if (execution_state.pause_on_failure && + (testing::Test::HasNonfatalFailure() || + testing::Test::HasFatalFailure() || validation_failures_.size() > 0)) { + // If set to pause on a failure, move limit to current, but then reset + // `pause_on_failure` so it can continue if the user requests. + execution_state.limit = execution_state.index; + execution_state.pause_on_failure = false; + } if (command_file_path.has_value()) { while (execution_state.limit <= execution_state.index) { bool thread_finished = false;
diff --git a/chrome/browser/autofill/form_structure_browsertest.cc b/chrome/browser/autofill/form_structure_browsertest.cc index 9842c012..58bfc69 100644 --- a/chrome/browser/autofill/form_structure_browsertest.cc +++ b/chrome/browser/autofill/form_structure_browsertest.cc
@@ -180,9 +180,8 @@ TestAutofillManagerWaiter& waiter() { return waiter_; } private: - TestAutofillManagerWaiter waiter_{ - *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + TestAutofillManagerWaiter waiter_{*this, + {AutofillManagerEvent::kFormsSeen}}; }; std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc index c62d8bc..f5df391 100644 --- a/chrome/browser/browser_features.cc +++ b/chrome/browser/browser_features.cc
@@ -42,6 +42,13 @@ "DevToolsTabTarget", base::FEATURE_DISABLED_BY_DEFAULT); +// Normally the toolbar texture is discarded when the toolbar is no longer +// visible. This feature keeps the texture around so it does not need to get +// re-uploaded when the toolbar becomes visible again. +BASE_FEATURE(kKeepToolbarTexture, + "KeepToolbarTexture", + base::FEATURE_DISABLED_BY_DEFAULT); + // Nukes profile directory before creating a new profile using // ProfileManager::CreateMultiProfileAsync(). BASE_FEATURE(kNukeProfileBeforeCreateMultiAsync,
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h index c148a76..131e64d2 100644 --- a/chrome/browser/browser_features.h +++ b/chrome/browser/browser_features.h
@@ -25,6 +25,8 @@ BASE_DECLARE_FEATURE(kDevToolsTabTarget); +BASE_DECLARE_FEATURE(kKeepToolbarTexture); + BASE_DECLARE_FEATURE(kNukeProfileBeforeCreateMultiAsync); BASE_DECLARE_FEATURE(kPromoBrowserCommands);
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.cc b/chrome/browser/devtools/device/port_forwarding_controller.cc index cd640dc0..f2380b8f 100644 --- a/chrome/browser/devtools/device/port_forwarding_controller.cc +++ b/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -67,20 +67,22 @@ const char kConnectionIdParam[] = "connectionId"; static bool ParseNotification(const std::string& json, - std::string* method, - absl::optional<base::Value>* params) { + std::string& method, + absl::optional<base::Value::Dict>& params) { absl::optional<base::Value> value = base::JSONReader::Read(json); if (!value || !value->is_dict()) return false; - const std::string* method_value = value->FindStringKey(kMethodParam); + base::Value::Dict& dict = value->GetDict(); + std::string* method_value = dict.FindString(kMethodParam); if (!method_value) return false; - *method = *method_value; + method = std::move(*method_value); - auto extracted_param = value->ExtractKey(kParamsParam); - if (extracted_param && extracted_param->is_dict()) - *params = std::move(extracted_param); + base::Value::Dict* param_dict = dict.FindDict(kParamsParam); + if (param_dict) { + params = std::move(*param_dict); + } return true; } @@ -90,12 +92,13 @@ absl::optional<base::Value> value = base::JSONReader::Read(json); if (!value || !value->is_dict()) return false; - absl::optional<int> command_id_opt = value->FindIntKey(kIdParam); + const base::Value::Dict& dict = value->GetDict(); + absl::optional<int> command_id_opt = dict.FindInt(kIdParam); if (!command_id_opt) return false; *command_id = *command_id_opt; - absl::optional<int> error_value = value->FindIntPath(kErrorCodePath); + absl::optional<int> error_value = dict.FindIntByDottedPath(kErrorCodePath); if (error_value) *error_code = *error_value; @@ -105,10 +108,10 @@ static std::string SerializeCommand(int command_id, const std::string& method, base::Value params) { - base::Value command(base::Value::Type::DICT); - command.SetIntKey(kIdParam, command_id); - command.SetStringKey(kMethodParam, method); - command.SetKey(kParamsParam, std::move(params)); + base::Value::Dict command; + command.Set(kIdParam, command_id); + command.Set(kMethodParam, method); + command.Set(kParamsParam, std::move(params)); std::string json_command; base::JSONWriter::Write(command, &json_command); @@ -493,9 +496,9 @@ void PortForwardingController::Connection::SendCommand( const std::string& method, int port) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::Value params(base::Value::Type::DICT); + base::Value::Dict params; DCHECK(method == kBindMethod || kUnbindMethod == method); - params.SetIntKey(kPortParam, port); + params.Set(kPortParam, port); int id = ++command_id_; if (method == kBindMethod) { @@ -513,7 +516,8 @@ base::Unretained(this), port); } - web_socket_->SendFrame(SerializeCommand(id, method, std::move(params))); + web_socket_->SendFrame( + SerializeCommand(id, method, base::Value(std::move(params)))); } bool PortForwardingController::Connection::ProcessResponse( @@ -571,17 +575,18 @@ return; std::string method; - absl::optional<base::Value> params; - if (!ParseNotification(message, &method, ¶ms)) + absl::optional<base::Value::Dict> params; + if (!ParseNotification(message, method, params)) { return; + } if (method != kAcceptedEvent || !params) return; - absl::optional<int> port = params->FindIntKey(kPortParam); + absl::optional<int> port = params->FindInt(kPortParam); if (!port) return; - const std::string* connection_id = params->FindStringKey(kConnectionIdParam); + const std::string* connection_id = params->FindString(kConnectionIdParam); if (!connection_id) return;
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_browsertest.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_browsertest.cc index 12edb9a..ca51ba4 100644 --- a/chrome/browser/enterprise/connectors/device_trust/device_trust_browsertest.cc +++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_browsertest.cc
@@ -450,6 +450,9 @@ #if BUILDFLAG(IS_WIN) device_trust_test_environment_win_.emplace(); + device_trust_test_environment_win_->SetExpectedDMToken(kFakeBrowserDMToken); + device_trust_test_environment_win_->SetExpectedClientID( + kFakeBrowserClientId); #else // BUILDFLAG(IS_WIN) scoped_persistence_delegate_factory_.emplace(); scoped_rotation_command_factory_.emplace();
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc index 3413008..117e4ea 100644 --- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc +++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc
@@ -31,4 +31,14 @@ upload_response_code_ = upload_response_code; } +void DeviceTrustTestEnvironment::SetExpectedDMToken( + std::string expected_dm_token) { + expected_dm_token_ = expected_dm_token; +} + +void DeviceTrustTestEnvironment::SetExpectedClientID( + std::string expected_client_id) { + expected_client_id_ = expected_client_id; +} + } // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.h b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.h index dec6d80..1e3323af 100644 --- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.h +++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.h
@@ -36,6 +36,12 @@ // KeyNetworkDelegate. void SetUploadResult(HttpResponseCode upload_response_code); + // Set up an expected DM token, which we will check if it's identical to + // the DM token we use when uploading to DM server + void SetExpectedDMToken(std::string expected_dm_token); + + void SetExpectedClientID(std::string expected_client_id); + // Check if device trust key exists on the device. bool KeyExists(); @@ -49,6 +55,12 @@ // KeyNetworkDelegate. HttpResponseCode upload_response_code_; + // Expected dm token to be used when upload to dm server + std::string expected_dm_token_; + + // Expected client ID to be included in dm server URL when upload to dm server + std::string expected_client_id_; + // Instance of platform-dependent KeyPersistenceDelegate to interact with // Device Trust keys. std::unique_ptr<KeyPersistenceDelegate> key_persistence_delegate_;
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc index 1436174..1f8d3b4 100644 --- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc +++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
@@ -42,6 +42,8 @@ HRESULT MockRunGoogleUpdateElevatedCommandFn( HttpResponseCode upload_response_code, + std::string expected_dm_token, + std::string expected_client_id, const wchar_t* command, const std::vector<std::string>& args, absl::optional<DWORD>* return_code) { @@ -54,9 +56,15 @@ std::make_unique<StrictMock<MockKeyNetworkDelegate>>(); EXPECT_CALL(*mock_network_delegate, SendPublicKeyToDmServer(_, _, _, _)) .WillOnce(Invoke( - [upload_response_code](const GURL& url, const std::string& dm_token, - const std::string& body, - base::OnceCallback<void(int)> callback) { + [upload_response_code, expected_dm_token, expected_client_id, args]( + const GURL& url, const std::string& dm_token, + const std::string& body, base::OnceCallback<void(int)> callback) { + // Check if the DM Server URL contains the correct Client ID + CHECK(url.spec().find(expected_client_id) != std::string::npos); + // Check if the correct DM Token is being uploaded + CHECK_EQ(dm_token, expected_dm_token); + // TODO(b/269746642): add a check for the 'body' parameter above + std::move(callback).Run(upload_response_code); })); const auto result = enterprise_connectors::RotateDeviceTrustKey( @@ -105,7 +113,8 @@ } return std::make_unique<WinKeyRotationCommand>( base::BindRepeating(&MockRunGoogleUpdateElevatedCommandFn, - upload_response_code_), + upload_response_code_, expected_dm_token_, + expected_client_id_), worker_thread_.task_runner()); }
diff --git a/chrome/browser/extensions/extension_context_menu_model_unittest.cc b/chrome/browser/extensions/extension_context_menu_model_unittest.cc index 6234697..7307a7f 100644 --- a/chrome/browser/extensions/extension_context_menu_model_unittest.cc +++ b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
@@ -63,7 +63,6 @@ #include "net/disk_cache/blockfile/disk_format_base.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/display/test/scoped_screen_override.h" #include "ui/display/test/test_screen.h" #include "ui/gfx/image/image.h" #include "url/origin.h" @@ -74,7 +73,6 @@ namespace extensions { -using display::test::ScopedScreenOverride; using mojom::ManifestLocation; using ContextMenuSource = ExtensionContextMenuModel::ContextMenuSource; using MenuEntries = ExtensionContextMenuModel::MenuEntries; @@ -300,7 +298,6 @@ std::unique_ptr<TestBrowserWindow> test_window_; std::unique_ptr<Browser> browser_; display::test::TestScreen test_screen_; - std::unique_ptr<ScopedScreenOverride> scoped_screen_override_; }; ExtensionContextMenuModelTest::ExtensionContextMenuModelTest() {} @@ -445,8 +442,7 @@ void ExtensionContextMenuModelTest::SetUp() { ExtensionServiceTestBase::SetUp(); - scoped_screen_override_ = - std::make_unique<ScopedScreenOverride>(&test_screen_); + display::Screen::SetScreenInstance(&test_screen_); } void ExtensionContextMenuModelTest::TearDown() { @@ -456,6 +452,7 @@ browser_->tab_strip_model()->DetachAndDeleteWebContentsAt(0); } + display::Screen::SetScreenInstance(nullptr); ExtensionServiceTestBase::TearDown(); }
diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc index f38d85e..563a7bc 100644 --- a/chrome/browser/extensions/extension_icon_manager_unittest.cc +++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc
@@ -24,7 +24,6 @@ #include "ui/base/layout.h" #include "ui/display/display_list.h" #include "ui/display/display_switches.h" -#include "ui/display/test/scoped_screen_override.h" #include "ui/display/test/test_screen.h" #include "ui/gfx/favicon_size.h" #include "ui/gfx/geometry/skia_conversions.h" @@ -45,8 +44,7 @@ switches::kForceDeviceScaleFactor, base::StringPrintf("%3.2f", scale)); // This has to be inited after fiddling with the command line. test_screen_ = std::make_unique<display::test::TestScreen>(); - screen_override_ = std::make_unique<display::test::ScopedScreenOverride>( - test_screen_.get()); + display::Screen::SetScreenInstance(test_screen_.get()); } ScopedSetDeviceScaleFactor(const ScopedSetDeviceScaleFactor&) = delete; @@ -54,12 +52,12 @@ delete; ~ScopedSetDeviceScaleFactor() { + display::Screen::SetScreenInstance(nullptr); display::Display::ResetForceDeviceScaleFactorForTesting(); } private: std::unique_ptr<display::test::TestScreen> test_screen_; - std::unique_ptr<display::test::ScopedScreenOverride> screen_override_; base::test::ScopedCommandLine command_line_; };
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl.cc b/chrome/browser/fast_checkout/fast_checkout_client_impl.cc index dba95aea..186a187 100644 --- a/chrome/browser/fast_checkout/fast_checkout_client_impl.cc +++ b/chrome/browser/fast_checkout/fast_checkout_client_impl.cc
@@ -293,7 +293,8 @@ selected_credit_card_guid_; } -void FastCheckoutClientImpl::OnAfterLoadedServerPredictions() { +void FastCheckoutClientImpl::OnAfterLoadedServerPredictions( + autofill::AutofillManager& manager) { TryToFillForms(); } @@ -442,7 +443,9 @@ } } -void FastCheckoutClientImpl::OnAfterDidFillAutofillFormData() { +void FastCheckoutClientImpl::OnAfterDidFillAutofillFormData( + autofill::AutofillManager& manager, + autofill::FormGlobalId form_id) { if (!IsFilling()) { return; } @@ -508,7 +511,8 @@ } } -void FastCheckoutClientImpl::OnAutofillManagerDestroyed() { +void FastCheckoutClientImpl::OnAutofillManagerDestroyed( + autofill::AutofillManager& manager) { if (IsRunning()) { if (GetWebContents().IsBeingDestroyed()) { OnRunComplete(FastCheckoutRunOutcome::kTabClosed); @@ -520,7 +524,8 @@ Stop(/*allow_further_runs=*/true); } -void FastCheckoutClientImpl::OnAutofillManagerReset() { +void FastCheckoutClientImpl::OnAutofillManagerReset( + autofill::AutofillManager& manager) { if (IsShowing()) { OnRunComplete(FastCheckoutRunOutcome::kNavigationWhileBottomsheetWasShown); }
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl.h b/chrome/browser/fast_checkout/fast_checkout_client_impl.h index 6405798..8fee89e1 100644 --- a/chrome/browser/fast_checkout/fast_checkout_client_impl.h +++ b/chrome/browser/fast_checkout/fast_checkout_client_impl.h
@@ -55,14 +55,16 @@ std::unique_ptr<autofill::CreditCard> selected_credit_card) override; void OnDismiss() override; - // AutofillManager::Observer: - void OnAfterLoadedServerPredictions() override; - void OnAfterDidFillAutofillFormData() override; + // autofill::AutofillManager::Observer: + void OnAfterLoadedServerPredictions( + autofill::AutofillManager& manager) override; + void OnAfterDidFillAutofillFormData(autofill::AutofillManager& manager, + autofill::FormGlobalId form_id) override; // Is owned by a `ContentAutofillDriver` instance and its lifecycle thus is // dependent on the one of `RenderFrameHost`. - void OnAutofillManagerDestroyed() override; + void OnAutofillManagerDestroyed(autofill::AutofillManager& manager) override; // Is called on navigation and resets its internal state. - void OnAutofillManagerReset() override; + void OnAutofillManagerReset(autofill::AutofillManager& manager) override; // autofill::payments::FullCardRequest::ResultDelegate: void OnFullCardRequestSucceeded(
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc b/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc index 5facecf..603bf77c 100644 --- a/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc +++ b/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc
@@ -660,7 +660,7 @@ EXPECT_CALL(*autofill_manager(), SetFastCheckoutRunId(autofill::FieldTypeGroup::kAddressHome, fast_checkout_client()->run_id_)); - fast_checkout_client()->OnAfterLoadedServerPredictions(); + fast_checkout_client()->OnAfterLoadedServerPredictions(*autofill_manager()); EXPECT_THAT( fast_checkout_client()->form_filling_states_, UnorderedElementsAre( @@ -708,7 +708,8 @@ autofill::FormType::kCreditCardForm), FastCheckoutClientImpl::FillingState::kFilling))); - fast_checkout_client()->OnAfterDidFillAutofillFormData(); + fast_checkout_client()->OnAfterDidFillAutofillFormData( + *autofill_manager(), credit_card_form->global_id()); EXPECT_FALSE(fast_checkout_client()->IsRunning()); ExpectRunOutcomeUkm(FastCheckoutRunOutcome::kSuccess); @@ -720,7 +721,7 @@ autofill_manager()->GetWeakPtr())); EXPECT_TRUE(fast_checkout_client()->IsRunning()); - fast_checkout_client()->OnAutofillManagerReset(); + fast_checkout_client()->OnAutofillManagerReset(*autofill_manager()); EXPECT_FALSE(fast_checkout_client()->IsRunning()); ExpectRunOutcomeUkm( FastCheckoutRunOutcome::kNavigationWhileBottomsheetWasShown); @@ -732,7 +733,7 @@ autofill_manager()->GetWeakPtr())); EXPECT_TRUE(fast_checkout_client()->IsRunning()); - fast_checkout_client()->OnAutofillManagerDestroyed(); + fast_checkout_client()->OnAutofillManagerDestroyed(*autofill_manager()); EXPECT_FALSE(fast_checkout_client()->IsRunning()); ExpectRunOutcomeUkm(FastCheckoutRunOutcome::kAutofillManagerDestroyed); } @@ -835,7 +836,8 @@ kAutofillProfileLabel + u" address form filled."; EXPECT_CALL(*accessibility_service(), Announce(announcement_text)); - fast_checkout_client()->OnAfterDidFillAutofillFormData(); + fast_checkout_client()->OnAfterDidFillAutofillFormData( + *autofill_manager(), address_form->global_id()); } TEST_F( @@ -850,7 +852,8 @@ std::u16string announcement_text = u"Email filled."; EXPECT_CALL(*accessibility_service(), Announce(announcement_text)); - fast_checkout_client()->OnAfterDidFillAutofillFormData(); + fast_checkout_client()->OnAfterDidFillAutofillFormData( + *autofill_manager(), address_form->global_id()); } TEST_F( @@ -867,7 +870,8 @@ std::u16string announcement_text = kCreditCardNickname + u" filled."; EXPECT_CALL(*accessibility_service(), Announce(announcement_text)); - fast_checkout_client()->OnAfterDidFillAutofillFormData(); + fast_checkout_client()->OnAfterDidFillAutofillFormData( + *autofill_manager(), credit_card_form->global_id()); } TEST_F(FastCheckoutClientImplTest, @@ -883,7 +887,7 @@ EXPECT_TRUE(fast_checkout_client()->IsRunning()); EXPECT_CALL(*autofill_manager(), FillProfileFormImpl).Times(0); - fast_checkout_client()->OnAfterLoadedServerPredictions(); + fast_checkout_client()->OnAfterLoadedServerPredictions(*autofill_manager()); EXPECT_FALSE(fast_checkout_client()->IsRunning()); } @@ -906,7 +910,7 @@ // `BrowserAutofillManager`. EXPECT_CALL(*autofill_manager(), FillCreditCardFormImpl).Times(0); - fast_checkout_client()->OnAfterLoadedServerPredictions(); + fast_checkout_client()->OnAfterLoadedServerPredictions(*autofill_manager()); EXPECT_FALSE(fast_checkout_client()->IsRunning()); }
diff --git a/chrome/browser/feedback/show_feedback_page_lacros.cc b/chrome/browser/feedback/show_feedback_page_lacros.cc index 2583c66..129a1de 100644 --- a/chrome/browser/feedback/show_feedback_page_lacros.cc +++ b/chrome/browser/feedback/show_feedback_page_lacros.cc
@@ -26,6 +26,8 @@ return crosapi::mojom::LacrosFeedbackSource::kLacrosChromeLabs; case kFeedbackSourceQuickAnswers: return crosapi::mojom::LacrosFeedbackSource::kLacrosQuickAnswers; + case kFeedbackSourceWindowLayoutMenu: + return crosapi::mojom::LacrosFeedbackSource::kLacrosWindowLayoutMenu; default: LOG(ERROR) << "ShowFeedbackPage is called by unknown Lacros source: " << static_cast<int>(source);
diff --git a/chrome/browser/feedback/show_feedback_page_lacros_browsertest.cc b/chrome/browser/feedback/show_feedback_page_lacros_browsertest.cc index 60865b79..f2d59be3 100644 --- a/chrome/browser/feedback/show_feedback_page_lacros_browsertest.cc +++ b/chrome/browser/feedback/show_feedback_page_lacros_browsertest.cc
@@ -71,3 +71,10 @@ ShowFeedbackPageWithFeedbackSource(chrome::kFeedbackSourceSadTabPage); histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1); } + +IN_PROC_BROWSER_TEST_F(ShowFeedbackPageBrowserTest, + ShowFeedbackPageFromWindowLayoutMenu) { + base::HistogramTester histogram_tester; + ShowFeedbackPageWithFeedbackSource(chrome::kFeedbackSourceWindowLayoutMenu); + histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1); +}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index ea8ada0..374d7c7 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -357,14 +357,6 @@ "expiry_milestone": 96 }, { - "name": "assistant-consent-modal", - "owners": [ - "jds@google.com", - "chrome-voice@google.com" - ], - "expiry_milestone": 110 - }, - { "name": "assistant-consent-simplified-text", "owners": [ "jds@google.com", @@ -7125,15 +7117,6 @@ "expiry_milestone": 120 }, { - "name": "voice-button-in-top-toolbar", - "owners": [ - "jds@google.com", - "basiaz@google.com", - "chrome-voice@google.com" - ], - "expiry_milestone": 103 - }, - { "name": "wait-threshold-seconds-for-capabilities-api", "owners": [ "jlebel", "chrome-signin-team" ], // Used for manual testing to extend the wait time for capabilities fetching on iOS devices.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index f87ad5e..cfc29191 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -4153,10 +4153,6 @@ "Adaptive button in top toolbar customization"; const char kAdaptiveButtonInTopToolbarCustomizationDescription[] = "Enables UI for customizing the adaptive action button in the top toolbar"; -const char kVoiceButtonInTopToolbarName[] = "Voice button in top toolbar"; -const char kVoiceButtonInTopToolbarDescription[] = - "Enables showing the voice search button in the top toolbar. Enabling " - "Adaptive Button overrides this."; const char kUseToastManagerName[] = "Use Toast manager"; const char kUseToastManagerDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 2053c74..d5167dc 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2363,8 +2363,6 @@ extern const char kAdaptiveButtonInTopToolbarDescription[]; extern const char kAdaptiveButtonInTopToolbarCustomizationName[]; extern const char kAdaptiveButtonInTopToolbarCustomizationDescription[]; -extern const char kVoiceButtonInTopToolbarName[]; -extern const char kVoiceButtonInTopToolbarDescription[]; extern const char kUseToastManagerName[]; extern const char kUseToastManagerDescription[];
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_storage.cc b/chrome/browser/k_anonymity_service/k_anonymity_service_storage.cc index 8c40fe08..aac9930 100644 --- a/chrome/browser/k_anonymity_service/k_anonymity_service_storage.cc +++ b/chrome/browser/k_anonymity_service/k_anonymity_service_storage.cc
@@ -71,13 +71,16 @@ table_manager_, trust_token_key_commitment_table_.get(), /*max_num_entries=*/absl::nullopt, - kFlushDelay)) {} + kFlushDelay)), + weak_factory_(this) {} ~KAnonymityServiceSqlStorage() override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Enqueue a flush - ohttp_key_data_->FlushDataToDisk(); - trust_token_key_commitment_data_->FlushDataToDisk(); + if (status_ == kInitOk) { + // Enqueue a flush + ohttp_key_data_->FlushDataToDisk(); + trust_token_key_commitment_data_->FlushDataToDisk(); + } // Shutdown `table_manager_`, delete database on db // sequence, then delete the KeyValueTable/KeyValueData on main sequence. @@ -103,11 +106,17 @@ void WaitUntilReady(base::OnceCallback<void(InitStatus)> on_ready) override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (ready_) { + if (status_ != kInitNotReady) { std::move(on_ready).Run(InitStatus::kInitOk); return; } - ready_ = true; + + waiting_tasks_.emplace_back(std::move(on_ready)); + if (waiting_tasks_.size() > 1) { + // We're in the process of initializing. + return; + } + // This is safe because tasks are serialized on the db_task_runner sequence // and the `table_manager_`, `ohttp_key_data_`, and // `trust_token_key_commitment_data_` are only freed after a response from a @@ -122,7 +131,15 @@ base::Unretained(table_manager_.get()), base::Unretained(ohttp_key_data_.get()), base::Unretained(trust_token_key_commitment_data_.get())), - std::move(on_ready)); + base::BindOnce(&KAnonymityServiceSqlStorage::OnReady, + weak_factory_.GetWeakPtr())); + } + + void OnReady(InitStatus status) { + status_ = status; + for (auto& waiting_task : waiting_tasks_) { + std::move(waiting_task).Run(status); + } } static InitStatus InitializeOnDbSequence( @@ -148,6 +165,7 @@ absl::optional<OHTTPKeyAndExpiration> GetOHTTPKeyFor( const url::Origin& origin) const override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(status_ != kInitNotReady); proto::OHTTPKeyAndExpiration result; if (!ohttp_key_data_->TryGetData(origin.Serialize(), &result)) { return absl::nullopt; @@ -159,6 +177,7 @@ void UpdateOHTTPKeyFor(const url::Origin& origin, const OHTTPKeyAndExpiration& value) override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(status_ != kInitNotReady); proto::OHTTPKeyAndExpiration proto_value; proto_value.set_hpke_key(value.key); proto_value.set_expiration_us( @@ -170,6 +189,7 @@ absl::optional<KeyAndNonUniqueUserIdWithExpiration> GetKeyAndNonUniqueUserId() const override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(status_ != kInitNotReady); proto::TrustTokenKeyCommitmentWithExpiration result; if (!trust_token_key_commitment_data_->TryGetData( kTrustTokenKeyCommitmentKey, &result)) { @@ -183,6 +203,7 @@ void UpdateKeyAndNonUniqueUserId( const KeyAndNonUniqueUserIdWithExpiration& value) override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(status_ != kInitNotReady); proto::TrustTokenKeyCommitmentWithExpiration proto_value; proto_value.set_key_commitment(value.key_and_id.key_commitment); proto_value.set_non_unique_id(value.key_and_id.non_unique_user_id); @@ -211,8 +232,11 @@ std::unique_ptr< sqlite_proto::KeyValueData<proto::TrustTokenKeyCommitmentWithExpiration>> trust_token_key_commitment_data_; - bool ready_ = false; + + InitStatus status_ = kInitNotReady; + std::vector<base::OnceCallback<void(InitStatus)>> waiting_tasks_; SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<KAnonymityServiceSqlStorage> weak_factory_; }; } // namespace
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_storage.h b/chrome/browser/k_anonymity_service/k_anonymity_service_storage.h index a97b713..626fdd6 100644 --- a/chrome/browser/k_anonymity_service/k_anonymity_service_storage.h +++ b/chrome/browser/k_anonymity_service/k_anonymity_service_storage.h
@@ -38,6 +38,9 @@ enum InitStatus { kInitOk, kInitError, + // kInitNotReady is only be used internally, not as a response to + // WaitUntilReady. + kInitNotReady, }; virtual ~KAnonymityServiceStorage(); virtual void WaitUntilReady(
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_storage_unittest.cc b/chrome/browser/k_anonymity_service/k_anonymity_service_storage_unittest.cc index f92bfa2..b4ebfcc 100644 --- a/chrome/browser/k_anonymity_service/k_anonymity_service_storage_unittest.cc +++ b/chrome/browser/k_anonymity_service/k_anonymity_service_storage_unittest.cc
@@ -66,6 +66,29 @@ std::unique_ptr<KAnonymityServiceStorage> storage = CreateStorage(); } +TEST_P(KAnonymityServiceStorageTest, MultipleWaitUntilReady) { + std::unique_ptr<KAnonymityServiceStorage> storage; + if (StorageIsPersistent()) { + storage = CreateKAnonymitySqlStorageForPath(db_path()); + } else { + storage = std::make_unique<KAnonymityServiceMemoryStorage>(); + } + int seq_num = 0; + base::RunLoop run_loop; + storage->WaitUntilReady(base::BindLambdaForTesting( + [&](KAnonymityServiceStorage::InitStatus status) { + EXPECT_EQ(status, KAnonymityServiceStorage::InitStatus::kInitOk); + EXPECT_EQ(1, ++seq_num); + })); + storage->WaitUntilReady(base::BindLambdaForTesting( + [&](KAnonymityServiceStorage::InitStatus status) { + EXPECT_EQ(status, KAnonymityServiceStorage::InitStatus::kInitOk); + EXPECT_EQ(2, ++seq_num); + run_loop.Quit(); + })); + run_loop.Run(); +} + TEST_P(KAnonymityServiceStorageTest, SaveAndLoadOHTTPKeys) { url::Origin test_origin = url::Origin::Create(GURL(kTestOrigin)); std::string test_ohttp_key(kTestOhttpKey);
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc index 90287b0..f27db0c 100644 --- a/chrome/browser/navigation_predictor/navigation_predictor.cc +++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -15,7 +15,6 @@ #include "base/system/sys_info.h" #include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h" #include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h" -#include "chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h" #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" @@ -103,6 +102,21 @@ return ukm::GetLinearBucketMin(static_cast<int64_t>(value), 5); } +PageAnchorsMetricsObserver::UserInteractionsData& +NavigationPredictor::GetUserInteractionsData() const { + // Create the UserInteractionsData object for this WebContents if it doesn't + // already exist. + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(&render_frame_host()); + PageAnchorsMetricsObserver::UserInteractionsData::CreateForWebContents( + web_contents); + PageAnchorsMetricsObserver::UserInteractionsData* data = + PageAnchorsMetricsObserver::UserInteractionsData::FromWebContents( + web_contents); + DCHECK(data); + return *data; +} + void NavigationPredictor::ReportNewAnchorElements( std::vector<blink::mojom::AnchorElementMetricsPtr> elements) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -204,13 +218,26 @@ if (it != anchors_.end()) { builder.SetHrefUnchanged(it->second->target_url == click->target_url); } + navigation_start_to_click_ = click->navigation_start_to_click; + GetUserInteractionsData().navigation_start_to_click_ = + navigation_start_to_click_; + + builder.SetNavigationStartToLinkClickedMs(ukm::GetExponentialBucketMin( + navigation_start_to_click_.value().InMilliseconds(), 1.3)); builder.Record(ukm_recorder_); } void NavigationPredictor::ReportAnchorElementsLeftViewport( std::vector<blink::mojom::AnchorElementLeftViewportPtr> elements) { + auto& user_interactions_data = GetUserInteractionsData(); + auto& user_interactions = user_interactions_data.user_interactions_; for (const auto& element : elements) { - auto& user_interaction = user_interactions_[AnchorId(element->anchor_id)]; + auto index_it = + tracked_anchor_id_to_index_.find(AnchorId(element->anchor_id)); + if (index_it == tracked_anchor_id_to_index_.end()) { + continue; + } + auto& user_interaction = user_interactions[index_it->second]; user_interaction.is_in_viewport = false; user_interaction.last_navigation_start_to_entered_viewport.reset(); user_interaction.max_time_in_viewport = std::max( @@ -221,8 +248,18 @@ void NavigationPredictor::ReportAnchorElementPointerOver( blink::mojom::AnchorElementPointerOverPtr pointer_over_event) { - auto& user_interaction = - user_interactions_[AnchorId(pointer_over_event->anchor_id)]; + auto& user_interactions_data = GetUserInteractionsData(); + auto& user_interactions = user_interactions_data.user_interactions_; + auto index_it = + tracked_anchor_id_to_index_.find(AnchorId(pointer_over_event->anchor_id)); + if (index_it == tracked_anchor_id_to_index_.end()) { + return; + } + + auto& user_interaction = user_interactions[index_it->second]; + if (!user_interaction.is_hovered) { + user_interaction.pointer_hovering_over_count++; + } user_interaction.is_hovered = true; user_interaction.last_navigation_start_to_pointer_over = pointer_over_event->navigation_start_to_pointer_over; @@ -230,7 +267,15 @@ void NavigationPredictor::ReportAnchorElementPointerOut( blink::mojom::AnchorElementPointerOutPtr hover_event) { - auto& user_interaction = user_interactions_[AnchorId(hover_event->anchor_id)]; + auto& user_interactions_data = GetUserInteractionsData(); + auto& user_interactions = user_interactions_data.user_interactions_; + auto index_it = + tracked_anchor_id_to_index_.find(AnchorId(hover_event->anchor_id)); + if (index_it == tracked_anchor_id_to_index_.end()) { + return; + } + + auto& user_interaction = user_interactions[index_it->second]; user_interaction.is_hovered = false; user_interaction.last_navigation_start_to_pointer_over.reset(); user_interaction.max_hover_dwell_time = std::max( @@ -248,9 +293,17 @@ return; } + auto& user_interactions_data = GetUserInteractionsData(); + auto& user_interactions = user_interactions_data.user_interactions_; for (const auto& element : elements) { AnchorId anchor_id(element->anchor_id); - auto& user_interaction = user_interactions_[anchor_id]; + auto index_it = tracked_anchor_id_to_index_.find(anchor_id); + if (index_it == tracked_anchor_id_to_index_.end()) { + // We're not tracking this element, no need to generate a + // NavigationPredictorAnchorElementMetrics record. + continue; + } + auto& user_interaction = user_interactions[index_it->second]; user_interaction.is_in_viewport = true; user_interaction.last_navigation_start_to_entered_viewport = element->navigation_start_to_entered_viewport; @@ -278,12 +331,6 @@ continue; } - auto index_it = tracked_anchor_id_to_index_.find(anchor_id); - if (index_it == tracked_anchor_id_to_index_.end()) { - // We're not tracking this element, no need to generate a - // NavigationPredictorAnchorElementMetrics record. - continue; - } ukm::builders::NavigationPredictorAnchorElementMetrics anchor_element_builder(ukm_source_id_);
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h index 73a51c3..0a6ed6e 100644 --- a/chrome/browser/navigation_predictor/navigation_predictor.h +++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -13,6 +13,7 @@ #include "base/memory/raw_ptr.h" #include "base/sequence_checker.h" #include "base/time/time.h" +#include "chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h" #include "content/public/browser/document_service.h" #include "content/public/browser/visibility.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -45,35 +46,6 @@ private: friend class MockNavigationPredictorForTesting; using AnchorId = base::StrongAlias<class AnchorId, uint32_t>; - // This structure holds the user interactions with a given anchor element. - // Whenever, the user clicks on a link, we iterate over all |UserInteractions| - // data and check if the anchor element is still in viewport or not. If it is - // still in viewport, we use |last_navigation_start_to_entered_viewport| and - // |navigation_start_to_click_| to update |max_time_in_viewport|. Similarly, - // we also check if the pointer is still hovering over the anchor element, and - // use |last_navigation_start_to_pointer_over| and - // |navigation_start_to_click_| to update |max_hover_dwell_time|. We then - // record |max_time_in_viewport|, and |max_hover_dwell_time| to UKM. - struct UserInteractions { - // True if the anchor element is still in viewport, otherwise false. - bool is_in_viewport = false; - // True if the pointer is still hovering over the anchor element, otherwise - // false; - bool is_hovered = false; - // If the anchor element is still in viewport, it is the TimeDelta between - // the navigation start of the anchor element's root document and the last - // time the anchor element entered the viewport, otherwise empty. - absl::optional<base::TimeDelta> last_navigation_start_to_entered_viewport; - // The maximum duration that the anchor element was in the viewport. - absl::optional<base::TimeDelta> max_time_in_viewport; - // If the anchor element is still in viewport, it is the TimeDelta between - // the navigation start of the anchor element's root document and the last - // time the pointer started to hover over the anchor element, otherwise - // empty. - absl::optional<base::TimeDelta> last_navigation_start_to_pointer_over; - // The maximum the pointer hover dwell time over the anchor element. - absl::optional<base::TimeDelta> max_hover_dwell_time; - }; NavigationPredictor(content::RenderFrameHost& render_frame_host, mojo::PendingReceiver<AnchorElementMetricsHost> receiver); @@ -116,6 +88,10 @@ // |ratio_area|. int GetLinearBucketForRatioArea(int value) const; + // Returns UserInteractionsData for the current page. + PageAnchorsMetricsObserver::UserInteractionsData& GetUserInteractionsData() + const; + // A count of clicks to prevent reporting more than 10 clicks to UKM. size_t clicked_count_ = 0; @@ -125,10 +101,6 @@ typename AnchorId::Hasher> anchors_; - // User interaction data for anchor ID that we track. - std::unordered_map<AnchorId, UserInteractions, typename AnchorId::Hasher> - user_interactions_; - // The time between navigation start and the last time user clicked on a link. absl::optional<base::TimeDelta> navigation_start_to_click_;
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc index c513b99..813be22 100644 --- a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc +++ b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
@@ -21,6 +21,7 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +#include "services/metrics/public/cpp/metrics_utils.h" #include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -398,8 +399,11 @@ predictor_service()->ReportNewAnchorElements(std::move(metrics)); auto click = blink::mojom::AnchorElementClick::New(); + const long navigation_start_to_click_ms = 333; click->anchor_id = anchor_id_0; click->target_url = target_url; + click->navigation_start_to_click = + base::Milliseconds(navigation_start_to_click_ms); predictor_service()->ReportAnchorElementClick(std::move(click)); base::RunLoop().RunUntilIdle(); @@ -412,6 +416,8 @@ }; EXPECT_EQ(0, get_metric(UkmEntry::kAnchorElementIndexName)); EXPECT_EQ(1, get_metric(UkmEntry::kHrefUnchangedName)); + EXPECT_EQ(ukm::GetExponentialBucketMin(navigation_start_to_click_ms, 1.3), + get_metric(UkmEntry::kNavigationStartToLinkClickedMsName)); click = blink::mojom::AnchorElementClick::New(); click->anchor_id = anchor_id_1; @@ -466,10 +472,20 @@ return new MockNavigationPredictorForTesting(*render_frame_host, std::move(receiver)); } - const std:: - unordered_map<AnchorId, UserInteractions, typename AnchorId::Hasher>& - user_interactions() const { - return user_interactions_; + void RecordUserInteractionMetrics() { + auto& user_interactions = GetUserInteractionsData(); + user_interactions.RecordUserInteractionMetrics(ukm_source_id_, {}); + } + const std::unordered_map< + int, + PageAnchorsMetricsObserver::UserInteractionsData::UserInteractions>& + user_interactions() const { + return GetUserInteractionsData().user_interactions_; + } + const PageAnchorsMetricsObserver::UserInteractionsData::UserInteractions& + user_interaction(AnchorId anchor_id) { + auto index_it = tracked_anchor_id_to_index_.find(anchor_id); + return GetUserInteractionsData().user_interactions_[index_it->second]; } absl::optional<base::TimeDelta> navigation_start_to_click() { return navigation_start_to_click_; @@ -511,15 +527,26 @@ base::RunLoop().RunUntilIdle(); }; - MockNavigationPredictorForTesting::AnchorId anchor_id{1}; + auto report_new_anchor_element = [&predictor_service, this]() { + std::vector<blink::mojom::AnchorElementMetricsPtr> metrics; + metrics.push_back(CreateMetricsPtr()); + + MockNavigationPredictorForTesting::AnchorId anchor_id( + metrics[0]->anchor_id); + predictor_service->ReportNewAnchorElements(std::move(metrics)); + return anchor_id; + }; + + auto const anchor_id = report_new_anchor_element(); + // Anchor element entered the viewport for the first time. Check user // interaction data to see if it is registered. const auto navigation_start_to_entered_viewport_1 = base::Milliseconds(150); report_anchor_element_entered_viewport( anchor_id, navigation_start_to_entered_viewport_1); - EXPECT_EQ(1u, predictor_service_host->user_interactions().size()); + ASSERT_EQ(1u, predictor_service_host->user_interactions().size()); const auto& user_interactions = - predictor_service_host->user_interactions().at(anchor_id); + predictor_service_host->user_interaction(anchor_id); EXPECT_TRUE(user_interactions.is_in_viewport); EXPECT_TRUE( user_interactions.last_navigation_start_to_entered_viewport.has_value()); @@ -592,14 +619,25 @@ base::RunLoop().RunUntilIdle(); }; - MockNavigationPredictorForTesting::AnchorId anchor_id{1}; + auto report_new_anchor_element = [&predictor_service, this]() { + std::vector<blink::mojom::AnchorElementMetricsPtr> metrics; + metrics.push_back(CreateMetricsPtr()); + + MockNavigationPredictorForTesting::AnchorId anchor_id( + metrics[0]->anchor_id); + predictor_service->ReportNewAnchorElements(std::move(metrics)); + return anchor_id; + }; + + auto const anchor_id = report_new_anchor_element(); + // Pointer started hovering over the anchor element for the first time. Check // user interaction data to see if it is registered. const auto navigation_start_to_pointer_over_1 = base::Milliseconds(150); report_pointer_over(anchor_id, navigation_start_to_pointer_over_1); - EXPECT_EQ(1u, predictor_service_host->user_interactions().size()); + ASSERT_EQ(1u, predictor_service_host->user_interactions().size()); const auto& user_interactions = - predictor_service_host->user_interactions().at(anchor_id); + predictor_service_host->user_interaction(anchor_id); EXPECT_TRUE(user_interactions.is_hovered); EXPECT_TRUE( user_interactions.last_navigation_start_to_pointer_over.has_value()); @@ -676,3 +714,146 @@ EXPECT_EQ(navigation_start_to_click, predictor_service_host->navigation_start_to_click()); } + +TEST_F(NavigationPredictorTest, RecordUserInteractionMetrics) { + using AnchorId = uint32_t; + mojo::Remote<blink::mojom::AnchorElementMetricsHost> predictor_service; + auto* predictor_service_host = MockNavigationPredictorForTesting::Create( + main_rfh(), predictor_service.BindNewPipeAndPassReceiver()); + + auto report_anchor_element_left_viewport = + [&predictor_service](AnchorId anchor_id, + const base::TimeDelta& time_in_viewport) { + std::vector<blink::mojom::AnchorElementLeftViewportPtr> metrics; + metrics.push_back(blink::mojom::AnchorElementLeftViewport::New( + anchor_id, time_in_viewport)); + predictor_service->ReportAnchorElementsLeftViewport(std::move(metrics)); + base::RunLoop().RunUntilIdle(); + }; + + auto report_anchor_element_entered_viewport = + [&predictor_service]( + AnchorId anchor_id, + const base::TimeDelta& navigation_start_to_entered_viewport) { + std::vector<blink::mojom::AnchorElementEnteredViewportPtr> metrics; + metrics.push_back(blink::mojom::AnchorElementEnteredViewport::New( + anchor_id, navigation_start_to_entered_viewport)); + predictor_service->ReportAnchorElementsEnteredViewport( + std::move(metrics)); + base::RunLoop().RunUntilIdle(); + }; + auto report_anchor_element_pointer_over = + [&predictor_service]( + AnchorId anchor_id, + const base::TimeDelta& navigation_start_to_pinter_over) { + blink::mojom::AnchorElementPointerOverPtr metrics = + blink::mojom::AnchorElementPointerOver::New( + anchor_id, navigation_start_to_pinter_over); + predictor_service->ReportAnchorElementPointerOver(std::move(metrics)); + base::RunLoop().RunUntilIdle(); + }; + + auto report_anchor_element_pointer_hover_dwell_time = + [&predictor_service](AnchorId anchor_id, + const base::TimeDelta& hover_dwell_time) { + blink::mojom::AnchorElementPointerOutPtr metrics = + blink::mojom::AnchorElementPointerOut::New(anchor_id, + hover_dwell_time); + predictor_service->ReportAnchorElementPointerOut(std::move(metrics)); + base::RunLoop().RunUntilIdle(); + }; + + ukm::TestAutoSetUkmRecorder ukm_recorder; + std::vector<blink::mojom::AnchorElementMetricsPtr> metrics; + metrics.push_back(CreateMetricsPtr()); + metrics.push_back(CreateMetricsPtr()); + + int anchor_id_0 = metrics[0]->anchor_id; + int anchor_id_1 = metrics[1]->anchor_id; + GURL target_url_1 = metrics[1]->target_url; + predictor_service->ReportNewAnchorElements(std::move(metrics)); + + // Both anchors enter the viewport. + const int navigation_start_to_entered_viewport = 30; + report_anchor_element_entered_viewport( + anchor_id_0, base::Milliseconds(navigation_start_to_entered_viewport)); + report_anchor_element_entered_viewport( + anchor_id_1, base::Milliseconds(navigation_start_to_entered_viewport)); + + // Mouse hover over anchor element 0 and moves away. + const int navigation_start_to_pinter_over_0 = 140; + const int hover_dwell_time_0 = 60; + report_anchor_element_pointer_over( + anchor_id_0, base::Milliseconds(navigation_start_to_pinter_over_0)); + report_anchor_element_pointer_hover_dwell_time( + anchor_id_0, base::Milliseconds(hover_dwell_time_0)); + + // Anchor element 0 leaves the viewport. + const int time_in_viewport_0 = 250; + report_anchor_element_left_viewport(anchor_id_0, + base::Milliseconds(time_in_viewport_0)); + + // Mouse hover over anchor element 1 and stays there. + const int navigation_start_to_pinter_over_1 = 280; + report_anchor_element_pointer_over( + anchor_id_1, base::Milliseconds(navigation_start_to_pinter_over_1)); + + // Mouse clicks on anchor element 1. + const int navigation_start_to_click_ms = 430; + auto click = blink::mojom::AnchorElementClick::New(); + click->anchor_id = anchor_id_1; + click->target_url = target_url_1; + click->navigation_start_to_click = + base::Milliseconds(navigation_start_to_click_ms); + predictor_service->ReportAnchorElementClick(std::move(click)); + base::RunLoop().RunUntilIdle(); + + predictor_service_host->RecordUserInteractionMetrics(); + base::RunLoop().RunUntilIdle(); + + // Now check the UKM records. + using UkmEntry = ukm::builders::NavigationPredictorUserInteractions; + auto entries = ukm_recorder.GetEntriesByName(UkmEntry::kEntryName); + ASSERT_EQ(2u, entries.size()); + auto get_metric = [&](auto anchor_id, auto name) { + return *ukm_recorder.GetEntryMetric(entries[anchor_id], name); + }; + for (size_t i = 0; i < entries.size(); i++) { + int anchor_id = get_metric(i, UkmEntry::kAnchorIndexName); + EXPECT_TRUE(anchor_id == 0 || anchor_id == 1); + switch (anchor_id) { + // Anchor element 0. + case 0: + EXPECT_EQ(0, get_metric(i, UkmEntry::kIsInViewportName)); + EXPECT_EQ(0, get_metric(i, UkmEntry::kIsPointerHoveringOverName)); + EXPECT_EQ( + ukm::GetExponentialBucketMin(time_in_viewport_0, 1.3), + get_metric(i, UkmEntry::kMaxEnteredViewportToLeftViewportMsName)); + EXPECT_EQ(ukm::GetExponentialBucketMin(hover_dwell_time_0, 1.3), + get_metric(i, UkmEntry::kMaxHoverDwellTimeMsName)); + EXPECT_EQ(ukm::GetExponentialBucketMin(1, 1.3), + get_metric(i, UkmEntry::kPointerHoveringOverCountName)); + break; + + // Anchor element 1. + case 1: + EXPECT_EQ(1, get_metric(i, UkmEntry::kAnchorIndexName)); + EXPECT_EQ(1, get_metric(i, UkmEntry::kIsInViewportName)); + EXPECT_EQ(1, get_metric(i, UkmEntry::kIsPointerHoveringOverName)); + EXPECT_EQ( + ukm::GetExponentialBucketMin( + navigation_start_to_click_ms - + navigation_start_to_entered_viewport, + 1.3), + get_metric(i, UkmEntry::kMaxEnteredViewportToLeftViewportMsName)); + EXPECT_EQ( + ukm::GetExponentialBucketMin(navigation_start_to_click_ms - + navigation_start_to_pinter_over_1, + 1.3), + get_metric(i, UkmEntry::kMaxHoverDwellTimeMsName)); + EXPECT_EQ(ukm::GetExponentialBucketMin(1, 1.3), + get_metric(i, UkmEntry::kPointerHoveringOverCountName)); + break; + } + } +}
diff --git a/chrome/browser/net/cert_verifier_service_browsertest.cc b/chrome/browser/net/cert_verifier_service_browsertest.cc index a589f94..b48d613 100644 --- a/chrome/browser/net/cert_verifier_service_browsertest.cc +++ b/chrome/browser/net/cert_verifier_service_browsertest.cc
@@ -2,25 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/strcat.h" #include "base/test/scoped_feature_list.h" +#include "base/test/test_future.h" #include "build/build_config.h" #include "chrome/browser/net/cert_verifier_configuration.h" -#include "chrome/common/buildflags.h" -#include "net/net_buildflags.h" - -#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) -#include "base/strings/strcat.h" -#include "base/test/test_future.h" #include "chrome/browser/policy/policy_test_utils.h" +#include "chrome/common/buildflags.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/policy_constants.h" #include "content/public/browser/network_service_instance.h" #include "content/public/test/browser_test.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" +#include "net/net_buildflags.h" #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#endif // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) class CertVerifierServiceChromeRootStoreFeaturePolicyTest @@ -122,3 +120,112 @@ : "PolicyNotSet"}); }); #endif // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) + +class CertVerifierServiceEnforceLocalAnchorConstraintsFeaturePolicyTest + : public policy::PolicyTest, + public testing::WithParamInterface< + std::tuple<bool, absl::optional<bool>>> { + public: + void SetUpInProcessBrowserTestFixture() override { + scoped_feature_list_.InitWithFeatureState( + net::features::kEnforceLocalAnchorConstraints, + feature_enforce_local_anchor_constraints()); + + policy::PolicyTest::SetUpInProcessBrowserTestFixture(); + + auto policy_val = policy_enforce_local_anchor_constraints(); + if (policy_val.has_value()) { + SetPolicyValue(*policy_val); + } + } + + void SetPolicyValue(absl::optional<bool> value) { + policy::PolicyMap policies; +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + SetPolicy(&policies, policy::key::kEnforceLocalAnchorConstraintsEnabled, + absl::optional<base::Value>(value)); +#endif + UpdateProviderPolicy(policies); + } + + void ExpectEnforceLocalAnchorConstraintsCorrect( + bool enforce_local_anchor_constraints) { + EXPECT_EQ(enforce_local_anchor_constraints, + net::IsLocalAnchorConstraintsEnforcementEnabled()); + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + // Set policy to the opposite value, and then test the value returned by + // IsLocalAnchorConstraintsEnforcementEnabled has changed. + SetPolicyValue(!enforce_local_anchor_constraints); + EXPECT_EQ(!enforce_local_anchor_constraints, + net::IsLocalAnchorConstraintsEnforcementEnabled()); + + // Unset the policy, the value used should go back to the one set by the + // feature flag. + SetPolicyValue(absl::nullopt); + EXPECT_EQ(feature_enforce_local_anchor_constraints(), + net::IsLocalAnchorConstraintsEnforcementEnabled()); +#endif + } + + bool feature_enforce_local_anchor_constraints() const { + return std::get<0>(GetParam()); + } + + absl::optional<bool> policy_enforce_local_anchor_constraints() const { + return std::get<1>(GetParam()); + } + + bool expected_enforce_local_anchor_constraints() const { + auto policy_val = policy_enforce_local_anchor_constraints(); + if (policy_val.has_value()) { + return *policy_val; + } + return feature_enforce_local_anchor_constraints(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_P( + CertVerifierServiceEnforceLocalAnchorConstraintsFeaturePolicyTest, + Test) { +#if BUILDFLAG(IS_ANDROID) + // TODO(https://crbug.com/1410924): Avoid flake on android browser tests by + // requiring the test to always take at least 1 second to finish. Remove this + // delay once issue 1410924 is resolved. + base::RunLoop run_loop; + base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), base::Seconds(1)); +#endif + ExpectEnforceLocalAnchorConstraintsCorrect( + expected_enforce_local_anchor_constraints()); +#if BUILDFLAG(IS_ANDROID) + run_loop.Run(); +#endif +} + +INSTANTIATE_TEST_SUITE_P( + All, + CertVerifierServiceEnforceLocalAnchorConstraintsFeaturePolicyTest, + ::testing::Combine(::testing::Bool(), + ::testing::Values(absl::nullopt +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + , + false, + true +#endif + )), + [](const testing::TestParamInfo< + CertVerifierServiceEnforceLocalAnchorConstraintsFeaturePolicyTest:: + ParamType>& info) { + return base::StrCat( + {std::get<0>(info.param) ? "FeatureTrue" : "FeatureFalse", + std::get<1>(info.param).has_value() + ? (*std::get<1>(info.param) ? "PolicyTrue" : "PolicyFalse") + : "PolicyNotSet"}); + });
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc index 3f6fcfb..398387d 100644 --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc
@@ -66,6 +66,7 @@ #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cookies/cookie_util.h" #include "net/net_buildflags.h" #include "net/third_party/uri_template/uri_template.h" @@ -487,6 +488,17 @@ &SystemNetworkContextManager::UpdateExplicitlyAllowedNetworkPorts, base::Unretained(this))); +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + pref_change_registrar_.Add( + prefs::kEnforceLocalAnchorConstraintsEnabled, + base::BindRepeating(&SystemNetworkContextManager:: + UpdateEnforceLocalAnchorConstraintsEnabled, + base::Unretained(this))); + // Call the update function immediately to set the initial value, if any. + UpdateEnforceLocalAnchorConstraintsEnabled(); +#endif + if (content::IsOutOfProcessNetworkService() && base::FeatureList::IsEnabled( features::kRestartNetworkServiceUnsandboxedForFailedLaunch)) { @@ -555,6 +567,13 @@ // evaluated when it is managed. registry->RegisterBooleanPref(prefs::kChromeRootStoreEnabled, false); #endif // BUILDFLAG(CHROME_ROOT_STORE_POLICY_SUPPORTED) +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + // Note that the default value is not relevant because the pref is only + // evaluated when it is managed. + registry->RegisterBooleanPref(prefs::kEnforceLocalAnchorConstraintsEnabled, + true); +#endif registry->RegisterListPref(prefs::kExplicitlyAllowedNetworkPorts); @@ -924,6 +943,25 @@ ConvertExplicitlyAllowedNetworkPortsPref(local_state_)); } +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +void SystemNetworkContextManager::UpdateEnforceLocalAnchorConstraintsEnabled() { + const PrefService::Preference* enforce_local_anchor_constraints_enabled_pref = + local_state_->FindPreference( + prefs::kEnforceLocalAnchorConstraintsEnabled); + if (enforce_local_anchor_constraints_enabled_pref->IsManaged()) { + // This depends on the CertVerifierService running in the browser process. + // If that changes, this would need to become a mojo message. + net::SetLocalAnchorConstraintsEnforcementEnabled( + enforce_local_anchor_constraints_enabled_pref->GetValue()->GetBool()); + } else { + net::SetLocalAnchorConstraintsEnforcementEnabled( + base::FeatureList::IsEnabled( + net::features::kEnforceLocalAnchorConstraints)); + } +} +#endif + void SystemNetworkContextManager::UpdateReferrersEnabled() { GetContext()->SetEnableReferrers(enable_referrers_.GetValue()); }
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h index e12acea..48bad7db 100644 --- a/chrome/browser/net/system_network_context_manager.h +++ b/chrome/browser/net/system_network_context_manager.h
@@ -193,6 +193,13 @@ // the network process. void UpdateExplicitlyAllowedNetworkPorts(); +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + // Applies the current value of the kEnforceLocalAnchorConstraintsEnabled + // pref to the enforcement state. + void UpdateEnforceLocalAnchorConstraintsEnabled(); +#endif + // The PrefService to retrieve all the pref values. raw_ptr<PrefService> local_state_;
diff --git a/chrome/browser/page_load_metrics/observers/formfill_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/formfill_page_load_metrics_observer_browsertest.cc index 59c6ef6..1f2a9799 100644 --- a/chrome/browser/page_load_metrics/observers/formfill_page_load_metrics_observer_browsertest.cc +++ b/chrome/browser/page_load_metrics/observers/formfill_page_load_metrics_observer_browsertest.cc
@@ -47,7 +47,7 @@ private: autofill::TestAutofillManagerWaiter waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {autofill::AutofillManagerEvent::kFormsSeen}}; }; void SetUpOnMainThread() override {
diff --git a/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.cc index 7c02518..78bcbcd 100644 --- a/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h" #include "content/public/browser/web_contents_user_data.h" +#include "services/metrics/public/cpp/metrics_utils.h" #include "services/metrics/public/cpp/ukm_builders.h" PageAnchorsMetricsObserver::AnchorsData::AnchorsData( @@ -37,7 +38,82 @@ WEB_CONTENTS_USER_DATA_KEY_IMPL(PageAnchorsMetricsObserver::AnchorsData); -void PageAnchorsMetricsObserver::RecordUkm() { +PageAnchorsMetricsObserver::UserInteractionsData::UserInteractionsData( + content::WebContents* contents) + : content::WebContentsUserData<UserInteractionsData>(*contents) {} + +PageAnchorsMetricsObserver::UserInteractionsData::~UserInteractionsData() = + default; + +void PageAnchorsMetricsObserver::UserInteractionsData:: + RecordUserInteractionMetrics( + ukm::SourceId ukm_source_id, + absl::optional<base::TimeDelta> navigation_start_to_now) { + // In case we don't have a valid |navigation_start_to_click_|, the best we + // could do is to use |navigation_start_to_now|. It may cause some + // inconsistency in the measurements but it is better than not recording it. + if (!navigation_start_to_click_.has_value()) { + navigation_start_to_click_ = navigation_start_to_now; + } + + auto get_max_time_ms = [this](auto const& max_time, + auto const last_navigation_start_to) { + int64_t max_time_ms = -1; + if (last_navigation_start_to.has_value() && + navigation_start_to_click_.has_value()) { + max_time_ms = std::max(max_time_ms, (navigation_start_to_click_.value() - + last_navigation_start_to.value()) + .InMilliseconds()); + } + if (max_time.has_value()) { + max_time_ms = std::max(max_time_ms, max_time.value().InMilliseconds()); + } + return max_time_ms; + }; + + auto* ukm_recorder = ukm::UkmRecorder::Get(); + + for (const auto& [anchor_index, user_interaction] : user_interactions_) { + ukm::builders::NavigationPredictorUserInteractions builder(ukm_source_id); + builder.SetAnchorIndex(anchor_index); + builder.SetIsInViewport(user_interaction.is_in_viewport); + builder.SetPointerHoveringOverCount(ukm::GetExponentialBucketMin( + user_interaction.pointer_hovering_over_count, 1.3)); + builder.SetIsPointerHoveringOver(user_interaction.is_hovered); + builder.SetMaxEnteredViewportToLeftViewportMs(ukm::GetExponentialBucketMin( + get_max_time_ms( + user_interaction.max_time_in_viewport, + user_interaction.last_navigation_start_to_entered_viewport), + 1.3)); + builder.SetMaxHoverDwellTimeMs(ukm::GetExponentialBucketMin( + get_max_time_ms(user_interaction.max_hover_dwell_time, + user_interaction.last_navigation_start_to_pointer_over), + 1.3)); + builder.Record(ukm_recorder); + } +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL( + PageAnchorsMetricsObserver::UserInteractionsData); + +void PageAnchorsMetricsObserver::RecordUserInteractionDataToUkm() { + PageAnchorsMetricsObserver::UserInteractionsData* data = + PageAnchorsMetricsObserver::UserInteractionsData::FromWebContents( + web_contents_); + if (!data) { + return; + } + auto ukm_source_id = GetDelegate().GetPageUkmSourceId(); + absl::optional<base::TimeDelta> navigation_start_to_now; + const base::TimeTicks navigation_start_time = + GetDelegate().GetNavigationStart(); + if (!navigation_start_time.is_null()) { + navigation_start_to_now = base::TimeTicks::Now() - navigation_start_time; + } + data->RecordUserInteractionMetrics(ukm_source_id, navigation_start_to_now); +} + +void PageAnchorsMetricsObserver::RecordAnchorDataToUkm() { PageAnchorsMetricsObserver::AnchorsData* data = PageAnchorsMetricsObserver::AnchorsData::FromWebContents(web_contents_); if (!data || data->number_of_anchors_ == 0) { @@ -85,7 +161,8 @@ if (is_in_prerendered_page_) return; - RecordUkm(); + RecordAnchorDataToUkm(); + RecordUserInteractionDataToUkm(); } page_load_metrics::PageLoadMetricsObserver::ObservePolicy PageAnchorsMetricsObserver::FlushMetricsOnAppEnterBackground( @@ -94,7 +171,8 @@ if (is_in_prerendered_page_) return CONTINUE_OBSERVING; - RecordUkm(); + RecordAnchorDataToUkm(); + RecordUserInteractionDataToUkm(); return STOP_OBSERVING; }
diff --git a/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h b/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h index 607510b6..d22a1ef 100644 --- a/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h +++ b/chrome/browser/page_load_metrics/observers/page_anchors_metrics_observer.h
@@ -18,6 +18,64 @@ class PageAnchorsMetricsObserver : public page_load_metrics::PageLoadMetricsObserver { public: + class UserInteractionsData + : public content::WebContentsUserData<UserInteractionsData> { + public: + UserInteractionsData(const UserInteractionsData&) = delete; + ~UserInteractionsData() override; + UserInteractionsData& operator=(const UserInteractionsData&) = delete; + + // This structure holds the user interactions with a given anchor element. + // Whenever, the user clicks on a link, we iterate over all + // |UserInteractions| data and check if the anchor element is still in + // viewport or not. If it is still in viewport, we use + // |last_navigation_start_to_entered_viewport| and + // |navigation_start_to_click_| to update |max_time_in_viewport|. Similarly, + // we also check if the pointer is still hovering over the anchor element, + // and use |last_navigation_start_to_pointer_over| and + // |navigation_start_to_click_| to update |max_hover_dwell_time|. We then + // record |max_time_in_viewport|, and |max_hover_dwell_time| to UKM. + struct UserInteractions { + // True if the anchor element is still in viewport, otherwise false. + bool is_in_viewport = false; + // True if the pointer is still hovering over the anchor element, + // otherwise false; + bool is_hovered = false; + // Number of times the pointer was hovering over the anchor element. + int pointer_hovering_over_count = 0; + // If the anchor element is still in viewport, it is the TimeDelta between + // the navigation start of the anchor element's root document and the last + // time the anchor element entered the viewport, otherwise empty. + absl::optional<base::TimeDelta> last_navigation_start_to_entered_viewport; + // The maximum duration that the anchor element was in the viewport. + absl::optional<base::TimeDelta> max_time_in_viewport; + // If the anchor element is still in viewport, it is the TimeDelta between + // the navigation start of the anchor element's root document and the last + // time the pointer started to hover over the anchor element, otherwise + // empty. + absl::optional<base::TimeDelta> last_navigation_start_to_pointer_over; + // The maximum the pointer hover dwell time over the anchor element. + absl::optional<base::TimeDelta> max_hover_dwell_time; + }; + + // Records user interaction metrics to UKM. + void RecordUserInteractionMetrics( + ukm::SourceId ukm_source_id, + absl::optional<base::TimeDelta> navigation_start_to_now); + + // User interaction data for the tracked anchor index. + std::unordered_map<int, UserInteractions> user_interactions_; + + // The time between navigation start and the last time user clicked on a + // link. + absl::optional<base::TimeDelta> navigation_start_to_click_; + + private: + friend class content::WebContentsUserData<UserInteractionsData>; + explicit UserInteractionsData(content::WebContents* contents); + WEB_CONTENTS_USER_DATA_KEY_DECL(); + }; + class AnchorsData : public content::WebContentsUserData<AnchorsData> { public: AnchorsData(const AnchorsData&) = delete; @@ -65,7 +123,8 @@ content::NavigationHandle* navigation_handle) override; private: - void RecordUkm(); + void RecordAnchorDataToUkm(); + void RecordUserInteractionDataToUkm(); bool is_in_prerendered_page_ = false;
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 28bc307..c8e1c6c 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1710,6 +1710,13 @@ base::Value::Type::BOOLEAN }, #endif // BUILDFLAG(CHROME_ROOT_STORE_POLICY_SUPPORTED) +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + { key::kEnforceLocalAnchorConstraintsEnabled, + prefs::kEnforceLocalAnchorConstraintsEnabled, + base::Value::Type::BOOLEAN }, +#endif + { key::kScrollToTextFragmentEnabled, prefs::kScrollToTextFragmentEnabled, base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc index 08a143d..304bdbb7 100644 --- a/chrome/browser/printing/print_browsertest.cc +++ b/chrome/browser/printing/print_browsertest.cc
@@ -2419,11 +2419,13 @@ std::unique_ptr<PrintingContext::Delegate> printing_context_delegate, std::unique_ptr<PrintingContext> printing_context, PrintJob* print_job, + mojom::PrintTargetType print_target_type, bool simulate_spooling_memory_errors, TestPrintJobWorkerOop::PrintCallbacks* callbacks) : PrintJobWorkerOop(std::move(printing_context_delegate), std::move(printing_context), print_job, + print_target_type, simulate_spooling_memory_errors), callbacks_(callbacks) {} TestPrintJobWorkerOop(const TestPrintJobWorkerOop&) = delete; @@ -2511,11 +2513,12 @@ } #endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) - std::unique_ptr<PrintJobWorker> TransferContextToNewWorker( + std::unique_ptr<PrintJobWorkerOop> CreatePrintJobWorker( PrintJob* print_job) override { return std::make_unique<TestPrintJobWorkerOop>( std::move(printing_context_delegate_), std::move(printing_context_), - print_job, simulate_spooling_memory_errors_, callbacks_); + print_job, print_target_type(), simulate_spooling_memory_errors_, + callbacks_); } bool simulate_spooling_memory_errors_;
diff --git a/chrome/browser/printing/print_job_worker_oop.cc b/chrome/browser/printing/print_job_worker_oop.cc index 4efd0ee..8ba3d0c 100644 --- a/chrome/browser/printing/print_job_worker_oop.cc +++ b/chrome/browser/printing/print_job_worker_oop.cc
@@ -50,20 +50,23 @@ std::unique_ptr<PrintingContext> printing_context, PrintJob* print_job, mojom::PrintTargetType print_target_type) - : PrintJobWorker(std::move(printing_context_delegate), - std::move(printing_context), - print_job), - print_target_type_(print_target_type) {} + : PrintJobWorkerOop(std::move(printing_context_delegate), + std::move(printing_context), + print_job, + print_target_type, + /*simulate_spooling_memory_errors=*/false) {} PrintJobWorkerOop::PrintJobWorkerOop( std::unique_ptr<PrintingContext::Delegate> printing_context_delegate, std::unique_ptr<PrintingContext> printing_context, PrintJob* print_job, + mojom::PrintTargetType print_target_type, bool simulate_spooling_memory_errors) : PrintJobWorker(std::move(printing_context_delegate), std::move(printing_context), print_job), - simulate_spooling_memory_errors_(simulate_spooling_memory_errors) {} + simulate_spooling_memory_errors_(simulate_spooling_memory_errors), + print_target_type_(print_target_type) {} PrintJobWorkerOop::~PrintJobWorkerOop() { DCHECK(!service_manager_client_id_.has_value());
diff --git a/chrome/browser/printing/print_job_worker_oop.h b/chrome/browser/printing/print_job_worker_oop.h index aff12ab8..b92e14d 100644 --- a/chrome/browser/printing/print_job_worker_oop.h +++ b/chrome/browser/printing/print_job_worker_oop.h
@@ -53,6 +53,7 @@ std::unique_ptr<PrintingContext::Delegate> printing_context_delegate, std::unique_ptr<PrintingContext> printing_context, PrintJob* print_job, + mojom::PrintTargetType print_target_type, bool simulate_spooling_memory_errors); virtual void OnDidStartPrinting(mojom::ResultCode result); @@ -99,7 +100,7 @@ void SendCancel(scoped_refptr<PrintJob> job); // Used to test spooling memory error handling. - bool simulate_spooling_memory_errors_ = false; + const bool simulate_spooling_memory_errors_; // Client ID with the print backend service manager for this print job. // Used only from UI thread.
diff --git a/chrome/browser/printing/printer_query_oop.cc b/chrome/browser/printing/printer_query_oop.cc index 7fbf5f9d..24c6772 100644 --- a/chrome/browser/printing/printer_query_oop.cc +++ b/chrome/browser/printing/printer_query_oop.cc
@@ -42,9 +42,9 @@ std::unique_ptr<PrintJobWorker> PrinterQueryOop::TransferContextToNewWorker( PrintJob* print_job) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - return std::make_unique<PrintJobWorkerOop>( - std::move(printing_context_delegate_), std::move(printing_context_), - print_job, print_target_type_); + // TODO(crbug.com/1414968) Do extra setup on the worker as needed for + // supporting OOP system print dialogs. + return CreatePrintJobWorker(print_job); } void PrinterQueryOop::OnDidUseDefaultSettings( @@ -224,4 +224,11 @@ } #endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG) +std::unique_ptr<PrintJobWorkerOop> PrinterQueryOop::CreatePrintJobWorker( + PrintJob* print_job) { + return std::make_unique<PrintJobWorkerOop>( + std::move(printing_context_delegate_), std::move(printing_context_), + print_job, print_target_type_); +} + } // namespace printing
diff --git a/chrome/browser/printing/printer_query_oop.h b/chrome/browser/printing/printer_query_oop.h index 53aed761..cf09c05 100644 --- a/chrome/browser/printing/printer_query_oop.h +++ b/chrome/browser/printing/printer_query_oop.h
@@ -10,6 +10,7 @@ #include "base/functional/callback.h" #include "base/values.h" #include "build/build_config.h" +#include "chrome/browser/printing/print_job_worker_oop.h" #include "chrome/browser/printing/printer_query.h" #include "chrome/services/printing/public/mojom/print_backend_service.mojom.h" #include "printing/buildflags/buildflags.h" @@ -59,6 +60,14 @@ SettingsCallback callback); #endif + // Used by `TransferContextToNewWorker()`. Virtual to support testing. + virtual std::unique_ptr<PrintJobWorkerOop> CreatePrintJobWorker( + PrintJob* print_job); + + mojom::PrintTargetType print_target_type() const { + return print_target_type_; + } + private: mojom::PrintTargetType print_target_type_ = mojom::PrintTargetType::kDirectToDevice;
diff --git a/chrome/browser/resources/chromeos/contact_center_insights/background.js b/chrome/browser/resources/chromeos/contact_center_insights/background.js index 963078f..9cdbcfdb 100644 --- a/chrome/browser/resources/chromeos/contact_center_insights/background.js +++ b/chrome/browser/resources/chromeos/contact_center_insights/background.js
@@ -2,107 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Bundle needs to be imported first so we can support closure style imports -// that follow -importScripts('ccaas_deps.js'); - -goog.require('proto.reporting.Record'); -goog.require('proto.reporting.Destination'); -goog.require('proto.reporting.Priority'); -goog.require('proto.reporting.MetricData'); -goog.require('proto.reporting.TelemetryData'); -goog.require('proto.reporting.UserStatusTelemetry'); -goog.require('proto.reporting.UserStatusTelemetry.DeviceActivityState'); - -const DEVICE_ACTIVITY_STATE_ALARM = 'DeviceActivityState'; -const REPORT_DEVICE_ACTIVITY_STATE_PERIOD_MINUTES = 15; -const IDLE_THRESHOLD_SECONDS = 5 /** minutes **/ * 60; - -function reportDeviceActivityState() { - chrome.idle.queryState(IDLE_THRESHOLD_SECONDS, (state) => { - const userStatusTelemetry = new proto.reporting.UserStatusTelemetry(); - const mappedState = getMappedDeviceActivityState(state); - userStatusTelemetry.setDeviceActivityState(mappedState); - - const telemetryData = new proto.reporting.TelemetryData(); - telemetryData.setUserStatusTelemetry(userStatusTelemetry); - - reportTelemetryData(telemetryData); - }); -} - -/** - * Returns the internal representation for the current device activity state. - * @param {string} activityState Device activity state. - * @return {!proto.reporting.UserStatusTelemetry.DeviceActivityState} internal - * proto enum representation. - */ -function getMappedDeviceActivityState(activityState) { - switch (activityState) { - case 'active': - return proto.reporting.UserStatusTelemetry.DeviceActivityState.ACTIVE; - case 'idle': - return proto.reporting.UserStatusTelemetry.DeviceActivityState.IDLE; - case 'locked': - return proto.reporting.UserStatusTelemetry.DeviceActivityState.LOCKED; - default: - return proto.reporting.UserStatusTelemetry.DeviceActivityState - .DEVICE_ACTIVITY_STATE_UNKNOWN; - } -} - -/** - * Reports collected telemetry data. - * @param {!proto.reporting.TelemetryData} telemetryData Data to report. - */ -function reportTelemetryData(telemetryData) { - const metricData = new proto.reporting.MetricData(); - metricData.setTelemetryData(telemetryData); - - // Also set metric timestamp (in ms) because the server also checks for these - // in addition to the ones set with the record (in microseconds) today. - const timestampMs = Date.now(); - metricData.setTimestampMs(timestampMs); - - const record = new proto.reporting.Record(); - record.setDestination(proto.reporting.Destination.TELEMETRY_METRIC); - record.setData(metricData.serializeBinary()); - record.setTimestampUs(timestampMs * 1000); - - // Prepare enqueue record request - const request = { - recordData: record.serializeBinary(), - priority: proto.reporting.Priority.FAST_BATCH, - eventType: chrome.enterprise.reportingPrivate.EventType.USER, - }; - - // Report prepared request - chrome.enterprise.reportingPrivate.enqueueRecord(request); -} - -/** - * Creates an alarm with specified polling interval if one is not registered - * already. - * @param {string} name Alarm name. - * @param {number} periodInMinutes Polling interval in minutes - */ -function createAlarm(name, periodInMinutes) { - chrome.alarms.get(name, (alarm) => { - if (!alarm) { - chrome.alarms.create(name, {periodInMinutes}); - } - }); -} - -// Global listener for all alarms -chrome.alarms.onAlarm.addListener((alarm) => { - if (alarm.name === DEVICE_ACTIVITY_STATE_ALARM) { - reportDeviceActivityState(); - } -}); - -// Register alarms for periodically reporting telemetry data. -chrome.runtime.onInstalled.addListener(() => { - createAlarm( - DEVICE_ACTIVITY_STATE_ALARM, REPORT_DEVICE_ACTIVITY_STATE_PERIOD_MINUTES); -}); +// We have graduated all of the metrics we used to collect using this background +// service worker. +// TODO (b/269766156): Delete this file and update the manifest once it is +// decoupled from grit.
diff --git a/chrome/browser/resources/chromeos/contact_center_insights/manifest.json b/chrome/browser/resources/chromeos/contact_center_insights/manifest.json index 286aafe..a0c84540 100644 --- a/chrome/browser/resources/chromeos/contact_center_insights/manifest.json +++ b/chrome/browser/resources/chromeos/contact_center_insights/manifest.json
@@ -2,15 +2,12 @@ // chrome-extension://oebfonohdfogiaaaelfmjlkjbgdbaahf "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxOnBHZ/Nxvc2WeeAxeSNXiOAmOPSeyjC70+3tfpbySO8Pslt/zWJBT76dhM/Gm+OwVoFdB5/C/pXfJGMqPTGFNyMq5MgJuo4giqWA542sKgn4y8lwtN4Z/2XhZPo5BXizyyxRq1lZdV21ZyImmW+3ODkC35CZ/bTXdpHzV0I5hJ14wVRzZ/fS047R5Dx+/JUJCp7uspL8nt00hPuwFmW/PLZjAnmMq3ULW216YP2VhF9ROUwRwvZqgJ5nWmfQj7dlVKstwa1PtQgqe/2p0oVif4NP/hg3+zpz7y9f0kXFRB/QVjg6R9hLGqDuqu5uh5LfTarVMH35zvaiZGhtB4rLQIDAQAB", "name": "Contact Center Insights", - "version": "1.0.1", + "version": "1.0.2", "manifest_version": 3, "background": { "service_worker": "background_wrapper.js" }, "permissions": [ - "alarms", - "background", - "enterprise.reportingPrivate", - "idle" + "background" ] }
diff --git a/chrome/browser/resources/internals/user_education/BUILD.gn b/chrome/browser/resources/internals/user_education/BUILD.gn index cce75227..6502d7b2 100644 --- a/chrome/browser/resources/internals/user_education/BUILD.gn +++ b/chrome/browser/resources/internals/user_education/BUILD.gn
@@ -12,19 +12,28 @@ preprocess_folder = "$target_gen_dir/preprocessed" html_to_wrapper("html_wrapper_files") { - in_files = [ "user_education_internals.html" ] + in_files = [ + "user_education_internals.html", + "user_education_internals_card.html", + ] } preprocess_if_expr("preprocess_gen") { in_folder = target_gen_dir out_folder = preprocess_folder - in_files = [ "user_education_internals.html.ts" ] + in_files = [ + "user_education_internals.html.ts", + "user_education_internals_card.html.ts", + ] deps = [ ":html_wrapper_files" ] } preprocess_if_expr("preprocess_src") { out_folder = preprocess_folder - in_files = [ "user_education_internals.ts" ] + in_files = [ + "user_education_internals.ts", + "user_education_internals_card.ts", + ] } copy("copy_mojo") { @@ -39,7 +48,9 @@ tsconfig_base = "tsconfig_base.json" in_files = [ "user_education_internals.ts", + "user_education_internals_card.ts", "user_education_internals.html.ts", + "user_education_internals_card.html.ts", "user_education_internals.mojom-webui.ts", ] deps = [
diff --git a/chrome/browser/resources/internals/user_education/index.html b/chrome/browser/resources/internals/user_education/index.html index 9462ae3..ea8aa9c 100644 --- a/chrome/browser/resources/internals/user_education/index.html +++ b/chrome/browser/resources/internals/user_education/index.html
@@ -13,6 +13,7 @@ html, body { + height: 100%; margin: 0; } </style>
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals.html b/chrome/browser/resources/internals/user_education/user_education_internals.html index 73186d6..369822b 100644 --- a/chrome/browser/resources/internals/user_education/user_education_internals.html +++ b/chrome/browser/resources/internals/user_education/user_education_internals.html
@@ -1,7 +1,8 @@ -<style include="cr-hidden-style"> +<style include="cr-hidden-style cr-shared-style"> :host { display: flex; flex-direction: column; + height: 100%; --main-column-max-width: 680px; --side-bar-width: 200px; @@ -9,9 +10,6 @@ cr-toolbar { min-height: 56px; - position: sticky; - top: 0; - z-index: 2; --cr-toolbar-center-basis: var(--main-column-max-width); } @@ -20,12 +18,17 @@ --cr-toolbar-left-spacer-width: var(--side-bar-width); } +#cr-container-shadow-top { + /* Needs to be higher than #container's z-index to appear above + * scrollbars. */ + z-index: 2; +} + #container { align-items: flex-start; display: flex; flex: 1; - overflow: overlay; - position: relative; + overflow: auto; } #left, @@ -42,6 +45,7 @@ display: flex; flex-basis: var(--main-column-max-width); justify-content: center; + position: relative; } #content { @@ -49,30 +53,6 @@ width: var(--main-column-max-width); } -.card { - border-top: 1px solid var(--cr-separator-color); - display: flex; - padding-block: var(--cr-section-vertical-padding); -} - -.card-content { - flex: 1; -} - -.card cr-button { - flex: 0; - margin-block-start: var(--cr-section-vertical-padding); - margin-inline-end: var(--cr-section-padding); -} - -.card ol { - padding-inline-start: 2ex; -} - -.card li { - margin-block: 1ex; -} - /* Width is left + --cr-section-padding + main. */ @media (max-width: 920px) { #main { @@ -114,56 +94,21 @@ --> <h2>Tutorials</h2> <template id="tutorials" is="dom-repeat" items="[[tutorials_]]"> - <!-- TODO(dfried): componentize this card object. --> - <div class="card" hidden$="[[!promoFilter_(item, filter)]]"> - <div class="card-content"> - <h3>[[item.displayTitle]]</h3> - <p>Added on [[formatItemDate_(item)]]</p> - <p hidden$="[[!showDescription_(item)]]"> - [[item.displayDescription]] - </p> - <p><b>Type:</b> [[item.type]]</p> - <p><b>OS: </b>[[formatPlatforms_(item)]]</p> - <div hidden$="[[!showInstructions_(item)]]"> - <p><b>Instructions:</b></p> - <ol> - <template is="dom-repeat" items="[[item.instructions]]"> - <li>[[item]]</li> - </template> - </ol> - </div> - </div> - <cr-button class="action-button" id="[[item.internalName]]" - on-click="startTutorial_"> - Launch - </cr-button> - </div> + <user-education-internals-card + id="[[item.internalName]]" + hidden$="[[!promoFilter_(item, filter)]]" + promo="[[item]]" + on-promo-launch="startTutorial_"> + </user-education-internals-card> </template> <h2>Feature Promos</h2> <template is="dom-repeat" id="promos" items="[[featurePromos_]]"> - <div class="card" hidden$="[[!promoFilter_(item, filter)]]"> - <div class="card-content"> - <h3>[[item.displayTitle]]</h3> - <p>Added on [[formatItemDate_(item)]]</p> - <p hidden$="[[!showDescription_(item)]]"> - [[item.displayDescription]] - </p> - <p><b>Type:</b> [[item.type]]</p> - <p><b>OS:</b> [[formatPlatforms_(item)]]</p> - <div hidden$="[[!showInstructions_(item)]]"> - <p><b>Instructions:</b></p> - <ol> - <template is="dom-repeat" items="[[item.instructions]]"> - <li>[[item]]</li> - </template> - </ol> - </div> - </div> - <cr-button class="action-button" id="[[item.internalName]]" - on-click="showFeaturePromo_"> - Launch - </cr-button> - </div> + <user-education-internals-card + id="[[item.internalName]]" + hidden$="[[!promoFilter_(item, filter)]]" + promo="[[item]]" + on-promo-launch="showFeaturePromo_"> + </user-education-internals-card> </template> </div> </div>
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals.ts b/chrome/browser/resources/internals/user_education/user_education_internals.ts index 22f2207..6815252 100644 --- a/chrome/browser/resources/internals/user_education/user_education_internals.ts +++ b/chrome/browser/resources/internals/user_education/user_education_internals.ts
@@ -3,25 +3,29 @@ // found in the LICENSE file. import 'chrome://resources/cr_components/help_bubble/help_bubble.js'; -import 'chrome://resources/cr_elements/cr_button/cr_button.js'; import 'chrome://resources/cr_elements/cr_hidden_style.css.js'; +import 'chrome://resources/cr_elements/cr_shared_style.css.js'; import 'chrome://resources/cr_elements/cr_shared_vars.css.js'; import 'chrome://resources/cr_elements/cr_toast/cr_toast.js'; import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js'; import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js'; import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js'; +import './user_education_internals_card.js'; import {HelpBubbleMixin, HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js'; +import {CrContainerShadowMixin, CrContainerShadowMixinInterface} from 'chrome://resources/cr_elements/cr_container_shadow_mixin.js'; import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js'; import {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js'; -import {DomRepeat, DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {getTemplate} from './user_education_internals.html.js'; import {FeaturePromoDemoPageInfo, UserEducationInternalsPageHandler, UserEducationInternalsPageHandlerInterface} from './user_education_internals.mojom-webui.js'; -const UserEducationInternalsElementBase = HelpBubbleMixin(PolymerElement) as { - new (): PolymerElement & HelpBubbleMixinInterface, -}; +const UserEducationInternalsElementBase = + CrContainerShadowMixin(HelpBubbleMixin(PolymerElement)) as { + new (): PolymerElement & HelpBubbleMixinInterface & + CrContainerShadowMixinInterface, + }; interface UserEducationInternalsElement { $: { @@ -89,7 +93,8 @@ this.$.promos.addEventListener( 'rendered-item-count-changed', (_: Event) => { this.registerHelpBubble( - 'kWebUIIPHDemoElementIdentifier', '#IPH_WebUiHelpBubbleTest'); + 'kWebUIIPHDemoElementIdentifier', + ['#IPH_WebUiHelpBubbleTest', '#launch']); }, { once: true, }); @@ -103,8 +108,8 @@ this.filter = (e.detail as string).toLowerCase(); } - private startTutorial_(e: DomRepeatEvent<FeaturePromoDemoPageInfo>) { - const id = e.model.item.internalName; + private startTutorial_(e: CustomEvent) { + const id = e.detail; this.featurePromoErrorMessage_ = ''; this.handler_.startTutorial(id).then(({errorMessage}) => { @@ -115,8 +120,8 @@ }); } - private showFeaturePromo_(e: DomRepeatEvent<FeaturePromoDemoPageInfo>) { - const id = e.model.item.internalName; + private showFeaturePromo_(e: CustomEvent) { + const id = e.detail; this.featurePromoErrorMessage_ = ''; this.handler_.showFeaturePromo(id).then(({errorMessage}) => { @@ -136,23 +141,6 @@ promo.supportedPlatforms.find( (platform: string) => platform.toLowerCase().includes(filter)); } - - private showDescription_(promo: FeaturePromoDemoPageInfo) { - return promo.displayDescription !== ''; - } - - private formatItemDate_(promo: FeaturePromoDemoPageInfo) { - const date = new Date(Number(promo.addedTimestampMs)); - return date.toDateString(); - } - - private formatPlatforms_(promo: FeaturePromoDemoPageInfo) { - return promo.supportedPlatforms.join(', '); - } - - private showInstructions_(promo: FeaturePromoDemoPageInfo) { - return promo.instructions.length; - } } customElements.define(
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals_card.html b/chrome/browser/resources/internals/user_education/user_education_internals_card.html new file mode 100644 index 0000000..4af9c0a --- /dev/null +++ b/chrome/browser/resources/internals/user_education/user_education_internals_card.html
@@ -0,0 +1,45 @@ +<style include="cr-hidden-style cr-shared-style"> +:host { + border-top: 1px solid var(--cr-separator-color); + display: flex; + padding-block: var(--cr-section-vertical-padding); +} + +.card-content { + flex: 1; +} + +cr-button { + flex: 0; + margin-block-start: var(--cr-section-vertical-padding); + margin-inline-end: var(--cr-section-padding); +} + +ol { + padding-inline-start: 2ex; +} + +li { + margin-block: 1ex; +} +</style> +<div class="card-content"> + <h3>[[promo.displayTitle]]</h3> + <p>Added on [[formatDate_()]]</p> +<p hidden$="[[!showDescription_()]]"> + [[promo.displayDescription]] +</p> +<p><b>Type:</b> [[promo.type]]</p> +<p><b>OS: </b>[[formatPlatforms_()]]</p> +<div hidden$="[[!showInstructions_()]]"> + <p><b>Instructions:</b></p> + <ol> + <template is="dom-repeat" items="[[promo.instructions]]"> + <li>[[item]]</li> + </template> + </ol> +</div> +</div> +<cr-button class="action-button" id="launch" on-click="launchPromo_"> + Launch +</cr-button>
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals_card.ts b/chrome/browser/resources/internals/user_education/user_education_internals_card.ts new file mode 100644 index 0000000..e5c812cc --- /dev/null +++ b/chrome/browser/resources/internals/user_education/user_education_internals_card.ts
@@ -0,0 +1,59 @@ +// 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. + +import 'chrome://resources/cr_elements/cr_button/cr_button.js'; +import 'chrome://resources/cr_elements/cr_hidden_style.css.js'; +import 'chrome://resources/cr_elements/cr_shared_style.css.js'; +import 'chrome://resources/cr_elements/cr_shared_vars.css.js'; + +import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {FeaturePromoDemoPageInfo} from './user_education_internals.mojom-webui.js'; +import {getTemplate} from './user_education_internals_card.html.js'; + +const PROMO_LAUNCH_EVENT = 'promo-launch'; + +class UserEducationInternalsCardElement extends PolymerElement { + static get is() { + return 'user-education-internals-card'; + } + + static get template() { + return getTemplate(); + } + + static get properties() { + return { + promo: Object, + }; + } + + promo: FeaturePromoDemoPageInfo; + + private launchPromo_() { + this.dispatchEvent(new CustomEvent( + PROMO_LAUNCH_EVENT, + {bubbles: true, composed: true, detail: this.promo.internalName})); + } + + private showDescription_() { + return this.promo.displayDescription !== ''; + } + + private formatDate_() { + const date = new Date(Number(this.promo.addedTimestampMs)); + return date.toDateString(); + } + + private formatPlatforms_() { + return this.promo.supportedPlatforms.join(', '); + } + + private showInstructions_() { + return this.promo.instructions.length; + } +} + +customElements.define( + UserEducationInternalsCardElement.is, UserEducationInternalsCardElement);
diff --git a/chrome/browser/resources/new_tab_page/ts_library_sourcemaps.gni b/chrome/browser/resources/new_tab_page/ts_library_sourcemaps.gni index 726a7cbf..483689c8 100644 --- a/chrome/browser/resources/new_tab_page/ts_library_sourcemaps.gni +++ b/chrome/browser/resources/new_tab_page/ts_library_sourcemaps.gni
@@ -31,6 +31,7 @@ manifest_files = [] sources = [] outputs = [] + out_dir = _out_dir foreach(_output, get_target_outputs(":$_ts_library_target_name")) { if (get_path_info(_output, "extension") == "manifest") { manifest_files += [ _output ]
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.html b/chrome/browser/resources/side_panel/customize_chrome/categories.html index ba8dcb85..47f9c227 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/categories.html +++ b/chrome/browser/resources/side_panel/customize_chrome/categories.html
@@ -187,7 +187,7 @@ <cr-grid columns="6" disable-arrow-navigation> <div class="tile" tabindex="0" id="classicChromeTile" role="button" on-click="onClassicChromeClick_" - aria-checked$="[[isClassicChromeSelected_]]"> + aria-current$="[[boolToString_(isClassicChromeSelected_)]]"> <customize-chrome-check-mark-wrapper checked="[[isClassicChromeSelected_]]"> <div class="image-container"> @@ -198,7 +198,7 @@ </div> <div class="tile" tabindex="0" id="uploadImageTile" role="button" on-click="onUploadImageClick_" - aria-checked$="[[isLocalImageSelected_]]"> + aria-current$="[[boolToString_(isLocalImageSelected_)]]"> <customize-chrome-check-mark-wrapper checked="[[isLocalImageSelected_]]"> <div class="image-container"> @@ -209,7 +209,7 @@ </div> <div class="tile" tabindex="0" id="chromeColorsTile" role="button" on-click="onChromeColorsClick_" - aria-checked$="[[isChromeColorsSelected_]]"> + aria-current$="[[boolToString_(isChromeColorsSelected_)]]"> <customize-chrome-check-mark-wrapper checked="[[isChromeColorsSelected_]]"> <div class="image-container"> @@ -228,7 +228,8 @@ on-rendered-item-count-changed="onCollectionsRendered_"> <div class="tile collection" tabindex="0" role="button" on-click="onCollectionClick_" - aria-checked$="[[isCollectionSelected_(item.id, selectedCategory_)]]"> + aria-current$= + "[[getCollectionCheckedStatus_(item.id, selectedCategory_)]]"> <customize-chrome-check-mark-wrapper checked="[[isCollectionSelected_(item.id, selectedCategory_)]]"> <div class="image-container">
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.ts b/chrome/browser/resources/side_panel/customize_chrome/categories.ts index fa02fb9b..00ae021 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/categories.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
@@ -168,6 +168,14 @@ this.selectedCategory_.collectionId === id; } + private boolToString_(value: boolean): string { + return value ? 'true' : 'false'; + } + + private getCollectionCheckedStatus_(id: string): string { + return this.boolToString_(this.isCollectionSelected_(id)); + } + private onClassicChromeClick_() { this.pageHandler_.setDefaultColor(); this.pageHandler_.removeBackgroundImage();
diff --git a/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html b/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html index 33e8cbea..cf7aca0 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html +++ b/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html
@@ -90,7 +90,8 @@ class="tile" title="$i18n{colorPickerLabel}" aria-label$="$i18n{colorPickerLabel}" - aria-checked$="[[isCustomColorSelected_]]" + role="button" + aria-current$="[[boolToString_(isCustomColorSelected_)]]" tabindex="0" on-click="onCustomColorClick_"> <customize-chrome-color @@ -108,7 +109,8 @@ class="tile" title="$i18n{defaultColorName}" aria-label$="$i18n{defaultColorName}" - aria-checked$="[[isDefaultColorSelected_]]" + role="button" + aria-current$="[[boolToString_(isDefaultColorSelected_)]]" tabindex="0" on-click="onDefaultColorClick_" background-color="[[defaultColor_.background]]" @@ -120,7 +122,9 @@ class="chrome-color tile" title="[[item.name]]" aria-label$="[[item.name]]" - aria-checked$="[[isChromeColorSelected_(item.seed, selectedColor_)]]" + role="button" + aria-current$= + "[[getChromeColorCheckedStatus_(item.seed, selectedColor_)]]" tabindex="0" on-click="onChromeColorClick_" background-color="[[item.background]]"
diff --git a/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts b/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts index 52dfd54..310761e 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts
@@ -150,6 +150,14 @@ this.selectedColor_.chromeColor!.value === color.value; } + private boolToString_(value: boolean): string { + return value ? 'true' : 'false'; + } + + private getChromeColorCheckedStatus_(color: SkColor): string { + return this.boolToString_(this.isChromeColorSelected_(color)); + } + private onBackClick_() { this.dispatchEvent(new Event('back-click')); }
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.html b/chrome/browser/resources/side_panel/customize_chrome/themes.html index c043273..9bf37c4 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/themes.html +++ b/chrome/browser/resources/side_panel/customize_chrome/themes.html
@@ -113,7 +113,7 @@ on-rendered-item-count-changed="onThemesRendered_"> <div class="tile theme" tabindex="0" role="button" on-click="onSelectTheme_" title$="[[item.attribution1]]" - aria-checked$="[[isThemeSelected_(item.attribution1, theme_)]]"> + aria-current$="[[getThemeCheckedStatus_(item.imageUrl.url, theme_)]]"> <customize-chrome-check-mark-wrapper checked="[[isThemeSelected_(item.imageUrl.url, theme_)]]"> <div class="image-container">
diff --git a/chrome/browser/resources/side_panel/customize_chrome/themes.ts b/chrome/browser/resources/side_panel/customize_chrome/themes.ts index a903fc04..6c48d2c5 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/themes.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/themes.ts
@@ -172,6 +172,10 @@ this.theme_.backgroundImage.url.url === url && !this.isRefreshToggleChecked_; } + + private getThemeCheckedStatus_(url: string): string { + return this.isThemeSelected_(url) ? 'true' : 'false'; + } } declare global {
diff --git a/chrome/browser/search/background/ntp_custom_background_service.cc b/chrome/browser/search/background/ntp_custom_background_service.cc index a398e78..8771406 100644 --- a/chrome/browser/search/background/ntp_custom_background_service.cc +++ b/chrome/browser/search/background/ntp_custom_background_service.cc
@@ -229,6 +229,10 @@ const GURL& image_url, const gfx::Image& fetched_image, const image_fetcher::RequestMetadata& metadata) { + if (metadata.http_response_code == + image_fetcher::RequestMetadata::ResponseCode::RESPONSE_CODE_INVALID) { + return; + } // Calculate the bitmap color asynchronously as it is slow (1-2 seconds for // the thumbnail). However, prefs should be updated on the main thread. base::ThreadPool::PostTaskAndReplyWithResult( @@ -242,8 +246,6 @@ void NtpCustomBackgroundService::FetchCustomBackgroundAndExtractBackgroundColor( const GURL& image_url, const GURL& fetch_url) { - DCHECK(!fetch_url.is_empty()); - net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("ntp_custom_background", R"(
diff --git a/chrome/browser/search/background/ntp_custom_background_service_unittest.cc b/chrome/browser/search/background/ntp_custom_background_service_unittest.cc index 23bc95d..f0b0859 100644 --- a/chrome/browser/search/background/ntp_custom_background_service_unittest.cc +++ b/chrome/browser/search/background/ntp_custom_background_service_unittest.cc
@@ -543,7 +543,7 @@ scoped_feature_list.InitAndEnableFeature( ntp_features::kCustomizeChromeColorExtraction); - EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(4); + EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2); SkBitmap bitmap; bitmap.allocN32Pixels(32, 32); bitmap.eraseColor(SK_ColorRED); @@ -573,9 +573,23 @@ kUrl, kThumbnailUrl, kAttributionLine1, kAttributionLine2, kActionUrl, ""); + image_fetcher::RequestMetadata metadata = image_fetcher::RequestMetadata(); + + // Background color will not update if metadata http code invalid. + metadata.http_response_code = + image_fetcher::RequestMetadata::ResponseCode::RESPONSE_CODE_INVALID; + custom_background_service_->UpdateCustomBackgroundColorAsync(kUrl, image, + metadata); + task_environment_.RunUntilIdle(); + custom_background = custom_background_service_->GetCustomBackground(); + EXPECT_NE( + SK_ColorRED, + custom_background->custom_background_main_color.value_or(SK_ColorWHITE)); + // Background color will not update if current background url changed. + metadata.http_response_code = 200; custom_background_service_->UpdateCustomBackgroundColorAsync( - GURL("different_url"), image, image_fetcher::RequestMetadata()); + GURL("different_url"), image, metadata); task_environment_.RunUntilIdle(); custom_background = custom_background_service_->GetCustomBackground(); EXPECT_NE( @@ -583,8 +597,8 @@ custom_background->custom_background_main_color.value_or(SK_ColorWHITE)); // Background color should update. - custom_background_service_->UpdateCustomBackgroundColorAsync( - kUrl, image, image_fetcher::RequestMetadata()); + custom_background_service_->UpdateCustomBackgroundColorAsync(kUrl, image, + metadata); task_environment_.RunUntilIdle(); custom_background = custom_background_service_->GetCustomBackground(); EXPECT_EQ(
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ChromeProvidedSharingOptionsProviderBase.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ChromeProvidedSharingOptionsProviderBase.java new file mode 100644 index 0000000..4dda47e7 --- /dev/null +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ChromeProvidedSharingOptionsProviderBase.java
@@ -0,0 +1,464 @@ +// 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. + +package org.chromium.chrome.browser.share; + +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.view.View; + +import androidx.annotation.Nullable; + +import org.chromium.base.BuildInfo; +import org.chromium.base.Callback; +import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.content_creation.notes.NoteCreationCoordinator; +import org.chromium.chrome.browser.content_creation.notes.NoteCreationCoordinatorFactory; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.preferences.Pref; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.share.ChromeShareExtras.DetailedContentType; +import org.chromium.chrome.browser.share.qrcode.QrCodeCoordinator; +import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge; +import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator; +import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback; +import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; +import org.chromium.components.browser_ui.share.ShareImageFileUtils; +import org.chromium.components.browser_ui.share.ShareParams; +import org.chromium.components.feature_engagement.EventConstants; +import org.chromium.components.feature_engagement.Tracker; +import org.chromium.components.user_prefs.UserPrefs; +import org.chromium.ui.base.Clipboard; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.widget.Toast; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Provides a list of Chrome-provided sharing options. + */ +public abstract class ChromeProvidedSharingOptionsProviderBase { + private static final String USER_ACTION_COPY_URL_SELECTED = "SharingHubAndroid.CopyURLSelected"; + private static final String USER_ACTION_COPY_GIF_SELECTED = "SharingHubAndroid.CopyGifSelected"; + private static final String USER_ACTION_COPY_IMAGE_SELECTED = + "SharingHubAndroid.CopyImageSelected"; + private static final String USER_ACTION_COPY_SELECTED = "SharingHubAndroid.CopySelected"; + private static final String USER_ACTION_COPY_TEXT_SELECTED = + "SharingHubAndroid.CopyTextSelected"; + private static final String USER_ACTION_SEND_TAB_TO_SELF_SELECTED = + "SharingHubAndroid.SendTabToSelfSelected"; + private static final String USER_ACTION_QR_CODE_SELECTED = "SharingHubAndroid.QRCodeSelected"; + private static final String USER_ACTION_PRINT_SELECTED = "SharingHubAndroid.PrintSelected"; + private static final String USER_ACTION_SAVE_IMAGE_SELECTED = + "SharingHubAndroid.SaveImageSelected"; + + protected static final String USER_ACTION_WEB_STYLE_NOTES_SELECTED = + "SharingHubAndroid.WebnotesStylize"; + + protected final Activity mActivity; + protected final WindowAndroid mWindowAndroid; + protected final Supplier<Tab> mTabProvider; + protected final BottomSheetController mBottomSheetController; + protected final ShareParams mShareParams; + protected final Callback<Tab> mPrintTabCallback; + protected final boolean mIsIncognito; + protected final List<FirstPartyOption> mOrderedFirstPartyOptions; + protected final ChromeOptionShareCallback mChromeOptionShareCallback; + protected final String mUrl; + protected final Tracker mFeatureEngagementTracker; + protected final Profile mProfile; + + /** + * Constructs a new {@link ChromeProvidedSharingOptionsProviderBase}. + * + * @param activity The current {@link Activity}. + * @param windowAndroid The current window. + * @param tabProvider Supplier for the current activity tab. + * @param bottomSheetController The {@link BottomSheetController} for the current activity. + * @param shareParams The {@link ShareParams} for the current share. + * @param printTab A {@link Callback} that will print a given Tab. + * @param isIncognito Whether incognito mode is enabled. + * @param chromeOptionShareCallback A ChromeOptionShareCallback that can be used by + * Chrome-provided sharing options. + * @param featureEngagementTracker feature engagement tracker. + * @param url Url to share. + * @param profile The current profile of the User. + */ + protected ChromeProvidedSharingOptionsProviderBase(Activity activity, + WindowAndroid windowAndroid, Supplier<Tab> tabProvider, + BottomSheetController bottomSheetController, ShareParams shareParams, + Callback<Tab> printTab, boolean isIncognito, + ChromeOptionShareCallback chromeOptionShareCallback, Tracker featureEngagementTracker, + String url, Profile profile) { + mActivity = activity; + mWindowAndroid = windowAndroid; + mTabProvider = tabProvider; + mBottomSheetController = bottomSheetController; + mShareParams = shareParams; + mPrintTabCallback = printTab; + mIsIncognito = isIncognito; + mFeatureEngagementTracker = featureEngagementTracker; + mChromeOptionShareCallback = chromeOptionShareCallback; + mUrl = url; + mProfile = profile; + + mOrderedFirstPartyOptions = new ArrayList<>(); + initializeFirstPartyOptionsInOrder(); + } + + /** + * Data structure carries details on how a first party option should be used. + */ + protected static class FirstPartyOption { + public final int icon; + public final int iconLabel; + public final String iconContentDescription; + public final String featureNameForMetrics; + public final Callback<View> onClickCallback; + public final Collection<Integer> contentTypes; + public final Collection<Integer> contentTypesToDisableFor; + public final Collection<Integer> detailedContentTypesToDisableFor; + public final boolean disableForMultiWindow; + + private FirstPartyOption(int icon, int iconLabel, String iconContentDescription, + String featureNameForMetrics, Callback<View> onClickCallback, + Collection<Integer> contentTypes, Collection<Integer> contentTypesToDisableFor, + Collection<Integer> detailedContentTypesToDisableFor, + boolean disableForMultiWindow) { + this.icon = icon; + this.iconLabel = iconLabel; + this.iconContentDescription = iconContentDescription; + this.featureNameForMetrics = featureNameForMetrics; + this.onClickCallback = onClickCallback; + this.contentTypes = contentTypes; + this.contentTypesToDisableFor = contentTypesToDisableFor; + this.detailedContentTypesToDisableFor = detailedContentTypesToDisableFor; + this.disableForMultiWindow = disableForMultiWindow; + } + } + + protected static class FirstPartyOptionBuilder { + private int mIcon; + private int mIconLabel; + private String mIconContentDescription; + private String mFeatureNameForMetrics; + private Callback<View> mOnClickCallback; + private boolean mDisableForMultiWindow; + private Integer[] mContentTypesToDisableFor; + private Integer[] mDetailedContentTypesToDisableFor; + private final Integer[] mContentTypesInBuilder; + + public FirstPartyOptionBuilder(Integer... contentTypes) { + mContentTypesInBuilder = contentTypes; + mContentTypesToDisableFor = new Integer[] {}; + mDetailedContentTypesToDisableFor = new Integer[] {}; + } + + public FirstPartyOptionBuilder setIcon(int icon, int iconLabel) { + mIcon = icon; + mIconLabel = iconLabel; + return this; + } + + public FirstPartyOptionBuilder setIconContentDescription(String iconContentDescription) { + mIconContentDescription = iconContentDescription; + return this; + } + + public FirstPartyOptionBuilder setFeatureNameForMetrics(String featureName) { + mFeatureNameForMetrics = featureName; + return this; + } + + public FirstPartyOptionBuilder setOnClickCallback(Callback<View> onClickCallback) { + mOnClickCallback = onClickCallback; + return this; + } + + public FirstPartyOptionBuilder setContentTypesToDisableFor( + Integer... contentTypesToDisableFor) { + mContentTypesToDisableFor = contentTypesToDisableFor; + return this; + } + + public FirstPartyOptionBuilder setDetailedContentTypesToDisableFor( + Integer... detailedContentTypesToDisableFor) { + mDetailedContentTypesToDisableFor = detailedContentTypesToDisableFor; + return this; + } + + public FirstPartyOptionBuilder setDisableForMultiWindow(boolean disableForMultiWindow) { + mDisableForMultiWindow = disableForMultiWindow; + return this; + } + + public FirstPartyOption build() { + assert mOnClickCallback != null; + return new FirstPartyOption(mIcon, mIconLabel, mIconContentDescription, + mFeatureNameForMetrics, mOnClickCallback, Arrays.asList(mContentTypesInBuilder), + Arrays.asList(mContentTypesToDisableFor), + Arrays.asList(mDetailedContentTypesToDisableFor), mDisableForMultiWindow); + } + } + + /** + * Returns an ordered list of {@link FirstPartyOption}s that should be shown given the {@code + * contentTypes} being shared. + * + * @param contentTypes a {@link Set} of {@link ContentType}. + * @param detailedContentType the {@link DetailedContentType} being shared. + * @param isMultiWindow if in multi-window mode. + * @return a list of {@link FirstPartyOption}s. + */ + protected List<FirstPartyOption> getFirstPartyOptions(Set<Integer> contentTypes, + @DetailedContentType int detailedContentType, boolean isMultiWindow) { + List<FirstPartyOption> availableOptions = new ArrayList<>(); + for (FirstPartyOption firstPartyOption : mOrderedFirstPartyOptions) { + if (!Collections.disjoint(contentTypes, firstPartyOption.contentTypes) + && Collections.disjoint(contentTypes, firstPartyOption.contentTypesToDisableFor) + && !firstPartyOption.detailedContentTypesToDisableFor.contains( + detailedContentType) + && !(isMultiWindow && firstPartyOption.disableForMultiWindow)) { + availableOptions.add(firstPartyOption); + } + } + return availableOptions; + } + + /** + * Creates all enabled {@link FirstPartyOption}s and adds them to {@code + * mOrderedFirstPartyOptions} in the order they should appear. + */ + private void initializeFirstPartyOptionsInOrder() { + // Only show a limited first party share selection for automotive + if (BuildInfo.getInstance().isAutomotive) { + mOrderedFirstPartyOptions.add(createCopyLinkFirstPartyOption()); + maybeAddSendTabToSelfFirstPartyOption(); + maybeAddQrCodeFirstPartyOption(); + return; + } + if (ChromeFeatureList.isEnabled(ChromeFeatureList.WEBNOTES_STYLIZE)) { + mOrderedFirstPartyOptions.add(createWebNotesStylizeFirstPartyOption()); + } + maybeAddScreenshotFirstPartyOption(); + maybeAddLongScreenshotFirstPartyOption(); + + mOrderedFirstPartyOptions.add(createCopyLinkFirstPartyOption()); + mOrderedFirstPartyOptions.add(createCopyGifFirstPartyOption()); + mOrderedFirstPartyOptions.add(createCopyImageFirstPartyOption()); + mOrderedFirstPartyOptions.add(createCopyFirstPartyOption()); + mOrderedFirstPartyOptions.add(createCopyTextFirstPartyOption()); + maybeAddSendTabToSelfFirstPartyOption(); + maybeAddQrCodeFirstPartyOption(); + if (mTabProvider.hasValue() && UserPrefs.get(mProfile).getBoolean(Pref.PRINTING_ENABLED)) { + mOrderedFirstPartyOptions.add(createPrintingFirstPartyOption()); + } + mOrderedFirstPartyOptions.add(createSaveImageFirstPartyOption()); + } + + private void maybeAddSendTabToSelfFirstPartyOption() { + Optional<Integer> sendTabToSelfDisplayReason = + SendTabToSelfAndroidBridge.getEntryPointDisplayReason(mProfile, mUrl); + if (sendTabToSelfDisplayReason.isPresent() + || !ChromeFeatureList.isEnabled(ChromeFeatureList.SEND_TAB_TO_SELF_SIGNIN_PROMO)) { + mOrderedFirstPartyOptions.add(createSendTabToSelfFirstPartyOption()); + } + } + + private void maybeAddQrCodeFirstPartyOption() { + if (!mIsIncognito) { + mOrderedFirstPartyOptions.add(createQrCodeFirstPartyOption()); + } + } + + private void maybeAddScreenshotFirstPartyOption() { + FirstPartyOption option = createScreenshotFirstPartyOption(); + if (option != null) { + mOrderedFirstPartyOptions.add(option); + } + } + + private void maybeAddLongScreenshotFirstPartyOption() { + // TODO(crbug.com/1250871): Long Screenshots on by default; supported on Android 7.0+. + if (!(ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT) + && mTabProvider.hasValue())) { + return; + } + FirstPartyOption option = createLongScreenshotsFirstPartyOption(); + if (option != null) { + mOrderedFirstPartyOptions.add(option); + } + } + + private FirstPartyOption createCopyLinkFirstPartyOption() { + return new FirstPartyOptionBuilder( + ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE) + .setContentTypesToDisableFor(ContentType.LINK_AND_TEXT) + .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_url) + .setFeatureNameForMetrics(USER_ACTION_COPY_URL_SELECTED) + .setOnClickCallback((view) -> { + ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( + Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip( + ClipData.newPlainText(mShareParams.getTitle(), mShareParams.getUrl())); + Toast.makeText(mActivity, R.string.link_copied, Toast.LENGTH_SHORT).show(); + }) + .build(); + } + + private FirstPartyOption createCopyGifFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) + .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_gif) + // Enables only for GIF. + .setDetailedContentTypesToDisableFor(DetailedContentType.IMAGE, + DetailedContentType.WEB_NOTES, DetailedContentType.NOT_SPECIFIED) + .setFeatureNameForMetrics(USER_ACTION_COPY_GIF_SELECTED) + .setOnClickCallback((view) -> { + if (!mShareParams.getFileUris().isEmpty()) { + Clipboard.getInstance().setImageUri(mShareParams.getFileUris().get(0)); + Toast.makeText(mActivity, R.string.gif_copied, Toast.LENGTH_SHORT).show(); + } + }) + .build(); + } + + private FirstPartyOption createCopyImageFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) + .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_image) + .setFeatureNameForMetrics(USER_ACTION_COPY_IMAGE_SELECTED) + .setDetailedContentTypesToDisableFor(DetailedContentType.GIF) + .setOnClickCallback((view) -> { + if (!mShareParams.getFileUris().isEmpty()) { + Clipboard.getInstance().setImageUri(mShareParams.getFileUris().get(0)); + Toast.makeText(mActivity, R.string.image_copied, Toast.LENGTH_SHORT).show(); + } + }) + .build(); + } + + private FirstPartyOption createCopyFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.LINK_AND_TEXT) + .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy) + .setFeatureNameForMetrics(USER_ACTION_COPY_SELECTED) + .setOnClickCallback((view) -> { + ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( + Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(ClipData.newPlainText( + mShareParams.getTitle(), mShareParams.getTextAndUrl())); + Toast.makeText(mActivity, R.string.copied, Toast.LENGTH_SHORT).show(); + }) + .build(); + } + + private FirstPartyOption createCopyTextFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.TEXT, ContentType.HIGHLIGHTED_TEXT) + .setContentTypesToDisableFor(ContentType.LINK_AND_TEXT) + .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_text) + .setFeatureNameForMetrics(USER_ACTION_COPY_TEXT_SELECTED) + .setOnClickCallback((view) -> { + ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( + Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip( + ClipData.newPlainText(mShareParams.getTitle(), mShareParams.getText())); + Toast.makeText(mActivity, R.string.text_copied, Toast.LENGTH_SHORT).show(); + }) + .build(); + } + + private FirstPartyOption createSendTabToSelfFirstPartyOption() { + return new FirstPartyOptionBuilder( + ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.IMAGE) + .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) + .setIcon(R.drawable.send_tab, R.string.send_tab_to_self_share_activity_title) + .setFeatureNameForMetrics(USER_ACTION_SEND_TAB_TO_SELF_SELECTED) + .setOnClickCallback((view) -> { + SendTabToSelfCoordinator sttsCoordinator = + new SendTabToSelfCoordinator(mActivity, mWindowAndroid, mUrl, + mShareParams.getTitle(), mBottomSheetController, mProfile); + sttsCoordinator.show(); + }) + .build(); + } + + private FirstPartyOption createQrCodeFirstPartyOption() { + return new FirstPartyOptionBuilder( + ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.IMAGE) + .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) + .setIcon(R.drawable.qr_code, R.string.qr_code_share_icon_label) + .setFeatureNameForMetrics(USER_ACTION_QR_CODE_SELECTED) + .setOnClickCallback((view) -> { + QrCodeCoordinator qrCodeCoordinator = + new QrCodeCoordinator(mActivity, mUrl, mShareParams.getWindow()); + qrCodeCoordinator.show(); + }) + .build(); + } + + private FirstPartyOption createPrintingFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.LINK_PAGE_VISIBLE) + .setIcon(R.drawable.sharing_print, R.string.print_share_activity_title) + .setFeatureNameForMetrics(USER_ACTION_PRINT_SELECTED) + .setOnClickCallback((view) -> { mPrintTabCallback.onResult(mTabProvider.get()); }) + .build(); + } + + private FirstPartyOption createSaveImageFirstPartyOption() { + return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) + .setIcon(R.drawable.save_to_device, R.string.sharing_save_image) + .setFeatureNameForMetrics(USER_ACTION_SAVE_IMAGE_SELECTED) + .setOnClickCallback((view) -> { + if (mShareParams.getFileUris().isEmpty()) return; + + ShareImageFileUtils.getBitmapFromUriAsync( + mActivity, mShareParams.getFileUris().get(0), (bitmap) -> { + SaveBitmapDelegate saveBitmapDelegate = new SaveBitmapDelegate( + mActivity, bitmap, R.string.save_image_filename_prefix, + null, mShareParams.getWindow()); + saveBitmapDelegate.save(); + }); + }) + .build(); + } + + protected FirstPartyOption createWebNotesStylizeFirstPartyOption() { + String title = mShareParams.getTitle(); + return new FirstPartyOptionBuilder(ContentType.HIGHLIGHTED_TEXT) + .setIcon(R.drawable.webnote, R.string.sharing_webnotes_create_card) + .setIconContentDescription( + mActivity.getString(R.string.sharing_webnotes_accessibility_description)) + .setFeatureNameForMetrics(USER_ACTION_WEB_STYLE_NOTES_SELECTED) + .setOnClickCallback((view) -> { + mFeatureEngagementTracker.notifyEvent( + EventConstants.SHARING_HUB_WEBNOTES_STYLIZE_USED); + NoteCreationCoordinator coordinator = NoteCreationCoordinatorFactory.create( + mActivity, mShareParams.getWindow(), mUrl, title, + mShareParams.getRawText().trim(), mChromeOptionShareCallback); + coordinator.showDialog(); + }) + .build(); + } + + /** + * Create a {@link FirstPartyOption} used to do screenshot. Return null if not supported. + */ + @Nullable + protected abstract FirstPartyOption createScreenshotFirstPartyOption(); + + /** + * Create a {@link FirstPartyOption} used to do long screenshot. Return null if not supported. + */ + @Nullable + protected abstract FirstPartyOption createLongScreenshotsFirstPartyOption(); +}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java index 4e9bc7f..1153ef2 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -5,80 +5,53 @@ package org.chromium.chrome.browser.share.share_sheet; import android.app.Activity; -import android.content.ClipData; -import android.content.ClipboardManager; import android.content.ComponentName; -import android.content.Context; -import android.view.View; import androidx.appcompat.content.res.AppCompatResources; -import org.chromium.base.BuildInfo; import org.chromium.base.Callback; import org.chromium.base.supplier.Supplier; import org.chromium.chrome.R; -import org.chromium.chrome.browser.content_creation.notes.NoteCreationCoordinator; -import org.chromium.chrome.browser.content_creation.notes.NoteCreationCoordinatorFactory; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.share.ChromeProvidedSharingOptionsProviderBase; import org.chromium.chrome.browser.share.ChromeShareExtras.DetailedContentType; -import org.chromium.chrome.browser.share.SaveBitmapDelegate; import org.chromium.chrome.browser.share.link_to_text.LinkToTextCoordinator.LinkGeneration; import org.chromium.chrome.browser.share.long_screenshots.LongScreenshotsCoordinator; -import org.chromium.chrome.browser.share.qrcode.QrCodeCoordinator; import org.chromium.chrome.browser.share.screenshot.ScreenshotCoordinator; -import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge; -import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator; import org.chromium.chrome.browser.share.share_sheet.ShareSheetLinkToggleMetricsHelper.LinkToggleMetricsDetails; import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.modules.image_editor.ImageEditorModuleProvider; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; -import org.chromium.components.browser_ui.share.ShareImageFileUtils; import org.chromium.components.browser_ui.share.ShareParams; import org.chromium.components.feature_engagement.EventConstants; import org.chromium.components.feature_engagement.FeatureConstants; import org.chromium.components.feature_engagement.Tracker; -import org.chromium.components.user_prefs.UserPrefs; -import org.chromium.ui.base.Clipboard; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.modelutil.PropertyModel; -import org.chromium.ui.widget.Toast; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.Set; /** * Provides {@code PropertyModel}s of Chrome-provided sharing options. */ -public class ChromeProvidedSharingOptionsProvider { +public class ChromeProvidedSharingOptionsProvider extends ChromeProvidedSharingOptionsProviderBase { // ComponentName used for Chrome share options in ShareParams.TargetChosenCallback public static final ComponentName CHROME_PROVIDED_FEATURE_COMPONENT_NAME = new ComponentName("CHROME", "CHROME_FEATURE"); - private final Activity mActivity; - private final WindowAndroid mWindowAndroid; - private final Supplier<Tab> mTabProvider; - private final BottomSheetController mBottomSheetController; + private static final String USER_ACTION_SCREENSHOT_SELECTED = + "SharingHubAndroid.ScreenshotSelected"; + private static final String USER_ACTION_LONG_SCREENSHOT_SELECTED = + "SharingHubAndroid.LongScreenshotSelected"; + private final ShareSheetBottomSheetContent mBottomSheetContent; - private final ShareParams mShareParams; - private final Callback<Tab> mPrintTabCallback; - private final boolean mIsIncognito; private final long mShareStartTime; - private final List<FirstPartyOption> mOrderedFirstPartyOptions; - private final ChromeOptionShareCallback mChromeOptionShareCallback; - private final String mUrl; private final ImageEditorModuleProvider mImageEditorModuleProvider; - private final Tracker mFeatureEngagementTracker; private final @LinkGeneration int mLinkGenerationStatusForMetrics; private final LinkToggleMetricsDetails mLinkToggleMetricsDetails; - private final Profile mProfile; /** * Constructs a new {@link ChromeProvidedSharingOptionsProvider}. @@ -112,143 +85,13 @@ ImageEditorModuleProvider imageEditorModuleProvider, Tracker featureEngagementTracker, String url, @LinkGeneration int linkGenerationStatusForMetrics, LinkToggleMetricsDetails linkToggleMetricsDetails, Profile profile) { - mActivity = activity; - mWindowAndroid = windowAndroid; - mTabProvider = tabProvider; - mBottomSheetController = bottomSheetController; + super(activity, windowAndroid, tabProvider, bottomSheetController, shareParams, printTab, + isIncognito, chromeOptionShareCallback, featureEngagementTracker, url, profile); mBottomSheetContent = bottomSheetContent; - mShareParams = shareParams; - mPrintTabCallback = printTab; - mIsIncognito = isIncognito; mShareStartTime = shareStartTime; mImageEditorModuleProvider = imageEditorModuleProvider; - mFeatureEngagementTracker = featureEngagementTracker; - mChromeOptionShareCallback = chromeOptionShareCallback; - mUrl = url; mLinkGenerationStatusForMetrics = linkGenerationStatusForMetrics; mLinkToggleMetricsDetails = linkToggleMetricsDetails; - mProfile = profile; - mOrderedFirstPartyOptions = new ArrayList<>(); - initializeFirstPartyOptionsInOrder(); - } - - /** - * Encapsulates a {@link PropertyModel} and the {@link ContentType}s it should be shown for. - */ - private static class FirstPartyOption { - final Collection<Integer> mContentTypes; - final Collection<Integer> mContentTypesToDisableFor; - final Collection<Integer> mDetailedContentTypesToDisableFor; - final PropertyModel mPropertyModel; - final boolean mDisableForMultiWindow; - - /** - * Should only be used when the default property model constructed in the builder does not - * fit the feature's needs. This should be rare. - * - * @param model Property model for the first party option. - * @param contentTypes Content types to trigger for. - * @param contentTypesToDisableFor Content types to disable for. - * @param detailedContentTypesToDisableFor {@link DetailedContentType}s to disable for. - * @param disableForMultiWindow If the feature should be disabled if in multi-window mode. - */ - FirstPartyOption(PropertyModel model, Collection<Integer> contentTypes, - Collection<Integer> contentTypesToDisableFor, - Collection<Integer> detailedContentTypesToDisableFor, - boolean disableForMultiWindow) { - mPropertyModel = model; - mContentTypes = contentTypes; - mContentTypesToDisableFor = contentTypesToDisableFor; - mDetailedContentTypesToDisableFor = detailedContentTypesToDisableFor; - mDisableForMultiWindow = disableForMultiWindow; - } - } - - private class FirstPartyOptionBuilder { - private int mIcon; - private int mIconLabel; - private String mIconContentDescription; - private String mFeatureNameForMetrics; - private Callback<View> mOnClickCallback; - private boolean mDisableForMultiWindow; - private Integer[] mContentTypesToDisableFor; - private Integer[] mDetailedContentTypesToDisableFor; - private final Integer[] mContentTypesInBuilder; - private boolean mShowNewBadge; - private boolean mHideBottomSheetContentOnTap = true; - - FirstPartyOptionBuilder(Integer... contentTypes) { - mContentTypesInBuilder = contentTypes; - mContentTypesToDisableFor = new Integer[] {}; - mDetailedContentTypesToDisableFor = new Integer[] {}; - } - - FirstPartyOptionBuilder setIcon(int icon, int iconLabel) { - mIcon = icon; - mIconLabel = iconLabel; - return this; - } - - FirstPartyOptionBuilder setIconContentDescription(int iconContentDescription) { - mIconContentDescription = mActivity.getResources().getString(iconContentDescription); - return this; - } - - FirstPartyOptionBuilder setFeatureNameForMetrics(String featureName) { - mFeatureNameForMetrics = featureName; - return this; - } - - FirstPartyOptionBuilder setOnClickCallback(Callback<View> onClickCallback) { - mOnClickCallback = onClickCallback; - return this; - } - - FirstPartyOptionBuilder setContentTypesToDisableFor(Integer... contentTypesToDisableFor) { - mContentTypesToDisableFor = contentTypesToDisableFor; - return this; - } - - FirstPartyOptionBuilder setDetailedContentTypesToDisableFor( - Integer... detailedContentTypesToDisableFor) { - mDetailedContentTypesToDisableFor = detailedContentTypesToDisableFor; - return this; - } - - FirstPartyOptionBuilder setDisableForMultiWindow(boolean disableForMultiWindow) { - mDisableForMultiWindow = disableForMultiWindow; - return this; - } - - FirstPartyOptionBuilder setShowNewBadge(boolean showNewBadge) { - mShowNewBadge = showNewBadge; - return this; - } - - FirstPartyOptionBuilder setHideBottomSheetContentOnTap( - boolean hideBottomSheetContentOnTap) { - mHideBottomSheetContentOnTap = hideBottomSheetContentOnTap; - return this; - } - - FirstPartyOption build() { - PropertyModel model = ShareSheetPropertyModelBuilder.createPropertyModel( - AppCompatResources.getDrawable(mActivity, mIcon), - mActivity.getResources().getString(mIconLabel), - mIconContentDescription, (view) -> { - ShareSheetCoordinator.recordShareMetrics(mFeatureNameForMetrics, - mLinkGenerationStatusForMetrics, mLinkToggleMetricsDetails, - mShareStartTime, mProfile); - if (mHideBottomSheetContentOnTap) { - mBottomSheetController.hideContent(mBottomSheetContent, true); - } - mOnClickCallback.onResult(view); - callTargetChosenCallback(); - }, mShowNewBadge); - return new FirstPartyOption(model, Arrays.asList(mContentTypesInBuilder), - Arrays.asList(mContentTypesToDisableFor), - Arrays.asList(mDetailedContentTypesToDisableFor), mDisableForMultiWindow); - } } /** @@ -263,84 +106,63 @@ List<PropertyModel> getPropertyModels(Set<Integer> contentTypes, @DetailedContentType int detailedContentType, boolean isMultiWindow) { List<PropertyModel> propertyModels = new ArrayList<>(); - for (FirstPartyOption firstPartyOption : mOrderedFirstPartyOptions) { - if (!Collections.disjoint(contentTypes, firstPartyOption.mContentTypes) - && Collections.disjoint( - contentTypes, firstPartyOption.mContentTypesToDisableFor) - && !firstPartyOption.mDetailedContentTypesToDisableFor.contains( - detailedContentType) - && !(isMultiWindow && firstPartyOption.mDisableForMultiWindow)) { - propertyModels.add(firstPartyOption.mPropertyModel); - } + for (FirstPartyOption firstPartyOption : + getFirstPartyOptions(contentTypes, detailedContentType, isMultiWindow)) { + propertyModels.add(getShareSheetModel(firstPartyOption)); } return propertyModels; } - /** - * Creates all enabled {@link FirstPartyOption}s and adds them to {@code - * mOrderedFirstPartyOptions} in the order they should appear. - */ - private void initializeFirstPartyOptionsInOrder() { - boolean enableAllUpcomingSharingFeatures = - ChromeFeatureList.isEnabled(ChromeFeatureList.UPCOMING_SHARING_FEATURES); + private PropertyModel getShareSheetModel(FirstPartyOption option) { + boolean setShowNewBadge = showNewBadge(option); + boolean hideBottomSheetContentOnTap = hideBottomSheetContentOnTap(option); - // Only show a limited first party share selection for automotive - if (BuildInfo.getInstance().isAutomotive) { - mOrderedFirstPartyOptions.add(createCopyLinkFirstPartyOption()); - maybeAddSendTabToSelfFirstPartyOption(); - maybeAddQrCodeFirstPartyOption(); - return; - } - if (ChromeFeatureList.isEnabled(ChromeFeatureList.WEBNOTES_STYLIZE)) { - mOrderedFirstPartyOptions.add(createWebNotesStylizeFirstPartyOption()); - } - mOrderedFirstPartyOptions.add(createScreenshotFirstPartyOption()); - - // TODO(crbug.com/1250871): Long Screenshots on by default; supported on Android 7.0+. - if (ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_SHARE_LONG_SCREENSHOT) - && mTabProvider.hasValue()) { - mOrderedFirstPartyOptions.add(createLongScreenshotsFirstPartyOption()); - } - mOrderedFirstPartyOptions.add(createCopyLinkFirstPartyOption()); - mOrderedFirstPartyOptions.add(createCopyGifFirstPartyOption()); - mOrderedFirstPartyOptions.add(createCopyImageFirstPartyOption()); - mOrderedFirstPartyOptions.add(createCopyFirstPartyOption()); - mOrderedFirstPartyOptions.add(createCopyTextFirstPartyOption()); - maybeAddSendTabToSelfFirstPartyOption(); - maybeAddQrCodeFirstPartyOption(); - if (mTabProvider.hasValue() && UserPrefs.get(mProfile).getBoolean(Pref.PRINTING_ENABLED)) { - mOrderedFirstPartyOptions.add(createPrintingFirstPartyOption()); - } - mOrderedFirstPartyOptions.add(createSaveImageFirstPartyOption()); + return ShareSheetPropertyModelBuilder.createPropertyModel( + AppCompatResources.getDrawable(mActivity, option.icon), + mActivity.getResources().getString(option.iconLabel), + option.iconContentDescription, (view) -> { + ShareSheetCoordinator.recordShareMetrics(option.featureNameForMetrics, + mLinkGenerationStatusForMetrics, mLinkToggleMetricsDetails, + mShareStartTime, mProfile); + if (hideBottomSheetContentOnTap) { + mBottomSheetController.hideContent(mBottomSheetContent, true); + } + option.onClickCallback.onResult(view); + callTargetChosenCallback(); + }, setShowNewBadge); } - private void maybeAddSendTabToSelfFirstPartyOption() { - Optional<Integer> sendTabToSelfDisplayReason = - SendTabToSelfAndroidBridge.getEntryPointDisplayReason(mProfile, mUrl); - if (sendTabToSelfDisplayReason.isPresent() - || !ChromeFeatureList.isEnabled(ChromeFeatureList.SEND_TAB_TO_SELF_SIGNIN_PROMO)) { - mOrderedFirstPartyOptions.add(createSendTabToSelfFirstPartyOption()); + private boolean showNewBadge(FirstPartyOption firstPartyOption) { + if (!mFeatureEngagementTracker.isInitialized()) return false; + + if (USER_ACTION_SCREENSHOT_SELECTED.equals(firstPartyOption.featureNameForMetrics)) { + return mFeatureEngagementTracker.shouldTriggerHelpUI( + FeatureConstants.IPH_SHARE_SCREENSHOT_FEATURE); } + if (USER_ACTION_WEB_STYLE_NOTES_SELECTED.equals(firstPartyOption.featureNameForMetrics)) { + return mFeatureEngagementTracker.shouldTriggerHelpUI( + FeatureConstants.SHARING_HUB_WEBNOTES_STYLIZE_FEATURE); + } + return false; } - private void maybeAddQrCodeFirstPartyOption() { - if (!mIsIncognito) { - mOrderedFirstPartyOptions.add(createQrCodeFirstPartyOption()); + private boolean hideBottomSheetContentOnTap(FirstPartyOption firstPartyOption) { + if (USER_ACTION_SCREENSHOT_SELECTED.equals(firstPartyOption.featureNameForMetrics) + || USER_ACTION_WEB_STYLE_NOTES_SELECTED.equals( + firstPartyOption.featureNameForMetrics)) { + return false; } + return true; } - private FirstPartyOption createScreenshotFirstPartyOption() { - boolean showNewBadge = mFeatureEngagementTracker.isInitialized() - && mFeatureEngagementTracker.shouldTriggerHelpUI( - FeatureConstants.IPH_SHARE_SCREENSHOT_FEATURE); + @Override + protected FirstPartyOption createScreenshotFirstPartyOption() { return new FirstPartyOptionBuilder(ContentType.LINK_PAGE_VISIBLE, ContentType.TEXT, ContentType.HIGHLIGHTED_TEXT, ContentType.IMAGE) .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) .setIcon(R.drawable.screenshot, R.string.sharing_screenshot) - .setFeatureNameForMetrics("SharingHubAndroid.ScreenshotSelected") + .setFeatureNameForMetrics(USER_ACTION_SCREENSHOT_SELECTED) .setDisableForMultiWindow(true) - .setShowNewBadge(showNewBadge) - .setHideBottomSheetContentOnTap(false) .setOnClickCallback((view) -> { mFeatureEngagementTracker.notifyEvent(EventConstants.SHARE_SCREENSHOT_SELECTED); ScreenshotCoordinator coordinator = new ScreenshotCoordinator(mActivity, @@ -352,14 +174,14 @@ .build(); } - private FirstPartyOption createLongScreenshotsFirstPartyOption() { + @Override + protected FirstPartyOption createLongScreenshotsFirstPartyOption() { return new FirstPartyOptionBuilder(ContentType.LINK_PAGE_VISIBLE, ContentType.TEXT, ContentType.HIGHLIGHTED_TEXT, ContentType.IMAGE) .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) .setIcon(R.drawable.long_screenshot, R.string.sharing_long_screenshot) - .setFeatureNameForMetrics("SharingHubAndroid.LongScreenshotSelected") + .setFeatureNameForMetrics(USER_ACTION_LONG_SCREENSHOT_SELECTED) .setDisableForMultiWindow(true) - .setHideBottomSheetContentOnTap(false) .setOnClickCallback((view) -> { mFeatureEngagementTracker.notifyEvent(EventConstants.SHARE_SCREENSHOT_SELECTED); LongScreenshotsCoordinator coordinator = LongScreenshotsCoordinator.create( @@ -371,157 +193,6 @@ .build(); } - private FirstPartyOption createCopyLinkFirstPartyOption() { - return new FirstPartyOptionBuilder( - ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE) - .setContentTypesToDisableFor(ContentType.LINK_AND_TEXT) - .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_url) - .setFeatureNameForMetrics("SharingHubAndroid.CopyURLSelected") - .setOnClickCallback((view) -> { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( - Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip( - ClipData.newPlainText(mShareParams.getTitle(), mShareParams.getUrl())); - Toast.makeText(mActivity, R.string.link_copied, Toast.LENGTH_SHORT).show(); - }) - .build(); - } - - private FirstPartyOption createCopyGifFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) - .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_gif) - // Enables only for GIF. - .setDetailedContentTypesToDisableFor(DetailedContentType.IMAGE, - DetailedContentType.WEB_NOTES, DetailedContentType.NOT_SPECIFIED) - .setFeatureNameForMetrics("SharingHubAndroid.CopyGifSelected") - .setOnClickCallback((view) -> { - if (!mShareParams.getFileUris().isEmpty()) { - Clipboard.getInstance().setImageUri(mShareParams.getFileUris().get(0)); - Toast.makeText(mActivity, R.string.gif_copied, Toast.LENGTH_SHORT).show(); - } - }) - .build(); - } - - private FirstPartyOption createCopyImageFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) - .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_image) - .setFeatureNameForMetrics("SharingHubAndroid.CopyImageSelected") - .setDetailedContentTypesToDisableFor(DetailedContentType.GIF) - .setOnClickCallback((view) -> { - if (!mShareParams.getFileUris().isEmpty()) { - Clipboard.getInstance().setImageUri(mShareParams.getFileUris().get(0)); - Toast.makeText(mActivity, R.string.image_copied, Toast.LENGTH_SHORT).show(); - } - }) - .build(); - } - - private FirstPartyOption createCopyFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.LINK_AND_TEXT) - .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy) - .setFeatureNameForMetrics("SharingHubAndroid.CopySelected") - .setOnClickCallback((view) -> { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( - Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(ClipData.newPlainText( - mShareParams.getTitle(), mShareParams.getTextAndUrl())); - Toast.makeText(mActivity, R.string.copied, Toast.LENGTH_SHORT).show(); - }) - .build(); - } - - private FirstPartyOption createCopyTextFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.TEXT, ContentType.HIGHLIGHTED_TEXT) - .setContentTypesToDisableFor(ContentType.LINK_AND_TEXT) - .setIcon(R.drawable.ic_content_copy_black, R.string.sharing_copy_text) - .setFeatureNameForMetrics("SharingHubAndroid.CopyTextSelected") - .setOnClickCallback((view) -> { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService( - Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip( - ClipData.newPlainText(mShareParams.getTitle(), mShareParams.getText())); - Toast.makeText(mActivity, R.string.text_copied, Toast.LENGTH_SHORT).show(); - }) - .build(); - } - - private FirstPartyOption createSendTabToSelfFirstPartyOption() { - return new FirstPartyOptionBuilder( - ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.IMAGE) - .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) - .setIcon(R.drawable.send_tab, R.string.send_tab_to_self_share_activity_title) - .setFeatureNameForMetrics("SharingHubAndroid.SendTabToSelfSelected") - .setOnClickCallback((view) -> { - SendTabToSelfCoordinator sttsCoordinator = - new SendTabToSelfCoordinator(mActivity, mWindowAndroid, mUrl, - mShareParams.getTitle(), mBottomSheetController, mProfile); - sttsCoordinator.show(); - }) - .build(); - } - - private FirstPartyOption createQrCodeFirstPartyOption() { - return new FirstPartyOptionBuilder( - ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.IMAGE) - .setDetailedContentTypesToDisableFor(DetailedContentType.WEB_NOTES) - .setIcon(R.drawable.qr_code, R.string.qr_code_share_icon_label) - .setFeatureNameForMetrics("SharingHubAndroid.QRCodeSelected") - .setOnClickCallback((view) -> { - QrCodeCoordinator qrCodeCoordinator = - new QrCodeCoordinator(mActivity, mUrl, mShareParams.getWindow()); - qrCodeCoordinator.show(); - }) - .build(); - } - - private FirstPartyOption createPrintingFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.LINK_PAGE_VISIBLE) - .setIcon(R.drawable.sharing_print, R.string.print_share_activity_title) - .setFeatureNameForMetrics("SharingHubAndroid.PrintSelected") - .setOnClickCallback((view) -> { mPrintTabCallback.onResult(mTabProvider.get()); }) - .build(); - } - - private FirstPartyOption createWebNotesStylizeFirstPartyOption() { - boolean showNewBadge = mFeatureEngagementTracker.isInitialized() - && mFeatureEngagementTracker.shouldTriggerHelpUI( - FeatureConstants.SHARING_HUB_WEBNOTES_STYLIZE_FEATURE); - String title = mShareParams.getTitle(); - return new FirstPartyOptionBuilder(ContentType.HIGHLIGHTED_TEXT) - .setIcon(R.drawable.webnote, R.string.sharing_webnotes_create_card) - .setIconContentDescription(R.string.sharing_webnotes_accessibility_description) - .setFeatureNameForMetrics("SharingHubAndroid.WebnotesStylize") - .setOnClickCallback((view) -> { - mFeatureEngagementTracker.notifyEvent( - EventConstants.SHARING_HUB_WEBNOTES_STYLIZE_USED); - NoteCreationCoordinator coordinator = NoteCreationCoordinatorFactory.create( - mActivity, mShareParams.getWindow(), mUrl, title, - mShareParams.getRawText().trim(), mChromeOptionShareCallback); - coordinator.showDialog(); - }) - .setShowNewBadge(showNewBadge) - .build(); - } - - private FirstPartyOption createSaveImageFirstPartyOption() { - return new FirstPartyOptionBuilder(ContentType.IMAGE, ContentType.IMAGE_AND_LINK) - .setIcon(R.drawable.save_to_device, R.string.sharing_save_image) - .setFeatureNameForMetrics("SharingHubAndroid.SaveImageSelected") - .setOnClickCallback((view) -> { - if (mShareParams.getFileUris().isEmpty()) return; - - ShareImageFileUtils.getBitmapFromUriAsync( - mActivity, mShareParams.getFileUris().get(0), (bitmap) -> { - SaveBitmapDelegate saveBitmapDelegate = new SaveBitmapDelegate( - mActivity, bitmap, R.string.save_image_filename_prefix, - null, mShareParams.getWindow()); - saveBitmapDelegate.save(); - }); - }) - .build(); - } - private void callTargetChosenCallback() { ShareParams.TargetChosenCallback callback = mShareParams.getCallback(); if (callback != null) { @@ -531,5 +202,4 @@ mShareParams.setCallback(null); } } - }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java index c940fc529..0253de4 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
@@ -52,7 +52,7 @@ ContentType.HIGHLIGHTED_TEXT, ContentType.LINK_AND_TEXT, ContentType.IMAGE, ContentType.OTHER_FILE_TYPE, ContentType.IMAGE_AND_LINK}) @Retention(RetentionPolicy.SOURCE) - @interface ContentType { + public @interface ContentType { int LINK_PAGE_VISIBLE = 0; int LINK_PAGE_NOT_VISIBLE = 1; int TEXT = 2;
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni index a561bb3f..954efa3 100644 --- a/chrome/browser/share/android/java_sources.gni +++ b/chrome/browser/share/android/java_sources.gni
@@ -7,6 +7,7 @@ share_java_sources = [ "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/BaseScreenshotCoordinator.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/BitmapDownloadRequest.java", + "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ChromeProvidedSharingOptionsProviderBase.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveBitmapDelegate.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/crow/CrowBridge.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/crow/CrowIphController.java",
diff --git a/chrome/browser/speech/crosapi_tts_engine_delegate_ash.cc b/chrome/browser/speech/crosapi_tts_engine_delegate_ash.cc index e78c1af..139bd1a 100644 --- a/chrome/browser/speech/crosapi_tts_engine_delegate_ash.cc +++ b/chrome/browser/speech/crosapi_tts_engine_delegate_ash.cc
@@ -51,3 +51,13 @@ crosapi::CrosapiManager::Get()->crosapi_ash()->tts_ash()->StopRemoteEngine( utterance); } + +void CrosapiTtsEngineDelegateAsh::Pause(content::TtsUtterance* utterance) { + crosapi::CrosapiManager::Get()->crosapi_ash()->tts_ash()->PauseRemoteEngine( + utterance); +} + +void CrosapiTtsEngineDelegateAsh::Resume(content::TtsUtterance* utterance) { + crosapi::CrosapiManager::Get()->crosapi_ash()->tts_ash()->ResumeRemoteEngine( + utterance); +}
diff --git a/chrome/browser/speech/crosapi_tts_engine_delegate_ash.h b/chrome/browser/speech/crosapi_tts_engine_delegate_ash.h index 7435e90..9222a92 100644 --- a/chrome/browser/speech/crosapi_tts_engine_delegate_ash.h +++ b/chrome/browser/speech/crosapi_tts_engine_delegate_ash.h
@@ -24,6 +24,8 @@ void Speak(content::TtsUtterance* utterance, const content::VoiceData& voice) override; void Stop(content::TtsUtterance* utterance) override; + void Pause(content::TtsUtterance* utterance) override; + void Resume(content::TtsUtterance* utterance) override; }; #endif // CHROME_BROWSER_SPEECH_CROSAPI_TTS_ENGINE_DELEGATE_ASH_H_
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc index f45aa44..de071675 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -339,27 +339,33 @@ } void TtsExtensionEngine::Pause(content::TtsUtterance* utterance) { - Profile* profile = - Profile::FromBrowserContext(utterance->GetBrowserContext()); + Pause(utterance->GetBrowserContext(), utterance->GetEngineId()); +} + +void TtsExtensionEngine::Pause(content::BrowserContext* browser_context, + const std::string& engine_id) { + Profile* profile = Profile::FromBrowserContext(browser_context); auto event = std::make_unique<extensions::Event>( extensions::events::TTS_ENGINE_ON_PAUSE, tts_engine_events::kOnPause, base::Value::List(), profile); EventRouter* event_router = EventRouter::Get(profile); - std::string id = utterance->GetEngineId(); - event_router->DispatchEventToExtension(id, std::move(event)); - WarnIfMissingPauseOrResumeListener(profile, event_router, id); + event_router->DispatchEventToExtension(engine_id, std::move(event)); + WarnIfMissingPauseOrResumeListener(profile, event_router, engine_id); } void TtsExtensionEngine::Resume(content::TtsUtterance* utterance) { - Profile* profile = - Profile::FromBrowserContext(utterance->GetBrowserContext()); + Resume(utterance->GetBrowserContext(), utterance->GetEngineId()); +} + +void TtsExtensionEngine::Resume(content::BrowserContext* browser_context, + const std::string& engine_id) { + Profile* profile = Profile::FromBrowserContext(browser_context); auto event = std::make_unique<extensions::Event>( extensions::events::TTS_ENGINE_ON_RESUME, tts_engine_events::kOnResume, base::Value::List(), profile); EventRouter* event_router = EventRouter::Get(profile); - std::string id = utterance->GetEngineId(); - event_router->DispatchEventToExtension(id, std::move(event)); - WarnIfMissingPauseOrResumeListener(profile, event_router, id); + event_router->DispatchEventToExtension(engine_id, std::move(event)); + WarnIfMissingPauseOrResumeListener(profile, event_router, engine_id); } void TtsExtensionEngine::LoadBuiltInTtsEngine(
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.h b/chrome/browser/speech/extension_api/tts_engine_extension_api.h index 819858f..7fb8057 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api.h +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
@@ -45,14 +45,22 @@ void Speak(content::TtsUtterance* utterance, const content::VoiceData& voice) override; void Stop(content::TtsUtterance* utterance) override; - void Stop(content::BrowserContext* browser_context, - const std::string& engine_id) override; void Pause(content::TtsUtterance* utterance) override; void Resume(content::TtsUtterance* utterance) override; void LoadBuiltInTtsEngine(content::BrowserContext* browser_context) override; bool IsBuiltInTtsEngineInitialized( content::BrowserContext* browser_context) override; + // Stops the given speech engine loaded in |browser_context|. + void Stop(content::BrowserContext* browser_context, + const std::string& engine_id); + // Pauses the given speech engine loaded in |browser_context|. + void Pause(content::BrowserContext* browser_context, + const std::string& engine_id); + // Resumes the given speech engine loaded in |browser_context|. + void Resume(content::BrowserContext* browser_context, + const std::string& engine_id); + void DisableBuiltInTTSEngineForTesting() { disable_built_in_tts_engine_for_testing_ = true; }
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_lacros_browsertest.cc b/chrome/browser/speech/extension_api/tts_extension_api_lacros_browsertest.cc index dffe3228..cc76aced 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api_lacros_browsertest.cc +++ b/chrome/browser/speech/extension_api/tts_extension_api_lacros_browsertest.cc
@@ -333,4 +333,36 @@ ASSERT_TRUE(IsUtteranceQueueEmpty()); } +IN_PROC_BROWSER_TEST_F(LacrosTtsApiTest, PauseBeforeSpeakWithLacrosTtsEngine) { + if (chromeos::LacrosService::Get()->GetInterfaceVersion( + crosapi::mojom::Tts::Uuid_) < + static_cast<int>(crosapi::mojom::Tts::kPauseMinVersion)) { + GTEST_SKIP() << "Unsupported ash version."; + } + + // Load Lacros tts engine extension, register the tts engine events, and + // call tts.pause, then tts.speak, tts.resume from the testing extension. + ASSERT_TRUE( + RunExtensionTest("tts_engine/lacros_tts_support/" + "tts_pause_before_speak_lacros_engine", + {}, {.ignore_manifest_warnings = true})) + << message_; +} + +IN_PROC_BROWSER_TEST_F(LacrosTtsApiTest, PauseDuringSpeakWithLacrosTtsEngine) { + if (chromeos::LacrosService::Get()->GetInterfaceVersion( + crosapi::mojom::Tts::Uuid_) < + static_cast<int>(crosapi::mojom::Tts::kPauseMinVersion)) { + GTEST_SKIP() << "Unsupported ash version."; + } + + // Load Lacros tts engine extension, register the tts engine events, and + // call tts.speak, then tts.pause, tts.resume from the testing extension. + ASSERT_TRUE( + RunExtensionTest("tts_engine/lacros_tts_support/" + "tts_pause_during_speak_lacros_engine", + {}, {.ignore_manifest_warnings = true})) + << message_; +} + } // namespace extensions
diff --git a/chrome/browser/speech/tts_ash.cc b/chrome/browser/speech/tts_ash.cc index 5d2a377..566934c2 100644 --- a/chrome/browser/speech/tts_ash.cc +++ b/chrome/browser/speech/tts_ash.cc
@@ -236,6 +236,14 @@ content::TtsController::GetInstance()->Stop(source_url); } +void TtsAsh::Pause() { + content::TtsController::GetInstance()->Pause(); +} + +void TtsAsh::Resume() { + content::TtsController::GetInstance()->Resume(); +} + void TtsAsh::SpeakWithLacrosVoice(content::TtsUtterance* utterance, const content::VoiceData& voice) { if (!HasTtsClient()) @@ -295,6 +303,18 @@ item->second->Stop(utterance->GetEngineId()); } +void TtsAsh::PauseRemoteEngine(content::TtsUtterance* utterance) { + auto item = tts_clients_.find(GetPrimaryProfileBrowserContextId()); + DCHECK(item != tts_clients_.end()); + item->second->Pause(utterance->GetEngineId()); +} + +void TtsAsh::ResumeRemoteEngine(content::TtsUtterance* utterance) { + auto item = tts_clients_.find(GetPrimaryProfileBrowserContextId()); + DCHECK(item != tts_clients_.end()); + item->second->Resume(utterance->GetEngineId()); +} + void TtsAsh::GetCrosapiVoices(base::UnguessableToken browser_context_id, std::vector<content::VoiceData>* out_voices) { // Returns the cached Lacros voices.
diff --git a/chrome/browser/speech/tts_ash.h b/chrome/browser/speech/tts_ash.h index 79ccc02f..029eaec1 100644 --- a/chrome/browser/speech/tts_ash.h +++ b/chrome/browser/speech/tts_ash.h
@@ -55,6 +55,14 @@ // |utterance|. void StopRemoteEngine(content::TtsUtterance* utterance); + // Requests the associated Lacros speech engine to pause speaking the + // |utterance|. + void PauseRemoteEngine(content::TtsUtterance* utterance); + + // Requests the associated Lacros speech engine to resume speaking the + // |utterance|. + void ResumeRemoteEngine(content::TtsUtterance* utterance); + void DeletePendingAshUtteranceClient(int utterance_id); // crosapi::mojom::Tts: @@ -67,6 +75,8 @@ mojom::TtsUtterancePtr utterance, mojo::PendingRemote<mojom::TtsUtteranceClient> utterance_client) override; void Stop(const GURL& source_url) override; + void Pause() override; + void Resume() override; private: class TtsUtteranceClient;
diff --git a/chrome/browser/speech/tts_client_lacros.cc b/chrome/browser/speech/tts_client_lacros.cc index c9dc174..434f8c84 100644 --- a/chrome/browser/speech/tts_client_lacros.cc +++ b/chrome/browser/speech/tts_client_lacros.cc
@@ -265,8 +265,15 @@ } void TtsClientLacros::Stop(const std::string& engine_id) { - content::TtsController::GetInstance()->GetTtsEngineDelegate()->Stop( - browser_context_, engine_id); + TtsExtensionEngine::GetInstance()->Stop(browser_context_, engine_id); +} + +void TtsClientLacros::Pause(const std::string& engine_id) { + TtsExtensionEngine::GetInstance()->Pause(browser_context_, engine_id); +} + +void TtsClientLacros::Resume(const std::string& engine_id) { + TtsExtensionEngine::GetInstance()->Resume(browser_context_, engine_id); } void TtsClientLacros::GetAllVoices( @@ -401,6 +408,30 @@ lacros_service->GetRemote<crosapi::mojom::Tts>()->Stop(source_url); } +void TtsClientLacros::RequestPause() { + auto* lacros_service = chromeos::LacrosService::Get(); + if (!lacros_service->IsAvailable<crosapi::mojom::Tts>() || + static_cast<uint32_t>( + lacros_service->GetInterfaceVersion(crosapi::mojom::Tts::Uuid_)) < + crosapi::mojom::Tts::kPauseMinVersion) { + LOG(WARNING) << kErrorUnsupportedVersion; + return; + } + lacros_service->GetRemote<crosapi::mojom::Tts>()->Pause(); +} + +void TtsClientLacros::RequestResume() { + auto* lacros_service = chromeos::LacrosService::Get(); + if (!lacros_service->IsAvailable<crosapi::mojom::Tts>() || + static_cast<uint32_t>( + lacros_service->GetInterfaceVersion(crosapi::mojom::Tts::Uuid_)) < + crosapi::mojom::Tts::kResumeMinVersion) { + LOG(WARNING) << kErrorUnsupportedVersion; + return; + } + lacros_service->GetRemote<crosapi::mojom::Tts>()->Resume(); +} + void TtsClientLacros::OnLacrosSpeechEngineTtsEvent( int utterance_id, content::TtsEventType event_type,
diff --git a/chrome/browser/speech/tts_client_lacros.h b/chrome/browser/speech/tts_client_lacros.h index b94bbd64..fd9e4ed 100644 --- a/chrome/browser/speech/tts_client_lacros.h +++ b/chrome/browser/speech/tts_client_lacros.h
@@ -45,6 +45,8 @@ mojo::PendingRemote<crosapi::mojom::TtsUtteranceClient> ash_utterance_client) override; void Stop(const std::string& engine_id) override; + void Pause(const std::string& engine_id) override; + void Resume(const std::string& engine_id) override; const base::UnguessableToken& browser_context_id() const { return browser_context_id_; @@ -61,6 +63,16 @@ // the given |source_url|) to Ash. void RequestStop(const GURL& source_url); + // Forwards the Pause request from Lacros Tts client (Tts extension api or + // speechSynthesis web api) to Ash, so that the request will be processed by + // Ash's TtsController. + void RequestPause(); + + // Forwards the Resume request from Lacros Tts client (Tts extension api or + // speechSynthesis web api) to Ash, so that the request will be processed by + // Ash's TtsController. + void RequestResume(); + // Handle events received from the Lacros speech engine. void OnLacrosSpeechEngineTtsEvent(int utterance_id, content::TtsEventType event_type,
diff --git a/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.cc b/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.cc index 7b3cc7ab..e4a1b9dc 100644 --- a/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.cc +++ b/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.cc
@@ -61,3 +61,15 @@ TtsClientLacros::GetForBrowserContext(browser_context) ->RequestStop(source_url); } + +void ExternalPlatformDelegateImplLacros::Pause() { + content::BrowserContext* browser_context = + ProfileManager::GetPrimaryUserProfile(); + TtsClientLacros::GetForBrowserContext(browser_context)->RequestPause(); +} + +void ExternalPlatformDelegateImplLacros::Resume() { + content::BrowserContext* browser_context = + ProfileManager::GetPrimaryUserProfile(); + TtsClientLacros::GetForBrowserContext(browser_context)->RequestResume(); +}
diff --git a/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.h b/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.h index ca6027db..22e921b 100644 --- a/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.h +++ b/chrome/browser/speech/tts_external_platform_delegate_impl_lacros.h
@@ -32,6 +32,8 @@ int length, const std::string& error_message) override; void Stop(const GURL& source_url) override; + void Pause() override; + void Resume() override; private: friend class base::NoDestructor<ExternalPlatformDelegateImplLacros>;
diff --git a/chrome/browser/storage/shared_storage_browsertest.cc b/chrome/browser/storage/shared_storage_browsertest.cc index addd9667..74d81f2 100644 --- a/chrome/browser/storage/shared_storage_browsertest.cc +++ b/chrome/browser/storage/shared_storage_browsertest.cc
@@ -16,6 +16,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/scoped_run_loop_timeout.h" #include "base/test/task_environment.h" +#include "base/test/with_feature_override.h" #include "chrome/browser/profiles/profile.h" #include "chrome/test/base/chrome_test_utils.h" #include "components/content_settings/core/browser/cookie_settings.h" @@ -31,6 +32,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/shared_storage_test_utils.h" #include "content/public/test/test_navigation_observer.h" +#include "content/public/test/test_select_url_fenced_frame_config_observer.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/request_handler_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -104,6 +106,14 @@ const double kBudgetAllowed = 5.0; +auto describe_param = [](const auto& info) { + if (info.param) { + return "ResolveSelectURLToConfig"; + } else { + return "ResolveSelectURLToURN"; + } +}; + #if BUILDFLAG(IS_ANDROID) base::FilePath GetChromeTestDataDir() { return base::FilePath(FILE_PATH_LITERAL("chrome/test/data")); @@ -199,9 +209,9 @@ } // namespace -class SharedStorageChromeBrowserTest : public PlatformBrowserTest { +class SharedStorageChromeBrowserTestBase : public PlatformBrowserTest { public: - SharedStorageChromeBrowserTest() { + SharedStorageChromeBrowserTestBase() { base::test::TaskEnvironment task_environment; // TODO(crbug.com/1378703): Update the tests to support Privacy Sandbox 4. @@ -223,7 +233,7 @@ InitPrefs(); } - ~SharedStorageChromeBrowserTest() override = default; + ~SharedStorageChromeBrowserTestBase() override = default; net::EmbeddedTestServer* https_server() { return &https_server_; } @@ -300,8 +310,7 @@ MakeFilter({"Finish executing customizable_module.js"})); base::StringPairs run_function_body_replacement; - run_function_body_replacement.push_back( - std::make_pair("{{RUN_FUNCTION_BODY}}", script)); + run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", script); std::string host = execution_target.render_frame_host()->GetLastCommittedOrigin().host(); @@ -383,6 +392,8 @@ return result; } + virtual bool ResolveSelectURLToConfig() { return false; } + protected: base::HistogramTester histogram_tester_; @@ -391,31 +402,44 @@ net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; }; -struct SharedStorageChromeBrowserParams { - bool enable_privacy_sandbox; - bool allow_third_party_cookies; +class SharedStorageChromeBrowserTest + : public base::test::WithFeatureOverride, + public SharedStorageChromeBrowserTestBase { + public: + SharedStorageChromeBrowserTest() + : base::test::WithFeatureOverride( + blink::features::kFencedFramesAPIChanges) { + scoped_feature_list_.InitAndEnableFeature(blink::features::kFencedFrames); + } + ~SharedStorageChromeBrowserTest() override = default; + + bool ResolveSelectURLToConfig() override { return IsParamFeatureEnabled(); } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; -// Used by `testing::PrintToStringParamName()`. -std::string PrintToString(const SharedStorageChromeBrowserParams& p) { - return base::StrCat( - {"PrivacySandbox", p.enable_privacy_sandbox ? "Enabled" : "Disabled", - "_3PCookies", p.allow_third_party_cookies ? "Allowed" : "Blocked"}); -} - -std::vector<SharedStorageChromeBrowserParams> -GetSharedStorageChromeBrowserParams() { - return std::vector<SharedStorageChromeBrowserParams>( - {{true, true}, {true, false}, {false, true}, {false, false}}); -} +using SharedStorageChromeBrowserParams = + std::tuple</*resolve_to_config=*/bool, + /*enable_privacy_sandbox=*/bool, + /*allow_third_party_cookies=*/bool>; class SharedStoragePrefBrowserTest - : public SharedStorageChromeBrowserTest, + : public SharedStorageChromeBrowserTestBase, public testing::WithParamInterface<SharedStorageChromeBrowserParams> { public: + SharedStoragePrefBrowserTest() { + fenced_frame_api_change_feature_.InitWithFeatureState( + blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig()); + fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames); + } + + bool ResolveSelectURLToConfig() override { return std::get<0>(GetParam()); } + bool EnablePrivacySandbox() const { return std::get<1>(GetParam()); } + bool AllowThirdPartyCookies() const { return std::get<2>(GetParam()); } + bool SuccessExpected() { - return GetParam().enable_privacy_sandbox && - GetParam().allow_third_party_cookies; + return EnablePrivacySandbox() && AllowThirdPartyCookies(); } // Sets prefs as parametrized. @@ -423,8 +447,7 @@ // TODO(crbug.com/1396748): We may need to update how preferences are set once // the Privacy Sandbox settings release 4 is launched (crbug.com/1378703). void InitPrefs() override { - SetPrefs(GetParam().enable_privacy_sandbox, - GetParam().allow_third_party_cookies); + SetPrefs(EnablePrivacySandbox(), AllowThirdPartyCookies()); } void AddSimpleModuleWithPermissionBypassed( @@ -462,8 +485,7 @@ MakeFilter({"Finish executing customizable_module.js"})); base::StringPairs run_function_body_replacement; - run_function_body_replacement.push_back( - std::make_pair("{{RUN_FUNCTION_BODY}}", script)); + run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", script); std::string host = execution_target.render_frame_host()->GetLastCommittedOrigin().host(); @@ -530,13 +552,23 @@ return result.error.empty(); } + + private: + base::test::ScopedFeatureList fenced_frame_api_change_feature_; + base::test::ScopedFeatureList fenced_frame_feature_; }; INSTANTIATE_TEST_SUITE_P( All, SharedStoragePrefBrowserTest, - testing::ValuesIn(GetSharedStorageChromeBrowserParams()), - testing::PrintToStringParamName()); + testing::Combine(testing::Bool(), testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<SharedStoragePrefBrowserTest::ParamType>& + info) { + return base::StrCat( + {"ResolveSelectURLTo", std::get<0>(info.param) ? "Config" : "URN", + "_PrivacySandbox", std::get<1>(info.param) ? "Enabled" : "Disabled", + "_3PCookies", std::get<2>(info.param) ? "Allowed" : "Blocked"}); + }); IN_PROC_BROWSER_TEST_P(SharedStoragePrefBrowserTest, AddModule) { EXPECT_TRUE(content::NavigateToURL( @@ -644,16 +676,48 @@ run_url_op_console_observer.SetFilter( MakeFilter({"Finish executing \'test-url-selection-operation\'"})); - content::EvalJsResult run_url_op_result = - content::EvalJs(GetActiveWebContents(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], - {data: {'mockResult': 1}}); - )"); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + // Construct and add the `TestSelectURLFencedFrameConfigObserver` to shared + // storage worklet host manager. + content::StoragePartition* storage_partition = + content::ToRenderFrameHost(GetActiveWebContents()) + .render_frame_host() + ->GetStoragePartition(); + content::TestSelectURLFencedFrameConfigObserver config_observer( + storage_partition); + content::EvalJsResult run_url_op_result = EvalJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); WaitForHistograms({kTimingDocumentAddModuleHistogram}); histogram_tester_.ExpectTotalCount(kTimingDocumentAddModuleHistogram, 1); @@ -679,8 +743,15 @@ // Privacy Sandbox is enabled and 3P cookies are allowed, so Shared Storage // should be allowed. EXPECT_TRUE(run_url_op_result.error.empty()); - EXPECT_TRUE( - blink::IsValidUrnUuidURL(GURL(run_url_op_result.ExtractString()))); + absl::optional<GURL> observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + GURL urn_uuid = observed_urn_uuid.value(); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(run_url_op_result.ExtractString(), observed_urn_uuid->spec()); + } + EXPECT_EQ(1u, run_url_op_console_observer.messages().size()); EXPECT_EQ( "Finish executing \'test-url-selection-operation\'", @@ -1059,7 +1130,7 @@ } } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletKeysEntries_AllIterated) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1142,7 +1213,7 @@ 2); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletKeysEntries_PartiallyIterated) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1239,7 +1310,7 @@ 0); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletKeysEntries_AllIteratedLessThanTenKeys) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1322,7 +1393,7 @@ 2); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletKeysEntries_PartiallyIteratedLessThanTenKeys) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1417,7 +1488,7 @@ 0); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletKeysEntries_AllIteratedNoKeys) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1480,7 +1551,7 @@ 0); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_InvalidScriptUrlError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1505,7 +1576,7 @@ blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_CrossOriginScriptError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1532,7 +1603,7 @@ blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_LoadFailureError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1555,7 +1626,7 @@ blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_UnexpectedRedirectError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1578,7 +1649,7 @@ blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_EmptyResultError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1601,7 +1672,7 @@ blink::SharedStorageWorkletErrorType::kAddModuleWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, AddModule_MultipleAddModuleError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1634,7 +1705,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, Run_NotLoadedError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_NotLoadedError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1651,7 +1722,7 @@ blink::SharedStorageWorkletErrorType::kRunNonWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, Run_NotRegisteredError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_NotRegisteredError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1680,7 +1751,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, Run_FunctionError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_FunctionError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1709,7 +1780,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, Run_NotAPromiseError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_NotAPromiseError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1738,7 +1809,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, Run_ScriptError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_ScriptError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1767,7 +1838,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, Run_UnexpectedCustomDataError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1797,18 +1868,36 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_NotLoadedError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); - content::EvalJsResult result = content::EvalJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation-1', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )"); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + content::EvalJsResult result = EvalJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation-1', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); EXPECT_EQ(base::StrCat({"a JavaScript error: \"Error: ", "sharedStorage.worklet.addModule() has to be ", @@ -1822,7 +1911,7 @@ blink::SharedStorageWorkletErrorType::kSelectURLWebVisible, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_NotRegisteredError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1834,12 +1923,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation-1', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation-1', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -1853,7 +1960,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_FunctionError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1865,12 +1972,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -1884,7 +2009,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_NotAPromiseError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1896,12 +2021,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -1915,7 +2058,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, SelectUrl_ScriptError) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_ScriptError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), https_server()->GetURL(kSimpleTestHost, kSimplePagePath))); @@ -1926,12 +2069,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -1945,7 +2106,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_UnexpectedCustomDataError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1957,13 +2118,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], - {data: {'customField': 'customValue123'}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'customField': 'customValue123'}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -1977,7 +2155,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_OutOfRangeError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -1989,12 +2167,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation-1', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation-1', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -2008,7 +2204,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, SelectUrl_ReturnValueToIntError) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -2020,12 +2216,30 @@ GetActiveWebContents(), content::JsReplace("sharedStorage.worklet.addModule($1)", script_url))); - EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), - R"( - sharedStorage.selectURL( - 'test-url-selection-operation-2', - [{url: "fenced_frames/title0.html"}], {data: {}}); - )")); + EXPECT_TRUE(ExecJs(GetActiveWebContents(), + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation-2', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )")); // Navigate away to record `kWorkletNumPerPageHistogram` histogram. EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), @@ -2039,7 +2253,7 @@ histogram_tester_.ExpectUniqueSample(kWorkletNumPerPageHistogram, 1, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, DocumentTiming) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, DocumentTiming) { base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60)); EXPECT_TRUE(content::NavigateToURL( @@ -2076,7 +2290,7 @@ histogram_tester_.ExpectTotalCount(kTimingDocumentClearHistogram, 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, WorkletTiming) { +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, WorkletTiming) { base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(60)); EXPECT_TRUE(content::NavigateToURL( @@ -2136,7 +2350,7 @@ } // Flaky: https://crbug.com/1406845 -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, DISABLED_WorkletNumPerPage_Two) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -2176,7 +2390,7 @@ } // Flaky: https://crbug.com/1406845 -IN_PROC_BROWSER_TEST_F(SharedStorageChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageChromeBrowserTest, DISABLED_WorkletNumPerPage_Three) { EXPECT_TRUE(content::NavigateToURL( GetActiveWebContents(), @@ -2225,24 +2439,32 @@ histogram_tester_.GetAllSamples(kTimingWorkletSetHistogram).size()); } +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageChromeBrowserTest, + testing::Bool(), + describe_param); + class SharedStorageFencedFrameChromeBrowserTest - : public SharedStorageChromeBrowserTest { + : public base::test::WithFeatureOverride, + public SharedStorageChromeBrowserTestBase { public: - SharedStorageFencedFrameChromeBrowserTest() { + SharedStorageFencedFrameChromeBrowserTest() + : base::test::WithFeatureOverride( + blink::features::kFencedFramesAPIChanges) { base::test::TaskEnvironment task_environment; scoped_feature_list_.InitWithFeaturesAndParameters( /*enabled_features=*/ {{blink::features::kSharedStorageAPI, {{"SharedStorageBitBudget", base::NumberToString(kBudgetAllowed)}}}, - {blink::features::kFencedFrames, {}}, - {privacy_sandbox::kPrivacySandboxSettings3, {}}, - {features::kPrivacySandboxAdsAPIsOverride, {}}}, + {blink::features::kFencedFrames, {}}}, /*disabled_features=*/{}); } ~SharedStorageFencedFrameChromeBrowserTest() override = default; + bool ResolveSelectURLToConfig() override { return IsParamFeatureEnabled(); } + content::RenderFrameHost* SelectURLAndCreateFencedFrame( content::RenderFrameHost* render_frame_host, bool should_add_module = true) { @@ -2254,36 +2476,80 @@ run_url_op_console_observer.SetFilter( MakeFilter({"Finish executing \'test-url-selection-operation\'"})); - content::EvalJsResult run_url_op_result = - content::EvalJs(render_frame_host, R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], - {data: {'mockResult': 1}}); - )"); + EXPECT_TRUE( + ExecJs(render_frame_host, + content::JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + // Construct and add the `TestSelectURLFencedFrameConfigObserver` to shared + // storage worklet host manager. + content::StoragePartition* storage_partition = + content::ToRenderFrameHost(GetActiveWebContents()) + .render_frame_host() + ->GetStoragePartition(); + content::TestSelectURLFencedFrameConfigObserver config_observer( + storage_partition); + content::EvalJsResult run_url_op_result = EvalJs(render_frame_host, R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: + { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); EXPECT_TRUE(run_url_op_console_observer.Wait()); - EXPECT_TRUE(run_url_op_result.error.empty()); - EXPECT_TRUE( - blink::IsValidUrnUuidURL(GURL(run_url_op_result.ExtractString()))); + const absl::optional<GURL>& observed_urn_uuid = + config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(run_url_op_result.ExtractString(), observed_urn_uuid->spec()); + } + + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); EXPECT_EQ(1u, run_url_op_console_observer.messages().size()); EXPECT_EQ( "Finish executing \'test-url-selection-operation\'", base::UTF16ToUTF8(run_url_op_console_observer.messages()[0].message)); - return content::CreateFencedFrame(render_frame_host, - GURL(run_url_op_result.ExtractString())); + return content::CreateFencedFrame( + render_frame_host, + ResolveSelectURLToConfig() + ? content::FencedFrameNavigationTarget("select_url_result") + : content::FencedFrameNavigationTarget(observed_urn_uuid.value())); } private: base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameChromeBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameChromeBrowserTest, FencedFrameNavigateTop_BudgetWithdrawal) { GURL main_url = https_server()->GetURL(kSimpleTestHost, kSimplePagePath); EXPECT_TRUE(NavigateToURL(GetActiveWebContents(), main_url)); @@ -2300,9 +2566,9 @@ content::TestNavigationObserver top_navigation_observer( GetActiveWebContents()); - EXPECT_TRUE(ExecJs(fenced_frame_root_node, - content::JsReplace("window.open($1, '_unfencedTop')", - new_page_url.spec()))); + EXPECT_TRUE(ExecJs( + fenced_frame_root_node, + content::JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); content::RenderFrameHost* new_iframe = @@ -2331,7 +2597,7 @@ EXPECT_EQ(2, histogram_tester_.GetTotalSum(kWorkletNumPerPageHistogram)); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameChromeBrowserTest, TwoFencedFrames_DifferentURNs_EachNavigateOnce_BudgetWithdrawalTwice) { GURL main_url = https_server()->GetURL(kSimpleTestHost, kSimplePagePath); @@ -2350,9 +2616,9 @@ content::TestNavigationObserver top_navigation_observer1( GetActiveWebContents()); - EXPECT_TRUE(ExecJs(fenced_frame_root_node1, - content::JsReplace("window.open($1, '_unfencedTop')", - new_page_url1.spec()))); + EXPECT_TRUE(ExecJs( + fenced_frame_root_node1, + content::JsReplace("window.open($1, '_unfencedTop')", new_page_url1))); top_navigation_observer1.Wait(); content::RenderFrameHost* iframe2 = @@ -2372,9 +2638,9 @@ content::TestNavigationObserver top_navigation_observer2( GetActiveWebContents()); - EXPECT_TRUE(ExecJs(fenced_frame_root_node2, - content::JsReplace("window.open($1, '_unfencedTop')", - new_page_url2.spec()))); + EXPECT_TRUE(ExecJs( + fenced_frame_root_node2, + content::JsReplace("window.open($1, '_unfencedTop')", new_page_url2))); top_navigation_observer2.Wait(); content::RenderFrameHost* iframe3 = @@ -2403,4 +2669,9 @@ EXPECT_EQ(3, histogram_tester_.GetTotalSum(kWorkletNumPerPageHistogram)); } +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageFencedFrameChromeBrowserTest, + testing::Bool(), + describe_param); + } // namespace storage
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc index c0828406..9e589e4 100644 --- a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc +++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
@@ -126,15 +126,13 @@ if (option == translate::TranslateUtils::OPTION_SOURCE_CODE) { std::string source_code = base::android::ConvertJavaStringToUTF8(env, value); - if (delegate->source_language_code().compare(source_code) != 0) - delegate->UpdateSourceLanguage(source_code); + delegate->UpdateSourceLanguage(source_code); delegate->ReportUIInteraction( translate::UIInteraction::kChangeSourceLanguage); } else if (option == translate::TranslateUtils::OPTION_TARGET_CODE) { std::string target_code = base::android::ConvertJavaStringToUTF8(env, value); - if (delegate->target_language_code().compare(target_code) != 0) - delegate->UpdateTargetLanguage(target_code); + delegate->UpdateTargetLanguage(target_code); delegate->ReportUIInteraction( translate::UIInteraction::kChangeTargetLanguage); } else {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java index 0852e0e10..e59c399 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -10,7 +10,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.chromium.base.LifetimeAssert; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.NativeMethods; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.VoiceResult; @@ -45,7 +44,6 @@ // Maximum number of voice suggestions to show. private static final int MAX_VOICE_SUGGESTION_COUNT = 3; - private final @NonNull LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this); private final @NonNull Profile mProfile; private final @NonNull Set<OnSuggestionsReceivedListener> mListeners = new HashSet<>(); private long mNativeController; @@ -231,7 +229,6 @@ if (mNativeController == 0) return; AutocompleteControllerJni.get().destroy(mNativeController); mNativeController = 0; - LifetimeAssert.setSafeToGc(mLifetimeAssert, true); } /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProvider.java index 60f7fd1..c6d971cc 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProvider.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProvider.java
@@ -10,7 +10,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.chromium.base.LifetimeAssert; import org.chromium.base.ThreadUtils; import org.chromium.base.UnownedUserData; import org.chromium.base.UnownedUserDataHost; @@ -27,7 +26,6 @@ private static final @NonNull UnownedUserDataKey<AutocompleteControllerProvider> KEY = new UnownedUserDataKey<>(AutocompleteControllerProvider.class); private static @Nullable AutocompleteController sControllerForTesting; - private final @NonNull LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this); private final @NonNull ArrayMap<Profile, AutocompleteController> mControllers = new ArrayMap<>(); @@ -84,7 +82,6 @@ } ProfileManager.removeObserver(this); mControllers.clear(); - LifetimeAssert.setSafeToGc(mLifetimeAssert, true); } /**
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd index 422e7b7e..a4dd3e45 100644 --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5578,7 +5578,7 @@ <message name="IDS_ACCOUNT_SELECTION_SHEET_CLOSED" desc="Accessibility string read when the Account Selection bottom sheet showing a list of the user's accounts is closed." is_accessibility_with_no_ui="true"> Sign in bottom sheet is closed. </message> - <message name="IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN" desc="Header for verify sheet for auto re-authentication." translateable="false"> + <message name="IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN" desc="Header for verify sheet for auto re-authentication."> Signing you in… </message> <message name="IDS_VERIFY_SHEET_TITLE" desc="Header for verify sheet for explicit sign-in.">
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1 new file mode 100644 index 0000000..847f4b1 --- /dev/null +++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN.png.sha1
@@ -0,0 +1 @@ +68981af90fef53db2f42d49223f5f30929d21729 \ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn index fe3fcbf1..fd69c88 100644 --- a/chrome/browser/ui/android/toolbar/BUILD.gn +++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -84,6 +84,7 @@ "java/src/org/chromium/chrome/browser/toolbar/top/IncognitoSwitchViewBinder.java", "java/src/org/chromium/chrome/browser/toolbar/top/NavigationPopup.java", "java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java", + "java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateToken.java", "java/src/org/chromium/chrome/browser/toolbar/top/ResourceFactory.java", "java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceTabSwitcherActionMenuCoordinator.java", "java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarCoordinator.java", @@ -102,7 +103,7 @@ "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java", - "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java", + "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotDifference.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java", "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java", "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarInteractabilityManager.java", @@ -309,9 +310,9 @@ "java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/HomeButtonCoordinatorTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonControllerTest.java", + "java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinatorTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java", - "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarInteractabilityManagerTest.java", "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/CaptureReadinessResult.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/CaptureReadinessResult.java index 96b93237..c8507c8 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/CaptureReadinessResult.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/CaptureReadinessResult.java
@@ -9,7 +9,6 @@ import org.chromium.base.TraceEvent; import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.browser.toolbar.ToolbarFeatures; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateToken.java similarity index 73% rename from chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateToken.java index 7ece667c..01f2e96 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateToken.java
@@ -9,14 +9,11 @@ import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; import androidx.annotation.Nullable; import org.chromium.chrome.browser.toolbar.ButtonData; import org.chromium.chrome.browser.toolbar.top.ToolbarPhone.VisualState; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -24,40 +21,7 @@ * against new states, to infer if anything important has changed. Especially useful when deciding * if a new bitmap capture is warranted. */ -public class ToolbarSnapshotState { - /** - * Reasons that two snapshots are different. Treat this list as append only and keep it in sync - * with ToolbarSnapshotDifference in enums.xml, as well as the proto in - * chrome_track_event.proto. - **/ - @IntDef({ToolbarSnapshotDifference.NONE, ToolbarSnapshotDifference.NULL, - ToolbarSnapshotDifference.TINT, ToolbarSnapshotDifference.TAB_COUNT, - ToolbarSnapshotDifference.OPTIONAL_BUTTON_DATA, ToolbarSnapshotDifference.VISUAL_STATE, - ToolbarSnapshotDifference.SECURITY_ICON, ToolbarSnapshotDifference.SHOWING_UPDATE_BADGE, - ToolbarSnapshotDifference.PAINT_PREVIEW, ToolbarSnapshotDifference.PROGRESS, - ToolbarSnapshotDifference.LOCATION_BAR_WIDTH, ToolbarSnapshotDifference.URL_TEXT, - ToolbarSnapshotDifference.HOME_BUTTON_COLOR, ToolbarSnapshotDifference.TITLE_TEXT, - ToolbarSnapshotDifference.CCT_ANIMATION, ToolbarSnapshotDifference.NUM_ENTRIES}) - @Retention(RetentionPolicy.SOURCE) - public @interface ToolbarSnapshotDifference { - int NONE = 0; - int NULL = 1; - int TINT = 2; - int TAB_COUNT = 3; - int OPTIONAL_BUTTON_DATA = 4; - int VISUAL_STATE = 5; - int SECURITY_ICON = 6; - int SHOWING_UPDATE_BADGE = 7; - int PAINT_PREVIEW = 8; - int PROGRESS = 9; - int LOCATION_BAR_WIDTH = 10; - int URL_TEXT = 11; - int HOME_BUTTON_COLOR = 12; - int TITLE_TEXT = 13; - int CCT_ANIMATION = 14; - int NUM_ENTRIES = 15; - } - +class PhoneCaptureStateToken { private final @ColorInt int mTint; private final int mTabCount; private final ButtonData mOptionalButtonData; @@ -71,7 +35,7 @@ private final boolean mIsPaintPreview; private final int mUnfocusedLocationBarLayoutWidth; - public ToolbarSnapshotState(@ColorInt int tint, int tabCount, ButtonData optionalButtonData, + public PhoneCaptureStateToken(@ColorInt int tint, int tabCount, ButtonData optionalButtonData, @VisualState int visualState, String urlText, @Nullable CharSequence visibleTextPrefixHint, @DrawableRes int securityIcon, ColorStateList colorStateList, boolean isShowingUpdateBadgeDuringLastCapture, @@ -100,7 +64,7 @@ * @param that The other snapshot to compare against. * @return The difference. */ - public @ToolbarSnapshotDifference int getAnyDifference(ToolbarSnapshotState that) { + public @ToolbarSnapshotDifference int getAnyDifference(PhoneCaptureStateToken that) { if (that == null) { return ToolbarSnapshotDifference.NULL; } else if (mTint != that.mTint) { @@ -131,7 +95,7 @@ return ToolbarSnapshotDifference.NONE; } - private boolean isVisibleUrlTextSame(ToolbarSnapshotState that) { + private boolean isVisibleUrlTextSame(PhoneCaptureStateToken that) { if (mVisibleTextPrefixHint != null && TextUtils.equals(mVisibleTextPrefixHint, that.mVisibleTextPrefixHint)) { return true;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java new file mode 100644 index 0000000..379d8ff --- /dev/null +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java
@@ -0,0 +1,340 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.top; + +import android.content.res.ColorStateList; +import android.graphics.Color; + +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.toolbar.ButtonData; +import org.chromium.chrome.browser.toolbar.ButtonDataImpl; +import org.chromium.chrome.browser.toolbar.top.ToolbarPhone.VisualState; + +/** Unit tests for {@link PhoneCaptureStateToken}. */ +@RunWith(BaseRobolectricTestRunner.class) +public class PhoneCaptureStateTokenTest { + private static final @ColorInt int DEFAULT_TINT = Color.TRANSPARENT; + private static final int DEFAULT_TAB_COUNT = 1; + private static final ButtonData DEFAULT_BUTTON_DATA = makeButtonDate(); + private static final @VisualState int DEFAULT_VISUAL_STATE = VisualState.NORMAL; + private static final String DEFAULT_URL_TEXT = "https://www.example.com/"; + private static final CharSequence DEFAULT_URL_HINT_TEXT = null; + private static final @DrawableRes int DEFAULT_SECURITY_ICON = 0; + private static final boolean DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE = false; + private static final boolean DEFAULT_IS_PAINT_PREVIEW = false; + private static final float DEFAULT_PROGRESS = 0.1f; + private static final int DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH = 2; + + // Not static/final because they're initialized in #before(). Apparently ColorStateList.valueOf + // calls into Android native code, and cannot be done too early. + private ColorStateList mDefaultColorStateList; + private PhoneCaptureStateToken mDefaultPhoneCaptureStateToken; + + private static ButtonData makeButtonDate() { + // Uses default equals impl, reference quality, to compare. Values do not matter. + return new ButtonDataImpl(false, null, null, "", false, null, false, 0); + } + + @Before + public void before() { + mDefaultColorStateList = ColorStateList.valueOf(DEFAULT_TINT); + mDefaultPhoneCaptureStateToken = new PhoneCustomTabCaptureStateTokenBuilder().build(); + } + + @Test + public void testSameSnapshots() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentTint() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setTint(Color.RED).build(); + Assert.assertEquals(ToolbarSnapshotDifference.TINT, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentTabCount() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setTabCount(2).build(); + Assert.assertEquals(ToolbarSnapshotDifference.TAB_COUNT, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentOptionalButtonData() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setOptionalButtonData(makeButtonDate()) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.OPTIONAL_BUTTON_DATA, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentVisualState() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setVisualState(VisualState.INCOGNITO) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.VISUAL_STATE, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentUrlText() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setUrlText("https://www.other.com/") + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.URL_TEXT, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentUrlText_SameHintText() { + PhoneCaptureStateToken initialPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) + .build(); + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setUrlText(DEFAULT_URL_TEXT + "additional/paths/") + .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + initialPhoneCaptureStateToken.getAnyDifference(otherPhoneCaptureStateToken)); + } + + @Test + public void testSameUrlText_DifferentHintText() { + PhoneCaptureStateToken initialPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 2)) + .build(); + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 3)) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + initialPhoneCaptureStateToken.getAnyDifference(otherPhoneCaptureStateToken)); + } + + @Test + public void testSameUrlText_BothNullHintText() { + PhoneCaptureStateToken initialPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build(); + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + initialPhoneCaptureStateToken.getAnyDifference(otherPhoneCaptureStateToken)); + } + + @Test + public void testSameUrlText_NullHintText() { + PhoneCaptureStateToken initialPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build(); + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + initialPhoneCaptureStateToken.getAnyDifference(otherPhoneCaptureStateToken)); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + otherPhoneCaptureStateToken.getAnyDifference(initialPhoneCaptureStateToken)); + } + + @Test + public void testDifferentSecurityIcon() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setSecurityIcon(-1).build(); + Assert.assertEquals(ToolbarSnapshotDifference.SECURITY_ICON, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentColorStateList() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setColorStateList(ColorStateList.valueOf(Color.RED)) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.HOME_BUTTON_COLOR, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testSameColorStateList() { + // Create the ColorStateList by hand. ColorStateList.valueOf will inconsistently reuse + // objects, but this ColorStateList should never have reference equality with the default. + ColorStateList colorStateList = + new ColorStateList(new int[][] {new int[] {}}, new int[] {DEFAULT_TINT}); + Assert.assertNotEquals(mDefaultColorStateList, colorStateList); + + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setColorStateList(colorStateList) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentIsShowingUpdateBadgeDuringLastCapture() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setIsShowingUpdateBadgeDuringLastCapture(true) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.SHOWING_UPDATE_BADGE, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentIsPaintPreview() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setIsPaintPreview(true).build(); + Assert.assertEquals(ToolbarSnapshotDifference.PAINT_PREVIEW, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentProgress() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder().setProgress(0.2f).build(); + Assert.assertEquals(ToolbarSnapshotDifference.NONE, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testDifferentUnfocusedLocationBarLayoutWidth() { + PhoneCaptureStateToken otherPhoneCaptureStateToken = + new PhoneCustomTabCaptureStateTokenBuilder() + .setUnfocusedLocationBarLayoutWidth(100) + .build(); + Assert.assertEquals(ToolbarSnapshotDifference.LOCATION_BAR_WIDTH, + otherPhoneCaptureStateToken.getAnyDifference(mDefaultPhoneCaptureStateToken)); + } + + @Test + public void testIsValidVisibleTextPrefixHint() { + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint(null, null)); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", null)); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint(null, "foo")); + + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("", "")); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", "")); + + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", "fooo")); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", "foo/")); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", "o/")); + Assert.assertFalse(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo", "oo")); + + Assert.assertTrue(PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo.com", "foo")); + Assert.assertTrue( + PhoneCaptureStateToken.isValidVisibleTextPrefixHint("foo.com", "foo.com")); + } + + private class PhoneCustomTabCaptureStateTokenBuilder { + private @ColorInt int mTint = DEFAULT_TINT; + private int mTabCount = DEFAULT_TAB_COUNT; + private ButtonData mOptionalButtonData = DEFAULT_BUTTON_DATA; + private @VisualState int mVisualState = DEFAULT_VISUAL_STATE; + private String mUrlText = DEFAULT_URL_TEXT; + @Nullable + private CharSequence mVisibleTextPrefixHint = DEFAULT_URL_HINT_TEXT; + private @DrawableRes int mSecurityIcon = DEFAULT_SECURITY_ICON; + private ColorStateList mColorStateList = mDefaultColorStateList; + private boolean mIsShowingUpdateBadgeDuringLastCapture = + DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE; + private boolean mIsPaintPreview = DEFAULT_IS_PAINT_PREVIEW; + private float mProgress = DEFAULT_PROGRESS; + private int mUnfocusedLocationBarLayoutWidth = DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH; + + public PhoneCustomTabCaptureStateTokenBuilder setTint(@ColorInt int tint) { + mTint = tint; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setTabCount(int tabCount) { + mTabCount = tabCount; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setOptionalButtonData( + ButtonData optionalButtonData) { + mOptionalButtonData = optionalButtonData; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setVisualState(@VisualState int visualState) { + mVisualState = visualState; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setUrlText(String urlText) { + mUrlText = urlText; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setVisibleTextPrefixHint( + CharSequence visibleTextPrefixHint) { + mVisibleTextPrefixHint = visibleTextPrefixHint; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setSecurityIcon( + @DrawableRes int securityIcon) { + mSecurityIcon = securityIcon; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setColorStateList( + ColorStateList colorStateList) { + mColorStateList = colorStateList; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setIsShowingUpdateBadgeDuringLastCapture( + boolean isShowingUpdateBadgeDuringLastCapture) { + mIsShowingUpdateBadgeDuringLastCapture = isShowingUpdateBadgeDuringLastCapture; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setIsPaintPreview(boolean isPaintPreview) { + mIsPaintPreview = isPaintPreview; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setProgress(float progress) { + mProgress = progress; + return this; + } + + public PhoneCustomTabCaptureStateTokenBuilder setUnfocusedLocationBarLayoutWidth( + int unfocusedLocationBarLayoutWidth) { + mUnfocusedLocationBarLayoutWidth = unfocusedLocationBarLayoutWidth; + return this; + } + + public PhoneCaptureStateToken build() { + return new PhoneCaptureStateToken(mTint, mTabCount, mOptionalButtonData, mVisualState, + mUrlText, mVisibleTextPrefixHint, mSecurityIcon, mColorStateList, + mIsShowingUpdateBadgeDuringLastCapture, mIsPaintPreview, mProgress, + mUnfocusedLocationBarLayoutWidth); + } + } +}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java index 29b0b83d..671fa847 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
@@ -35,7 +35,6 @@ import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason; import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer.ToolbarViewResourceAdapter; import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer.ToolbarViewResourceAdapter.ToolbarInMotionStage; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; import org.chromium.chrome.test.util.browser.Features.DisableFeatures; import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.chrome.test.util.browser.Features.JUnitProcessor;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index 94a1065..e92f9cc 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -75,7 +75,6 @@ import org.chromium.chrome.browser.toolbar.optional_button.OptionalButtonCoordinator; import org.chromium.chrome.browser.toolbar.optional_button.OptionalButtonCoordinator.TransitionType; import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.chrome.browser.user_education.UserEducationHelper; @@ -238,7 +237,7 @@ /** Whether the toolbar has a pending request to call {@link triggerUrlFocusAnimation()}. */ private boolean mPendingTriggerUrlFocusRequest; - private ToolbarSnapshotState mToolbarSnapshotState; + private PhoneCaptureStateToken mPhoneCaptureStateToken; private ButtonData mButtonData; /** * Whether the tab switcher is currently showing and controlled by the start surface. For @@ -1568,9 +1567,9 @@ } else if (isInTabSwitcherMode() || mIsShowingStartSurfaceTabSwitcher) { return CaptureReadinessResult.notReady(TopToolbarBlockCaptureReason.TAB_SWITCHER_MODE); } else { - ToolbarSnapshotState newSnapshotState = generateToolbarSnapshotState(); + PhoneCaptureStateToken newSnapshotState = generateToolbarSnapshotState(); @ToolbarSnapshotDifference - int snapshotDifference = newSnapshotState.getAnyDifference(mToolbarSnapshotState); + int snapshotDifference = newSnapshotState.getAnyDifference(mPhoneCaptureStateToken); if (snapshotDifference == ToolbarSnapshotDifference.NONE) { return CaptureReadinessResult.notReady(TopToolbarBlockCaptureReason.SNAPSHOT_SAME); } else { @@ -1584,15 +1583,15 @@ if (forceTextureCapture) { // Only force a texture capture if the tint for the toolbar drawables is changing or // if the tab count has changed since the last texture capture. - if (mToolbarSnapshotState == null) { - mToolbarSnapshotState = generateToolbarSnapshotState(); + if (mPhoneCaptureStateToken == null) { + mPhoneCaptureStateToken = generateToolbarSnapshotState(); } - mForceTextureCapture = mToolbarSnapshotState.getTint() != getTint().getDefaultColor(); + mForceTextureCapture = mPhoneCaptureStateToken.getTint() != getTint().getDefaultColor(); if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) { mForceTextureCapture = mForceTextureCapture - || mToolbarSnapshotState.getTabCount() + || mPhoneCaptureStateToken.getTabCount() != mTabSwitcherAnimationTabStackDrawable.getTabCount(); } @@ -1603,7 +1602,7 @@ return false; } - private ToolbarSnapshotState generateToolbarSnapshotState() { + private PhoneCaptureStateToken generateToolbarSnapshotState() { UrlBarData urlBarData; int securityIconResource; if (ToolbarFeatures.shouldSuppressCaptures()) { @@ -1621,8 +1620,8 @@ String displayedUrlText = urlBarData.displayText.toString(); CharSequence prefixHint = mLocationBar.getOmniboxVisibleTextPrefixHint(); boolean isValidPrefixHint = - ToolbarSnapshotState.isValidVisibleTextPrefixHint(displayedUrlText, prefixHint); - return new ToolbarSnapshotState(getTint().getDefaultColor(), + PhoneCaptureStateToken.isValidVisibleTextPrefixHint(displayedUrlText, prefixHint); + return new PhoneCaptureStateToken(getTint().getDefaultColor(), mTabCountProvider.getTabCount(), mButtonData, mVisualState, displayedUrlText, isValidPrefixHint ? prefixHint : null, securityIconResource, ImageViewCompat.getImageTintList(mHomeButton), @@ -1737,7 +1736,7 @@ // When texture mode is turned off, we know a capture has just been completed. Update // our snapshot so that we can suppress correctly on the next // #isReadyForTextureCapture() call. - mToolbarSnapshotState = generateToolbarSnapshotState(); + mPhoneCaptureStateToken = generateToolbarSnapshotState(); } }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotDifference.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotDifference.java new file mode 100644 index 0000000..89e7db5 --- /dev/null +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotDifference.java
@@ -0,0 +1,44 @@ +// 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. + +package org.chromium.chrome.browser.toolbar.top; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Reasons that two toolbar snapshots are different. Contains a superset of differences and each + * toolbar instance will only be able to report a subset. Treat this list as append only and keep + * it in sync with ToolbarSnapshotDifference in enums.xml, as well as the proto in + * chrome_track_event.proto. + **/ +@IntDef({ToolbarSnapshotDifference.NONE, ToolbarSnapshotDifference.NULL, + ToolbarSnapshotDifference.TINT, ToolbarSnapshotDifference.TAB_COUNT, + ToolbarSnapshotDifference.OPTIONAL_BUTTON_DATA, ToolbarSnapshotDifference.VISUAL_STATE, + ToolbarSnapshotDifference.SECURITY_ICON, ToolbarSnapshotDifference.SHOWING_UPDATE_BADGE, + ToolbarSnapshotDifference.PAINT_PREVIEW, ToolbarSnapshotDifference.PROGRESS, + ToolbarSnapshotDifference.LOCATION_BAR_WIDTH, ToolbarSnapshotDifference.URL_TEXT, + ToolbarSnapshotDifference.HOME_BUTTON_COLOR, ToolbarSnapshotDifference.TITLE_TEXT, + ToolbarSnapshotDifference.CCT_ANIMATION, ToolbarSnapshotDifference.NUM_ENTRIES}) +@Retention(RetentionPolicy.SOURCE) +public @interface ToolbarSnapshotDifference { + int NONE = 0; + int NULL = 1; + int TINT = 2; + int TAB_COUNT = 3; + int OPTIONAL_BUTTON_DATA = 4; + int VISUAL_STATE = 5; + int SECURITY_ICON = 6; + int SHOWING_UPDATE_BADGE = 7; + int PAINT_PREVIEW = 8; + int PROGRESS = 9; + int LOCATION_BAR_WIDTH = 10; + int URL_TEXT = 11; + int HOME_BUTTON_COLOR = 12; + int TITLE_TEXT = 13; + int CCT_ANIMATION = 14; + int NUM_ENTRIES = 15; +} \ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java deleted file mode 100644 index 06f2bd1..0000000 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java +++ /dev/null
@@ -1,326 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.toolbar.top; - -import android.content.res.ColorStateList; -import android.graphics.Color; - -import androidx.annotation.ColorInt; -import androidx.annotation.DrawableRes; -import androidx.annotation.Nullable; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.browser.toolbar.ButtonData; -import org.chromium.chrome.browser.toolbar.ButtonDataImpl; -import org.chromium.chrome.browser.toolbar.top.ToolbarPhone.VisualState; -import org.chromium.chrome.browser.toolbar.top.ToolbarSnapshotState.ToolbarSnapshotDifference; - -/** Unit tests for {@link ToolbarSnapshotState}. */ -@RunWith(BaseRobolectricTestRunner.class) -public class ToolbarSnapshotStateTest { - private static final @ColorInt int DEFAULT_TINT = Color.TRANSPARENT; - private static final int DEFAULT_TAB_COUNT = 1; - private static final ButtonData DEFAULT_BUTTON_DATA = makeButtonDate(); - private static final @VisualState int DEFAULT_VISUAL_STATE = VisualState.NORMAL; - private static final String DEFAULT_URL_TEXT = "https://www.example.com/"; - private static final CharSequence DEFAULT_URL_HINT_TEXT = null; - private static final @DrawableRes int DEFAULT_SECURITY_ICON = 0; - private static final boolean DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE = false; - private static final boolean DEFAULT_IS_PAINT_PREVIEW = false; - private static final float DEFAULT_PROGRESS = 0.1f; - private static final int DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH = 2; - - // Not static/final because they're initialized in #before(). Apparently ColorStateList.valueOf - // calls into Android native code, and cannot be done too early. - private ColorStateList mDefaultColorStateList; - private ToolbarSnapshotState mDefaultToolbarSnapshotState; - - private static ButtonData makeButtonDate() { - // Uses default equals impl, reference quality, to compare. Values do not matter. - return new ButtonDataImpl(false, null, null, "", false, null, false, 0); - } - - @Before - public void before() { - mDefaultColorStateList = ColorStateList.valueOf(DEFAULT_TINT); - mDefaultToolbarSnapshotState = new ToolbarSnapshotStateBuilder().build(); - } - - @Test - public void testSameSnapshots() { - ToolbarSnapshotState otherToolbarSnapshotState = new ToolbarSnapshotStateBuilder().build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentTint() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setTint(Color.RED).build(); - Assert.assertEquals(ToolbarSnapshotDifference.TINT, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentTabCount() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setTabCount(2).build(); - Assert.assertEquals(ToolbarSnapshotDifference.TAB_COUNT, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentOptionalButtonData() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setOptionalButtonData(makeButtonDate()).build(); - Assert.assertEquals(ToolbarSnapshotDifference.OPTIONAL_BUTTON_DATA, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentVisualState() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setVisualState(VisualState.INCOGNITO).build(); - Assert.assertEquals(ToolbarSnapshotDifference.VISUAL_STATE, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentUrlText() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setUrlText("https://www.other.com/").build(); - Assert.assertEquals(ToolbarSnapshotDifference.URL_TEXT, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentUrlText_SameHintText() { - ToolbarSnapshotState initialToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) - .build(); - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setUrlText(DEFAULT_URL_TEXT + "additional/paths/") - .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) - .build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - initialToolbarSnapshotState.getAnyDifference(otherToolbarSnapshotState)); - } - - @Test - public void testSameUrlText_DifferentHintText() { - ToolbarSnapshotState initialToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 2)) - .build(); - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 3)) - .build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - initialToolbarSnapshotState.getAnyDifference(otherToolbarSnapshotState)); - } - - @Test - public void testSameUrlText_BothNullHintText() { - ToolbarSnapshotState initialToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setVisibleTextPrefixHint(null).build(); - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setVisibleTextPrefixHint(null).build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - initialToolbarSnapshotState.getAnyDifference(otherToolbarSnapshotState)); - } - - @Test - public void testSameUrlText_NullHintText() { - ToolbarSnapshotState initialToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setVisibleTextPrefixHint(null).build(); - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setVisibleTextPrefixHint(DEFAULT_URL_TEXT) - .build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - initialToolbarSnapshotState.getAnyDifference(otherToolbarSnapshotState)); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - otherToolbarSnapshotState.getAnyDifference(initialToolbarSnapshotState)); - } - - @Test - public void testDifferentSecurityIcon() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setSecurityIcon(-1).build(); - Assert.assertEquals(ToolbarSnapshotDifference.SECURITY_ICON, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentColorStateList() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setColorStateList(ColorStateList.valueOf(Color.RED)) - .build(); - Assert.assertEquals(ToolbarSnapshotDifference.HOME_BUTTON_COLOR, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testSameColorStateList() { - // Create the ColorStateList by hand. ColorStateList.valueOf will inconsistently reuse - // objects, but this ColorStateList should never have reference equality with the default. - ColorStateList colorStateList = - new ColorStateList(new int[][] {new int[] {}}, new int[] {DEFAULT_TINT}); - Assert.assertNotEquals(mDefaultColorStateList, colorStateList); - - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setColorStateList(colorStateList).build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentIsShowingUpdateBadgeDuringLastCapture() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder() - .setIsShowingUpdateBadgeDuringLastCapture(true) - .build(); - Assert.assertEquals(ToolbarSnapshotDifference.SHOWING_UPDATE_BADGE, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentIsPaintPreview() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setIsPaintPreview(true).build(); - Assert.assertEquals(ToolbarSnapshotDifference.PAINT_PREVIEW, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentProgress() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setProgress(0.2f).build(); - Assert.assertEquals(ToolbarSnapshotDifference.NONE, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testDifferentUnfocusedLocationBarLayoutWidth() { - ToolbarSnapshotState otherToolbarSnapshotState = - new ToolbarSnapshotStateBuilder().setUnfocusedLocationBarLayoutWidth(100).build(); - Assert.assertEquals(ToolbarSnapshotDifference.LOCATION_BAR_WIDTH, - otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState)); - } - - @Test - public void testIsValidVisibleTextPrefixHint() { - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint(null, null)); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", null)); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint(null, "foo")); - - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("", "")); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", "")); - - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", "fooo")); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", "foo/")); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", "o/")); - Assert.assertFalse(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo", "oo")); - - Assert.assertTrue(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo.com", "foo")); - Assert.assertTrue(ToolbarSnapshotState.isValidVisibleTextPrefixHint("foo.com", "foo.com")); - } - - private class ToolbarSnapshotStateBuilder { - private @ColorInt int mTint = DEFAULT_TINT; - private int mTabCount = DEFAULT_TAB_COUNT; - private ButtonData mOptionalButtonData = DEFAULT_BUTTON_DATA; - private @VisualState int mVisualState = DEFAULT_VISUAL_STATE; - private String mUrlText = DEFAULT_URL_TEXT; - @Nullable - private CharSequence mVisibleTextPrefixHint = DEFAULT_URL_HINT_TEXT; - private @DrawableRes int mSecurityIcon = DEFAULT_SECURITY_ICON; - private ColorStateList mColorStateList = mDefaultColorStateList; - private boolean mIsShowingUpdateBadgeDuringLastCapture = - DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE; - private boolean mIsPaintPreview = DEFAULT_IS_PAINT_PREVIEW; - private float mProgress = DEFAULT_PROGRESS; - private int mUnfocusedLocationBarLayoutWidth = DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH; - - public ToolbarSnapshotStateBuilder setTint(@ColorInt int tint) { - mTint = tint; - return this; - } - - public ToolbarSnapshotStateBuilder setTabCount(int tabCount) { - mTabCount = tabCount; - return this; - } - - public ToolbarSnapshotStateBuilder setOptionalButtonData(ButtonData optionalButtonData) { - mOptionalButtonData = optionalButtonData; - return this; - } - - public ToolbarSnapshotStateBuilder setVisualState(@VisualState int visualState) { - mVisualState = visualState; - return this; - } - - public ToolbarSnapshotStateBuilder setUrlText(String urlText) { - mUrlText = urlText; - return this; - } - - public ToolbarSnapshotStateBuilder setVisibleTextPrefixHint( - CharSequence visibleTextPrefixHint) { - mVisibleTextPrefixHint = visibleTextPrefixHint; - return this; - } - - public ToolbarSnapshotStateBuilder setSecurityIcon(@DrawableRes int securityIcon) { - mSecurityIcon = securityIcon; - return this; - } - - public ToolbarSnapshotStateBuilder setColorStateList(ColorStateList colorStateList) { - mColorStateList = colorStateList; - return this; - } - - public ToolbarSnapshotStateBuilder setIsShowingUpdateBadgeDuringLastCapture( - boolean isShowingUpdateBadgeDuringLastCapture) { - mIsShowingUpdateBadgeDuringLastCapture = isShowingUpdateBadgeDuringLastCapture; - return this; - } - - public ToolbarSnapshotStateBuilder setIsPaintPreview(boolean isPaintPreview) { - mIsPaintPreview = isPaintPreview; - return this; - } - - public ToolbarSnapshotStateBuilder setProgress(float progress) { - mProgress = progress; - return this; - } - - public ToolbarSnapshotStateBuilder setUnfocusedLocationBarLayoutWidth( - int unfocusedLocationBarLayoutWidth) { - mUnfocusedLocationBarLayoutWidth = unfocusedLocationBarLayoutWidth; - return this; - } - - public ToolbarSnapshotState build() { - return new ToolbarSnapshotState(mTint, mTabCount, mOptionalButtonData, mVisualState, - mUrlText, mVisibleTextPrefixHint, mSecurityIcon, mColorStateList, - mIsShowingUpdateBadgeDuringLastCapture, mIsPaintPreview, mProgress, - mUnfocusedLocationBarLayoutWidth); - } - } -}
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc index d4627ae3..b642db71 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -294,7 +294,10 @@ return; } - const Suggestion& suggestion = suggestions_[index]; + // Use a copy instead of a reference here. Under certain circumstances, + // `DidAcceptSuggestion()` can call `SetSuggestions()` and invalidate the + // reference. + Suggestion suggestion = suggestions_[index]; #if BUILDFLAG(IS_ANDROID) auto mf_controller = ManualFillingController::GetOrCreate(web_contents_); // Accepting a suggestion should hide all suggestions. To prevent them from
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index 7f1313e..f91c359d 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1066,9 +1066,10 @@ return; // Navigation commands - command_updater_.UpdateCommandEnabled(IDC_RELOAD, true); - command_updater_.UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, true); - command_updater_.UpdateCommandEnabled(IDC_RELOAD_CLEARING_CACHE, true); + const bool can_reload = CanReload(browser_); + command_updater_.UpdateCommandEnabled(IDC_RELOAD, can_reload); + command_updater_.UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, can_reload); + command_updater_.UpdateCommandEnabled(IDC_RELOAD_CLEARING_CACHE, can_reload); // Window management commands command_updater_.UpdateCommandEnabled(IDC_CLOSE_WINDOW, true); @@ -1326,11 +1327,10 @@ // Navigation commands command_updater_.UpdateCommandEnabled(IDC_BACK, CanGoBack(browser_)); command_updater_.UpdateCommandEnabled(IDC_FORWARD, CanGoForward(browser_)); - command_updater_.UpdateCommandEnabled(IDC_RELOAD, CanReload(browser_)); - command_updater_.UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, - CanReload(browser_)); - command_updater_.UpdateCommandEnabled(IDC_RELOAD_CLEARING_CACHE, - CanReload(browser_)); + const bool can_reload = CanReload(browser_); + command_updater_.UpdateCommandEnabled(IDC_RELOAD, can_reload); + command_updater_.UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, can_reload); + command_updater_.UpdateCommandEnabled(IDC_RELOAD_CLEARING_CACHE, can_reload); // Window management commands bool is_app = browser_->is_type_app() || browser_->is_type_app_popup();
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index 0da5058..4176459 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc
@@ -630,7 +630,8 @@ } bool CanReload(const Browser* browser) { - return browser && !browser->is_type_devtools(); + return browser && !browser->is_type_devtools() && + !browser->is_type_picture_in_picture(); } void Home(Browser* browser, WindowOpenDisposition disposition) {
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc index c5f51396..c94b77e3f 100644 --- a/chrome/browser/ui/chrome_pages.cc +++ b/chrome/browser/ui/chrome_pages.cc
@@ -38,6 +38,7 @@ #include "chrome/common/url_constants.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_node.h" +#include "components/password_manager/core/common/password_manager_features.h" #include "components/privacy_sandbox/privacy_sandbox_features.h" #include "components/safe_browsing/core/common/safe_browsing_settings_metrics.h" #include "components/signin/public/base/consent_level.h" @@ -433,12 +434,24 @@ void ShowPasswordManager(Browser* browser) { base::RecordAction(UserMetricsAction("Options_ShowPasswordManager")); - ShowSettingsSubPage(browser, kPasswordManagerSubPage); + if (base::FeatureList::IsEnabled( + password_manager::features::kPasswordManagerRedesign)) { + ShowSingletonTabIgnorePathOverwriteNTP(browser, + GURL(kChromeUIPasswordManagerURL)); + } else { + ShowSettingsSubPage(browser, kPasswordManagerSubPage); + } } void ShowPasswordCheck(Browser* browser) { base::RecordAction(UserMetricsAction("Options_ShowPasswordCheck")); - ShowSettingsSubPage(browser, kPasswordCheckSubPage); + if (base::FeatureList::IsEnabled( + password_manager::features::kPasswordManagerRedesign)) { + ShowSingletonTabIgnorePathOverwriteNTP( + browser, GURL(kChromeUIPasswordManagerCheckupURL)); + } else { + ShowSettingsSubPage(browser, kPasswordCheckSubPage); + } } void ShowSafeBrowsingEnhancedProtection(Browser* browser) {
diff --git a/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc b/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc index d1212b4..fcf744a 100644 --- a/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc +++ b/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc
@@ -20,6 +20,8 @@ #if BUILDFLAG(IS_ANDROID) #include "chrome/browser/password_manager/android/password_manager_launcher_android.h" +#else +#include "chrome/browser/password_manager/chrome_password_manager_client.h" #endif namespace { @@ -28,10 +30,6 @@ using content::NavigationThrottle; using content::WebContents; -#if !BUILDFLAG(IS_ANDROID) -constexpr char kChromeUIPasswordsURL[] = "chrome:/settings/passwords"; -#endif - bool IsTriggeredOnGoogleOwnedUI(NavigationHandle* handle) { // Only cover cases when the user clicked on a link. if (!ui::PageTransitionCoreTypeIs(handle->GetPageTransition(), @@ -43,10 +41,9 @@ return false; url::Origin origin = handle->GetInitiatorOrigin().value_or(url::Origin()); - if (origin != url::Origin::Create(GURL(password_manager::kReferrerURL)) && - origin != - url::Origin::Create(GURL(password_manager::kTestingReferrerURL))) + if (origin != url::Origin::Create(GURL(password_manager::kReferrerURL))) { return false; + } return true; } @@ -85,24 +82,16 @@ web_contents, password_manager::ManagePasswordsReferrer::kPasswordsGoogleWebsite); #else - content::OpenURLParams params = - content::OpenURLParams::FromNavigationHandle(navigation_handle()); - params.url = GURL(kChromeUIPasswordsURL); - params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT; - - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce( - [](base::WeakPtr<content::WebContents> web_contents, - const content::OpenURLParams& params) { - if (!web_contents) - return; - web_contents->OpenURL(params); - }, - web_contents->GetWeakPtr(), std::move(params))); - UMA_HISTOGRAM_ENUMERATION( - "PasswordManager.ManagePasswordsReferrer", - password_manager::ManagePasswordsReferrer::kPasswordsGoogleWebsite); + ChromePasswordManagerClient::FromWebContents(web_contents) + ->NavigateToManagePasswordsPage( + password_manager::ManagePasswordsReferrer::kPasswordsGoogleWebsite); #endif + // Schedule a task to close current tab since on Android Password Manager is + // shown in the Native UI, and on desktop NavigateToManagePasswordsPage() + // handles creation or redirection to Passwords Manager tab. + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, + base::BindOnce(&content::WebContents::Close, web_contents->GetWeakPtr())); return NavigationThrottle::CANCEL_AND_IGNORE; }
diff --git a/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc b/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc index 2d40fabb..d18e625 100644 --- a/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc +++ b/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc
@@ -97,13 +97,3 @@ url::Origin::Create(GURL(password_manager::kReferrerURL)), })); } - -TEST_F(PasswordManagerNavigationThrottleTest, - CreatesNavigationThrottleForTestingWebsite) { - EXPECT_TRUE(CreateNavigationThrottle({ - .url = GURL(password_manager::kManageMyPasswordsURL), - .page_transition = ui::PAGE_TRANSITION_LINK, - .initiator_origin = - url::Origin::Create(GURL(password_manager::kTestingReferrerURL)), - })); -}
diff --git a/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc b/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc index 292d513..9068a9f 100644 --- a/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc +++ b/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc
@@ -58,7 +58,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; void SetUpOnMainThread() override {
diff --git a/chrome/browser/ui/views/autofill/payments/credit_card_access_manager_browsertest.cc b/chrome/browser/ui/views/autofill/payments/credit_card_access_manager_browsertest.cc index 8f2b370..7590d79 100644 --- a/chrome/browser/ui/views/autofill/payments/credit_card_access_manager_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/credit_card_access_manager_browsertest.cc
@@ -39,7 +39,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; void SetUpOnMainThread() override {
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc index 251ece1..822f298 100644 --- a/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc +++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_uitest.cc
@@ -174,7 +174,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; // Various events that can be waited on by the DialogEventWaiter.
diff --git a/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.h b/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.h index f6bda06..661833f 100644 --- a/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.h +++ b/chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.h
@@ -51,7 +51,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; // Various events that can be waited on by the DialogEventWaiter.
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc index 2703ece..d2e1fc8 100644 --- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -160,7 +160,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; // Various events that can be waited on by the DialogEventWaiter.
diff --git a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view_uitest.cc b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view_uitest.cc index 3ec1c41..d60da9e 100644 --- a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view_uitest.cc +++ b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view_uitest.cc
@@ -76,7 +76,7 @@ private: TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {AutofillManagerEvent::kFormsSeen}}; }; // Various events that can be waited on by the DialogEventWaiter.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc index 67d553c..26d96dc 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -52,7 +52,7 @@ private: autofill::TestAutofillManagerWaiter forms_seen_waiter_{ *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + {autofill::AutofillManagerEvent::kFormsSeen}}; }; } // namespace
diff --git a/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc b/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc index 1aff5bd..77183cc 100644 --- a/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc +++ b/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc
@@ -204,15 +204,22 @@ if (browser->tab_strip_model()->GetActiveWebContents() != contents) return base::DoNothing(); + // `GetExtensionsToolbarContainer` may return `nullptr`, for instance in + // extension popup windows. + auto* extensions_toolbar_container = + BrowserView::GetBrowserViewForBrowser(browser) + ->toolbar_button_provider() + ->GetExtensionsToolbarContainer(); + if (!extensions_toolbar_container) { + return base::DoNothing(); + } + auto bubble = std::make_unique<ChooserBubbleUiViewDelegate>( browser, contents, std::move(controller)); base::OnceClosure close_closure = bubble->MakeCloseClosure(); - views::Widget* widget = - views::BubbleDialogDelegateView::CreateBubble(std::move(bubble)); - BrowserView::GetBrowserViewForBrowser(browser) - ->toolbar_button_provider() - ->GetExtensionsToolbarContainer() - ->ShowWidgetForExtension(widget, extension->id()); + extensions_toolbar_container->ShowWidgetForExtension( + views::BubbleDialogDelegateView::CreateBubble(std::move(bubble)), + extension->id()); return close_closure; } #endif // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/ui/views/tabs/compound_tab_container.cc b/chrome/browser/ui/views/tabs/compound_tab_container.cc index 3fe533d..bc42043 100644 --- a/chrome/browser/ui/views/tabs/compound_tab_container.cc +++ b/chrome/browser/ui/views/tabs/compound_tab_container.cc
@@ -249,7 +249,14 @@ bounds_animator_.SetAnimationDuration(base::TimeDelta()); } -CompoundTabContainer::~CompoundTabContainer() = default; +CompoundTabContainer::~CompoundTabContainer() { + // Tabs call back up to the TabStrip during animation end and destruction. + // Ensure that happens now so we aren't in a half-destructed state when they + // do so. + CancelAnimation(); + RemoveChildViewT(base::to_address(pinned_tab_container_)); + RemoveChildViewT(base::to_address(unpinned_tab_container_)); +} void CompoundTabContainer::SetAvailableWidthCallback( base::RepeatingCallback<int()> available_width_callback) { @@ -547,12 +554,21 @@ pinned_tab_container_->AnimateToIdealBounds(); unpinned_tab_container_->AnimateToIdealBounds(); + // Animate the pinning or unpinning tabs too. for (views::View* child : children()) { Tab* tab = views::AsViewClass<Tab>(child); if (!tab) continue; - AnimateTabTo(tab, GetIdealBounds(GetModelIndexOf(tab).value())); + const absl::optional<int> model_index = GetModelIndexOf(tab); + // The tab may have been closed during a pin/unpin animation, in which case + // it a) has no model index and b) is already animating to its correct + // bounds because that will have been updated in `UpdateAnimationTarget()`. + if (!model_index.has_value()) { + continue; + } + + AnimateTabTo(tab, GetIdealBounds(model_index.value())); } } @@ -874,8 +890,14 @@ int to_model_index) { // If the tab at `from_model_index` is already being transferred, complete // all pending transfers before we embark upon this one to avoid conflicts. - if (bounds_animator_.IsAnimating(GetTabAtModelIndex(from_model_index))) - CompleteAnimationAndLayout(); + if (bounds_animator_.IsAnimating(GetTabAtModelIndex(from_model_index))) { + // We are out of sync with the model right now (because we're handling a + // model update), so we need to be careful here. We can complete our + // directly managed animations, but we can't ask the sub-containers to do + // the same, as their ideal bounds calculations assume the model and + // viewmodel are in sync. + bounds_animator_.Complete(); + } const bool prev_pinned = from_model_index < NumPinnedTabs(); const bool next_pinned = !prev_pinned;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc index 48772fed..c119117 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -10,6 +10,7 @@ #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" +#include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/timer/timer.h" @@ -184,12 +185,21 @@ tab_strip_->GetDragContext()->StoppedDragging(views); } - std::vector<views::View*> GetChildViews() { - // The first (and only) View child of TabStrip is its TabContainer, which - // contains the tabs and group views. - // TODO(1116121): Move tests that test view/focus order to test - // TabContainer directly, once that functionality has been moved there. - return tab_strip_->children()[0]->children(); + size_t NumTabSlotViews() { + base::RepeatingCallback<size_t(views::View*)> num_tab_slot_views = + base::BindLambdaForTesting([&num_tab_slot_views](views::View* parent) { + if (views::IsViewClass<TabSlotView>(parent)) { + return size_t(1); + } else { + size_t sum = 0; + for (views::View* child : parent->children()) { + sum += num_tab_slot_views.Run(child); + } + return sum; + } + }); + + return num_tab_slot_views.Run(tab_strip_); } std::vector<TabGroupViews*> ListGroupViews() const { @@ -294,18 +304,18 @@ TestTabStripObserver observer(tab_strip_); controller_->AddTab(0, TabActive::kInactive); controller_->AddTab(1, TabActive::kInactive); - const size_t num_children = GetChildViews().size(); + const size_t num_children = NumTabSlotViews(); EXPECT_EQ(2, tab_strip_->GetTabCount()); controller_->RemoveTab(0); EXPECT_EQ(0, observer.last_tab_removed()); // When removing a tab the tabcount should immediately decrement. EXPECT_EQ(1, tab_strip_->GetTabCount()); // But the number of views should remain the same (it's animatining closed). - EXPECT_EQ(num_children, GetChildViews().size()); + EXPECT_EQ(num_children, NumTabSlotViews()); CompleteAnimationAndLayout(); - EXPECT_EQ(num_children - 1, GetChildViews().size()); + EXPECT_EQ(num_children - 1, NumTabSlotViews()); // Remove the last tab to make sure things are cleaned up correctly when // the TabStrip is destroyed and an animation is ongoing.
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc index df7bbdc3..3eea8f49 100644 --- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc +++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -329,7 +329,7 @@ IDS_PASSWORD_MANAGER_IPH_BODY_SAVE_TO_ACCOUNT) .SetBubbleTitleText(IDS_PASSWORD_MANAGER_IPH_TITLE_SAVE_TO_ACCOUNT) .SetInAnyContext(true) - .SetBubbleArrow(HelpBubbleArrow::kBottomLeft) + .SetBubbleArrow(HelpBubbleArrow::kBottomRight) .SetBubbleIcon(&vector_icons::kCelebrationIcon))); // kIPHBatterySaverModeFeature:
diff --git a/chrome/browser/updater/scheduler_mac.cc b/chrome/browser/updater/scheduler_mac.cc index 3fcba7b..a5e0d51 100644 --- a/chrome/browser/updater/scheduler_mac.cc +++ b/chrome/browser/updater/scheduler_mac.cc
@@ -75,6 +75,7 @@ } } } + std::move(callback).Run(); } }
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc index 88d02e23c..7b00a6a 100644 --- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc +++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -31,6 +31,7 @@ #include "device/fido/discoverable_credential_metadata.h" #include "device/fido/features.h" #include "device/fido/fido_authenticator.h" +#include "device/fido/fido_transport_protocol.h" #include "device/fido/fido_types.h" #include "device/fido/pin.h" #include "device/fido/public_key_credential_user_entity.h" @@ -1071,7 +1072,9 @@ const bool is_get_assertion = transport_availability_.request_type == device::FidoRequestType::kGetAssertion; const bool is_passkey_request = - ((is_get_assertion && transport_availability_.has_empty_allow_list) || + ((is_get_assertion && + (transport_availability_.has_empty_allow_list || + transport_availability_.is_only_hybrid_or_internal)) || (!is_get_assertion && resident_key_requirement() != device::ResidentKeyRequirement::kDiscouraged)); // priority_transport contains the transport that should be activated
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc index 4be65b6..b5099ae 100644 --- a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc +++ b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
@@ -104,6 +104,7 @@ kOneRecognizedCred, kTwoRecognizedCreds, kEmptyAllowList, + kOnlyHybridOrInternal, kHasWinNativeAuthenticator, kHasCableV1Extension, kHasCableV2Extension, @@ -125,6 +126,8 @@ return "kTwoRecognizedCreds"; case TransportAvailabilityParam::kEmptyAllowList: return "kEmptyAllowList"; + case TransportAvailabilityParam::kOnlyHybridOrInternal: + return "kOnlyHybridOrInternal"; case TransportAvailabilityParam::kHasWinNativeAuthenticator: return "kHasWinNativeAuthenticator"; case TransportAvailabilityParam::kHasCableV1Extension: @@ -184,6 +187,8 @@ const auto one_cred = TransportAvailabilityParam::kOneRecognizedCred; const auto two_cred = TransportAvailabilityParam::kTwoRecognizedCreds; const auto empty_al = TransportAvailabilityParam::kEmptyAllowList; + const auto only_hybrid_or_internal = + TransportAvailabilityParam::kOnlyHybridOrInternal; const auto rk = TransportAvailabilityParam::kRequireResidentKey; const auto c_ui = TransportAvailabilityParam::kIsConditionalUI; using t = AuthenticatorRequestDialogModel::Mechanism::Transport; @@ -306,7 +311,8 @@ // caBLE isn't an option. {ga, {cable}, {has_winapi, empty_al}, {}, {winapi, add}, mss}, {ga, {}, {has_winapi, empty_al}, {}, {winapi}, plat_ui}, - // But with a non-empty allow list, always jump to Windows UI. + // But with a non-empty allow list containing non phone credentials, + // always jump to Windows UI. {ga, {cable}, {has_winapi}, {}, {winapi, add}, plat_ui}, {ga, {}, {has_winapi}, {}, {winapi}, plat_ui}, // Except when the request is legacy cable. @@ -358,6 +364,14 @@ {t(internal), t(usb), add}, qr, {qr1st}}, + // And if the allow list only contains phones. + {ga, + {internal, cable}, + {only_hybrid_or_internal}, + {}, + {t(internal), add}, + qr, + {qr1st}}, // Unless there is a phone paired already. {ga, {usb, internal, cable}, @@ -382,7 +396,8 @@ {t(usb), add}, qr, {qr1st}}, - // If there is an allow-list, go to transport selection instead. + // If there is an allow-list containing USB, go to transport selection + // instead. {ga, {usb, internal, cable}, {}, @@ -456,6 +471,8 @@ } transports_info.has_empty_allow_list = base::Contains( test.params, TransportAvailabilityParam::kEmptyAllowList); + transports_info.is_only_hybrid_or_internal = base::Contains( + test.params, TransportAvailabilityParam::kOnlyHybridOrInternal); if (base::Contains( test.params,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index a4f7302..53eff49d 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1676635198-985656c4c8f8c864219be56acd9ad96e64158cd6.profdata +chrome-linux-main-1676656701-f9b954d106c330345c1769e674295f6dc682efea.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index cc1df68..8c76b01 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1676635198-d9c070fc3ac187a6fc78506298f02bcc703436f4.profdata +chrome-mac-arm-main-1676656701-c19bf79ccd8178da0f09f5700b478205029b9eef.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index d9c32369..fb34a6a 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1676635198-68379fcc089eca33d09943f7c369329ef51cabe2.profdata +chrome-mac-main-1676656701-de66fa95d4ff92418365c2f65fb24b5355e5cd53.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 93af1ab9..85da5e9a 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1676635198-4817542e58ede2d7868e5eebef98fbe04a877e39.profdata +chrome-win32-main-1676656701-7f1c3354d4d278e7adf75844663fe940142f9023.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 15e0702..c4237a0 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1676635198-8eb87c282dc69b99dd61c2aea7a5fb13608e22f6.profdata +chrome-win64-main-1676656701-0009794feb607261fb99123d2d37723dcc62a7ba.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index e9a8e23..e76377ce 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -3371,6 +3371,11 @@ // If not set, Chrome will choose the root store based on experiments. const char kChromeRootStoreEnabled[] = "chrome_root_store_enabled"; #endif +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +const char kEnforceLocalAnchorConstraintsEnabled[] = + "enforce_local_anchor_constraints_enabled"; +#endif const char kSharingVapidKey[] = "sharing.vapid_key"; const char kSharingFCMRegistration[] = "sharing.fcm_registration";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 89f33d4f..77a3cfb 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -1163,6 +1163,11 @@ #if BUILDFLAG(CHROME_ROOT_STORE_POLICY_SUPPORTED) extern const char kChromeRootStoreEnabled[]; #endif +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +// TODO(https://crbug.com/1406103): delete this after a few milestones. +extern const char kEnforceLocalAnchorConstraintsEnabled[]; +#endif extern const char kSharingVapidKey[]; extern const char kSharingFCMRegistration[];
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc index f6ddbcbd..5dffc00 100644 --- a/chrome/common/webui_url_constants.cc +++ b/chrome/common/webui_url_constants.cc
@@ -153,6 +153,8 @@ const char kChromeUIPasswordManagerInternalsHost[] = "password-manager-internals"; const char kChromeUIPasswordManagerURL[] = "chrome://password-manager"; +const char kChromeUIPasswordManagerCheckupURL[] = + "chrome://password-manager/checkup?start=true"; const char kChromeUIPerformanceSettingsURL[] = "chrome://settings/performance"; const char kChromeUIPolicyHost[] = "policy"; const char kChromeUIPolicyURL[] = "chrome://policy/";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h index 59b58a2..9aa5180d 100644 --- a/chrome/common/webui_url_constants.h +++ b/chrome/common/webui_url_constants.h
@@ -157,6 +157,7 @@ #endif extern const char kChromeUIPasswordManagerInternalsHost[]; extern const char kChromeUIPasswordManagerURL[]; +extern const char kChromeUIPasswordManagerCheckupURL[]; extern const char kChromeUIPerformanceSettingsURL[]; extern const char kChromeUIPolicyHost[]; extern const char kChromeUIPolicyURL[];
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc index 9cade91..e78c7d6c 100644 --- a/chrome/test/base/chrome_test_launcher.cc +++ b/chrome/test/base/chrome_test_launcher.cc
@@ -77,13 +77,6 @@ #include "testing/gtest/include/gtest/gtest.h" #endif -#if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/files/file_enumerator.h" -#include "base/files/file_util.h" -#include "base/strings/string_number_conversions.h" -#include "content/public/test/browser_test_switches.h" -#endif - // static int ChromeTestSuiteRunner::RunTestSuiteInternal(ChromeTestSuite* test_suite) { // Browser tests are expected not to tear-down various globals. @@ -240,35 +233,12 @@ if (IsUserAnAdmin()) firewall_rules_ = std::make_unique<ScopedFirewallRules>(); #endif - -#if BUILDFLAG(IS_CHROMEOS_LACROS) - CHECK(ash_processes_dir_.CreateUniqueTempDir()); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - content::test::switches::kAshProcessesDirPath, - ash_processes_dir_.GetPath()); -#endif } void ChromeTestLauncherDelegate::OnDoneRunningTests() { #if BUILDFLAG(IS_WIN) firewall_rules_.reset(); #endif - -// In test runners, they may create ash processes outlive the runner process. -// So we need to terminate those ash processes in the test launcher. -#if BUILDFLAG(IS_CHROMEOS_LACROS) - base::FileEnumerator files(ash_processes_dir_.GetPath(), false, - base::FileEnumerator::FILES); - for (base::FilePath name = files.Next(); !name.empty(); name = files.Next()) { - std::string str_pid; - CHECK(base::ReadFileToString(name, &str_pid)); - int pid; - CHECK(base::StringToInt(str_pid, &pid)) << "Cannot read pid. The content " - << "of the file is: " << str_pid; - base::Process process = base::Process::Open(pid); - process.Terminate(0, false); - } -#endif } int LaunchChromeTests(size_t parallel_jobs,
diff --git a/chrome/test/base/chrome_test_launcher.h b/chrome/test/base/chrome_test_launcher.h index ccbcf2e..eb54742 100644 --- a/chrome/test/base/chrome_test_launcher.h +++ b/chrome/test/base/chrome_test_launcher.h
@@ -18,10 +18,6 @@ #include "chrome/app/chrome_main_delegate.h" #endif -#if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/files/scoped_temp_dir.h" -#endif - class ChromeTestSuite; // Allows a test suite to override the TestSuite class used. By default it is an @@ -90,10 +86,6 @@ #endif raw_ptr<ChromeTestSuiteRunner> runner_; - -#if BUILDFLAG(IS_CHROMEOS_LACROS) - base::ScopedTempDir ash_processes_dir_; -#endif }; // Launches Chrome browser tests. |parallel_jobs| is number of test jobs to be
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/manifest.json b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/manifest.json new file mode 100644 index 0000000..e68bb7f --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/manifest.json
@@ -0,0 +1,18 @@ +{ + "name": "chrome.ttsEngine", + "version": "0.1", + "manifest_version": 2, + "description": "browser test for chrome.ttsEngine API", + "background": { + "page": "test.html" + }, + "tts_engine": { + "voices": [{ + "voice_name": "Alice", + "lang": "en-US", + "gender": "female", + "event_types": ["start", "end"] + }] + }, + "permissions": ["tts", "ttsEngine"] +}
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.html b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.html new file mode 100644 index 0000000..f39ab08a --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.html
@@ -0,0 +1,6 @@ +<!-- + * Copyright 2023 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.js b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.js new file mode 100644 index 0000000..562368c --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_before_speak_lacros_engine/test.js
@@ -0,0 +1,60 @@ +// 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. + +// TTS api test running from Lacros with Ash. +// build/lacros/test_runner.py test +// {path_to_lacros_build}/lacros_chrome_browsertests_run_in_series +// --gtest_filter=LacrosTtsApiTest.PauseBeforeSpeakWithLacrosTtsEngine +// --ash-chrome-path {path_to_ash_build}/test_ash_chrome + +chrome.test.runTests([ + async function testPauseBeforeSpeakWithLacrosTtsEngine() { + const utteranceText = "Hello"; + + // Register listeners for speech functions, enabling this extension + // to be a TTS engine. + let speakListener = function (utterance, options, sendTtsEvent) { + chrome.test.assertNoLastError(); + chrome.test.assertEq(utteranceText, utterance); + sendTtsEvent({ + 'type': 'end', + 'charIndex': utterance.length + }); + }; + chrome.ttsEngine.onSpeak.addListener(speakListener); + + let stopListener = function () {}; + chrome.ttsEngine.onStop.addListener(stopListener); + + // Pause speech synthesis before calling tts.speak. + chrome.tts.pause(); + + // Request to speak an utterance with Lacros speech engine. The utterance + // should be queued by Ash's TtsController. + let ttsSpeakPromise = new Promise((resolve) => { + chrome.tts.speak( + utteranceText, { + 'voiceName': 'Alice', + 'onEvent': function (event) { + if (event.type == 'end') { + resolve(); + } + } + }, + function () { + chrome.test.assertNoLastError(); + }); + }); + + // Resume speech synthesis. This should enable the Ash TtsController to send + // the queued utterance to Lacros speech engine. + chrome.tts.resume(); + + await ttsSpeakPromise.then(() => { + chrome.ttsEngine.onSpeak.removeListener(speakListener); + chrome.ttsEngine.onStop.removeListener(stopListener); + chrome.test.succeed(); + }); + } +]);
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/manifest.json b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/manifest.json new file mode 100644 index 0000000..e68bb7f --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/manifest.json
@@ -0,0 +1,18 @@ +{ + "name": "chrome.ttsEngine", + "version": "0.1", + "manifest_version": 2, + "description": "browser test for chrome.ttsEngine API", + "background": { + "page": "test.html" + }, + "tts_engine": { + "voices": [{ + "voice_name": "Alice", + "lang": "en-US", + "gender": "female", + "event_types": ["start", "end"] + }] + }, + "permissions": ["tts", "ttsEngine"] +}
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.html b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.html new file mode 100644 index 0000000..f39ab08a --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.html
@@ -0,0 +1,6 @@ +<!-- + * Copyright 2023 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.js b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.js new file mode 100644 index 0000000..25e6597 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts_engine/lacros_tts_support/tts_pause_during_speak_lacros_engine/test.js
@@ -0,0 +1,98 @@ +// 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. + +// TTS api test running from Lacros with Ash. +// build/lacros/test_runner.py test +// {path_to_lacros_build}/lacros_chrome_browsertests_run_in_series +// --gtest_filter=LacrosTtsApiTest.PauseDuringSpeakWithLacrosTtsEngine +// --ash-chrome-path {path_to_ash_build}/test_ash_chrome + +chrome.test.runTests([ + function testPauseDuringSpeakWithLacrosTtsEngine() { + let sendTtsEventFunc; + const utteranceText = "Hello"; + + // Register listeners for speech functions, enabling this extension + // to be a TTS engine. + let onSpeakPromise = new Promise((resolve) => { + chrome.ttsEngine.onSpeak.addListener(function listener( + utterance, options, sendTtsEvent) { + chrome.test.assertNoLastError(); + sendTtsEventFunc = sendTtsEvent; + chrome.test.assertEq(utteranceText, utterance); + sendTtsEvent({ + 'type': 'start', + 'charIndex': utterance.length + }); + chrome.ttsEngine.onSpeak.removeListener(listener); + resolve(); + }); + }); + + let stopListener = function () {}; + chrome.ttsEngine.onStop.addListener(stopListener); + + let onPausePromise = new Promise((resolve) => { + chrome.ttsEngine.onPause.addListener(function listener() { + chrome.test.assertNoLastError(); + chrome.ttsEngine.onPause.removeListener(listener); + resolve(); + }) + }); + + let onResumePromise = new Promise((resolve) => { + chrome.ttsEngine.onResume.addListener(function listener() { + chrome.test.assertNoLastError(); + // Simulate the speech engine resuming speech synthesis and finish + // the utterance. + sendTtsEventFunc({ + 'type': 'end', + 'charIndex': utteranceText.length + }); + chrome.ttsEngine.onResume.removeListener(listener); + resolve(); + }) + }); + + // Call tts.speak to speak an utterance with our own speech engine running + // in Lacros. + let ttsSpeakPromise = new Promise((resolve) => { + let startEventReceived = false; + chrome.tts.speak( + utteranceText, { + 'voiceName': 'Alice', + 'onEvent': function (event) { + if (event.type == 'start') { + startEventReceived = true; + } else if (event.type == 'end') { + chrome.test.assertEq(startEventReceived, true); + resolve(); + } + } + }, + function () { + chrome.test.assertNoLastError(); + }); + }); + + // Request pausing speech synthesis during the Lacros speech engine speaking + // an utterance. Ash's TtsController will pause speech synthesis and send + // onPause event to the Lacros speech engine speaking the current utterance. + chrome.tts.pause(); + + // Request resuming speech synthesis. Ash's TtsController will resume speech + // synthesis and send onResume event to the Lacros speech engine it has + // paused previously. + chrome.tts.resume(); + + // Verify that all ttsEngine events have been delivered to the speech + // engine, and tts.speak finishes the utterance. + Promise.all([onSpeakPromise, onPausePromise, onResumePromise, + ttsSpeakPromise + ]).then(() => { + chrome.ttsEngine.onStop.removeListener(stopListener); + chrome.test.succeed(); + }); + } +]);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 3720461..e6dce78 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2732,6 +2732,50 @@ } ] }, + "EnforceLocalAnchorConstraintsEnabled": { + "os": [ + "win", + "mac", + "linux", + "chromeos_ash", + "chromeos_lacros", + "android" + ], + "policy_pref_mapping_tests": [ + { + "note": "Policy not set. (The actual value isn't used as long as the pref is unmanaged.)", + "policies": {}, + "prefs": { + "enforce_local_anchor_constraints_enabled": { + "location": "local_state", + "default_value": true + } + } + }, + { + "policies": { + "EnforceLocalAnchorConstraintsEnabled": false + }, + "prefs": { + "enforce_local_anchor_constraints_enabled": { + "location": "local_state", + "value": false + } + } + }, + { + "policies": { + "EnforceLocalAnchorConstraintsEnabled": true + }, + "prefs": { + "enforce_local_anchor_constraints_enabled": { + "location": "local_state", + "value": true + } + } + } + ] + }, "AuthSchemes": { "os": [ "win",
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts index ea78ca6e..46e86f0 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts +++ b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
@@ -143,6 +143,9 @@ categoriesElement.shadowRoot!.querySelectorAll('[checked]'); assertEquals(1, checkedCategories.length); assertEquals(checkedCategories[0]!.parentElement!.id, 'classicChromeTile'); + assertEquals( + checkedCategories[0]!.parentElement!.getAttribute('aria-current'), + 'true'); // Set a theme with a color. theme.foregroundColor = {value: 0xffff0000}; @@ -155,6 +158,9 @@ categoriesElement.shadowRoot!.querySelectorAll('[checked]'); assertEquals(1, checkedCategories.length); assertEquals(checkedCategories[0]!.parentElement!.id, 'chromeColorsTile'); + assertEquals( + checkedCategories[0]!.parentElement!.getAttribute('aria-current'), + 'true'); // Set a theme with local background. const backgroundImage = createBackgroundImage('https://test.jpg'); @@ -169,6 +175,9 @@ categoriesElement.shadowRoot!.querySelectorAll('[checked]'); assertEquals(1, checkedCategories.length); assertEquals(checkedCategories[0]!.parentElement!.id, 'uploadImageTile'); + assertEquals( + checkedCategories[0]!.parentElement!.getAttribute('aria-current'), + 'true'); // Set a theme with collection background. backgroundImage.isUploadedImage = false; @@ -184,6 +193,9 @@ assertEquals(1, checkedCategories.length); assertEquals( checkedCategories[0]!.parentElement!.className, 'tile collection'); + assertEquals( + checkedCategories[0]!.parentElement!.getAttribute('aria-current'), + 'true'); // Set a CWS theme. theme.thirdPartyThemeInfo = {
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts index 19ba447..e0a873b 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts +++ b/chrome/test/data/webui/side_panel/customize_chrome/chrome_colors_test.ts
@@ -122,6 +122,7 @@ chromeColorsElement.shadowRoot!.querySelectorAll('[checked]'); assertEquals(1, checkedColors.length); assertEquals(defaultColorElement, checkedColors[0]); + assertEquals('true', checkedColors[0]!.getAttribute('aria-current')); // Set Chrome color. theme.seedColor = {value: 1}; @@ -136,6 +137,7 @@ assertEquals(1, checkedColors.length); assertEquals('chrome-color tile', checkedColors[0]!.className); assertEquals(3, (checkedColors[0]! as ColorElement).foregroundColor.value); + assertEquals('true', checkedColors[0]!.getAttribute('aria-current')); // Set custom color. theme.seedColor = {value: 10}; @@ -149,6 +151,8 @@ chromeColorsElement.shadowRoot!.querySelectorAll('[checked]'); assertEquals(1, checkedColors.length); assertEquals(chromeColorsElement.$.customColor, checkedColors[0]); + assertEquals( + 'true', checkedColors[0]!.parentElement!.getAttribute('aria-current')); // Set a CWS theme. theme.thirdPartyThemeInfo = {
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts index cad3cbc..a9d0c1f3 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts +++ b/chrome/test/data/webui/side_panel/customize_chrome/themes_test.ts
@@ -215,6 +215,7 @@ assertEquals(1, checkedThemes.length); const checkedTile = checkedThemes[0]!.parentElement as HTMLElement; assertEquals(checkedTile!.title, 'attribution1_1'); + assertEquals(checkedTile!.getAttribute('aria-current'), 'true'); // Set daily refresh. theme.backgroundImage.collectionId = 'test_collection_id';
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn index bf3cafc1..37d5f665 100644 --- a/chrome/updater/mac/BUILD.gn +++ b/chrome/updater/mac/BUILD.gn
@@ -65,7 +65,7 @@ sources = [ "main.cc" ] deps = [ - ":keystone_agent_bundle_resource_executable", + ":keystone_agent_bundle_resource_executable_test", ":keystone_agent_bundle_resource_plist", ":updater_bundle_helpers_test", ":updater_bundle_keystone_executable", @@ -74,6 +74,9 @@ "//chrome/updater:base", "//chrome/updater:constants_test", ] + if (is_asan) { + deps += [ ":keystone_agent_bundle_resource_executable_test_asan_dylib" ] + } public_deps = [ ":ksadmin_test_copy", ":ksadmin_test_link", @@ -422,6 +425,26 @@ "MAC_BUNDLE_IDENTIFIER=${keystone_bundle_identifier}.Agent", "PACKAGE_TYPE=APPL", ] + deps = [ + "//base", + "//chrome/updater:base", + "//chrome/updater:constants_prod", + ] +} + +mac_app_bundle("keystone_agent_bundle_test") { + info_plist = "keystone/Info.plist" + sources = [ "keystone/agent_main.cc" ] + output_name = keystone_app_name + "Agent_test" + extra_substitutions = [ + "MAC_BUNDLE_IDENTIFIER=${keystone_bundle_identifier}.Agent", + "PACKAGE_TYPE=APPL", + ] + deps = [ + "//base", + "//chrome/updater:base", + "//chrome/updater:constants_test", + ] } bundle_data("keystone_agent_bundle_resource_executable") { @@ -435,6 +458,21 @@ } } +bundle_data("keystone_agent_bundle_resource_executable_test") { + sources = [ "$root_out_dir/${keystone_app_name}Agent_test.app/Contents/MacOS/${keystone_app_name}Agent_test" ] + + outputs = [ "{{bundle_contents_dir}}/Helpers/$keystone_app_name.bundle/Contents/Resources/${keystone_app_name}Agent.app/Contents/MacOS/${keystone_app_name}Agent" ] + public_deps = [ ":keystone_agent_bundle_test" ] +} + +if (is_asan) { + bundle_data("keystone_agent_bundle_resource_executable_test_asan_dylib") { + sources = [ "$root_out_dir/${keystone_app_name}Agent_test.app/Contents/MacOS/libclang_rt.asan_osx_dynamic.dylib" ] + outputs = [ "{{bundle_contents_dir}}/Helpers/$keystone_app_name.bundle/Contents/Resources/${keystone_app_name}Agent.app/Contents/MacOS/{{source_file_part}}" ] + public_deps = [ ":keystone_agent_bundle_test" ] + } +} + bundle_data("keystone_agent_bundle_resource_plist") { sources = [ "$root_out_dir/${keystone_app_name}Agent.app/Contents/Info.plist" ]
diff --git a/chrome/updater/mac/keystone/agent_main.cc b/chrome/updater/mac/keystone/agent_main.cc index e5c14e9..537cc5b 100644 --- a/chrome/updater/mac/keystone/agent_main.cc +++ b/chrome/updater/mac/keystone/agent_main.cc
@@ -2,5 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// The agent is a shim to trick the keystone registration framework. -int main() {} +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/process/launch.h" +#include "chrome/updater/constants.h" +#include "chrome/updater/updater_scope.h" +#include "chrome/updater/util/util.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace updater { + +void Main() { + for (UpdaterScope scope : {UpdaterScope::kSystem, UpdaterScope::kUser}) { + absl::optional<base::FilePath> path = GetUpdaterExecutablePath(scope); + if (!path) { + continue; + } + base::CommandLine command(*path); + command.AppendSwitch(kWakeSwitch); + if (scope == UpdaterScope::kSystem) { + command.AppendSwitch(kSystemSwitch); + } + command.AppendSwitch(kEnableLoggingSwitch); + command.AppendSwitchNative(kLoggingModuleSwitch, kLoggingModuleSwitchValue); + base::LaunchProcess(command, {}); + } +} + +} // namespace updater + +// The agent is a shim to trick the keystone registration framework. When run, +// it should launch the --wake task. Not all callers correctly provide a scope, +// so it will wake both scopes (if present). +int main() { + updater::Main(); + return 0; +}
diff --git a/chromecast/media/cma/backend/BUILD.gn b/chromecast/media/cma/backend/BUILD.gn index 841b4ee..0537f9d 100644 --- a/chromecast/media/cma/backend/BUILD.gn +++ b/chromecast/media/cma/backend/BUILD.gn
@@ -195,6 +195,7 @@ ":volume_map", "//base", "//base/test:run_all_unittests", + "//base/test:test_support", "//chromecast/media/cma/backend/mixer:unittests", "//media", "//testing/gmock",
diff --git a/chromecast/media/cma/backend/cast_audio_json.cc b/chromecast/media/cma/backend/cast_audio_json.cc index c5f8163..45761538 100644 --- a/chromecast/media/cma/backend/cast_audio_json.cc +++ b/chromecast/media/cma/backend/cast_audio_json.cc
@@ -29,10 +29,9 @@ std::string contents; base::ReadFileToString(path, &contents); - std::unique_ptr<base::Value> value = - base::JSONReader::ReadDeprecated(contents); - if (value) { - callback.Run(std::move(value)); + absl::optional<base::Value> value = base::JSONReader::Read(contents); + if (value && value->is_dict()) { + callback.Run(std::move(*value).TakeDict()); return; } LOG(ERROR) << "Unable to parse JSON in " << path; @@ -77,10 +76,16 @@ CastAudioJsonProviderImpl::~CastAudioJsonProviderImpl() = default; -std::unique_ptr<base::Value> CastAudioJsonProviderImpl::GetCastAudioConfig() { +absl::optional<base::Value::Dict> +CastAudioJsonProviderImpl::GetCastAudioConfig() { std::string contents; base::ReadFileToString(CastAudioJson::GetFilePath(), &contents); - return base::JSONReader::ReadDeprecated(contents); + absl::optional<base::Value> value = base::JSONReader::Read(contents); + if (!value || value->is_dict()) { + return absl::nullopt; + } + + return std::move(*value).TakeDict(); } void CastAudioJsonProviderImpl::SetTuningChangedCallback(
diff --git a/chromecast/media/cma/backend/cast_audio_json.h b/chromecast/media/cma/backend/cast_audio_json.h index 40a045d..eeaa391 100644 --- a/chromecast/media/cma/backend/cast_audio_json.h +++ b/chromecast/media/cma/backend/cast_audio_json.h
@@ -5,14 +5,13 @@ #ifndef CHROMECAST_MEDIA_CMA_BACKEND_CAST_AUDIO_JSON_H_ #define CHROMECAST_MEDIA_CMA_BACKEND_CAST_AUDIO_JSON_H_ -#include <memory> - #include "base/files/file_path.h" #include "base/files/file_path_watcher.h" #include "base/functional/callback.h" #include "base/memory/scoped_refptr.h" #include "base/threading/sequence_bound.h" #include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace chromecast { namespace media { @@ -31,7 +30,7 @@ class CastAudioJsonProvider { public: using TuningChangedCallback = - base::RepeatingCallback<void(std::unique_ptr<base::Value> contents)>; + base::RepeatingCallback<void(absl::optional<base::Value::Dict> contents)>; virtual ~CastAudioJsonProvider() = default; @@ -41,7 +40,7 @@ // at CastAudioJson::GetReadOnlyFilePath() will be returned. // This function will run on the thread on which it is called, and may // perform blocking I/O. - virtual std::unique_ptr<base::Value> GetCastAudioConfig() = 0; + virtual absl::optional<base::Value::Dict> GetCastAudioConfig() = 0; // |callback| will be called when a new cast_audio config is available. // |callback| will always be called from the same thread, but not necessarily @@ -72,7 +71,7 @@ }; // CastAudioJsonProvider implementation: - std::unique_ptr<base::Value> GetCastAudioConfig() override; + absl::optional<base::Value::Dict> GetCastAudioConfig() override; void SetTuningChangedCallback(TuningChangedCallback callback) override; base::SequenceBound<FileWatcher> cast_audio_watcher_;
diff --git a/chromecast/media/cma/backend/cplay/cplay.cc b/chromecast/media/cma/backend/cplay/cplay.cc index d68a830..b1d18e60 100644 --- a/chromecast/media/cma/backend/cplay/cplay.cc +++ b/chromecast/media/cma/backend/cplay/cplay.cc
@@ -340,7 +340,12 @@ // Set volume. std::string contents; base::ReadFileToString(params.cast_audio_json_path, &contents); - GetVolumeMap().LoadVolumeMap(base::JSONReader::ReadDeprecated(contents)); + absl::optional<base::Value> parsed_json = base::JSONReader::Read(contents); + if (parsed_json && parsed_json->is_dict()) { + GetVolumeMap().LoadVolumeMap(std::move(*parsed_json).TakeDict()); + } else { + GetVolumeMap().LoadVolumeMap(absl::nullopt); + } float volume_dbfs = GetVolumeMap().VolumeToDbFS(params.cast_volume); float volume_multiplier = std::pow(10.0, volume_dbfs / 20.0); mixer_input.SetVolumeMultiplier(1.0);
diff --git a/chromecast/media/cma/backend/volume_map.cc b/chromecast/media/cma/backend/volume_map.cc index d5060a6..200e69a 100644 --- a/chromecast/media/cma/backend/volume_map.cc +++ b/chromecast/media/cma/backend/volume_map.cc
@@ -48,15 +48,16 @@ LoadVolumeMap(config_provider_->GetCastAudioConfig()); } -void VolumeMap::LoadVolumeMap(std::unique_ptr<base::Value> cast_audio_config) { - if (!cast_audio_config || !cast_audio_config->is_dict()) { +void VolumeMap::LoadVolumeMap( + absl::optional<base::Value::Dict> cast_audio_config) { + if (!cast_audio_config) { LOG(WARNING) << "No cast audio config found; using default volume map."; UseDefaultVolumeMap(); return; } const base::Value::List* volume_map_list = - cast_audio_config->GetDict().FindList(kKeyVolumeMap); + cast_audio_config->FindList(kKeyVolumeMap); if (!volume_map_list) { LOG(WARNING) << "No volume map found; using default volume map."; UseDefaultVolumeMap();
diff --git a/chromecast/media/cma/backend/volume_map.h b/chromecast/media/cma/backend/volume_map.h index ad8c11cd..4d3bdb9 100644 --- a/chromecast/media/cma/backend/volume_map.h +++ b/chromecast/media/cma/backend/volume_map.h
@@ -36,7 +36,7 @@ float DbFSToVolume(float db); - void LoadVolumeMap(std::unique_ptr<base::Value> cast_audio_config); + void LoadVolumeMap(absl::optional<base::Value::Dict> cast_audio_config); private: struct LevelToDb {
diff --git a/chromecast/media/cma/backend/volume_map_unittest.cc b/chromecast/media/cma/backend/volume_map_unittest.cc index 403df9d..aa0c3ef 100644 --- a/chromecast/media/cma/backend/volume_map_unittest.cc +++ b/chromecast/media/cma/backend/volume_map_unittest.cc
@@ -7,7 +7,7 @@ #include <string> #include "base/check.h" -#include "base/json/json_reader.h" +#include "base/test/values_test_util.h" #include "chromecast/media/cma/backend/cast_audio_json.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,12 +35,12 @@ void CallTuningChangedCallback(const std::string& new_config) { DCHECK(callback_); - callback_.Run(base::JSONReader::ReadDeprecated(new_config)); + callback_.Run(base::test::ParseJsonDict(new_config)); } private: - std::unique_ptr<base::Value> GetCastAudioConfig() override { - return base::JSONReader::ReadDeprecated(file_contents_); + absl::optional<base::Value::Dict> GetCastAudioConfig() override { + return base::test::ParseJsonDict(file_contents_); } void SetTuningChangedCallback(TuningChangedCallback callback) override { @@ -52,7 +52,7 @@ }; TEST(VolumeMapTest, UsesDefaultMapIfConfigEmpty) { - VolumeMap volume_map(std::make_unique<TestFileProvider>("")); + VolumeMap volume_map(std::make_unique<TestFileProvider>("{}")); EXPECT_NEAR(-58.0f, volume_map.VolumeToDbFS(0.01f), kEpsilon); EXPECT_NEAR(-48.0f, volume_map.VolumeToDbFS(1.0 / 11.0), kEpsilon); EXPECT_NEAR(-8.0f, volume_map.VolumeToDbFS(9.0 / 11.0), kEpsilon); @@ -82,7 +82,7 @@ } TEST(VolumeMapTest, LoadsNewMapWhenFileChanges) { - auto provider = std::make_unique<TestFileProvider>(""); + auto provider = std::make_unique<TestFileProvider>("{}"); TestFileProvider* provider_ptr = provider.get(); VolumeMap volume_map(std::move(provider));
diff --git a/chromeos/ash/services/BUILD.gn b/chromeos/ash/services/BUILD.gn index 7e48892..2f7dcda0 100644 --- a/chromeos/ash/services/BUILD.gn +++ b/chromeos/ash/services/BUILD.gn
@@ -34,6 +34,7 @@ "//chromeos/ash/services/network_config:unit_tests", "//chromeos/ash/services/network_health:unit_tests", "//chromeos/ash/services/quick_pair:unit_tests", + "//chromeos/ash/services/recording:unit_tests", "//chromeos/ash/services/secure_channel:unit_tests", "//chromeos/services/machine_learning/public/cpp:ash_unit_tests", ]
diff --git a/chromeos/ash/services/recording/BUILD.gn b/chromeos/ash/services/recording/BUILD.gn index 6f9ef4e..13a6edda 100644 --- a/chromeos/ash/services/recording/BUILD.gn +++ b/chromeos/ash/services/recording/BUILD.gn
@@ -10,6 +10,10 @@ sources = [ "gif_encoder.cc", "gif_encoder.h", + "gif_file_writer.cc", + "gif_file_writer.h", + "lzw_pixel_color_indices_writer.cc", + "lzw_pixel_color_indices_writer.h", "recording_encoder.cc", "recording_encoder.h", "recording_file_io_helper.cc", @@ -47,3 +51,16 @@ "//media", ] } + +source_set("unit_tests") { + testonly = true + + sources = [ "lzw_pixel_color_indices_writer_test.cc" ] + + deps = [ + ":recording", + "//base", + "//base/test:test_support", + "//chromeos/ash/services/recording/public/mojom", + ] +}
diff --git a/chromeos/ash/services/recording/DEPS b/chromeos/ash/services/recording/DEPS index dd2b1c5..375f8a1a 100644 --- a/chromeos/ash/services/recording/DEPS +++ b/chromeos/ash/services/recording/DEPS
@@ -10,6 +10,8 @@ "+mojo/public", "+services/audio/public", "+services/viz/privileged/mojom/compositing", + "+testing/gmock/include/gmock/gmock.h", + "+testing/gtest/include/gtest/gtest.h", "+ui/gfx", # Abseil is allowed by default, but some features are banned. See
diff --git a/chromeos/ash/services/recording/gif_encoder.cc b/chromeos/ash/services/recording/gif_encoder.cc index 8754d5e..d67de4bd 100644 --- a/chromeos/ash/services/recording/gif_encoder.cc +++ b/chromeos/ash/services/recording/gif_encoder.cc
@@ -5,8 +5,8 @@ #include "chromeos/ash/services/recording/gif_encoder.h" #include "base/notreached.h" +#include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h" #include "chromeos/ash/services/recording/recording_encoder.h" -#include "chromeos/ash/services/recording/recording_file_io_helper.h" #include "media/base/audio_bus.h" #include "media/base/video_frame.h" @@ -32,11 +32,10 @@ const base::FilePath& gif_file_path, OnFailureCallback on_failure_callback) : RecordingEncoder(std::move(on_failure_callback)), - gif_file_(gif_file_path, - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE), - file_io_helper_(gif_file_path, - std::move(drive_fs_quota_delegate), - /*delegate=*/this) { + gif_file_writer_(std::move(drive_fs_quota_delegate), + gif_file_path, + /*file_io_helper_delegate=*/this), + lzw_encoder_(&gif_file_writer_) { InitializeVideoEncoder(video_encoder_options); }
diff --git a/chromeos/ash/services/recording/gif_encoder.h b/chromeos/ash/services/recording/gif_encoder.h index 2ad7cbd4..01412c7 100644 --- a/chromeos/ash/services/recording/gif_encoder.h +++ b/chromeos/ash/services/recording/gif_encoder.h
@@ -5,11 +5,11 @@ #ifndef CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODER_H_ #define CHROMEOS_ASH_SERVICES_RECORDING_GIF_ENCODER_H_ -#include "base/files/file.h" #include "base/threading/sequence_bound.h" #include "base/types/pass_key.h" +#include "chromeos/ash/services/recording/gif_file_writer.h" +#include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h" #include "chromeos/ash/services/recording/recording_encoder.h" -#include "chromeos/ash/services/recording/recording_file_io_helper.h" namespace recording { @@ -67,14 +67,14 @@ void FlushAndFinalize(base::OnceClosure on_done) override; private: - // The file created at `gif_file_path` to which the output of the GIF encoder - // will be written. - base::File gif_file_; + // Abstracts writing bytes to the GIF file, and takes care of handling IO + // errors and remaining disk space / DriveFS quota issues. + GifFileWriter gif_file_writer_; - // A helper that will be used to calculate either the remaining disk space (if - // writing to a local file), or the remaining quota if the file exists on - // DriveFS. - RecordingFileIoHelper file_io_helper_; + // Abstracts encoding the video frame's image color indices using the + // Variable-Length-Code LZW compression algorithm and writing the output + // stream to the GIF file. + LzwPixelColorIndicesWriter lzw_encoder_; }; } // namespace recording
diff --git a/chromeos/ash/services/recording/gif_file_writer.cc b/chromeos/ash/services/recording/gif_file_writer.cc new file mode 100644 index 0000000..d28ba48 --- /dev/null +++ b/chromeos/ash/services/recording/gif_file_writer.cc
@@ -0,0 +1,40 @@ +// 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. + +#include "chromeos/ash/services/recording/gif_file_writer.h" +#include "base/containers/span.h" + +namespace recording { + +GifFileWriter::GifFileWriter( + mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, + const base::FilePath& gif_file_path, + RecordingFileIoHelper::Delegate* file_io_helper_delegate) + : gif_file_(gif_file_path, + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE), + file_io_helper_(gif_file_path, + std::move(drive_fs_quota_delegate), + file_io_helper_delegate) {} + +GifFileWriter::~GifFileWriter() = default; + +void GifFileWriter::WriteByte(uint8_t byte) { + WriteBytesAndCheck(base::make_span(&byte, sizeof(byte))); +} + +void GifFileWriter::WriteBuffer(const uint8_t* const buffer, + size_t buffer_size) { + WriteBytesAndCheck(base::make_span(buffer, buffer_size)); +} + +void GifFileWriter::WriteBytesAndCheck(base::span<const uint8_t> data) { + if (!gif_file_.WriteAtCurrentPosAndCheck(data)) { + file_io_helper_.delegate()->NotifyFailure(mojom::RecordingStatus::kIoError); + return; + } + + file_io_helper_.OnBytesWritten(data.size_bytes()); +} + +} // namespace recording
diff --git a/chromeos/ash/services/recording/gif_file_writer.h b/chromeos/ash/services/recording/gif_file_writer.h new file mode 100644 index 0000000..6345c9a --- /dev/null +++ b/chromeos/ash/services/recording/gif_file_writer.h
@@ -0,0 +1,53 @@ +// 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. + +#ifndef CHROMEOS_ASH_SERVICES_RECORDING_GIF_FILE_WRITER_H_ +#define CHROMEOS_ASH_SERVICES_RECORDING_GIF_FILE_WRITER_H_ + +#include <cstdint> + +#include "base/files/file.h" +#include "chromeos/ash/services/recording/recording_file_io_helper.h" + +namespace recording { + +// Creates a file at the given `gif_file_path`, and provides various APIs to +// allow writing bytes to it with various lengths. It also takes care of +// handling any IO errors while writing as well as running out of disk space or +// DriveFS quota conditions. +class GifFileWriter { + public: + GifFileWriter( + mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, + const base::FilePath& gif_file_path, + RecordingFileIoHelper::Delegate* file_io_helper_delegate); + GifFileWriter(const GifFileWriter&) = delete; + GifFileWriter& operator=(const GifFileWriter&) = delete; + ~GifFileWriter(); + + // Writes the given `bytes` to the `gif_file_`. + void WriteByte(uint8_t byte); + + // Writes the contents of the given `buffer` whose length is `buffer_size` to + // the `gif_file_`. + void WriteBuffer(const uint8_t* const buffer, size_t buffer_size); + + private: + // Writes the given `data` to the `gif_file_` and check for IO errors or disk + // space / DriveFS quota issues. + void WriteBytesAndCheck(base::span<const uint8_t> data); + + // The file created at `gif_file_path` to which the output of the GIF encoder + // will be written. + base::File gif_file_; + + // A helper that will be used to calculate either the remaining disk space (if + // writing to a local file), or the remaining quota if the file exists on + // DriveFS. + RecordingFileIoHelper file_io_helper_; +}; + +} // namespace recording + +#endif // CHROMEOS_ASH_SERVICES_RECORDING_GIF_FILE_WRITER_H_
diff --git a/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.cc b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.cc new file mode 100644 index 0000000..ccf7bfb --- /dev/null +++ b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.cc
@@ -0,0 +1,210 @@ +// 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. + +#include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h" + +#include <cstdint> + +#include "chromeos/ash/services/recording/gif_file_writer.h" + +namespace recording { + +namespace { + +// The GIF specs specify a maximum of 12 bits per LZW compression code, which +// means that the maximum possible value for the codes is (2 ^ 12) - 1, which is +// equal to 4095, and the maximum number of codes is (2 ^ 12 = 4096). +constexpr LzwCode kMaxNumberOfLzwCodes = 1 << 12; + +// The maximum number of bytes contained in a data sub block of the LZW encoded +// output. +constexpr uint8_t kMaxBytesPerDataSubBlock = 0xFF; + +} // namespace + +LzwPixelColorIndicesWriter::LzwPixelColorIndicesWriter( + GifFileWriter* gif_file_writer) + : gif_file_writer_(gif_file_writer) { + code_table_.reserve(kMaxNumberOfLzwCodes); + byte_stream_buffer_.reserve(kMaxBytesPerDataSubBlock); +} + +LzwPixelColorIndicesWriter::~LzwPixelColorIndicesWriter() = default; + +void LzwPixelColorIndicesWriter::EncodeAndWrite( + const ColorIndices& pixel_color_indices, + uint8_t color_bit_depth) { + if (pixel_color_indices.empty()) { + return; + } + + // Start the process with an empty table. + code_table_.clear(); + + // The `color_bit_depth` is the minimum number of bits needed to represent all + // the indices in `pixel_color_indices`. For example, if we have 4 colors, + // with indices 0, 1, 2, 3, The minimum number of bits needed to represent the + // largest index (3) is 2 bits. This is the `color_bit_depth` and it's also + // the value of the LZW minimum code size, and is required to be written to + // the file as the first byte of the image data block. + const LzwCode lzw_minimum_code_size = color_bit_depth; + gif_file_writer_->WriteByte(lzw_minimum_code_size); + + // Remember, we're not writing color indices to the file, but rather LZW + // compression codes. So we map each color index to an LZW code. So in the + // above example with 4 colors, our LZW codes will be as follows: + // + // +-------------+----------+ + // | Color Index | LZW Code | + // +-------------+----------+ + // | 0 | 0 | + // | 1 | 1 | + // | 2 | 2 | + // | 3 | 3 | + // +-------------+----------+ + // + // We add 2 more control codes that act as directives to the decoder, a clear + // code, and an End-of-Information (EoI) code. They're assigned the next + // available codes 4, and 5 respectively. + const LzwCode clear_code = 1 << color_bit_depth; + const LzwCode eoi_code = clear_code + 1; + + // EoI code is currently the maximum LZW code used, and requires the minimum + // number of bits `color_bit_depth + 1` to be represented in binary, which is + // 3 bits in our above example. + uint8_t current_code_bit_size = color_bit_depth + 1; + + // The next available unassigned LZW code is the value after the EoI code, + // which is 6 in our above example. + LzwCode next_available_code = eoi_code + 1; + + // First, output the `clear_code` with the `current_code_bit_size` to start + // the compression process. + AppendCodeToStreamBuffer(clear_code, current_code_bit_size); + + // We start at the very first pixel color index, and we use the value of that + // index as the current LZW code (remember from the above table that color + // indices map to LZW codes that have the same values). + LzwCode current_code = pixel_color_indices[0]; + + // Then we start iterating at the following index ... + for (size_t i = 1; i < pixel_color_indices.size(); ++i) { + // ... asking if the current sequence of indices represented by the + // `current_code` when it gets appended with `next_color_index`, does it map + // to an existing LZW code in the table? + const ColorIndex next_color_index = pixel_color_indices[i]; + LzwCode& code_in_table = + code_table_[current_code].next_index_to_code[next_color_index]; + if (code_in_table) { + // If yes, then `code_in_table` is a valid one, which means we have seen + // this pattern of indices before and mapped it to a code in the table. + // So we use that code we just found and the current code to represent the + // new pattern that results from appending `next_color_index` to the + // pattern of indices we had before. + current_code = code_in_table; + } else { + // If no, then this is the first time ever we see this new pattern, so we + // output `current_code` since we're starting now a new pattern for a + // sequence of indices that begin with `next_color_index`. + AppendCodeToStreamBuffer(current_code, current_code_bit_size); + + // We also assign a new LZW code to that pattern we didn't see before. + // Note that `code_in_table` is a reference, so changing its value changes + // the entry in the table. + code_in_table = next_available_code++; + current_code = next_color_index; + + // If the code we just added can no longer be represented as a binary + // value using the current minimum number of bits `current_code_bit_size`, + // we need to increment it by 1. + if (code_in_table >= (1 << current_code_bit_size)) { + ++current_code_bit_size; + } + + // If we are about to exceed the maximum 12 bits per LZW code defined by + // the specs, we need to signal to the decoder that we're starting a new + // compression table. + if (next_available_code >= kMaxNumberOfLzwCodes) { + AppendCodeToStreamBuffer(clear_code, current_code_bit_size); + + code_table_.clear(); + next_available_code = eoi_code + 1; + current_code_bit_size = color_bit_depth + 1; + } + } + } + + // At the end, we output the last code to the stream, and the control codes + // `clear_code` and `eoi_code` to signal the end of the compression. + AppendCodeToStreamBuffer(current_code, current_code_bit_size); + AppendCodeToStreamBuffer(clear_code, current_code_bit_size); + AppendCodeToStreamBuffer(eoi_code, current_code_bit_size); + + // We need to flush all the remaining data in `pending_byte_` and + // `byte_stream_buffer_` to the file. + if (next_bit_ != 0) { + FlushPendingByteToStream(); + } + + if (!byte_stream_buffer_.empty()) { + FlushStreamBufferToFile(); + } + + // Finally, we write the block terminator. + gif_file_writer_->WriteByte(0x00); +} + +// ----------------------------------------------------------------------------- +// LzwPixelColorIndicesWriter::CodeTableEntry: + +LzwPixelColorIndicesWriter::CodeTableEntry::CodeTableEntry() = default; +LzwPixelColorIndicesWriter::CodeTableEntry::CodeTableEntry(CodeTableEntry&&) = + default; +LzwPixelColorIndicesWriter::CodeTableEntry& +LzwPixelColorIndicesWriter::CodeTableEntry::operator=(CodeTableEntry&&) = + default; +LzwPixelColorIndicesWriter::CodeTableEntry::~CodeTableEntry() = default; + +// ----------------------------------------------------------------------------- +// LzwPixelColorIndicesWriter: + +void LzwPixelColorIndicesWriter::AppendCodeToStreamBuffer( + LzwCode code, + uint8_t code_bit_size) { + for (uint8_t i = 0; i < code_bit_size; ++i) { + AppendBitToPendingByte(code & 0x01); + code >>= 1; + } +} + +void LzwPixelColorIndicesWriter::AppendBitToPendingByte(uint8_t bit) { + bit <<= next_bit_; + pending_byte_ |= bit; + ++next_bit_; + + if (next_bit_ > 7) { + FlushPendingByteToStream(); + } +} + +void LzwPixelColorIndicesWriter::FlushPendingByteToStream() { + byte_stream_buffer_.push_back(pending_byte_); + if (byte_stream_buffer_.size() == kMaxBytesPerDataSubBlock) { + FlushStreamBufferToFile(); + } + DCHECK_LE(byte_stream_buffer_.size(), kMaxBytesPerDataSubBlock); + pending_byte_ = 0; + next_bit_ = 0; +} + +void LzwPixelColorIndicesWriter::FlushStreamBufferToFile() { + DCHECK(!byte_stream_buffer_.empty()); + + gif_file_writer_->WriteByte(byte_stream_buffer_.size()); + gif_file_writer_->WriteBuffer(byte_stream_buffer_.data(), + byte_stream_buffer_.size()); + byte_stream_buffer_.clear(); +} + +} // namespace recording
diff --git a/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h new file mode 100644 index 0000000..b7094712 --- /dev/null +++ b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h
@@ -0,0 +1,168 @@ +// 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. + +#ifndef CHROMEOS_ASH_SERVICES_RECORDING_LZW_PIXEL_COLOR_INDICES_WRITER_H_ +#define CHROMEOS_ASH_SERVICES_RECORDING_LZW_PIXEL_COLOR_INDICES_WRITER_H_ + +#include <cstdint> +#include <vector> + +#include "base/containers/flat_map.h" + +namespace recording { + +class GifFileWriter; + +// The specs specify a maximum of 12 bits per LZW compression code, so a 16-bit +// unsigned integer is perfect for this type. +using LzwCode = uint16_t; + +// We have a maximum of 256 colors in our color palette, so an 8-bit unsigned +// integer is enough to represent indices to these colors. +using ColorIndex = uint8_t; +using ColorIndices = std::vector<ColorIndex>; + +// Background: After color quantization is performed on the video frame, we end +// up with: +// 1- A color table (or a color palette) that contains a list of colors (up to +// 256 colors) that can best represent all the colors in the video frame. +// 2- A list of indices (`pixel_color_indices`), which defines a mapping between +// each pixel in the video frame and an index of a color in the above +// mentioned color palette. Pixels are processed from left to right and from +// top to bottom. +// If we try to write these pixel indices as they are to the GIF file, that +// could be a huge waste of space (magine an animated GIF image of size 1920 x +// 1080, this means we would write (1920 x 1080 = 2073600) indices for each +// video frame. +// In order to solve this problem, GIF uses the Variable-Length-Code Lempel-Ziv- +// Welch (LZW) compression algorithm, which tries to take advantage of the fact +// that many neighboring pixels may share the same color, and therefore, it’s +// possible to use special codes to indicate a sequence of colors, rather than +// one color at a time. +// To have even more compression when writing those LZW codes to the file, the +// GIF specs employs the Variable-Length-Code aspect of the algorithm as +// follows: +// - The number of bits needed to represent the currently used compression codes +// is tracked as `current_code_bit_size` (see .cc file). +// - Instead of writing whole bytes for these codes to the file, we write only +// the bits needed to represent them using the `current_code_bit_size`. These +// bits are packages in a byte stream and eventually get written to the file. +// - As we add new codes, when the new number of codes exceeds what's currently +// representable by `current_code_bit_size`, we increment it by one. +// +// This class implements the GIF LZW compression of a given +// `pixel_color_indices`, and takes care of writing the output to the GIF file +// using the given `gif_file_writer`. +class LzwPixelColorIndicesWriter { + public: + explicit LzwPixelColorIndicesWriter(GifFileWriter* gif_file_writer); + LzwPixelColorIndicesWriter(const LzwPixelColorIndicesWriter&) = delete; + LzwPixelColorIndicesWriter& operator=(const LzwPixelColorIndicesWriter&) = + delete; + ~LzwPixelColorIndicesWriter(); + + // Encodes (using the variable-length-code LZW algorithm) the given + // `pixel_color_indices`, which correspond to a new video frame, and writes + // the output to the GIF file using `gif_file_writer_`. Note that the given + // `color_bit_depth` is the least number of bits needed to represent all the + // color indices in `pixel_color_indices` as binary values (e.g. if we have 4 + // colors, then 2 bits are needed to represent them as binary values, and + // therefore `color_bit_depth` is 2). + void EncodeAndWrite(const ColorIndices& pixel_color_indices, + uint8_t color_bit_depth); + + private: + // Defines an entry in the below `code_table_`. An LZW compression code maps + // in `code_table_` to a value of `CodeTableEntry` to determine if followed by + // a certain pixel color index, does this map to an existing LZW code or not. + struct CodeTableEntry { + CodeTableEntry(); + CodeTableEntry(CodeTableEntry&&); + CodeTableEntry& operator=(CodeTableEntry&&); + ~CodeTableEntry(); + + // Maps from a color index appearing in a sequence of color indices in the + // input stream to an LZW compression code that represents this sequence of + // indices. + base::flat_map<ColorIndex, LzwCode> next_index_to_code; + }; + + // Appends the given `code` to the `byte_stream_buffer_` by first appending it + // bit-by-bit to `pending_byte_` using the number of bits provided in + // `code_bit_size` (this is done by calling `AppendBitToPendingByte()`). Once + // `pending_byte_` is complete, it will be pushed back to + // `byte_stream_buffer_` and `byte_stream_buffer_` will be flushed to the GIF + // file once it reaches the `kMaxBytesPerDataSubBlock`. + void AppendCodeToStreamBuffer(LzwCode code, uint8_t code_bit_size); + + // Appends the least significant bit of `bit` to `pending_byte_` at the + // `next_bit_` index. Once `pending_byte_` gets filled completely, i.e. 8 bits + // have been appended to it (i.e. `next_bit_` > 7), `pending_byte_` will be + // pushed back to the `byte_stream_buffer_` using the below call to + // `FlushPendingByteToStream()`. + void AppendBitToPendingByte(uint8_t bit); + + // Pushes back the `pending_byte_` to the `byte_stream_buffer_` and resets + // `pending_byte_` and `next_bit_` back to zeros. Once the size of + // `byte_stream_buffer_` exceeds `kMaxBytesPerDataSubBlock`, the contents of + // the buffer will be flushed to the GIF file via a call to + // `FlushStreamBufferToFile()`. + void FlushPendingByteToStream(); + + // Writes the number of bytes in `byte_stream_buffer_` to the GIF file, + // followed by the contents of the buffer. The buffer is then cleared. + void FlushStreamBufferToFile(); + + // Used for writing bytes to the GIF file and takes care of handling IO errors + // and disk space / DriveFS quota issues. + GifFileWriter* const gif_file_writer_; + + // See above background, we don't write the generated LZW codes directly to + // the file, however we try to do further compression by writing only the bits + // needed to represent them with the number bits as the value of + // `current_code_bit_size`. Those bits are packages in `pending_byte_` at the + // current `next_bit_` (which is the index of the bit at which the next bit + // will be appended to `pending_byte_`). Once a whole byte has been written to + // `pending_byte_` (i.e. when `next_bit_` is greater than 7), `pending_byte_` + // will be pushed back to the below `byte_stream_buffer_`, and both + // `pending_byte_` and `next_bit_` will be reset back to zeros waiting for the + // next bits to be appended. + uint8_t pending_byte_ = 0; + uint8_t next_bit_ = 0; + + // See the above comment for `pending_byte_`. Once `pending_byte_` is + // complete, it will be pushed back to this vector. The contents of this + // vector will not be written to the file until: + // - Either the number of bytes reached `kMaxBytesPerDataSubBlock` (which is + // `0xFF` or 255). + // - Or at the end of the compression, to output the remaining bytes in this + // buffer resulting in a partial data sub-block. + // Before the bytes in this buffer is written to the file, the number of bytes + // is written to the file first (see `FlushStreamBufferToFile()`). + std::vector<uint8_t> byte_stream_buffer_; + + // This is the LZW compression table, which is re-built every frame or when + // the number of generated codes exceeds the `kMaxNumberOfLzwCodes` (4096). + // We key into this table with the "current LZW compression code" (which + // represents the pattern of color indices seen so far in the input stream + // `pixel_color_indices`), to get a value of type `CodeTableEntry` (which is + // another map from a color index to an LZW compression code). What this table + // tells us is, for the current LZW code [G] when the next color index in the + // input stream [K] is appended: Do we have an existing LZW code for this new + // pattern (indices represented by [G])[K]? + // - Either yes, and in this `case code_table_[G].next_index_to_code[K]` is a + // valid code [H], so we now use [H] to represent all the pattern or color + // indices seen so far in the input stream. + // - Or no, and therefore we append the code [G] to the `byte_stream_buffer_`, + // add the next available LZW code to the table to represent the pattern + // (sequence of indices mapped to [G])[K], and use [K] as the code that + // represents a new color indices pattern that starts at this pixel. + // + // This table implements a variant of an R-way Trie data structure. + base::flat_map<LzwCode, CodeTableEntry> code_table_; +}; + +} // namespace recording + +#endif // CHROMEOS_ASH_SERVICES_RECORDING_LZW_PIXEL_COLOR_INDICES_WRITER_H_
diff --git a/chromeos/ash/services/recording/lzw_pixel_color_indices_writer_test.cc b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer_test.cc new file mode 100644 index 0000000..cc573578 --- /dev/null +++ b/chromeos/ash/services/recording/lzw_pixel_color_indices_writer_test.cc
@@ -0,0 +1,94 @@ +// 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. + +#include <cstdint> +#include <string> +#include <vector> + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "chromeos/ash/services/recording/gif_file_writer.h" +#include "chromeos/ash/services/recording/lzw_pixel_color_indices_writer.h" +#include "chromeos/ash/services/recording/public/mojom/recording_service.mojom.h" +#include "chromeos/ash/services/recording/recording_file_io_helper.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace recording { + +class FakeDelegate : public RecordingFileIoHelper::Delegate { + public: + FakeDelegate() = default; + FakeDelegate(const FakeDelegate&) = delete; + FakeDelegate& operator=(const FakeDelegate&) = delete; + ~FakeDelegate() override = default; + + // RecordingFileIoHelper::Delegate: + void NotifyFailure(mojom::RecordingStatus status) override {} +}; + +class LzwTest : public testing::Test { + public: + LzwTest() { + const bool dir_created = temp_dir_.CreateUniqueTempDir(); + DCHECK(dir_created); + } + LzwTest(const LzwTest&) = delete; + LzwTest& operator=(const LzwTest&) = delete; + ~LzwTest() override = default; + + // Returns a path for the given `file_name` under the temp dir created by this + // fixture. + base::FilePath GetPathInTempDir(const std::string& file_name) { + return temp_dir_.GetPath().Append(file_name); + } + + private: + base::ScopedTempDir temp_dir_; +}; + +TEST_F(LzwTest, VerifyOutputStream) { + const auto gif_path = GetPathInTempDir("test.gif"); + FakeDelegate delegate; + GifFileWriter gif_file_writer( + mojo::PendingRemote<mojom::DriveFsQuotaDelegate>(), gif_path, &delegate); + LzwPixelColorIndicesWriter lzw_encoder(&gif_file_writer); + + // Let's assume we have the following image with only 3 colors; red, green, + // and blue. + // + // +---+---+---+ + // | R | R | R | + // +---+---+---+ + // | G | G | G | + // +---+---+---+ + // | B | B | B | + // +---+---+---+ + // + // This means we only have 3 color indices; R => 0, G => 1, B => 2. The bit + // depth in this case is 2. Let's feed this pixel color indices to the LZW + // encoder. + const ColorIndices color_indices{0, 0, 0, 1, 1, 1, 2, 2, 2}; + lzw_encoder.EncodeAndWrite(color_indices, /*color_bit_depth=*/2); + + // Verify that the contents of the file are the expected output of the + // encoder. + absl::optional<std::vector<uint8_t>> actual_file_contents = + base::ReadFileToBytes(gif_path); + ASSERT_TRUE(actual_file_contents.has_value()); + EXPECT_THAT( + *actual_file_contents, + testing::ElementsAre(0x02, // LZW minimum code size. + 0x04, // Number of bytes in the data sub-block. + 0x84, // -+ + 0x83, // +-- LZW-compressed indices. + 0xA2, // -+ + 0x54, // Clear code + EoI code. + 0x00)); // Block terminator. +} + +} // namespace recording
diff --git a/chromeos/ash/services/recording/recording_encoder.h b/chromeos/ash/services/recording/recording_encoder.h index f4679f9d..f311459 100644 --- a/chromeos/ash/services/recording/recording_encoder.h +++ b/chromeos/ash/services/recording/recording_encoder.h
@@ -10,6 +10,7 @@ #include "base/sequence_checker.h" #include "base/thread_annotations.h" #include "base/time/time.h" +#include "chromeos/ash/services/recording/recording_file_io_helper.h" #include "media/base/encoder_status.h" #include "media/base/video_encoder.h" @@ -32,12 +33,12 @@ // Defines a common interface for encoding audio and video frames. The concrete // implementation classes decides how encoding is done, and the type of the // underlying actual encoders. -class RecordingEncoder { +class RecordingEncoder : public RecordingFileIoHelper::Delegate { public: explicit RecordingEncoder(OnFailureCallback on_failure_callback); RecordingEncoder(const RecordingEncoder&) = delete; RecordingEncoder& operator=(const RecordingEncoder&) = delete; - virtual ~RecordingEncoder(); + ~RecordingEncoder() override; bool did_failure_occur() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -71,10 +72,8 @@ // of `base::BindPostTask()`. virtual void FlushAndFinalize(base::OnceClosure on_done) = 0; - // Notifies the owner of this object (via `on_failure_callback_`) that a - // failure noted by `status` has occurred during encoding or saving to the - // output file. - void NotifyFailure(mojom::RecordingStatus status); + // RecordingFileIoHelper::Delegate: + void NotifyFailure(mojom::RecordingStatus status) override; protected: // Called by both the audio and video encoders to provide the `status` of
diff --git a/chromeos/ash/services/recording/recording_file_io_helper.cc b/chromeos/ash/services/recording/recording_file_io_helper.cc index a0c4c93..674025b 100644 --- a/chromeos/ash/services/recording/recording_file_io_helper.cc +++ b/chromeos/ash/services/recording/recording_file_io_helper.cc
@@ -24,7 +24,7 @@ RecordingFileIoHelper::RecordingFileIoHelper( const base::FilePath& output_file_path, mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, - RecordingEncoder* delegate) + Delegate* delegate) : output_file_path_(output_file_path), drive_fs_quota_delegate_remote_(std::move(drive_fs_quota_delegate)), delegate_(delegate) {
diff --git a/chromeos/ash/services/recording/recording_file_io_helper.h b/chromeos/ash/services/recording/recording_file_io_helper.h index c14b47b..3aa83af 100644 --- a/chromeos/ash/services/recording/recording_file_io_helper.h +++ b/chromeos/ash/services/recording/recording_file_io_helper.h
@@ -6,9 +6,7 @@ #define CHROMEOS_ASH_SERVICES_RECORDING_RECORDING_FILE_IO_HELPER_H_ #include "base/files/file_path.h" -#include "base/functional/callback_forward.h" #include "chromeos/ash/services/recording/public/mojom/recording_service.mojom.h" -#include "chromeos/ash/services/recording/recording_encoder.h" #include "mojo/public/cpp/bindings/remote.h" namespace recording { @@ -17,21 +15,30 @@ enum class RecordingStatus; } // namespace mojom -class RecordingEncoder; - -// Defines a helper that can be used to performs remaining free space or +// Defines a helper that can be used to perform remaining free space or // remaining DriveFs quota checks when writing bytes to the output file. class RecordingFileIoHelper { public: + // Defines an API so that the helper can notify with any IO errors that happen + // while writing to the file, or when the remaining disk space / DriveFS quota + // are less than the minimum threshold. + class Delegate { + public: + virtual void NotifyFailure(mojom::RecordingStatus status) = 0; + + protected: + virtual ~Delegate() = default; + }; + RecordingFileIoHelper( const base::FilePath& output_file_path, mojo::PendingRemote<mojom::DriveFsQuotaDelegate> drive_fs_quota_delegate, - RecordingEncoder* delegate); + Delegate* delegate); RecordingFileIoHelper(const RecordingFileIoHelper&) = delete; RecordingFileIoHelper& operator=(const RecordingFileIoHelper&) = delete; ~RecordingFileIoHelper(); - RecordingEncoder* delegate() { return delegate_; } + Delegate* delegate() { return delegate_; } // Tells this helper that `num_bytes` have been written to the output file so // that it can perform any remaining disk space checks if needed. @@ -63,7 +70,7 @@ // The `RecordingEncoder` that owns this helper (either directly or // indirectly) which acts as a delegate of this class to notify with any IO // errors. - RecordingEncoder* const delegate_; + Delegate* const delegate_; // Once this value becomes <= 0, we trigger a remaining disk space poll. // Initialized to 0, so that we poll the disk space on the very first write
diff --git a/chromeos/crosapi/mojom/feedback.mojom b/chromeos/crosapi/mojom/feedback.mojom index 4a55af3b..55ea975f 100644 --- a/chromeos/crosapi/mojom/feedback.mojom +++ b/chromeos/crosapi/mojom/feedback.mojom
@@ -10,6 +10,10 @@ // The feedback sources supported for invoking feedback report from Lacros. // Note: When you add a new value, please add a test case accordingly in: // chrome/browser/feedback/show_feedback_page_lacros_browertest.cc. +// +// Next MinVersion: 4 +// Next ID: 8 +// [Stable, Extensible] enum LacrosFeedbackSource { kLacrosBrowserCommand = 0, @@ -19,6 +23,7 @@ [MinVersion=2] kLacrosSadTabPage = 4, [MinVersion=2] kLacrosChromeLabs = 5, [MinVersion=2] kLacrosQuickAnswers = 6, + [MinVersion=3] kLacrosWindowLayoutMenu = 7, }; [Stable]
diff --git a/chromeos/crosapi/mojom/tts.mojom b/chromeos/crosapi/mojom/tts.mojom index 352db542..f25eef1 100644 --- a/chromeos/crosapi/mojom/tts.mojom +++ b/chromeos/crosapi/mojom/tts.mojom
@@ -114,8 +114,8 @@ // Interface for Tts, implemented in ash-chrome. Used by lacros-chrome to // communicate with ash TtsController to send the voice data and // speech requests to ash. -// Next version: 3 -// Next method id: 4 +// Next version: 4 +// Next method id: 6 [Stable, Uuid="8550e8d0-a818-49a3-93c1-d8053a33b2e6"] interface Tts { // A TtsClient can register itself with Tts, so that Tts can communicate with @@ -140,11 +140,22 @@ // the given |source_url|. [MinVersion=2] Stop@3(url.mojom.Url source_url); + + // Requests Ash TtsController to pause speeach synthesis. + [MinVersion=3] + Pause@4(); + + // Requests Ash TtsController to resume speeach synthesis. + [MinVersion=3] + Resume@5(); + }; // Interface for tts client. Implemented in lacros-chrome. // Each Tts client is associated with a browser context object in Lacros. // Used by ash-chrome to send voices to Lacros. +// Next version: 3 +// Next method id: 5 [Stable, Uuid="60ce0365-451e-402d-9a1c-e57350f9a202"] interface TtsClient { // Called when voices changed in ash TtsController . @@ -163,6 +174,16 @@ // speaking. [MinVersion=1] Stop@2(string engine_id); + + // Called from Ash to request the specified Lacros speech engine to pause + // speaking. + [MinVersion=2] + Pause@3(string engine_id); + + // Called from Ash to request the specified Lacros speech engine to resume + // speaking. + [MinVersion=2] + Resume@4(string engine_id); }; // This interface receives events from an utterance which is being spoken by
diff --git a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc index 2897edd..bd5c1f6 100644 --- a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc +++ b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -133,7 +133,7 @@ return false; } - if (!chromeos::TabletState::Get()->InTabletMode()) { + if (!TabletState::Get()->InTabletMode()) { return true; } @@ -234,6 +234,8 @@ views::kWindowControlCloseIcon); UpdateCaptionButtonState(/*animate=*/false); + + frame_observer_.Observe(frame_); } FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() = default; @@ -460,6 +462,24 @@ } } +void FrameCaptionButtonContainerView::OnWidgetActivationChanged( + views::Widget* widget, + bool active) { + if (!active) { + return; + } + + // Tablet nudge is controlled by ash by another class + // (`::ash::TabletModeMultitaskCue`). + if (TabletState::Get()->InTabletMode()) { + return; + } + + nudge_controller_.MaybeShowNudge( + widget->GetNativeWindow(), + /*anchor_view=*/static_cast<views::View*>(size_button_)); +} + void FrameCaptionButtonContainerView::SetButtonIcon( views::FrameCaptionButton* button, views::CaptionButtonIcon icon, @@ -571,7 +591,7 @@ SetButtonsToNormal(Animate::kNo); frame_->Close(); - if (chromeos::TabletState::Get()->InTabletMode()) { + if (TabletState::Get()->InTabletMode()) { base::RecordAction( base::UserMetricsAction("Tablet_WindowCloseFromCaptionButton")); } else {
diff --git a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h index c95cbc1..a019c02 100644 --- a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h +++ b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h
@@ -14,11 +14,13 @@ #include "chromeos/ui/frame/caption_buttons/frame_size_button.h" #include "chromeos/ui/frame/caption_buttons/frame_size_button_delegate.h" #include "chromeos/ui/frame/caption_buttons/snap_controller.h" +#include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" #include "chromeos/ui/wm/features.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/views/animation/animation_delegate_views.h" #include "ui/views/layout/box_layout_view.h" #include "ui/views/view.h" +#include "ui/views/widget/widget_observer.h" #include "ui/views/window/frame_caption_button.h" namespace gfx { @@ -41,7 +43,8 @@ class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) FrameCaptionButtonContainerView : public views::BoxLayoutView, public FrameSizeButtonDelegate, - public views::AnimationDelegateViews { + public views::AnimationDelegateViews, + public views::WidgetObserver { public: METADATA_HEADER(FrameCaptionButtonContainerView); @@ -87,6 +90,10 @@ return container_view_->custom_button_; } + MultitaskMenuNudgeController* nudge_controller() const { + return &container_view_->nudge_controller_; + } + private: raw_ptr<FrameCaptionButtonContainerView> container_view_; }; @@ -152,6 +159,9 @@ void AnimationEnded(const gfx::Animation* animation) override; void AnimationProgressed(const gfx::Animation* animation) override; + // views::WidgetObserver: + void OnWidgetActivationChanged(views::Widget* widget, bool active) override; + private: // Sets |button|'s icon to |icon|. If |animate| is Animate::kYes, the button // will crossfade to the new icon. If |animate| is Animate::kNo and @@ -199,6 +209,9 @@ // Stored as a `FrameSizeButton` so the multitask menu can be accessed. raw_ptr<chromeos::FrameSizeButton> size_button_ = nullptr; + // Handles showing the educational nudge for the clamshell multitask menu. + MultitaskMenuNudgeController nudge_controller_; + // Mapping of the image needed to paint a button for each of the values of // CaptionButtonIcon. std::map<views::CaptionButtonIcon, const gfx::VectorIcon*> button_icon_map_; @@ -222,6 +235,9 @@ // Keeps track of the borderless mode being enabled or not. This defines the // visibility of the caption button container. bool is_borderless_mode_enabled_ = false; + + base::ScopedObservation<views::Widget, views::WidgetObserver> frame_observer_{ + this}; }; } // namespace chromeos
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc index 4f21459..dfc8b36f 100644 --- a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc +++ b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.cc
@@ -4,15 +4,12 @@ #include "chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h" -#include "base/check_is_test.h" #include "chromeos/strings/grit/chromeos_strings.h" #include "chromeos/ui/base/tablet_state.h" -#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" -#include "chromeos/ui/frame/frame_header.h" +#include "chromeos/ui/wm/features.h" #include "components/prefs/pref_registry_simple.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/window.h" -#include "ui/aura/window_tracker.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/display/screen.h" @@ -21,9 +18,8 @@ #include "ui/views/background.h" #include "ui/views/controls/label.h" #include "ui/views/highlight_border.h" +#include "ui/views/layout/box_layout_view.h" #include "ui/views/widget/widget.h" -#include "ui/views/window/frame_caption_button.h" -#include "ui/wm/public/activation_client.h" namespace chromeos { @@ -38,8 +34,7 @@ constexpr base::TimeDelta kFadeDuration = base::Milliseconds(50); -constexpr gfx::Insets kLabelInsets(10); -constexpr int kLabelRoundingDp = 16; +constexpr gfx::Insets kLabelInsets = gfx::Insets::VH(8, 16); constexpr int kLabelMaxWidth = 512; constexpr int kNudgeDistanceFromAnchor = 8; @@ -55,6 +50,8 @@ // Clock that can be overridden for testing. base::Clock* g_clock_override = nullptr; +MultitaskMenuNudgeController::Delegate* g_delegate_instance = nullptr; + base::Time GetTime() { return g_clock_override ? g_clock_override->Now() : base::Time::Now(); } @@ -76,11 +73,6 @@ auto contents_view = views::Builder<views::BoxLayoutView>() .SetInsideBorderInsets(kLabelInsets) - .SetBackground(views::CreateThemedRoundedRectBackground( - ui::kColorSysSurface3, kLabelRoundingDp)) - .SetBorder(std::make_unique<views::HighlightBorder>( - kLabelRoundingDp, views::HighlightBorder::Type::kHighlightBorder1, - /*use_light_colors=*/false)) .AddChildren( views::Builder<views::Label>() .SetHorizontalAlignment(gfx::ALIGN_CENTER) @@ -94,28 +86,36 @@ gfx::Font::Weight::NORMAL)) .SetText(l10n_util::GetStringUTF16(message_id))) .Build(); + const float corner_radius = contents_view->GetPreferredSize().height() / 2.0f; + contents_view->SetBackground(views::CreateThemedRoundedRectBackground( + ui::kColorSysSurface3, corner_radius)); + contents_view->SetBorder(std::make_unique<views::HighlightBorder>( + corner_radius, views::HighlightBorder::Type::kHighlightBorder1, + /*use_light_colors=*/false)); + widget->SetContentsView(std::move(contents_view)); return widget; } } // namespace -MultitaskMenuNudgeController::MultitaskMenuNudgeController( - aura::Window* root_window, - std::unique_ptr<Delegate> delegate) - : root_window_(root_window), delegate_(std::move(delegate)) { - display::Screen::GetScreen()->AddObserver(this); +MultitaskMenuNudgeController::Delegate::~Delegate() { + DCHECK_EQ(this, g_delegate_instance); + g_delegate_instance = nullptr; +} - ::wm::GetActivationClient(root_window_)->AddObserver(this); - root_window_observation_.Observe(root_window_); +MultitaskMenuNudgeController::Delegate::Delegate() { + DCHECK_EQ(nullptr, g_delegate_instance); + g_delegate_instance = this; +} + +MultitaskMenuNudgeController::MultitaskMenuNudgeController() { + display::Screen::GetScreen()->AddObserver(this); } MultitaskMenuNudgeController::~MultitaskMenuNudgeController() { DismissNudge(); display::Screen::GetScreen()->RemoveObserver(this); - if (root_window_) { - ::wm::GetActivationClient(root_window_)->RemoveObserver(this); - } } // static @@ -128,25 +128,33 @@ } void MultitaskMenuNudgeController::MaybeShowNudge(aura::Window* window) { + MaybeShowNudge(window, /*anchor_view=*/nullptr); +} + +void MultitaskMenuNudgeController::MaybeShowNudge(aura::Window* window, + views::View* anchor_view) { if (!chromeos::wm::features::IsWindowLayoutMenuEnabled() || g_suppress_nudge_for_testing || nudge_widget_) { return; } + // TODO(sammiequon): Delegate is not available for lacros yet. + if (!g_delegate_instance) { + return; + } + // If the window is not visible, do not show the nudge. if (!window->IsVisible()) { return; } - // Tracks `window` in case it gets destroyed during the async pref fetching in - // lacros. - auto window_tracker = std::make_unique<aura::WindowTracker>(); - window_tracker->Add(window); - delegate_->GetNudgePreferences( + // `window` and `anchor_view` can be passed safely on clamshell because they + // are owned by the frame which also owns `this`. They can be passed safely on + // tablet since tablet is controlled by ash which is sync. + g_delegate_instance->GetNudgePreferences( TabletState::Get()->InTabletMode(), base::BindOnce(&MultitaskMenuNudgeController::OnGetPreferences, - weak_ptr_factory_.GetWeakPtr(), - std::move(window_tracker))); + weak_ptr_factory_.GetWeakPtr(), window, anchor_view)); } void MultitaskMenuNudgeController::DismissNudge() { @@ -178,28 +186,24 @@ const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, ui::PropertyChangeReason reason) { - if (window == window_) { - UpdateWidgetAndPulse(); - } + DCHECK_EQ(window_, window); + UpdateWidgetAndPulse(); } void MultitaskMenuNudgeController::OnWindowTargetTransformChanging( aura::Window* window, const gfx::Transform& new_transform) { - if (window == window_) { - // Prevent unintended behaviour in situations that use transforms such as - // overview mode. - // TODO(hewer): Decide how the cue behaves when adjusting the split view - // bounds in tablet mode. - DismissNudge(); - } + DCHECK_EQ(window_, window); + // Prevent unintended behaviour in situations that use transforms such as + // overview mode. + // TODO(hewer): Decide how the cue behaves when adjusting the split view + // bounds in tablet mode. + DismissNudge(); } void MultitaskMenuNudgeController::OnWindowStackingChanged( aura::Window* window) { - if (window != window_) { - return; - } + DCHECK_EQ(window_, window); // Stacking may change during the construction of the widget, at which // `nudge_widget_` would still be null. @@ -214,27 +218,10 @@ } void MultitaskMenuNudgeController::OnWindowDestroying(aura::Window* window) { - if (window == root_window_) { - ::wm::GetActivationClient(root_window_)->RemoveObserver(this); - root_window_ = nullptr; - root_window_observation_.Reset(); - return; - } - DCHECK_EQ(window_, window); DismissNudge(); } -void MultitaskMenuNudgeController::OnWindowActivated( - ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) { - // Tablet mode window activation is handled by `TabletModeMultitaskCue`. - if (gained_active && !TabletState::Get()->InTabletMode()) { - MaybeShowNudge(gained_active); - } -} - void MultitaskMenuNudgeController::OnDisplayTabletStateChanged( display::TabletState state) { switch (state) { @@ -268,18 +255,11 @@ } void MultitaskMenuNudgeController::OnGetPreferences( - std::unique_ptr<aura::WindowTracker> candidate_tracker, + aura::Window* window, + views::View* anchor_view, bool tablet_mode, int shown_count, base::Time last_shown_time) { - // Candidate window was destroyed during the async fetch preferences. - if (candidate_tracker->windows().empty()) { - return; - } - - DCHECK_EQ(1u, candidate_tracker->windows().size()); - aura::Window* candidate_window = candidate_tracker->windows()[0]; - // Tablet state has changed since we fetched preferences. if (tablet_mode != TabletState::Get()->InTabletMode()) { return; @@ -300,36 +280,12 @@ return; } - // The anchor is the button on the header that serves as the maximize or - // restore button (depending on the window state). Not used in tablet - // mode. - views::FrameCaptionButton* anchor_view = nullptr; - - if (!tablet_mode) { - // Some tests create windows without a backing widget - // (`CreateTestWindow()`), and some widgets may not have a header, test or - // custom header. - auto* widget = views::Widget::GetWidgetForNativeWindow(candidate_window); - if (!widget) { - CHECK_IS_TEST(); - return; - } - - auto* frame_header = chromeos::FrameHeader::Get(widget); - if (!frame_header) { - return; - } - - anchor_view = frame_header->caption_button_container()->size_button(); - DCHECK(anchor_view); - - // If the anchor is not visible, do not show the nudge. - if (!anchor_view->IsDrawn()) { - return; - } + // If the anchor is passed and hidden, we cannot show the nudge. + if (anchor_view && !anchor_view->IsDrawn()) { + return; } - window_ = candidate_window; + window_ = window; window_observation_.Observe(window_); nudge_widget_ = CreateWidget(window_); @@ -340,8 +296,8 @@ if (!tablet_mode) { // Create the layer which pulses on the maximize/restore button. pulse_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR); - // TODO(b/267646118): Update the color to match the theme. - pulse_layer_->SetColor(SK_ColorGRAY); + pulse_layer_->SetColor(nudge_widget_->GetColorProvider()->GetColor( + ui::kColorMultitaskMenuNudgePulse)); window_->parent()->layer()->Add(pulse_layer_.get()); } @@ -359,7 +315,8 @@ .SetOpacity(layer, 1.0f, gfx::Tween::LINEAR); // Update the preferences. - delegate_->SetNudgePreferences(tablet_mode, shown_count + 1, GetTime()); + g_delegate_instance->SetNudgePreferences(tablet_mode, shown_count + 1, + GetTime()); // No need to update pulse or start timer in tablet mode. if (!tablet_mode) { @@ -420,7 +377,8 @@ if (tablet_mode) { // The nudge is placed in the top center of the window, just below the cue. - const int tablet_nudge_y_offset = delegate_->GetTabletNudgeYOffset(); + const int tablet_nudge_y_offset = + g_delegate_instance->GetTabletNudgeYOffset(); nudge_widget_->SetBounds(gfx::Rect( (window_->bounds().width() - size.width()) / 2 + window_->bounds().x(), tablet_nudge_y_offset + window_->bounds().y(), size.width(),
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h index 426c2db..7e41019b 100644 --- a/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h +++ b/chromeos/ui/frame/multitask_menu/multitask_menu_nudge_controller.h
@@ -14,7 +14,6 @@ #include "ui/display/display_observer.h" #include "ui/views/view.h" #include "ui/views/widget/unique_widget_ptr.h" -#include "ui/wm/public/activation_change_observer.h" class PrefRegistrySimple; @@ -22,10 +21,6 @@ class MultitaskMenuNudgeControllerTest; } -namespace aura { -class WindowTracker; -} - namespace ui { class Layer; } @@ -35,7 +30,6 @@ // Controller for showing the user education nudge for the multitask menu. class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) MultitaskMenuNudgeController : public aura::WindowObserver, - public ::wm::ActivationChangeObserver, public display::DisplayObserver { public: // `tablet_mode` refers to the tablet state when the prefs are fetched. If @@ -49,13 +43,17 @@ // A delegate to provide platform specific implementation (ash, lacros). class Delegate { public: - virtual ~Delegate() = default; + virtual ~Delegate(); + virtual int GetTabletNudgeYOffset() const = 0; virtual void GetNudgePreferences(bool tablet_mode, GetPreferencesCallback callback) = 0; virtual void SetNudgePreferences(bool tablet_mode, int count, base::Time time) = 0; + + protected: + Delegate(); }; // The name of an integer pref that counts the number of times we have shown @@ -72,8 +70,7 @@ static constexpr char kTabletLastShownPrefName[] = "cros.wm_nudge.tablet_multitask_nudge_last_shown"; - MultitaskMenuNudgeController(aura::Window* root_window, - std::unique_ptr<Delegate> delegate); + MultitaskMenuNudgeController(); MultitaskMenuNudgeController(const MultitaskMenuNudgeController&) = delete; MultitaskMenuNudgeController& operator=(const MultitaskMenuNudgeController&) = delete; @@ -84,6 +81,7 @@ // Attempts to show the nudge. Reads preferences and then calls // `OnGetPreferences()`. void MaybeShowNudge(aura::Window* window); + void MaybeShowNudge(aura::Window* window, views::View* anchor_view); // Closes the widget and cleans up all pointers in this class. void DismissNudge(); @@ -101,11 +99,6 @@ void OnWindowStackingChanged(aura::Window* window) override; void OnWindowDestroying(aura::Window* window) override; - // wm::ActivationChangeObserver: - void OnWindowActivated(ActivationReason reason, - aura::Window* gained_active, - aura::Window* lost_active) override; - // display::DisplayObserver: void OnDisplayTabletStateChanged(display::TabletState state) override; @@ -119,9 +112,12 @@ // Callback function after fetching preferences. Shows the nudge if it can be // shown. The nudge can be shown if it hasn't been shown 3 times already, or - // shown in the last 24 hours. `candidate_tracker` tracks our candidate - // window; it may be destroyed during the async pref fetching in lacros. - void OnGetPreferences(std::unique_ptr<aura::WindowTracker> candidate_tracker, + // shown in the last 24 hours. `window` and `anchor_view` are the associated + // window and the anchor for the nudge. `anchor_view` will be null in tablet + // mode as the nudge shows in the top center of the window and is not anchored + // to anything. + void OnGetPreferences(aura::Window* window, + views::View* anchor_view, bool tablet_mode, int shown_count, base::Time last_shown_time); @@ -153,14 +149,8 @@ // button on `window_`'s frame. Null in tablet mode. views::View* anchor_view_ = nullptr; - aura::Window* root_window_ = nullptr; - - std::unique_ptr<Delegate> delegate_; - base::ScopedObservation<aura::Window, aura::WindowObserver> window_observation_{this}; - base::ScopedObservation<aura::Window, aura::WindowObserver> - root_window_observation_{this}; base::WeakPtrFactory<MultitaskMenuNudgeController> weak_ptr_factory_{this}; };
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index f59a028c..77089c5 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -289,6 +289,8 @@ "metrics/payments/virtual_card_enrollment_metrics.h", "metrics/payments/virtual_card_manual_fallback_bubble_metrics.cc", "metrics/payments/virtual_card_manual_fallback_bubble_metrics.h", + "metrics/payments/wallet_usage_data_metrics.cc", + "metrics/payments/wallet_usage_data_metrics.h", "metrics/profile_import_metrics.cc", "metrics/profile_import_metrics.h", "metrics/shadow_prediction_metrics.cc", @@ -914,6 +916,7 @@ "metrics/form_events/form_event_logger_base_unittest.cc", "metrics/payments/iban_metrics_unittest.cc", "metrics/payments/offers_metrics_unittest.cc", + "metrics/payments/wallet_usage_data_metrics_unittest.cc", "metrics/profile_import_metrics_unittest.cc", "metrics/shadow_prediction_metrics_unittest.cc", "metrics/stored_profile_metrics_unittest.cc",
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index 8eee2c5..c13d668c 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc
@@ -30,7 +30,8 @@ namespace { -// Creates a reply callback for ParseFormAsync(). +// ParsingCallback(), NotifyObserversCallback(), and NotifyNoObserversCallback() +// assemble the reply callback for ParseFormAsync(). // // An event // AutofillManager::OnFoo(const FormData& form, args...) @@ -39,26 +40,58 @@ // AutofillManager::OnFooImpl(const FormData& form, args...) // unless the AutofillManager has been destructed or reset in the meantime. // -// ParsingCallback(&AutofillManager::OnFooImpl, args...) creates the -// corresponding callback to be passed to ParseFormAsync(). +// For some events, AutofillManager::Observer::On{Before,After}Foo() must be +// called before/after AutofillManager::OnFooImpl(). // -// The `after_event` is supposed to be &Observer::OnAfterFoo (or nullptr if no -// such event exists). The callback notifies the observers of `after_event`. +// The corresponding callback for ParseFormAsync() is assembled by +// ParsingCallback(&AutofillManager::OnFooImpl, ...) +// .Then(NotifyNoObserversCallback()) +// or +// ParsingCallback(&AutofillManager::OnFooImpl, ...) +// .Then(NotifyObserversCallback(&Observer::OnAfterFoo, ...)) +// +// `.Then(NotifyNoObserversCallback())` is needed in the first case to discard +// the return type of ParsingCallback(). template <typename Functor, typename... Args> -base::OnceCallback<void(AutofillManager&, const FormData&)> ParsingCallback( - Functor&& functor, - void (AutofillManager::Observer::*after_event)(), - Args&&... args) { +base::OnceCallback<AutofillManager&(AutofillManager&, const FormData&)> +ParsingCallback(Functor&& functor, Args&&... args) { return base::BindOnce( - [](Functor&& functor, void (AutofillManager::Observer::*after_event)(), - std::remove_reference_t<Args&&>... args, AutofillManager& self, - const FormData& form) { + [](Functor&& functor, std::remove_reference_t<Args&&>... args, + AutofillManager& self, const FormData& form) -> AutofillManager& { base::invoke(std::forward<Functor>(functor), self, form, std::forward<Args>(args)...); - if (after_event) - self.NotifyObservers(after_event); + return self; }, - std::forward<Functor>(functor), after_event, std::forward<Args>(args)...); + std::forward<Functor>(functor), std::forward<Args>(args)...); +} + +// See ParsingCallback(). +template <typename Functor, typename... Args> +base::OnceCallback<void(AutofillManager&)> NotifyObserversCallback( + Functor&& functor, + Args&&... args) { + return base::BindOnce( + [](Functor&& functor, std::remove_reference_t<Args&&>... args, + AutofillManager& self) { + self.NotifyObservers(std::forward<Functor>(functor), + std::forward<Args>(args)...); + }, + std::forward<Functor>(functor), std::forward<Args>(args)...); +} + +// See ParsingCallback(). +base::OnceCallback<void(AutofillManager&)> NotifyNoObserversCallback() { + return base::DoNothingAs<void(AutofillManager&)>(); +} + +// Collects the FormGlobalIds of `forms`. +std::vector<FormGlobalId> GetFormGlobalIds(base::span<const FormData> forms) { + std::vector<FormGlobalId> form_ids; + form_ids.reserve(forms.size()); + for (const FormData& form : forms) { + form_ids.push_back(form.global_id()); + } + return form_ids; } // Returns the AutofillField* corresponding to |field| in |form| or nullptr, @@ -228,9 +261,9 @@ FillCreditCardFormImpl(form, field, credit_card, cvc); return; } - ParseFormAsync( - form, ParsingCallback(&AutofillManager::FillCreditCardFormImpl, - /*after_event=*/nullptr, field, credit_card, cvc)); + ParseFormAsync(form, ParsingCallback(&AutofillManager::FillCreditCardFormImpl, + field, credit_card, cvc) + .Then(NotifyNoObserversCallback())); } void AutofillManager::FillProfileForm(const AutofillProfile& profile, @@ -240,9 +273,9 @@ FillProfileFormImpl(form, field, profile); return; } - ParseFormAsync(form, - ParsingCallback(&AutofillManager::FillProfileFormImpl, - /*after_event=*/nullptr, field, profile)); + ParseFormAsync(form, ParsingCallback(&AutofillManager::FillProfileFormImpl, + field, profile) + .Then(NotifyNoObserversCallback())); } void AutofillManager::OnDidFillAutofillFormData( @@ -251,16 +284,19 @@ if (!IsValidFormData(form)) return; - NotifyObservers(&Observer::OnBeforeDidFillAutofillFormData); + NotifyObservers(&Observer::OnBeforeDidFillAutofillFormData, form.global_id()); if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { OnDidFillAutofillFormDataImpl(form, timestamp); - NotifyObservers(&Observer::OnAfterDidFillAutofillFormData); + NotifyObservers(&Observer::OnAfterDidFillAutofillFormData, + form.global_id()); return; } ParseFormAsync( form, ParsingCallback(&AutofillManager::OnDidFillAutofillFormDataImpl, - &Observer::OnAfterDidFillAutofillFormData, timestamp)); + timestamp) + .Then(NotifyObserversCallback( + &Observer::OnAfterDidFillAutofillFormData, form.global_id()))); } void AutofillManager::OnFormSubmitted(const FormData& form, @@ -270,10 +306,10 @@ return; } - NotifyObservers(&Observer::OnBeforeFormSubmitted); + NotifyObservers(&Observer::OnBeforeFormSubmitted, form.global_id()); NotifyObservers(&Observer::OnFormSubmitted); OnFormSubmittedImpl(form, known_success, source); - NotifyObservers(&Observer::OnAfterFormSubmitted); + NotifyObservers(&Observer::OnAfterFormSubmitted, form.global_id()); } void AutofillManager::OnFormsSeen( @@ -293,7 +329,8 @@ if (!ShouldParseForms(updated_forms)) return; - NotifyObservers(&Observer::OnBeforeFormsSeen); + NotifyObservers(&Observer::OnBeforeFormsSeen, + GetFormGlobalIds(updated_forms)); if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { std::vector<FormData> parsed_forms; for (const FormData& form : updated_forms) { @@ -325,7 +362,8 @@ } if (!parsed_forms.empty()) OnFormsParsed(parsed_forms); - NotifyObservers(&Observer::OnAfterFormsSeen); + NotifyObservers(&Observer::OnAfterFormsSeen, + GetFormGlobalIds(parsed_forms)); return; } DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync)); @@ -333,7 +371,8 @@ const std::vector<FormData>& parsed_forms) { if (!parsed_forms.empty()) self.OnFormsParsed(parsed_forms); - self.NotifyObservers(&Observer::OnAfterFormsSeen); + self.NotifyObservers(&Observer::OnAfterFormsSeen, + GetFormGlobalIds(parsed_forms)); }; ParseFormsAsync(updated_forms, base::BindOnce(ProcessParsedForms)); } @@ -403,17 +442,21 @@ if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - NotifyObservers(&Observer::OnBeforeTextFieldDidChange); + NotifyObservers(&Observer::OnBeforeTextFieldDidChange, form.global_id(), + field.global_id()); NotifyObservers(&Observer::OnTextFieldDidChange); if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { OnTextFieldDidChangeImpl(form, field, bounding_box, timestamp); - NotifyObservers(&Observer::OnAfterTextFieldDidChange); + NotifyObservers(&Observer::OnAfterTextFieldDidChange, form.global_id(), + field.global_id()); return; } - ParseFormAsync(form, - ParsingCallback(&AutofillManager::OnTextFieldDidChangeImpl, - &Observer::OnAfterTextFieldDidChange, field, - bounding_box, timestamp)); + ParseFormAsync( + form, + ParsingCallback(&AutofillManager::OnTextFieldDidChangeImpl, field, + bounding_box, timestamp) + .Then(NotifyObserversCallback(&Observer::OnAfterTextFieldDidChange, + form.global_id(), field.global_id()))); } void AutofillManager::OnTextFieldDidScroll(const FormData& form, @@ -427,9 +470,10 @@ OnTextFieldDidScrollImpl(form, field, bounding_box); return; } - ParseFormAsync(form, - ParsingCallback(&AutofillManager::OnTextFieldDidScrollImpl, - /*after_event=*/nullptr, field, bounding_box)); + ParseFormAsync( + form, ParsingCallback(&AutofillManager::OnTextFieldDidScrollImpl, field, + bounding_box) + .Then(NotifyNoObserversCallback())); } void AutofillManager::OnSelectControlDidChange(const FormData& form, @@ -445,7 +489,8 @@ } ParseFormAsync(form, ParsingCallback(&AutofillManager::OnSelectControlDidChangeImpl, - /*after_event=*/nullptr, field, bounding_box)); + field, bounding_box) + .Then(NotifyNoObserversCallback())); } void AutofillManager::OnAskForValuesToFill( @@ -457,7 +502,8 @@ if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - NotifyObservers(&Observer::OnBeforeAskForValuesToFill); + NotifyObservers(&Observer::OnBeforeAskForValuesToFill, form.global_id(), + field.global_id()); if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync) #if BUILDFLAG(IS_ANDROID) // TODO(crbug.com/1379149) Asynchronous parsing breaks Touch To Fill's @@ -470,14 +516,17 @@ OnAskForValuesToFillImpl(form, field, bounding_box, autoselect_first_suggestion, form_element_was_clicked); - NotifyObservers(&Observer::OnAfterAskForValuesToFill); + NotifyObservers(&Observer::OnAfterAskForValuesToFill, form.global_id(), + field.global_id()); return; } ParseFormAsync( form, - ParsingCallback(&AutofillManager::OnAskForValuesToFillImpl, - &Observer::OnAfterAskForValuesToFill, field, bounding_box, - autoselect_first_suggestion, form_element_was_clicked)); + ParsingCallback(&AutofillManager::OnAskForValuesToFillImpl, field, + bounding_box, autoselect_first_suggestion, + form_element_was_clicked) + .Then(NotifyObserversCallback(&Observer::OnAfterAskForValuesToFill, + form.global_id(), field.global_id()))); } void AutofillManager::OnFocusOnFormField(const FormData& form, @@ -490,9 +539,9 @@ OnFocusOnFormFieldImpl(form, field, bounding_box); return; } - ParseFormAsync(form, - ParsingCallback(&AutofillManager::OnFocusOnFormFieldImpl, - /*after_event=*/nullptr, field, bounding_box)); + ParseFormAsync(form, ParsingCallback(&AutofillManager::OnFocusOnFormFieldImpl, + field, bounding_box) + .Then(NotifyNoObserversCallback())); } void AutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) { @@ -520,8 +569,8 @@ return; } ParseFormAsync( - form, ParsingCallback(&AutofillManager::OnSelectFieldOptionsDidChangeImpl, - /*after_event=*/nullptr)); + form, ParsingCallback(&AutofillManager::OnSelectFieldOptionsDidChangeImpl) + .Then(NotifyNoObserversCallback())); } void AutofillManager::OnJavaScriptChangedAutofilledValue( @@ -531,17 +580,21 @@ if (!IsValidFormData(form)) return; - NotifyObservers(&Observer::OnBeforeJavaScriptChangedAutofilledValue); + NotifyObservers(&Observer::OnBeforeJavaScriptChangedAutofilledValue, + form.global_id(), field.global_id()); if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { OnJavaScriptChangedAutofilledValueImpl(form, field, old_value); - NotifyObservers(&Observer::OnAfterJavaScriptChangedAutofilledValue); + NotifyObservers(&Observer::OnAfterJavaScriptChangedAutofilledValue, + form.global_id(), field.global_id()); return; } ParseFormAsync( form, ParsingCallback(&AutofillManager::OnJavaScriptChangedAutofilledValueImpl, - &Observer::OnAfterJavaScriptChangedAutofilledValue, field, - old_value)); + field, old_value) + .Then(NotifyObserversCallback( + &Observer::OnAfterJavaScriptChangedAutofilledValue, + form.global_id(), field.global_id()))); } // Returns true if |live_form| does not match |cached_form|.
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 398368c..a85cea3c 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h
@@ -10,6 +10,7 @@ #include <string> #include <vector> +#include "base/containers/span.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" @@ -71,40 +72,60 @@ // needed. class Observer : public base::CheckedObserver { public: - virtual void OnAutofillManagerDestroyed() {} - virtual void OnAutofillManagerReset() {} + virtual void OnAutofillManagerDestroyed(AutofillManager& manager) {} + virtual void OnAutofillManagerReset(AutofillManager& manager) {} - virtual void OnBeforeLanguageDetermined() {} - virtual void OnAfterLanguageDetermined() {} + virtual void OnBeforeLanguageDetermined(AutofillManager& manager) {} + virtual void OnAfterLanguageDetermined(AutofillManager& manager) {} - virtual void OnBeforeFormsSeen() {} - virtual void OnAfterFormsSeen() {} + virtual void OnBeforeFormsSeen(AutofillManager& manager, + base::span<const FormGlobalId> forms) {} + virtual void OnAfterFormsSeen(AutofillManager& manager, + base::span<const FormGlobalId> forms) {} - virtual void OnBeforeTextFieldDidChange() {} - virtual void OnAfterTextFieldDidChange() {} + virtual void OnBeforeTextFieldDidChange(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} + virtual void OnAfterTextFieldDidChange(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} - virtual void OnBeforeDidFillAutofillFormData() {} - virtual void OnAfterDidFillAutofillFormData() {} + virtual void OnBeforeDidFillAutofillFormData(AutofillManager& manager, + FormGlobalId form) {} + virtual void OnAfterDidFillAutofillFormData(AutofillManager& manager, + FormGlobalId form) {} - virtual void OnBeforeAskForValuesToFill() {} - virtual void OnAfterAskForValuesToFill() {} + virtual void OnBeforeAskForValuesToFill(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} + virtual void OnAfterAskForValuesToFill(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} - virtual void OnBeforeJavaScriptChangedAutofilledValue() {} - virtual void OnAfterJavaScriptChangedAutofilledValue() {} + virtual void OnBeforeJavaScriptChangedAutofilledValue( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} + virtual void OnAfterJavaScriptChangedAutofilledValue( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) {} - virtual void OnBeforeFormSubmitted() {} - virtual void OnAfterFormSubmitted() {} + virtual void OnBeforeFormSubmitted(AutofillManager& manager, + FormGlobalId form) {} + virtual void OnAfterFormSubmitted(AutofillManager& manager, + FormGlobalId form) {} - virtual void OnBeforeLoadedServerPredictions() {} - virtual void OnAfterLoadedServerPredictions() {} + virtual void OnBeforeLoadedServerPredictions(AutofillManager& manager) {} + virtual void OnAfterLoadedServerPredictions(AutofillManager& manager) {} // TODO(crbug.com/1330105): Clean up API: delete the events that don't // follow the OnBeforeFoo() / OnAfterFoo() pattern. - virtual void OnFormParsed() {} - virtual void OnTextFieldDidChange() {} - virtual void OnTextFieldDidScroll() {} - virtual void OnSelectControlDidChange() {} - virtual void OnFormSubmitted() {} + virtual void OnFormParsed(AutofillManager& manager) {} + virtual void OnTextFieldDidChange(AutofillManager& manager) {} + virtual void OnTextFieldDidScroll(AutofillManager& manager) {} + virtual void OnSelectControlDidChange(AutofillManager& manager) {} + virtual void OnFormSubmitted(AutofillManager& manager) {} }; // TODO(crbug.com/1151542): Move to anonymous namespace once @@ -301,9 +322,11 @@ observers_.RemoveObserver(observer); } - void NotifyObservers(void (Observer::*event)()) { - for (Observer& observer : observers_) - base::invoke(event, observer); + template <typename Functor, typename... Args> + void NotifyObservers(const Functor& functor, const Args&... args) { + for (Observer& observer : observers_) { + base::invoke(functor, observer, *this, args...); + } } // Returns the present form structures seen by Autofill handler.
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 898b1e01a..4cc6f3f0 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -202,19 +202,25 @@ MockAutofillObserver& operator=(const MockAutofillObserver&) = delete; ~MockAutofillObserver() override = default; - MOCK_METHOD(void, OnFormParsed, (), (override)); + MOCK_METHOD(void, OnFormParsed, (AutofillManager&), (override)); - MOCK_METHOD(void, OnTextFieldDidChange, (), (override)); + MOCK_METHOD(void, OnTextFieldDidChange, (AutofillManager&), (override)); - MOCK_METHOD(void, OnTextFieldDidScroll, (), (override)); + MOCK_METHOD(void, OnTextFieldDidScroll, (AutofillManager&), (override)); - MOCK_METHOD(void, OnSelectControlDidChange, (), (override)); + MOCK_METHOD(void, OnSelectControlDidChange, (AutofillManager&), (override)); - MOCK_METHOD(void, OnFormSubmitted, (), (override)); + MOCK_METHOD(void, OnFormSubmitted, (AutofillManager&), (override)); - MOCK_METHOD(void, OnBeforeLoadedServerPredictions, (), (override)); + MOCK_METHOD(void, + OnBeforeLoadedServerPredictions, + (AutofillManager&), + (override)); - MOCK_METHOD(void, OnAfterLoadedServerPredictions, (), (override)); + MOCK_METHOD(void, + OnAfterLoadedServerPredictions, + (AutofillManager&), + (override)); }; // Creates a vector of test forms which differ in their FormGlobalIds @@ -261,12 +267,11 @@ size_t num = std::min(updated_forms.size(), kAutofillManagerMaxFormCacheSize - manager.form_structures().size()); - EXPECT_CALL(manager, ShouldParseForms(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(manager, ShouldParseForms).Times(1).WillOnce(Return(true)); EXPECT_CALL(manager, OnBeforeProcessParsedForms()).Times(num > 0); - EXPECT_CALL(manager, OnFormProcessed(_, _)).Times(num); - EXPECT_CALL(manager, OnAfterProcessParsedForms(_)).Times(num > 0); - TestAutofillManagerWaiter waiter( - manager, {&AutofillManager::Observer::OnAfterFormsSeen}); + EXPECT_CALL(manager, OnFormProcessed).Times(num); + EXPECT_CALL(manager, OnAfterProcessParsedForms).Times(num > 0); + TestAutofillManagerWaiter waiter(manager, {AutofillManagerEvent::kFormsSeen}); manager.OnFormsSeen(updated_forms, removed_forms); ASSERT_TRUE(waiter.Wait()); EXPECT_THAT(manager.form_structures(), HaveSameFormIdsAs(expectation)); @@ -387,26 +392,27 @@ // Reset the manager, the observers should stick around. manager_->Reset(); - EXPECT_CALL(observer, OnTextFieldDidChange()).Times(1); + EXPECT_CALL(observer, OnTextFieldDidChange(testing::Address(manager_.get()))); manager_->OnTextFieldDidChange(form, field, bounds, time); - EXPECT_CALL(observer, OnTextFieldDidChange()).Times(0); + EXPECT_CALL(observer, OnTextFieldDidChange).Times(0); - EXPECT_CALL(observer, OnTextFieldDidScroll()).Times(1); + EXPECT_CALL(observer, OnTextFieldDidScroll(testing::Address(manager_.get()))); manager_->OnTextFieldDidScroll(form, field, bounds); - EXPECT_CALL(observer, OnTextFieldDidScroll()).Times(0); + EXPECT_CALL(observer, OnTextFieldDidScroll).Times(0); - EXPECT_CALL(observer, OnSelectControlDidChange()).Times(1); + EXPECT_CALL(observer, + OnSelectControlDidChange(testing::Address(manager_.get()))); manager_->OnSelectControlDidChange(form, field, bounds); - EXPECT_CALL(observer, OnSelectControlDidChange()).Times(0); + EXPECT_CALL(observer, OnSelectControlDidChange).Times(0); - EXPECT_CALL(observer, OnFormSubmitted()).Times(1); + EXPECT_CALL(observer, OnFormSubmitted(testing::Address(manager_.get()))); manager_->OnFormSubmitted(form, true, mojom::SubmissionSource::FORM_SUBMISSION); - EXPECT_CALL(observer, OnFormSubmitted()).Times(0); + EXPECT_CALL(observer, OnFormSubmitted).Times(0); // Remove observer from manager, the observer should no longer receive pings. manager_->RemoveObserver(&observer); - EXPECT_CALL(observer, OnTextFieldDidChange()).Times(0); + EXPECT_CALL(observer, OnTextFieldDidChange).Times(0); manager_->OnTextFieldDidChange(form, field, bounds, time); } @@ -431,8 +437,9 @@ SetUpObserverAndDownloadManager(/*successful_request=*/true); std::vector<FormData> forms = CreateTestForms(1); - EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions()); - EXPECT_CALL(observer_, OnAfterLoadedServerPredictions()).Times(0); + EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions( + testing::Address(manager_.get()))); + EXPECT_CALL(observer_, OnAfterLoadedServerPredictions).Times(0); OnFormsSeenWithExpectations(*manager_, forms, {}, forms); task_environment_.RunUntilIdle(); @@ -445,8 +452,10 @@ SetUpObserverAndDownloadManager(/*successful_request=*/false); std::vector<FormData> forms = CreateTestForms(1); - EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions()); - EXPECT_CALL(observer_, OnAfterLoadedServerPredictions()); + EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions( + testing::Address(manager_.get()))); + EXPECT_CALL(observer_, + OnAfterLoadedServerPredictions(testing::Address(manager_.get()))); OnFormsSeenWithExpectations(*manager_, forms, {}, forms); task_environment_.RunUntilIdle(); @@ -459,11 +468,13 @@ SetUpObserverAndDownloadManager(/*successful_request=*/true); std::vector<FormData> forms = CreateTestForms(1); - EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions()); + EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions( + testing::Address(manager_.get()))); OnFormsSeenWithExpectations(*manager_, forms, {}, forms); task_environment_.RunUntilIdle(); - EXPECT_CALL(observer_, OnAfterLoadedServerPredictions()); + EXPECT_CALL(observer_, + OnAfterLoadedServerPredictions(testing::Address(manager_.get()))); manager_->OnLoadedServerPredictionsForTest("", {}); manager_->RemoveObserver(&observer_); @@ -475,11 +486,13 @@ SetUpObserverAndDownloadManager(/*successful_request=*/true); std::vector<FormData> forms = CreateTestForms(1); - EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions()); + EXPECT_CALL(observer_, OnBeforeLoadedServerPredictions( + testing::Address(manager_.get()))); OnFormsSeenWithExpectations(*manager_, forms, {}, forms); task_environment_.RunUntilIdle(); - EXPECT_CALL(observer_, OnAfterLoadedServerPredictions()); + EXPECT_CALL(observer_, + OnAfterLoadedServerPredictions(testing::Address(manager_.get()))); manager_->OnLoadedServerPredictionsForTest( "", {manager_->FindCachedFormById(forms[0].global_id())->form_signature()});
diff --git a/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.cc b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.cc new file mode 100644 index 0000000..7e238fa --- /dev/null +++ b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.cc
@@ -0,0 +1,22 @@ +// 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. + +#include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h" + +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" + +namespace autofill::autofill_metrics { + +void LogStoredVirtualCardUsageCount(const size_t usage_data_size) { + base::UmaHistogramCounts1000( + "Autofill.VirtualCardUsageData.StoredUsageDataCount", usage_data_size); +} + +void LogSyncedVirtualCardUsageDataBeingValid(bool valid) { + base::UmaHistogramBoolean( + "Autofill.VirtualCardUsageData.SyncedUsageDataBeingValid", valid); +} + +} // namespace autofill::autofill_metrics \ No newline at end of file
diff --git a/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h new file mode 100644 index 0000000..9e9b5db7b --- /dev/null +++ b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h
@@ -0,0 +1,25 @@ +// 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_WALLET_USAGE_DATA_METRICS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_WALLET_USAGE_DATA_METRICS_H_ + +#include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h" +#include "components/autofill/core/browser/metrics/autofill_metrics.h" + +class VirtualCardUsageData; + +namespace autofill::autofill_metrics { + +// Logs metrics about the virtual card usage data associated with a Chrome +// profile. This should be called each time a Chrome profile is launched. +void LogStoredVirtualCardUsageCount(size_t usage_data_size); + +// Logs whether a synced virtual card usage data is valid. Checked for every +// synced virtual card usage data. +void LogSyncedVirtualCardUsageDataBeingValid(bool invalid); + +} // namespace autofill::autofill_metrics + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_WALLET_USAGE_DATA_METRICS_H_ \ No newline at end of file
diff --git a/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics_unittest.cc b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics_unittest.cc new file mode 100644 index 0000000..a73033d8 --- /dev/null +++ b/components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics_unittest.cc
@@ -0,0 +1,44 @@ +// 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. + +#include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h" +#include "base/test/metrics/histogram_tester.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h" +#include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill::autofill_metrics { + +class WalletUsageDataMetricsTest : public metrics::AutofillMetricsBaseTest, + public testing::Test { + public: + void SetUp() override { SetUpHelper(); } + + void TearDown() override { TearDownHelper(); } +}; + +// Tests that we correctly log the number of stored virtual card usage data. +TEST_F(WalletUsageDataMetricsTest, LogStoredVirtualCardUsageMetrics) { + std::vector<std::unique_ptr<VirtualCardUsageData>> virtual_card_usage_data; + VirtualCardUsageData virtual_card_usage_data1 = + test::GetVirtualCardUsageData1(); + VirtualCardUsageData virtual_card_usage_data2 = + test::GetVirtualCardUsageData2(); + virtual_card_usage_data.push_back( + std::make_unique<VirtualCardUsageData>(virtual_card_usage_data1)); + virtual_card_usage_data.push_back( + std::make_unique<VirtualCardUsageData>(virtual_card_usage_data2)); + + base::HistogramTester histogram_tester; + autofill_metrics::LogStoredVirtualCardUsageCount( + virtual_card_usage_data.size()); + + // Validate the count metrics. + histogram_tester.ExpectBucketCount( + "Autofill.VirtualCardUsageData.StoredUsageDataCount", 2, 1); +} + +} // namespace autofill::autofill_metrics \ No newline at end of file
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index cba2393..dad9b34 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -48,6 +48,7 @@ #include "components/autofill/core/browser/metrics/autofill_metrics.h" #include "components/autofill/core/browser/metrics/payments/iban_metrics.h" #include "components/autofill/core/browser/metrics/payments/offers_metrics.h" +#include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h" #include "components/autofill/core/browser/metrics/stored_profile_metrics.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" #include "components/autofill/core/browser/strike_databases/autofill_profile_migration_strike_database.h" @@ -2151,6 +2152,16 @@ } } +void PersonalDataManager::LogStoredVirtualCardUsageMetrics() const { + if (!has_logged_stored_virtual_card_usage_metrics_) { + autofill_metrics::LogStoredVirtualCardUsageCount( + autofill_virtual_card_usage_data_.size()); + + // Only log this info once per chrome user profile load. + has_logged_stored_virtual_card_usage_metrics_ = true; + } +} + std::string PersonalDataManager::MostCommonCountryCodeFromProfiles() const { if (!IsAutofillEnabled()) return std::string();
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h index c4259aa..adba601 100644 --- a/components/autofill/core/browser/personal_data_manager.h +++ b/components/autofill/core/browser/personal_data_manager.h
@@ -713,6 +713,10 @@ // offer data. On subsequent calls, does nothing. void LogStoredOfferMetrics() const; + // The first time this is called, logs UMA metrics about the users's autofill + // virtual card usage data. On subsequent calls, does nothing. + void LogStoredVirtualCardUsageMetrics() const; + // Whether the server cards are enabled and should be suggested to the user. virtual bool ShouldSuggestServerCards() const; @@ -974,6 +978,10 @@ // Whether we have already logged the stored offer metrics this session. mutable bool has_logged_stored_offer_metrics_ = false; + // Whether we have already logged the stored virtual card usage metrics this + // session. + mutable bool has_logged_stored_virtual_card_usage_metrics_ = false; + // An observer to listen for changes to prefs::kAutofillCreditCardEnabled. std::unique_ptr<BooleanPrefMember> credit_card_enabled_pref_;
diff --git a/components/autofill/core/browser/personal_data_manager_cleaner.cc b/components/autofill/core/browser/personal_data_manager_cleaner.cc index 35f8fa06..50e0315 100644 --- a/components/autofill/core/browser/personal_data_manager_cleaner.cc +++ b/components/autofill/core/browser/personal_data_manager_cleaner.cc
@@ -65,11 +65,12 @@ if (!personal_data_manager_->IsSyncEnabledFor(syncer::AUTOFILL_WALLET_DATA)) ApplyCardFixesAndCleanups(); - // Log address, credit card and offer startup metrics. + // Log address, credit card, offer, and usage data startup metrics. personal_data_manager_->LogStoredProfileMetrics(); personal_data_manager_->LogStoredCreditCardMetrics(); personal_data_manager_->LogStoredIbanMetrics(); personal_data_manager_->LogStoredOfferMetrics(); + personal_data_manager_->LogStoredVirtualCardUsageMetrics(); personal_data_manager_->NotifyPersonalDataObserver(); }
diff --git a/components/autofill/core/browser/test_autofill_manager_waiter.cc b/components/autofill/core/browser/test_autofill_manager_waiter.cc index 99b7910..6dfce1d 100644 --- a/components/autofill/core/browser/test_autofill_manager_waiter.cc +++ b/components/autofill/core/browser/test_autofill_manager_waiter.cc
@@ -18,116 +18,152 @@ TestAutofillManagerWaiter::State::~State() = default; TestAutofillManagerWaiter::EventCount* TestAutofillManagerWaiter::State::Get( - AfterEvent event) { - auto it = base::ranges::find(events, event, &EventCount::event); - return it != events.end() ? &*it : nullptr; + Event event) { + auto it = events.find(event); + return it != events.end() ? &it->second : nullptr; } TestAutofillManagerWaiter::EventCount& -TestAutofillManagerWaiter::State::GetOrCreate(AfterEvent event, +TestAutofillManagerWaiter::State::GetOrCreate(Event event, base::Location location) { - if (EventCount* e = Get(event)) + if (EventCount* e = Get(event)) { return *e; - return *events.insert(events.end(), {event, location}); + } + EventCount& e = events[event]; + e = EventCount{.location = location}; + return e; } size_t TestAutofillManagerWaiter::State::num_pending_calls() const { size_t pending_calls = 0; - for (const EventCount& e : events) - pending_calls += e.num_pending_calls; + for (const auto& [_, event_count] : events) { + pending_calls += event_count.num_pending_calls; + } return pending_calls; } size_t TestAutofillManagerWaiter::State::num_total_calls() const { size_t total_calls = 0; - for (const EventCount& e : events) - total_calls += e.num_total_calls; + for (const auto& [_, event_count] : events) { + total_calls += event_count.num_total_calls; + } return total_calls; } std::string TestAutofillManagerWaiter::State::Describe() const { std::vector<std::string> strings; - for (const auto& e : events) { - strings.push_back(base::StringPrintf( - "[event=%s, pending=%zu, total=%zu]", e.location.function_name(), - e.num_pending_calls, e.num_total_calls)); + for (const auto& [_, event_count] : events) { + strings.push_back(base::StringPrintf("[event=%s, pending=%zu, total=%zu]", + event_count.location.function_name(), + event_count.num_pending_calls, + event_count.num_total_calls)); } return base::JoinString(strings, ", "); } TestAutofillManagerWaiter::TestAutofillManagerWaiter( AutofillManager& manager, - std::initializer_list<AfterEvent> relevant_events) + std::initializer_list<Event> relevant_events) : relevant_events_(relevant_events) { observation_.Observe(&manager); } TestAutofillManagerWaiter::~TestAutofillManagerWaiter() = default; -void TestAutofillManagerWaiter::OnAutofillManagerDestroyed() { +void TestAutofillManagerWaiter::OnAutofillManagerDestroyed( + AutofillManager& manager) { observation_.Reset(); } -void TestAutofillManagerWaiter::OnAutofillManagerReset() { +void TestAutofillManagerWaiter::OnAutofillManagerReset( + AutofillManager& manager) { Reset(); } -void TestAutofillManagerWaiter::OnBeforeLanguageDetermined() { - Increment(&AutofillManager::Observer::OnAfterLanguageDetermined); +void TestAutofillManagerWaiter::OnBeforeLanguageDetermined( + AutofillManager& manager) { + Increment(Event::kLanguageDetermined); } -void TestAutofillManagerWaiter::OnAfterLanguageDetermined() { - Decrement(&AutofillManager::Observer::OnAfterLanguageDetermined); +void TestAutofillManagerWaiter::OnAfterLanguageDetermined( + AutofillManager& manager) { + Decrement(Event::kLanguageDetermined); } -void TestAutofillManagerWaiter::OnBeforeFormsSeen() { - Increment(&AutofillManager::Observer::OnAfterFormsSeen); +void TestAutofillManagerWaiter::OnBeforeFormsSeen( + AutofillManager& manager, + base::span<const FormGlobalId> forms) { + Increment(Event::kFormsSeen); } -void TestAutofillManagerWaiter::OnAfterFormsSeen() { - Decrement(&AutofillManager::Observer::OnAfterFormsSeen); +void TestAutofillManagerWaiter::OnAfterFormsSeen( + AutofillManager& manager, + base::span<const FormGlobalId> forms) { + Decrement(Event::kFormsSeen); } -void TestAutofillManagerWaiter::OnBeforeTextFieldDidChange() { - Increment(&AutofillManager::Observer::OnAfterTextFieldDidChange); +void TestAutofillManagerWaiter::OnBeforeTextFieldDidChange( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Increment(Event::kTextFieldDidChange); } -void TestAutofillManagerWaiter::OnAfterTextFieldDidChange() { - Decrement(&AutofillManager::Observer::OnAfterTextFieldDidChange); +void TestAutofillManagerWaiter::OnAfterTextFieldDidChange( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Decrement(Event::kTextFieldDidChange); } -void TestAutofillManagerWaiter::OnBeforeAskForValuesToFill() { - Increment(&AutofillManager::Observer::OnAfterAskForValuesToFill); +void TestAutofillManagerWaiter::OnBeforeAskForValuesToFill( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Increment(Event::kAskForValuesToFill); } -void TestAutofillManagerWaiter::OnAfterAskForValuesToFill() { - Decrement(&AutofillManager::Observer::OnAfterAskForValuesToFill); +void TestAutofillManagerWaiter::OnAfterAskForValuesToFill( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Decrement(Event::kAskForValuesToFill); } -void TestAutofillManagerWaiter::OnBeforeDidFillAutofillFormData() { - Increment(&AutofillManager::Observer::OnAfterDidFillAutofillFormData); +void TestAutofillManagerWaiter::OnBeforeDidFillAutofillFormData( + AutofillManager& manager, + FormGlobalId form) { + Increment(Event::kDidFillAutofillFormData); } -void TestAutofillManagerWaiter::OnAfterDidFillAutofillFormData() { - Decrement(&AutofillManager::Observer::OnAfterDidFillAutofillFormData); +void TestAutofillManagerWaiter::OnAfterDidFillAutofillFormData( + AutofillManager& manager, + FormGlobalId form) { + Decrement(Event::kDidFillAutofillFormData); } -void TestAutofillManagerWaiter::OnBeforeJavaScriptChangedAutofilledValue() { - Increment( - &AutofillManager::Observer::OnAfterJavaScriptChangedAutofilledValue); +void TestAutofillManagerWaiter::OnBeforeJavaScriptChangedAutofilledValue( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Increment(Event::kJavaScriptChangedAutofilledValue); } -void TestAutofillManagerWaiter::OnAfterJavaScriptChangedAutofilledValue() { - Decrement( - &AutofillManager::Observer::OnAfterJavaScriptChangedAutofilledValue); +void TestAutofillManagerWaiter::OnAfterJavaScriptChangedAutofilledValue( + AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) { + Decrement(Event::kJavaScriptChangedAutofilledValue); } -void TestAutofillManagerWaiter::OnBeforeFormSubmitted() { - Increment(&AutofillManager::Observer::OnAfterFormSubmitted); +void TestAutofillManagerWaiter::OnBeforeFormSubmitted(AutofillManager& manager, + FormGlobalId form) { + Increment(Event::kFormSubmitted); } -void TestAutofillManagerWaiter::OnAfterFormSubmitted() { - Decrement(&AutofillManager::Observer::OnAfterFormSubmitted); +void TestAutofillManagerWaiter::OnAfterFormSubmitted(AutofillManager& manager, + FormGlobalId form) { + Decrement(Event::kFormSubmitted); } void TestAutofillManagerWaiter::Reset() { @@ -141,11 +177,11 @@ swap(state_, state); } -bool TestAutofillManagerWaiter::IsRelevant(AfterEvent event) const { +bool TestAutofillManagerWaiter::IsRelevant(Event event) const { return relevant_events_.empty() || base::Contains(relevant_events_, event); } -void TestAutofillManagerWaiter::Increment(AfterEvent event, +void TestAutofillManagerWaiter::Increment(Event event, base::Location location) { base::AutoLock lock(state_->lock); if (!IsRelevant(event)) { @@ -165,7 +201,7 @@ ++e.num_pending_calls; } -void TestAutofillManagerWaiter::Decrement(AfterEvent event, +void TestAutofillManagerWaiter::Decrement(Event event, base::Location location) { base::AutoLock lock(state_->lock); if (!IsRelevant(event)) { @@ -251,17 +287,21 @@ } private: - void OnAutofillManagerDestroyed() override { + void OnAutofillManagerDestroyed(AutofillManager& manager) override { + DCHECK_EQ(&manager, &manager_); run_loop_.Quit(); observation_.Reset(); } - void OnAutofillManagerReset() override { + void OnAutofillManagerReset(AutofillManager& manager) override { + DCHECK_EQ(&manager, &manager_); run_loop_.Quit(); observation_.Reset(); } - void OnAfterFormsSeen() override { + void OnAfterFormsSeen(AutofillManager& manager, + base::span<const FormGlobalId> forms) override { + DCHECK_EQ(&manager, &manager_); if (const auto* form = FindForm()) { matching_form_ = form; run_loop_.Quit();
diff --git a/components/autofill/core/browser/test_autofill_manager_waiter.h b/components/autofill/core/browser/test_autofill_manager_waiter.h index 233f68cd..d9dc6876 100644 --- a/components/autofill/core/browser/test_autofill_manager_waiter.h +++ b/components/autofill/core/browser/test_autofill_manager_waiter.h
@@ -5,19 +5,34 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_WAITER_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_WAITER_H_ -#include <list> +#include <map> #include <memory> #include <vector> +#include "base/containers/span.h" #include "base/run_loop.h" #include "base/scoped_observation.h" #include "base/synchronization/lock.h" #include "base/time/time.h" +#include "base/types/cxx23_to_underlying.h" #include "components/autofill/core/browser/autofill_manager.h" +#include "components/autofill/core/common/unique_ids.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { +// One constant `kFoo` for each event +// `AutofillManager::Observer::On{Before,After}Foo()`. +enum class AutofillManagerEvent { + kLanguageDetermined, + kFormsSeen, + kTextFieldDidChange, + kAskForValuesToFill, + kDidFillAutofillFormData, + kJavaScriptChangedAutofilledValue, + kFormSubmitted, +}; + // Records AutofillManager::Observer::OnBeforeFoo() events and blocks until the // corresponding OnAfterFoo() events have happened. // @@ -43,8 +58,8 @@ // Typical usage is as follows: // // TestAutofillManagerWaiter waiter(manager, -// {&AutofillManager::Observer::OnAfterFoo, -// &AutofillManager::Observer::OnAfterBar, +// {AutofillManagerEvent::kFoo, +// AutofillManagerEvent::kBar, // ...}); // ... trigger events ... // ASSERT_TRUE(waiter.Wait()); // Blocks. @@ -53,7 +68,7 @@ // say, 1 event because triggering events is asynchronous due to Mojo: // // TestAutofillManagerWaiter waiter(manager, -// {&AutofillManager::Observer::OnAfterFoo, +// {AutofillManagerEvent::kFoo, // ...}); // ... trigger asynchronous OnFoo event ... // ASSERT_TRUE(waiter.Wait(1)); // Blocks until at least one OnFoo() event @@ -64,13 +79,11 @@ // OnAfterFoo() calls. class TestAutofillManagerWaiter : public AutofillManager::Observer { public: - // An OnFooAfter() event. As a convention, throughout this class we use the - // OnAfterFoo() events to identify the pair of OnAfterFoo() / OnBeforeFoo(). - using AfterEvent = void (AutofillManager::Observer::*)(); + using Event = AutofillManagerEvent; explicit TestAutofillManagerWaiter( AutofillManager& manager, - std::initializer_list<AfterEvent> relevant_events = {}); + std::initializer_list<Event> relevant_events = {}); TestAutofillManagerWaiter(const TestAutofillManagerWaiter&) = delete; TestAutofillManagerWaiter& operator=(const TestAutofillManagerWaiter&) = delete; @@ -91,8 +104,6 @@ private: struct EventCount { - // An AutofillManager::Observer::OnAfterFoo() event. - AfterEvent event; // The OnBeforeFoo() function. Used for meaningful error messages. base::Location location; // The total number of recorded OnBeforeFoo() events. @@ -109,18 +120,17 @@ State& operator=(State&) = delete; ~State(); - EventCount& GetOrCreate(AfterEvent event, base::Location location); - EventCount* Get(AfterEvent event); + EventCount& GetOrCreate(Event event, base::Location location); + EventCount* Get(Event event); size_t num_total_calls() const; size_t num_pending_calls() const; std::string Describe() const; - // Effectively a map from `AfterEvent` to its count. Since `AfterEvent` is - // only equality-comparable, we use a list. (The list, rather than a vector, - // avoids invalidation of the references returned by GetOrCreate().) - std::list<EventCount> events; + // The std::map guarantees that references aren't invalidated by + // GetOrCreate(). + std::map<Event, EventCount> events; // Decrement() unblocks Wait() when the number of awaited calls reaches 0. size_t num_awaiting_total_calls = std::numeric_limits<size_t>::max(); // Running iff there are no awaited and no pending calls. @@ -131,37 +141,55 @@ base::Lock lock; }; - bool IsRelevant(AfterEvent event) const; - void Increment(AfterEvent event, + bool IsRelevant(Event event) const; + void Increment(Event event, base::Location location = base::Location::Current()); - void Decrement(AfterEvent event, + void Decrement(Event event, base::Location location = base::Location::Current()); - void OnAutofillManagerDestroyed() override; - void OnAutofillManagerReset() override; + void OnAutofillManagerDestroyed(AutofillManager& manager) override; + void OnAutofillManagerReset(AutofillManager& manager) override; - void OnBeforeLanguageDetermined() override; - void OnAfterLanguageDetermined() override; + void OnBeforeLanguageDetermined(AutofillManager& manager) override; + void OnAfterLanguageDetermined(AutofillManager& manager) override; - void OnBeforeFormsSeen() override; - void OnAfterFormsSeen() override; + void OnBeforeFormsSeen(AutofillManager& manager, + base::span<const FormGlobalId> forms) override; + void OnAfterFormsSeen(AutofillManager& manager, + base::span<const FormGlobalId> forms) override; - void OnBeforeTextFieldDidChange() override; - void OnAfterTextFieldDidChange() override; + void OnBeforeTextFieldDidChange(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; + void OnAfterTextFieldDidChange(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; - void OnBeforeAskForValuesToFill() override; - void OnAfterAskForValuesToFill() override; + void OnBeforeAskForValuesToFill(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; + void OnAfterAskForValuesToFill(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; - void OnBeforeDidFillAutofillFormData() override; - void OnAfterDidFillAutofillFormData() override; + void OnBeforeDidFillAutofillFormData(AutofillManager& manager, + FormGlobalId form) override; + void OnAfterDidFillAutofillFormData(AutofillManager& manager, + FormGlobalId form) override; - void OnBeforeJavaScriptChangedAutofilledValue() override; - void OnAfterJavaScriptChangedAutofilledValue() override; + void OnBeforeJavaScriptChangedAutofilledValue(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; + void OnAfterJavaScriptChangedAutofilledValue(AutofillManager& manager, + FormGlobalId form, + FieldGlobalId field) override; - void OnBeforeFormSubmitted() override; - void OnAfterFormSubmitted() override; + void OnBeforeFormSubmitted(AutofillManager& manager, + FormGlobalId form) override; + void OnAfterFormSubmitted(AutofillManager& manager, + FormGlobalId form) override; - std::vector<AfterEvent> relevant_events_; + std::vector<Event> relevant_events_; std::unique_ptr<State> state_ = std::make_unique<State>(); base::TimeDelta timeout_ = base::Seconds(30); base::ScopedObservation<AutofillManager, AutofillManager::Observer>
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.cc b/components/autofill/core/browser/test_browser_autofill_manager.cc index d9d92856..1077e422 100644 --- a/components/autofill/core/browser/test_browser_autofill_manager.cc +++ b/components/autofill/core/browser/test_browser_autofill_manager.cc
@@ -42,8 +42,8 @@ void TestBrowserAutofillManager::OnLanguageDetermined( const translate::LanguageDetectionDetails& details) { - TestAutofillManagerWaiter waiter( - *this, {&AutofillManager::Observer::OnAfterLanguageDetermined}); + TestAutofillManagerWaiter waiter(*this, + {AutofillManagerEvent::kLanguageDetermined}); AutofillManager::OnLanguageDetermined(details); ASSERT_TRUE(waiter.Wait()); } @@ -51,7 +51,7 @@ void TestBrowserAutofillManager::OnFormsSeen( const std::vector<FormData>& updated_forms, const std::vector<FormGlobalId>& removed_forms) { - TestAutofillManagerWaiter waiter(*this, {&Observer::OnAfterFormsSeen}); + TestAutofillManagerWaiter waiter(*this, {AutofillManagerEvent::kFormsSeen}); AutofillManager::OnFormsSeen(updated_forms, removed_forms); ASSERT_TRUE(waiter.Wait()); } @@ -62,7 +62,7 @@ const gfx::RectF& bounding_box, const base::TimeTicks timestamp) { TestAutofillManagerWaiter waiter(*this, - {&Observer::OnAfterTextFieldDidChange}); + {AutofillManagerEvent::kTextFieldDidChange}); AutofillManager::OnTextFieldDidChange(form, field, bounding_box, timestamp); ASSERT_TRUE(waiter.Wait()); } @@ -70,8 +70,8 @@ void TestBrowserAutofillManager::OnDidFillAutofillFormData( const FormData& form, const base::TimeTicks timestamp) { - TestAutofillManagerWaiter waiter(*this, - {&Observer::OnAfterDidFillAutofillFormData}); + TestAutofillManagerWaiter waiter( + *this, {AutofillManagerEvent::kDidFillAutofillFormData}); AutofillManager::OnDidFillAutofillFormData(form, timestamp); ASSERT_TRUE(waiter.Wait()); } @@ -83,7 +83,7 @@ AutoselectFirstSuggestion autoselect_first_suggestion, FormElementWasClicked form_element_was_clicked) { TestAutofillManagerWaiter waiter(*this, - {&Observer::OnAfterAskForValuesToFill}); + {AutofillManagerEvent::kAskForValuesToFill}); AutofillManager::OnAskForValuesToFill(form, field, bounding_box, autoselect_first_suggestion, form_element_was_clicked); @@ -95,7 +95,7 @@ const FormFieldData& field, const std::u16string& old_value) { TestAutofillManagerWaiter waiter( - *this, {&Observer::OnAfterJavaScriptChangedAutofilledValue}); + *this, {AutofillManagerEvent::kJavaScriptChangedAutofilledValue}); AutofillManager::OnJavaScriptChangedAutofilledValue(form, field, old_value); ASSERT_TRUE(waiter.Wait()); } @@ -104,7 +104,7 @@ const FormData& form, const bool known_success, const mojom::SubmissionSource source) { - TestAutofillManagerWaiter waiter(*this, {&Observer::OnAfterFormsSeen}); + TestAutofillManagerWaiter waiter(*this, {AutofillManagerEvent::kFormsSeen}); AutofillManager::OnFormSubmitted(form, known_success, source); ASSERT_TRUE(waiter.Wait()); } @@ -256,8 +256,8 @@ const gfx::RectF& bounding_box, AutoselectFirstSuggestion autoselect_first_suggestion, FormElementWasClicked form_element_was_clicked) { - TestAutofillManagerWaiter waiter( - *this, {&AutofillManager::Observer::OnAfterAskForValuesToFill}); + TestAutofillManagerWaiter waiter(*this, + {AutofillManagerEvent::kAskForValuesToFill}); BrowserAutofillManager::OnAskForValuesToFill(form, field, bounding_box, autoselect_first_suggestion, form_element_was_clicked);
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc index 42fa4b03..7d779f8 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc +++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge.cc
@@ -9,6 +9,7 @@ #include "base/ranges/algorithm.h" #include "base/strings/string_util.h" #include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h" +#include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h" #include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" @@ -103,6 +104,14 @@ // TODO(crbug.com/1412207): AddOrUpdate VirtualCardUsageData method for // Autofill Table DCHECK(IsEntityDataValid(change->data())); + bool valid_data = IsVirtualCardUsageDataSpecificsValid( + change->data() + .specifics.autofill_wallet_usage() + .virtual_card_usage_data()); + autofill_metrics::LogSyncedVirtualCardUsageDataBeingValid(valid_data); + if (!valid_data) { + continue; + } VirtualCardUsageData remote = VirtualCardUsageDataFromUsageSpecifics( change->data().specifics.autofill_wallet_usage()); if (table && table->GetVirtualCardUsageData(change->storage_key())) { @@ -117,7 +126,6 @@ FROM_HERE, "Failed to add virtual card usage data in table."); } } - break; } } }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc index dfdff125..d10070f3 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc +++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
@@ -13,6 +13,7 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/test/bind.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/geo/country_names.h" @@ -306,4 +307,31 @@ EXPECT_FALSE(GetVirtualCardUsageDataFromTable().empty()); } +// Test to ensure whether the data being valid is logged correctly. +TEST_F(AutofillWalletUsageDataSyncBridgeTest, ApplySyncData_LogDataValidity) { + VirtualCardUsageData virtual_card_usage_data1 = + test::GetVirtualCardUsageData1(); + + // AutofillWalletUsageSpecifics with missing fields. + AutofillWalletUsageSpecifics specifics; + specifics.set_guid("guid"); + specifics.mutable_virtual_card_usage_data()->set_instrument_id(1234); + + syncer::EntityChangeList entity_change_list; + entity_change_list.push_back(syncer::EntityChange::CreateAdd( + *virtual_card_usage_data1.usage_data_id(), + VirtualCardUsageDataToEntity(virtual_card_usage_data1))); + entity_change_list.push_back(syncer::EntityChange::CreateAdd( + specifics.guid(), SpecificsToEntity(specifics))); + + EXPECT_CALL(backend(), CommitChanges()); + base::HistogramTester histogram_tester; + bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(), + std::move(entity_change_list)); + histogram_tester.ExpectBucketCount( + "Autofill.VirtualCardUsageData.SyncedUsageDataBeingValid", true, 1); + histogram_tester.ExpectBucketCount( + "Autofill.VirtualCardUsageData.SyncedUsageDataBeingValid", false, 1); +} + } // namespace autofill
diff --git a/components/commerce/core/mock_shopping_service.cc b/components/commerce/core/mock_shopping_service.cc index 5828438..4c8c6c2b 100644 --- a/components/commerce/core/mock_shopping_service.cc +++ b/components/commerce/core/mock_shopping_service.cc
@@ -31,6 +31,8 @@ SetResponseForGetMerchantInfoForUrl(absl::nullopt); SetSubscribeCallbackValue(true); SetUnsubscribeCallbackValue(true); + SetIsSubscribedCallbackValue(true); + SetGetAllSubscriptionsCallbackValue(std::vector<CommerceSubscription>()); SetIsShoppingListEligible(true); SetIsClusterIdTrackedByUserResponse(true); SetIsMerchantViewerEnabled(true); @@ -108,6 +110,29 @@ }); } +void MockShoppingService::SetIsSubscribedCallbackValue(bool is_subscribed) { + ON_CALL(*this, IsSubscribed) + .WillByDefault([is_subscribed](CommerceSubscription subscription, + base::OnceCallback<void(bool)> callback) { + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), is_subscribed)); + }); + ON_CALL(*this, IsSubscribedFromCache) + .WillByDefault(testing::Return(is_subscribed)); +} + +void MockShoppingService::SetGetAllSubscriptionsCallbackValue( + std::vector<CommerceSubscription> subscriptions) { + ON_CALL(*this, GetAllSubscriptions) + .WillByDefault([subs = std::move(subscriptions)]( + SubscriptionType type, + base::OnceCallback<void( + std::vector<CommerceSubscription>)> callback) { + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), std::move(subs))); + }); +} + void MockShoppingService::SetIsShoppingListEligible(bool eligible) { ON_CALL(*this, IsShoppingListEligible) .WillByDefault(testing::Return(eligible));
diff --git a/components/commerce/core/mock_shopping_service.h b/components/commerce/core/mock_shopping_service.h index 152724c..c2f50852 100644 --- a/components/commerce/core/mock_shopping_service.h +++ b/components/commerce/core/mock_shopping_service.h
@@ -12,7 +12,6 @@ #include "components/commerce/core/subscriptions/commerce_subscription.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" namespace commerce { @@ -52,6 +51,21 @@ (std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, base::OnceCallback<void(bool)> callback), (override)); + MOCK_METHOD( + void, + GetAllSubscriptions, + (SubscriptionType type, + base::OnceCallback<void(std::vector<CommerceSubscription>)> callback), + (override)); + MOCK_METHOD(void, + IsSubscribed, + (CommerceSubscription subscription, + base::OnceCallback<void(bool)> callback), + (override)); + MOCK_METHOD(bool, + IsSubscribedFromCache, + (const CommerceSubscription& subscription), + (override)); MOCK_METHOD(void, ScheduleSavedProductUpdate, (), (override)); MOCK_METHOD(bool, IsShoppingListEligible, (), (override)); MOCK_METHOD(void, @@ -68,6 +82,9 @@ absl::optional<commerce::MerchantInfo> merchant_info); void SetSubscribeCallbackValue(bool subscribe_should_succeed); void SetUnsubscribeCallbackValue(bool unsubscribe_should_succeed); + void SetIsSubscribedCallbackValue(bool is_subscribed); + void SetGetAllSubscriptionsCallbackValue( + std::vector<CommerceSubscription> subscriptions); void SetIsShoppingListEligible(bool enabled); void SetIsClusterIdTrackedByUserResponse(bool is_tracked); void SetIsMerchantViewerEnabled(bool is_enabled);
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc index 8d9c26db..fa1723ce 100644 --- a/components/commerce/core/shopping_service.cc +++ b/components/commerce/core/shopping_service.cc
@@ -721,6 +721,16 @@ } } +void ShoppingService::GetAllSubscriptions( + SubscriptionType type, + base::OnceCallback<void(std::vector<CommerceSubscription>)> callback) { + if (subscriptions_manager_) { + subscriptions_manager_->GetAllSubscriptions(type, std::move(callback)); + } else { + CHECK_IS_TEST(); + } +} + void ShoppingService::IsSubscribed(CommerceSubscription subscription, base::OnceCallback<void(bool)> callback) { if (subscriptions_manager_) { @@ -731,7 +741,7 @@ } } -bool ShoppingService::IsSubscriptedFromCache( +bool ShoppingService::IsSubscribedFromCache( const CommerceSubscription& subscription) { if (subscriptions_manager_) { return subscriptions_manager_->IsSubscribedFromCache(subscription);
diff --git a/components/commerce/core/shopping_service.h b/components/commerce/core/shopping_service.h index fc7eb75e..db0893e0 100644 --- a/components/commerce/core/shopping_service.h +++ b/components/commerce/core/shopping_service.h
@@ -21,6 +21,7 @@ #include "base/supports_user_data.h" #include "components/commerce/core/account_checker.h" #include "components/commerce/core/proto/commerce_subscription_db_content.pb.h" +#include "components/commerce/core/subscriptions/commerce_subscription.h" #include "components/keyed_service/core/keyed_service.h" #include "components/optimization_guide/core/optimization_guide_decision.h" #include "services/data_decoder/public/cpp/data_decoder.h" @@ -216,6 +217,12 @@ std::unique_ptr<std::vector<CommerceSubscription>> subscriptions, base::OnceCallback<void(bool)> callback); + // Gets all subscriptions for the specified type. The list of subscriptions + // will be provided as input to the |callback| passed to this function. + virtual void GetAllSubscriptions( + SubscriptionType type, + base::OnceCallback<void(std::vector<CommerceSubscription>)> callback); + // Methods to register or remove SubscriptionsObserver, which will be notified // when a (un)subscribe request has finished. void AddSubscriptionsObserver(SubscriptionsObserver* observer); @@ -228,7 +235,7 @@ // Checks if a subscription exists from the in-memory cache. Use of the the // callback-based version |IsSubscribed| is preferred. Information provided // by this API is not guaranteed to be correct. - virtual bool IsSubscriptedFromCache(const CommerceSubscription& subscription); + virtual bool IsSubscribedFromCache(const CommerceSubscription& subscription); // Fetch users' pref from server on whether to receive price tracking emails. void FetchPriceEmailPref();
diff --git a/components/history_clusters/core/history_clusters_service.cc b/components/history_clusters/core/history_clusters_service.cc index 4ac56b1..b22ccaf 100644 --- a/components/history_clusters/core/history_clusters_service.cc +++ b/components/history_clusters/core/history_clusters_service.cc
@@ -40,6 +40,15 @@ namespace history_clusters { +namespace { + +void RecordUpdateClustersLatencyHistogram(const std::string& histogram_name, + base::ElapsedTimer elapsed_timer) { + base::UmaHistogramMediumTimes(histogram_name, elapsed_timer.Elapsed()); +} + +} // namespace + VisitDeletionObserver::VisitDeletionObserver( HistoryClustersService* history_clusters_service) : history_clusters_service_(history_clusters_service) {} @@ -262,13 +271,19 @@ update_clusters_task_ = std::make_unique<HistoryClustersServiceTaskUpdateClusterTriggerability>( weak_ptr_factory_.GetWeakPtr(), backend_.get(), history_service_, - base::DoNothing()); + base::BindOnce( + &RecordUpdateClustersLatencyHistogram, + "History.Clusters.Backend.UpdateClusterTriggerability.Total", + base::ElapsedTimer())); } else { update_clusters_task_ = std::make_unique<HistoryClustersServiceTaskUpdateClusters>( weak_ptr_factory_.GetWeakPtr(), incomplete_visit_context_annotations_, backend_.get(), - history_service_, base::DoNothing()); + history_service_, + base::BindOnce(&RecordUpdateClustersLatencyHistogram, + "History.Clusters.Backend.UpdateClusters.Total", + base::ElapsedTimer())); } }
diff --git a/components/image_service/BUILD.gn b/components/image_service/BUILD.gn index 5e67915b..645ae4d 100644 --- a/components/image_service/BUILD.gn +++ b/components/image_service/BUILD.gn
@@ -5,10 +5,13 @@ component("image_service") { defines = [ "IS_IMAGE_SERVICE_IMPL" ] sources = [ + "features.cc", + "features.h", "image_service.cc", "image_service.h", ] deps = [ + "mojom:mojo_bindings", "//base", "//components/google/core/common", "//components/keyed_service/core",
diff --git a/components/image_service/features.cc b/components/image_service/features.cc new file mode 100644 index 0000000..045270eb --- /dev/null +++ b/components/image_service/features.cc
@@ -0,0 +1,22 @@ +// 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. + +#include "components/image_service/features.h" + +namespace image_service { + +// Enabled by default because we are only using this as a killswitch. +BASE_FEATURE(kImageService, "ImageService", base::FEATURE_ENABLED_BY_DEFAULT); + +// Disabled by default because the usage of this is still not approved. +BASE_FEATURE(kImageServiceSuggestPoweredImages, + "ImageServiceSuggestPoweredImages", + base::FEATURE_DISABLED_BY_DEFAULT); + +// Disabled by default, usage is approved but we still want to control rollout. +BASE_FEATURE(kImageServiceOptimizationGuideSalientImages, + "ImageServiceOptimizationGuideSalientImages", + base::FEATURE_DISABLED_BY_DEFAULT); + +} // namespace image_service
diff --git a/components/image_service/features.h b/components/image_service/features.h new file mode 100644 index 0000000..9959b360 --- /dev/null +++ b/components/image_service/features.h
@@ -0,0 +1,18 @@ +// 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. + +#ifndef COMPONENTS_IMAGE_SERVICE_FEATURES_H_ +#define COMPONENTS_IMAGE_SERVICE_FEATURES_H_ + +#include "base/feature_list.h" + +namespace image_service { + +BASE_DECLARE_FEATURE(kImageService); +BASE_DECLARE_FEATURE(kImageServiceSuggestPoweredImages); +BASE_DECLARE_FEATURE(kImageServiceOptimizationGuideSalientImages); + +} // namespace image_service + +#endif // COMPONENTS_IMAGE_SERVICE_FEATURES_H_
diff --git a/components/image_service/image_service.cc b/components/image_service/image_service.cc index 258cf06..1c7774d 100644 --- a/components/image_service/image_service.cc +++ b/components/image_service/image_service.cc
@@ -4,11 +4,13 @@ #include "components/image_service/image_service.h" +#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/i18n/case_conversion.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "components/image_service/features.h" #include "components/omnibox/browser/remote_suggestions_service.h" #include "components/omnibox/browser/search_suggestion_parser.h" #include "components/search_engines/search_engine_type.h" @@ -23,6 +25,7 @@ // A one-time use object that uses Suggest to get an image URL corresponding // to `search_query` and `entity_id`. This is a hacky temporary implementation, // ideally this should be replaced by persisted Suggest-provided entities. +// TODO(tommycli): Move this to its own separate file with unit tests. class ImageService::SuggestEntityImageURLFetcher { public: SuggestEntityImageURLFetcher( @@ -150,7 +153,51 @@ return weak_factory_.GetWeakPtr(); } -bool ImageService::FetchImageFor(const std::u16string& search_query, +void ImageService::FetchImageFor(mojom::ClientId client_id, + const GURL& page_url, + const mojom::Options& options, + ResultCallback callback) { + if (!base::FeatureList::IsEnabled(kImageService)) { + // In general this should never happen, because each UI should have its own + // feature gate, but this is just so we have a whole-service killswitch. + return std::move(callback).Run(GURL()); + } + + // TODO(b/244507194): This one only checks Sync consent, we probably need to + // delegate consent checking to the UI layer entirely, since Bookmarks needs + // to use a Bookmarks-specific Sync permission checker. + DCHECK(url_consent_helper_ && url_consent_helper_->IsEnabled()); + + if (options.suggest_images && + base::FeatureList::IsEnabled(kImageServiceSuggestPoweredImages)) { + // TODO(b/244507194): Get our "own" TemplateURLService. + if (auto* template_url_service = + autocomplete_provider_client_->GetTemplateURLService()) { + auto search_metadata = + template_url_service->ExtractSearchMetadata(page_url); + // Fetch entity-keyed images for Google SRP visits only, because only + // Google SRP visits can expect to have a reasonable entity from Google + // Suggest. + if (search_metadata && search_metadata->template_url && + search_metadata->template_url->GetEngineType( + template_url_service->search_terms_data()) == + SEARCH_ENGINE_GOOGLE) { + return FetchImageFor(/*search_query=*/search_metadata->search_terms, + /*entity_id=*/"", std::move(callback)); + } + } + } + + if (options.optimization_guide_images && + base::FeatureList::IsEnabled( + kImageServiceOptimizationGuideSalientImages)) { + // TODO(b/248367751): Insert OptimizationGuide Salient Image call here. + } + + std::move(callback).Run(GURL()); +} + +void ImageService::FetchImageFor(const std::u16string& search_query, const std::string& entity_id, ResultCallback callback) { DCHECK(url_consent_helper_ && url_consent_helper_->IsEnabled()); @@ -164,7 +211,6 @@ fetcher_raw_ptr->Start( base::BindOnce(&ImageService::OnImageFetched, weak_factory_.GetWeakPtr(), std::move(fetcher), std::move(callback))); - return true; } void ImageService::OnImageFetched(
diff --git a/components/image_service/image_service.h b/components/image_service/image_service.h index 3cf561a..a7d826d 100644 --- a/components/image_service/image_service.h +++ b/components/image_service/image_service.h
@@ -11,6 +11,7 @@ #include "base/component_export.h" #include "base/functional/callback_forward.h" #include "base/memory/weak_ptr.h" +#include "components/image_service/mojom/image_service.mojom.h" #include "components/keyed_service/core/keyed_service.h" #include "components/omnibox/browser/autocomplete_provider_client.h" #include "components/sync/driver/sync_service.h" @@ -36,16 +37,23 @@ // object whose lifetime might exceed the service. base::WeakPtr<ImageService> GetWeakPtr(); - // Fetches an image appropriate for `search_query` and `entity_id`, returning - // the result asynchronously to `callback`. Returns false if we can't do it - // for configuration or privacy reasons. - bool FetchImageFor(const std::u16string& search_query, - const std::string& entity_id, + // Fetches an image appropriate for `page_url`, returning the result + // asynchronously to `callback`. The callback is always invoked. If there are + // no images available, it is invoked with an empty GURL result. + void FetchImageFor(mojom::ClientId client_id, + const GURL& page_url, + const mojom::Options& options, ResultCallback callback); private: class SuggestEntityImageURLFetcher; + // Fetches an image appropriate for `search_query` and `entity_id`, returning + // the result asynchronously to `callback`. + void FetchImageFor(const std::u16string& search_query, + const std::string& entity_id, + ResultCallback callback); + // Callback for `FetchImageFor`. void OnImageFetched(std::unique_ptr<SuggestEntityImageURLFetcher> fetcher, ResultCallback callback,
diff --git a/components/image_service/mojom/BUILD.gn b/components/image_service/mojom/BUILD.gn new file mode 100644 index 0000000..e7ebe25d --- /dev/null +++ b/components/image_service/mojom/BUILD.gn
@@ -0,0 +1,16 @@ +# Copyright 2023 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("mojo_bindings") { + sources = [ "image_service.mojom" ] + public_deps = [ + "//mojo/public/mojom/base", + "//url/mojom:url_mojom_gurl", + ] + + webui_module_path = "" + use_typescript_sources = true +}
diff --git a/components/image_service/mojom/OWNERS b/components/image_service/mojom/OWNERS new file mode 100644 index 0000000..9c3b66c8 --- /dev/null +++ b/components/image_service/mojom/OWNERS
@@ -0,0 +1,6 @@ +file://components/history_clusters/OWNERS + +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS +per-file *_mojom_traits*.*=set noparent +per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/components/image_service/mojom/image_service.mojom b/components/image_service/mojom/image_service.mojom new file mode 100644 index 0000000..e024ad8 --- /dev/null +++ b/components/image_service/mojom/image_service.mojom
@@ -0,0 +1,24 @@ +// 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. + +module image_service.mojom; + +// The UI client that's calling the service. Used to track usage. +enum ClientId { + // Approved as part of launch/4224996. + Journeys, + JourneysSidePanel, + NtpRealbox, + NtpQuests, + + // Tracked at launch/4225832. + Bookmarks, +}; + +// Options for image fetching. +struct Options { + // Turn these off if any UI client wants to disable a source. + bool suggest_images = true; + bool optimization_guide_images = true; +};
diff --git a/components/ownership/owner_settings_service.cc b/components/ownership/owner_settings_service.cc index c54e70a..5768a0c7 100644 --- a/components/ownership/owner_settings_service.cc +++ b/components/ownership/owner_settings_service.cc
@@ -84,7 +84,7 @@ BASE_FEATURE(kChromeSideOwnerKeyGeneration, "ChromeSideOwnerKeyGeneration", - base::FeatureState::FEATURE_ENABLED_BY_DEFAULT); + base::FeatureState::FEATURE_DISABLED_BY_DEFAULT); OwnerSettingsService::OwnerSettingsService( const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util)
diff --git a/components/password_manager/core/browser/password_manager_constants.cc b/components/password_manager/core/browser/password_manager_constants.cc index 6bc9b65..e313f86f 100644 --- a/components/password_manager/core/browser/password_manager_constants.cc +++ b/components/password_manager/core/browser/password_manager_constants.cc
@@ -26,7 +26,4 @@ const char kReferrerURL[] = "https://passwords.google/"; -const char kTestingReferrerURL[] = - "https://xl-password-manager-staging.uc.r.appspot.com/"; - } // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager_constants.h b/components/password_manager/core/browser/password_manager_constants.h index 327af0f..8da603c8 100644 --- a/components/password_manager/core/browser/password_manager_constants.h +++ b/components/password_manager/core/browser/password_manager_constants.h
@@ -29,11 +29,6 @@ // URL from which native Password Manager UI can be opened. extern const char kReferrerURL[]; -// URL for a testing website from which native Password Manager UI can be -// opened. -// TODO(crbug.com/1329165): remove when the main website is launched. -extern const char kTestingReferrerURL[]; - } // namespace password_manager #endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_CONSTANTS_H_
diff --git a/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java b/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java index a191ecb0..b1e6419 100644 --- a/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java +++ b/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java
@@ -238,6 +238,13 @@ enableWriteOnlyMode(); } + /** Delete all entries from the cache. */ + public void reset() { + SharedPreferences.Editor sharedPreferencesEditor = getSharedPreferencesEditor(); + sharedPreferencesEditor.clear(); + sharedPreferencesEditor.apply(); + } + private void enableWriteOnlyMode() { mSharedPreferences = null; mReadable = false;
diff --git a/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyCacheUpdaterTestSupporter.java b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyCacheUpdaterTestSupporter.java index f348c84d..fcf7b08 100644 --- a/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyCacheUpdaterTestSupporter.java +++ b/components/policy/android/javatests/src/org/chromium/components/policy/test/PolicyCacheUpdaterTestSupporter.java
@@ -23,16 +23,30 @@ @CalledByNative private PolicyCacheUpdaterTestSupporter() {} + /** Checks value for {@code policy} is not cached. */ @CalledByNative - private void verifyPolicyCacheIntValue(String policy, boolean hasValue, int expectedValue) { + private void verifyIntPolicyNotCached(String policy) { + PolicyCache policyCache = PolicyCache.get(); + policyCache.setReadableForTesting(true); + Assert.assertNull(policyCache.getIntValue(policy)); + } + + /** + * Checks value for {@code policy} is cached and the corresponding value is {@code + * expectedValue}. + */ + @CalledByNative + private void verifyIntPolicyHasValue(String policy, int expectedValue) { PolicyCache policyCache = PolicyCache.get(); policyCache.setReadableForTesting(true); Integer actualValue = policyCache.getIntValue(policy); - if (hasValue) { - Assert.assertNotNull(actualValue); - Assert.assertEquals(expectedValue, actualValue.intValue()); - } else { - Assert.assertNull(actualValue); - } + Assert.assertNotNull(actualValue); + Assert.assertEquals(expectedValue, actualValue.intValue()); + } + + /** Deletes all entries from the policy cache. */ + @CalledByNative + private void resetPolicyCache() { + PolicyCache.get().reset(); } }
diff --git a/components/policy/core/browser/android/policy_cache_updater_android_unittest.cc b/components/policy/core/browser/android/policy_cache_updater_android_unittest.cc index 917bb910..94499d8 100644 --- a/components/policy/core/browser/android/policy_cache_updater_android_unittest.cc +++ b/components/policy/core/browser/android/policy_cache_updater_android_unittest.cc
@@ -89,6 +89,10 @@ } ~PolicyCacheUpdaterAndroidTest() override = default; + void TearDown() override { + Java_PolicyCacheUpdaterTestSupporter_resetPolicyCache(env_, j_support_); + } + void SetPolicy(const std::string& policy, int policy_value) { policy_map_.Set(policy, PolicyLevel::POLICY_LEVEL_MANDATORY, PolicyScope::POLICY_SCOPE_MACHINE, @@ -99,12 +103,15 @@ void UpdatePolicy() { policy_provider_.UpdateChromePolicy(policy_map_); } - void VerifyPolicyName(const std::string& policy, - bool has_value, - int expected_value) { - Java_PolicyCacheUpdaterTestSupporter_verifyPolicyCacheIntValue( + void VerifyIntPolicyNotCached(const std::string& policy) { + Java_PolicyCacheUpdaterTestSupporter_verifyIntPolicyNotCached( + env_, j_support_, base::android::ConvertUTF8ToJavaString(env_, policy)); + } + + void VerifyIntPolicyHasValue(const std::string& policy, int expected_value) { + Java_PolicyCacheUpdaterTestSupporter_verifyIntPolicyHasValue( env_, j_support_, base::android::ConvertUTF8ToJavaString(env_, policy), - has_value, expected_value); + expected_value); } ConfigurationPolicyHandlerList* policy_handler_list() { @@ -132,7 +139,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/true, kPolicyValue); + VerifyIntPolicyHasValue(kPolicyName, kPolicyValue); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyNotExist) { @@ -141,7 +148,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyErrorPolicy) { @@ -151,7 +158,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyMapIgnoredPolicy) { @@ -162,7 +169,7 @@ SetPolicy(kPolicyName, kPolicyValue); policy_map()->GetMutable(kPolicyName)->SetIgnored(); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyMapErrorMessagePolicy) { @@ -175,7 +182,7 @@ ->GetMutable(kPolicyName) ->AddMessage(PolicyMap::MessageType::kError, IDS_POLICY_BLOCKED); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyMapWarningMessagePolicy) { @@ -188,7 +195,7 @@ ->GetMutable(kPolicyName) ->AddMessage(PolicyMap::MessageType::kWarning, IDS_POLICY_BLOCKED); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/true, kPolicyValue); + VerifyIntPolicyHasValue(kPolicyName, kPolicyValue); } TEST_F(PolicyCacheUpdaterAndroidTest, TestPolicyUpdatedBeforeUpdaterCreated) { @@ -197,9 +204,9 @@ SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); PolicyCacheUpdater updater(policy_service(), policy_handler_list()); - VerifyPolicyName(kPolicyName, /*has_value=*/true, kPolicyValue); + VerifyIntPolicyHasValue(kPolicyName, kPolicyValue); } TEST_F(PolicyCacheUpdaterAndroidTest, @@ -210,7 +217,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/false, kPolicyValue); + VerifyIntPolicyNotCached(kPolicyName); } TEST_F(PolicyCacheUpdaterAndroidTest, TestWithWarningError_PolicyHasValue) { @@ -220,7 +227,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/true, kPolicyValue); + VerifyIntPolicyHasValue(kPolicyName, kPolicyValue); } TEST_F(PolicyCacheUpdaterAndroidTest, TestWithInfoError_PolicyHasValue) { @@ -230,7 +237,7 @@ PolicyCacheUpdater updater(policy_service(), policy_handler_list()); SetPolicy(kPolicyName, kPolicyValue); UpdatePolicy(); - VerifyPolicyName(kPolicyName, /*has_value=*/true, kPolicyValue); + VerifyIntPolicyHasValue(kPolicyName, kPolicyValue); } } // namespace android
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml index e09ef96..2f7d462 100644 --- a/components/policy/resources/templates/policies.yaml +++ b/components/policy/resources/templates/policies.yaml
@@ -1077,6 +1077,7 @@ 1076: DeviceActivityHeartbeatCollectionRateMs 1077: WallpaperGooglePhotosIntegrationEnabled 1078: WebRtcTextLogCollectionAllowed + 1079: EnforceLocalAnchorConstraintsEnabled atomic_groups: 1: Homepage 2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/EnforceLocalAnchorConstraintsEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/EnforceLocalAnchorConstraintsEnabled.yaml new file mode 100644 index 0000000..5eb229e --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/EnforceLocalAnchorConstraintsEnabled.yaml
@@ -0,0 +1,56 @@ +caption: Determines whether the built-in certificate verifier + will enforce constraints encoded into trust anchors loaded from the platform + trust store. +default: true +desc: |- + X.509 certificates may encode constraints, such as Name Constraints, + in extensions in the certificate. RFC 5280 specifies that enforcing such + constraints on trust anchor certificates is optional. Starting in + <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> 112, such constraints + in certificates loaded from the platform certificate store will now be + enforced. + + This policy exists as a temporary opt-out in case an enterprise encounters + issues with the constraints encoded in their private roots. In that case this + policy may be used to temporarily disable enforcement of the constraints + while correcting the certificate issues. + + When this policy is not set, or is set to enabled, + <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will enforce + constraints encoded into trust anchors loaded from the platform trust store. + + When this policy is set to disabled, + <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not enforce + constraints encoded into trust anchors loaded from the platform trust store. + + This policy has no effect if the + <ph name="CHROME_ROOT_STORE_ENABLED_POLICY_NAME">ChromeRootStoreEnabled</ph> + policy is disabled. + + This policy is planned to be removed in + <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 115. + +example_value: false +features: + dynamic_refresh: true + per_profile: false +items: +- caption: Enforce constraints in locally added trust anchors + value: true +- caption: Do not enforce constraints in locally added trust anchors + value: false +owners: +- mattm@chromium.org +- file://net/cert/OWNERS +schema: + type: boolean +supported_on: +- chrome.win:112- +- chrome.mac:112- +- chrome.linux:112- +- chrome_os:112- +future_on: +- android +tags: [] +type: main +
diff --git a/components/reading_list/core/dual_reading_list_model.cc b/components/reading_list/core/dual_reading_list_model.cc index 8ef06fba..cad6a35 100644 --- a/components/reading_list/core/dual_reading_list_model.cc +++ b/components/reading_list/core/dual_reading_list_model.cc
@@ -9,14 +9,15 @@ #include "base/notreached.h" #include "base/stl_util.h" #include "components/reading_list/core/reading_list_entry.h" +#include "components/reading_list/core/reading_list_model_impl.h" #include "components/reading_list/features/reading_list_switches.h" #include "url/gurl.h" namespace reading_list { DualReadingListModel::DualReadingListModel( - std::unique_ptr<ReadingListModel> local_or_syncable_model, - std::unique_ptr<ReadingListModel> account_model) + std::unique_ptr<ReadingListModelImpl> local_or_syncable_model, + std::unique_ptr<ReadingListModelImpl> account_model) : local_or_syncable_model_(std::move(local_or_syncable_model)), account_model_(std::move(account_model)) { DCHECK(local_or_syncable_model_); @@ -154,6 +155,17 @@ return local_or_syncable_model_->IsUrlSupported(url); } +bool DualReadingListModel::NeedsExplicitUploadToSyncServer( + const GURL& url) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!local_or_syncable_model_->IsTrackingSyncMetadata() || + !account_model_->IsTrackingSyncMetadata()); + + return account_model_->IsTrackingSyncMetadata() && + local_or_syncable_model_->GetEntryByURL(url) != nullptr && + account_model_->GetEntryByURL(url) == nullptr; +} + const ReadingListEntry& DualReadingListModel::AddOrReplaceEntry( const GURL& url, const std::string& title,
diff --git a/components/reading_list/core/dual_reading_list_model.h b/components/reading_list/core/dual_reading_list_model.h index d174723..7bd970f 100644 --- a/components/reading_list/core/dual_reading_list_model.h +++ b/components/reading_list/core/dual_reading_list_model.h
@@ -14,6 +14,7 @@ #include "base/sequence_checker.h" #include "components/reading_list/core/reading_list_entry.h" #include "components/reading_list/core/reading_list_model.h" +#include "components/reading_list/core/reading_list_model_impl.h" #include "components/reading_list/core/reading_list_model_observer.h" #include "url/gurl.h" @@ -38,8 +39,8 @@ }; DualReadingListModel( - std::unique_ptr<ReadingListModel> local_or_syncable_model, - std::unique_ptr<ReadingListModel> account_model); + std::unique_ptr<ReadingListModelImpl> local_or_syncable_model, + std::unique_ptr<ReadingListModelImpl> account_model); ~DualReadingListModel() override; // KeyedService implementation. @@ -62,6 +63,7 @@ scoped_refptr<const ReadingListEntry> GetEntryByURL( const GURL& gurl) const override; bool IsUrlSupported(const GURL& url) override; + bool NeedsExplicitUploadToSyncServer(const GURL& url) const override; const ReadingListEntry& AddOrReplaceEntry( const GURL& url, const std::string& title, @@ -114,8 +116,8 @@ void NotifyObserversWithDidRemoveEntry(const GURL& url); void NotifyObserversWithDidApplyChanges(); - const std::unique_ptr<ReadingListModel> local_or_syncable_model_; - const std::unique_ptr<ReadingListModel> account_model_; + const std::unique_ptr<ReadingListModelImpl> local_or_syncable_model_; + const std::unique_ptr<ReadingListModelImpl> account_model_; // Indicates whether a ReadingListModelImpl::RemoveEntryByURL is currently // performing on `local_or_syncable_model_` and `account_model_`.
diff --git a/components/reading_list/core/dual_reading_list_model_unittest.cc b/components/reading_list/core/dual_reading_list_model_unittest.cc index c3f07d5..fa0244b 100644 --- a/components/reading_list/core/dual_reading_list_model_unittest.cc +++ b/components/reading_list/core/dual_reading_list_model_unittest.cc
@@ -55,7 +55,7 @@ std::make_unique<FakeReadingListModelStorage>(); account_model_storage_ptr_ = account_model_storage->AsWeakPtr(); auto account_model = std::make_unique<ReadingListModelImpl>( - std::move(account_model_storage), syncer::StorageType::kUnspecified, + std::move(account_model_storage), syncer::StorageType::kAccount, &clock_); account_model_ptr_ = account_model.get(); @@ -65,56 +65,60 @@ } bool ResetStorageAndTriggerLoadCompletion( - std::vector<scoped_refptr<ReadingListEntry>> initial_local_entries, - std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries) { + std::vector<scoped_refptr<ReadingListEntry>> + initial_local_or_syncable_entries = {}, + std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries = + {}) { ResetStorage(); return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( - std::move(initial_local_entries)) && + std::move(initial_local_or_syncable_entries)) && account_model_storage_ptr_->TriggerLoadCompletion( std::move(initial_account_entries)); } - bool ResetStorageAndTriggerLoadCompletion( - const std::vector<GURL>& initial_local_urls = {}, - const std::vector<GURL>& initial_account_urls = {}) { - std::vector<scoped_refptr<ReadingListEntry>> initial_local_entries; - for (const auto& url : initial_local_urls) { - initial_local_entries.push_back(base::MakeRefCounted<ReadingListEntry>( - url, "Title for " + url.spec(), clock_.Now())); - } - - std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries; - for (const auto& url : initial_account_urls) { - initial_account_entries.push_back(base::MakeRefCounted<ReadingListEntry>( - url, "Title for " + url.spec(), clock_.Now())); - } - + bool ResetStorageAndMimicSignedOut( + std::vector<scoped_refptr<ReadingListEntry>> initial_local_entries = {}) { return ResetStorageAndTriggerLoadCompletion( - std::move(initial_local_entries), std::move(initial_account_entries)); + std::move(initial_local_entries), /*initial_account_entries=*/{}); } - size_t UnreadSize() { - size_t size = 0; - for (const auto& url : dual_model_->GetKeys()) { - scoped_refptr<const ReadingListEntry> entry = - dual_model_->GetEntryByURL(url); - if (!entry->IsRead()) { - size++; - } - } - return size; + bool ResetStorageAndMimicSignedInSyncDisabled( + std::vector<scoped_refptr<ReadingListEntry>> initial_local_entries = {}, + std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries = + {}) { + ResetStorage(); + auto metadata_batch = std::make_unique<syncer::MetadataBatch>(); + sync_pb::ModelTypeState state; + state.set_initial_sync_done(true); + metadata_batch->SetModelTypeState(state); + return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( + std::move(initial_local_entries)) && + account_model_storage_ptr_->TriggerLoadCompletion( + std::move(initial_account_entries), std::move(metadata_batch)); } - size_t ReadSize() { - size_t size = 0; - for (const auto& url : dual_model_->GetKeys()) { - scoped_refptr<const ReadingListEntry> entry = - dual_model_->GetEntryByURL(url); - if (entry->IsRead()) { - size++; - } + bool ResetStorageAndMimicSyncEnabled( + std::vector<scoped_refptr<ReadingListEntry>> initial_syncable_entries = + {}) { + ResetStorage(); + auto metadata_batch = std::make_unique<syncer::MetadataBatch>(); + sync_pb::ModelTypeState state; + state.set_initial_sync_done(true); + metadata_batch->SetModelTypeState(state); + return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( + std::move(initial_syncable_entries), + std::move(metadata_batch)) && + account_model_storage_ptr_->TriggerLoadCompletion(); + } + + std::vector<scoped_refptr<ReadingListEntry>> MakeTestEntriesForURLs( + const std::vector<GURL>& urls) { + std::vector<scoped_refptr<ReadingListEntry>> entries; + for (const auto& url : urls) { + entries.push_back(base::MakeRefCounted<ReadingListEntry>( + url, "Title for " + url.spec(), clock_.Now())); } - return size; + return entries; } protected: @@ -140,8 +144,6 @@ EXPECT_CALL(observer_, ReadingListModelLoaded(dual_model_.get())); ASSERT_TRUE(account_model_storage_ptr_->TriggerLoadCompletion()); EXPECT_TRUE(dual_model_->loaded()); - EXPECT_EQ(0ul, UnreadSize()); - EXPECT_EQ(0ul, ReadSize()); } // Tests errors during load model. @@ -156,7 +158,8 @@ TEST_F(DualReadingListModelTest, ReturnAccountModelSize) { ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( - /*initial_local_urls=*/{}, {GURL("https://url.com")})); + /*initial_local_or_syncable_entries=*/{}, + MakeTestEntriesForURLs({GURL("https://url.com")}))); ASSERT_EQ(0ul, local_or_syncable_model_ptr_->size()); ASSERT_EQ(1ul, account_model_ptr_->size()); EXPECT_EQ(1ul, dual_model_->size()); @@ -164,15 +167,17 @@ TEST_F(DualReadingListModelTest, ReturnLocalModelSize) { ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( - {GURL("https://url.com")}, /*initial_account_urls=*/{})); + MakeTestEntriesForURLs({GURL("https://url.com")}), + /*initial_account_entries=*/{})); ASSERT_EQ(1ul, local_or_syncable_model_ptr_->size()); ASSERT_EQ(0ul, account_model_ptr_->size()); EXPECT_EQ(1ul, dual_model_->size()); } TEST_F(DualReadingListModelTest, ReturnKeysSize) { - ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion({GURL("https://url1.com")}, - {GURL("https://url2.com")})); + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( + MakeTestEntriesForURLs({GURL("https://url1.com")}), + MakeTestEntriesForURLs({GURL("https://url2.com")}))); ASSERT_EQ(1ul, local_or_syncable_model_ptr_->size()); ASSERT_EQ(1ul, account_model_ptr_->size()); EXPECT_EQ(2ul, dual_model_->size()); @@ -246,27 +251,75 @@ ReadingListEntry::DISTILLATION_ERROR); } -TEST_F(DualReadingListModelTest, RemoveNonExistingEntryByUrl) { - ResetStorageAndTriggerLoadCompletion(); - const GURL kUrl = GURL("http://url.com/"); +TEST_F(DualReadingListModelTest, NeedsExplicitUploadToSyncServerWhenSignedOut) { + const GURL kLocalUrl("http://local_url.com/"); + ASSERT_TRUE( + ResetStorageAndMimicSignedOut(MakeTestEntriesForURLs({kLocalUrl}))); + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalUrl), + StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); - ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kLocalUrl)); + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( + GURL("http://non_existing_url.com/"))); +} + +TEST_F(DualReadingListModelTest, + NeedsExplicitUploadToSyncServerWhenSignedInSyncDisabled) { + const GURL kLocalUrl("http://local_url.com/"); + const GURL kAccountUrl("http://account_url.com/"); + const GURL kCommonUrl("http://common_url.com/"); + ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( + MakeTestEntriesForURLs({kLocalUrl, kCommonUrl}), + MakeTestEntriesForURLs({kAccountUrl, kCommonUrl}))); + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalUrl), + StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kAccountUrl), + StorageStateForTesting::kExistsInAccountModelOnly); + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kCommonUrl), + StorageStateForTesting::kExistsInBothModels); + + EXPECT_TRUE(dual_model_->NeedsExplicitUploadToSyncServer(kLocalUrl)); + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kAccountUrl)); + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kCommonUrl)); + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( + GURL("http://non_existing_url.com/"))); +} + +TEST_F(DualReadingListModelTest, + NeedsExplicitUploadToSyncServerWhenSyncEnabled) { + const GURL kSyncableUrl("http://syncable_url.com/"); + ASSERT_TRUE( + ResetStorageAndMimicSyncEnabled(MakeTestEntriesForURLs({kSyncableUrl}))); + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kSyncableUrl), + StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); + + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kSyncableUrl)); + EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( + GURL("http://non_existing_url.com/"))); +} + +TEST_F(DualReadingListModelTest, RemoveNonExistingEntryByUrl) { + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); + const GURL kNonExistingURL("http://non_existing_url.com/"); + + ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kNonExistingURL), StorageStateForTesting::kNotFound); - ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); + ASSERT_THAT(dual_model_->GetEntryByURL(kNonExistingURL), IsNull()); EXPECT_CALL(observer_, ReadingListWillRemoveEntry).Times(0); EXPECT_CALL(observer_, ReadingListDidRemoveEntry).Times(0); EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); - dual_model_->RemoveEntryByURL(kUrl); + dual_model_->RemoveEntryByURL(kNonExistingURL); - EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); + EXPECT_THAT(dual_model_->GetEntryByURL(kNonExistingURL), IsNull()); } TEST_F(DualReadingListModelTest, RemoveLocalEntryByUrl) { - const GURL kLocalUrl = GURL("http://local_url.com/"); - ResetStorageAndTriggerLoadCompletion({kLocalUrl}, - /*initial_account_urls=*/{}); + const GURL kLocalUrl("http://local_url.com/"); + ASSERT_TRUE( + ResetStorageAndTriggerLoadCompletion(MakeTestEntriesForURLs({kLocalUrl}), + /*initial_account_entries=*/{})); ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalUrl), StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); @@ -285,9 +338,10 @@ } TEST_F(DualReadingListModelTest, RemoveAccountEntryByUrl) { - const GURL kAccountUrl = GURL("http://account_url.com/"); - ResetStorageAndTriggerLoadCompletion(/*initial_local_urls=*/{}, - {kAccountUrl}); + const GURL kAccountUrl("http://account_url.com/"); + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( + /*initial_local_or_syncable_entries=*/{}, + MakeTestEntriesForURLs({kAccountUrl}))); ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kAccountUrl), StorageStateForTesting::kExistsInAccountModelOnly); @@ -306,8 +360,10 @@ } TEST_F(DualReadingListModelTest, RemoveCommonEntryByUrl) { - const GURL kCommonUrl = GURL("http://Common_url.com/"); - ResetStorageAndTriggerLoadCompletion({kCommonUrl}, {kCommonUrl}); + const GURL kCommonUrl("http://common_url.com/"); + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( + MakeTestEntriesForURLs({kCommonUrl}), + MakeTestEntriesForURLs({kCommonUrl}))); ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kCommonUrl), StorageStateForTesting::kExistsInBothModels); @@ -326,9 +382,10 @@ } TEST_F(DualReadingListModelTest, RemoveLocalEntryByUrlFromSync) { - const GURL kLocalUrl = GURL("http://local_url.com/"); - ResetStorageAndTriggerLoadCompletion({kLocalUrl}, - /*initial_account_urls=*/{}); + const GURL kLocalUrl("http://local_url.com/"); + ASSERT_TRUE( + ResetStorageAndTriggerLoadCompletion(MakeTestEntriesForURLs({kLocalUrl}), + /*initial_account_entries=*/{})); // DCHECKs verify that sync updates are issued as batch updates. auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); @@ -349,9 +406,10 @@ } TEST_F(DualReadingListModelTest, RemoveAccountEntryByUrlFromSync) { - const GURL kAccountUrl = GURL("http://account_url.com/"); - ResetStorageAndTriggerLoadCompletion(/*initial_local_urls=*/{}, - {kAccountUrl}); + const GURL kAccountUrl("http://account_url.com/"); + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( + /*initial_local_or_syncable_entries=*/{}, + MakeTestEntriesForURLs({kAccountUrl}))); // DCHECKs verify that sync updates are issued as batch updates. auto token = account_model_ptr_->BeginBatchUpdates(); @@ -372,8 +430,10 @@ } TEST_F(DualReadingListModelTest, RemoveCommonEntryByUrlFromSync) { - const GURL kCommonUrl = GURL("http://Common_url.com/"); - ResetStorageAndTriggerLoadCompletion({kCommonUrl}, {kCommonUrl}); + const GURL kCommonUrl("http://common_url.com/"); + ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( + MakeTestEntriesForURLs({kCommonUrl}), + MakeTestEntriesForURLs({kCommonUrl}))); // DCHECKs verify that sync updates are issued as batch updates. auto token = account_model_ptr_->BeginBatchUpdates();
diff --git a/components/reading_list/core/fake_reading_list_model_storage.cc b/components/reading_list/core/fake_reading_list_model_storage.cc index a1ffa41..c4a7135b 100644 --- a/components/reading_list/core/fake_reading_list_model_storage.cc +++ b/components/reading_list/core/fake_reading_list_model_storage.cc
@@ -4,6 +4,8 @@ #include "components/reading_list/core/fake_reading_list_model_storage.h" +#include <memory> + #include "base/memory/scoped_refptr.h" #include "components/sync/model/metadata_batch.h" @@ -52,9 +54,10 @@ } bool FakeReadingListModelStorage::TriggerLoadCompletion( - std::vector<scoped_refptr<ReadingListEntry>> entries) { + std::vector<scoped_refptr<ReadingListEntry>> entries, + std::unique_ptr<syncer::MetadataBatch> metadata_batch) { LoadResult result; - result.second = std::make_unique<syncer::MetadataBatch>(); + result.second = std::move(metadata_batch); for (auto& entry : entries) { GURL url = entry->URL(); result.first.emplace(entry->URL(), std::move(entry));
diff --git a/components/reading_list/core/fake_reading_list_model_storage.h b/components/reading_list/core/fake_reading_list_model_storage.h index 848c2b4b..993fd8cf 100644 --- a/components/reading_list/core/fake_reading_list_model_storage.h +++ b/components/reading_list/core/fake_reading_list_model_storage.h
@@ -5,6 +5,7 @@ #ifndef COMPONENTS_READING_LIST_CORE_FAKE_READING_LIST_MODEL_STORAGE_H_ #define COMPONENTS_READING_LIST_CORE_FAKE_READING_LIST_MODEL_STORAGE_H_ +#include <memory> #include <vector> #include "base/memory/raw_ptr.h" @@ -13,6 +14,7 @@ #include "components/reading_list/core/reading_list_entry.h" #include "components/reading_list/core/reading_list_model_storage.h" #include "components/sync/model/dummy_metadata_change_list.h" +#include "components/sync/model/metadata_batch.h" // Test-only implementation of ReadingListModelStorage that doesn't do any // actual I/O but allows populating the initial list of entries. It also @@ -59,7 +61,9 @@ // Convenience overload that uses sensible defaults (empty store) for success // case. bool TriggerLoadCompletion( - std::vector<scoped_refptr<ReadingListEntry>> entries = {}); + std::vector<scoped_refptr<ReadingListEntry>> entries = {}, + std::unique_ptr<syncer::MetadataBatch> metadata_batch = + std::make_unique<syncer::MetadataBatch>()); // ReadingListModelStorage implementation. void Load(base::Clock* clock, LoadCallback load_cb) override;
diff --git a/components/reading_list/core/reading_list_model.h b/components/reading_list/core/reading_list_model.h index e11e10b..4b0f88e 100644 --- a/components/reading_list/core/reading_list_model.h +++ b/components/reading_list/core/reading_list_model.h
@@ -89,6 +89,10 @@ // Returns true if |url| can be added to the reading list. virtual bool IsUrlSupported(const GURL& url) = 0; + // Returns true if the entry with `url` requires explicit user action to + // upload to sync servers. + virtual bool NeedsExplicitUploadToSyncServer(const GURL& url) const = 0; + // Adds |url| at the top of the unread entries, and removes entries with the // same |url| from everywhere else if they exist. The entry title will be a // trimmed copy of |title|. |time_to_read_minutes| is the estimated time to
diff --git a/components/reading_list/core/reading_list_model_impl.cc b/components/reading_list/core/reading_list_model_impl.cc index 008f64e1..a8200039 100644 --- a/components/reading_list/core/reading_list_model_impl.cc +++ b/components/reading_list/core/reading_list_model_impl.cc
@@ -11,6 +11,7 @@ #include "base/memory/ptr_util.h" #include "base/memory/scoped_refptr.h" #include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "base/observer_list.h" #include "base/strings/string_util.h" #include "base/time/clock.h" @@ -324,6 +325,15 @@ return url.SchemeIsHTTPOrHTTPS(); } +bool ReadingListModelImpl::NeedsExplicitUploadToSyncServer( + const GURL& url) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Returning true only makes sense for an implementation that maintains a + // separate set of local and account entries (DualReadingListModel). + return false; +} + const ReadingListEntry& ReadingListModelImpl::AddOrReplaceEntry( const GURL& url, const std::string& title, @@ -544,6 +554,11 @@ return token; } +bool ReadingListModelImpl::IsTrackingSyncMetadata() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return sync_bridge_.change_processor()->IsTrackingMetadata(); +} + // static std::unique_ptr<ReadingListModelImpl> ReadingListModelImpl::BuildNewForTest( std::unique_ptr<ReadingListModelStorage> storage_layer,
diff --git a/components/reading_list/core/reading_list_model_impl.h b/components/reading_list/core/reading_list_model_impl.h index fa47219..1fcb8bc 100644 --- a/components/reading_list/core/reading_list_model_impl.h +++ b/components/reading_list/core/reading_list_model_impl.h
@@ -60,6 +60,7 @@ scoped_refptr<const ReadingListEntry> GetEntryByURL( const GURL& gurl) const override; bool IsUrlSupported(const GURL& url) override; + bool NeedsExplicitUploadToSyncServer(const GURL& url) const override; const ReadingListEntry& AddOrReplaceEntry( const GURL& url, const std::string& title, @@ -112,6 +113,10 @@ std::unique_ptr<ScopedReadingListBatchUpdateImpl> BeginBatchUpdatesWithSyncMetadata(); + // Returns true if the model is sync-ing with the server and the initial + // download of data and corresponding merge has completed. + bool IsTrackingSyncMetadata() const; + // Test-only factory function to inject an arbitrary change processor. static std::unique_ptr<ReadingListModelImpl> BuildNewForTest( std::unique_ptr<ReadingListModelStorage> storage_layer,
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc index c8142d9..2bd1ce0 100644 --- a/components/search_engines/template_url_service.cc +++ b/components/search_engines/template_url_service.cc
@@ -846,7 +846,7 @@ return absl::nullopt; } - return SearchMetadata{normalized_url, normalized_search_terms}; + return SearchMetadata{template_url, normalized_url, normalized_search_terms}; } bool TemplateURLService::IsSideSearchSupportedForDefaultSearchProvider() const {
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h index dd735e5..e61f640 100644 --- a/components/search_engines/template_url_service.h +++ b/components/search_engines/template_url_service.h
@@ -105,6 +105,7 @@ // Search metadata that's often used to persist into History. struct SearchMetadata { + const TemplateURL* template_url; GURL normalized_url; std::u16string search_terms; };
diff --git a/components/signin/internal/identity_manager/DEPS b/components/signin/internal/identity_manager/DEPS index b2b6d8c..4374378 100644 --- a/components/signin/internal/identity_manager/DEPS +++ b/components/signin/internal/identity_manager/DEPS
@@ -5,6 +5,5 @@ "+components/signin/public/webdata", "+components/signin/public/android/jni_headers", "+components/signin/public/android/test_support_jni_headers", - "+google_apis", "+mojo/public", ]
diff --git a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc index a4ad94e1..fffb8451 100644 --- a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc +++ b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
@@ -31,7 +31,6 @@ #include "components/signin/public/base/signin_metrics.h" #include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/load_flags.h" @@ -348,8 +347,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = url; - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; std::unique_ptr<network::SimpleURLLoader> loader = network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
diff --git a/components/sync/driver/sync_stopped_reporter.cc b/components/sync/driver/sync_stopped_reporter.cc index cebc3f35..e388680 100644 --- a/components/sync/driver/sync_stopped_reporter.cc +++ b/components/sync/driver/sync_stopped_reporter.cc
@@ -12,7 +12,6 @@ #include "base/metrics/histogram_functions.h" #include "base/strings/stringprintf.h" #include "components/sync/protocol/sync.pb.h" -#include "google_apis/credentials_mode.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" #include "net/traffic_annotation/network_traffic_annotation.h" @@ -111,8 +110,7 @@ resource_request->url = sync_event_url_; resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; resource_request->method = "POST"; resource_request->headers.SetHeader( net::HttpRequestHeaders::kAuthorization,
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc index 387aa93..8e5c296a 100644 --- a/components/sync/engine/net/http_bridge.cc +++ b/components/sync/engine/net/http_bridge.cc
@@ -17,7 +17,6 @@ #include "base/task/thread_pool.h" #include "base/threading/thread_restrictions.h" #include "components/variations/net/variations_http_headers.h" -#include "google_apis/credentials_mode.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/http/http_request_headers.h" @@ -243,8 +242,7 @@ resource_request->method = "POST"; resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!extra_headers_.empty()) resource_request->headers.AddHeadersFromString(extra_headers_);
diff --git a/components/sync/trusted_vault/trusted_vault_request.cc b/components/sync/trusted_vault/trusted_vault_request.cc index 8ea66bb..6deec60a 100644 --- a/components/sync/trusted_vault/trusted_vault_request.cc +++ b/components/sync/trusted_vault/trusted_vault_request.cc
@@ -12,7 +12,6 @@ #include "components/sync/driver/trusted_vault_histograms.h" #include "components/sync/trusted_vault/trusted_vault_access_token_fetcher.h" #include "components/sync/trusted_vault/trusted_vault_server_constants.h" -#include "google_apis/credentials_mode.h" #include "net/base/url_util.h" #include "net/http/http_request_headers.h" #include "net/http/http_status_code.h" @@ -179,8 +178,7 @@ net::AppendQueryParameter(request_url_, kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto); request->method = GetHttpMethodString(http_method_); - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; request->headers.SetHeader( kAuthorizationHeader, /*value=*/base::StringPrintf("Bearer %s", access_token.c_str()));
diff --git a/components/translate/content/android/translate_message.cc b/components/translate/content/android/translate_message.cc index bdb47105..92813964 100644 --- a/components/translate/content/android/translate_message.cc +++ b/components/translate/content/android/translate_message.cc
@@ -182,10 +182,8 @@ RecordCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION); } - if (ui_delegate_->GetSourceLanguageCode() != source_language) - ui_delegate_->UpdateSourceLanguage(source_language); - if (ui_delegate_->GetTargetLanguageCode() != target_language) - ui_delegate_->UpdateTargetLanguage(target_language); + ui_delegate_->UpdateSourceLanguage(source_language); + ui_delegate_->UpdateTargetLanguage(target_language); if (step == TRANSLATE_STEP_TRANSLATE_ERROR) { // Prevent auto-always-translate from triggering if an error occurs.
diff --git a/components/translate/core/browser/translate_ui_delegate.cc b/components/translate/core/browser/translate_ui_delegate.cc index 64e01f7..3746bac 100644 --- a/components/translate/core/browser/translate_ui_delegate.cc +++ b/components/translate/core/browser/translate_ui_delegate.cc
@@ -228,6 +228,9 @@ void TranslateUIDelegate::UpdateSourceLanguage( const std::string& language_code) { + if (GetSourceLanguageCode() == language_code) { + return; + } for (size_t i = 0; i < languages_.size(); ++i) { if (languages_[i].first.compare(language_code) == 0) { UpdateSourceLanguageIndex(i); @@ -257,6 +260,9 @@ void TranslateUIDelegate::UpdateTargetLanguage( const std::string& language_code) { + if (GetTargetLanguageCode() == language_code) { + return; + } for (size_t i = 0; i < languages_.size(); ++i) { if (languages_[i].first.compare(language_code) == 0) { UpdateTargetLanguageIndex(i);
diff --git a/components/translate/core/browser/translate_ui_delegate.h b/components/translate/core/browser/translate_ui_delegate.h index 793339b..ff42bca 100644 --- a/components/translate/core/browser/translate_ui_delegate.h +++ b/components/translate/core/browser/translate_ui_delegate.h
@@ -69,9 +69,12 @@ // Returns the source language code. std::string GetSourceLanguageCode() const; - // Updates the source language index. + // Updates the source language index if |language_index| is different from the + // current index. void UpdateSourceLanguageIndex(size_t language_index); + // Updates the source language and saves the change for logging if the provided + // |language_code| is valid. void UpdateSourceLanguage(const std::string& language_code); // Returns the target language index. @@ -80,9 +83,12 @@ // Returns the target language code. std::string GetTargetLanguageCode() const; - // Updates the target language index. + // Updates the target language index if |language_index| is different from the + // current index. void UpdateTargetLanguageIndex(size_t language_index); + // Updates the target language and saves the change for logging if the provided + // |language_code| is valid. void UpdateTargetLanguage(const std::string& language_code); // Returns the ISO code for the language at |index|.
diff --git a/components/translate/core/browser/translate_ui_delegate_unittest.cc b/components/translate/core/browser/translate_ui_delegate_unittest.cc index c292ed996..279644f 100644 --- a/components/translate/core/browser/translate_ui_delegate_unittest.cc +++ b/components/translate/core/browser/translate_ui_delegate_unittest.cc
@@ -263,6 +263,40 @@ EXPECT_EQ("fr", delegate_->GetTargetLanguageCode()); } +TEST_F(TranslateUIDelegateTest, UpdateSourceLanguageTranslateEvent) { + // Test source language and corresponding TranslateEvent field. + EXPECT_EQ("ar", delegate_->GetSourceLanguageCode()); + EXPECT_FALSE( + manager_->mutable_translate_event()->has_modified_source_language()); + + // Test that updating with current language does not update TranslateEvent. + delegate_->UpdateSourceLanguage("ar"); + EXPECT_FALSE( + manager_->mutable_translate_event()->has_modified_source_language()); + + // Test that updating with different language does update TranslateEvent. + delegate_->UpdateSourceLanguage("es"); + EXPECT_TRUE( + manager_->mutable_translate_event()->has_modified_source_language()); +} + +TEST_F(TranslateUIDelegateTest, UpdateTargetLanguageTranslateEvent) { + // Test target language and corresponding TranslateEvent field. + EXPECT_EQ("fr", delegate_->GetTargetLanguageCode()); + EXPECT_FALSE( + manager_->mutable_translate_event()->has_modified_target_language()); + + // Test that updating with current language does not update TranslateEvent. + delegate_->UpdateTargetLanguage("fr"); + EXPECT_FALSE( + manager_->mutable_translate_event()->has_modified_target_language()); + + // Test that updating with different language does update TranslateEvent. + delegate_->UpdateTargetLanguage("es"); + EXPECT_TRUE( + manager_->mutable_translate_event()->has_modified_target_language()); +} + TEST_F(TranslateUIDelegateTest, ContentLanguagesWhenPrefChangeObserverEnabled) { testContentLanguages(/*disableObservers=*/false); }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index e14ae9fa..89c0f02 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1875,6 +1875,8 @@ "renderer_host/should_swap_browsing_instance.h", "renderer_host/stored_page.cc", "renderer_host/stored_page.h", + "renderer_host/subframe_history_navigation_throttle.cc", + "renderer_host/subframe_history_navigation_throttle.h", "renderer_host/text_input_manager.cc", "renderer_host/text_input_manager.h", "renderer_host/transient_allow_popup.cc", @@ -2993,6 +2995,8 @@ "android/web_contents_observer_proxy.h", "attribution_reporting/attribution_input_event_tracker_android.cc", "attribution_reporting/attribution_input_event_tracker_android.h", + "attribution_reporting/attribution_os_level_manager_android.cc", + "attribution_reporting/attribution_os_level_manager_android.h", "child_process_launcher_helper_android.cc", "contacts/contacts_provider_android.cc", "contacts/contacts_provider_android.h",
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc index 6923f1ad..625e1bb 100644 --- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc +++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -80,7 +80,7 @@ // the first event. AccessibilityNotificationWaiter waiter(shell()->web_contents(), ui::kAXModeComplete, - ax::mojom::Event::kLayoutComplete); + ax::mojom::Event::kLoadComplete); ASSERT_TRUE(waiter.WaitForNotification()); } @@ -176,7 +176,7 @@ // the first event. AccessibilityNotificationWaiter waiter(shell()->web_contents(), ui::kAXModeComplete, - ax::mojom::Event::kLayoutComplete); + ax::mojom::Event::kLoadComplete); ASSERT_TRUE(waiter.WaitForNotification()); }
diff --git a/content/browser/attribution_reporting/attribution_interop_unittest.cc b/content/browser/attribution_reporting/attribution_interop_unittest.cc index b7725fe..395ac507 100644 --- a/content/browser/attribution_reporting/attribution_interop_unittest.cc +++ b/content/browser/attribution_reporting/attribution_interop_unittest.cc
@@ -12,6 +12,7 @@ #include "base/files/file_util.h" #include "base/path_service.h" #include "base/ranges/algorithm.h" +#include "base/strings/string_piece.h" #include "base/test/scoped_feature_list.h" #include "base/test/values_test_util.h" #include "base/types/expected.h" @@ -63,18 +64,16 @@ return input_paths; } -bool UnorderedMatch(base::Value::List* a, base::Value::List* b) { - if (!a) { - return !b || b->empty(); +void ProcessReports(base::Value::Dict& dict, base::StringPiece key) { + base::Value::List* list = dict.FindList(key); + if (!list) { + return; } - - if (!b) { - return !a || a->empty(); + if (list->empty()) { + dict.Remove(key); + return; } - - base::ranges::sort(*a); - base::ranges::sort(*b); - return *a == *b; + base::ranges::sort(*list); } class AttributionInteropTest : public ::testing::TestWithParam<base::FilePath> { @@ -129,14 +128,14 @@ base::Value::Dict& expected_output_dict = expected_output->GetDict(); - for (const char* field : - {kEventLevelResultsKey, kDebugEventLevelResultsKey, - kAggregatableResultsKey, kDebugAggregatableResultsKey, - kVerboseDebugReportsKey}) { - EXPECT_TRUE(UnorderedMatch(actual_output->FindList(field), - expected_output_dict.FindList(field))) - << field; + for (const char* key : {kEventLevelResultsKey, kDebugEventLevelResultsKey, + kAggregatableResultsKey, kDebugAggregatableResultsKey, + kVerboseDebugReportsKey}) { + ProcessReports(*actual_output, key); + ProcessReports(expected_output_dict, key); } + + EXPECT_THAT(*actual_output, base::test::IsJson(expected_output_dict)); } INSTANTIATE_TEST_SUITE_P(
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc index 480e18ab..03c7e68 100644 --- a/content/browser/attribution_reporting/attribution_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -29,6 +29,7 @@ #include "base/threading/sequence_bound.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" +#include "build/build_config.h" #include "components/attribution_reporting/os_support.mojom.h" #include "components/attribution_reporting/source_registration_error.mojom.h" #include "components/attribution_reporting/suitable_origin.h" @@ -72,6 +73,10 @@ #include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/gurl.h" +#if BUILDFLAG(IS_ANDROID) +#include "content/browser/attribution_reporting/attribution_os_level_manager_android.h" +#endif + namespace content { namespace { @@ -479,6 +484,11 @@ DCHECK(storage_task_runner_); DCHECK(cookie_checker_); DCHECK(report_sender_); + +#if BUILDFLAG(IS_ANDROID) + attribution_os_level_manager_ = + std::make_unique<AttributionOsLevelManagerAndroid>(); +#endif } AttributionManagerImpl::~AttributionManagerImpl() {
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.h b/content/browser/attribution_reporting/attribution_manager_impl.h index 4faae779..a51127f9 100644 --- a/content/browser/attribution_reporting/attribution_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_manager_impl.h
@@ -18,6 +18,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/threading/sequence_bound.h" +#include "build/build_config.h" #include "components/attribution_reporting/os_support.mojom.h" #include "components/attribution_reporting/source_registration_error.mojom-forward.h" #include "content/browser/aggregation_service/aggregation_service.h" @@ -58,6 +59,10 @@ class StoragePartitionImpl; class StoredSource; +#if BUILDFLAG(IS_ANDROID) +class AttributionOsLevelManagerAndroid; +#endif + struct GlobalRenderFrameHostId; struct SendResult; @@ -311,6 +316,11 @@ base::ObserverList<AttributionObserver> observers_; +#if BUILDFLAG(IS_ANDROID) + std::unique_ptr<AttributionOsLevelManagerAndroid> + attribution_os_level_manager_; +#endif + base::WeakPtrFactory<AttributionManagerImpl> weak_factory_{this}; };
diff --git a/content/browser/attribution_reporting/attribution_os_level_manager_android.cc b/content/browser/attribution_reporting/attribution_os_level_manager_android.cc new file mode 100644 index 0000000..4e8a29ed --- /dev/null +++ b/content/browser/attribution_reporting/attribution_os_level_manager_android.cc
@@ -0,0 +1,39 @@ +// 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. + +#include "content/browser/attribution_reporting/attribution_os_level_manager_android.h" + +#include "content/public/android/content_jni_headers/AttributionOsLevelManager_jni.h" +#include "url/android/gurl_android.h" +#include "url/origin.h" + +namespace content { + +AttributionOsLevelManagerAndroid::AttributionOsLevelManagerAndroid() = default; + +AttributionOsLevelManagerAndroid::~AttributionOsLevelManagerAndroid() { + if (jobj_) { + Java_AttributionOsLevelManager_nativeDestroyed( + base::android::AttachCurrentThread(), jobj_); + } +} + +void AttributionOsLevelManagerAndroid::RegisterAttributionSource( + const GURL& registration_url, + const url::Origin& top_level_origin, + bool is_debug_key_allowed) { + JNIEnv* env = base::android::AttachCurrentThread(); + + if (!jobj_) { + jobj_ = Java_AttributionOsLevelManager_Constructor( + env, reinterpret_cast<intptr_t>(this)); + } + + Java_AttributionOsLevelManager_registerAttributionSource( + env, jobj_, url::GURLAndroid::FromNativeGURL(env, registration_url), + url::GURLAndroid::FromNativeGURL(env, top_level_origin.GetURL()), + is_debug_key_allowed); +} + +} // namespace content
diff --git a/content/browser/attribution_reporting/attribution_os_level_manager_android.h b/content/browser/attribution_reporting/attribution_os_level_manager_android.h new file mode 100644 index 0000000..8ebd8b4 --- /dev/null +++ b/content/browser/attribution_reporting/attribution_os_level_manager_android.h
@@ -0,0 +1,46 @@ +// 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. + +#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_OS_LEVEL_MANAGER_ANDROID_H_ +#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_OS_LEVEL_MANAGER_ANDROID_H_ + +#include <jni.h> + +#include "base/android/jni_android.h" + +class GURL; + +namespace url { +class Origin; +} // namespace url + +namespace content { + +// This class is responsible for communicating with java code to handle +// registering events received on the web with Android. +class AttributionOsLevelManagerAndroid { + public: + AttributionOsLevelManagerAndroid(); + ~AttributionOsLevelManagerAndroid(); + + AttributionOsLevelManagerAndroid(const AttributionOsLevelManagerAndroid&) = + delete; + AttributionOsLevelManagerAndroid& operator=( + const AttributionOsLevelManagerAndroid&) = delete; + + AttributionOsLevelManagerAndroid(AttributionOsLevelManagerAndroid&&) = delete; + AttributionOsLevelManagerAndroid& operator=( + AttributionOsLevelManagerAndroid&&) = delete; + + void RegisterAttributionSource(const GURL& registration_url, + const url::Origin& top_level_origin, + bool is_debug_key_allowed); + + private: + base::android::ScopedJavaGlobalRef<jobject> jobj_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_OS_LEVEL_MANAGER_ANDROID_H_
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc index 6c469ad2..df66378 100644 --- a/content/browser/devtools/protocol/storage_handler.cc +++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -294,6 +294,11 @@ owner_origin, params); } + void OnUrnUuidGenerated(const GURL& urn_uuid) override {} + + void OnConfigPopulated( + const absl::optional<FencedFrameConfig>& config) override {} + private: raw_ptr<StorageHandler> const owner_; base::ScopedObservation<
diff --git a/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc b/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc index f902c308..d799bd4 100644 --- a/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc +++ b/content/browser/direct_sockets/direct_sockets_udp_browsertest.cc
@@ -133,49 +133,6 @@ EXPECT_EQ("closeUdp succeeded", EvalJs(shell(), script)); } -IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, SendUdp) { - // We send datagrams with one byte, two bytes, three bytes, ... - const uint32_t kRequiredDatagrams = 35; - const uint32_t kRequiredBytes = - kRequiredDatagrams * (kRequiredDatagrams + 1) / 2; - - // Any attempt to make this a class member results into - // "This caller requires a single-threaded context". - network::test::UDPSocketListenerImpl listener; - mojo::Receiver<network::mojom::UDPSocketListener> listener_receiver{ - &listener}; - - auto [server_address, server_helper] = - CreateUDPServerSocket(listener_receiver.BindNewPipeAndPassRemote()); - - GetUDPServerSocket()->ReceiveMore(kRequiredDatagrams); - - const std::string script = - JsReplace("sendUdp({ remoteAddress: $1, remotePort: $2 }, $3)", - server_address.ToStringWithoutPort(), server_address.port(), - static_cast<int>(kRequiredBytes)); - - EXPECT_EQ("send succeeded", EvalJs(shell(), script)); - - listener.WaitForReceivedResults(kRequiredDatagrams); - EXPECT_EQ(listener.results().size(), kRequiredDatagrams); - - uint32_t bytes_received = 0, expected_data_size = 0; - for (const network::test::UDPSocketListenerImpl::ReceivedResult& result : - listener.results()) { - expected_data_size++; - EXPECT_EQ(result.net_error, net::OK); - EXPECT_TRUE(result.src_addr.has_value()); - EXPECT_TRUE(result.data.has_value()); - EXPECT_EQ(result.data->size(), expected_data_size); - for (uint8_t current : *result.data) { - EXPECT_EQ(current, bytes_received % 256); - ++bytes_received; - } - } - EXPECT_EQ(bytes_received, kRequiredBytes); -} - IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, SendUdpAfterClose) { const int32_t kRequiredBytes = 1; const std::string script = @@ -186,59 +143,6 @@ ::testing::HasSubstr("Stream closed.")); } -IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, ReadUdp) { - const uint32_t kRequiredDatagrams = 35; - const uint32_t kRequiredBytes = - kRequiredDatagrams * (kRequiredDatagrams + 1) / 2; - - network::test::UDPSocketListenerImpl listener; - mojo::Receiver<network::mojom::UDPSocketListener> listener_receiver{ - &listener}; - - auto [server_address, server_helper] = - CreateUDPServerSocket(listener_receiver.BindNewPipeAndPassRemote()); - - // Why so complicated? Turns out that in order to send udp datagrams from - // server to client we need to be aware what the client's local port is. - // It cannot be predefined, so the first step is to create a socket in the - // global scope and retrieve the assigned local port. - const std::string open_socket = JsReplace( - R"((async () => { - socket = new UDPSocket({ remoteAddress: $1, remotePort: $2 }); - let { localPort } = await socket.opened; - return localPort; - })())", - server_address.ToStringWithoutPort(), server_address.port()); - - const uint16_t local_port = EvalJs(shell(), open_socket).ExtractInt(); - - const std::string async_read = content::test::WrapAsync(JsReplace( - R"( - let { readable } = await socket.opened; - let reader = readable.getReader(); - return await readLoop(reader, $1); - )", - static_cast<int>(kRequiredBytes))); - auto future = GetAsyncJsRunner()->RunScript(async_read); - - // With a client socket listening in the javascript code, we can finally start - // sending out data. - net::IPEndPoint client_addr(net::IPAddress::IPv4Localhost(), local_port); - uint32_t bytesSent = 0; - for (uint32_t i = 0; i < kRequiredDatagrams; i++) { - std::vector<uint8_t> message(i + 1); - for (uint8_t& byte : message) { - byte = bytesSent % 256; - bytesSent++; - } - EXPECT_EQ(net::OK, server_helper->SendToSync(client_addr, message)); - } - - // Blocks until script execution is complete and returns the resulting - // message. - ASSERT_EQ(future->Get(), "readLoop succeeded."); -} - IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, ReadUdpAfterSocketClose) { network::test::UDPSocketListenerImpl listener; mojo::Receiver<network::mojom::UDPSocketListener> listener_receiver{ @@ -358,7 +262,7 @@ } IN_PROC_BROWSER_TEST_F(DirectSocketsUdpBrowserTest, ExchangeUdp) { - ASSERT_THAT(EvalJs(shell(), "exchangeSingleUdpPacketBetweenClientAndServer()") + ASSERT_THAT(EvalJs(shell(), "exchangeUdpPacketsBetweenClientAndServer()") .ExtractString(), testing::HasSubstr("succeeded")); }
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping.cc b/content/browser/fenced_frame/fenced_frame_url_mapping.cc index b95e000..53b02f6d8 100644 --- a/content/browser/fenced_frame/fenced_frame_url_mapping.cc +++ b/content/browser/fenced_frame/fenced_frame_url_mapping.cc
@@ -219,7 +219,8 @@ it->second.erase(observer_it); } -void FencedFrameURLMapping::OnSharedStorageURNMappingResultDetermined( +absl::optional<FencedFrameConfig> +FencedFrameURLMapping::OnSharedStorageURNMappingResultDetermined( const GURL& urn_uuid, const SharedStorageURNMappingResult& mapping_result) { auto pending_it = pending_urn_uuid_to_url_map_.find(urn_uuid); @@ -254,6 +255,8 @@ } pending_urn_uuid_to_url_map_.erase(pending_it); + + return config; } SharedStorageBudgetMetadata*
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping.h b/content/browser/fenced_frame/fenced_frame_url_mapping.h index 926cbfb..275ceb6 100644 --- a/content/browser/fenced_frame/fenced_frame_url_mapping.h +++ b/content/browser/fenced_frame/fenced_frame_url_mapping.h
@@ -31,6 +31,9 @@ // Keeps a mapping of fenced frames URN:UUID and URL. Also keeps a set of // pending mapped URN:UUIDs to support asynchronous mapping. See // https://github.com/WICG/fenced-frame/blob/master/explainer/opaque_src.md +// TODO(crbug.com/1405477): Add methods for: +// 1. generating the pending config. +// 2. finalizing the pending config. class CONTENT_EXPORT FencedFrameURLMapping { public: // The runURLSelectionOperation's url mapping result. It contains the mapped @@ -126,7 +129,12 @@ // will trigger the observers' OnFencedFrameURLMappingComplete() method // associated with the `urn_uuid`, unregister those observers, and move the // `urn_uuid` from `pending_urn_uuid_to_url_map_` to `urn_uuid_to_url_map_`. - void OnSharedStorageURNMappingResultDetermined( + // If the resolved URL is fenced-frame-compatible, the return value is the + // populated fenced frame config. It is used to notify the observers in shared + // storage worklet host manager. Tests can then obtain the populated fenced + // frame configs from the observers. + // Otherwise this method returns an absl::nullopt. + absl::optional<FencedFrameConfig> OnSharedStorageURNMappingResultDetermined( const GURL& urn_uuid, const SharedStorageURNMappingResult& mapping_result);
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc index 134901a7..4bd98af 100644 --- a/content/browser/interest_group/auction_runner.cc +++ b/content/browser/interest_group/auction_runner.cc
@@ -158,6 +158,7 @@ void AuctionRunner::ResolvedBuyerTimeoutsPromise( blink::mojom::AuctionAdConfigAuctionIdPtr auction_id, + blink::mojom::AuctionAdConfigBuyerTimeoutField field, const blink::AuctionConfig::BuyerTimeouts& buyer_timeouts) { if (state_ == State::kFailed) { return; @@ -171,14 +172,24 @@ return; } - if (!config->non_shared_params.buyer_timeouts.is_promise()) { + blink::AuctionConfig::MaybePromiseBuyerTimeouts* field_ptr = nullptr; + switch (field) { + case blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts: + field_ptr = &config->non_shared_params.buyer_timeouts; + break; + case blink::mojom::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts: + field_ptr = &config->non_shared_params.buyer_cumulative_timeouts; + break; + } + + if (!field_ptr->is_promise()) { mojo::ReportBadMessage("ResolvedBuyerTimeoutsPromise updating non-promise"); return; } - config->non_shared_params.buyer_timeouts = - blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( - buyer_timeouts); + *field_ptr = blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( + buyer_timeouts); NotifyPromiseResolved(auction_id.get(), config); }
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h index 5a2c415d..399875b 100644 --- a/content/browser/interest_group/auction_runner.h +++ b/content/browser/interest_group/auction_runner.h
@@ -168,6 +168,7 @@ per_buyer_signals) override; void ResolvedBuyerTimeoutsPromise( blink::mojom::AuctionAdConfigAuctionIdPtr auction_id, + blink::mojom::AuctionAdConfigBuyerTimeoutField field, const blink::AuctionConfig::BuyerTimeouts& buyer_timeouts) override; void ResolvedDirectFromSellerSignalsPromise( blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc index e366452..3e6bdf88 100644 --- a/content/browser/interest_group/auction_runner_unittest.cc +++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -557,13 +557,16 @@ if (signals1[auctionConfig.seller + 'Signals'] !== 'Ad PlatformSignals') throw new Error("Wrong perBuyerSignals in auctionConfig"); if (typeof auctionConfig.perBuyerTimeouts['https://adplatform.com'] !== - "number") { + "number" || + typeof auctionConfig.perBuyerTimeouts['*'] !== "number") { throw new Error("timeout in auctionConfig.perBuyerTimeouts is not a " + "number. huh"); } - if (typeof auctionConfig.perBuyerTimeouts['*'] !== "number") { - throw new Error("timeout in auctionConfig.perBuyerTimeouts is not a " + - "number. huh"); + if (auctionConfig.perBuyerCumulativeTimeouts['https://adplatform.com'] !== + 12345 || + auctionConfig.perBuyerCumulativeTimeouts['*'] !== 23456) { + throw new Error("timeout in auctionConfig.perBuyerCumulativeTimeouts " + + "is the wrong value. huh"); } if (auctionConfig.sellerSignals["url"] != decisionLogicUrl) throw new Error("Wrong sellerSignals"); @@ -1286,6 +1289,21 @@ } } + blink::AuctionConfig::MaybePromiseBuyerTimeouts MakeBuyerCumulativeTimeouts( + bool use_promise) { + if (use_promise) { + return blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromPromise(); + } else { + blink::AuctionConfig::BuyerTimeouts buyer_cumulative_timeouts; + buyer_cumulative_timeouts.per_buyer_timeouts.emplace(); + buyer_cumulative_timeouts.per_buyer_timeouts.value()[kBidder1] = + base::Milliseconds(12345); + buyer_cumulative_timeouts.all_buyers_timeout = base::Milliseconds(23456); + return blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( + std::move(buyer_cumulative_timeouts)); + } + } + // Helper to create an auction config with the specified values. blink::AuctionConfig CreateAuctionConfig( const GURL& seller_decision_logic_url, @@ -1307,6 +1325,8 @@ use_promise_for_per_buyer_signals_, auction_config.seller); auction_config.non_shared_params.buyer_timeouts = MakeBuyerTimeouts(use_promise_for_buyer_timeouts_); + auction_config.non_shared_params.buyer_cumulative_timeouts = + MakeBuyerCumulativeTimeouts(use_promise_for_buyer_cumulative_timeouts_); auction_config.non_shared_params.auction_signals = MakeAuctionSignals( use_promise_for_auction_signals_, auction_config.seller); @@ -1886,6 +1906,7 @@ bool use_promise_for_auction_signals_ = false; bool use_promise_for_per_buyer_signals_ = false; bool use_promise_for_buyer_timeouts_ = false; + bool use_promise_for_buyer_cumulative_timeouts_ = false; // Unlike others, this is only test with promises at this level. bool pass_promise_for_direct_from_seller_signals_ = false; @@ -5065,10 +5086,12 @@ HasSubstr("Uncaught Error: wrong auctionSignals.")))); } -// An auction that passes perBuyerSignals and buyerTimeouts via promises. +// An auction that passes perBuyerSignals, perBuyerTimeouts, and +// perBuyerCumulativeTimeouts via promises. TEST_F(AuctionRunnerTest, PromiseSignals3) { use_promise_for_per_buyer_signals_ = true; use_promise_for_buyer_timeouts_ = true; + use_promise_for_buyer_cumulative_timeouts_ = true; auction_worklet::AddJavascriptResponse( &url_loader_factory_, kBidder1Url, @@ -5107,11 +5130,20 @@ task_environment()->RunUntilIdle(); EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); - // Feed in buyerTimeouts. + // Feed in perBuyerTimeouts. abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, MakeBuyerTimeouts(/*use_promise=*/false).value()); + task_environment()->RunUntilIdle(); + EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); + // Feed in perBuyerCumulativeTimeouts. + abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( + blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts, + MakeBuyerCumulativeTimeouts(/*use_promise=*/false).value()); auction_run_loop_->Run(); EXPECT_EQ(InterestGroupKey(kBidder2, kBidder2Name), result_.winning_group_id); @@ -5119,7 +5151,7 @@ EXPECT_THAT(result_.errors, testing::ElementsAre()); } -// An auction that passes perBuyerSignals and buyerTimeouts via promises. +// An auction that passes perBuyerSignals and perBuyerTimeouts via promises. // Empty values are provided, which causes the validation scripts to complain. TEST_F(AuctionRunnerTest, PromiseSignals4) { use_promise_for_per_buyer_signals_ = true; @@ -5159,9 +5191,10 @@ task_environment()->RunUntilIdle(); EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); - // Feed in buyerTimeouts. + // Feed in perBuyerTimeouts. abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, blink::AuctionConfig::BuyerTimeouts()); auction_run_loop_->Run(); @@ -5591,10 +5624,11 @@ task_environment()->RunUntilIdle(); EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); - // Feed in sellerSignals with wrong component ID. + // Feed in perBuyerSignals with wrong component ID. blink::AuctionConfig::BuyerTimeouts buyer_timeouts; abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( blink::mojom::AuctionAdConfigAuctionId::NewComponentAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, buyer_timeouts); auction_run_loop_->RunUntilIdle(); EXPECT_EQ("Invalid auction ID in ResolvedBuyerTimeoutsPromise", @@ -5602,6 +5636,49 @@ } TEST_F(AuctionRunnerTest, PromiseSignalsBadAuctionId4) { + use_promise_for_buyer_cumulative_timeouts_ = true; + + auction_worklet::AddJavascriptResponse( + &url_loader_factory_, kBidder1Url, + MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0, + kBidder1, kBidder1Name)); + auction_worklet::AddJavascriptResponse( + &url_loader_factory_, kBidder2Url, + MakeBidScript(kSeller, "2", "https://ad2.com/", /*num_ad_components=*/0, + kBidder2, kBidder2Name)); + auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl, + MakeAuctionScript()); + + std::vector<StorageInterestGroup> bidders; + bidders.emplace_back(MakeInterestGroup( + kBidder1, kBidder1Name, kBidder1Url, + /*trusted_bidding_signals_url=*/absl::nullopt, + /*trusted_bidding_signals_keys=*/{}, GURL("https://ad1.com"), + /*ad_component_urls=*/absl::nullopt)); + bidders.emplace_back(MakeInterestGroup( + kBidder2, kBidder2Name, kBidder2Url, + /*trusted_bidding_signals_url=*/absl::nullopt, + /*trusted_bidding_signals_keys=*/{}, GURL("https://ad2.com"), + /*ad_component_urls=*/absl::nullopt)); + StartAuction(kSellerUrl, std::move(bidders)); + + // Can't complete yet. + task_environment()->RunUntilIdle(); + EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); + + // Feed in perBuyerSignals with wrong component ID. + blink::AuctionConfig::BuyerTimeouts buyer_cumulative_timeouts; + abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( + blink::mojom::AuctionAdConfigAuctionId::NewComponentAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts, + buyer_cumulative_timeouts); + auction_run_loop_->RunUntilIdle(); + EXPECT_EQ("Invalid auction ID in ResolvedBuyerTimeoutsPromise", + TakeBadMessage()); +} + +TEST_F(AuctionRunnerTest, PromiseSignalsBadAuctionId5) { pass_promise_for_direct_from_seller_signals_ = true; auction_worklet::AddJavascriptResponse( @@ -5938,16 +6015,70 @@ // Feed in buyer timeouts twice. blink::AuctionConfig::BuyerTimeouts timeouts; abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( - blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), timeouts); + blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, + timeouts); abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( - blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), timeouts); + blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, + timeouts); + task_environment()->RunUntilIdle(); + EXPECT_EQ("ResolvedBuyerTimeoutsPromise updating non-promise", + TakeBadMessage()); +} + +// Trying to update buyer cumulative timeouts twice. +TEST_F(AuctionRunnerTest, PromiseSignalsUpdateNonPromise7) { + // Have two kind of promises so we don't just finish after first update. + use_promise_for_per_buyer_signals_ = true; + use_promise_for_buyer_cumulative_timeouts_ = true; + + auction_worklet::AddJavascriptResponse( + &url_loader_factory_, kBidder1Url, + MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0, + kBidder1, kBidder1Name)); + auction_worklet::AddJavascriptResponse( + &url_loader_factory_, kBidder2Url, + MakeBidScript(kSeller, "2", "https://ad2.com/", /*num_ad_components=*/0, + kBidder2, kBidder2Name)); + auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl, + MakeAuctionScript()); + + std::vector<StorageInterestGroup> bidders; + bidders.emplace_back(MakeInterestGroup( + kBidder1, kBidder1Name, kBidder1Url, + /*trusted_bidding_signals_url=*/absl::nullopt, + /*trusted_bidding_signals_keys=*/{}, GURL("https://ad1.com"), + /*ad_component_urls=*/absl::nullopt)); + bidders.emplace_back(MakeInterestGroup( + kBidder2, kBidder2Name, kBidder2Url, + /*trusted_bidding_signals_url=*/absl::nullopt, + /*trusted_bidding_signals_keys=*/{}, GURL("https://ad2.com"), + /*ad_component_urls=*/absl::nullopt)); + StartAuction(kSellerUrl, std::move(bidders)); + + // Can't complete yet. + task_environment()->RunUntilIdle(); + EXPECT_FALSE(auction_run_loop_->AnyQuitCalled()); + + // Feed in buyer timeouts twice. + blink::AuctionConfig::BuyerTimeouts timeouts; + abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( + blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts, + timeouts); + abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( + blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0), + blink::mojom::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts, + timeouts); task_environment()->RunUntilIdle(); EXPECT_EQ("ResolvedBuyerTimeoutsPromise updating non-promise", TakeBadMessage()); } // Trying to update direct from seller signals twice. -TEST_F(AuctionRunnerTest, PromiseSignalsUpdateNonPromise7) { +TEST_F(AuctionRunnerTest, PromiseSignalsUpdateNonPromise8) { // Have two kind of promises so we don't just finish after first update. use_promise_for_per_buyer_signals_ = true; pass_promise_for_direct_from_seller_signals_ = true;
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc index a0cd698..97ed794 100644 --- a/content/browser/interest_group/interest_group_browsertest.cc +++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -3619,7 +3619,7 @@ } // Exercise error-handling path in the renderer for promise-delivered -// perBuyerSignals. +// perBuyerTimeouts. IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionResolvePromiseInvalidPerBuyerTimeouts) { GURL test_url = https_server_->GetURL("a.test", "/echo"); @@ -3681,6 +3681,111 @@ WaitForAccessObserved({}); } +// Test rejection path in the renderer for promise-delivered +// perBuyerCumulativeTimeouts. +IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, + RunAdAuctionRejectPromisePerBuyerCumulativeTimeouts) { + GURL test_url = https_server_->GetURL("a.test", "/echo"); + url::Origin test_origin = url::Origin::Create(test_url); + ASSERT_TRUE(NavigateToURL(shell(), test_url)); + GURL ad_url = https_server_->GetURL("c.test", "/echo?render_cars"); + GURL decision_url = + https_server_->GetURL("a.test", "/interest_group/decision_logic.js"); + // Note: at present at least one bid must be made for promise checking to + // be guaranteed to happen; if the auction is (effectively) empty whether + // it happens or not is timing-dependent. + EXPECT_EQ( + kSuccess, + JoinInterestGroupAndVerify( + /*owner=*/test_origin, + /*name=*/"cars", + /*priority=*/0.0, + /*execution_mode=*/ + blink::InterestGroup::ExecutionMode::kCompatibilityMode, + /*bidding_url=*/ + https_server_->GetURL("a.test", "/interest_group/bidding_logic.js"), + /*ads=*/{{{ad_url, /*metadata=*/absl::nullopt}}})); + + const char kAuctionConfigTemplate[] = R"({ + seller: $1, + decisionLogicUrl: $2, + perBuyerCumulativeTimeouts: new Promise((resolve, reject) => { setTimeout( + () => { reject('boo'); }, 10) }), + interestGroupBuyers: [$1] + })"; + + EXPECT_EQ("Promise argument rejected or resolved to invalid value.", + RunAuctionAndWait( + JsReplace(kAuctionConfigTemplate, test_origin, decision_url))); + WaitForAccessObserved({}); +} + +// Exercise error-handling path in the renderer for promise-delivered +// perBuyerCumulativeTimeouts. +IN_PROC_BROWSER_TEST_F( + InterestGroupBrowserTest, + RunAdAuctionResolvePromiseInvalidPerBuyerCumulativeTimeouts) { + GURL test_url = https_server_->GetURL("a.test", "/echo"); + url::Origin test_origin = url::Origin::Create(test_url); + ASSERT_TRUE(NavigateToURL(shell(), test_url)); + GURL ad_url = https_server_->GetURL("c.test", "/echo?render_cars"); + GURL decision_url = + https_server_->GetURL("a.test", "/interest_group/decision_logic.js"); + // Note: at present at least one bid must be made for promise checking to + // be guaranteed to happen; if the auction is (effectively) empty whether + // it happens or not is timing-dependent. + EXPECT_EQ( + kSuccess, + JoinInterestGroupAndVerify( + /*owner=*/test_origin, + /*name=*/"cars", + /*priority=*/0.0, + /*execution_mode=*/ + blink::InterestGroup::ExecutionMode::kCompatibilityMode, + /*bidding_url=*/ + https_server_->GetURL("a.test", "/interest_group/bidding_logic.js"), + /*ads=*/{{{ad_url, /*metadata=*/absl::nullopt}}})); + + const char kAuctionConfigTemplate[] = R"({ + seller: $1, + decisionLogicUrl: $2, + perBuyerCumulativeTimeouts: new Promise((resolve, reject) => { setTimeout( + () => { resolve({'http://b.com': 52}); }, 10) }), + interestGroupBuyers: [$1] + })"; + + WebContentsConsoleObserver console_observer(shell()->web_contents()); + console_observer.SetPattern( + "Uncaught (in promise) TypeError: Failed to execute 'runAdAuction' on " + "'NavigatorAuction': perBuyerCumulativeTimeouts buyer 'http://b.com' for " + "AuctionAdConfig with seller 'https://a.test:*' must be \"*\" (wildcard) " + "or a valid https origin."); + EXPECT_EQ("Promise argument rejected or resolved to invalid value.", + RunAuctionAndWait( + JsReplace(kAuctionConfigTemplate, test_origin, decision_url))); + EXPECT_TRUE(console_observer.Wait()); + WaitForAccessObserved({}); +} + +IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, + RunAdAuctionInvalidPerBuyerCumulativeTimeoutsOrigin) { + ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo"))); + + EXPECT_EQ( + "TypeError: Failed to execute 'runAdAuction' on 'Navigator': " + "perBuyerCumulativeTimeouts buyer 'https://invalid^&' for " + "AuctionAdConfig " + "with seller 'https://test.com' must be \"*\" (wildcard) or a valid " + "https " + "origin.", + RunAuctionAndWait(R"({ + seller: 'https://test.com', + decisionLogicUrl: 'https://test.com', + perBuyerCumulativeTimeouts: {'https://invalid^&': 100} + })")); + WaitForAccessObserved({}); +} + IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionInvalidPerBuyerGroupLimitsValue) { ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo"))); @@ -7512,6 +7617,7 @@ sellerTimeout: 200, perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}}, perBuyerTimeouts: {$4: 110, $5: 120, '*': 150}, + perBuyerCumulativeTimeouts: {$4: 130, $5: 140, '*': 160}, perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}} }); })())", @@ -7633,6 +7739,7 @@ sellerTimeout: 200, perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}}, perBuyerTimeouts: {$4: 110, $5: 120, '*': 150}, + perBuyerCumulativeTimeouts: {$4: 130, $5: 140, '*': 160}, perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}} }); })())", @@ -7801,6 +7908,7 @@ sellerTimeout: 300, perBuyerSignals: maybePromise({$8: ["top-level buyer signals"]}), perBuyerTimeouts: maybePromise({$8: 110, '*': 150}), + perBuyerCumulativeTimeouts: maybePromise({$8: 111, '*': 151}), perBuyerPrioritySignals: {'*': {foo: 3}}, componentAuctions: [{ seller: $5, @@ -7813,6 +7921,7 @@ sellerTimeout: 200, perBuyerSignals: maybePromise({$8: ["component buyer signals"]}), perBuyerTimeouts: maybePromise({$8: 200}), + perBuyerCumulativeTimeouts: maybePromise({$8: 201}), perBuyerPrioritySignals: {$8: {bar: 1}, '*': {BaZ: -2}}, }], }); @@ -8409,8 +8518,10 @@ EXPECT_EQ(GURL("https://example.com/render"), observer.mapped_url()); } -// Test for perBuyerTimeouts being passed to runAdAuction as promises. -IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, PromiseBuyerTimeouts) { +// Test for perBuyerTimeouts and perBuyerCumulativeTimeouts being passed to +// runAdAuction as promises. +IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, + PromiseBuyerTimeoutsAndCumulativeBuyerTimeouts) { // These scripts are generated by this test. constexpr char kBiddingLogicPath[] = "/interest_group/test_generated_bidding_argument_validator.js"; @@ -8438,22 +8549,21 @@ function scoreAd( adMetadata, bid, auctionConfig, unusedTrustedScoringSignals, unusedBrowserSignals) { - validateAuctionConfig(auctionConfig); + validatePerBuyerTimeouts(auctionConfig.perBuyerTimeouts); + validatePerBuyerCumulativeTimeouts(auctionConfig.perBuyerCumulativeTimeouts); return bid; } -function validateAuctionConfig(auctionConfig) { - const perBuyerTimeoutsJSON = JSON.stringify(auctionConfig.perBuyerTimeouts); +function validatePerBuyerTimeouts(perBuyerTimeouts) { + const perBuyerTimeoutsJSON = JSON.stringify(perBuyerTimeouts); let ok = 0; - for (key in auctionConfig.perBuyerTimeouts) { - if (key.startsWith("https://a.test") && - auctionConfig.perBuyerTimeouts[key] === 50) { + for (key in perBuyerTimeouts) { + if (key.startsWith("https://a.test") && perBuyerTimeouts[key] === 50) { ++ok; - } else if (key.startsWith("https://b.test") && - auctionConfig.perBuyerTimeouts[key] === 60) { + } else if (key === "https://b.test" && perBuyerTimeouts[key] === 60) { ++ok; } else if (key === '*' && - auctionConfig.perBuyerTimeouts[key] === 56) { + perBuyerTimeouts[key] === 56) { ++ok; } else { throw 'Wrong key in perBuyerTimeouts ' + perBuyerTimeoutsJSON; @@ -8463,6 +8573,30 @@ throw 'Wrong perBuyerTimeouts ' + perBuyerTimeoutsJSON; } } + +function validatePerBuyerCumulativeTimeouts(perBuyerCumulativeTimeouts) { + const perBuyerCumulativeTimeoutsJSON = + JSON.stringify(perBuyerCumulativeTimeouts); + let ok = 0; + for (key in perBuyerCumulativeTimeouts) { + if (key.startsWith("https://a.test") && + perBuyerCumulativeTimeouts[key] === 70) { + ++ok; + } else if (key === "https://c.test" && + perBuyerCumulativeTimeouts[key] === 80) { + ++ok; + } else if (key === '*' && + perBuyerCumulativeTimeouts[key] === 76) { + ++ok; + } else { + throw 'Wrong key in perCumulativeBuyerTimeouts ' + + perBuyerCumulativeTimeoutsJSON; + } + } + if (ok !== 3) { + throw 'Wrong perCumulativeBuyerTimeouts ' + perBuyerCumulativeTimeoutsJSON; + } +} )"; network_responder_->RegisterNetworkResponse( @@ -8505,6 +8639,10 @@ perBuyerTimeouts: new Promise((resolve, reject) => { setTimeout( () => { resolve({$1: 50, 'https://b.test': 60, '*': 56}); }, 1) + }), + perBuyerCumulativeTimeouts: new Promise((resolve, reject) => { + setTimeout( + () => { resolve({$1: 70, 'https://c.test': 80, '*': 76}); }, 1) }) }); })())",
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc index 1ee80fd..8c0177e 100644 --- a/content/browser/renderer_host/navigation_controller_impl.cc +++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -3253,6 +3253,32 @@ // function. std::unique_ptr<PendingEntryRef> pending_entry_ref = ReferencePendingEntry(); + // If there is a main-frame same-document history navigation, we may defer + // the subframe history navigations in order to give JS in the main frame the + // opportunity to cancel the entire traverse via the navigate event. In that + // case, we need to stash the main frame request's navigation token on the + // subframes, so they can look up the main frame request and defer themselves + // until it completes. + if (!same_document_loads.empty() && + same_document_loads.at(0)->frame_tree_node()->IsMainFrame()) { + NavigationRequest* main_frame_request = same_document_loads.at(0).get(); + // The token will only be returned in cases where deferring the navigation + // is necessary. + if (auto main_frame_same_document_token = + main_frame_request->GetNavigationTokenForDeferringSubframes()) { + for (auto& item : same_document_loads) { + if (item.get() != main_frame_request) { + item->set_main_frame_same_document_history_token( + main_frame_same_document_token); + } + } + for (auto& item : different_document_loads) { + item->set_main_frame_same_document_history_token( + main_frame_same_document_token); + } + } + } + // Send all the same document frame loads before the different document loads. for (auto& item : same_document_loads) { FrameTreeNode* frame = item->frame_tree_node();
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index c7279d9..af5da1f4 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -1971,6 +1971,15 @@ *pending_navigation_api_key_, blink::mojom::TraverseCancelledReason::kAbortedBeforeCommit); } + + // If subframe history navigations were deferred waiting for this request, + // the cancelation of this request should cancel them, too. + for (auto& throttle : subframe_history_navigation_throttles_) { + if (throttle) { + throttle->Cancel(); + } + } + subframe_history_navigation_throttles_.clear(); } // If this NavigationRequest is the last one referencing the pending @@ -6567,6 +6576,8 @@ frame_tree_node()->SetCollapsed(false); } + UnblockPendingSubframeNavigationRequestsIfNeeded(); + if (service_worker_handle_) { // Notify the service worker navigation handle that the navigation finished // committing. @@ -8436,6 +8447,47 @@ } } +absl::optional<base::UnguessableToken> +NavigationRequest::GetNavigationTokenForDeferringSubframes() { + DCHECK(IsInMainFrame()); + if (!base::FeatureList::IsEnabled( + blink::features::kNavigateEventCancelableTraversals)) { + return absl::nullopt; + } + if (!IsSameDocument() || + !NavigationTypeUtils::IsHistory(common_params_->navigation_type)) { + return absl::nullopt; + } + RenderFrameHostImpl* current_frame_host = + frame_tree_node_->current_frame_host(); + if (!current_frame_host->has_navigate_event_handler()) { + return absl::nullopt; + } + if (commit_params_->is_browser_initiated && + !current_frame_host->IsHistoryUserActivationActive()) { + return absl::nullopt; + } + return commit_params_->navigation_token; +} + +void NavigationRequest::AddDeferredSubframeNavigationThrottle( + base::WeakPtr<SubframeHistoryNavigationThrottle> throttle) { + DCHECK(IsInMainFrame()); + subframe_history_navigation_throttles_.push_back(throttle); +} + +void NavigationRequest::UnblockPendingSubframeNavigationRequestsIfNeeded() { + // After a main frame same-document history navigation completes successfully, + // we can resume any corresponding subframe history navigations that were + // blocked on it. + for (auto& throttle : subframe_history_navigation_throttles_) { + if (throttle) { + throttle->Resume(); + } + } + subframe_history_navigation_throttles_.clear(); +} + bool NavigationRequest::IsServedFromBackForwardCache() const { return is_back_forward_cache_restore_; }
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h index 9b5228864..1d1b3e3 100644 --- a/content/browser/renderer_host/navigation_request.h +++ b/content/browser/renderer_host/navigation_request.h
@@ -36,6 +36,7 @@ #include "content/browser/renderer_host/render_frame_host_csp_context.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/should_swap_browsing_instance.h" +#include "content/browser/renderer_host/subframe_history_navigation_throttle.h" #include "content/browser/site_instance_impl.h" #include "content/common/content_export.h" #include "content/common/navigation_client.mojom-forward.h" @@ -1095,6 +1096,43 @@ pending_navigation_api_key_ = key; } + // Returns the navigation token for this request if this NavigationRequest + // is for a history navigation, and it might be cancelled via the navigate + // event. In that case, any associated subframe history navigations will be + // deferred until this navigation commits. + // This may only be called if this is a main-frame same-document navigation. + // Will return a valid token if canceling traversals via the navigate event is + // enabled, this is a history navigation, a navigate event is registered in + // the renderer, and the frame has met the user activation requirements to be + // allowed to cancel the navigation. + // This token will then be provided to the subframe NavigationRequests via + // set_main_frame_same_document_history_token(). + absl::optional<base::UnguessableToken> + GetNavigationTokenForDeferringSubframes(); + + // For subframe NavigationRequests, these set and return the main frame's + // NavigationRequest token, in the case that the main frame returns it from + // GetNavigationTokenForDeferringSubframes(). + void set_main_frame_same_document_history_token( + absl::optional<base::UnguessableToken> token) { + main_frame_same_document_navigation_token_ = token; + } + absl::optional<base::UnguessableToken> + main_frame_same_document_history_token() { + return main_frame_same_document_navigation_token_; + } + + // When a SubframeHistoryNavigationThrottle is created, it registers itself + // with the NavigationRequest for the main frame. If the main frame + // NavigationRequest commits, then these throttles will be resumed in + // UnblockPendingSubframeNavigationRequestsIfNeeded(). + // If it is canceled instead, these throttles will all be canceled. + // Takes a WeakPtr because `throttle` is owned by another NavigationRequest, + // and that request may be canceled outside of our control, in which case + // `throttle` will be destroyed. + void AddDeferredSubframeNavigationThrottle( + base::WeakPtr<SubframeHistoryNavigationThrottle> throttle); + private: friend class NavigationRequestTest; @@ -1732,6 +1770,12 @@ // See https://crbug.com/1412365 void CheckSoftNavigationHeuristicsInvariants(); + // Used to resume any SubframeHistoryNavigationThrottles in this FrameTree + // when this NavigationRequest commits. + // `subframe_history_navigation_throttles_` will only be populated if this + // IsInMainFrame(). + void UnblockPendingSubframeNavigationRequestsIfNeeded(); + // Never null. The pointee node owns this navigation request instance. FrameTreeNode* const frame_tree_node_; @@ -2353,6 +2397,25 @@ // See `set_pending_navigation_api_key()` for context. absl::optional<std::string> pending_navigation_api_key_; + // If this NavigationRequest is for a main-frame same-document back/forward + // navigation, any subframe NavigationRequests are deferred until the renderer + // has a chance to fire a navigate event. If the navigate + // event allows the navigation to proceed, + // UnblockPendingSubframeNavigationRequestsIfNeeded() will resume these + // requests + std::vector<base::WeakPtr<SubframeHistoryNavigationThrottle>> + subframe_history_navigation_throttles_; + + // If this NavigationRequest is in a subframe and part of a history traversal, + // and the main frame is performing a same-document navigation, this token + // may be set if there is a possibility that JS in the main frame will cancel + // the history traversal via the navigate event. In that case, this token is + // used to look up the main frame's NavigationRequest so that it can be passed + // SubframeHistoryNavigationThrottle can defer this request until the main + // frame commits. + absl::optional<base::UnguessableToken> + main_frame_same_document_navigation_token_; + base::WeakPtrFactory<NavigationRequest> weak_factory_{this}; };
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc index bf471013..8cfee57 100644 --- a/content/browser/renderer_host/navigation_throttle_runner.cc +++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -197,6 +197,14 @@ // to the end to not delay running other throttles. AddThrottle(RendererCancellationThrottle::MaybeCreateThrottleFor(request)); + // Defer any cross-document subframe history navigations if there is an + // associated main-frame same-document history navigation in progress, until + // the main frame has had an opportunity to fire a navigate event in the + // renderer. If the navigate event cancels the history navigation, the + // subframe navigations should not proceed. + AddThrottle( + SubframeHistoryNavigationThrottle::MaybeCreateThrottleFor(request)); + // Insert all testing NavigationThrottles last. throttles_.insert(throttles_.end(), std::make_move_iterator(testing_throttles.begin()), @@ -219,10 +227,15 @@ // Unit tests that do not use NavigationRequest should never call // RegisterNavigationThrottlesForCommitWithoutUrlLoader as this function // expects |delegate_| to be a NavigationRequest. - // - // TODO(japhet): Uncomment this once there are throttles for commits without - // a URL loader. - // NavigationRequest* request = static_cast<NavigationRequest*>(delegate_); + NavigationRequest* request = static_cast<NavigationRequest*>(delegate_); + + // Defer any same-document subframe history navigations if there is an + // associated main-frame same-document history navigation in progress, until + // the main frame has had an opportunity to fire a navigate event in the + // renderer. If the navigate event cancels the history navigation, the + // subframe navigations should not proceed. + AddThrottle( + SubframeHistoryNavigationThrottle::MaybeCreateThrottleFor(request)); // Insert all testing NavigationThrottles last. throttles_.insert(throttles_.end(),
diff --git a/content/browser/renderer_host/subframe_history_navigation_throttle.cc b/content/browser/renderer_host/subframe_history_navigation_throttle.cc new file mode 100644 index 0000000..fc5881d --- /dev/null +++ b/content/browser/renderer_host/subframe_history_navigation_throttle.cc
@@ -0,0 +1,73 @@ +// 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. + +#include "content/browser/renderer_host/subframe_history_navigation_throttle.h" + +#include "content/browser/renderer_host/frame_tree.h" +#include "content/browser/renderer_host/frame_tree_node.h" +#include "content/browser/renderer_host/navigation_request.h" +#include "content/browser/renderer_host/render_frame_host_impl.h" +#include "content/public/browser/navigation_handle.h" + +namespace content { + +SubframeHistoryNavigationThrottle::SubframeHistoryNavigationThrottle( + NavigationHandle* navigation_handle) + : NavigationThrottle(navigation_handle) {} + +SubframeHistoryNavigationThrottle::~SubframeHistoryNavigationThrottle() = + default; + +NavigationThrottle::ThrottleCheckResult +SubframeHistoryNavigationThrottle::WillStartRequest() { + // This will defer cross-document subframe history requests. + return DEFER; +} + +NavigationThrottle::ThrottleCheckResult +SubframeHistoryNavigationThrottle::WillCommitWithoutUrlLoader() { + // This will defer same-document subframe history commits. + return DEFER; +} + +const char* SubframeHistoryNavigationThrottle::GetNameForLogging() { + return "SubframeHistoryNavigationThrottle"; +} + +void SubframeHistoryNavigationThrottle::Resume() { + NavigationThrottle::Resume(); +} + +void SubframeHistoryNavigationThrottle::Cancel() { + CancelDeferredNavigation(CANCEL_AND_IGNORE); +} + +// static +std::unique_ptr<NavigationThrottle> +SubframeHistoryNavigationThrottle::MaybeCreateThrottleFor( + NavigationHandle* navigation_handle) { + // `main_frame_same_document_history_token()` will only be set if the + // navigation is a main-frame same-document history navigation and it might + // be cancelable by a navigate event. There's no need to defer the subframe + // navigation unless the navigate event might cancel the entire history + // traversal. + NavigationRequest* request = NavigationRequest::From(navigation_handle); + auto main_frame_token = request->main_frame_same_document_history_token(); + if (!main_frame_token) { + return nullptr; + } + DCHECK(!request->IsInMainFrame()); + + RenderFrameHostImpl* root_frame_host = + request->frame_tree_node()->frame_tree().root()->current_frame_host(); + NavigationRequest* root_frame_navigation_request = + root_frame_host->GetSameDocumentNavigationRequest(*main_frame_token); + DCHECK(root_frame_navigation_request); + auto throttle = std::make_unique<SubframeHistoryNavigationThrottle>(request); + root_frame_navigation_request->AddDeferredSubframeNavigationThrottle( + throttle->weak_factory_.GetWeakPtr()); + return throttle; +} + +} // namespace content
diff --git a/content/browser/renderer_host/subframe_history_navigation_throttle.h b/content/browser/renderer_host/subframe_history_navigation_throttle.h new file mode 100644 index 0000000..6bbd509 --- /dev/null +++ b/content/browser/renderer_host/subframe_history_navigation_throttle.h
@@ -0,0 +1,46 @@ +// 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. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_SUBFRAME_HISTORY_NAVIGATION_THROTTLE_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SUBFRAME_HISTORY_NAVIGATION_THROTTLE_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/navigation_throttle.h" + +namespace content { + +// Defers subframe history navigations while waiting for a main-frame +// same-document history navigation. If a main-frame same-document navigation is +// cancelled by the web-exposed Navigation API, all associated subframe history +// navigations should also be cancelled. +class SubframeHistoryNavigationThrottle final : public NavigationThrottle { + public: + explicit SubframeHistoryNavigationThrottle( + NavigationHandle* navigation_handle); + SubframeHistoryNavigationThrottle(const SubframeHistoryNavigationThrottle&) = + delete; + SubframeHistoryNavigationThrottle& operator=( + const SubframeHistoryNavigationThrottle&) = delete; + ~SubframeHistoryNavigationThrottle() final; + + // NavigationThrottle method: + ThrottleCheckResult WillStartRequest() final; + ThrottleCheckResult WillCommitWithoutUrlLoader() final; + const char* GetNameForLogging() final; + void Resume() final; + + void Cancel(); + + static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor( + NavigationHandle* navigation_handle); + + private: + base::WeakPtrFactory<SubframeHistoryNavigationThrottle> weak_factory_{this}; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SUBFRAME_HISTORY_NAVIGATION_THROTTLE_H_
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc index 7798aeb4..db96c1f 100644 --- a/content/browser/shared_storage/shared_storage_browsertest.cc +++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -8,6 +8,7 @@ #include <tuple> #include <vector> +#include "base/functional/overloaded.h" #include "base/memory/raw_ptr.h" #include "base/metrics/statistics_recorder.h" #include "base/strings/strcat.h" @@ -18,6 +19,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/test/test_future.h" +#include "base/test/with_feature_override.h" #include "content/browser/private_aggregation/private_aggregation_manager_impl.h" #include "content/browser/private_aggregation/private_aggregation_test_utils.h" #include "content/browser/renderer_host/navigation_request.h" @@ -38,9 +40,9 @@ #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_content_browser_client.h" #include "content/public/test/content_browser_test_utils.h" -#include "content/public/test/fenced_frame_test_util.h" #include "content/public/test/test_frame_navigation_observer.h" #include "content/public/test/test_navigation_observer.h" +#include "content/public/test/test_select_url_fenced_frame_config_observer.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "content/test/content_browser_test_utils_internal.h" @@ -52,6 +54,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/numeric/int128.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h" #include "third_party/blink/public/common/shared_storage/shared_storage_utils.h" @@ -99,35 +102,20 @@ const int kReportEventBitBudget = 6; -const char kSelectFrom8URLsScript[] = R"( - let urls = []; - for (let i = 0; i < 8; ++i) { - urls.push({url: '/fenced_frames/title' + i.toString() + '.html', - reportingMetadata: { - 'click': '/fenced_frames/report' + i.toString() + '.html', - 'mouse interaction': - '/fenced_frames/report' + (i + 1).toString() + '.html' - }}); - } - - sharedStorage.selectURL( - 'test-url-selection-operation', urls, {data: {'mockResult': 1}}); - )"; - -const char kSelectFrom4URLsScript[] = R"( - let urls = []; - for (let i = 0; i < 4; ++i) { - urls.push({url: '/fenced_frames/title' + i.toString() + '.html', - reportingMetadata: { - 'click': '/fenced_frames/report' + i.toString() + '.html', - 'mouse interaction': - '/fenced_frames/report' + (i + 1).toString() + '.html' - }}); - } - - sharedStorage.selectURL( - 'test-url-selection-operation', urls, {data: {'mockResult': 1}}); - )"; +const char kGenerateURLsListScript[] = R"( + function generateUrls(size) { + return new Array(size).fill(0).map((e, i) => { + return { + url: '/fenced_frames/title' + i.toString() + '.html', + reportingMetadata: { + 'click': '/fenced_frames/report' + i.toString() + '.html', + 'mouse interaction': + '/fenced_frames/report' + (i + 1).toString() + '.html' + } + } + }); + } +)"; const char kRemainingBudgetPrefix[] = "remaining budget: "; @@ -213,6 +201,18 @@ return base::StrCat(urls_str_vector); } +bool IsErrorMessage(const content::WebContentsConsoleObserver::Message& msg) { + return msg.log_level == blink::mojom::ConsoleMessageLevel::kError; +} + +auto describe_param = [](const auto& info) { + if (info.param) { + return "ResolveSelectURLToConfig"; + } else { + return "ResolveSelectURLToURN"; + } +}; + } // namespace class TestSharedStorageWorkletHost : public SharedStorageWorkletHost { @@ -421,6 +421,11 @@ accesses_.emplace_back(type, main_frame_id, owner_origin, params); } + void OnUrnUuidGenerated(const GURL& urn_uuid) override {} + + void OnConfigPopulated( + const absl::optional<FencedFrameConfig>& config) override {} + bool EventParamsMatch(const SharedStorageEventParams& expected_params, const SharedStorageEventParams& actual_params) { if (expected_params.script_source_url != actual_params.script_source_url) { @@ -614,24 +619,21 @@ bool should_defer_worklet_messages_ = false; }; -class SharedStorageBrowserTest : public ContentBrowserTest { +class SharedStorageBrowserTestBase : public ContentBrowserTest { public: using AccessType = TestSharedStorageObserver::AccessType; - SharedStorageBrowserTest() { - scoped_feature_list_ - .InitWithFeaturesAndParameters(/*enabled_features=*/ - {{blink::features::kSharedStorageAPI, - {{"SharedStorageBitBudget", - base::NumberToString( - kBudgetAllowed)}, - {"SharedStorageStalenessThreshold", - TimeDeltaToString(base::Days( - kStalenessThresholdDays))}}}, - {features:: - kPrivacySandboxAdsAPIsOverride, - {}}}, - /*disabled_features=*/{}); + SharedStorageBrowserTestBase() { + scoped_feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/ + {{blink::features::kSharedStorageAPI, + { + {"SharedStorageBitBudget", base::NumberToString(kBudgetAllowed)}, + {"SharedStorageStalenessThreshold", + TimeDeltaToString(base::Days(kStalenessThresholdDays))}, + }}, + {features::kPrivacySandboxAdsAPIsOverride, {}}}, + /*disabled_features=*/{}); } void SetUpOnMainThread() override { @@ -642,10 +644,7 @@ test_worklet_host_manager->AddSharedStorageObserver(observer_.get()); test_worklet_host_manager_ = test_worklet_host_manager.get(); - static_cast<StoragePartitionImpl*>(shell() - ->web_contents() - ->GetBrowserContext() - ->GetDefaultStoragePartition()) + static_cast<StoragePartitionImpl*>(GetStoragePartition()) ->OverrideSharedStorageWorkletHostManagerForTesting( std::move(test_worklet_host_manager)); @@ -653,6 +652,15 @@ FinishSetup(); } + virtual bool ResolveSelectURLToConfig() { return false; } + + StoragePartition* GetStoragePartition() { + return shell() + ->web_contents() + ->GetBrowserContext() + ->GetDefaultStoragePartition(); + } + void TearDownOnMainThread() override { test_worklet_host_manager_->RemoveSharedStorageObserver(observer_.get()); } @@ -673,10 +681,7 @@ double GetRemainingBudget(const url::Origin& origin) { base::test::TestFuture<SharedStorageWorkletHost::BudgetResult> future; - static_cast<StoragePartitionImpl*>(shell() - ->web_contents() - ->GetBrowserContext() - ->GetDefaultStoragePartition()) + static_cast<StoragePartitionImpl*>(GetStoragePartition()) ->GetSharedStorageManager() ->GetRemainingBudget(origin, future.GetCallback()); return future.Take().bits; @@ -733,8 +738,7 @@ DCHECK(out_module_script_url); base::StringPairs run_function_body_replacement; - run_function_body_replacement.push_back( - std::make_pair("{{RUN_FUNCTION_BODY}}", script)); + run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", script); std::string host = execution_target.render_frame_host()->GetLastCommittedOrigin().host(); @@ -771,11 +775,9 @@ EXPECT_EQ(initial_child_count + 1, root->child_count()); FrameTreeNode* child_node = root->child_at(initial_child_count); - std::string navigate_frame_script = JsReplace("f.src = $1;", url.spec()); + TestFrameNavigationObserver observer(child_node); - TestFrameNavigationObserver observer(child_node->current_frame_host()); - - EXPECT_EQ(url.spec(), EvalJs(root, navigate_frame_script)); + EXPECT_EQ(url.spec(), EvalJs(root, JsReplace("f.src = $1;", url))); observer.Wait(); @@ -802,15 +804,49 @@ sharedStorage.worklet.addModule('/shared_storage/simple_module.js'); )")); - std::string urn_uuid = - EvalJs(iframe, kSelectFrom8URLsScript).ExtractString(); + // Generate 8 candidates urls in to a list variable `urls`. + EXPECT_TRUE(ExecJs(iframe, kGenerateURLsListScript)); + EXPECT_TRUE( + ExecJs(iframe, JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver config_observer( + GetStoragePartition()); + EvalJsResult result = EvalJs(iframe, R"( + (async function() { + const urls = generateUrls(8); + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = + config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHostForFrame(iframe->current_frame_host()) ->WaitForWorkletResponsesCount(2); - return GURL(urn_uuid); + return observed_urn_uuid.value(); } // Prerequisite: The worklet for `frame` has registered a @@ -874,7 +910,7 @@ return *test_worklet_host_manager_; } - ~SharedStorageBrowserTest() override = default; + ~SharedStorageBrowserTestBase() override = default; protected: base::test::ScopedFeatureList scoped_feature_list_; @@ -886,7 +922,24 @@ std::unique_ptr<TestSharedStorageObserver> observer_; }; -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, AddModule_Success) { +class SharedStorageBrowserTest : public base::test::WithFeatureOverride, + public SharedStorageBrowserTestBase { + public: + SharedStorageBrowserTest() + : base::test::WithFeatureOverride( + blink::features::kFencedFramesAPIChanges) { + scoped_feature_list_.InitAndEnableFeature(blink::features::kFencedFrames); + } + + bool ResolveSelectURLToConfig() override { return IsParamFeatureEnabled(); } + + ~SharedStorageBrowserTest() override = default; + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AddModule_Success) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. content::DisableBackForwardCacheForTesting( @@ -927,7 +980,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, AddModule_ScriptNotFound) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AddModule_ScriptNotFound) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -957,7 +1010,7 @@ "a.test", "/shared_storage/nonexistent_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, AddModule_RedirectNotAllowed) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AddModule_RedirectNotAllowed) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -989,7 +1042,7 @@ "a.test", "/server-redirect?shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AddModule_ScriptExecutionFailure) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -1022,7 +1075,7 @@ "a.test", "/shared_storage/erroneous_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AddModule_MultipleAddModuleFailure) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. @@ -1076,7 +1129,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, RunOperation_Success) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, RunOperation_Success) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -1125,7 +1178,7 @@ std::vector<uint8_t>())}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, RunOperation_Failure_RunOperationBeforeAddModule) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. @@ -1192,7 +1245,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, RunOperation_Failure_InvalidOptionsArgument) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -1225,7 +1278,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, RunOperation_Failure_ErrorInRunOperation) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -1282,7 +1335,7 @@ std::vector<uint8_t>())}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, WorkletDestroyed) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, WorkletDestroyed) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. content::DisableBackForwardCacheForTesting( @@ -1319,7 +1372,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, TwoWorklets) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, TwoWorklets) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. content::DisableBackForwardCacheForTesting( @@ -1382,7 +1435,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageBrowserTest, KeepAlive_StartBeforeAddModuleComplete_EndAfterAddModuleComplete) { // The test assumes pages get deleted after navigation. To ensure this, @@ -1450,7 +1503,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, KeepAlive_StartBeforeAddModuleComplete_EndAfterTimeout) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. @@ -1512,7 +1565,7 @@ "a.test", "/shared_storage/simple_module.js"))}}); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageBrowserTest, KeepAlive_StartBeforeRunOperationComplete_EndAfterRunOperationComplete) { // The test assumes pages get deleted after navigation. To ensure this, @@ -1594,7 +1647,7 @@ std::vector<uint8_t>())}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, KeepAlive_SubframeWorklet) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, KeepAlive_SubframeWorklet) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. content::DisableBackForwardCacheForTesting( @@ -1688,7 +1741,7 @@ "a.test", "/shared_storage/simple_module2.js"))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, RenderProcessHostDestroyedDuringWorkletKeepAlive) { // The test assumes pages gets deleted after navigation, letting the worklet // enter keep-alive phase. To ensure this, disable back/forward cache. @@ -1722,7 +1775,7 @@ // Test that there's no need to charge budget if the input urls' size is 1. // This specifically tests the operation success scenario. -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageBrowserTest, SelectURL_BudgetMetadata_OperationSuccess_SingleInputURL) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -1733,31 +1786,63 @@ EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( 'test-url-selection-operation', - [{url: "fenced_frames/title0.html", - reportingMetadata: {"click": "fenced_frames/report1.html", - "mouse interaction": "fenced_frames/report2.html"}}], - {data: {'mockResult':0}}); - )") - .ExtractString(); + [ + { + url: "fenced_frames/title0.html", + reportingMetadata: { + "click": "fenced_frames/report1.html", + "mouse interaction": "fenced_frames/report2.html" + } + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")), @@ -1795,7 +1880,7 @@ // Test that there's no need to charge budget if the input urls' size is 1. // This specifically tests the operation failure scenario. -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageBrowserTest, SelectURL_BudgetMetadata_OperationFailure_SingleInputURL) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -1806,30 +1891,62 @@ EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( 'test-url-selection-operation', - [{url: "fenced_frames/title0.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}], - {data: {'mockResult':-1}}); - )") - .ExtractString(); + [ + { + url: "fenced_frames/title0.html", + reportingMetadata: { + "click": "fenced_frames/report1.html" + } + } + ], + { + data: {'mockResult': -1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")))); @@ -1859,7 +1976,7 @@ .spec()}}}}))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, SelectURL_BudgetMetadata_Origin) { EXPECT_TRUE(NavigateToURL( shell(), https_server()->GetURL("a.test", kPageWithBlankIframePath))); @@ -1877,31 +1994,69 @@ EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount()); EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount()); - std::string urn_uuid = EvalJs(iframe, R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], {data: {'mockResult': 1}}); - )") - .ExtractString(); + EXPECT_TRUE(ExecJs(iframe, JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(iframe, R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("b.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3)); SharedStorageReportingMap reporting_map = - GetSharedStorageReportingMap(GURL(urn_uuid)); + GetSharedStorageReportingMap(observed_urn_uuid.value()); EXPECT_FALSE(reporting_map.empty()); EXPECT_EQ(1U, reporting_map.size()); EXPECT_EQ("click", reporting_map.begin()->first); @@ -1935,7 +2090,7 @@ {}}}))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, SelectURL_ReportingMetadata_EmptyReportEvent) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1945,30 +2100,62 @@ EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( 'test-url-selection-operation', - [{url: "fenced_frames/title0.html", - reportingMetadata: {"": "fenced_frames/report1.html"}}], - {data: {'mockResult':0}}); - )") - .ExtractString(); + [ + { + url: "fenced_frames/title0.html", + reportingMetadata: { + "": "fenced_frames/report1.html" + } + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")))); @@ -1996,7 +2183,7 @@ .spec()}}}}))}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, SetAppendOperationInDocument) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, SetAppendOperationInDocument) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2072,7 +2259,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, DeleteOperationInDocument) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, DeleteOperationInDocument) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2119,7 +2306,7 @@ SharedStorageEventParams::CreateForGetOrDelete("key0")}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, ClearOperationInDocument) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, ClearOperationInDocument) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2157,7 +2344,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, SetAppendOperationInWorklet) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, SetAppendOperationInWorklet) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2231,7 +2418,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AppendOperationFailedInWorklet) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2270,7 +2457,7 @@ SharedStorageEventParams::CreateForAppend("key0", "a")}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, DeleteOperationInWorklet) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, DeleteOperationInWorklet) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2329,7 +2516,7 @@ SharedStorageEventParams::CreateForGetOrDelete("key0")}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, ClearOperationInWorklet) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, ClearOperationInWorklet) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2375,13 +2562,10 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, GetOperationInWorklet) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, GetOperationInWorklet) { base::SimpleTestClock clock; base::RunLoop loop; - static_cast<StoragePartitionImpl*>(shell() - ->web_contents() - ->GetBrowserContext() - ->GetDefaultStoragePartition()) + static_cast<StoragePartitionImpl*>(GetStoragePartition()) ->GetSharedStorageManager() ->OverrideClockForTesting(&clock, loop.QuitClosure()); loop.Run(); @@ -2465,7 +2649,7 @@ SharedStorageEventParams::CreateForGetOrDelete("key0")}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AccessStorageInSameOriginDocument) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2504,7 +2688,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, AccessStorageInDifferentOriginDocument) { GURL url1 = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url1)); @@ -2544,7 +2728,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, KeysAndEntriesOperation) { +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, KeysAndEntriesOperation) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2600,7 +2784,7 @@ SharedStorageEventParams::CreateDefault()}}); } -IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, KeysAndEntriesOperation_MultipleBatches) { GURL url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), url)); @@ -2664,19 +2848,31 @@ ExpectAccessObserved(expected_accesses); } +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageBrowserTest, + testing::Bool(), + describe_param); + class SharedStorageAllowURNsInIframesBrowserTest - : public SharedStorageBrowserTest { + : public base::test::WithFeatureOverride, + public SharedStorageBrowserTestBase { public: - SharedStorageAllowURNsInIframesBrowserTest() { - scoped_feature_list_.InitAndEnableFeature( - blink::features::kAllowURNsInIframes); + SharedStorageAllowURNsInIframesBrowserTest() + : base::test::WithFeatureOverride( + blink::features::kFencedFramesAPIChanges) { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/ + {blink::features::kFencedFrames, blink::features::kAllowURNsInIframes}, + /*disabled_features=*/{}); } + bool ResolveSelectURLToConfig() override { return GetParam(); } + private: base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(SharedStorageAllowURNsInIframesBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageAllowURNsInIframesBrowserTest, RenderSelectURLResultInIframe) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -2698,7 +2894,7 @@ TestNavigationObserver top_navigation_observer(shell()->web_contents()); EXPECT_TRUE( - ExecJs(iframe_node, JsReplace("top.location = $1", new_page_url.spec()))); + ExecJs(iframe_node, JsReplace("top.location = $1", new_page_url))); top_navigation_observer.Wait(); // After the top navigation, log(8)=3 bits should have been withdrawn from the @@ -2707,17 +2903,24 @@ kBudgetAllowed - 3); } -class SharedStorageFencedFrameInteractionBrowserTest - : public SharedStorageBrowserTest { +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageAllowURNsInIframesBrowserTest, + testing::Bool(), + describe_param); + +class SharedStorageFencedFrameInteractionBrowserTestBase + : public SharedStorageBrowserTestBase { public: - SharedStorageFencedFrameInteractionBrowserTest() { - scoped_feature_list_ - .InitWithFeaturesAndParameters(/*enabled_features=*/ - {{blink::features::kFencedFrames, {}}}, - /*disabled_features=*/{}); + SharedStorageFencedFrameInteractionBrowserTestBase() { + scoped_feature_list_.InitAndEnableFeature(blink::features::kFencedFrames); } - FrameTreeNode* CreateFencedFrame(FrameTreeNode* root, const GURL& url) { + using FencedFrameNavigationTarget = absl::variant<GURL, std::string>; + + // TODO(crbug.com/1414429): This function should be removed. Use + // `CreateFencedFrame` in fenced_frame_test_util.h instead. + FrameTreeNode* CreateFencedFrame(FrameTreeNode* root, + const FencedFrameNavigationTarget& target) { size_t initial_child_count = root->child_count(); EXPECT_TRUE(ExecJs(root, @@ -2729,28 +2932,55 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(initial_child_count)); - std::string navigate_fenced_frame_script = - JsReplace("f.src = $1;", url.spec()); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(url.spec(), EvalJs(root, navigate_fenced_frame_script)); + EvalJsResult result = NavigateFencedFrame(root, target); observer.Wait(); + EXPECT_TRUE(result.error.empty()); + if (absl::holds_alternative<GURL>(target)) { + EXPECT_EQ(result, absl::get<GURL>(target).spec()); + } + return fenced_frame_root_node; } - FrameTreeNode* CreateFencedFrame(const GURL& url) { - return CreateFencedFrame(PrimaryFrameTreeNodeRoot(), url); + FrameTreeNode* CreateFencedFrame(const FencedFrameNavigationTarget& target) { + return CreateFencedFrame(PrimaryFrameTreeNodeRoot(), target); + } + + EvalJsResult NavigateFencedFrame(FrameTreeNode* root, + const FencedFrameNavigationTarget& target) { + return EvalJs(root, absl::visit(base::Overloaded{ + [](const GURL& url) { + return JsReplace("f.src = $1;", url); + }, + [](const std::string& config) { + return JsReplace( + "f.config = window[$1]", config); + }, + }, + target)); + ; } private: base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +class SharedStorageFencedFrameInteractionBrowserTest + : public base::test::WithFeatureOverride, + public SharedStorageFencedFrameInteractionBrowserTestBase { + public: + SharedStorageFencedFrameInteractionBrowserTest() + : base::test::WithFeatureOverride( + blink::features::kFencedFramesAPIChanges) {} + + bool ResolveSelectURLToConfig() override { return IsParamFeatureEnabled(); } +}; + +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_FinishBeforeStartingFencedFrameNavigation) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -2769,30 +2999,68 @@ EXPECT_EQ("Finish executing simple_module.js", base::UTF16ToUTF8(console_observer.messages()[1].message)); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], {data: {'mockResult': 1}}); - )") - .ExtractString(); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3)); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")))); @@ -2823,13 +3091,15 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(0)); - std::string navigate_fenced_frame_to_urn_script = - JsReplace("f.src = $1;", urn_uuid); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script)); + EvalJsResult navigation_result = NavigateFencedFrame( + root, ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(navigation_result, observed_urn_uuid.value()); + } observer.Wait(); @@ -2842,7 +3112,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_FinishAfterStartingFencedFrameNavigation) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -2860,17 +3130,49 @@ .GetAttachedWorkletHost() ->set_should_defer_worklet_messages(true); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], {data: {'mockResult': 1}}); - )") - .ExtractString(); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() @@ -2888,13 +3190,15 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(0)); - std::string navigate_fenced_frame_to_urn_script = - JsReplace("f.src = $1;", urn_uuid); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script)); + EvalJsResult navigation_result = NavigateFencedFrame( + root, ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(navigation_result, observed_urn_uuid.value()); + } // After the previous EvalJs, the NavigationRequest should have been created, // but may not have begun. Wait for BeginNavigation() and expect it to be @@ -2913,7 +3217,8 @@ root->current_frame_host()->GetPage().fenced_frame_urls_map(); FencedFrameURLMappingTestPeer url_mapping_test_peer(&url_mapping); - EXPECT_TRUE(url_mapping_test_peer.HasObserver(GURL(urn_uuid), request)); + EXPECT_TRUE( + url_mapping_test_peer.HasObserver(observed_urn_uuid.value(), request)); // Execute the deferred messages. This should finish the url mapping and // resume the deferred navigation. @@ -2923,13 +3228,19 @@ observer.Wait(); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3)); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")))); @@ -2948,7 +3259,7 @@ // Tests that the URN from SelectURL() is valid in different // context in the page, but it's not valid in a new page. -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_URNLifetime) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -2959,9 +3270,9 @@ FrameTreeNode* iframe_node = PrimaryFrameTreeNodeRoot()->child_at(0); // Navigate the iframe to about:blank. - TestFrameNavigationObserver observer(iframe_node->current_frame_host()); + TestFrameNavigationObserver observer(iframe_node); EXPECT_TRUE(ExecJs(iframe_node, JsReplace("window.location.href=$1", - GURL(url::kAboutBlankURL).spec()))); + GURL(url::kAboutBlankURL)))); observer.Wait(); // Verify that the `urn_uuid` is still valid in the main page. @@ -2987,7 +3298,7 @@ // Tests that if the URN mapping is not finished before the keep-alive timeout, // the mapping will be considered to be failed when the timeout is reached. -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_NotFinishBeforeKeepAliveTimeout) { // The test assumes pages get deleted after navigation. To ensure this, // disable back/forward cache. @@ -3014,7 +3325,49 @@ .GetAttachedWorkletHost() ->set_should_defer_worklet_messages(true); - std::string urn_uuid = EvalJs(iframe, kSelectFrom8URLsScript).ExtractString(); + EXPECT_TRUE(ExecJs(iframe, kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(iframe, JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(iframe, R"( + (async function() { + const urls = generateUrls(8); + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } + + FrameTreeNode* root = PrimaryFrameTreeNodeRoot(); + if (ResolveSelectURLToConfig()) { + // Preserve the config in a variable. It is then installed to the new fenced + // frame. Without this step, the config will be gone after navigating the + // iframe to about::blank. + EXPECT_TRUE(ExecJs(root, R"(var fenced_frame_config = document + .getElementById('test_iframe') + .contentWindow + .select_url_result;)")); + } // Navigate away to let the subframe's worklet enter keep-alive. NavigateIframeToURL(shell()->web_contents(), "test_iframe", @@ -3028,8 +3381,6 @@ .GetKeepAliveWorkletHost() ->WaitForWorkletResponsesCount(2); - FrameTreeNode* root = PrimaryFrameTreeNodeRoot(); - EXPECT_TRUE(ExecJs(root, "var f = document.createElement('fencedframe');" "f.mode = 'opaque-ads';" @@ -3039,13 +3390,15 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(1)); - std::string navigate_fenced_frame_to_urn_script = - JsReplace("f.src = $1;", urn_uuid); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script)); + EvalJsResult navigation_result = NavigateFencedFrame( + root, ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("fenced_frame_config") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(navigation_result, observed_urn_uuid.value()); + } // After the previous EvalJs, the NavigationRequest should have been created, // but may not have begun. Wait for BeginNavigation() and expect it to be @@ -3060,6 +3413,11 @@ EXPECT_TRUE(request->is_deferred_on_fenced_frame_url_mapping_for_testing()); } + ASSERT_FALSE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_FALSE(fenced_frame_config.has_value()); + // Fire the keep-alive timer. This will terminate the keep-alive, and the // deferred navigation will resume to navigate to the default url (at index // 0). @@ -3073,12 +3431,12 @@ observer.Wait(); SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report0.html")), @@ -3094,14 +3452,21 @@ // `kTimingSelectUrlExecutedInWorkletHistogram` histogram isn't recorded. histogram_tester_.ExpectTotalCount(kTimingSelectUrlExecutedInWorkletHistogram, 0); + + // The worklet is destructed. The config corresponds to the unresolved URN is + // populated in the destructor of `SharedStorageWorkletHost`. + ASSERT_TRUE(config_observer.ConfigObserved()); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_WorkletReturnInvalidIndex) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); WebContentsConsoleObserver console_observer(shell()->web_contents()); + console_observer.SetFilter(base::BindRepeating(IsErrorMessage)); EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); @@ -3110,34 +3475,73 @@ EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount()); EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount()); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], {data: {'mockResult': 3}}); - )") - .ExtractString(); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: + { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 3}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid->spec()); + EXPECT_EQ( "Promise resolved to a number outside the length of the input urls.", base::UTF16ToUTF8(console_observer.messages().back().message)); SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3)); - EXPECT_TRUE(GetSharedStorageReportingMap(GURL(urn_uuid)).empty()); + EXPECT_TRUE(GetSharedStorageReportingMap(observed_urn_uuid.value()).empty()); FrameTreeNode* root = PrimaryFrameTreeNodeRoot(); @@ -3150,13 +3554,15 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(0)); - std::string navigate_fenced_frame_to_urn_script = - JsReplace("f.src = $1;", urn_uuid); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script)); + EvalJsResult navigation_result = NavigateFencedFrame( + root, ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(navigation_result, observed_urn_uuid.value()); + } observer.Wait(); @@ -3169,7 +3575,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_DuplicateUrl) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3183,34 +3589,72 @@ EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount()); EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount()); - std::string urn_uuid = EvalJs(shell(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title.html"}, - {url: "fenced_frames/title0.html", - url: "fenced_frames/title1.html", - reportingMetadata: {"click": "fenced_frames/report1.html"}}, - {url: "fenced_frames/title2.html"}], {data: {'mockResult': 1}}); - )") - .ExtractString(); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: + { + "click": "fenced_frames/report1.html" + } + }, + { + url: "fenced_frames/title2.html" + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); + ASSERT_TRUE(config_observer.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config = + config_observer.GetConfig(); + EXPECT_TRUE(fenced_frame_config.has_value()); + EXPECT_EQ(fenced_frame_config->urn_uuid_, observed_urn_uuid.value()); + EXPECT_EQ("Finish executing 'test-url-selection-operation'", base::UTF16ToUTF8(console_observer.messages().back().message)); SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test")); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3)); - EXPECT_THAT(GetSharedStorageReportingMap(GURL(urn_uuid)), + EXPECT_THAT(GetSharedStorageReportingMap(observed_urn_uuid.value()), UnorderedElementsAre( Pair("click", https_server()->GetURL( "a.test", "/fenced_frames/report1.html")))); @@ -3226,13 +3670,15 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root->child_at(0)); - std::string navigate_fenced_frame_to_urn_script = - JsReplace("f.src = $1;", urn_uuid); + TestFrameNavigationObserver observer(fenced_frame_root_node); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - - EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script)); + EvalJsResult navigation_result = NavigateFencedFrame( + root, ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(navigation_result, observed_urn_uuid.value()); + } observer.Wait(); @@ -3245,7 +3691,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, FencedFrameNavigateSelf_NoBudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3262,8 +3708,7 @@ RemainingBudgetViaJSForFrame(PrimaryFrameTreeNodeRoot()->child_at(0)), kBudgetAllowed); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); + TestFrameNavigationObserver observer(fenced_frame_root_node); EXPECT_TRUE(ExecJs(fenced_frame_root_node, "location.reload()")); observer.Wait(); @@ -3278,7 +3723,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, FencedFrameNavigateTop_BudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3298,9 +3743,9 @@ GURL new_page_url = https_server()->GetURL("c.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - fenced_frame_root_node, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(fenced_frame_root_node, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); // After the top navigation, log(8)=3 bits should have been withdrawn from the @@ -3315,7 +3760,7 @@ 1); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameInteractionBrowserTest, FencedFrameNavigateFromParentToRegularURLAndThenNavigateTop_NoBudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -3335,11 +3780,10 @@ GURL new_frame_url = https_server()->GetURL("c.test", kFencedFramePath); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); + TestFrameNavigationObserver observer(fenced_frame_root_node); std::string navigate_fenced_frame_script = JsReplace( "var f = document.getElementsByTagName('fencedframe')[0]; f.src = $1;", - new_frame_url.spec()); + new_frame_url); EXPECT_TRUE(ExecJs(shell(), navigate_fenced_frame_script)); observer.Wait(); @@ -3347,9 +3791,9 @@ GURL new_page_url = https_server()->GetURL("d.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - fenced_frame_root_node, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(fenced_frame_root_node, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); // No budget withdrawal as the initial fenced frame was navigated away by its @@ -3367,7 +3811,7 @@ 1); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameInteractionBrowserTest, FencedFrameNavigateSelfAndThenNavigateTop_BudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -3383,11 +3827,9 @@ { GURL new_frame_url = https_server()->GetURL("c.test", kFencedFramePath); - TestFrameNavigationObserver observer( - fenced_frame_root_node->current_frame_host()); - EXPECT_TRUE( - ExecJs(fenced_frame_root_node, - JsReplace("window.location.href=$1", new_frame_url.spec()))); + TestFrameNavigationObserver observer(fenced_frame_root_node); + EXPECT_TRUE(ExecJs(fenced_frame_root_node, + JsReplace("window.location.href=$1", new_frame_url))); observer.Wait(); } @@ -3400,9 +3842,9 @@ GURL new_page_url = https_server()->GetURL("d.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - fenced_frame_root_node, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(fenced_frame_root_node, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); } @@ -3418,7 +3860,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, NestedFencedFrameNavigateTop_BudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3442,9 +3884,9 @@ GURL new_page_url = https_server()->GetURL("d.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - nested_fenced_frame_root_node, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(nested_fenced_frame_root_node, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); // After the top navigation, log(8)=3 bits should have been withdrawn from the @@ -3459,73 +3901,85 @@ 1); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameInteractionBrowserTest, NestedFencedFrameNavigateTop_BudgetWithdrawalFromTwoMetadata) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); - url::Origin shared_storage_origin1 = + url::Origin shared_storage_origin_1 = url::Origin::Create(https_server()->GetURL("b.test", kSimplePagePath)); - GURL urn_uuid1 = SelectFrom8URLsInContext(shared_storage_origin1); - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urn_uuid1); + GURL urn_uuid_1 = SelectFrom8URLsInContext(shared_storage_origin_1); + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame(urn_uuid_1); - url::Origin shared_storage_origin2 = + url::Origin shared_storage_origin_2 = url::Origin::Create(https_server()->GetURL("c.test", kSimplePagePath)); - GURL urn_uuid2 = - SelectFrom8URLsInContext(shared_storage_origin2, fenced_frame_root_node1); + GURL urn_uuid_2 = SelectFrom8URLsInContext(shared_storage_origin_2, + fenced_frame_root_node_1); - FrameTreeNode* fenced_frame_root_node2 = - CreateFencedFrame(fenced_frame_root_node1, urn_uuid2); + FrameTreeNode* fenced_frame_root_node_2 = + CreateFencedFrame(fenced_frame_root_node_1, urn_uuid_2); - EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin1), kBudgetAllowed); - EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin2), kBudgetAllowed); + EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin_1), kBudgetAllowed); + EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin_2), kBudgetAllowed); GURL new_page_url = https_server()->GetURL("d.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - fenced_frame_root_node2, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(fenced_frame_root_node_2, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); // After the top navigation, log(8)=3 bits should have been withdrawn from - // both `shared_storage_origin1` and `shared_storage_origin2`. - EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin1), + // both `shared_storage_origin_1` and `shared_storage_origin_2`. + EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin_1), kBudgetAllowed - 3); - EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin2), + EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin_2), kBudgetAllowed - 3); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURLNotAllowedInNestedFencedFrame) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); - url::Origin shared_storage_origin1 = + url::Origin shared_storage_origin_1 = url::Origin::Create(https_server()->GetURL("b.test", kSimplePagePath)); - GURL urn_uuid1 = SelectFrom8URLsInContext(shared_storage_origin1); - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urn_uuid1); + GURL urn_uuid_1 = SelectFrom8URLsInContext(shared_storage_origin_1); + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame(urn_uuid_1); - url::Origin shared_storage_origin2 = + url::Origin shared_storage_origin_2 = url::Origin::Create(https_server()->GetURL("c.test", kSimplePagePath)); - GURL urn_uuid2 = - SelectFrom8URLsInContext(shared_storage_origin2, fenced_frame_root_node1); + GURL urn_uuid_2 = SelectFrom8URLsInContext(shared_storage_origin_2, + fenced_frame_root_node_1); - FrameTreeNode* fenced_frame_root_node2 = - CreateFencedFrame(fenced_frame_root_node1, urn_uuid2); + FrameTreeNode* fenced_frame_root_node_2 = + CreateFencedFrame(fenced_frame_root_node_1, urn_uuid_2); - EXPECT_TRUE(ExecJs(fenced_frame_root_node2, R"( + EXPECT_TRUE(ExecJs(fenced_frame_root_node_2, R"( sharedStorage.worklet.addModule('/shared_storage/simple_module.js'); )")); + EXPECT_TRUE(ExecJs(fenced_frame_root_node_2, + JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - EvalJsResult result = EvalJs(fenced_frame_root_node2, R"( + EvalJsResult result = EvalJs(fenced_frame_root_node_2, R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "/fenced_frames/title0.html"}], {data: {'mockResult': 0}}); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); )"); EXPECT_THAT(result.error, @@ -3534,7 +3988,7 @@ "depth (2) exceeding the maximum allowed number (1).")); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, IframeInFencedFrameNavigateTop_BudgetWithdrawal) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3558,9 +4012,9 @@ GURL new_page_url = https_server()->GetURL("d.test", kSimplePagePath); TestNavigationObserver top_navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecJs( - nested_fenced_frame_root_node, - JsReplace("window.open($1, '_unfencedTop')", new_page_url.spec()))); + EXPECT_TRUE( + ExecJs(nested_fenced_frame_root_node, + JsReplace("window.open($1, '_unfencedTop')", new_page_url))); top_navigation_observer.Wait(); // After the top navigation, log(8)=3 bits should have been withdrawn from the @@ -3575,7 +4029,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, FencedFrame_PopupTwice_BudgetWithdrawalOnce) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3614,7 +4068,7 @@ 1); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameInteractionBrowserTest, TwoFencedFrames_DifferentURNs_EachPopupOnce_BudgetWithdrawalTwice) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -3626,10 +4080,70 @@ sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); - GURL urn_uuid1 = - GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); - GURL urn_uuid2 = - GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), "window.urls = generateUrls(8);")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver config_observer_1( + GetStoragePartition()); + EvalJsResult result_1 = EvalJs(shell(), R"( + (async function() { + window.select_url_result_1 = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result_1 instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result_1; + })() + )"); + + EXPECT_TRUE(result_1.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_1 = + config_observer_1.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_1.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_1.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result_1.ExtractString(), observed_urn_uuid_1->spec()); + } + + TestSelectURLFencedFrameConfigObserver config_observer_2( + GetStoragePartition()); + EvalJsResult result_2 = EvalJs(shell(), R"( + (async function() { + window.select_url_result_2 = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result_2 instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result_2; + })() + )"); + + EXPECT_TRUE(result_2.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_2 = + config_observer_2.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_2.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_2.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result_2.ExtractString(), observed_urn_uuid_2->spec()); + } // There are three "worklet operations": one `addModule()` and two // `selectURL()`. @@ -3637,14 +4151,32 @@ .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(3); - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urn_uuid1); - FrameTreeNode* fenced_frame_root_node2 = CreateFencedFrame(urn_uuid2); + ASSERT_TRUE(config_observer_1.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config_1 = + config_observer_1.GetConfig(); + EXPECT_TRUE(fenced_frame_config_1.has_value()); + EXPECT_EQ(fenced_frame_config_1->urn_uuid_, observed_urn_uuid_1.value()); + + ASSERT_TRUE(config_observer_2.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config_2 = + config_observer_2.GetConfig(); + EXPECT_TRUE(fenced_frame_config_2.has_value()); + EXPECT_EQ(fenced_frame_config_2->urn_uuid_, observed_urn_uuid_2.value()); + + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result_1") + : FencedFrameNavigationTarget(observed_urn_uuid_1.value())); + FrameTreeNode* fenced_frame_root_node_2 = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result_2") + : FencedFrameNavigationTarget(observed_urn_uuid_2.value())); EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed); EXPECT_DOUBLE_EQ(RemainingBudgetViaJSForFrame(PrimaryFrameTreeNodeRoot()), kBudgetAllowed); - OpenPopup(fenced_frame_root_node1, + OpenPopup(fenced_frame_root_node_1, https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); // After the popup, log(8)=3 bits should have been withdrawn from the @@ -3652,7 +4184,7 @@ EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed - 3); - OpenPopup(fenced_frame_root_node2, + OpenPopup(fenced_frame_root_node_2, https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); // After the popup, log(8)=3 bits should have been withdrawn from the @@ -3669,7 +4201,7 @@ 2); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( SharedStorageFencedFrameInteractionBrowserTest, TwoFencedFrames_SameURNs_EachPopupOnce_BudgetWithdrawalOnce) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -3679,15 +4211,15 @@ GURL urn_uuid = SelectFrom8URLsInContext(shared_storage_origin); - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urn_uuid); - FrameTreeNode* fenced_frame_root_node2 = CreateFencedFrame(urn_uuid); + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame(urn_uuid); + FrameTreeNode* fenced_frame_root_node_2 = CreateFencedFrame(urn_uuid); EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed); EXPECT_DOUBLE_EQ( RemainingBudgetViaJSForFrame(PrimaryFrameTreeNodeRoot()->child_at(0)), kBudgetAllowed); - OpenPopup(fenced_frame_root_node1, + OpenPopup(fenced_frame_root_node_1, https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); // After the popup, log(8)=3 bits should have been withdrawn from the @@ -3695,7 +4227,7 @@ EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed - 3); - OpenPopup(fenced_frame_root_node2, + OpenPopup(fenced_frame_root_node_2, https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); // The budget can only be withdrawn once for each urn_uuid. @@ -3709,7 +4241,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_InsufficientBudget) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3717,37 +4249,115 @@ url::Origin shared_storage_origin = url::Origin::Create(main_url); WebContentsConsoleObserver console_observer(shell()->web_contents()); + console_observer.SetFilter(base::BindRepeating(IsErrorMessage)); EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); - GURL urn_uuid1 = - GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), "window.urls = generateUrls(8);")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urn_uuid1); - OpenPopup(fenced_frame_root_node1, + TestSelectURLFencedFrameConfigObserver config_observer_1( + GetStoragePartition()); + EvalJsResult result_1 = EvalJs(shell(), R"( + (async function() { + window.select_url_result_1 = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result_1 instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result_1; + })() + )"); + + EXPECT_TRUE(result_1.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_1 = + config_observer_1.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_1.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_1.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result_1.ExtractString(), observed_urn_uuid_1->spec()); + } + + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result_1") + : FencedFrameNavigationTarget(observed_urn_uuid_1.value())); + OpenPopup(fenced_frame_root_node_1, https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed - 3); - GURL urn_uuid2 = - GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + TestSelectURLFencedFrameConfigObserver config_observer_2( + GetStoragePartition()); + EvalJsResult result_2 = EvalJs(shell(), R"( + (async function() { + window.select_url_result_2 = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result_2 instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result_2; + })() + )"); + + EXPECT_TRUE(result_2.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_2 = + config_observer_2.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_2.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_2.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result_2.ExtractString(), observed_urn_uuid_2->spec()); + } // Wait for the `addModule()` and two `selectURL()` to finish. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(3); + ASSERT_TRUE(config_observer_1.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config_1 = + config_observer_1.GetConfig(); + EXPECT_TRUE(fenced_frame_config_1.has_value()); + EXPECT_EQ(fenced_frame_config_1->urn_uuid_, observed_urn_uuid_1.value()); + + ASSERT_TRUE(config_observer_2.ConfigObserved()); + const absl::optional<FencedFrameConfig>& fenced_frame_config_2 = + config_observer_2.GetConfig(); + EXPECT_TRUE(fenced_frame_config_2.has_value()); + EXPECT_EQ(fenced_frame_config_2->urn_uuid_, observed_urn_uuid_2.value()); + EXPECT_EQ("Insufficient budget for selectURL().", base::UTF16ToUTF8(console_observer.messages().back().message)); - // The failed mapping due to insufficient budget (i.e. `urn_uuid2`) should not - // incur any budget withdrawal on subsequent top navigation from inside + // The failed mapping due to insufficient budget (i.e. `urn_uuid_2`) should + // not incur any budget withdrawal on subsequent top navigation from inside // the fenced frame. - FrameTreeNode* fenced_frame_root_node2 = CreateFencedFrame(urn_uuid2); - OpenPopup(fenced_frame_root_node2, + FrameTreeNode* fenced_frame_root_node_2 = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result_2") + : FencedFrameNavigationTarget(observed_urn_uuid_2.value())); + OpenPopup(fenced_frame_root_node_2, https_server()->GetURL("c.test", kSimplePagePath), /*name=*/""); EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), @@ -3762,7 +4372,7 @@ // When number of urn mappings limit has been reached, subsequent `selectURL()` // calls will fail. -IN_PROC_BROWSER_TEST_F(SharedStorageFencedFrameInteractionBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest, SelectURL_Fails_ExceedNumOfUrnMappingsLimit) { GURL main_url = https_server()->GetURL("a.test", kSimplePagePath); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3776,12 +4386,23 @@ sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); - EvalJsResult result = EvalJs(shell(), R"( + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + std::string select_url_script = R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], {data: {'mockResult': 0}}); - )"); - EXPECT_TRUE(result.error.empty()); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); + )"; + EXPECT_TRUE(ExecJs(shell(), select_url_script)); // Wait for the `addModule()` and `selectURL()` to finish. test_worklet_host_manager() @@ -3797,11 +4418,7 @@ GURL url("https://a.test"); fenced_frame_url_mapping_test_peer.FillMap(url); - EvalJsResult extra_result = EvalJs(shell(), R"( - sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title1.html"}], {data: {'mockResult': 0}}); - )"); + EvalJsResult extra_result = EvalJs(shell(), select_url_script); // `selectURL()` fails when map is full. std::string expected_error = base::StrCat( @@ -3811,30 +4428,33 @@ EXPECT_EQ(expected_error, extra_result.error); } +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageFencedFrameInteractionBrowserTest, + testing::Bool(), + describe_param); + class SharedStorageSelectURLNotAllowedInFencedFrameBrowserTest : public SharedStorageFencedFrameInteractionBrowserTest { public: SharedStorageSelectURLNotAllowedInFencedFrameBrowserTest() { - scoped_feature_list_ - .InitWithFeaturesAndParameters(/*enabled_features=*/ - {{blink::features::kSharedStorageAPI, - {{"SharedStorageBitBudget", - base::NumberToString( - kBudgetAllowed)}, - {"SharedStorageMaxAllowedFencedFrameD" - "epthForSelectURL", - "0"}}}, - {features:: - kPrivacySandboxAdsAPIsOverride, - {}}}, - /*disabled_features=*/{}); + scoped_feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/ + {{blink::features::kSharedStorageAPI, + {{"SharedStorageBitBudget", base::NumberToString(kBudgetAllowed)}, + {"SharedStorageMaxAllowedFencedFrameDepthForSelectURL", "0"}}}, + {features::kPrivacySandboxAdsAPIsOverride, {}}}, + /*disabled_features=*/{}); + + fenced_frame_api_change_feature_.InitWithFeatureState( + blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig()); } private: base::test::ScopedFeatureList scoped_feature_list_; + base::test::ScopedFeatureList fenced_frame_api_change_feature_; }; -IN_PROC_BROWSER_TEST_F(SharedStorageSelectURLNotAllowedInFencedFrameBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageSelectURLNotAllowedInFencedFrameBrowserTest, SelectURLNotAllowedInFencedFrame) { GURL main_frame_url = https_server()->GetURL("a.test", kSimplePagePath); @@ -3852,10 +4472,22 @@ EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount()); EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount()); + EXPECT_TRUE(ExecJs(fenced_frame_node, + JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); EvalJsResult result = EvalJs(fenced_frame_node, R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], {data: {'mockResult': 0}}); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); )"); EXPECT_THAT(result.error, @@ -3864,6 +4496,12 @@ "depth (1) exceeding the maximum allowed number (0).")); } +INSTANTIATE_TEST_SUITE_P( + All, + SharedStorageSelectURLNotAllowedInFencedFrameBrowserTest, + testing::Bool(), + describe_param); + class SharedStorageReportEventBrowserTest : public SharedStorageFencedFrameInteractionBrowserTest { void FinishSetup() override { @@ -3872,7 +4510,7 @@ } }; -IN_PROC_BROWSER_TEST_F(SharedStorageReportEventBrowserTest, +IN_PROC_BROWSER_TEST_P(SharedStorageReportEventBrowserTest, SelectURL_ReportEvent) { net::test_server::ControllableHttpResponse response1( https_server(), "/fenced_frames/report1.html"); @@ -3888,17 +4526,47 @@ EXPECT_TRUE(ExecJs(shell(), R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - GURL urn_uuid = GURL(EvalJs(shell(), R"( - sharedStorage.selectURL( + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}, - {url: "fenced_frames/title1.html", - reportingMetadata: {'click': "fenced_frames/report1.html", - 'mouse interaction': "fenced_frames/report2.html"}}], - {data: {'mockResult':1}}); - )") - .ExtractString()); + [ + { + url: "fenced_frames/title0.html" + }, + { + url: "fenced_frames/title1.html", + reportingMetadata: { + "click": "fenced_frames/report1.html", + "mouse interaction": "fenced_frames/report2.html" + } + } + ], + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are three "worklet operations": one `addModule()` and two // `selectURL()`. @@ -3906,7 +4574,10 @@ .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(2); - FrameTreeNode* fenced_frame_root_node = CreateFencedFrame(urn_uuid); + FrameTreeNode* fenced_frame_root_node = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); std::string event_data1 = "this is a click"; EXPECT_TRUE( @@ -3937,8 +4608,13 @@ 1); } +INSTANTIATE_TEST_SUITE_P(All, + SharedStorageReportEventBrowserTest, + testing::Bool(), + describe_param); + class SharedStoragePrivateAggregationDisabledBrowserTest - : public SharedStorageBrowserTest { + : public SharedStorageBrowserTestBase { public: SharedStoragePrivateAggregationDisabledBrowserTest() { scoped_feature_list_.InitAndDisableFeature(content::kPrivateAggregationApi); @@ -3969,7 +4645,7 @@ } class SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest - : public SharedStorageBrowserTest { + : public SharedStorageBrowserTestBase { public: SharedStoragePrivateAggregationDisabledForSharedStorageOnlyBrowserTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( @@ -4003,7 +4679,7 @@ } class SharedStoragePrivateAggregationEnabledBrowserTest - : public SharedStorageBrowserTest { + : public SharedStorageBrowserTestBase { public: // TODO(alexmt): Consider factoring out along with FLEDGE definition. class TestPrivateAggregationManagerImpl @@ -4022,7 +4698,7 @@ } void SetUpOnMainThread() override { - SharedStorageBrowserTest::SetUpOnMainThread(); + SharedStorageBrowserTestBase::SetUpOnMainThread(); browser_client_ = std::make_unique<MockPrivateAggregationShellContentBrowserClient>(); @@ -4030,10 +4706,7 @@ a_test_origin_ = https_server()->GetOrigin("a.test"); auto* storage_partition_impl = - static_cast<StoragePartitionImpl*>(shell() - ->web_contents() - ->GetBrowserContext() - ->GetDefaultStoragePartition()); + static_cast<StoragePartitionImpl*>(GetStoragePartition()); private_aggregation_host_ = new PrivateAggregationHost( /*on_report_request_received=*/mock_callback_.Get(), @@ -4243,29 +4916,31 @@ } class SharedStorageSelectURLLimitBrowserTest - : public SharedStorageBrowserTest, - public testing::WithParamInterface<bool> { + : public SharedStorageBrowserTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { public: SharedStorageSelectURLLimitBrowserTest() { - if (GetParam()) { - feature_list_ - .InitWithFeaturesAndParameters(/*enabled_features=*/ - {{blink::features:: - kSharedStorageSelectURLLimit, - {{"SharedStorageMaxAllowedSelectURLC" - "al" - "lsPerOriginPerPageLoad", - base::NumberToString( - kMaxSelectURLCalls)}}}}, - /*disabled_features=*/{}); + if (LimitSelectURLCalls()) { + select_url_limit_feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/{{blink::features::kSharedStorageSelectURLLimit, + {{"SharedStorageMaxAllowedSelectURLCallsPerOri" + "ginPerPageLoad", + base::NumberToString(kMaxSelectURLCalls)}}}}, + /*disabled_features=*/{}); } else { - feature_list_.InitWithFeaturesAndParameters( - /*enabled_features=*/{}, - /*disabled_features=*/ - {blink::features::kSharedStorageSelectURLLimit}); + select_url_limit_feature_list_.InitAndDisableFeature( + blink::features::kSharedStorageSelectURLLimit); } + + fenced_frame_api_change_feature_.InitWithFeatureState( + blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig()); + fenced_frame_feature_.InitAndEnableFeature(blink::features::kFencedFrames); } + bool LimitSelectURLCalls() const { return std::get<0>(GetParam()); } + + bool ResolveSelectURLToConfig() override { return std::get<1>(GetParam()); } + // Precondition: `addModule('shared_storage/simple_module.js')` has been // called in the main frame. void RunSuccessfulSelectURLInMainFrame( @@ -4273,9 +4948,16 @@ WebContentsConsoleObserver* console_observer) { std::string urn_uuid = EvalJs(shell(), R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], - {data: {'mockResult':0}}); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult':0} + } + ); )") .ExtractString(); @@ -4303,16 +4985,43 @@ EXPECT_TRUE(ExecJs(iframe_node, R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); + EXPECT_TRUE( + ExecJs(iframe_node, JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); - std::string urn_uuid = EvalJs(iframe_node, R"( - sharedStorage.selectURL( + TestSelectURLFencedFrameConfigObserver config_observer( + GetStoragePartition()); + EvalJsResult result = EvalJs(iframe_node, R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], - {data: {'mockResult':0}}); - )") - .ExtractString(); + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); - EXPECT_TRUE(blink::IsValidUrnUuidURL(GURL(urn_uuid))); + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = + config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There are 2 "worklet operations": `addModule()` and `selectURL()`. test_worklet_host_manager() @@ -4320,7 +5029,7 @@ ->WaitForWorkletResponsesCount(2); SharedStorageBudgetMetadata* metadata = - GetSharedStorageBudgetMetadata(GURL(urn_uuid)); + GetSharedStorageBudgetMetadata(observed_urn_uuid.value()); EXPECT_TRUE(metadata); EXPECT_EQ(metadata->origin, https_server()->GetOrigin(host_str)); EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0); @@ -4330,18 +5039,21 @@ } private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList select_url_limit_feature_list_; + base::test::ScopedFeatureList fenced_frame_api_change_feature_; + base::test::ScopedFeatureList fenced_frame_feature_; }; INSTANTIATE_TEST_SUITE_P(All, SharedStorageSelectURLLimitBrowserTest, - testing::Bool(), + testing::Combine(testing::Bool(), testing::Bool()), [](const auto& info) { - if (info.param) { - return "SelectURLLimit"; - } else { - return "NoSelectURLLimit"; - } + return base::StrCat( + {"LimitSelectURLCalls", + std::get<0>(info.param) ? "Enabled" + : "Disabled", + "_ResolveSelectURLTo", + std::get<1>(info.param) ? "Config" : "URN"}); }); IN_PROC_BROWSER_TEST_P(SharedStorageSelectURLLimitBrowserTest, @@ -4364,14 +5076,25 @@ RunSuccessfulSelectURLInMainFrame("a.test", &console_observer); } - if (GetParam()) { + if (LimitSelectURLCalls()) { // The limit for `selectURL()` has now been reached for "a.test". Make one // more call, which will be blocked. + EXPECT_TRUE( + ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); EvalJsResult result = EvalJs(shell(), R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], - {data: {'mockResult':0}}); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); )"); EXPECT_EQ( @@ -4386,7 +5109,7 @@ WaitForHistograms({kTimingSelectUrlExecutedInWorkletHistogram}); int expected_success_count = - GetParam() ? kMaxSelectURLCalls : kMaxSelectURLCalls + 1; + LimitSelectURLCalls() ? kMaxSelectURLCalls : kMaxSelectURLCalls + 1; histogram_tester_.ExpectTotalCount(kTimingSelectUrlExecutedInWorkletHistogram, expected_success_count); @@ -4429,7 +5152,7 @@ FrameTreeNode* iframe_node = CreateIFrame(PrimaryFrameTreeNodeRoot(), iframe_url); - if (GetParam()) { + if (LimitSelectURLCalls()) { EXPECT_TRUE(ExecJs(iframe_node, R"( sharedStorage.worklet.addModule('shared_storage/simple_module.js'); )")); @@ -4441,11 +5164,22 @@ // The limit for `selectURL()` has now been reached for "b.test". Make one // more call, which will be blocked. + EXPECT_TRUE( + ExecJs(iframe_node, JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); EvalJsResult result = EvalJs(iframe_node, R"( sharedStorage.selectURL( - 'test-url-selection-operation', - [{url: "fenced_frames/title0.html"}], - {data: {'mockResult':0}}); + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + data: {'mockResult': 0}, + resolveToConfig: resolveSelectURLToConfig + } + ); )"); EXPECT_EQ( @@ -4460,7 +5194,7 @@ WaitForHistograms({kTimingSelectUrlExecutedInWorkletHistogram}); int expected_success_count = - GetParam() ? kMaxSelectURLCalls : kMaxSelectURLCalls + 1; + LimitSelectURLCalls() ? kMaxSelectURLCalls : kMaxSelectURLCalls + 1; histogram_tester_.ExpectTotalCount(kTimingSelectUrlExecutedInWorkletHistogram, expected_success_count); @@ -4471,7 +5205,7 @@ AccessType::kDocumentAddModule, MainFrameId(), origin_str, SharedStorageEventParams::CreateForAddModule(https_server()->GetURL( "b.test", "/shared_storage/simple_module.js"))); - if (GetParam() && i == kMaxSelectURLCalls) { + if (LimitSelectURLCalls() && i == kMaxSelectURLCalls) { break; } expected_accesses.emplace_back( @@ -4551,28 +5285,40 @@ } class SharedStorageReportEventLimitBrowserTest - : public SharedStorageReportEventBrowserTest, - public testing::WithParamInterface<bool> { + : public SharedStorageFencedFrameInteractionBrowserTestBase, + public testing::WithParamInterface<std::tuple<bool, bool>> { public: SharedStorageReportEventLimitBrowserTest() { - if (GetParam()) { - feature_list_ - .InitWithFeaturesAndParameters(/*enabled_features=*/ - {{blink::features:: - kSharedStorageReportEventLimit, - {{"SharedStorageReportEventBitBudget" - "PerPageLoad", - base::NumberToString( - kReportEventBitBudget)}}}}, - /*disabled_features=*/{}); + if (LimitSharedStorageReportEventCalls()) { + report_event_feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/ + {{blink::features::kSharedStorageReportEventLimit, + {{"SharedStorageReportEventBitBudgetPerPageLoad", + base::NumberToString(kReportEventBitBudget)}}}}, + /*disabled_features=*/{}); } else { - feature_list_.InitWithFeaturesAndParameters( + report_event_feature_list_.InitWithFeaturesAndParameters( /*enabled_features=*/{}, /*disabled_features=*/ {blink::features::kSharedStorageReportEventLimit}); } + + fenced_frame_feature_list_.InitWithFeatureState( + blink::features::kFencedFramesAPIChanges, ResolveSelectURLToConfig()); } + // Defer the server to start after `ControllableHttpResponse` is constructed. + void FinishSetup() override { + https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath()); + https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + } + + bool LimitSharedStorageReportEventCalls() const { + return std::get<0>(GetParam()); + } + + bool ResolveSelectURLToConfig() override { return std::get<1>(GetParam()); } + // Precondition: `addModule('shared_storage/simple_module.js')` and // `selectURL()` have been called in the main frame. void RunSuccessfulReportEvents( @@ -4605,19 +5351,19 @@ } private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList report_event_feature_list_; + base::test::ScopedFeatureList fenced_frame_feature_list_; }; -INSTANTIATE_TEST_SUITE_P(All, - SharedStorageReportEventLimitBrowserTest, - testing::Bool(), - [](const auto& info) { - if (info.param) { - return "ReportEventLimit"; - } else { - return "NoReportEventLimit"; - } - }); +INSTANTIATE_TEST_SUITE_P( + All, + SharedStorageReportEventLimitBrowserTest, + testing::Combine(testing::Bool(), testing::Bool()), + [](const auto& info) { + return base::StrCat( + {"ReportEventLimit", std::get<0>(info.param) ? "Enabled" : "Disabled", + "_ResolveSelectURLTo", std::get<1>(info.param) ? "Config" : "URN"}); + }); IN_PROC_BROWSER_TEST_P(SharedStorageReportEventLimitBrowserTest, ReportEvent_SameEntropyCalls_LimitReached) { @@ -4653,9 +5399,43 @@ .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(1); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), "window.urls = generateUrls(8);")); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); std::vector<GURL> urns; for (size_t i = 0; i <= call_limit; ++i) { - urns.emplace_back(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + TestSelectURLFencedFrameConfigObserver config_observer( + GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = + config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } + + urns.push_back(observed_urn_uuid.value()); } // There are `call_limit + 1` "worklet operations": `selectURL()`. @@ -4672,7 +5452,7 @@ FrameTreeNode* fenced_frame_root_node = CreateFencedFrame(urns[call_limit]); - if (GetParam()) { + if (LimitSharedStorageReportEventCalls()) { // The limit for `reportEvent()` has now been reached for this page. Make // one more call, which will be blocked. std::string click_event_data = "this is a click"; @@ -4733,10 +5513,78 @@ ->WaitForWorkletResponsesCount(1); std::vector<GURL> urns; - urns.emplace_back(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver select_from_8urls_config_observer( + GetStoragePartition()); + EvalJsResult select_from_8urls_result = EvalJs(shell(), R"( + (async function() { + const urls_8 = generateUrls(8); + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls_8, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(select_from_8urls_result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_from_8urls = + select_from_8urls_config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_from_8urls.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_from_8urls.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(select_from_8urls_result.ExtractString(), + observed_urn_uuid_from_8urls->spec()); + } + + urns.push_back(observed_urn_uuid_from_8urls.value()); + EXPECT_TRUE(ExecJs(shell(), "window.urls_4 = generateUrls(4);")); for (size_t i = 0; i <= input4_call_limit; ++i) { - urns.emplace_back(EvalJs(shell(), kSelectFrom4URLsScript).ExtractString()); + TestSelectURLFencedFrameConfigObserver select_from_4urls_config_observer( + GetStoragePartition()); + EvalJsResult select_from_4urls_result = EvalJs(shell(), R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls_4, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(select_from_4urls_result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid_from_4urls = + select_from_4urls_config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid_from_4urls.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid_from_4urls.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(select_from_4urls_result.ExtractString(), + observed_urn_uuid_from_4urls->spec()); + } + + urns.push_back(observed_urn_uuid_from_4urls.value()); } // There are `input4_call_limit + 2` "worklet operations": `selectURL()`. @@ -4745,31 +5593,31 @@ ->WaitForWorkletResponsesCount(input4_call_limit + 2); // The first pair of `reportEvent()` calls will deduct 3 bits from the budget. - FrameTreeNode* fenced_frame_root_node0 = CreateFencedFrame(urns[0]); + FrameTreeNode* fenced_frame_root_node_0 = CreateFencedFrame(urns[0]); - RunSuccessfulReportEvents(fenced_frame_root_node0, responses[0].get(), + RunSuccessfulReportEvents(fenced_frame_root_node_0, responses[0].get(), responses[1].get()); for (size_t i = 1; i <= input4_call_limit; ++i) { // Subsequent pairs of calls to `reportEvent()` will deduct 2 bits from the // budget. - FrameTreeNode* fenced_frame_root_node1 = CreateFencedFrame(urns[i]); + FrameTreeNode* fenced_frame_root_node_1 = CreateFencedFrame(urns[i]); - RunSuccessfulReportEvents(fenced_frame_root_node1, responses[2 * i].get(), + RunSuccessfulReportEvents(fenced_frame_root_node_1, responses[2 * i].get(), responses[2 * i + 1].get()); } - FrameTreeNode* fenced_frame_root_node2 = + FrameTreeNode* fenced_frame_root_node_2 = CreateFencedFrame(urns[input4_call_limit + 1]); size_t current_response_index = 2 * (input4_call_limit + 1); - if (GetParam()) { + if (LimitSharedStorageReportEventCalls()) { // The limit for `reportEvent()` has now been reached for this page. Make // one more call, which will be blocked. std::string click_event_data = "this is a click"; EXPECT_TRUE( - ExecJs(fenced_frame_root_node2, + ExecJs(fenced_frame_root_node_2, JsReplace("window.fence.reportEvent({" " eventType: 'click'," " eventData: $1," @@ -4783,12 +5631,12 @@ base::UTF16ToUTF8(console_observer.messages().back().message)); // Running the first pair of calls again will not cause any errors. - RunSuccessfulReportEvents(fenced_frame_root_node0, + RunSuccessfulReportEvents(fenced_frame_root_node_0, responses[current_response_index].get(), responses[current_response_index + 1].get()); } else { // The `reportEvent()` limit is disabled. The calls will run successfully. - RunSuccessfulReportEvents(fenced_frame_root_node2, + RunSuccessfulReportEvents(fenced_frame_root_node_2, responses[current_response_index].get(), responses[current_response_index + 1].get()); } @@ -4820,14 +5668,48 @@ .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(1); - GURL urn = GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + const urls = generateUrls(8); + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There is one "worklet operation": `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(1); - FrameTreeNode* fenced_frame_root_node = CreateFencedFrame(urn); + FrameTreeNode* fenced_frame_root_node = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); RunSuccessfulReportEvents(fenced_frame_root_node, responses[0].get(), responses[1].get()); @@ -4872,21 +5754,56 @@ .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(1); - GURL urn = GURL(EvalJs(shell(), kSelectFrom8URLsScript).ExtractString()); + EXPECT_TRUE(ExecJs(shell(), kGenerateURLsListScript)); + EXPECT_TRUE(ExecJs(shell(), JsReplace("window.resolveSelectURLToConfig = $1;", + ResolveSelectURLToConfig()))); + + TestSelectURLFencedFrameConfigObserver config_observer(GetStoragePartition()); + EvalJsResult result = EvalJs(shell(), R"( + (async function() { + const urls = generateUrls(8); + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + urls, + { + data: {'mockResult': 1}, + resolveToConfig: resolveSelectURLToConfig + } + ); + if (resolveSelectURLToConfig && + !(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )"); + + EXPECT_TRUE(result.error.empty()); + const absl::optional<GURL>& observed_urn_uuid = config_observer.GetUrnUuid(); + EXPECT_TRUE(observed_urn_uuid.has_value()); + EXPECT_TRUE(blink::IsValidUrnUuidURL(observed_urn_uuid.value())); + + if (!ResolveSelectURLToConfig()) { + EXPECT_EQ(result.ExtractString(), observed_urn_uuid->spec()); + } // There is one "worklet operation": `selectURL()`. test_worklet_host_manager() .GetAttachedWorkletHost() ->WaitForWorkletResponsesCount(1); - FrameTreeNode* fenced_frame_root_node = CreateFencedFrame(urn); + FrameTreeNode* fenced_frame_root_node = CreateFencedFrame( + ResolveSelectURLToConfig() + ? FencedFrameNavigationTarget("select_url_result") + : FencedFrameNavigationTarget(observed_urn_uuid.value())); EXPECT_DOUBLE_EQ(GetRemainingBudget(shared_storage_origin), kBudgetAllowed); EXPECT_DOUBLE_EQ(RemainingBudgetViaJSForFrame(PrimaryFrameTreeNodeRoot()), kBudgetAllowed); OpenPopup(fenced_frame_root_node, - https_server()->GetURL("b.test", kSimplePagePath), /*name=*/""); + https_server()->GetURL("b.test", kSimplePagePath), + /*name=*/""); // After the popup, log(8)=3 bits should have been withdrawn from the // original shared storage origin. @@ -4919,35 +5836,35 @@ MakeFilter({"The call to fence.reportEvent was blocked due to " "insufficient budget."})); - url::Origin shared_storage_origin1 = + url::Origin shared_storage_origin_1 = url::Origin::Create(https_server()->GetURL("b.test", kSimplePagePath)); // This call to `selectURL()` will have 8 input URLs, and hence // 3 = log2(8) bits of entropy. - GURL urn_uuid1 = SelectFrom8URLsInContext(shared_storage_origin1); - FrameTreeNode* outer_fenced_frame_root_node = CreateFencedFrame(urn_uuid1); + GURL urn_uuid_1 = SelectFrom8URLsInContext(shared_storage_origin_1); + FrameTreeNode* outer_fenced_frame_root_node = CreateFencedFrame(urn_uuid_1); - url::Origin shared_storage_origin2 = + url::Origin shared_storage_origin_2 = url::Origin::Create(https_server()->GetURL("c.test", kSimplePagePath)); // This call to `selectURL()` will have 8 input URLs, and hence // 3 = log2(8) bits of entropy. - GURL urn_uuid2 = SelectFrom8URLsInContext(shared_storage_origin2, - outer_fenced_frame_root_node); + GURL urn_uuid_2 = SelectFrom8URLsInContext(shared_storage_origin_2, + outer_fenced_frame_root_node); FrameTreeNode* inner_fenced_frame_root_node = - CreateFencedFrame(outer_fenced_frame_root_node, urn_uuid2); + CreateFencedFrame(outer_fenced_frame_root_node, urn_uuid_2); RunSuccessfulReportEvents(inner_fenced_frame_root_node, responses[0].get(), responses[1].get()); // This call to `selectURL()` will have 8 input URLs, and hence // 3 = log2(8) bits of entropy. - GURL extra_urn = SelectFrom8URLsInContext(shared_storage_origin1); + GURL extra_urn = SelectFrom8URLsInContext(shared_storage_origin_1); FrameTreeNode* extra_fenced_frame_root_node = CreateFencedFrame(extra_urn); - if (GetParam()) { + if (LimitSharedStorageReportEventCalls()) { // The limit for `reportEvent()` has now been reached for this page. Make // one more call, which will be blocked. std::string click_event_data = "this is a click";
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.cc b/content/browser/shared_storage/shared_storage_worklet_host.cc index 23c77b3..32a23fa 100644 --- a/content/browser/shared_storage/shared_storage_worklet_host.cc +++ b/content/browser/shared_storage/shared_storage_worklet_host.cc
@@ -136,12 +136,16 @@ const GURL& urn_uuid = it->first; bool failed_due_to_no_budget = false; - page_->fenced_frame_urls_map().OnSharedStorageURNMappingResultDetermined( - urn_uuid, - CreateSharedStorageURNMappingResult( - storage_partition_, browser_context_, shared_storage_origin_, - std::move(it->second), - /*index=*/0, /*budget_remaining=*/0.0, failed_due_to_no_budget)); + absl::optional<FencedFrameConfig> config = + page_->fenced_frame_urls_map() + .OnSharedStorageURNMappingResultDetermined( + urn_uuid, CreateSharedStorageURNMappingResult( + storage_partition_, browser_context_, + shared_storage_origin_, std::move(it->second), + /*index=*/0, /*budget_remaining=*/0.0, + failed_due_to_no_budget)); + + shared_storage_worklet_host_manager_->NotifyConfigPopulated(config); it = unresolved_urns_.erase(it); } @@ -265,6 +269,8 @@ /*result_config=*/ config.RedactFor(FencedFrameEntity::kEmbedder)); + shared_storage_worklet_host_manager_->NotifyUrnUuidGenerated(urn_uuid); + GetAndConnectToSharedStorageWorkletService()->RunURLSelectionOperation( name, urls, serialized_data, base::BindOnce( @@ -774,8 +780,12 @@ } } - page_->fenced_frame_urls_map().OnSharedStorageURNMappingResultDetermined( - urn_uuid, std::move(mapping_result)); + absl::optional<FencedFrameConfig> config = + page_->fenced_frame_urls_map() + .OnSharedStorageURNMappingResultDetermined( + urn_uuid, std::move(mapping_result)); + + shared_storage_worklet_host_manager_->NotifyConfigPopulated(config); } base::UmaHistogramLongTimes(
diff --git a/content/browser/shared_storage/shared_storage_worklet_host_manager.cc b/content/browser/shared_storage/shared_storage_worklet_host_manager.cc index 593846a..4ef52e47 100644 --- a/content/browser/shared_storage/shared_storage_worklet_host_manager.cc +++ b/content/browser/shared_storage/shared_storage_worklet_host_manager.cc
@@ -101,4 +101,18 @@ keep_alive_shared_storage_worklet_hosts_.erase(worklet_host); } +void SharedStorageWorkletHostManager::NotifyUrnUuidGenerated( + const GURL& urn_uuid) { + for (SharedStorageObserverInterface& observer : observers_) { + observer.OnUrnUuidGenerated(urn_uuid); + } +} + +void SharedStorageWorkletHostManager::NotifyConfigPopulated( + const absl::optional<FencedFrameConfig>& config) { + for (SharedStorageObserverInterface& observer : observers_) { + observer.OnConfigPopulated(config); + } +} + } // namespace content
diff --git a/content/browser/shared_storage/shared_storage_worklet_host_manager.h b/content/browser/shared_storage/shared_storage_worklet_host_manager.h index 3850e9f..032ba05 100644 --- a/content/browser/shared_storage/shared_storage_worklet_host_manager.h +++ b/content/browser/shared_storage/shared_storage_worklet_host_manager.h
@@ -17,6 +17,7 @@ namespace content { +struct FencedFrameConfig; class SharedStorageDocumentServiceImpl; class SharedStorageWorkletDriver; class SharedStorageWorkletHost; @@ -55,6 +56,11 @@ const std::string& main_frame_id, const std::string& owner_origin, const SharedStorageEventParams& params) = 0; + + virtual void OnUrnUuidGenerated(const GURL& urn_uuid) = 0; + + virtual void OnConfigPopulated( + const absl::optional<FencedFrameConfig>& config) = 0; }; void OnDocumentServiceDestroyed( @@ -85,6 +91,10 @@ return keep_alive_shared_storage_worklet_hosts_; } + void NotifyUrnUuidGenerated(const GURL& urn_uuid); + + void NotifyConfigPopulated(const absl::optional<FencedFrameConfig>& config); + protected: void OnWorkletKeepAliveFinished(SharedStorageWorkletHost*);
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc index a083e7b..203bcf9 100644 --- a/content/browser/speech/tts_controller_impl.cc +++ b/content/browser/speech/tts_controller_impl.cc
@@ -61,6 +61,14 @@ return static_cast<TtsUtteranceImpl*>(utterance); } +bool IsUtteranceSpokenByRemoteEngine(TtsUtterance* utterance) { + if (utterance && !utterance->GetEngineId().empty()) { + TtsUtteranceImpl* utterance_impl = AsUtteranceImpl(utterance); + return utterance_impl->spoken_by_remote_engine(); + } + return false; +} + } // namespace // @@ -233,13 +241,8 @@ } void TtsControllerImpl::StopCurrentUtterance() { - bool spoken_by_remote_engine = false; - if (current_utterance_ && !current_utterance_->GetEngineId().empty()) { - TtsUtteranceImpl* utterance_impl = - AsUtteranceImpl(current_utterance_.get()); - spoken_by_remote_engine = utterance_impl->spoken_by_remote_engine(); - } - + bool spoken_by_remote_engine = + IsUtteranceSpokenByRemoteEngine(current_utterance_.get()); if (engine_delegate_ && current_utterance_ && !current_utterance_->GetEngineId().empty() && !spoken_by_remote_engine) { engine_delegate_->Stop(current_utterance_.get()); @@ -261,13 +264,25 @@ void TtsControllerImpl::Pause() { base::RecordAction(base::UserMetricsAction("TextToSpeech.Pause")); + + auto* external_delegate = GetTtsPlatform()->GetExternalPlatformDelegate(); + if (external_delegate) { + external_delegate->Pause(); + return; + } + if (paused_) return; paused_ = true; + bool spoken_by_remote_engine = + IsUtteranceSpokenByRemoteEngine(current_utterance_.get()); if (engine_delegate_ && current_utterance_ && - !current_utterance_->GetEngineId().empty()) { + !current_utterance_->GetEngineId().empty() && !spoken_by_remote_engine) { engine_delegate_->Pause(current_utterance_.get()); + } else if (current_utterance_ && !current_utterance_->GetEngineId().empty() && + spoken_by_remote_engine && remote_engine_delegate_) { + remote_engine_delegate_->Pause(current_utterance_.get()); } else if (current_utterance_) { DCHECK(TtsPlatformReady()); GetTtsPlatform()->ClearError(); @@ -277,13 +292,24 @@ void TtsControllerImpl::Resume() { base::RecordAction(base::UserMetricsAction("TextToSpeech.Resume")); + auto* external_delegate = GetTtsPlatform()->GetExternalPlatformDelegate(); + if (external_delegate) { + external_delegate->Resume(); + return; + } + if (!paused_) return; paused_ = false; + bool spoken_by_remote_engine = + IsUtteranceSpokenByRemoteEngine(current_utterance_.get()); if (engine_delegate_ && current_utterance_ && - !current_utterance_->GetEngineId().empty()) { + !current_utterance_->GetEngineId().empty() && !spoken_by_remote_engine) { engine_delegate_->Resume(current_utterance_.get()); + } else if (current_utterance_ && !current_utterance_->GetEngineId().empty() && + spoken_by_remote_engine && remote_engine_delegate_) { + remote_engine_delegate_->Resume(current_utterance_.get()); } else if (current_utterance_) { DCHECK(TtsPlatformReady()); GetTtsPlatform()->ClearError();
diff --git a/content/browser/speech/tts_controller_unittest.cc b/content/browser/speech/tts_controller_unittest.cc index 06057705..2f59ab28 100644 --- a/content/browser/speech/tts_controller_unittest.cc +++ b/content/browser/speech/tts_controller_unittest.cc
@@ -148,10 +148,6 @@ // Count API calls (TtsEngineDelegate:) void Stop(TtsUtterance* utterance) override { ++stop_called_; } - void Stop(BrowserContext* browser_context, - const std::string& engine_id) override { - ++stop_called_; - } void Pause(TtsUtterance* utterance) override { ++pause_called_; } void Resume(TtsUtterance* utterance) override { ++resume_called_; }
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 9c56059..13fefa88 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -196,6 +196,7 @@ sources = [ "java/src/org/chromium/content/browser/AppWebMessagePort.java", + "java/src/org/chromium/content/browser/AttributionOsLevelManager.java", "java/src/org/chromium/content/browser/AudioFocusDelegate.java", "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java", "java/src/org/chromium/content/browser/BindingManager.java", @@ -419,6 +420,7 @@ "java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java", "java/src/org/chromium/content/app/ContentMain.java", "java/src/org/chromium/content/browser/AppWebMessagePort.java", + "java/src/org/chromium/content/browser/AttributionOsLevelManager.java", "java/src/org/chromium/content/browser/AudioFocusDelegate.java", "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java", "java/src/org/chromium/content/browser/BrowserContextHandleImpl.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/AttributionOsLevelManager.java b/content/public/android/java/src/org/chromium/content/browser/AttributionOsLevelManager.java new file mode 100644 index 0000000..bbd6ff3 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/AttributionOsLevelManager.java
@@ -0,0 +1,38 @@ +// 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. + +package org.chromium.content.browser; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.url.GURL; + +/** + * Handles passing registrations with Web Attribution Reporting API to the underlying native + * library. + */ +public class AttributionOsLevelManager { + private long mNativePtr; + + @CalledByNative + private AttributionOsLevelManager(long nativePtr) { + mNativePtr = nativePtr; + } + + /** + * Registers a web attribution source with native, see `registerWebSource()`: + * https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/measurement/MeasurementManager. + */ + @CalledByNative + private void registerAttributionSource( + GURL registrationUrl, GURL topLevelOrigin, boolean isDebugKeyAllowed) { + // TODO(johnidel): Register with the Android API, see + // https://developer.android.com/design-for-safety/privacy-sandbox/guides/attribution. + // This is dependent on support for the Tiramisu Privacy Sandbox SDK. + } + + @CalledByNative + private void nativeDestroyed() { + mNativePtr = 0; + } +}
diff --git a/content/public/browser/tts_controller.h b/content/public/browser/tts_controller.h index 3d17c9a..97a1816 100644 --- a/content/public/browser/tts_controller.h +++ b/content/public/browser/tts_controller.h
@@ -65,17 +65,10 @@ // Stop speaking the given utterance by sending an event to the target // associated with this utterance. virtual void Stop(TtsUtterance* utterance) = 0; - - // Stop the given speech engine loaded in |browser_context|. - virtual void Stop(BrowserContext* browser_context, - const std::string& engine_id) = 0; - // Pause in the middle of speaking this utterance. virtual void Pause(TtsUtterance* utterance) = 0; - // Resume speaking this utterance. virtual void Resume(TtsUtterance* utterance) = 0; - // Load the built-in TTS engine. virtual void LoadBuiltInTtsEngine(BrowserContext* browser_context) = 0; @@ -100,6 +93,14 @@ // Requests the remote TTS engine associated with |utterance| to stop // speaking the |utterance|. virtual void Stop(TtsUtterance* utterance) = 0; + + // Requests the remote TTS engine associated with |utterance| to pause + // speaking the |utterance|. + virtual void Pause(TtsUtterance* utterance) = 0; + + // Requests the remote TTS engine associated with |utterance| to resume + // speaking the |utterance|. + virtual void Resume(TtsUtterance* utterance) = 0; }; // Class that wants to be notified when the set of
diff --git a/content/public/browser/tts_platform.h b/content/public/browser/tts_platform.h index 5c80cf0..53f23a0 100644 --- a/content/public/browser/tts_platform.h +++ b/content/public/browser/tts_platform.h
@@ -58,6 +58,12 @@ // Requests external TtsController to stop the current utterance if it matches // the given |source_url|. virtual void Stop(const GURL& source_url) = 0; + + // Requests external TtsController to pause speech synthesis. + virtual void Pause() = 0; + + // Requests external TtsController to resume speech synthesis. + virtual void Resume() = 0; }; // Abstract class that defines the native platform TTS interface,
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc index 986dd5c..f29b7b99 100644 --- a/content/public/test/browser_test_base.cc +++ b/content/public/test/browser_test_base.cc
@@ -123,16 +123,10 @@ #endif #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/base_switches.h" -#include "base/files/file_path_watcher.h" -#include "base/files/file_util.h" #include "base/files/scoped_file.h" -#include "base/strings/string_util.h" -#include "base/test/task_environment.h" #include "chromeos/crosapi/cpp/crosapi_constants.h" // nogncheck #include "chromeos/lacros/lacros_test_helper.h" #include "chromeos/startup/startup_switches.h" // nogncheck -#include "content/public/test/browser_test_switches.h" #include "mojo/public/cpp/platform/socket_utils_posix.h" #endif @@ -436,35 +430,17 @@ disable_crosapi_ = std::make_unique<chromeos::ScopedDisableCrosapiForTesting>(); } else { - bool connected_to_ash = false; - int retry_left = 2; - base::ScopedFD socket_fd; - int flags = 0; - while (retry_left-- > 0 && !connected_to_ash) { - auto channel = mojo::NamedPlatformChannel::ConnectToServer(socket_path); - socket_fd = channel.TakePlatformHandle().TakeFD(); + auto channel = mojo::NamedPlatformChannel::ConnectToServer(socket_path); + base::ScopedFD socket_fd = channel.TakePlatformHandle().TakeFD(); - // Mark the channel as blocking. - flags = fcntl(socket_fd.get(), F_GETFL); - - connected_to_ash = flags != -1; - - if (!connected_to_ash) { - LOG(WARNING) << "Ash is probably not running. Perhaps it crashed?" - << " Try to start ash again."; - StartAshChrome(); - } - } + // Mark the channel as blocking. + int flags = fcntl(socket_fd.get(), F_GETFL); std::string helper_msg = "On bot, open CAS outputs on test result page(Milo)," "there is a ash_chrome.log file which contains ash log." "For local debugging, pass in --ash-logging-path to test runner."; - if (connected_to_ash) { - LOG(INFO) << "Connected to ash."; - } else { - LOG(FATAL) << "Ash is probably not running. Perhaps it crashed?" - << helper_msg; - } + PCHECK(flags != -1) << "Ash is probably not running. Perhaps it crashed?" + << helper_msg; fcntl(socket_fd.get(), F_SETFL, flags & ~O_NONBLOCK); uint8_t buf[32]; @@ -1165,82 +1141,4 @@ return nullptr; } -#if BUILDFLAG(IS_CHROMEOS_LACROS) -void BrowserTestBase::StartAshChrome() { - base::test::SingleThreadTaskEnvironment task_environment; - base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); - - base::FilePath mojo_socket_file = - cmdline->GetSwitchValuePath("lacros-mojo-socket-for-testing"); - if (mojo_socket_file.empty()) { - LOG(WARNING) << "Start ash failed because of missing " - << "--lacros-mojo-socket-for-testing"; - return; - } - // Delete the existing file because we will reuse the same file for the new - // ash chrome instance. - CHECK(base::DeleteFile(mojo_socket_file)); - base::FilePath ash_chrome_path = - cmdline->GetSwitchValuePath("ash-chrome-path"); - CHECK(!ash_chrome_path.empty()); - - base::CommandLine ash_cmdline(ash_chrome_path); - base::FilePath ash_user_data_dir = - cmdline->GetSwitchValuePath(content::test::switches::kAshUserDataDir); - CHECK(base::DeletePathRecursively(ash_user_data_dir)); - ash_cmdline.AppendSwitchPath("user-data-dir", ash_user_data_dir); - ash_cmdline.AppendSwitch("enable-wayland-server"); - ash_cmdline.AppendSwitch("no-startup-window"); - ash_cmdline.AppendSwitch("disable-lacros-keep-alive"); - ash_cmdline.AppendSwitch("disable-login-lacros-opening"); - - std::string ash_features = "LacrosSupport,LacrosPrimary,LacrosOnly"; - ash_cmdline.AppendSwitchASCII(switches::kEnableFeatures, ash_features); - - ash_cmdline.AppendSwitchPath("lacros-mojo-socket-for-testing", - mojo_socket_file); - - std::string wayland_socket; - CHECK( - base::Environment::Create()->GetVar("WAYLAND_DISPLAY", &wayland_socket)); - DCHECK(wayland_socket.length() > 0); - ash_cmdline.AppendSwitchASCII("wayland-server-socket", wayland_socket); - - const std::string ash_ready_file = - ash_user_data_dir.AppendASCII("ash_ready.txt").MaybeAsASCII(); - ash_cmdline.AppendSwitchASCII(content::test::switches::kAshReadyFilePath, - ash_ready_file); - - base::FilePathWatcher watcher; - base::RunLoop run_loop; - CHECK(watcher.Watch(base::FilePath(ash_ready_file), - base::FilePathWatcher::Type::kNonRecursive, - base::BindLambdaForTesting( - [&](const base::FilePath& filepath, bool error) { - CHECK(!error); - run_loop.Quit(); - }))); - base::LaunchOptions option; - option.new_process_group = true; - base::Process process = base::LaunchProcess(ash_cmdline, option); - CHECK(process.IsValid()); - run_loop.Run(); - // When ash is ready and crosapi was enabled, we expect mojo socket is - // also ready. - CHECK(base::PathExists(mojo_socket_file)); - base::FilePath ash_processes_dir = cmdline->GetSwitchValuePath( - content::test::switches::kAshProcessesDirPath); - CHECK(!ash_processes_dir.empty()); - base::FilePath temp_filepath; - std::string str_pid = base::StringPrintf("%d", process.Pid()); - base::File f = - CreateAndOpenTemporaryFileInDir(ash_processes_dir, &temp_filepath); - int ret_code = f.Write(0, str_pid.c_str(), str_pid.length()); - CHECK(ret_code >= 0 && (unsigned int)ret_code == str_pid.length()) - << "Cannot write to file " << temp_filepath.AsUTF8Unsafe() - << "Return code is " << ret_code; - f.Close(); -} -#endif - } // namespace content
diff --git a/content/public/test/browser_test_base.h b/content/public/test/browser_test_base.h index 28072eb8..1bc8e19b 100644 --- a/content/public/test/browser_test_base.h +++ b/content/public/test/browser_test_base.h
@@ -102,10 +102,6 @@ // needed when triggering the crash via SimulateNetworkServiceCrash method. void IgnoreNetworkServiceCrashes(); -#if BUILDFLAG(IS_CHROMEOS_LACROS) - void StartAshChrome(); -#endif - // Returns the host resolver being used for the tests. Subclasses might want // to configure it inside tests. net::RuleBasedHostResolverProc* host_resolver() {
diff --git a/content/public/test/browser_test_switches.cc b/content/public/test/browser_test_switches.cc deleted file mode 100644 index e15cac5..0000000 --- a/content/public/test/browser_test_switches.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/public/test/browser_test_switches.h" - -#if BUILDFLAG(IS_CHROMEOS_LACROS) -// The file path to indicate if ash is ready for testing. -// The file should not be on the file system initially. After -// ash is ready for testing, the file will be created. -// This is used to communicate between launcher and runner process. In general -// you should not pass in this arg directly. -const char content::test::switches::kAshReadyFilePath[] = "ash-ready-file-path"; - -// Ash chrome user data dir path. -const char content::test::switches::kAshUserDataDir[] = "ash-user-data-dir"; - -// A dir to store all the pids of ash chrome created during tests. -// This is used to communicate between launcher and runner process. In general -// you should not pass in this arg directly. -const char content::test::switches::kAshProcessesDirPath[] = - "ash-processes-dir-path"; -#endif
diff --git a/content/public/test/browser_test_switches.h b/content/public/test/browser_test_switches.h deleted file mode 100644 index 5cdfba8..0000000 --- a/content/public/test/browser_test_switches.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file should contain test switches for non unit gtests. Like -// browser test or interactive ui test. - -#ifndef CONTENT_PUBLIC_TEST_BROWSER_TEST_SWITCHES_H_ -#define CONTENT_PUBLIC_TEST_BROWSER_TEST_SWITCHES_H_ - -#include "build/chromeos_buildflags.h" - -namespace content::test::switches { - -#if BUILDFLAG(IS_CHROMEOS_LACROS) -extern const char kAshReadyFilePath[]; -extern const char kAshUserDataDir[]; -extern const char kAshProcessesDirPath[]; -#endif - -} // namespace content::test::switches - -#endif // CONTENT_PUBLIC_TEST_BROWSER_TEST_SWITCHES_H_
diff --git a/content/public/test/shared_storage_test_utils.cc b/content/public/test/shared_storage_test_utils.cc index 919b9e9..2d34c4ef 100644 --- a/content/public/test/shared_storage_test_utils.cc +++ b/content/public/test/shared_storage_test_utils.cc
@@ -6,7 +6,7 @@ #include <map> -#include "base/task/task_runner.h" +#include "base/functional/overloaded.h" #include "content/browser/renderer_host/frame_tree_node.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/shared_storage/shared_storage_document_service_impl.h" @@ -22,8 +22,6 @@ namespace content { -namespace { - SharedStorageWorkletHostManager* GetSharedStorageWorkletHostManagerForStoragePartition( StoragePartition* storage_partition) { @@ -31,8 +29,6 @@ ->GetSharedStorageWorkletHostManager(); } -} // namespace - std::string GetSharedStorageDisabledMessage() { return kSharedStorageDisabledMessage; } @@ -66,7 +62,8 @@ return manager->GetKeepAliveWorkletHostsForTesting().size(); } -RenderFrameHost* CreateFencedFrame(RenderFrameHost* root, const GURL& url) { +RenderFrameHost* CreateFencedFrame(RenderFrameHost* root, + const FencedFrameNavigationTarget& target) { FrameTreeNode* root_node = static_cast<RenderFrameHostImpl*>(root)->frame_tree_node(); size_t initial_child_count = root_node->child_count(); @@ -80,18 +77,28 @@ FrameTreeNode* fenced_frame_root_node = GetFencedFrameRootNode(root_node->child_at(initial_child_count)); - std::string navigate_fenced_frame_script = - JsReplace("f.src = $1;", url.spec()); - TestFrameNavigationObserver observer( fenced_frame_root_node->current_frame_host()); - EXPECT_EQ(url.spec(), EvalJs(root, navigate_fenced_frame_script)); + EvalJsResult result = EvalJs( + root, + absl::visit( + base::Overloaded{ + [](const GURL& url) { return JsReplace("f.src = $1;", url); }, + [](const std::string& config) { + return JsReplace("f.config = window[$1]", config); + }, + }, + target)); observer.Wait(); - return static_cast<RenderFrameHost*>( - fenced_frame_root_node->current_frame_host()); + EXPECT_TRUE(result.error.empty()); + if (absl::holds_alternative<GURL>(target)) { + EXPECT_EQ(result, absl::get<GURL>(target).spec()); + } + + return fenced_frame_root_node->current_frame_host(); } } // namespace content
diff --git a/content/public/test/shared_storage_test_utils.h b/content/public/test/shared_storage_test_utils.h index aee8684..7938309 100644 --- a/content/public/test/shared_storage_test_utils.h +++ b/content/public/test/shared_storage_test_utils.h
@@ -8,13 +8,22 @@ #include <stddef.h> #include <string> +#include "third_party/abseil-cpp/absl/types/variant.h" + class GURL; namespace content { class RenderFrameHost; +class SharedStorageWorkletHostManager; class StoragePartition; +using FencedFrameNavigationTarget = absl::variant<GURL, std::string>; + +SharedStorageWorkletHostManager* +GetSharedStorageWorkletHostManagerForStoragePartition( + StoragePartition* storage_partition); + std::string GetSharedStorageDisabledMessage(); std::string GetSharedStorageSelectURLDisabledMessage(); @@ -29,7 +38,10 @@ size_t GetKeepAliveSharedStorageWorkletHostsCount( StoragePartition* storage_partition); -RenderFrameHost* CreateFencedFrame(RenderFrameHost* root, const GURL& url); +// TODO(crbug.com/1414429): This function should be removed. Use +// `CreateFencedFrame` in fenced_frame_test_util.h instead. +RenderFrameHost* CreateFencedFrame(RenderFrameHost* root, + const FencedFrameNavigationTarget& target); } // namespace content
diff --git a/content/public/test/test_select_url_fenced_frame_config_observer.h b/content/public/test/test_select_url_fenced_frame_config_observer.h new file mode 100644 index 0000000..fbbe61c7 --- /dev/null +++ b/content/public/test/test_select_url_fenced_frame_config_observer.h
@@ -0,0 +1,51 @@ +// 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. + +#ifndef CONTENT_PUBLIC_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_H_ +#define CONTENT_PUBLIC_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_H_ + +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +class GURL; + +namespace content { + +struct FencedFrameConfig; +class StoragePartition; +class TestSelectURLFencedFrameConfigObserverImpl; + +// This observes: +// 1. the next generated urn::uuid. +// 2. the next populated fenced frame config that contains the observed +// urn::uuid. +// +// With the fenced frame API change, `selectURL()` can return a fenced frame +// config object instead of an urn::uuid. The urn::uuid in the returned config +// is opaque, but it is often needed to check the associated info +// (e.g. SharedStorageBudgetMetadata). Tests can use this observer to obtain the +// urn::uuid and the browser-side FencedFrameConfig. +// +// This observes only the first url::uuid and config. To observe a new one, a +// new observer must be created before calling `selectURL(). +class TestSelectURLFencedFrameConfigObserver { + public: + explicit TestSelectURLFencedFrameConfigObserver( + StoragePartition* storage_partition); + ~TestSelectURLFencedFrameConfigObserver(); + + const absl::optional<GURL>& GetUrnUuid() const; + const absl::optional<FencedFrameConfig>& GetConfig() const; + bool ConfigObserved() const; + + private: + raw_ptr<StoragePartition> storage_partition_; + std::unique_ptr<TestSelectURLFencedFrameConfigObserverImpl> impl_; +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_H_
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc index dfbaa5a1..0de4af1 100644 --- a/content/services/auction_worklet/seller_worklet.cc +++ b/content/services/auction_worklet/seller_worklet.cc
@@ -72,6 +72,46 @@ return v8_helper->InsertValue(key, v8_priority_signals, object); } +// Attempts to create an v8 Object from `maybe_promise_buyer_timeouts`. On fatal +// error, returns false. Otherwise, writes the result to +// `out_per_buyer_timeouts`, which will be left unchanged if there are no times +// to write to it. +bool CreatePerBuyerTimeoutsObject( + v8::Isolate* isolate, + const blink::AuctionConfig::MaybePromiseBuyerTimeouts& + maybe_promise_buyer_timeouts, + v8::Local<v8::Object>& out_per_buyer_timeouts) { + DCHECK(!maybe_promise_buyer_timeouts.is_promise()); + + const blink::AuctionConfig::BuyerTimeouts& buyer_timeouts = + maybe_promise_buyer_timeouts.value(); + // If there are no times, leave `out_per_buyer_timeouts` empty, and indicate + // success. + if (!buyer_timeouts.per_buyer_timeouts.has_value() && + !buyer_timeouts.all_buyers_timeout.has_value()) { + return true; + } + + out_per_buyer_timeouts = v8::Object::New(isolate); + gin::Dictionary per_buyer_timeouts_dict(isolate, out_per_buyer_timeouts); + + if (buyer_timeouts.per_buyer_timeouts.has_value()) { + for (const auto& kv : buyer_timeouts.per_buyer_timeouts.value()) { + if (!per_buyer_timeouts_dict.Set(kv.first.Serialize(), + kv.second.InMilliseconds())) { + return false; + } + } + } + if (buyer_timeouts.all_buyers_timeout.has_value()) { + if (!per_buyer_timeouts_dict.Set( + "*", buyer_timeouts.all_buyers_timeout.value().InMilliseconds())) { + return false; + } + } + return true; +} + // Converts `auction_config` back to JSON format, and appends to args. // Returns true if conversion succeeded. // @@ -172,30 +212,27 @@ } v8::Local<v8::Object> per_buyer_timeouts; - DCHECK(!auction_ad_config_non_shared_params.buyer_timeouts.is_promise()); - const blink::AuctionConfig::BuyerTimeouts& buyer_timeouts = - auction_ad_config_non_shared_params.buyer_timeouts.value(); - if (buyer_timeouts.per_buyer_timeouts.has_value() || - buyer_timeouts.all_buyers_timeout.has_value()) { - per_buyer_timeouts = v8::Object::New(isolate); - gin::Dictionary per_buyer_timeouts_dict(isolate, per_buyer_timeouts); - if (buyer_timeouts.per_buyer_timeouts.has_value()) { - for (const auto& kv : buyer_timeouts.per_buyer_timeouts.value()) { - if (!per_buyer_timeouts_dict.Set(kv.first.Serialize(), - kv.second.InMilliseconds())) { - return false; - } - } - } - if (buyer_timeouts.all_buyers_timeout.has_value()) { - if (!per_buyer_timeouts_dict.Set( - "*", buyer_timeouts.all_buyers_timeout->InMilliseconds())) { - return false; - } - } + if (!CreatePerBuyerTimeoutsObject( + isolate, auction_ad_config_non_shared_params.buyer_timeouts, + per_buyer_timeouts)) { + return false; + } + if (!per_buyer_timeouts.IsEmpty()) { auction_config_dict.Set("perBuyerTimeouts", per_buyer_timeouts); } + v8::Local<v8::Object> per_buyer_cumulative_timeouts; + if (!CreatePerBuyerTimeoutsObject( + isolate, + auction_ad_config_non_shared_params.buyer_cumulative_timeouts, + per_buyer_cumulative_timeouts)) { + return false; + } + if (!per_buyer_cumulative_timeouts.IsEmpty()) { + auction_config_dict.Set("perBuyerCumulativeTimeouts", + per_buyer_cumulative_timeouts); + } + if (auction_ad_config_non_shared_params.per_buyer_priority_signals || auction_ad_config_non_shared_params.all_buyers_priority_signals) { v8::Local<v8::Object> per_buyer_priority_signals = v8::Object::New(isolate);
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc index 65eb7f6..1cccd24 100644 --- a/content/services/auction_worklet/seller_worklet_unittest.cc +++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -2521,6 +2521,16 @@ blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( std::move(buyer_timeouts)); + blink::AuctionConfig::BuyerTimeouts buyer_cumulative_timeouts; + buyer_cumulative_timeouts.per_buyer_timeouts.emplace(); + buyer_cumulative_timeouts.per_buyer_timeouts + .value()[url::Origin::Create(GURL("https://a.com"))] = + base::Milliseconds(101); + buyer_cumulative_timeouts.all_buyers_timeout = base::Milliseconds(151); + auction_ad_config_non_shared_params_.buyer_cumulative_timeouts = + blink::AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( + std::move(buyer_cumulative_timeouts)); + auction_ad_config_non_shared_params_.per_buyer_priority_signals = { {url::Origin::Create(GURL("https://a.com")), {{"signals_c", 0.5}}}}; auction_ad_config_non_shared_params_.all_buyers_priority_signals = { @@ -2538,6 +2548,7 @@ "perBuyerSignals":{"https://a.com":{"signals_a":"A"}, "https://b.com":{"signals_b":"B"}}, "perBuyerTimeouts":{"https://a.com":100,"*":150}, + "perBuyerCumulativeTimeouts":{"https://a.com":101,"*":151}, "perBuyerPrioritySignals":{"https://a.com":{"signals_c":0.5}, "*": {"signals_d":0}} })";
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 36c9d85..380dcf1 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -142,8 +142,6 @@ "../public/test/browser_test.h", "../public/test/browser_test_base.cc", "../public/test/browser_test_base.h", - "../public/test/browser_test_switches.cc", - "../public/test/browser_test_switches.h", "../public/test/browser_test_utils.cc", "../public/test/browser_test_utils.h", "../public/test/browsing_data_remover_test_util.cc", @@ -296,6 +294,7 @@ "../public/test/test_navigation_ui_data.h", "../public/test/test_renderer_host.cc", "../public/test/test_renderer_host.h", + "../public/test/test_select_url_fenced_frame_config_observer.h", "../public/test/test_storage_partition.cc", "../public/test/test_storage_partition.h", "../public/test/test_utils.cc", @@ -434,6 +433,8 @@ "test_render_widget_host.h", "test_render_widget_host_factory.cc", "test_render_widget_host_factory.h", + "test_select_url_fenced_frame_config_observer_impl.cc", + "test_select_url_fenced_frame_config_observer_impl.h", "test_web_contents.cc", "test_web_contents.h", "test_web_contents_factory.cc",
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt index e75d0f8..8785e4b 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt
@@ -4,4 +4,15 @@ ++++View text:"treeitem 3 of 5, level 1" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=2, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] ++++View text:"treeitem 1 of 2, level 2" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=0, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] ++++View text:"treeitem 1 of 1, level 3" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=0, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] -++++View text:"treeitem 2 of 2, level 2" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=1, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] \ No newline at end of file +++++View text:"treeitem 2 of 2, level 2" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=1, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] +++View CollectionInfo:[hierarchical, rows=3, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"] +++++TextView text:"schedule" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++View text:"wake up" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=0, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] +++++View text:"drink coffee" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=1, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] +++++++TextView text:"• " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++++TextView text:"drink coffee" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++++View CollectionInfo:[hierarchical, rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"] +++++++++TextView text:"tasks" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] +++++++++View text:"meeting" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=0, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] +++++++++View text:"lunch" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=1, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"] +++++View text:"cook dinner" clickable CollectionItemInfo:[rowSpan=0, colSpan=0, rowIndex=2, colIndex=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android.txt index 102a2e4..6a78b87 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android.txt
@@ -4,4 +4,15 @@ ++++android.view.View role_description='tree item' clickable collection_item name='treeitem 3 of 5, level 1' item_index=2 row_index=2 ++++android.view.View role_description='tree item' clickable collection_item name='treeitem 1 of 2, level 2' ++++android.view.View role_description='tree item' clickable collection_item name='treeitem 1 of 1, level 3' -++++android.view.View role_description='tree item' clickable collection_item name='treeitem 2 of 2, level 2' item_index=1 row_index=1 \ No newline at end of file +++++android.view.View role_description='tree item' clickable collection_item name='treeitem 2 of 2, level 2' item_index=1 row_index=1 +++android.view.View role_description='tree' collection hierarchical item_count=3 row_count=3 +++++android.widget.TextView name='schedule' +++++android.view.View role_description='tree item' clickable collection_item name='wake up' +++++android.view.View role_description='tree item' clickable collection_item name='drink coffee' item_index=1 row_index=1 +++++++android.widget.TextView name='%E2%80%A2 ' +++++++android.widget.TextView name='drink coffee' +++++++android.view.View role_description='tree' collection hierarchical item_count=2 row_count=2 +++++++++android.widget.TextView name='tasks' +++++++++android.view.View role_description='tree item' clickable collection_item name='meeting' +++++++++android.view.View role_description='tree item' clickable collection_item name='lunch' item_index=1 row_index=1 +++++android.view.View role_description='tree item' clickable collection_item name='cook dinner' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-auralinux.txt index ef3384ed..db1e7af 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-auralinux.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-auralinux.txt
@@ -10,3 +10,22 @@ ++++++[static] name='treeitem 1 of 1, level 3' ++++[tree item] name='treeitem 2 of 2, level 2' selectable posinset:2 setsize:2 ++++++[static] name='treeitem 2 of 2, level 2' +++[tree] setsize:3 +++++[static] name='schedule' +++++[tree item] name='wake up' selectable posinset:1 setsize:3 +++++++[static] name='%E2%80%A2 ' +++++++[static] name='wake up' +++++[tree item] name='drink coffee' selectable posinset:2 setsize:3 +++++++[static] name='%E2%80%A2 ' +++++++[static] name='drink coffee' +++++++[tree] setsize:2 +++++++++[static] name='tasks' +++++++++[tree item] name='meeting' selectable posinset:1 setsize:2 +++++++++++[static] name='%E2%97%A6 ' +++++++++++[static] name='meeting' +++++++++[tree item] name='lunch' selectable posinset:2 setsize:2 +++++++++++[static] name='%E2%97%A6 ' +++++++++++[static] name='lunch' +++++[tree item] name='cook dinner' selectable posinset:3 setsize:3 +++++++[static] name='%E2%80%A2 ' +++++++[static] name='cook dinner' \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-blink.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-blink.txt index 231d115..ce29886 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-blink.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-blink.txt
@@ -1,19 +1,55 @@ -rootWebArea -++genericContainer ignored -++++genericContainer ignored -++++++tree setSize=5 -++++++++treeItem name='treeitem 2 of 5, level 1' hierarchicalLevel=1 setSize=5 posInSet=2 selected=false +rootWebArea isLineBreakingObject=true +++genericContainer ignored isLineBreakingObject=true +++++genericContainer ignored isLineBreakingObject=true +++++++tree setSize=5 isLineBreakingObject=true +++++++++treeItem name='treeitem 2 of 5, level 1' hierarchicalLevel=1 setSize=5 posInSet=2 selected=false isLineBreakingObject=true ++++++++++staticText name='treeitem 2 of 5, level 1' ++++++++++++inlineTextBox name='treeitem 2 of 5, level 1' -++++++++treeItem name='treeitem 3 of 5, level 1' hierarchicalLevel=1 setSize=5 posInSet=3 selected=false +++++++++treeItem name='treeitem 3 of 5, level 1' hierarchicalLevel=1 setSize=5 posInSet=3 selected=false isLineBreakingObject=true ++++++++++staticText name='treeitem 3 of 5, level 1' ++++++++++++inlineTextBox name='treeitem 3 of 5, level 1' -++++++++treeItem name='treeitem 1 of 2, level 2' hierarchicalLevel=2 setSize=2 posInSet=1 selected=false +++++++++treeItem name='treeitem 1 of 2, level 2' hierarchicalLevel=2 setSize=2 posInSet=1 selected=false isLineBreakingObject=true ++++++++++staticText name='treeitem 1 of 2, level 2' ++++++++++++inlineTextBox name='treeitem 1 of 2, level 2' -++++++++treeItem name='treeitem 1 of 1, level 3' hierarchicalLevel=3 setSize=1 posInSet=1 selected=false +++++++++treeItem name='treeitem 1 of 1, level 3' hierarchicalLevel=3 setSize=1 posInSet=1 selected=false isLineBreakingObject=true ++++++++++staticText name='treeitem 1 of 1, level 3' ++++++++++++inlineTextBox name='treeitem 1 of 1, level 3' -++++++++treeItem name='treeitem 2 of 2, level 2' hierarchicalLevel=2 setSize=2 posInSet=2 selected=false +++++++++treeItem name='treeitem 2 of 2, level 2' hierarchicalLevel=2 setSize=2 posInSet=2 selected=false isLineBreakingObject=true ++++++++++staticText name='treeitem 2 of 2, level 2' ++++++++++++inlineTextBox name='treeitem 2 of 2, level 2' +++++++tree setSize=3 isLineBreakingObject=true +++++++++staticText name='schedule' +++++++++++inlineTextBox name='schedule' +++++++++treeItem name='wake up' hierarchicalLevel=1 setSize=3 posInSet=1 selected=false isLineBreakingObject=true +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"wake up" +++++++++++++++inlineTextBox name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"wake up" +++++++++++staticText name='wake up' previousOnLineId=inlineTextBox:"%E2%80%A2 " +++++++++++++inlineTextBox name='wake up' previousOnLineId=inlineTextBox:"%E2%80%A2 " +++++++++treeItem name='drink coffee' hierarchicalLevel=1 setSize=3 posInSet=2 selected=false isLineBreakingObject=true +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"drink coffee" +++++++++++++++inlineTextBox name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"drink coffee" +++++++++++staticText name='drink coffee' previousOnLineId=inlineTextBox:"%E2%80%A2 " +++++++++++++inlineTextBox name='drink coffee' previousOnLineId=inlineTextBox:"%E2%80%A2 " +++++++++++tree setSize=2 isLineBreakingObject=true +++++++++++++staticText name='tasks' +++++++++++++++inlineTextBox name='tasks' +++++++++++++treeItem name='meeting' hierarchicalLevel=2 setSize=2 posInSet=1 selected=false isLineBreakingObject=true +++++++++++++++none ignored +++++++++++++++++staticText name='%E2%97%A6 ' nextOnLineId=inlineTextBox:"meeting" +++++++++++++++++++inlineTextBox name='%E2%97%A6 ' nextOnLineId=inlineTextBox:"meeting" +++++++++++++++staticText name='meeting' previousOnLineId=inlineTextBox:"%E2%97%A6 " +++++++++++++++++inlineTextBox name='meeting' previousOnLineId=inlineTextBox:"%E2%97%A6 " +++++++++++++treeItem name='lunch' hierarchicalLevel=2 setSize=2 posInSet=2 selected=false isLineBreakingObject=true +++++++++++++++none ignored +++++++++++++++++staticText name='%E2%97%A6 ' nextOnLineId=inlineTextBox:"lunch" +++++++++++++++++++inlineTextBox name='%E2%97%A6 ' nextOnLineId=inlineTextBox:"lunch" +++++++++++++++staticText name='lunch' previousOnLineId=inlineTextBox:"%E2%97%A6 " +++++++++++++++++inlineTextBox name='lunch' previousOnLineId=inlineTextBox:"%E2%97%A6 " +++++++++treeItem name='cook dinner' hierarchicalLevel=1 setSize=3 posInSet=3 selected=false isLineBreakingObject=true +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"cook dinner" +++++++++++++++inlineTextBox name='%E2%80%A2 ' nextOnLineId=inlineTextBox:"cook dinner" +++++++++++staticText name='cook dinner' previousOnLineId=inlineTextBox:"%E2%80%A2 " +++++++++++++inlineTextBox name='cook dinner' previousOnLineId=inlineTextBox:"%E2%80%A2 "
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-mac.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-mac.txt index d640a7d9..945877b 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-mac.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-mac.txt
@@ -10,3 +10,22 @@ ++++++AXStaticText AXRoleDescription='text' AXValue='treeitem 1 of 1, level 3' ++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=2 AXARIASetSize=2 AXIndex=4 AXRoleDescription='outline row' AXTitle='treeitem 2 of 2, level 2' ++++++AXStaticText AXRoleDescription='text' AXValue='treeitem 2 of 2, level 2' +++AXOutline AXARIASetSize=3 AXRoleDescription='outline' +++++AXStaticText AXRoleDescription='text' AXValue='schedule' +++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=1 AXARIASetSize=3 AXIndex=0 AXRoleDescription='outline row' AXTitle='wake up' +++++++AXStaticText AXRoleDescription='text' AXValue='%E2%80%A2 ' +++++++AXStaticText AXRoleDescription='text' AXValue='wake up' +++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=2 AXARIASetSize=3 AXIndex=1 AXRoleDescription='outline row' AXTitle='drink coffee' +++++++AXStaticText AXRoleDescription='text' AXValue='%E2%80%A2 ' +++++++AXStaticText AXRoleDescription='text' AXValue='drink coffee' +++++++AXOutline AXARIASetSize=2 AXRoleDescription='outline' +++++++++AXStaticText AXRoleDescription='text' AXValue='tasks' +++++++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=1 AXARIASetSize=2 AXIndex=0 AXRoleDescription='outline row' AXTitle='meeting' +++++++++++AXStaticText AXRoleDescription='text' AXValue='%E2%97%A6 ' +++++++++++AXStaticText AXRoleDescription='text' AXValue='meeting' +++++++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=2 AXARIASetSize=2 AXIndex=1 AXRoleDescription='outline row' AXTitle='lunch' +++++++++++AXStaticText AXRoleDescription='text' AXValue='%E2%97%A6 ' +++++++++++AXStaticText AXRoleDescription='text' AXValue='lunch' +++++AXRow AXSubrole=AXOutlineRow AXARIAPosInSet=3 AXARIASetSize=3 AXIndex=4 AXRoleDescription='outline row' AXTitle='cook dinner' +++++++AXStaticText AXRoleDescription='text' AXValue='%E2%80%A2 ' +++++++AXStaticText AXRoleDescription='text' AXValue='cook dinner' \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-uia-win.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-uia-win.txt new file mode 100644 index 0000000..adef7c6 --- /dev/null +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-uia-win.txt
@@ -0,0 +1,31 @@ +Document +++Tree Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++TreeItem Name='treeitem 2 of 5, level 1' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='treeitem 2 of 5, level 1' IsControlElement=false +++++TreeItem Name='treeitem 3 of 5, level 1' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='treeitem 3 of 5, level 1' IsControlElement=false +++++TreeItem Name='treeitem 1 of 2, level 2' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='treeitem 1 of 2, level 2' IsControlElement=false +++++TreeItem Name='treeitem 1 of 1, level 3' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='treeitem 1 of 1, level 3' IsControlElement=false +++++TreeItem Name='treeitem 2 of 2, level 2' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='treeitem 2 of 2, level 2' IsControlElement=false +++Tree Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++Text Name='schedule' +++++TreeItem Name='wake up' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='%E2%80%A2 ' IsControlElement=false +++++++Text Name='wake up' IsControlElement=false +++++TreeItem Name='drink coffee' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='%E2%80%A2 ' IsControlElement=false +++++++Text Name='drink coffee' IsControlElement=false +++++++Tree Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++++++Text Name='tasks' IsControlElement=false +++++++++TreeItem Name='meeting' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++++++Text Name='%E2%97%A6 ' IsControlElement=false +++++++++++Text Name='meeting' IsControlElement=false +++++++++TreeItem Name='lunch' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++++++Text Name='%E2%97%A6 ' IsControlElement=false +++++++++++Text Name='lunch' IsControlElement=false +++++TreeItem Name='cook dinner' ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false +++++++Text Name='%E2%80%A2 ' IsControlElement=false +++++++Text Name='cook dinner' IsControlElement=false
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-win.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-win.txt index 0c13de1..3243af42 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-win.txt +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-win.txt
@@ -9,4 +9,23 @@ ++++ROLE_SYSTEM_OUTLINEITEM name='treeitem 1 of 1, level 3' xml-roles:treeitem level:3 setsize:1 posinset:1 ++++++ROLE_SYSTEM_STATICTEXT name='treeitem 1 of 1, level 3' ++++ROLE_SYSTEM_OUTLINEITEM name='treeitem 2 of 2, level 2' xml-roles:treeitem level:2 setsize:2 posinset:2 -++++++ROLE_SYSTEM_STATICTEXT name='treeitem 2 of 2, level 2' \ No newline at end of file +++++++ROLE_SYSTEM_STATICTEXT name='treeitem 2 of 2, level 2' +++ROLE_SYSTEM_OUTLINE xml-roles:tree setsize:3 +++++ROLE_SYSTEM_STATICTEXT name='schedule' +++++ROLE_SYSTEM_OUTLINEITEM name='wake up' xml-roles:treeitem level:1 setsize:3 posinset:1 +++++++ROLE_SYSTEM_STATICTEXT name='%E2%80%A2 ' +++++++ROLE_SYSTEM_STATICTEXT name='wake up' +++++ROLE_SYSTEM_OUTLINEITEM name='drink coffee' xml-roles:treeitem level:1 setsize:3 posinset:2 +++++++ROLE_SYSTEM_STATICTEXT name='%E2%80%A2 ' +++++++ROLE_SYSTEM_STATICTEXT name='drink coffee' +++++++ROLE_SYSTEM_OUTLINE xml-roles:tree setsize:2 +++++++++ROLE_SYSTEM_STATICTEXT name='tasks' +++++++++ROLE_SYSTEM_OUTLINEITEM name='meeting' xml-roles:treeitem level:2 setsize:2 posinset:1 +++++++++++ROLE_SYSTEM_STATICTEXT name='%E2%97%A6 ' +++++++++++ROLE_SYSTEM_STATICTEXT name='meeting' +++++++++ROLE_SYSTEM_OUTLINEITEM name='lunch' xml-roles:treeitem level:2 setsize:2 posinset:2 +++++++++++ROLE_SYSTEM_STATICTEXT name='%E2%97%A6 ' +++++++++++ROLE_SYSTEM_STATICTEXT name='lunch' +++++ROLE_SYSTEM_OUTLINEITEM name='cook dinner' xml-roles:treeitem level:1 setsize:3 posinset:3 +++++++ROLE_SYSTEM_STATICTEXT name='%E2%80%A2 ' +++++++ROLE_SYSTEM_STATICTEXT name='cook dinner' \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists.html b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists.html index 38f77c6..143adb7 100644 --- a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists.html +++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists.html
@@ -20,11 +20,21 @@ @BLINK-ALLOW:posInSet* @BLINK-DENY:setSize=0 @BLINK-DENY:posInSet=0 +@BLINK-ALLOW:next* +@BLINK-ALLOW:prev* +@BLINK-ALLOW:isLineBreakingObject=true --> <!DOCTYPE html> <html> +<head> +<style> + .no-bullets { + list-style-type: none; + } +</style> +</head> <body> - <ul role="tree"> + <ul role="tree" class="no-bullets"> <li> <div role="treeitem" aria-posinset="2" aria-setsize="5">treeitem 2 of 5, level 1</div> </li> @@ -40,12 +50,16 @@ <li> <div role="treeitem" aria-level="2">treeitem 2 of 2, level 2</div> </li> - </ul> + </ul> - <style> - li { - list-style-type: none; - } - </style> + <ul role="tree">schedule + <li role="treeitem">wake up + <li role="treeitem">drink coffee + <ul role="tree">tasks + <li role="treeitem" aria-level='2'>meeting + <li role="treeitem" aria-level='2'>lunch + </ul> + <li role="treeitem">cook dinner + </ul> </body> </html>
diff --git a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt index 5f95011..dfe3c5aa 100644 --- a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt +++ b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt
@@ -4,5 +4,7 @@ ++++++genericContainer ++++++++genericContainer ignored invisible ++++++++++staticText ignored invisible name='aria-hidden' -++++++++genericContainer -++++++++++staticText name='Not hidden' +++++++++genericContainer ignored +++++++++++staticText name='Unsemantic' +++++++++heading name='Semantic' +++++++++++staticText name='Semantic'
diff --git a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html index 4c5bfefa6..4e3fc7d 100644 --- a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html +++ b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html
@@ -14,7 +14,7 @@ </head> <body> <div class="push-content-down" role="none"></div> -<div class="auto"><div aria-hidden="true">aria-hidden</div><div>Not hidden</div></div> +<div class="auto"><div aria-hidden="true">aria-hidden</div><div>Unsemantic</div><h1>Semantic</h1></div> <script> function runTest() { document.title = 'Done';
diff --git a/content/test/data/accessibility/display-locking/activatable-expected-auralinux.txt b/content/test/data/accessibility/display-locking/activatable-expected-auralinux.txt index 7385724..e8fb96d4 100644 --- a/content/test/data/accessibility/display-locking/activatable-expected-auralinux.txt +++ b/content/test/data/accessibility/display-locking/activatable-expected-auralinux.txt
@@ -2,12 +2,11 @@ ++[section] ++[section] ++++[static] name='<newline> ' -++++[section] -++++++[static] name='child' +++++[static] name='child' ++++[static] name='<newline> ' ++++[section] ++++++[static] name='nested locked element!' ++++[static] name='<newline> ' ++++[section] ++++++[static] name='nested non activatable locked element' -++++[static] name='<newline> ' +++++[static] name='<newline> ' \ No newline at end of file
diff --git a/content/test/data/accessibility/display-locking/activatable-expected-blink.txt b/content/test/data/accessibility/display-locking/activatable-expected-blink.txt index 7a25ce8..fb2333c 100644 --- a/content/test/data/accessibility/display-locking/activatable-expected-blink.txt +++ b/content/test/data/accessibility/display-locking/activatable-expected-blink.txt
@@ -5,7 +5,7 @@ ++++++++genericContainer ++++++++genericContainer offscreen ++++++++++staticText offscreen name='<newline> ' -++++++++++genericContainer offscreen +++++++++++genericContainer ignored offscreen ++++++++++++staticText offscreen name='child' ++++++++++staticText offscreen name='<newline> ' ++++++++++genericContainer offscreen
diff --git a/content/test/data/accessibility/display-locking/activatable-expected-mac.txt b/content/test/data/accessibility/display-locking/activatable-expected-mac.txt index cefb15b..2cdb544 100644 --- a/content/test/data/accessibility/display-locking/activatable-expected-mac.txt +++ b/content/test/data/accessibility/display-locking/activatable-expected-mac.txt
@@ -2,12 +2,11 @@ ++AXGroup ++AXGroup ++++AXStaticText AXValue='<newline> ' -++++AXGroup -++++++AXStaticText AXValue='child' +++++AXStaticText AXValue='child' ++++AXStaticText AXValue='<newline> ' ++++AXGroup ++++++AXStaticText AXValue='nested locked element!' ++++AXStaticText AXValue='<newline> ' ++++AXGroup ++++++AXStaticText AXValue='nested non activatable locked element' -++++AXStaticText AXValue='<newline> ' +++++AXStaticText AXValue='<newline> ' \ No newline at end of file
diff --git a/content/test/data/accessibility/display-locking/activatable-expected-win.txt b/content/test/data/accessibility/display-locking/activatable-expected-win.txt index d6462e1..7eb9ee52 100644 --- a/content/test/data/accessibility/display-locking/activatable-expected-win.txt +++ b/content/test/data/accessibility/display-locking/activatable-expected-win.txt
@@ -2,12 +2,11 @@ ++IA2_ROLE_SECTION ++IA2_ROLE_SECTION ++++ROLE_SYSTEM_STATICTEXT name='<newline> ' -++++IA2_ROLE_SECTION -++++++ROLE_SYSTEM_STATICTEXT name='child' +++++ROLE_SYSTEM_STATICTEXT name='child' ++++ROLE_SYSTEM_STATICTEXT name='<newline> ' ++++IA2_ROLE_SECTION ++++++ROLE_SYSTEM_STATICTEXT name='nested locked element!' ++++ROLE_SYSTEM_STATICTEXT name='<newline> ' ++++IA2_ROLE_SECTION ++++++ROLE_SYSTEM_STATICTEXT name='nested non activatable locked element' -++++ROLE_SYSTEM_STATICTEXT name='<newline> ' +++++ROLE_SYSTEM_STATICTEXT name='<newline> ' \ No newline at end of file
diff --git a/content/test/data/accessibility/display-locking/all-expected-auralinux.txt b/content/test/data/accessibility/display-locking/all-expected-auralinux.txt index 6437251..f5ae82e 100644 --- a/content/test/data/accessibility/display-locking/all-expected-auralinux.txt +++ b/content/test/data/accessibility/display-locking/all-expected-auralinux.txt
@@ -3,8 +3,7 @@ ++++[static] name='spacer so that everything below will be offscreen (and won't get viewport-activated)' visible ++[section] visible ++++[static] name='<newline> ' visible -++++[section] visible -++++++[static] name='child text will be in AX tree but without layout' visible +++++[static] name='child text will be in AX tree but without layout' visible ++++[static] name='<newline> ' visible ++++[section] visible ++++++[static] name='<newline> nested activatable locked element will be in AX tree but without layout<newline> ' visible @@ -13,4 +12,4 @@ ++[section] visible ++[static] name='normal text 2' visible ++[section] visible -++[static] name='normal text 3' visible +++[static] name='normal text 3' visible \ No newline at end of file
diff --git a/content/test/data/accessibility/display-locking/all-expected-blink.txt b/content/test/data/accessibility/display-locking/all-expected-blink.txt index 1598c7d..cbc21ee 100644 --- a/content/test/data/accessibility/display-locking/all-expected-blink.txt +++ b/content/test/data/accessibility/display-locking/all-expected-blink.txt
@@ -7,7 +7,7 @@ ++++++++++++inlineTextBox name='spacer so that everything below will be offscreen (and won't get viewport-activated)' ++++++++genericContainer offscreen ++++++++++staticText offscreen name='<newline> ' -++++++++++genericContainer offscreen +++++++++++genericContainer ignored offscreen ++++++++++++staticText offscreen name='child text will be in AX tree but without layout' ++++++++++staticText offscreen name='<newline> ' ++++++++++genericContainer offscreen
diff --git a/content/test/data/accessibility/display-locking/all-expected-mac.txt b/content/test/data/accessibility/display-locking/all-expected-mac.txt index aa332dd..2f2a52e1 100644 --- a/content/test/data/accessibility/display-locking/all-expected-mac.txt +++ b/content/test/data/accessibility/display-locking/all-expected-mac.txt
@@ -3,8 +3,7 @@ ++++AXStaticText AXValue='spacer so that everything below will be offscreen (and won't get viewport-activated)' ++AXGroup ++++AXStaticText AXValue='<newline> ' -++++AXGroup -++++++AXStaticText AXValue='child text will be in AX tree but without layout' +++++AXStaticText AXValue='child text will be in AX tree but without layout' ++++AXStaticText AXValue='<newline> ' ++++AXGroup ++++++AXStaticText AXValue='<newline> nested activatable locked element will be in AX tree but without layout<newline> ' @@ -13,4 +12,4 @@ ++AXGroup ++AXStaticText AXValue='normal text 2' ++AXGroup -++AXStaticText AXValue='normal text 3' +++AXStaticText AXValue='normal text 3' \ No newline at end of file
diff --git a/content/test/data/accessibility/display-locking/all-expected-win.txt b/content/test/data/accessibility/display-locking/all-expected-win.txt index afc58e0..12c77f15 100644 --- a/content/test/data/accessibility/display-locking/all-expected-win.txt +++ b/content/test/data/accessibility/display-locking/all-expected-win.txt
@@ -3,8 +3,7 @@ ++++ROLE_SYSTEM_STATICTEXT name='spacer so that everything below will be offscreen (and won't get viewport-activated)' ++IA2_ROLE_SECTION ++++ROLE_SYSTEM_STATICTEXT name='<newline> ' -++++IA2_ROLE_SECTION -++++++ROLE_SYSTEM_STATICTEXT name='child text will be in AX tree but without layout' +++++ROLE_SYSTEM_STATICTEXT name='child text will be in AX tree but without layout' ++++ROLE_SYSTEM_STATICTEXT name='<newline> ' ++++IA2_ROLE_SECTION ++++++ROLE_SYSTEM_STATICTEXT name='<newline> nested activatable locked element will be in AX tree but without layout<newline> ' @@ -13,4 +12,4 @@ ++IA2_ROLE_SECTION ++ROLE_SYSTEM_STATICTEXT name='normal text 2' ++IA2_ROLE_SECTION -++ROLE_SYSTEM_STATICTEXT name='normal text 3' +++ROLE_SYSTEM_STATICTEXT name='normal text 3' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-android-external.txt b/content/test/data/accessibility/html/area-expected-android-external.txt index 3f987614..21f26bb 100644 --- a/content/test/data/accessibility/html/area-expected-android-external.txt +++ b/content/test/data/accessibility/html/area-expected-android-external.txt
@@ -2,4 +2,3 @@ ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer", hasImage="true"] ++++Image text:"pipe" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="image", hasImage="true", roleDescription="graphic", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/html/pipe.jpg"] ++++++View text:"null" contentDescription:"pipe1" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/html/fake.htm"] -++++++TextView text:"pipe2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText", clickableScore="200"] \ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-auralinux.txt b/content/test/data/accessibility/html/area-expected-auralinux.txt index 9ff7ce9..8e1155f 100644 --- a/content/test/data/accessibility/html/area-expected-auralinux.txt +++ b/content/test/data/accessibility/html/area-expected-auralinux.txt
@@ -1,5 +1,4 @@ [document web] ++[section] ++++[image map] name='pipe' -++++++[link] name='pipe1' -++++++[static] name='pipe2' +++++++[link] name='pipe1' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-blink.txt b/content/test/data/accessibility/html/area-expected-blink.txt index 8316198..1cc29aef 100644 --- a/content/test/data/accessibility/html/area-expected-blink.txt +++ b/content/test/data/accessibility/html/area-expected-blink.txt
@@ -3,4 +3,4 @@ ++++genericContainer ++++++image name='pipe' ++++++++link name='pipe1' -++++++++staticText name='pipe2' +++++++++staticText ignored name='pipe2'
diff --git a/content/test/data/accessibility/html/area-expected-mac.txt b/content/test/data/accessibility/html/area-expected-mac.txt index b778f4c..b6a1482 100644 --- a/content/test/data/accessibility/html/area-expected-mac.txt +++ b/content/test/data/accessibility/html/area-expected-mac.txt
@@ -1,5 +1,4 @@ AXWebArea AXRoleDescription='HTML content' ++AXGroup AXRoleDescription='group' ++++AXGroup AXDescription='pipe' AXRoleDescription='group' -++++++AXLink AXDescription='pipe1' AXRoleDescription='link' -++++++AXStaticText AXRoleDescription='text' AXValue='pipe2' +++++++AXLink AXDescription='pipe1' AXRoleDescription='link' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-uia-win.txt b/content/test/data/accessibility/html/area-expected-uia-win.txt index 435a60028..02590411 100644 --- a/content/test/data/accessibility/html/area-expected-uia-win.txt +++ b/content/test/data/accessibility/html/area-expected-uia-win.txt
@@ -1,5 +1,4 @@ Document ++Group IsControlElement=false ++++Document Name='pipe' -++++++Hyperlink Name='pipe1' -++++++Text Name='pipe2' +++++++Hyperlink Name='pipe1' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-win.txt b/content/test/data/accessibility/html/area-expected-win.txt index 7f58f8ed..3900f98 100644 --- a/content/test/data/accessibility/html/area-expected-win.txt +++ b/content/test/data/accessibility/html/area-expected-win.txt
@@ -1,5 +1,4 @@ ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ++IA2_ROLE_SECTION ++++IA2_ROLE_IMAGE_MAP name='pipe' READONLY -++++++ROLE_SYSTEM_LINK name='pipe1' FOCUSABLE LINKED -++++++ROLE_SYSTEM_STATICTEXT name='pipe2' LINKED +++++++ROLE_SYSTEM_LINK name='pipe1' FOCUSABLE LINKED \ No newline at end of file
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-android-external.txt b/content/test/data/accessibility/html/canvas-fallback-expected-android-external.txt index 36672ba..8256ed0 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-android-external.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-android-external.txt
@@ -7,9 +7,6 @@ ++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] ++++++TextView text:"This is another paragraph in fallback" hint:"Visibility hidden paragraph in fallback content" viewIdResName:"p2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", hint="Visibility hidden paragraph in fallback content"] ++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] -++++++TextView text:"Aria hidden paragraph in fallback content" viewIdResName:"h1" notVisibleToUser actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"] ++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] -++++++TextView text:"Display none text in fallback content " notVisibleToUser actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"] ++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] -++++++TextView text:"Visibility hidden paragraph in fallback content" viewIdResName:"h2" notVisibleToUser actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 2"] -++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] \ No newline at end of file +++++++TextView text:"\n " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-android.txt b/content/test/data/accessibility/html/canvas-fallback-expected-android.txt index d7cc3bb..d417c89 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-android.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-android.txt
@@ -7,9 +7,6 @@ ++++++android.widget.TextView name='<newline> ' ++++++android.widget.TextView name='This is another paragraph in fallback' hint='Visibility hidden paragraph in fallback content' ++++++android.widget.TextView name='<newline> ' -++++++android.widget.TextView role_description='heading 1' heading invisible name='Aria hidden paragraph in fallback content' ++++++android.widget.TextView name='<newline> ' -++++++android.widget.TextView role_description='heading 1' heading invisible name='Display none text in fallback content ' ++++++android.widget.TextView name='<newline> ' -++++++android.widget.TextView role_description='heading 2' heading invisible name='Visibility hidden paragraph in fallback content' -++++++android.widget.TextView name='<newline> ' \ No newline at end of file +++++++android.widget.TextView name='<newline> '
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-auralinux.txt b/content/test/data/accessibility/html/canvas-fallback-expected-auralinux.txt index f171c61..4d0ffb2 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-auralinux.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-auralinux.txt
@@ -4,18 +4,12 @@ ++++++[static] name='Static fallback' ++++[canvas] ++++++[static] name='<newline> ' -++++++[paragraph] description='Aria hidden paragraph in fallback content' described-by +++++++[paragraph] description='Aria hidden paragraph in fallback content' ++++++++[static] name='Line breaking content in a fallback' ++++++[static] name='<newline> ' -++++++[paragraph] description='Visibility hidden paragraph in fallback content' described-by +++++++[paragraph] description='Visibility hidden paragraph in fallback content' ++++++++[static] name='This is another paragraph in fallback' ++++++[static] name='<newline> ' -++++++[heading] description-for -++++++++[static] name='Aria hidden paragraph in fallback content' ++++++[static] name='<newline> ' -++++++[heading] -++++++++[static] name='Display none text in fallback content ' ++++++[static] name='<newline> ' -++++++[heading] description-for -++++++++[static] name='Visibility hidden paragraph in fallback content' -++++++[static] name='<newline> ' +++++++[static] name='<newline> ' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-blink.txt b/content/test/data/accessibility/html/canvas-fallback-expected-blink.txt index 9373113..e7caa453 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-blink.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-blink.txt
@@ -11,12 +11,12 @@ ++++++++paragraph description='Visibility hidden paragraph in fallback content' ++++++++++staticText name='This is another paragraph in fallback' ++++++++staticText name='<newline> ' -++++++++heading invisible hierarchicalLevel=1 -++++++++++staticText invisible name='Aria hidden paragraph in fallback content' +++++++++heading ignored invisible +++++++++++staticText ignored invisible name='Aria hidden paragraph in fallback content' ++++++++staticText name='<newline> ' -++++++++heading invisible hierarchicalLevel=1 -++++++++++staticText invisible name='Display none text in fallback content ' +++++++++heading ignored invisible +++++++++++staticText ignored invisible name='Display none text in fallback content ' ++++++++staticText name='<newline> ' -++++++++heading invisible hierarchicalLevel=2 -++++++++++staticText invisible name='Visibility hidden paragraph in fallback content' -++++++++staticText name='<newline> ' \ No newline at end of file +++++++++heading ignored invisible +++++++++++staticText ignored invisible name='Visibility hidden paragraph in fallback content' +++++++++staticText name='<newline> '
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt b/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt index 7fab11d..022f6c5 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt
@@ -10,12 +10,6 @@ ++++++Group ++++++++Text Name='This is another paragraph in fallback' ++++++Text Name='<newline> ' -++++++Text IsControlElement=false -++++++++Text Name='Aria hidden paragraph in fallback content' IsControlElement=false ++++++Text Name='<newline> ' -++++++Text IsControlElement=false -++++++++Text Name='Display none text in fallback content ' IsControlElement=false ++++++Text Name='<newline> ' -++++++Text IsControlElement=false -++++++++Text Name='Visibility hidden paragraph in fallback content' IsControlElement=false -++++++Text Name='<newline> ' +++++++Text Name='<newline> ' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-win.txt b/content/test/data/accessibility/html/canvas-fallback-expected-win.txt index c032886..50daa2c 100644 --- a/content/test/data/accessibility/html/canvas-fallback-expected-win.txt +++ b/content/test/data/accessibility/html/canvas-fallback-expected-win.txt
@@ -10,12 +10,6 @@ ++++++IA2_ROLE_PARAGRAPH description='Visibility hidden paragraph in fallback content' ++++++++ROLE_SYSTEM_STATICTEXT ++++++ROLE_SYSTEM_STATICTEXT -++++++IA2_ROLE_HEADING INVISIBLE -++++++++ROLE_SYSTEM_STATICTEXT INVISIBLE ++++++ROLE_SYSTEM_STATICTEXT -++++++IA2_ROLE_HEADING INVISIBLE -++++++++ROLE_SYSTEM_STATICTEXT INVISIBLE ++++++ROLE_SYSTEM_STATICTEXT -++++++IA2_ROLE_HEADING INVISIBLE -++++++++ROLE_SYSTEM_STATICTEXT INVISIBLE ++++++ROLE_SYSTEM_STATICTEXT \ No newline at end of file
diff --git a/content/test/data/accessibility/html/inert-attribute-expected-blink.txt b/content/test/data/accessibility/html/inert-attribute-expected-blink.txt index 2d312bd..ba0cc6c 100644 --- a/content/test/data/accessibility/html/inert-attribute-expected-blink.txt +++ b/content/test/data/accessibility/html/inert-attribute-expected-blink.txt
@@ -17,12 +17,12 @@ ++++++genericContainer focusable htmlTag='div' name=' consectetur adipiscing tempor ' ++++++++staticText name='<newline> consectetur<newline> ' ++++++++genericContainer ignored invisible htmlTag='span' -++++++++++staticText invisible name='adipiscing' +++++++++++staticText ignored invisible name='adipiscing' ++++++++staticText name='<newline> ' -++++++++genericContainer htmlTag='div' +++++++++genericContainer ignored htmlTag='div' ++++++++++staticText name='<newline> ' ++++++++++genericContainer ignored invisible htmlTag='span' -++++++++++++staticText invisible name='tempor' +++++++++++++staticText ignored invisible name='tempor' ++++++++++staticText name='<newline> ' ++++++++staticText name='<newline>' ++++++canvas focusable htmlTag='canvas' name=' sed do eiusmod tempor ' @@ -39,11 +39,11 @@ ++++++++++++staticText invisible name='tempor' ++++++++++staticText name='<newline> ' ++++++++staticText name='<newline> ' -++++++++genericContainer invisible htmlTag='div' -++++++++++staticText invisible name='<newline> ' +++++++++genericContainer ignored invisible htmlTag='div' +++++++++++staticText ignored invisible name='<newline> ' ++++++++++genericContainer ignored invisible htmlTag='span' -++++++++++++staticText invisible name='incididunt' -++++++++++staticText invisible name='<newline> ' +++++++++++++staticText ignored invisible name='incididunt' +++++++++++staticText ignored invisible name='<newline> ' ++++++++staticText name='<newline>' ++++++iframe htmlTag='iframe' ++++++++rootWebArea focusable htmlTag='#document' @@ -81,4 +81,4 @@ ++++++++++++++++++genericContainer ignored invisible htmlTag='html' ++++++++++++++++++++genericContainer ignored invisible htmlTag='body' ++++++++++++++++++++++genericContainer ignored invisible htmlTag='div' -++++++++++++++++++++++++staticText ignored invisible name='Sandboxed frame nested in sandboxed inert frame' \ No newline at end of file +++++++++++++++++++++++++staticText ignored invisible name='Sandboxed frame nested in sandboxed inert frame'
diff --git a/content/test/data/accessibility/html/map-any-contents-expected-android-external.txt b/content/test/data/accessibility/html/map-any-contents-expected-android-external.txt index c0df62c..a77a1a9 100644 --- a/content/test/data/accessibility/html/map-any-contents-expected-android-external.txt +++ b/content/test/data/accessibility/html/map-any-contents-expected-android-external.txt
@@ -7,5 +7,4 @@ ++++++++TextView text:"So are " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] ++++++++View text:"other elements" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mark", roleDescription="highlight"] ++++++++TextView text:"!" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"] -++++++TextView text:"pipe2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText", clickableScore="200"] -++++++Button text:"Even a button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"] \ No newline at end of file +++++++Button text:"Even a button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
diff --git a/content/test/data/accessibility/html/map-any-contents-expected-auralinux.txt b/content/test/data/accessibility/html/map-any-contents-expected-auralinux.txt index 59d1f39d..0483293 100644 --- a/content/test/data/accessibility/html/map-any-contents-expected-auralinux.txt +++ b/content/test/data/accessibility/html/map-any-contents-expected-auralinux.txt
@@ -9,5 +9,4 @@ ++++++++[static] ++++++++++[static] name='other elements' ++++++++[static] name='!' -++++++[static] name='pipe2' ++++++[push button] name='Even a button' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/map-any-contents-expected-blink.txt b/content/test/data/accessibility/html/map-any-contents-expected-blink.txt index f88a81d..62dc040 100644 --- a/content/test/data/accessibility/html/map-any-contents-expected-blink.txt +++ b/content/test/data/accessibility/html/map-any-contents-expected-blink.txt
@@ -14,7 +14,7 @@ ++++++++++++++inlineTextBox name='other elements' ++++++++++staticText name='!' ++++++++++++inlineTextBox name='!' -++++++++staticText name='pipe2' +++++++++staticText ignored name='pipe2' ++++++++button name='Even a button' ++++++++++staticText name='Even a button' -++++++++++++inlineTextBox name='Even a button' \ No newline at end of file +++++++++++++inlineTextBox name='Even a button'
diff --git a/content/test/data/direct_sockets/udp.js b/content/test/data/direct_sockets/udp.js index a2cbb669..a01cd991 100644 --- a/content/test/data/direct_sockets/udp.js +++ b/content/test/data/direct_sockets/udp.js
@@ -6,10 +6,37 @@ } }; -async function sendLoop(writer, requiredBytes) { +async function launchUdpEchoServer(server, requiredBytes, clientAddress, clientPort) { + let bytesEchoed = 0; + + const { readable, writable } = await server.opened; + const reader = readable.getReader(); + const writer = writable.getWriter(); + + while (bytesEchoed < requiredBytes) { + const { value: { data, remoteAddress, remotePort }, done } = await reader.read(); + assertEq(done, false); + assertEq(remoteAddress, clientAddress); + assertEq(remotePort, clientPort); + for (let index = 0; index < data.length; index++) { + assertEq(data[index], bytesEchoed % 256); + bytesEchoed++; + } + await writer.write({ data, remoteAddress, remotePort }); + } + + assertEq(bytesEchoed, requiredBytes); + reader.releaseLock(); + writer.releaseLock(); +} + +async function sendLoop(socket, requiredBytes) { let bytesWritten = 0; let chunkLength = 0; + const { writable } = await socket.opened; + const writer = writable.getWriter(); + while (bytesWritten < requiredBytes) { chunkLength = Math.min(chunkLength + 1, requiredBytes - bytesWritten); @@ -21,41 +48,28 @@ await writer.ready; await writer.write({ data: chunk }); } - return 'send succeeded'; + assertEq(bytesWritten, requiredBytes); + + writer.releaseLock(); } -async function readLoop(reader, requiredBytes) { +async function readLoop(socket, requiredBytes) { let bytesRead = 0; + + const { readable } = await socket.opened; + const reader = readable.getReader(); + while (bytesRead < requiredBytes) { - const { value, done } = await reader.read(); - if (done) { - return 'readLoop failed: stream closed prematurely.'; - } - - const { data } = value; - if (!data || data.length === 0) { - return 'readLoop failed: no data returned.'; - } - + const { value: { data }, done } = await reader.read(); + assertEq(done, false); for (let index = 0; index < data.length; index++) { - if (data[index] != bytesRead % 256) { - console.log(`Expected ${bytesRead % 256}, received ${data[index]}`); - return 'readLoop failed: bad data.'; - } + assertEq(data[index], bytesRead % 256); bytesRead++; } } - return 'readLoop succeeded.'; -} + assertEq(bytesRead, requiredBytes); -async function sendUdp(options, requiredBytes) { - try { - let udpSocket = new UDPSocket(options); - let { writable } = await udpSocket.opened; - return await sendLoop(writable.getWriter(), requiredBytes); - } catch (error) { - return ('sendUdp failed: ' + error); - } + reader.releaseLock(); } async function closeUdp(options) { @@ -75,8 +89,7 @@ let { writable } = await udpSocket.opened; await udpSocket.close(); - const writer = writable.getWriter(); - return await sendLoop(writer, requiredBytes); + return await sendLoop(udpSocket, requiredBytes); } catch (error) { return ('send failed: ' + error); } @@ -169,8 +182,10 @@ } } -async function exchangeSingleUdpPacketBetweenClientAndServer() { - const kPacket = "I'm a UDP packet. Meow-meow!"; +async function exchangeUdpPacketsBetweenClientAndServer() { + const kRequiredDatagrams = 35; + const kRequiredBytes = + kRequiredDatagrams * (kRequiredDatagrams + 1) / 2; try { // |localPort| is intentionally omitted so that the OS will pick one itself. @@ -182,96 +197,18 @@ remoteAddress: "127.0.0.1", remotePort: serverSocketPort }); + const { localAddress: clientLocalAddress, localPort: clientLocalPort } = await clientSocket.opened; - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - - // Waits for a packet to arrive and then echoes it back to - // the original sender. - async function serverEcho() { - const { - readable: serverReadable, - writable: serverWritable, - } = await serverSocket.opened; - - const reader = serverReadable.getReader(); - const writer = serverWritable.getWriter(); - - const { value: { - data, - remoteAddress: packetRemoteAddress, - remotePort: packetRemotePort - }, done } = await reader.read(); - - // Stream shouldn't be exhausted yet. - assertEq(done, false); - - // Data should match. - assertEq(kPacket, decoder.decode(data)); - - const { - localAddress: clientLocalAddr, - localPort: clientLocalPort - } = await clientSocket.opened; - - // Check that the packet arrived from the client. - assertEq(clientLocalAddr, packetRemoteAddress); - assertEq(clientLocalPort, packetRemotePort); - - // Echo back. - await writer.ready; - writer.write({ - data, - remoteAddress: clientLocalAddr, - remotePort: clientLocalPort - }); - - reader.releaseLock(); - writer.releaseLock(); - } - - serverEcho(); - - // Sends the initial packet from client to server. - async function clientSend() { - const { writable: clientWritable } = await clientSocket.opened; - const writer = clientWritable.getWriter(); - writer.write({ data: encoder.encode(kPacket) }); - writer.releaseLock(); - } - - clientSend(); - - // Waits for the server to respond and verifies that the packet received - // is indeed the original one. - async function clientReceive() { - const { readable: clientReadable } = await clientSocket.opened; - const reader = clientReadable.getReader(); - - const { value: { - data, remoteAddress, remotePort - }, done } = await reader.read(); - reader.releaseLock(); - - // Stream shouldn't be exhausted yet. - assertEq(done, false); - - // Data should match. - assertEq(kPacket, decoder.decode(data)); - - // In connected mode remoteAddress/remotePort should be undefined. - assertEq(remoteAddress, undefined); - assertEq(remotePort, undefined); - } - - await clientReceive(); + launchUdpEchoServer(serverSocket, kRequiredBytes, clientLocalAddress, clientLocalPort); + sendLoop(clientSocket, kRequiredBytes); + await readLoop(clientSocket, kRequiredBytes); await clientSocket.close(); await serverSocket.close(); - return "exchangeSingleUdpPacketBetweenClientAndServer succeeded."; + return "exchangeUdpPacketsBetweenClientAndServer succeeded."; } catch (error) { - return "exchangeSingleUdpPacketBetweenClientAndServer failed: " + error; + return "exchangeUdpPacketsBetweenClientAndServer failed: " + error; } }
diff --git a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js index 6c189579..9c7c7fe 100644 --- a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js +++ b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
@@ -38,7 +38,7 @@ } function validateAuctionConfig(auctionConfig) { - if (Object.keys(auctionConfig).length !== 10) { + if (Object.keys(auctionConfig).length !== 11) { throw 'Wrong number of auctionConfig fields ' + JSON.stringify(auctionConfig); } @@ -87,6 +87,11 @@ JSON.stringify(auctionConfig.perBuyerTimeouts); } + if (auctionConfig.perBuyerCumulativeTimeouts[buyerOrigin] !== 201) { + throw 'Wrong perBuyerCumulativeTimeouts ' + + JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts); + } + const perBuyerPrioritySignals = auctionConfig.perBuyerPrioritySignals; if (Object.keys(perBuyerPrioritySignals).length !== 2 || JSON.stringify(perBuyerPrioritySignals[buyerOrigin]) !==
diff --git a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js index 46fd97ae..0dc46876 100644 --- a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js +++ b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
@@ -39,7 +39,7 @@ } function validateAuctionConfig(auctionConfig) { - if (Object.keys(auctionConfig).length !== 10) { + if (Object.keys(auctionConfig).length !== 11) { throw 'Wrong number of auctionConfig fields ' + JSON.stringify(auctionConfig); } @@ -87,6 +87,14 @@ throw 'Wrong perBuyerTimeouts ' + perBuyerTimeoutsJson; } + const perBuyerCumulativeTimeoutsJson = + JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts); + if (!perBuyerCumulativeTimeoutsJson.includes('a.test') || + !perBuyerCumulativeTimeoutsJson.includes('111') || + auctionConfig.perBuyerCumulativeTimeouts['*'] != 151) { + throw 'Wrong perBuyerCumulativeTimeouts ' + perBuyerCumulativeTimeoutsJson; + } + const perBuyerPrioritySignalsJson = JSON.stringify(auctionConfig.perBuyerPrioritySignals); if (Object.keys(auctionConfig.perBuyerPrioritySignals).length !== 1 ||
diff --git a/content/test/data/interest_group/decision_argument_validator.js b/content/test/data/interest_group/decision_argument_validator.js index 852775a..bd8f3a82 100644 --- a/content/test/data/interest_group/decision_argument_validator.js +++ b/content/test/data/interest_group/decision_argument_validator.js
@@ -27,7 +27,7 @@ } function validateAuctionConfig(auctionConfig) { - if (Object.keys(auctionConfig).length !== 10) { + if (Object.keys(auctionConfig).length !== 11) { throw 'Wrong number of auctionConfig fields ' + JSON.stringify(auctionConfig); } @@ -87,6 +87,13 @@ JSON.stringify(auctionConfig.perBuyerTimeouts); } + if (auctionConfig.perBuyerCumulativeTimeouts[buyerAOrigin] !== 130 || + auctionConfig.perBuyerCumulativeTimeouts[buyerBOrigin] !== 140 || + auctionConfig.perBuyerCumulativeTimeouts['*'] !== 160) { + throw 'Wrong perBuyerCumulativeTimeouts ' + + JSON.stringify(auctionConfig.perBuyerCumulativeTimeouts); + } + const perBuyerPrioritySignals = auctionConfig.perBuyerPrioritySignals; if (Object.keys(perBuyerPrioritySignals).length !== 2 || JSON.stringify(perBuyerPrioritySignals[buyerAOrigin]) !==
diff --git a/content/test/test_select_url_fenced_frame_config_observer_impl.cc b/content/test/test_select_url_fenced_frame_config_observer_impl.cc new file mode 100644 index 0000000..e80571f --- /dev/null +++ b/content/test/test_select_url_fenced_frame_config_observer_impl.cc
@@ -0,0 +1,96 @@ +// 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. + +#include "content/test/test_select_url_fenced_frame_config_observer_impl.h" + +#include "content/public/browser/storage_partition.h" +#include "content/public/test/shared_storage_test_utils.h" +#include "content/public/test/test_select_url_fenced_frame_config_observer.h" +#include "url/gurl.h" + +namespace content { + +TestSelectURLFencedFrameConfigObserverImpl:: + TestSelectURLFencedFrameConfigObserverImpl() = default; +TestSelectURLFencedFrameConfigObserverImpl:: + ~TestSelectURLFencedFrameConfigObserverImpl() = default; + +void TestSelectURLFencedFrameConfigObserverImpl::OnSharedStorageAccessed( + const base::Time& access_time, + AccessType type, + const std::string& main_frame_id, + const std::string& owner_origin, + const SharedStorageEventParams& params) {} + +void TestSelectURLFencedFrameConfigObserverImpl::OnUrnUuidGenerated( + const GURL& urn_uuid) { + if (urn_uuid_.has_value()) { + // This observer has already observed an urn::uuid. + return; + } + urn_uuid_ = urn_uuid; +} + +void TestSelectURLFencedFrameConfigObserverImpl::OnConfigPopulated( + const absl::optional<FencedFrameConfig>& config) { + if (config_observed_ || !urn_uuid_.has_value() || !config.has_value() || + (urn_uuid_.value() != config->urn_uuid_)) { + // 1. This observer has already observed a config. + // 2. This observer hasn't observed an urn::uuid yet. + // 3. The given config is `absl::nullopt`. + // 4. The given config does not correspond to the observed urn::uuid. + return; + } + config_observed_ = true; + config_ = config; +} + +const absl::optional<GURL>& +TestSelectURLFencedFrameConfigObserverImpl::GetUrnUuid() const { + return urn_uuid_; +} + +const absl::optional<FencedFrameConfig>& +TestSelectURLFencedFrameConfigObserverImpl::GetConfig() const { + return config_; +} + +bool TestSelectURLFencedFrameConfigObserverImpl::ConfigObserved() const { + return config_observed_; +} + +TestSelectURLFencedFrameConfigObserver::TestSelectURLFencedFrameConfigObserver( + StoragePartition* storage_partition) + : storage_partition_(storage_partition), + impl_(std::make_unique<TestSelectURLFencedFrameConfigObserverImpl>()) { + SharedStorageWorkletHostManager* manager = + GetSharedStorageWorkletHostManagerForStoragePartition(storage_partition_); + DCHECK(manager); + + manager->AddSharedStorageObserver(impl_.get()); +} + +TestSelectURLFencedFrameConfigObserver:: + ~TestSelectURLFencedFrameConfigObserver() { + SharedStorageWorkletHostManager* manager = + GetSharedStorageWorkletHostManagerForStoragePartition(storage_partition_); + + manager->RemoveSharedStorageObserver(impl_.get()); +} + +const absl::optional<GURL>& TestSelectURLFencedFrameConfigObserver::GetUrnUuid() + const { + return impl_->GetUrnUuid(); +} + +const absl::optional<FencedFrameConfig>& +TestSelectURLFencedFrameConfigObserver::GetConfig() const { + return impl_->GetConfig(); +} + +bool TestSelectURLFencedFrameConfigObserver::ConfigObserved() const { + return impl_->ConfigObserved(); +} + +} // namespace content \ No newline at end of file
diff --git a/content/test/test_select_url_fenced_frame_config_observer_impl.h b/content/test/test_select_url_fenced_frame_config_observer_impl.h new file mode 100644 index 0000000..7f46a9f --- /dev/null +++ b/content/test/test_select_url_fenced_frame_config_observer_impl.h
@@ -0,0 +1,42 @@ +// 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. + +#ifndef CONTENT_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_IMPL_H_ +#define CONTENT_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_IMPL_H_ + +#include "content/browser/fenced_frame/fenced_frame_config.h" +#include "content/browser/shared_storage/shared_storage_worklet_host_manager.h" + +class GURL; + +namespace content { + +class TestSelectURLFencedFrameConfigObserverImpl + : public SharedStorageWorkletHostManager::SharedStorageObserverInterface { + public: + TestSelectURLFencedFrameConfigObserverImpl(); + ~TestSelectURLFencedFrameConfigObserverImpl() override; + + void OnSharedStorageAccessed(const base::Time& access_time, + AccessType type, + const std::string& main_frame_id, + const std::string& owner_origin, + const SharedStorageEventParams& params) override; + void OnUrnUuidGenerated(const GURL& urn_uuid) override; + void OnConfigPopulated( + const absl::optional<FencedFrameConfig>& config) override; + + const absl::optional<GURL>& GetUrnUuid() const; + const absl::optional<FencedFrameConfig>& GetConfig() const; + bool ConfigObserved() const; + + private: + absl::optional<GURL> urn_uuid_; + absl::optional<FencedFrameConfig> config_; + bool config_observed_ = false; +}; + +} // namespace content + +#endif // CONTENT_TEST_TEST_SELECT_URL_FENCED_FRAME_CONFIG_OBSERVER_IMPL_H_ \ No newline at end of file
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h index 8160380b..07dcf89 100644 --- a/device/fido/fido_request_handler_base.h +++ b/device/fido/fido_request_handler_base.h
@@ -70,6 +70,10 @@ // list. bool has_empty_allow_list = false; + // is_only_hybrid_or_internal is true if credentials in the allow-list only + // contain the hybrid or internal transports. + bool is_only_hybrid_or_internal = false; + // The intersection of transports supported by the client and allowed by the // relying party. base::flat_set<FidoTransportProtocol> available_transports;
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc index a68d6ab..0ddc8df 100644 --- a/device/fido/get_assertion_handler_unittest.cc +++ b/device/fido/get_assertion_handler_unittest.cc
@@ -30,6 +30,7 @@ #include "device/fido/hid/fake_hid_impl_for_testing.h" #include "device/fido/make_credential_task.h" #include "device/fido/mock_fido_device.h" +#include "device/fido/public_key_credential_descriptor.h" #include "device/fido/test_callback_receiver.h" #include "device/fido/u2f_command_constructor.h" #include "device/fido/virtual_fido_device_factory.h" @@ -127,6 +128,19 @@ return handler; } + std::unique_ptr<GetAssertionRequestHandler> + CreateGetAssertionHandlerWithRequestedTransports( + std::vector<std::vector<FidoTransportProtocol>> transports) { + CtapGetAssertionRequest request(test_data::kRelyingPartyId, + test_data::kClientDataJson); + for (uint8_t i = 0; i < transports.size(); ++i) { + request.allow_list.emplace_back(CredentialType::kPublicKey, + std::vector<uint8_t>{i}); + request.allow_list.back().transports = transports[i]; + } + return CreateGetAssertionHandlerWithRequest(std::move(request)); + } + void ExpectAllowedTransportsForRequestAre( GetAssertionRequestHandler* request_handler, base::flat_set<FidoTransportProtocol> transports) { @@ -205,12 +219,73 @@ }; TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) { - auto request_handler = - CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest( - test_data::kRelyingPartyId, test_data::kClientDataJson)); - - EXPECT_EQ(FidoRequestType::kGetAssertion, - request_handler->transport_availability_info().request_type); + { + // Empty allow list. + auto request_handler = CreateGetAssertionHandlerWithRequestedTransports({}); + EXPECT_EQ(FidoRequestType::kGetAssertion, + request_handler->transport_availability_info().request_type); + EXPECT_FALSE(request_handler->transport_availability_info() + .transport_list_did_include_internal); + EXPECT_TRUE( + request_handler->transport_availability_info().has_empty_allow_list); + EXPECT_FALSE(request_handler->transport_availability_info() + .is_only_hybrid_or_internal); + } + { + // Internal and a phone. + auto request_handler = CreateGetAssertionHandlerWithRequestedTransports( + {{FidoTransportProtocol::kInternal}, + {FidoTransportProtocol::kInternal, FidoTransportProtocol::kHybrid}}); + EXPECT_EQ(FidoRequestType::kGetAssertion, + request_handler->transport_availability_info().request_type); + EXPECT_TRUE(request_handler->transport_availability_info() + .transport_list_did_include_internal); + EXPECT_FALSE( + request_handler->transport_availability_info().has_empty_allow_list); + EXPECT_TRUE(request_handler->transport_availability_info() + .is_only_hybrid_or_internal); + } + { + // Internal, a phone, and USB. + auto request_handler = CreateGetAssertionHandlerWithRequestedTransports( + {{FidoTransportProtocol::kUsbHumanInterfaceDevice}, + {FidoTransportProtocol::kInternal}, + {FidoTransportProtocol::kInternal, FidoTransportProtocol::kHybrid}}); + EXPECT_EQ(FidoRequestType::kGetAssertion, + request_handler->transport_availability_info().request_type); + EXPECT_TRUE(request_handler->transport_availability_info() + .transport_list_did_include_internal); + EXPECT_FALSE( + request_handler->transport_availability_info().has_empty_allow_list); + EXPECT_FALSE(request_handler->transport_availability_info() + .is_only_hybrid_or_internal); + } + { + // Only USB. + auto request_handler = CreateGetAssertionHandlerWithRequestedTransports( + {{FidoTransportProtocol::kUsbHumanInterfaceDevice}}); + EXPECT_EQ(FidoRequestType::kGetAssertion, + request_handler->transport_availability_info().request_type); + EXPECT_FALSE(request_handler->transport_availability_info() + .transport_list_did_include_internal); + EXPECT_FALSE( + request_handler->transport_availability_info().has_empty_allow_list); + EXPECT_FALSE(request_handler->transport_availability_info() + .is_only_hybrid_or_internal); + } + { + // A phone and an unknown (empty) transport credential. + auto request_handler = CreateGetAssertionHandlerWithRequestedTransports( + {{}, {FidoTransportProtocol::kHybrid}}); + EXPECT_EQ(FidoRequestType::kGetAssertion, + request_handler->transport_availability_info().request_type); + EXPECT_TRUE(request_handler->transport_availability_info() + .transport_list_did_include_internal); + EXPECT_FALSE( + request_handler->transport_availability_info().has_empty_allow_list); + EXPECT_FALSE(request_handler->transport_availability_info() + .is_only_hybrid_or_internal); + } } TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc index c46f345..fd1d11c 100644 --- a/device/fido/get_assertion_request_handler.cc +++ b/device/fido/get_assertion_request_handler.cc
@@ -27,8 +27,10 @@ #include "device/fido/fido_authenticator.h" #include "device/fido/fido_discovery_factory.h" #include "device/fido/fido_parsing_utils.h" +#include "device/fido/fido_transport_protocol.h" #include "device/fido/filter.h" #include "device/fido/pin.h" +#include "device/fido/public_key_credential_descriptor.h" #if BUILDFLAG(IS_MAC) #include "device/fido/mac/authenticator.h" @@ -326,6 +328,21 @@ return request; } +bool IsOnlyHybridOrInternal(const PublicKeyCredentialDescriptor& credential) { + if (credential.transports.empty()) { + return false; + } + return base::ranges::all_of(credential.transports, [](const auto& transport) { + return transport == FidoTransportProtocol::kHybrid || + transport == FidoTransportProtocol::kInternal; + }); +} + +bool AllowListOnlyHybridOrInternal(const CtapGetAssertionRequest& request) { + return !request.allow_list.empty() && + base::ranges::all_of(request.allow_list, &IsOnlyHybridOrInternal); +} + } // namespace GetAssertionRequestHandler::GetAssertionRequestHandler( @@ -347,6 +364,8 @@ transport_availability_info().request_type = FidoRequestType::kGetAssertion; transport_availability_info().has_empty_allow_list = request_.allow_list.empty(); + transport_availability_info().is_only_hybrid_or_internal = + AllowListOnlyHybridOrInternal(request_); transport_availability_info().is_off_the_record_context = options_.is_off_the_record_context; transport_availability_info().transport_list_did_include_internal =
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc index e0ab89f..bcccf01 100644 --- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc +++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
@@ -123,9 +123,9 @@ } // namespace -BluetoothSocketAsyncApiFunction::BluetoothSocketAsyncApiFunction() {} +BluetoothSocketAsyncApiFunction::BluetoothSocketAsyncApiFunction() = default; -BluetoothSocketAsyncApiFunction::~BluetoothSocketAsyncApiFunction() {} +BluetoothSocketAsyncApiFunction::~BluetoothSocketAsyncApiFunction() = default; bool BluetoothSocketAsyncApiFunction::PreRunValidation(std::string* error) { if (!ExtensionFunction::PreRunValidation(error)) @@ -172,9 +172,9 @@ return manager_->GetResourceIds(extension_id()); } -BluetoothSocketCreateFunction::BluetoothSocketCreateFunction() {} +BluetoothSocketCreateFunction::BluetoothSocketCreateFunction() = default; -BluetoothSocketCreateFunction::~BluetoothSocketCreateFunction() {} +BluetoothSocketCreateFunction::~BluetoothSocketCreateFunction() = default; ExtensionFunction::ResponseAction BluetoothSocketCreateFunction::Run() { DCHECK_CURRENTLY_ON(work_thread_id()); @@ -195,9 +195,9 @@ ArgumentList(bluetooth_socket::Create::Results::Create(create_info))); } -BluetoothSocketUpdateFunction::BluetoothSocketUpdateFunction() {} +BluetoothSocketUpdateFunction::BluetoothSocketUpdateFunction() = default; -BluetoothSocketUpdateFunction::~BluetoothSocketUpdateFunction() {} +BluetoothSocketUpdateFunction::~BluetoothSocketUpdateFunction() = default; ExtensionFunction::ResponseAction BluetoothSocketUpdateFunction::Run() { auto params = bluetooth_socket::Update::Params::Create(args()); @@ -211,9 +211,9 @@ return RespondNow(ArgumentList(bluetooth_socket::Update::Results::Create())); } -BluetoothSocketSetPausedFunction::BluetoothSocketSetPausedFunction() {} +BluetoothSocketSetPausedFunction::BluetoothSocketSetPausedFunction() = default; -BluetoothSocketSetPausedFunction::~BluetoothSocketSetPausedFunction() {} +BluetoothSocketSetPausedFunction::~BluetoothSocketSetPausedFunction() = default; ExtensionFunction::ResponseAction BluetoothSocketSetPausedFunction::Run() { auto params = bluetooth_socket::SetPaused::Params::Create(args()); @@ -240,9 +240,9 @@ ArgumentList(bluetooth_socket::SetPaused::Results::Create())); } -BluetoothSocketListenFunction::BluetoothSocketListenFunction() {} +BluetoothSocketListenFunction::BluetoothSocketListenFunction() = default; -BluetoothSocketListenFunction::~BluetoothSocketListenFunction() {} +BluetoothSocketListenFunction::~BluetoothSocketListenFunction() = default; bool BluetoothSocketListenFunction::PreRunValidation(std::string* error) { if (!BluetoothSocketAsyncApiFunction::PreRunValidation(error)) @@ -489,9 +489,9 @@ Respond(Error(message)); } -BluetoothSocketConnectFunction::BluetoothSocketConnectFunction() {} +BluetoothSocketConnectFunction::BluetoothSocketConnectFunction() = default; -BluetoothSocketConnectFunction::~BluetoothSocketConnectFunction() {} +BluetoothSocketConnectFunction::~BluetoothSocketConnectFunction() = default; void BluetoothSocketConnectFunction::ConnectToService( device::BluetoothDevice* device, @@ -501,9 +501,11 @@ base::BindOnce(&BluetoothSocketConnectFunction::OnConnectError, this)); } -BluetoothSocketDisconnectFunction::BluetoothSocketDisconnectFunction() {} +BluetoothSocketDisconnectFunction::BluetoothSocketDisconnectFunction() = + default; -BluetoothSocketDisconnectFunction::~BluetoothSocketDisconnectFunction() {} +BluetoothSocketDisconnectFunction::~BluetoothSocketDisconnectFunction() = + default; ExtensionFunction::ResponseAction BluetoothSocketDisconnectFunction::Run() { DCHECK_CURRENTLY_ON(work_thread_id()); @@ -525,7 +527,7 @@ Respond(ArgumentList(bluetooth_socket::Disconnect::Results::Create())); } -BluetoothSocketCloseFunction::BluetoothSocketCloseFunction() {} +BluetoothSocketCloseFunction::BluetoothSocketCloseFunction() = default; BluetoothSocketCloseFunction::~BluetoothSocketCloseFunction() = default; @@ -543,7 +545,7 @@ BluetoothSocketSendFunction::BluetoothSocketSendFunction() : io_buffer_size_(0) {} -BluetoothSocketSendFunction::~BluetoothSocketSendFunction() {} +BluetoothSocketSendFunction::~BluetoothSocketSendFunction() = default; ExtensionFunction::ResponseAction BluetoothSocketSendFunction::Run() { DCHECK_CURRENTLY_ON(work_thread_id()); @@ -577,9 +579,9 @@ Respond(Error(message)); } -BluetoothSocketGetInfoFunction::BluetoothSocketGetInfoFunction() {} +BluetoothSocketGetInfoFunction::BluetoothSocketGetInfoFunction() = default; -BluetoothSocketGetInfoFunction::~BluetoothSocketGetInfoFunction() {} +BluetoothSocketGetInfoFunction::~BluetoothSocketGetInfoFunction() = default; ExtensionFunction::ResponseAction BluetoothSocketGetInfoFunction::Run() { auto params = bluetooth_socket::GetInfo::Params::Create(args()); @@ -593,9 +595,11 @@ CreateSocketInfo(params->socket_id, socket)))); } -BluetoothSocketGetSocketsFunction::BluetoothSocketGetSocketsFunction() {} +BluetoothSocketGetSocketsFunction::BluetoothSocketGetSocketsFunction() = + default; -BluetoothSocketGetSocketsFunction::~BluetoothSocketGetSocketsFunction() {} +BluetoothSocketGetSocketsFunction::~BluetoothSocketGetSocketsFunction() = + default; ExtensionFunction::ResponseAction BluetoothSocketGetSocketsFunction::Run() { std::vector<bluetooth_socket::SocketInfo> socket_infos;
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc index fdb6637b..155e6e2 100644 --- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc +++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.cc
@@ -95,14 +95,14 @@ sockets_ = manager->data_; } -BluetoothSocketEventDispatcher::~BluetoothSocketEventDispatcher() {} +BluetoothSocketEventDispatcher::~BluetoothSocketEventDispatcher() = default; -BluetoothSocketEventDispatcher::SocketParams::SocketParams() {} +BluetoothSocketEventDispatcher::SocketParams::SocketParams() = default; BluetoothSocketEventDispatcher::SocketParams::SocketParams( const SocketParams& other) = default; -BluetoothSocketEventDispatcher::SocketParams::~SocketParams() {} +BluetoothSocketEventDispatcher::SocketParams::~SocketParams() = default; void BluetoothSocketEventDispatcher::OnSocketConnect( const std::string& extension_id,
diff --git a/extensions/browser/api/printer_provider/printer_provider_internal_api.cc b/extensions/browser/api/printer_provider/printer_provider_internal_api.cc index 0769f1d..4aedb8e 100644 --- a/extensions/browser/api/printer_provider/printer_provider_internal_api.cc +++ b/extensions/browser/api/printer_provider/printer_provider_internal_api.cc
@@ -50,7 +50,7 @@ PrinterProviderInternalAPI::PrinterProviderInternalAPI( content::BrowserContext* browser_context) {} -PrinterProviderInternalAPI::~PrinterProviderInternalAPI() {} +PrinterProviderInternalAPI::~PrinterProviderInternalAPI() = default; void PrinterProviderInternalAPI::AddObserver( PrinterProviderInternalAPIObserver* observer) {
diff --git a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc index fa32ce1..fe80c65 100644 --- a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc +++ b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
@@ -63,8 +63,8 @@ screen_->display_list().AddDisplay( display::Display(200, gfx::Rect(1920, 1080, 800, 600)), display::DisplayList::Type::NOT_PRIMARY); - screen_override_ = - std::make_unique<display::test::ScopedScreenOverride>(screen_.get()); + display::Screen::SetScreenInstance(screen_.get()); + ShellTestBaseAura::SetUp(); #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -81,7 +81,7 @@ chromeos::PowerManagerClient::Shutdown(); #endif ShellTestBaseAura::TearDown(); - screen_override_.reset(); + display::Screen::SetScreenInstance(nullptr); screen_.reset(); }
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn index e644779..cb48db2 100644 --- a/google_apis/BUILD.gn +++ b/google_apis/BUILD.gn
@@ -115,8 +115,6 @@ template("google_apis_tmpl") { source_set(target_name) { sources = [ - "credentials_mode.cc", - "credentials_mode.h", "gaia/core_account_id.cc", "gaia/core_account_id.h", "gaia/gaia_access_token_fetcher.cc", @@ -168,7 +166,6 @@ public_deps = [ ":buildflags", "//build:chromeos_buildflags", - "//services/network/public/cpp", ] deps = [ @@ -179,6 +176,7 @@ "//build:chromeos_buildflags", "//crypto", "//mojo/public/cpp/bindings:struct_traits", + "//services/network/public/cpp", ] if (_use_official_google_keys_and_generate_metrics_key_header) {
diff --git a/google_apis/common/base_requests.cc b/google_apis/common/base_requests.cc index 741aa2fd..c37149b 100644 --- a/google_apis/common/base_requests.cc +++ b/google_apis/common/base_requests.cc
@@ -20,7 +20,6 @@ #include "base/values.h" #include "google_apis/common/request_sender.h" #include "google_apis/common/task_util.h" -#include "google_apis/credentials_mode.h" #include "net/base/load_flags.h" #include "net/http/http_util.h" #include "services/network/public/cpp/resource_request.h" @@ -175,7 +174,7 @@ request->url = url; request->method = GetRequestType(); request->load_flags = net::LOAD_DISABLE_CACHE; - request->credentials_mode = GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; // Add request headers. // Note that SetHeader clears the current headers and sets it to the passed-in
diff --git a/google_apis/credentials_mode.cc b/google_apis/credentials_mode.cc deleted file mode 100644 index 54cf67c4..0000000 --- a/google_apis/credentials_mode.cc +++ /dev/null
@@ -1,27 +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. - -#include "google_apis/credentials_mode.h" - -#include "base/feature_list.h" -#include "services/network/public/mojom/fetch_api.mojom.h" - -namespace google_apis { - -namespace { - -BASE_FEATURE(kGaiaCredentialsModeOmitBug_775438_Workaround, - "GaiaCredentialsModeOmitBug_775438_Workaround", - base::FEATURE_ENABLED_BY_DEFAULT); - -} // namespace - -network::mojom::CredentialsMode GetOmitCredentialsModeForGaiaRequests() { - return base::FeatureList::IsEnabled( - kGaiaCredentialsModeOmitBug_775438_Workaround) - ? network::mojom::CredentialsMode::kOmitBug_775438_Workaround - : network::mojom::CredentialsMode::kOmit; -} - -} // namespace google_apis
diff --git a/google_apis/credentials_mode.h b/google_apis/credentials_mode.h deleted file mode 100644 index 3aa7f9f..0000000 --- a/google_apis/credentials_mode.h +++ /dev/null
@@ -1,19 +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. - -#ifndef GOOGLE_APIS_CREDENTIALS_MODE_H_ -#define GOOGLE_APIS_CREDENTIALS_MODE_H_ - -#include "services/network/public/mojom/fetch_api.mojom-forward.h" - -namespace google_apis { - -// Returns the CredentialsMode that should be used for requests to Gaia. It -// returns kCredentialsMode::kOmit or kOmitBug_775438_Workaround depending on a -// variations-controlled feature toggle. -network::mojom::CredentialsMode GetOmitCredentialsModeForGaiaRequests(); - -} // namespace google_apis - -#endif // GOOGLE_APIS_CREDENTIALS_MODE_H_
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc index 24f3b12..51faad8 100644 --- a/google_apis/gaia/gaia_auth_fetcher.cc +++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -22,7 +22,6 @@ #include "base/system/sys_info.h" #include "base/types/optional_util.h" #include "base/values.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_constants.h" @@ -267,9 +266,7 @@ resource_request->url = gaia_gurl; original_url_ = gaia_gurl; - if (credentials_mode != network::mojom::CredentialsMode::kOmit && - credentials_mode != - network::mojom::CredentialsMode::kOmitBug_775438_Workaround) { + if (credentials_mode != network::mojom::CredentialsMode::kOmit) { CHECK(gaia::HasGaiaSchemeHostPort(gaia_gurl)) << gaia_gurl; url::Origin origin = GaiaUrls::GetInstance()->gaia_origin(); @@ -413,10 +410,10 @@ } } })"); - CreateAndStartGaiaFetcher( - request_body_, kFormEncodedContentType, std::string(), - oauth2_revoke_gurl_, google_apis::GetOmitCredentialsModeForGaiaRequests(), - traffic_annotation); + CreateAndStartGaiaFetcher(request_body_, kFormEncodedContentType, + std::string(), oauth2_revoke_gurl_, + network::mojom::CredentialsMode::kOmit, + traffic_annotation); } void GaiaAuthFetcher::StartAuthCodeForOAuth2TokenExchange( @@ -461,7 +458,7 @@ })"); CreateAndStartGaiaFetcher( request_body_, kFormEncodedContentType, std::string(), oauth2_token_gurl_, - google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); + network::mojom::CredentialsMode::kOmit, traffic_annotation); } void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token, @@ -552,7 +549,7 @@ })"); CreateAndStartGaiaFetcher( std::string(), std::string(), authentication_header, uberauth_token_gurl_, - google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); + network::mojom::CredentialsMode::kOmit, traffic_annotation); } void GaiaAuthFetcher::StartListAccounts() { @@ -750,9 +747,9 @@ DCHECK(reauth_url.is_valid()); // Start the request. - CreateAndStartGaiaFetcher( - post_body, kJsonContentType, headers, reauth_url, - google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); + CreateAndStartGaiaFetcher(post_body, kJsonContentType, headers, reauth_url, + network::mojom::CredentialsMode::kOmit, + traffic_annotation); } void GaiaAuthFetcher::StartGetCheckConnectionInfo() { @@ -783,10 +780,10 @@ } } })"); - CreateAndStartGaiaFetcher( - std::string(), std::string(), std::string(), - get_check_connection_info_url_, - google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); + CreateAndStartGaiaFetcher(std::string(), std::string(), std::string(), + get_check_connection_info_url_, + network::mojom::CredentialsMode::kOmit, + traffic_annotation); } // static
diff --git a/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/google_apis/gaia/gaia_auth_fetcher_unittest.cc index 43e264e..e7d9e62 100644 --- a/google_apis/gaia/gaia_auth_fetcher_unittest.cc +++ b/google_apis/gaia/gaia_auth_fetcher_unittest.cc
@@ -17,7 +17,6 @@ #include "base/test/task_environment.h" #include "base/values.h" #include "build/build_config.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" @@ -256,7 +255,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.StartAuthCodeForOAuth2TokenExchange("auth_code"); ASSERT_EQ(received_requests_.size(), 1U); - EXPECT_EQ(google_apis::GetOmitCredentialsModeForGaiaRequests(), + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, received_requests_.at(0).credentials_mode); std::string body = GetRequestBodyAsString(&received_requests_.at(0)); EXPECT_EQ(std::string::npos, body.find("device_type=chrome")); @@ -275,7 +274,7 @@ "device_ABCDE_1"); ASSERT_EQ(1U, received_requests_.size()); - EXPECT_EQ(google_apis::GetOmitCredentialsModeForGaiaRequests(), + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, received_requests_.at(0).credentials_mode); std::string body = GetRequestBodyAsString(&received_requests_.at(0)); EXPECT_NE(std::string::npos, body.find("device_type=chrome")); @@ -626,8 +625,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data); } @@ -642,8 +640,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); } @@ -658,8 +655,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); } @@ -675,8 +671,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data); } @@ -691,8 +686,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data); } @@ -707,7 +701,6 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), - google_apis::GetOmitCredentialsModeForGaiaRequests(), - TRAFFIC_ANNOTATION_FOR_TESTS); + network::mojom::CredentialsMode ::kOmit, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data); }
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc index 4f64f84..502ba94 100644 --- a/google_apis/gaia/gaia_oauth_client.cc +++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -19,7 +19,6 @@ #include "base/strings/string_util.h" #include "base/task/single_thread_task_runner.h" #include "base/values.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/backoff_entry.h" @@ -461,8 +460,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url_; resource_request->method = post_body_.empty() ? "GET" : "POST"; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!authorization_header_.empty()) resource_request->headers.SetHeader("Authorization", authorization_header_); if (!http_method_override_header_.empty()) {
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc index c3fcb30..4a3c42a 100644 --- a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc +++ b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc
@@ -16,7 +16,6 @@ #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/values.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" #include "net/http/http_status_code.h" @@ -96,8 +95,7 @@ const net::NetworkTrafficAnnotationTag& traffic_annotation) { auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = url; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; if (!body.empty()) resource_request->method = "POST";
diff --git a/google_apis/gaia/oauth2_api_call_flow.cc b/google_apis/gaia/oauth2_api_call_flow.cc index b9f28b94..37a1b34 100644 --- a/google_apis/gaia/oauth2_api_call_flow.cc +++ b/google_apis/gaia/oauth2_api_call_flow.cc
@@ -10,7 +10,6 @@ #include "base/functional/bind.h" #include "base/strings/escape.h" #include "base/strings/stringprintf.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/load_flags.h" @@ -99,8 +98,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = CreateApiCallUrl(); request->method = request_type; - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; request->headers = CreateApiCallHeaders(); request->headers.SetHeader("Authorization", MakeAuthorizationValue(access_token));
diff --git a/google_apis/gcm/engine/checkin_request.cc b/google_apis/gcm/engine/checkin_request.cc index ff40cfa..544bdca 100644 --- a/google_apis/gcm/engine/checkin_request.cc +++ b/google_apis/gcm/engine/checkin_request.cc
@@ -9,7 +9,6 @@ #include "base/metrics/histogram_functions.h" #include "base/task/sequenced_task_runner.h" #include "build/chromeos_buildflags.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gcm/monitoring/gcm_stats_recorder.h" #include "google_apis/gcm/protocol/checkin.pb.h" #include "net/base/load_flags.h" @@ -194,8 +193,7 @@ auto resource_request = std::make_unique<network::ResourceRequest>(); resource_request->url = checkin_url_; resource_request->method = "POST"; - resource_request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; DVLOG(1) << "Performing check-in request with android id: " << request_info_.android_id
diff --git a/google_apis/gcm/engine/registration_request.cc b/google_apis/gcm/engine/registration_request.cc index 36adf30..50d754d9 100644 --- a/google_apis/gcm/engine/registration_request.cc +++ b/google_apis/gcm/engine/registration_request.cc
@@ -14,7 +14,6 @@ #include "base/strings/string_number_conversions.h" #include "base/task/sequenced_task_runner.h" #include "base/values.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gcm/base/gcm_util.h" #include "google_apis/gcm/monitoring/gcm_stats_recorder.h" #include "net/base/load_flags.h" @@ -177,8 +176,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = registration_url_; request->method = "POST"; - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; BuildRequestHeaders(&request->headers); std::string body;
diff --git a/google_apis/gcm/engine/registration_request_unittest.cc b/google_apis/gcm/engine/registration_request_unittest.cc index ec5ce5b..7dc1cb8 100644 --- a/google_apis/gcm/engine/registration_request_unittest.cc +++ b/google_apis/gcm/engine/registration_request_unittest.cc
@@ -15,7 +15,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_tokenizer.h" #include "base/task/single_thread_task_runner.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gcm/engine/gcm_registration_request_handler.h" #include "google_apis/gcm/engine/gcm_request_test_base.h" #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h" @@ -460,7 +459,7 @@ const network::ResourceRequest* pending_request; ASSERT_TRUE( test_url_loader_factory()->IsPending(kRegistrationURL, &pending_request)); - EXPECT_EQ(google_apis::GetOmitCredentialsModeForGaiaRequests(), + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, pending_request->credentials_mode); // Verify that authorization header was put together properly.
diff --git a/google_apis/gcm/engine/unregistration_request.cc b/google_apis/gcm/engine/unregistration_request.cc index 195f5a97..3782675 100644 --- a/google_apis/gcm/engine/unregistration_request.cc +++ b/google_apis/gcm/engine/unregistration_request.cc
@@ -13,7 +13,6 @@ #include "base/strings/string_piece.h" #include "base/task/sequenced_task_runner.h" #include "base/values.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gcm/base/gcm_util.h" #include "google_apis/gcm/monitoring/gcm_stats_recorder.h" #include "net/base/load_flags.h" @@ -164,8 +163,7 @@ auto request = std::make_unique<network::ResourceRequest>(); request->url = registration_url_; request->method = "POST"; - request->credentials_mode = - google_apis::GetOmitCredentialsModeForGaiaRequests(); + request->credentials_mode = network::mojom::CredentialsMode::kOmit; BuildRequestHeaders(&request->headers); std::string body;
diff --git a/google_apis/gcm/engine/unregistration_request_unittest.cc b/google_apis/gcm/engine/unregistration_request_unittest.cc index d4637128..760ea34 100644 --- a/google_apis/gcm/engine/unregistration_request_unittest.cc +++ b/google_apis/gcm/engine/unregistration_request_unittest.cc
@@ -14,7 +14,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_tokenizer.h" #include "base/task/single_thread_task_runner.h" -#include "google_apis/credentials_mode.h" #include "google_apis/gcm/engine/gcm_request_test_base.h" #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h" #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h" @@ -119,7 +118,7 @@ const network::ResourceRequest* pending_request; ASSERT_TRUE( test_url_loader_factory()->IsPending(kRegistrationURL, &pending_request)); - EXPECT_EQ(google_apis::GetOmitCredentialsModeForGaiaRequests(), + EXPECT_EQ(network::mojom::CredentialsMode::kOmit, pending_request->credentials_mode); // Verify that authorization header was put together properly.
diff --git a/infra/config/generated/builders/ci/ios-blink-dbg-fyi/properties.json b/infra/config/generated/builders/ci/ios-blink-dbg-fyi/properties.json new file mode 100644 index 0000000..99728d81d --- /dev/null +++ b/infra/config/generated/builders/ci/ios-blink-dbg-fyi/properties.json
@@ -0,0 +1,64 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "ios-blink-dbg-fyi", + "project": "chromium" + }, + "builder_spec": { + "build_gs_bucket": "chromium-fyi-archive", + "builder_group": "chromium.fyi", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb", + "mac_toolchain" + ], + "build_config": "Debug", + "config": "chromium", + "target_bits": 64, + "target_platform": "ios" + }, + "legacy_gclient_config": { + "config": "ios" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "ios-blink-dbg-fyi", + "project": "chromium" + } + ], + "mirroring_builder_group_and_names": [ + { + "builder": "ios-blink-dbg-fyi", + "group": "tryserver.chromium.mac" + } + ] + } + }, + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "jobs": 250, + "metrics_project": "chromium-reclient-metrics", + "scandeps_server": true + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "chromium.fyi", + "recipe": "chromium", + "xcode_build_version": "14c18" +} \ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios-blink-dbg-fyi/properties.json b/infra/config/generated/builders/try/ios-blink-dbg-fyi/properties.json new file mode 100644 index 0000000..b7e5f60 --- /dev/null +++ b/infra/config/generated/builders/try/ios-blink-dbg-fyi/properties.json
@@ -0,0 +1,56 @@ +{ + "$build/chromium_tests_builder_config": { + "builder_config": { + "builder_db": { + "entries": [ + { + "builder_id": { + "bucket": "ci", + "builder": "ios-blink-dbg-fyi", + "project": "chromium" + }, + "builder_spec": { + "build_gs_bucket": "chromium-fyi-archive", + "builder_group": "chromium.fyi", + "execution_mode": "COMPILE_AND_TEST", + "legacy_chromium_config": { + "apply_configs": [ + "mb", + "mac_toolchain" + ], + "build_config": "Debug", + "config": "chromium", + "target_bits": 64, + "target_platform": "ios" + }, + "legacy_gclient_config": { + "config": "ios" + } + } + } + ] + }, + "builder_ids": [ + { + "bucket": "ci", + "builder": "ios-blink-dbg-fyi", + "project": "chromium" + } + ] + } + }, + "$build/reclient": { + "instance": "rbe-chromium-untrusted", + "metrics_project": "chromium-reclient-metrics" + }, + "$recipe_engine/resultdb/test_presentation": { + "column_keys": [], + "grouping_keys": [ + "status", + "v.test_suite" + ] + }, + "builder_group": "tryserver.chromium.mac", + "recipe": "chromium_trybot", + "xcode_build_version": "14c18" +} \ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index bbdb0d43..5de8d8a 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1992,6 +1992,10 @@ includable_only: true } builders { + name: "chromium/try/ios-blink-dbg-fyi" + includable_only: true + } + builders { name: "chromium/try/ios-catalyst" includable_only: true }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 9d5b374..04ac6a0 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -31622,6 +31622,91 @@ } } builders { + name: "ios-blink-dbg-fyi" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builder:ios-blink-dbg-fyi" + dimensions: "cpu:x86-64" + dimensions: "pool:luci.chromium.ci" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/ci/ios-blink-dbg-fyi/properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "chromium.fyi",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium"' + '}' + priority: 35 + execution_timeout_secs: 36000 + caches { + name: "xcode_ios_14c18" + path: "xcode_ios_14c18.app" + } + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "chromium_swarming.expose_merge_script_failures" + value: 100 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + } + builders { name: "ios-catalyst" swarming_host: "chromium-swarm.appspot.com" dimensions: "builder:ios-catalyst" @@ -68834,6 +68919,102 @@ } } builders { + name: "ios-blink-dbg-fyi" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builder:ios-blink-dbg-fyi" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-12" + dimensions: "pool:luci.chromium.try" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/try/ios-blink-dbg-fyi/properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "tryserver.chromium.mac",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium_trybot"' + '}' + execution_timeout_secs: 14400 + expiration_secs: 7200 + grace_period { + seconds: 120 + } + caches { + name: "win_toolchain" + path: "win_toolchain" + } + caches { + name: "xcode_ios_14c18" + path: "xcode_ios_14c18.app" + } + build_numbers: YES + service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" + task_template_canary_percentage { + value: 5 + } + experiments { + key: "chromium_swarming.expose_merge_script_failures" + value: 100 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "try_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_try_test_results" + test_results { + predicate { + test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_try_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + } + builders { name: "ios-catalyst" swarming_host: "chromium-swarm.appspot.com" dimensions: "builder:ios-catalyst"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index b6a1fbd..64d10d9 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -8969,6 +8969,11 @@ short_name: "dbg" } builders { + name: "buildbucket/luci.chromium.ci/ios-blink-dbg-fyi" + category: "iOS" + short_name: "ios-blk" + } + builders { name: "buildbucket/luci.chromium.ci/ios-simulator-multi-window" category: "iOS" short_name: "mwd" @@ -17406,6 +17411,9 @@ name: "buildbucket/luci.chromium.try/ios-asan" } builders { + name: "buildbucket/luci.chromium.try/ios-blink-dbg-fyi" + } + builders { name: "buildbucket/luci.chromium.try/ios-catalyst" } builders { @@ -18864,6 +18872,9 @@ name: "buildbucket/luci.chromium.try/ios-asan" } builders { + name: "buildbucket/luci.chromium.try/ios-blink-dbg-fyi" + } + builders { name: "buildbucket/luci.chromium.try/ios-catalyst" } builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 2b67f9d2..c9a670cd 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4236,6 +4236,15 @@ } } job { + id: "ios-blink-dbg-fyi" + realm: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "ios-blink-dbg-fyi" + } +} +job { id: "ios-catalyst" realm: "ci" buildbucket { @@ -6405,6 +6414,7 @@ triggers: "fuchsia-x64-rel" triggers: "ios-angle-builder" triggers: "ios-asan" + triggers: "ios-blink-dbg-fyi" triggers: "ios-catalyst" triggers: "ios-device" triggers: "ios-fieldtrial-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index dcbf70a..e9163f5 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -2089,6 +2089,30 @@ ) fyi_ios_builder( + name = "ios-blink-dbg-fyi", + builder_spec = builder_config.builder_spec( + gclient_config = builder_config.gclient_config( + config = "ios", + ), + chromium_config = builder_config.chromium_config( + config = "chromium", + apply_configs = [ + "mb", + "mac_toolchain", + ], + build_config = builder_config.build_config.DEBUG, + target_bits = 64, + target_platform = builder_config.target_platform.IOS, + ), + build_gs_bucket = "chromium-fyi-archive", + ), + console_view_entry = consoles.console_view_entry( + category = "iOS", + short_name = "ios-blk", + ), +) + +fyi_ios_builder( name = "ios-simulator-cronet", branch_selector = branches.selector.IOS_BRANCHES, builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star index 17947a9d..40c9c1a 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -347,6 +347,13 @@ ) ios_builder( + name = "ios-blink-dbg-fyi", + mirrors = [ + "ci/ios-blink-dbg-fyi", + ], +) + +ios_builder( name = "ios-catalyst", mirrors = [ "ci/ios-catalyst",
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm index 91cfb83..6cca443 100644 --- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm +++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -247,9 +247,8 @@ TestAutofillManagerWaiter& waiter() { return waiter_; } private: - TestAutofillManagerWaiter waiter_{ - *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + TestAutofillManagerWaiter waiter_{*this, + {AutofillManagerEvent::kFormsSeen}}; }; void SetUp() override;
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm index a58252ca..1f58c73 100644 --- a/ios/chrome/browser/autofill/form_structure_browsertest.mm +++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -144,9 +144,8 @@ TestAutofillManagerWaiter& waiter() { return waiter_; } private: - TestAutofillManagerWaiter waiter_{ - *this, - {&AutofillManager::Observer::OnAfterFormsSeen}}; + TestAutofillManagerWaiter waiter_{*this, + {AutofillManagerEvent::kFormsSeen}}; }; FormStructureBrowserTest();
diff --git a/ios/chrome/browser/discover_feed/BUILD.gn b/ios/chrome/browser/discover_feed/BUILD.gn index fe8785ab..07452ec 100644 --- a/ios/chrome/browser/discover_feed/BUILD.gn +++ b/ios/chrome/browser/discover_feed/BUILD.gn
@@ -28,6 +28,7 @@ ] public_deps = [ ":constants", + ":discover_feed_refresher", "//base", ] frameworks = [ "UIKit.framework" ] @@ -63,3 +64,7 @@ "feed_constants.mm", ] } + +source_set("discover_feed_refresher") { + sources = [ "discover_feed_refresher.h" ] +}
diff --git a/ios/chrome/browser/discover_feed/discover_feed_refresher.h b/ios/chrome/browser/discover_feed/discover_feed_refresher.h new file mode 100644 index 0000000..1817c4b5e --- /dev/null +++ b/ios/chrome/browser/discover_feed/discover_feed_refresher.h
@@ -0,0 +1,25 @@ +// 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. + +#ifndef IOS_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_REFRESHER_H_ +#define IOS_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_REFRESHER_H_ + +// An interface to refresh the Discover Feed. +class DiscoverFeedRefresher { + public: + // Refreshes the Discover Feed. + // DEPRECATED: use `RefreshFeed(bool feed_visible)`. + virtual void RefreshFeed() = 0; + + // Refreshes the Discover Feed, indicating whether the feed is visible at the + // time of the request. + virtual void RefreshFeed(bool feed_visible) {} + + // Refreshes the Discover Feed if needed. The implementer decides if a refresh + // is needed or not. This should only be called when the feed is visible to + // the user. + virtual void RefreshFeedIfNeeded() = 0; +}; + +#endif // IOS_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_REFRESHER_H_
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.h b/ios/chrome/browser/discover_feed/discover_feed_service.h index 76106032..7ff41b2 100644 --- a/ios/chrome/browser/discover_feed/discover_feed_service.h +++ b/ios/chrome/browser/discover_feed/discover_feed_service.h
@@ -9,6 +9,7 @@ #include "components/keyed_service/core/keyed_service.h" #include "ios/chrome/browser/discover_feed/discover_feed_observer.h" +#include "ios/chrome/browser/discover_feed/discover_feed_refresher.h" #include "ios/chrome/browser/discover_feed/discover_feed_view_controller_configuration.h" #include "ios/chrome/browser/discover_feed/feed_constants.h" #include "ios/chrome/browser/discover_feed/feed_model_configuration.h" @@ -17,7 +18,7 @@ // A browser-context keyed service that is used to keep the Discover Feed data // up to date. -class DiscoverFeedService : public KeyedService { +class DiscoverFeedService : public DiscoverFeedRefresher, public KeyedService { public: DiscoverFeedService(); ~DiscoverFeedService() override; @@ -61,14 +62,6 @@ // Updates the feed's theme to match the user's theme (light/dark). virtual void UpdateTheme() = 0; - // Refreshes the Discover Feed if needed. The provider decides if a refresh is - // needed or not. - virtual void RefreshFeedIfNeeded() = 0; - - // Refreshes the Discover Feed. Once the Feed model is refreshed it will - // update all ViewControllers returned by NewFeedViewController. - virtual void RefreshFeed() = 0; - // Performs a background refresh for the feed. `completion` is called // after success, failure, or timeout. The BOOL argument indicates whether the // refresh was successful or a failure.
diff --git a/ios/chrome/browser/prefs/pref_names.cc b/ios/chrome/browser/prefs/pref_names.cc index d227142e..a6887021 100644 --- a/ios/chrome/browser/prefs/pref_names.cc +++ b/ios/chrome/browser/prefs/pref_names.cc
@@ -96,10 +96,16 @@ const char kIosCredentialProviderPromoStopPromo[] = "ios.credential_provider_promo.stop_promo"; -// The time when the DiscoverFeed was last refreshed. +// The time when the DiscoverFeed was last refreshed while the feed was visible +// to the user. const char kIosDiscoverFeedLastRefreshTime[] = "ios.discover_feed.last_refresh_time"; +// The time when the DiscoverFeed was last refreshed while the feed was not +// visible to the user. +const char kIosDiscoverFeedLastUnseenRefreshTime[] = + "ios.discover_feed.last_unseen_refresh_time"; + // The user's account info from before a device restore. const char kIosPreRestoreAccountInfo[] = "ios.pre_restore_account_info";
diff --git a/ios/chrome/browser/prefs/pref_names.h b/ios/chrome/browser/prefs/pref_names.h index f91dd9c..af281506 100644 --- a/ios/chrome/browser/prefs/pref_names.h +++ b/ios/chrome/browser/prefs/pref_names.h
@@ -33,6 +33,7 @@ extern const char kIosShareChromeCount[]; extern const char kIosShareChromeLastShare[]; extern const char kIosDiscoverFeedLastRefreshTime[]; +extern const char kIosDiscoverFeedLastUnseenRefreshTime[]; extern const char kIosPreRestoreAccountInfo[]; extern const char kIosPromosManagerActivePromos[]; extern const char kIosPromosManagerImpressions[];
diff --git a/ios/chrome/browser/providers/signin/chromium_trusted_vault.mm b/ios/chrome/browser/providers/signin/chromium_trusted_vault.mm index 5981d45..1eeb1ea 100644 --- a/ios/chrome/browser/providers/signin/chromium_trusted_vault.mm +++ b/ios/chrome/browser/providers/signin/chromium_trusted_vault.mm
@@ -24,6 +24,8 @@ // TrustedVaultClientBackend implementation. void AddObserver(Observer* observer) final; void RemoveObserver(Observer* observer) final; + void SetDeviceRegistrationPublicKeyVerifierForUMA( + VerifierCallback verifier) final; void FetchKeys(id<SystemIdentity> identity, KeyFetchedCallback callback) final; void MarkLocalKeysAsStale(id<SystemIdentity> identity, @@ -38,6 +40,8 @@ UIViewController* presenting_view_controller, CompletionBlock callback) final; void CancelDialog(BOOL animated, ProceduralBlock callback) final; + void ClearLocalData(id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback) final; }; void ChromiumTrustedVaultClientBackend::AddObserver(Observer* observer) { @@ -48,6 +52,11 @@ // Do nothing. } +void ChromiumTrustedVaultClientBackend:: + SetDeviceRegistrationPublicKeyVerifierForUMA(VerifierCallback verifier) { + // Do nothing. +} + void ChromiumTrustedVaultClientBackend::FetchKeys(id<SystemIdentity> identity, KeyFetchedCallback callback) { NOTREACHED(); @@ -84,6 +93,12 @@ NOTREACHED(); } +void ChromiumTrustedVaultClientBackend::ClearLocalData( + id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback) { + NOTREACHED(); +} + } // anonymous namespace std::unique_ptr<TrustedVaultClientBackend> CreateTrustedVaultClientBackend(
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm index 49ffe7f..2c42335d 100644 --- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm +++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios.mm
@@ -37,10 +37,7 @@ DCHECK(!HasPendingFetch()) << "Tried to fetch two things at once!"; bool cookies_required = - credentials_mode != network::mojom::CredentialsMode::kOmit && - credentials_mode != - network::mojom::CredentialsMode::kOmitBug_775438_Workaround; - + credentials_mode != network::mojom::CredentialsMode::kOmit; if (!cookies_required) { GaiaAuthFetcher::CreateAndStartGaiaFetcher(body, body_content_type, headers, gaia_gurl, credentials_mode,
diff --git a/ios/chrome/browser/signin/trusted_vault_client_backend.h b/ios/chrome/browser/signin/trusted_vault_client_backend.h index d2aaaac..32eedb3 100644 --- a/ios/chrome/browser/signin/trusted_vault_client_backend.h +++ b/ios/chrome/browser/signin/trusted_vault_client_backend.h
@@ -7,6 +7,7 @@ #include <UIKit/UIKit.h> +#include <string> #include <vector> #include "base/functional/callback_forward.h" @@ -23,6 +24,9 @@ using SharedKey = std::vector<uint8_t>; using SharedKeyList = std::vector<SharedKey>; + // A public key. + using PublicKey = std::vector<uint8_t>; + // Represents the TrustedVaultClientBackend observers. using Observer = syncer::TrustedVaultClient::Observer; @@ -30,6 +34,12 @@ using KeyFetchedCallback = base::OnceCallback<void(const SharedKeyList&)>; using CompletionBlock = void (^)(BOOL success, NSError* error); + // Callback used to verify local device registration and log the result to + // UMA metrics. The first argument is the gaia ID and the second is the local + // client's public key. + using VerifierCallback = + base::OnceCallback<void(const std::string&, const PublicKey&)>; + TrustedVaultClientBackend(); TrustedVaultClientBackend(const TrustedVaultClientBackend&) = delete; @@ -42,6 +52,12 @@ virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; + // Registers a delegate-like callback that implements device registration + // verification. + // TODO(crbug.com/1416626): Make abstract once all implementations land. + virtual void SetDeviceRegistrationPublicKeyVerifierForUMA( + VerifierCallback verifier); + // Asynchronously fetches the shared keys for `identity` and invokes // `callback` with the fetched keys. virtual void FetchKeys(id<SystemIdentity> identity, @@ -85,6 +101,12 @@ // will not be called. If no reauthentication dialog is not present, // `callback` is called synchronously. virtual void CancelDialog(BOOL animated, ProceduralBlock callback) = 0; + + // Clears local data belonging to `identity`, such as shared keys. This + // excludes the physical client's key pair, which remains unchanged. + // TODO(crbug.com/1416626): Make abstract once all implementations land. + virtual void ClearLocalData(id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback); }; #endif // IOS_CHROME_BROWSER_SIGNIN_TRUSTED_VAULT_CLIENT_BACKEND_H_
diff --git a/ios/chrome/browser/signin/trusted_vault_client_backend.mm b/ios/chrome/browser/signin/trusted_vault_client_backend.mm index 9964c875..6b744cb 100644 --- a/ios/chrome/browser/signin/trusted_vault_client_backend.mm +++ b/ios/chrome/browser/signin/trusted_vault_client_backend.mm
@@ -4,6 +4,9 @@ #import "ios/chrome/browser/signin/trusted_vault_client_backend.h" +#import "base/functional/callback.h" +#import "base/notreached.h" + #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif @@ -11,3 +14,14 @@ TrustedVaultClientBackend::TrustedVaultClientBackend() = default; TrustedVaultClientBackend::~TrustedVaultClientBackend() = default; + +void TrustedVaultClientBackend::SetDeviceRegistrationPublicKeyVerifierForUMA( + VerifierCallback verifier) { + NOTREACHED(); +} + +void TrustedVaultClientBackend::ClearLocalData( + id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback) { + NOTREACHED(); +}
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h index e98ee66e..9143cd6 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
@@ -39,6 +39,8 @@ // Returns the bottom padding for the header. This represents the spacing // between the fake omnibox and the content suggestions tiles. CGFloat HeaderBottomPadding(); +// Creates a magnifying glass to be added to the fake omnibox. +UIImageView* CreateMagnifyingGlassView(); // Configure the `search_hint_label` for the fake omnibox. `hintLabelContainer` // is added to the `search_tab_target` with autolayout and `search_hint_label` // is added to `hintLabelContainer` with autoresizing. This is done due to the
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm index ad61a44..f0d48297 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -157,6 +157,28 @@ : kNTPSearchFieldBottomPadding; } +UIImageView* CreateMagnifyingGlassView() { + UIImageView* image_view = [[UIImageView alloc] init]; + image_view.translatesAutoresizingMaskIntoConstraints = NO; + image_view.contentMode = UIViewContentModeScaleAspectFit; + image_view.userInteractionEnabled = NO; + + UIImage* magnifying_glass_image; + if (UseSymbols()) { + magnifying_glass_image = DefaultSymbolWithPointSize( + kMagnifyingglassSymbol, kSymbolContentSuggestionsPointSize); + image_view.tintColor = [UIColor colorNamed:kGrey500Color]; + } else { + magnifying_glass_image = + [[UIImage imageNamed:@"location_bar_magnifyingglass"] + imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + image_view.tintColor = [UIColor colorNamed:kGrey500Color]; + } + + [image_view setImage:magnifying_glass_image]; + return image_view; +} + void ConfigureSearchHintLabel(UILabel* search_hint_label, UIView* search_tab_target) { [search_hint_label setTranslatesAutoresizingMaskIntoConstraints:NO]; @@ -218,7 +240,7 @@ [camera_image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [lens_button setImage:camera_image forState:UIControlStateNormal]; - lens_button.tintColor = [UIColor colorNamed:kGrey500Color]; + lens_button.tintColor = [UIColor colorNamed:kGrey600Color]; lens_button.accessibilityLabel = l10n_util::GetNSString(IDS_IOS_ACCNAME_LENS); lens_button.accessibilityIdentifier = @"Lens";
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm index 81b905db7..56261c7d 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -61,12 +61,20 @@ const CGFloat kEndButtonFakeboxTrailingSpace = 12.0; const CGFloat kEndButtonOmniboxTrailingSpace = 7.0; +// The constants for the constraints affecting the magnifying glass. +const CGFloat kMagnifyingGlassFakeboxLeadingSpace = 12.0; +const CGFloat kMagnifyingGlassOmniboxLeadingSpace = 7.0; + // The constants for the constraints affecting the vertical separator that // appears between the Lens and Voice Search buttons. const CGFloat kLensButtonSeparatorWidth = 1; const CGFloat kLensButtonSeparatorHeight = 12; -const CGFloat kLensButtonSeparatorLeftMargin = 8; -const CGFloat kLensButtonSeparatorRightMargin = 13; +const CGFloat kLensButtonSeparatorLeadingMargin = 8; +const CGFloat kLensButtonSeparatorTrailingMargin = 10; + +// The constants for positioning the magnifying glass. +const CGFloat kMagnifyingGlassTrailingMargin = 8; +const CGFloat kMagnifyingGlassViewSize = 24; // Returns the height of the toolbar based on the preferred content size of the // application. @@ -87,6 +95,9 @@ // Lens is not available. @property(nonatomic, strong) UIView* lensButtonSeparator; +// Image view that shows the magnifying glass. +@property(nonatomic, strong) UIImageView* magnifyingGlassView; + @property(nonatomic, strong, readwrite) ExtendedTouchTargetButton* voiceSearchButton; @@ -106,6 +117,15 @@ // Constraint for positioning the end button away from the fake box rounded // rectangle. @property(nonatomic, strong) NSLayoutConstraint* endButtonTrailingConstraint; +// The magnifying glass should always be at least inside the fake omnibox. +// When the fake omnibox is shrunk, the position from the leading side of +// the search field should yield. +@property(nonatomic, strong) + NSLayoutConstraint* magnifyingGlassLeadingMarginConstraint; +// Constraint for positioning the magnifying glass away from the fake box +// rounded rectangle. +@property(nonatomic, strong) + NSLayoutConstraint* magnifyingGlassLeadingConstraint; // Layout constraint for the invisible button that is where the omnibox should // be and that focuses the omnibox when tapped. @property(nonatomic, strong) NSLayoutConstraint* invisibleOmniboxConstraint; @@ -212,17 +232,39 @@ constraintEqualToAnchor:self.fakeLocationBar.trailingAnchor], ]]; + // Configure the magnifying glass icon. + UIImageView* magnifyingGlass = + content_suggestions::CreateMagnifyingGlassView(); + [searchField addSubview:magnifyingGlass]; + self.magnifyingGlassView = magnifyingGlass; + + self.magnifyingGlassLeadingMarginConstraint = [magnifyingGlass.leadingAnchor + constraintEqualToAnchor:[searchField leadingAnchor]]; + self.magnifyingGlassLeadingMarginConstraint.priority = + UILayoutPriorityDefaultHigh + 1; + self.magnifyingGlassLeadingConstraint = [magnifyingGlass.leadingAnchor + constraintGreaterThanOrEqualToAnchor:self.fakeLocationBar.leadingAnchor + constant:kMagnifyingGlassFakeboxLeadingSpace]; + [NSLayoutConstraint activateConstraints:@[ + self.magnifyingGlassLeadingMarginConstraint, + self.magnifyingGlassLeadingConstraint, + [magnifyingGlass.centerYAnchor + constraintEqualToAnchor:self.fakeLocationBar.centerYAnchor], + [magnifyingGlass.widthAnchor + constraintEqualToConstant:kMagnifyingGlassViewSize], + [magnifyingGlass.heightAnchor + constraintEqualToConstant:kMagnifyingGlassViewSize], + ]]; + // Hint label. self.searchHintLabel = [[UILabel alloc] init]; content_suggestions::ConfigureSearchHintLabel(self.searchHintLabel, searchField); self.searchHintLabel.font = [self hintLabelFont]; self.hintLabelLeadingConstraint = [self.searchHintLabel.leadingAnchor - constraintGreaterThanOrEqualToAnchor:[searchField leadingAnchor] - constant:ntp_header::kHintLabelSidePadding]; + constraintEqualToAnchor:magnifyingGlass.trailingAnchor + constant:kMagnifyingGlassTrailingMargin]; [NSLayoutConstraint activateConstraints:@[ - [self.searchHintLabel.centerXAnchor - constraintEqualToAnchor:self.fakeLocationBar.centerXAnchor], self.hintLabelLeadingConstraint, [self.searchHintLabel.heightAnchor constraintEqualToAnchor:self.fakeLocationBar.heightAnchor @@ -295,10 +337,10 @@ // Separator constraints. [self.lensButtonSeparator.leadingAnchor constraintEqualToAnchor:self.voiceSearchButton.trailingAnchor - constant:kLensButtonSeparatorLeftMargin], + constant:kLensButtonSeparatorLeadingMargin], [self.lensButtonSeparator.trailingAnchor constraintEqualToAnchor:self.lensButton.leadingAnchor - constant:-kLensButtonSeparatorRightMargin], + constant:-kLensButtonSeparatorTrailingMargin], [self.lensButtonSeparator.centerYAnchor constraintEqualToAnchor:self.fakeLocationBar.centerYAnchor], [self.lensButtonSeparator.widthAnchor @@ -422,15 +464,14 @@ toolbarExpandedHeight - kFakeLocationBarHeightMargin; self.fakeLocationBar.layer.cornerRadius = self.fakeLocationBarHeightConstraint.constant / 2; - [self scaleHintLabelForPercent:percent]; + [self scaleAndUpdateConstraintsForHintLabelWithPercent:percent]; self.fakeToolbarTopConstraint.constant = 0; self.fakeLocationBarLeadingConstraint.constant = 0; self.fakeLocationBarTrailingConstraint.constant = 0; self.fakeLocationBarTopConstraint.constant = 0; - self.hintLabelLeadingConstraint.constant = - ntp_header::kHintLabelSidePadding; + self.magnifyingGlassLeadingMarginConstraint.constant = 0; self.endButtonTrailingMarginConstraint.constant = 0; self.separator.alpha = 0; @@ -477,8 +518,8 @@ self.fakeLocationBar.layer.cornerRadius = self.fakeLocationBarHeightConstraint.constant / 2; - // Scale the hintLabel, and make sure the frame stays left aligned. - [self scaleHintLabelForPercent:percent]; + // Scale the hintLabel and update the horizontal constraint constant. + [self scaleAndUpdateConstraintsForHintLabelWithPercent:percent]; // Adjust the position of the search field's subviews by adjusting their // constraint constant value. @@ -491,8 +532,14 @@ -kEndButtonFakeboxTrailingSpace + (kEndButtonFakeboxTrailingSpace - kEndButtonOmniboxTrailingSpace) * percent; - self.hintLabelLeadingConstraint.constant = - subviewsDiff + ntp_header::kHintLabelSidePadding; + // A similar scaling is applied to the magnifying glass icon on the other + // side of the fakebox. + self.magnifyingGlassLeadingMarginConstraint.constant = subviewsDiff; + self.magnifyingGlassLeadingConstraint.constant = + kMagnifyingGlassFakeboxLeadingSpace - + (kMagnifyingGlassFakeboxLeadingSpace - + kMagnifyingGlassOmniboxLeadingSpace) * + percent; } - (void)setFakeboxHighlighted:(BOOL)highlighted { @@ -563,12 +610,24 @@ self.traitCollection.preferredContentSizeCategory); } -// Scale the the hint label down to at most content_suggestions::kHintTextScale. -- (void)scaleHintLabelForPercent:(CGFloat)percent { +// Scale the the hint label down to at most content_suggestions::kHintTextScale +// and updates the horizontal constraint constant accordingly. +- (void)scaleAndUpdateConstraintsForHintLabelWithPercent:(CGFloat)percent { + if (!self.searchHintLabel) { + return; + } CGFloat scaleValue = 1 + (content_suggestions::kHintTextScale * (1 - percent)); self.searchHintLabel.transform = CGAffineTransformMakeScale(scaleValue, scaleValue); + + // Update the hint label constraint based with half of the change in width + // from the original scale, since constraints are calculated before + // transformations are applied. + self.hintLabelLeadingConstraint.constant = + kMagnifyingGlassTrailingMargin + + (1 - percent) * content_suggestions::kHintTextScale * + self.searchHintLabel.bounds.size.width * 0.5; } @end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm index 699be81..ac615bb 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -207,6 +207,7 @@ + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry { registry->RegisterInt64Pref(prefs::kIosDiscoverFeedLastRefreshTime, 0); + registry->RegisterInt64Pref(prefs::kIosDiscoverFeedLastUnseenRefreshTime, 0); } - (void)disconnect {
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm index 8ff2f4a..ced387a 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -110,7 +110,7 @@ // visible, but not actually zero, probably due to some sort of floating // point calculation. id<GREYMatcher> notPracticallyVisible() { - return grey_not(grey_minimumVisiblePercent(0.001)); + return grey_not(grey_minimumVisiblePercent(0.01)); } }
diff --git a/ios/chrome/browser/ui/icons/BUILD.gn b/ios/chrome/browser/ui/icons/BUILD.gn index c77ad4ba..c06c8c1e 100644 --- a/ios/chrome/browser/ui/icons/BUILD.gn +++ b/ios/chrome/browser/ui/icons/BUILD.gn
@@ -62,6 +62,7 @@ "//ios/chrome/browser/ui/icons/resources:camera_lens", "//ios/chrome/browser/ui/icons/resources:checkerboard_shield", "//ios/chrome/browser/ui/icons/resources:checkermark_shield", + "//ios/chrome/browser/ui/icons/resources:dino", "//ios/chrome/browser/ui/icons/resources:incognito", "//ios/chrome/browser/ui/icons/resources:incognito_circle_fill", "//ios/chrome/browser/ui/icons/resources:incognito_circle_fill_ios14", @@ -95,6 +96,20 @@ } } +source_set("symbols_views") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "colorful_background_symbol_view.h", + "colorful_background_symbol_view.mm", + ] + deps = [ + ":symbols", + "//ios/chrome/common/ui/colors", + "//ios/chrome/common/ui/table_view:cells_constants", + "//ios/chrome/common/ui/util", + ] +} + source_set("unit_tests") { testonly = true sources = [ "chrome_icon_unittest.mm" ]
diff --git a/ios/chrome/browser/ui/icons/colorful_background_symbol_view.h b/ios/chrome/browser/ui/icons/colorful_background_symbol_view.h new file mode 100644 index 0000000..f81319d --- /dev/null +++ b/ios/chrome/browser/ui/icons/colorful_background_symbol_view.h
@@ -0,0 +1,36 @@ +// 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. + +#ifndef IOS_CHROME_BROWSER_UI_ICONS_COLORFUL_BACKGROUND_SYMBOL_VIEW_H_ +#define IOS_CHROME_BROWSER_UI_ICONS_COLORFUL_BACKGROUND_SYMBOL_VIEW_H_ + +#import <UIKit/UIKit.h> + +// A view used to display a symbol with a colorful background. +@interface ColorfulBackgroundSymbolView : UIView + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE; + +// The color of the border. If nil, then there is no border (default is nil). +@property(nonatomic, strong) UIColor* borderColor; + +// Sets the symbol to the symbol named `symbolName`, `systemSymbol` is used to +// check if it is a symbol provided by the system or not. When using this +// method, the default size is used. +- (void)setSymbolName:(NSString*)symbolName systemSymbol:(BOOL)systemSymbol; + +// Sets the symbol. +// @discussion +// Use this setter when your symbol needs to be of a custom size. +- (void)setSymbol:(UIImage*)symbol; + +// Sets the tint color of the symbol (default is white). +- (void)setSymbolTintColor:(UIColor*)color; + +@end + +#endif // IOS_CHROME_BROWSER_UI_ICONS_COLORFUL_BACKGROUND_SYMBOL_VIEW_H_
diff --git a/ios/chrome/browser/ui/icons/colorful_background_symbol_view.mm b/ios/chrome/browser/ui/icons/colorful_background_symbol_view.mm new file mode 100644 index 0000000..6bf0f2d --- /dev/null +++ b/ios/chrome/browser/ui/icons/colorful_background_symbol_view.mm
@@ -0,0 +1,79 @@ +// 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. + +#import "ios/chrome/browser/ui/icons/colorful_background_symbol_view.h" + +#import "ios/chrome/browser/ui/icons/symbols.h" +#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h" +#import "ios/chrome/common/ui/util/constraints_ui_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +const CGFloat kSymbolSize = 18; +} // namespace + +@implementation ColorfulBackgroundSymbolView { + UIImageView* _symbolView; +} + +- (instancetype)init { + self = [super initWithFrame:CGRectMake(0, 0, kTableViewIconImageSize, + kTableViewIconImageSize)]; + if (self) { + _symbolView = [[UIImageView alloc] init]; + _symbolView.translatesAutoresizingMaskIntoConstraints = NO; + _symbolView.contentMode = UIViewContentModeCenter; + _symbolView.tintColor = UIColor.whiteColor; + [self addSubview:_symbolView]; + + self.layer.cornerRadius = kColorfulBackgroundSymbolCornerRadius; + + AddSameConstraints(self, _symbolView); + } + return self; +} + +#pragma mark - Accessors + +- (void)setBorderColor:(UIColor*)borderColor { + if (_borderColor == borderColor) { + return; + } + + _borderColor = borderColor; + + if (borderColor) { + self.layer.borderWidth = 1; + } else { + self.layer.borderWidth = 0; + } + self.layer.borderColor = [borderColor CGColor]; +} + +- (void)setSymbolName:(NSString*)symbolName systemSymbol:(BOOL)systemSymbol { + if (systemSymbol) { + _symbolView.image = DefaultSymbolWithPointSize(symbolName, kSymbolSize); + } else { + _symbolView.image = CustomSymbolWithPointSize(symbolName, kSymbolSize); + } +} + +- (void)setSymbol:(UIImage*)symbol { + _symbolView.image = symbol; +} + +- (void)setSymbolTintColor:(UIColor*)color { + _symbolView.tintColor = color; +} + +#pragma mark - UIView + +- (CGSize)intrinsicContentSize { + return CGSizeMake(kTableViewIconImageSize, kTableViewIconImageSize); +} + +@end
diff --git a/ios/chrome/browser/ui/icons/resources/BUILD.gn b/ios/chrome/browser/ui/icons/resources/BUILD.gn index 9f2ff1a..40cd697 100644 --- a/ios/chrome/browser/ui/icons/resources/BUILD.gn +++ b/ios/chrome/browser/ui/icons/resources/BUILD.gn
@@ -26,6 +26,13 @@ ] } +symbolset("dino") { + sources = [ + "dino.symbolset/Contents.json", + "dino.symbolset/dino.cr.svg", + ] +} + symbolset("incognito") { sources = [ "incognito.symbolset/Contents.json",
diff --git a/ios/chrome/browser/ui/icons/resources/dino.symbolset/Contents.json b/ios/chrome/browser/ui/icons/resources/dino.symbolset/Contents.json new file mode 100644 index 0000000..d8ee87bd --- /dev/null +++ b/ios/chrome/browser/ui/icons/resources/dino.symbolset/Contents.json
@@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "dino.cr.svg", + "idiom": "universal" + } + ] +}
diff --git a/ios/chrome/browser/ui/icons/resources/dino.symbolset/dino.cr.svg b/ios/chrome/browser/ui/icons/resources/dino.symbolset/dino.cr.svg new file mode 100644 index 0000000..e4c5fca7 --- /dev/null +++ b/ios/chrome/browser/ui/icons/resources/dino.symbolset/dino.cr.svg
@@ -0,0 +1,161 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--Generator: Apple Native CoreSVG 175.5--> +<!DOCTYPE svg +PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3300" height="2200"> + <!--glyph: "", point size: 100.0, font version: "18.0d12e2", template writer version: "101"--> + <g id="Notes"> + <rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/> + <line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text> + <line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/> + <g transform="matrix(1 0 0 1 263 1933)"> + <path d="M9.24805 0.830078C13.5547 0.830078 17.1387-2.74414 17.1387-7.05078C17.1387-11.3574 13.5449-14.9316 9.23828-14.9316C4.94141-14.9316 1.36719-11.3574 1.36719-7.05078C1.36719-2.74414 4.95117 0.830078 9.24805 0.830078ZM9.24805-0.654297C5.70312-0.654297 2.87109-3.49609 2.87109-7.05078C2.87109-10.6055 5.69336-13.4473 9.23828-13.4473C12.793-13.4473 15.6445-10.6055 15.6445-7.05078C15.6445-3.49609 12.8027-0.654297 9.24805-0.654297ZM5.6543-7.05078C5.6543-6.62109 5.95703-6.32812 6.40625-6.32812L8.50586-6.32812L8.50586-4.20898C8.50586-3.76953 8.79883-3.4668 9.22852-3.4668C9.67773-3.4668 9.9707-3.76953 9.9707-4.20898L9.9707-6.32812L12.0898-6.32812C12.5293-6.32812 12.832-6.62109 12.832-7.05078C12.832-7.49023 12.5293-7.79297 12.0898-7.79297L9.9707-7.79297L9.9707-9.90234C9.9707-10.3516 9.67773-10.6543 9.22852-10.6543C8.79883-10.6543 8.50586-10.3516 8.50586-9.90234L8.50586-7.79297L6.40625-7.79297C5.95703-7.79297 5.6543-7.49023 5.6543-7.05078Z"/> + </g> + <g transform="matrix(1 0 0 1 281.867 1933)"> + <path d="M11.709 2.91016C17.1582 2.91016 21.6699-1.61133 21.6699-7.05078C21.6699-12.5 17.1484-17.0117 11.6992-17.0117C6.25977-17.0117 1.74805-12.5 1.74805-7.05078C1.74805-1.61133 6.26953 2.91016 11.709 2.91016ZM11.709 1.25C7.09961 1.25 3.41797-2.44141 3.41797-7.05078C3.41797-11.6602 7.08984-15.3516 11.6992-15.3516C16.3086-15.3516 20.0098-11.6602 20.0098-7.05078C20.0098-2.44141 16.3184 1.25 11.709 1.25ZM7.17773-7.05078C7.17773-6.57227 7.50977-6.25 8.00781-6.25L10.8789-6.25L10.8789-3.36914C10.8789-2.88086 11.2109-2.53906 11.6895-2.53906C12.1777-2.53906 12.5195-2.87109 12.5195-3.36914L12.5195-6.25L15.4004-6.25C15.8887-6.25 16.2305-6.57227 16.2305-7.05078C16.2305-7.53906 15.8887-7.88086 15.4004-7.88086L12.5195-7.88086L12.5195-10.752C12.5195-11.25 12.1777-11.5918 11.6895-11.5918C11.2109-11.5918 10.8789-11.25 10.8789-10.752L10.8789-7.88086L8.00781-7.88086C7.50977-7.88086 7.17773-7.53906 7.17773-7.05078Z"/> + </g> + <g transform="matrix(1 0 0 1 305.646 1933)"> + <path d="M14.9707 5.66406C21.9336 5.66406 27.6953-0.0976562 27.6953-7.05078C27.6953-14.0137 21.9238-19.7754 14.9609-19.7754C8.00781-19.7754 2.25586-14.0137 2.25586-7.05078C2.25586-0.0976562 8.01758 5.66406 14.9707 5.66406ZM14.9707 3.84766C8.93555 3.84766 4.08203-1.01562 4.08203-7.05078C4.08203-13.0957 8.92578-17.9492 14.9609-17.9492C21.0059-17.9492 25.8691-13.0957 25.8691-7.05078C25.8691-1.01562 21.0156 3.84766 14.9707 3.84766ZM9.19922-7.05078C9.19922-6.5332 9.57031-6.17188 10.1172-6.17188L14.0625-6.17188L14.0625-2.2168C14.0625-1.67969 14.4336-1.29883 14.9512-1.29883C15.4883-1.29883 15.8594-1.66992 15.8594-2.2168L15.8594-6.17188L19.8145-6.17188C20.3516-6.17188 20.7324-6.5332 20.7324-7.05078C20.7324-7.59766 20.3613-7.96875 19.8145-7.96875L15.8594-7.96875L15.8594-11.9141C15.8594-12.4609 15.4883-12.8418 14.9512-12.8418C14.4336-12.8418 14.0625-12.4609 14.0625-11.9141L14.0625-7.96875L10.1172-7.96875C9.57031-7.96875 9.19922-7.59766 9.19922-7.05078Z"/> + </g> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text> + <line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/> + <g transform="matrix(1 0 0 1 776 1933)"> + <path d="M3.31055 0.15625C3.82812 0.15625 4.08203-0.0390625 4.26758-0.585938L5.52734-4.0332L11.2891-4.0332L12.5488-0.585938C12.7344-0.0390625 12.9883 0.15625 13.4961 0.15625C14.0137 0.15625 14.3457-0.15625 14.3457-0.644531C14.3457-0.810547 14.3164-0.966797 14.2383-1.17188L9.6582-13.3691C9.43359-13.9648 9.0332-14.2676 8.4082-14.2676C7.80273-14.2676 7.39258-13.9746 7.17773-13.3789L2.59766-1.16211C2.51953-0.957031 2.49023-0.800781 2.49023-0.634766C2.49023-0.146484 2.80273 0.15625 3.31055 0.15625ZM6.00586-5.51758L8.37891-12.0898L8.42773-12.0898L10.8008-5.51758Z"/> + </g> + <line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="793.197" x2="793.197" y1="1919" y2="1933"/> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text> + <g transform="matrix(1 0 0 1 1289 1933)"> + <path d="M2.8418 1.86523L4.54102 3.57422C5.40039 4.44336 6.38672 4.38477 7.31445 3.35938L18.0078-8.42773L17.041-9.4043L6.42578 2.27539C6.07422 2.67578 5.74219 2.77344 5.27344 2.30469L4.10156 1.14258C3.63281 0.683594 3.74023 0.341797 4.14062-0.0195312L15.6152-10.8203L14.6387-11.7871L3.04688-0.898438C2.06055 0.0195312 1.98242 0.996094 2.8418 1.86523ZM9.25781-16.3281C8.83789-15.918 8.80859-15.3418 9.04297-14.9512C9.27734-14.5898 9.73633-14.3555 10.3809-14.5215C11.8457-14.8633 13.3691-14.9219 14.7949-13.9844L14.209-12.5293C13.8672-11.6992 14.043-11.1133 14.5801-10.5664L16.875-8.25195C17.3633-7.76367 17.7734-7.74414 18.3398-7.8418L19.4043-8.03711L20.0684-7.36328L20.0293-6.80664C19.9902-6.30859 20.1172-5.92773 20.6055-5.44922L21.3672-4.70703C21.8457-4.22852 22.4609-4.19922 22.9297-4.66797L25.8398-7.58789C26.3086-8.05664 26.2891-8.65234 25.8105-9.13086L25.0391-9.89258C24.5605-10.3711 24.1895-10.5273 23.7109-10.4883L23.1348-10.4395L22.4902-11.0742L22.7344-12.1973C22.8613-12.7637 22.7051-13.2031 22.1191-13.7891L19.9219-15.9766C16.582-19.2969 12.1484-19.2188 9.25781-16.3281ZM10.752-15.957C13.1836-17.7344 16.4746-17.4316 18.7012-15.2051L21.1328-12.793C21.3672-12.5586 21.4062-12.373 21.3379-12.0312L21.0156-10.5469L22.5195-9.0625L23.5059-9.12109C23.7598-9.13086 23.8379-9.11133 24.0332-8.91602L24.6094-8.33984L22.168-5.89844L21.5918-6.47461C21.3965-6.66992 21.3672-6.74805 21.377-7.01172L21.4453-7.98828L19.9512-9.47266L18.4277-9.21875C18.1055-9.15039 17.959-9.17969 17.7148-9.41406L15.7129-11.416C15.459-11.6504 15.4297-11.8164 15.5859-12.1875L16.4648-14.2773C14.9023-15.7324 12.8711-16.3574 10.8398-15.7617C10.6836-15.7227 10.625-15.8496 10.752-15.957Z"/> + </g> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text> + <text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.2.0</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 12 or greater</text> + <text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from dino.cr</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100 points</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text> + <text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text> + </g> + <g id="Guides"> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)"> + <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/> + </g> + <line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/> + <line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)"> + <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/> + </g> + <line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/> + <line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)"> + <path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/> + </g> + <line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/> + <line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/> + <line id="left-margin" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1389.97" x2="1389.97" y1="1030.79" y2="1150.12"/> + <line id="right-margin" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1509.72" x2="1509.72" y1="1030.79" y2="1150.12"/> + </g> + <g id="Symbols"> + <g id="Black-L" transform="matrix(1 0 0 1 2853.87 1556)"> + <path d="M46.15 43.34L46.15 16.09L39.38 16.09L39.38 9.15L32.6 9.15L32.6 2.38L25.83 2.38L25.83-4.39L18.9-4.39L18.9-11.16L11.96-11.16L11.96-52.12L18.73-52.12L18.73-38.41L25.67-38.41L25.67-31.64L32.6-31.64L32.6-24.87L46.15-24.87L46.15-31.64L52.76-31.64L52.92-38.41L65.34-38.41L65.34-45.35L77.59-45.35L77.59-52.12L84.36-52.12L84.36-94.37L91.14-94.37L91.14-101.14L141.61-101.14L141.61-94.37L148.54-94.37L148.54-65.67L114.36-65.67L114.36-58.89L134.83-58.89L134.83-52.12L107.42-52.12L107.42-38.58L121.13-38.58L121.13-24.87L114.36-24.87L114.36-31.64L107.42-31.64L107.42-9.87L100.65-9.87L100.65-1.65L93.88-1.65L93.88 5.28L87.11 5.28L87.11 36.57L93.88 36.57L93.88 43.34L80.17 43.34L80.17 23.02L73.4 23.02L73.4 16.09L66.63 16.09L66.63 23.02L59.85 23.02L59.85 29.79L53.08 29.79L53.08 36.57L59.85 36.57L59.85 43.34L46.15 43.34ZM93.39-83.56L100.17-83.56L100.17-90.34L93.39-90.34L93.39-83.56ZM5.19 24.96L32.6 24.96L32.6 21.57L5.19 21.57L5.19 24.96ZM100.65 24.96L154.51 24.96L154.51 21.57L100.65 21.57L100.65 24.96ZM25.67 38.66L39.38 38.66L39.38 35.12L25.67 35.12L25.67 38.66ZM5.19 48.82L18.73 48.82L18.73 45.44L5.19 45.44L5.19 48.82ZM144.99 45.44L148.54 45.44L148.54 42.05L144.99 42.05L144.99 45.44Z"/> + </g> + <g id="Heavy-L" transform="matrix(1 0 0 1 2557.16 1556)"> + <path d="M46.14 43.34L46.14 16.09L39.37 16.09L39.37 9.15L32.6 9.15L32.6 2.38L25.83 2.38L25.83-4.39L18.89-4.39L18.89-11.16L11.96-11.16L11.96-52.12L18.73-52.12L18.73-38.41L25.66-38.41L25.66-31.64L32.6-31.64L32.6-24.87L46.14-24.87L46.14-31.64L52.75-31.64L52.92-38.41L65.33-38.41L65.33-45.35L77.59-45.35L77.59-52.12L84.36-52.12L84.36-94.37L91.13-94.37L91.13-101.14L141.6-101.14L141.6-94.37L148.54-94.37L148.54-65.67L114.35-65.67L114.35-58.89L134.83-58.89L134.83-52.12L107.42-52.12L107.42-38.58L121.12-38.58L121.12-24.87L114.35-24.87L114.35-31.64L107.42-31.64L107.42-9.87L100.65-9.87L100.65-1.65L93.87-1.65L93.87 5.28L87.1 5.28L87.1 36.57L93.87 36.57L93.87 43.34L80.17 43.34L80.17 23.02L73.39 23.02L73.39 16.09L66.62 16.09L66.62 23.02L59.85 23.02L59.85 29.79L53.08 29.79L53.08 36.57L59.85 36.57L59.85 43.34L46.14 43.34ZM93.39-83.56L100.16-83.56L100.16-90.34L93.39-90.34L93.39-83.56ZM5.19 24.96L32.6 24.96L32.6 21.57L5.19 21.57L5.19 24.96ZM100.65 24.96L154.5 24.96L154.5 21.57L100.65 21.57L100.65 24.96ZM25.66 38.66L39.37 38.66L39.37 35.12L25.66 35.12L25.66 38.66ZM5.19 48.82L18.73 48.82L18.73 45.44L5.19 45.44L5.19 48.82ZM144.99 45.44L148.54 45.44L148.54 42.05L144.99 42.05L144.99 45.44Z"/> + </g> + <g id="Bold-L" transform="matrix(1 0 0 1 2260.45 1556)"> + <path d="M46.14 41.34L46.14 14.09L39.37 14.09L39.37 7.15L32.6 7.15L32.6 0.38L25.83 0.38L25.83-6.39L18.89-6.39L18.89-13.16L11.96-13.16L11.96-54.12L18.73-54.12L18.73-40.41L25.66-40.41L25.66-33.64L32.6-33.64L32.6-26.87L46.14-26.87L46.14-33.64L52.75-33.64L52.92-40.41L65.33-40.41L65.33-47.35L77.59-47.35L77.59-54.12L84.36-54.12L84.36-96.37L91.13-96.37L91.13-103.14L141.6-103.14L141.6-96.37L148.54-96.37L148.54-67.67L114.35-67.67L114.35-60.89L134.83-60.89L134.83-54.12L107.42-54.12L107.42-40.58L121.12-40.58L121.12-26.87L114.35-26.87L114.35-33.64L107.42-33.64L107.42-11.87L100.65-11.87L100.65-3.65L93.87-3.65L93.87 3.28L87.1 3.28L87.1 34.57L93.87 34.57L93.87 41.34L80.17 41.34L80.17 21.02L73.39 21.02L73.39 14.09L66.62 14.09L66.62 21.02L59.85 21.02L59.85 27.79L53.08 27.79L53.08 34.57L59.85 34.57L59.85 41.34L46.14 41.34ZM93.39-85.56L100.16-85.56L100.16-92.34L93.39-92.34L93.39-85.56ZM5.19 22.96L32.6 22.96L32.6 19.57L5.19 19.57L5.19 22.96ZM100.65 22.96L154.5 22.96L154.5 19.57L100.65 19.57L100.65 22.96ZM25.66 36.66L39.37 36.66L39.37 33.12L25.66 33.12L25.66 36.66ZM5.19 46.82L18.73 46.82L18.73 43.44L5.19 43.44L5.19 46.82ZM144.99 43.44L148.54 43.44L148.54 40.05L144.99 40.05L144.99 43.44Z"/> + </g> + <g id="Semibold-L" transform="matrix(1 0 0 1 1965.35 1556)"> + <path d="M45.18 40.69L45.18 14.08L38.57 14.08L38.57 7.31L31.96 7.31L31.96 0.7L25.18 0.7L25.18-5.92L18.57-5.92L18.57-12.69L11.8-12.69L11.8-52.52L18.41-52.52L18.41-39.29L25.18-39.29L25.18-32.52L31.8-32.52L31.8-25.91L45.18-25.91L45.18-32.52L51.63-32.52L51.79-39.29L62.92-39.29L62.92-45.91L74.2-45.91L74.2-52.52L80.82-52.52L80.82-94.76L87.43-94.76L87.43-101.38L138.38-101.38L138.38-94.76L145.16-94.76L145.16-65.9L111.78-65.9L111.78-59.29L131.77-59.29L131.77-52.52L105-52.52L105-39.29L118.39-39.29L118.39-25.91L111.78-25.91L111.78-32.52L105-32.52L105-10.43L98.39-10.43L98.39-1.56L91.78-1.56L91.78 5.05L85.17 5.05L85.17 34.07L91.78 34.07L91.78 40.69L78.4 40.69L78.4 20.69L71.79 20.69L71.79 14.08L65.18 14.08L65.18 20.69L58.56 20.69L58.56 27.3L51.79 27.3L51.79 34.07L58.56 34.07L58.56 40.69L45.18 40.69ZM91.46-84.44L98.07-84.44L98.07-91.06L91.46-91.06L91.46-84.44ZM5.19 21.82L31.96 21.82L31.96 18.43L5.19 18.43L5.19 21.82ZM98.39 21.82L151.28 21.82L151.28 18.43L98.39 18.43L98.39 21.82ZM25.18 35.2L38.57 35.2L38.57 31.82L25.18 31.82L25.18 35.2ZM5.19 45.2L18.41 45.2L18.41 41.82L5.19 41.82L5.19 45.2ZM141.77 41.82L145.16 41.82L145.16 38.43L141.77 38.43L141.77 41.82Z"/> + </g> + <g id="Medium-L" transform="matrix(1 0 0 1 1669.77 1556)"> + <path d="M44.53 40.05L44.53 13.93L37.92 13.93L37.92 7.48L31.47 7.48L31.47 0.87L24.86 0.87L24.86-5.58L18.41-5.58L18.41-12.19L11.8-12.19L11.8-51.38L18.41-51.38L18.41-38.32L24.86-38.32L24.86-31.87L31.47-31.87L31.47-25.26L44.53-25.26L44.53-31.87L50.98-31.87L50.98-38.32L61.3-38.32L61.3-44.93L71.62-44.93L71.62-51.38L78.23-51.38L78.23-93.46L84.68-93.46L84.68-99.91L136.12-99.91L136.12-93.46L142.57-93.46L142.57-64.44L109.84-64.44L109.84-57.99L129.51-57.99L129.51-51.38L103.39-51.38L103.39-38.32L116.45-38.32L116.45-25.26L109.84-25.26L109.84-31.87L103.39-31.87L103.39-9.45L96.78-9.45L96.78-0.1L90.33-0.1L90.33 6.51L83.72 6.51L83.72 33.6L90.33 33.6L90.33 40.05L77.11 40.05L77.11 20.54L70.66 20.54L70.66 13.93L64.21 13.93L64.21 20.54L57.59 20.54L57.59 26.99L50.98 26.99L50.98 33.6L57.59 33.6L57.59 40.05L44.53 40.05ZM90.17-83.47L96.62-83.47L96.62-90.08L90.17-90.08L90.17-83.47ZM5.19 21.02L31.47 21.02L31.47 17.8L5.19 17.8L5.19 21.02ZM96.78 21.02L149.02 21.02L149.02 17.8L96.78 17.8L96.78 21.02ZM24.86 34.08L37.92 34.08L37.92 30.86L24.86 30.86L24.86 34.08ZM5.19 43.92L18.25 43.92L18.25 40.7L5.19 40.7L5.19 43.92ZM139.35 40.7L142.57 40.7L142.57 37.31L139.35 37.31L139.35 40.7Z"/> + </g> + <g id="Regular-L" transform="matrix(1 0 0 1 1374.02 1556)"> + <path d="M43.89 39.72L43.89 13.92L37.44 13.92L37.44 7.47L30.99 7.47L30.99 1.02L24.54 1.02L24.54-5.43L18.09-5.43L18.09-11.88L11.64-11.88L11.64-50.58L18.09-50.58L18.09-37.68L24.54-37.68L24.54-31.23L30.99-31.23L30.99-24.78L43.89-24.78L43.89-31.23L50.34-31.23L50.34-37.68L60.01-37.68L60.01-44.13L69.69-44.13L69.69-50.58L76.14-50.58L76.14-92.5L82.59-92.5L82.59-98.95L134.19-98.95L134.19-92.5L140.64-92.5L140.64-63.48L108.39-63.48L108.39-57.03L127.74-57.03L127.74-50.58L101.94-50.58L101.94-37.68L114.84-37.68L114.84-24.78L108.39-24.78L108.39-31.23L101.94-31.23L101.94-8.65L95.49-8.65L95.49 1.02L89.04 1.02L89.04 7.47L82.59 7.47L82.59 33.27L89.04 33.27L89.04 39.72L76.14 39.72L76.14 20.37L69.69 20.37L69.69 13.92L63.24 13.92L63.24 20.37L56.79 20.37L56.79 26.82L50.34 26.82L50.34 33.27L56.79 33.27L56.79 39.72L43.89 39.72ZM89.04-82.83L95.49-82.83L95.49-89.28L89.04-89.28L89.04-82.83ZM5.19 20.37L30.99 20.37L30.99 17.15L5.19 17.15L5.19 20.37ZM95.49 20.37L147.09 20.37L147.09 17.15L95.49 17.15L95.49 20.37ZM24.54 33.27L37.44 33.27L37.44 30.05L24.54 30.05L24.54 33.27ZM5.19 42.95L18.09 42.95L18.09 39.72L5.19 39.72L5.19 42.95ZM137.41 39.72L140.64 39.72L140.64 36.5L137.41 36.5L137.41 39.72Z"/> + </g> + <g id="Light-L" transform="matrix(1 0 0 1 1082.79 1556)"> + <path d="M40.82 34.56L40.82 10.7L34.86 10.7L34.86 4.73L29.05 4.73L29.05-1.23L23.09-1.23L23.09-7.2L17.12-7.2L17.12-13.17L11.15-13.17L11.15-48.8L17.12-48.8L17.12-36.87L23.09-36.87L23.09-30.9L29.05-30.9L29.05-25.1L40.82-25.1L40.82-30.9L46.79-30.9L46.79-36.87L55.82-36.87L55.82-42.84L64.69-42.84L64.69-48.8L70.65-48.8L70.65-87.5L76.62-87.5L76.62-93.47L124.19-93.47L124.19-87.5L130.16-87.5L130.16-60.73L100.49-60.73L100.49-54.77L118.22-54.77L118.22-48.8L94.52-48.8L94.52-36.87L106.45-36.87L106.45-25.1L100.49-25.1L100.49-30.9L94.52-30.9L94.52-10.1L88.55-10.1L88.55-1.23L82.59-1.23L82.59 4.73L76.62 4.73L76.62 28.6L82.59 28.6L82.59 34.56L70.65 34.56L70.65 16.67L64.69 16.67L64.69 10.7L58.72 10.7L58.72 16.67L52.76 16.67L52.76 22.63L46.79 22.63L46.79 28.6L52.76 28.6L52.76 34.56L40.82 34.56ZM82.59-78.63L88.55-78.63L88.55-84.6L82.59-84.6L82.59-78.63ZM5.19 16.67L29.05 16.67L29.05 13.6L5.19 13.6L5.19 16.67ZM88.55 16.67L136.12 16.67L136.12 13.6L88.55 13.6L88.55 16.67ZM23.09 28.6L34.86 28.6L34.86 25.53L23.09 25.53L23.09 28.6ZM5.19 37.47L17.12 37.47L17.12 34.56L5.19 34.56L5.19 37.47ZM127.25 34.56L130.16 34.56L130.16 31.5L127.25 31.5L127.25 34.56Z"/> + </g> + <g id="Thin-L" transform="matrix(1 0 0 1 791.562 1556)"> + <path d="M37.924 29.24L37.924 7.48L32.441 7.48L32.441 1.99L26.959 1.99L26.959-3.49L21.476-3.49L21.476-8.97L15.994-8.97L15.994-14.29L10.673-14.29L10.673-47.03L15.994-47.03L15.994-36.22L21.476-36.22L21.476-30.74L26.959-30.74L26.959-25.26L37.924-25.26L37.924-30.74L43.406-30.74L43.406-36.22L51.469-36.22L51.469-41.71L59.693-41.71L59.693-47.03L65.175-47.03L65.175-82.5L70.658-82.5L70.658-87.98L114.356-87.98L114.356-82.5L119.678-82.5L119.678-57.99L92.426-57.99L92.426-52.51L108.874-52.51L108.874-47.03L86.944-47.03L86.944-36.22L97.909-36.22L97.909-25.26L92.426-25.26L92.426-30.74L86.944-30.74L86.944-11.55L81.623-11.55L81.623-3.49L76.14-3.49L76.14 1.99L70.658 1.99L70.658 23.92L76.14 23.92L76.14 29.24L65.175 29.24L65.175 12.96L59.693 12.96L59.693 7.48L54.21 7.48L54.21 12.96L48.728 12.96L48.728 18.44L43.406 18.44L43.406 23.92L48.728 23.92L48.728 29.24L37.924 29.24ZM76.14-74.44L81.623-74.44L81.623-79.92L76.14-79.92L76.14-74.44ZM5.19 12.96L26.959 12.96L26.959 10.22L5.19 10.22L5.19 12.96ZM81.623 12.96L125.16 12.96L125.16 10.22L81.623 10.22L81.623 12.96ZM21.476 23.92L32.441 23.92L32.441 21.18L21.476 21.18L21.476 23.92ZM5.19 31.99L15.994 31.99L15.994 29.24L5.19 29.24L5.19 31.99ZM117.098 29.24L119.678 29.24L119.678 26.5L117.098 26.5L117.098 29.24Z"/> + </g> + <g id="Ultralight-L" transform="matrix(1 0 0 1 498.076 1556)"> + <path d="M36.15 26.18L36.15 5.54L30.99 5.54L30.99 0.38L25.83 0.38L25.83-4.78L20.67-4.78L20.67-9.94L15.51-9.94L15.51-15.1L10.35-15.1L10.35-46.06L15.51-46.06L15.51-35.74L20.67-35.74L20.67-30.58L25.83-30.58L25.83-25.42L36.15-25.42L36.15-30.58L41.31-30.58L41.31-35.74L49.05-35.74L49.05-40.9L56.79-40.9L56.79-46.06L61.95-46.06L61.95-79.6L67.11-79.6L67.11-84.76L108.39-84.76L108.39-79.6L113.55-79.6L113.55-56.38L87.75-56.38L87.75-51.22L103.23-51.22L103.23-46.06L82.59-46.06L82.59-35.74L92.91-35.74L92.91-25.42L87.75-25.42L87.75-30.58L82.59-30.58L82.59-12.52L77.43-12.52L77.43-4.78L72.27-4.78L72.27 0.38L67.11 0.38L67.11 21.02L72.27 21.02L72.27 26.18L61.95 26.18L61.95 10.7L56.79 10.7L56.79 5.54L51.63 5.54L51.63 10.7L46.47 10.7L46.47 15.86L41.31 15.86L41.31 21.02L46.47 21.02L46.47 26.18L36.15 26.18ZM72.27-71.86L77.43-71.86L77.43-77.02L72.27-77.02L72.27-71.86ZM5.19 10.7L25.83 10.7L25.83 8.12L5.19 8.12L5.19 10.7ZM77.43 10.7L118.71 10.7L118.71 8.12L77.43 8.12L77.43 10.7ZM20.67 21.02L30.99 21.02L30.99 18.44L20.67 18.44L20.67 21.02ZM5.19 28.76L15.51 28.76L15.51 26.18L5.19 26.18L5.19 28.76ZM110.97 26.18L113.55 26.18L113.55 23.6L110.97 23.6L110.97 26.18Z"/> + </g> + <g id="Black-M" transform="matrix(1 0 0 1 2869.65 1126)"> + <path d="M37.57 21.52L37.57 0.02L32.19 0.02L32.19-5.48L26.81-5.48L26.81-10.86L21.44-10.86L21.44-16.23L16.07-16.23L16.07-21.61L10.57-21.61L10.57-53.86L15.94-53.86L15.94-43.11L21.44-43.11L21.44-37.73L26.81-37.73L26.81-32.36L37.57-32.36L37.57-37.73L42.69-37.73L42.94-43.11L53.19-43.11L53.19-48.48L63.31-48.48L63.31-53.86L68.69-53.86L68.69-86.73L74.07-86.73L74.07-92.11L112.94-92.11L112.94-86.73L118.31-86.73L118.31-64.61L91.31-64.61L91.31-59.23L107.57-59.23L107.57-53.86L85.94-53.86L85.94-43.11L96.69-43.11L96.69-32.36L91.31-32.36L91.31-37.73L85.94-37.73L85.94-20.98L80.57-20.98L80.57-14.98L75.19-14.98L75.19-9.61L69.81-9.61L69.81 16.14L75.19 16.14L75.19 21.52L64.44 21.52L64.44 5.39L59.07 5.39L59.07 0.02L53.69 0.02L53.69 5.39L48.31 5.39L48.31 10.77L42.94 10.77L42.94 16.14L48.31 16.14L48.31 21.52L37.57 21.52ZM74.69-77.98L80.07-77.98L80.07-83.36L74.69-83.36L74.69-77.98ZM5.19 7.39L26.81 7.39L26.81 4.77L5.19 4.77L5.19 7.39ZM80.57 7.39L122.94 7.39L122.94 4.77L80.57 4.77L80.57 7.39ZM21.44 18.27L32.19 18.27L32.19 15.52L21.44 15.52L21.44 18.27ZM5.19 26.27L15.94 26.27L15.94 23.64L5.19 23.64L5.19 26.27ZM115.57 23.64L118.31 23.64L118.31 20.89L115.57 20.89L115.57 23.64Z"/> + </g> + <g id="Heavy-M" transform="matrix(1 0 0 1 2572.94 1126)"> + <path d="M37.57 21.52L37.57 0.02L32.19 0.02L32.19-5.48L26.81-5.48L26.81-10.86L21.44-10.86L21.44-16.23L16.07-16.23L16.07-21.61L10.57-21.61L10.57-53.86L15.94-53.86L15.94-43.11L21.44-43.11L21.44-37.73L26.81-37.73L26.81-32.36L37.57-32.36L37.57-37.73L42.69-37.73L42.94-43.11L53.19-43.11L53.19-48.48L63.31-48.48L63.31-53.86L68.69-53.86L68.69-86.73L74.07-86.73L74.07-92.11L112.94-92.11L112.94-86.73L118.31-86.73L118.31-64.61L91.31-64.61L91.31-59.23L107.57-59.23L107.57-53.86L85.94-53.86L85.94-43.11L96.69-43.11L96.69-32.36L91.31-32.36L91.31-37.73L85.94-37.73L85.94-20.98L80.57-20.98L80.57-14.98L75.19-14.98L75.19-9.61L69.81-9.61L69.81 16.14L75.19 16.14L75.19 21.52L64.44 21.52L64.44 5.39L59.07 5.39L59.07 0.02L53.69 0.02L53.69 5.39L48.31 5.39L48.31 10.77L42.94 10.77L42.94 16.14L48.31 16.14L48.31 21.52L37.57 21.52ZM74.69-77.98L80.07-77.98L80.07-83.36L74.69-83.36L74.69-77.98ZM5.19 7.39L26.81 7.39L26.81 4.77L5.19 4.77L5.19 7.39ZM80.57 7.39L122.94 7.39L122.94 4.77L80.57 4.77L80.57 7.39ZM21.44 18.27L32.19 18.27L32.19 15.52L21.44 15.52L21.44 18.27ZM5.19 26.27L15.94 26.27L15.94 23.64L5.19 23.64L5.19 26.27ZM115.57 23.64L118.31 23.64L118.31 20.89L115.57 20.89L115.57 23.64Z"/> + </g> + <g id="Bold-M" transform="matrix(1 0 0 1 2276.23 1126)"> + <path d="M37.57 21.52L37.57 0.02L32.19 0.02L32.19-5.48L26.82-5.48L26.82-10.86L21.44-10.86L21.44-16.23L16.07-16.23L16.07-21.61L10.57-21.61L10.57-53.86L15.94-53.86L15.94-43.11L21.44-43.11L21.44-37.73L26.82-37.73L26.82-32.36L37.57-32.36L37.57-37.73L42.69-37.73L42.94-43.11L53.19-43.11L53.19-48.48L63.32-48.48L63.32-53.86L68.69-53.86L68.69-86.73L74.07-86.73L74.07-92.11L112.94-92.11L112.94-86.73L118.32-86.73L118.32-64.61L91.32-64.61L91.32-59.23L107.57-59.23L107.57-53.86L85.94-53.86L85.94-43.11L96.69-43.11L96.69-32.36L91.32-32.36L91.32-37.73L85.94-37.73L85.94-20.98L80.57-20.98L80.57-14.98L75.19-14.98L75.19-9.61L69.82-9.61L69.82 16.14L75.19 16.14L75.19 21.52L64.44 21.52L64.44 5.39L59.07 5.39L59.07 0.02L53.69 0.02L53.69 5.39L48.32 5.39L48.32 10.77L42.94 10.77L42.94 16.14L48.32 16.14L48.32 21.52L37.57 21.52ZM74.69-77.98L80.07-77.98L80.07-83.36L74.69-83.36L74.69-77.98ZM5.19 7.39L26.82 7.39L26.82 4.77L5.19 4.77L5.19 7.39ZM80.57 7.39L122.94 7.39L122.94 4.77L80.57 4.77L80.57 7.39ZM21.44 18.27L32.19 18.27L32.19 15.52L21.44 15.52L21.44 18.27ZM5.19 26.27L15.94 26.27L15.94 23.64L5.19 23.64L5.19 26.27ZM115.57 23.64L118.32 23.64L118.32 20.89L115.57 20.89L115.57 23.64Z"/> + </g> + <g id="Semibold-M" transform="matrix(1 0 0 1 1981.14 1126)"> + <path d="M36.57 20.77L36.57-0.11L31.44-0.11L31.44-5.36L26.19-5.36L26.19-10.61L20.94-10.61L20.94-15.86L15.69-15.86L15.69-20.98L10.44-20.98L10.44-52.23L15.69-52.23L15.69-41.86L20.94-41.86L20.94-36.61L26.19-36.61L26.19-31.48L36.57-31.48L36.57-36.61L41.69-36.61L41.82-41.86L50.82-41.86L50.82-47.11L59.82-47.11L59.82-52.23L65.07-52.23L65.07-84.98L70.32-84.98L70.32-90.23L109.69-90.23L109.69-84.98L114.82-84.98L114.82-62.73L88.69-62.73L88.69-57.48L104.44-57.48L104.44-52.23L83.44-52.23L83.44-41.86L93.94-41.86L93.94-31.48L88.69-31.48L88.69-36.61L83.44-36.61L83.44-19.61L78.32-19.61L78.32-12.86L73.07-12.86L73.07-7.73L67.82-7.73L67.82 15.52L73.07 15.52L73.07 20.77L62.69 20.77L62.69 5.14L57.44 5.14L57.44-0.11L52.19-0.11L52.19 5.14L47.07 5.14L47.07 10.39L41.82 10.39L41.82 15.52L47.07 15.52L47.07 20.77L36.57 20.77ZM72.82-76.73L78.07-76.73L78.07-81.98L72.82-81.98L72.82-76.73ZM5.19 6.27L26.19 6.27L26.19 3.64L5.19 3.64L5.19 6.27ZM78.32 6.27L119.69 6.27L119.69 3.64L78.32 3.64L78.32 6.27ZM20.94 16.77L31.44 16.77L31.44 14.14L20.94 14.14L20.94 16.77ZM5.19 24.52L15.69 24.52L15.69 22.02L5.19 22.02L5.19 24.52ZM112.19 22.02L114.82 22.02L114.82 19.27L112.19 19.27L112.19 22.02Z"/> + </g> + <g id="Medium-M" transform="matrix(1 0 0 1 1685.81 1126)"> + <path d="M35.69 20.14L35.69-0.23L30.57-0.23L30.57-5.23L25.57-5.23L25.57-10.36L20.44-10.36L20.44-15.48L15.32-15.48L15.32-20.61L10.19-20.61L10.19-51.11L15.32-51.11L15.32-40.98L20.44-40.98L20.44-35.86L25.57-35.86L25.57-30.73L35.69-30.73L35.69-35.86L40.69-35.86L40.82-40.98L48.94-40.98L48.94-45.98L57.07-45.98L57.07-51.11L62.19-51.11L62.19-83.73L67.32-83.73L67.32-88.73L106.94-88.73L106.94-83.73L112.07-83.73L112.07-61.23L86.57-61.23L86.57-56.23L101.94-56.23L101.94-51.11L81.57-51.11L81.57-40.98L91.69-40.98L91.69-30.73L86.57-30.73L86.57-35.86L81.57-35.86L81.57-18.48L76.44-18.48L76.44-11.36L71.32-11.36L71.32-6.23L66.19-6.23L66.19 15.14L71.32 15.14L71.32 20.14L61.19 20.14L61.19 4.89L56.07 4.89L56.07-0.23L50.94-0.23L50.94 4.89L45.94 4.89L45.94 10.02L40.82 10.02L40.82 15.14L45.94 15.14L45.94 20.14L35.69 20.14ZM71.19-75.86L76.32-75.86L76.32-80.98L71.19-80.98L71.19-75.86ZM5.19 5.39L25.57 5.39L25.57 2.89L5.19 2.89L5.19 5.39ZM76.44 5.39L116.94 5.39L116.94 2.89L76.44 2.89L76.44 5.39ZM20.44 15.64L30.57 15.64L30.57 13.02L20.44 13.02L20.44 15.64ZM5.19 23.27L15.32 23.27L15.32 20.64L5.19 20.64L5.19 23.27ZM109.57 20.64L112.07 20.64L112.07 18.14L109.57 18.14L109.57 20.64Z"/> + </g> + <g id="Regular-M" transform="matrix(1 0 0 1 1389.97 1126)"> + <path d="M35.19 19.77L35.19-0.23L30.19-0.23L30.19-5.23L25.19-5.23L25.19-10.23L20.19-10.23L20.19-15.23L15.19-15.23L15.19-20.23L10.19-20.23L10.19-50.23L15.19-50.23L15.19-40.23L20.19-40.23L20.19-35.23L25.19-35.23L25.19-30.23L35.19-30.23L35.19-35.23L40.19-35.23L40.19-40.23L47.69-40.23L47.69-45.23L55.19-45.23L55.19-50.23L60.19-50.23L60.19-82.73L65.19-82.73L65.19-87.73L105.19-87.73L105.19-82.73L110.19-82.73L110.19-60.23L85.19-60.23L85.19-55.23L100.19-55.23L100.19-50.23L80.19-50.23L80.19-40.23L90.19-40.23L90.19-30.23L85.19-30.23L85.19-35.23L80.19-35.23L80.19-17.73L75.19-17.73L75.19-10.23L70.19-10.23L70.19-5.23L65.19-5.23L65.19 14.77L70.19 14.77L70.19 19.77L60.19 19.77L60.19 4.77L55.19 4.77L55.19-0.23L50.19-0.23L50.19 4.77L45.19 4.77L45.19 9.77L40.19 9.77L40.19 14.77L45.19 14.77L45.19 19.77L35.19 19.77ZM70.19-75.23L75.19-75.23L75.19-80.23L70.19-80.23L70.19-75.23ZM5.19 4.77L25.19 4.77L25.19 2.27L5.19 2.27L5.19 4.77ZM75.19 4.77L115.19 4.77L115.19 2.27L75.19 2.27L75.19 4.77ZM20.19 14.77L30.19 14.77L30.19 12.27L20.19 12.27L20.19 14.77ZM5.19 22.27L15.19 22.27L15.19 19.77L5.19 19.77L5.19 22.27ZM107.69 19.77L110.19 19.77L110.19 17.27L107.69 17.27L107.69 19.77Z"/> + </g> + <g id="Light-M" transform="matrix(1 0 0 1 1097.51 1126)"> + <path d="M32.81 15.77L32.81-2.73L28.19-2.73L28.19-7.36L23.69-7.36L23.69-11.98L19.06-11.98L19.06-16.61L14.44-16.61L14.44-21.23L9.81-21.23L9.81-48.86L14.44-48.86L14.44-39.61L19.06-39.61L19.06-34.98L23.69-34.98L23.69-30.48L32.81-30.48L32.81-34.98L37.44-34.98L37.44-39.61L44.44-39.61L44.44-44.23L51.31-44.23L51.31-48.86L55.94-48.86L55.94-78.86L60.56-78.86L60.56-83.48L97.44-83.48L97.44-78.86L102.06-78.86L102.06-58.11L79.06-58.11L79.06-53.48L92.81-53.48L92.81-48.86L74.44-48.86L74.44-39.61L83.69-39.61L83.69-30.48L79.06-30.48L79.06-34.98L74.44-34.98L74.44-18.86L69.81-18.86L69.81-11.98L65.19-11.98L65.19-7.36L60.56-7.36L60.56 11.14L65.19 11.14L65.19 15.77L55.94 15.77L55.94 1.89L51.31 1.89L51.31-2.73L46.69-2.73L46.69 1.89L42.06 1.89L42.06 6.52L37.44 6.52L37.44 11.14L42.06 11.14L42.06 15.77L32.81 15.77ZM65.19-71.98L69.81-71.98L69.81-76.61L65.19-76.61L65.19-71.98ZM5.19 1.89L23.69 1.89L23.69-0.48L5.19-0.48L5.19 1.89ZM69.81 1.89L106.69 1.89L106.69-0.48L69.81-0.48L69.81 1.89ZM19.06 11.14L28.19 11.14L28.19 8.77L19.06 8.77L19.06 11.14ZM5.19 18.02L14.44 18.02L14.44 15.77L5.19 15.77L5.19 18.02ZM99.81 15.77L102.06 15.77L102.06 13.39L99.81 13.39L99.81 15.77Z"/> + </g> + <g id="Thin-M" transform="matrix(1 0 0 1 805.047 1126)"> + <path d="M30.565 11.64L30.565-5.23L26.315-5.23L26.315-9.48L22.065-9.48L22.065-13.73L17.815-13.73L17.815-17.98L13.565-17.98L13.565-22.11L9.44-22.11L9.44-47.48L13.565-47.48L13.565-39.11L17.815-39.11L17.815-34.86L22.065-34.86L22.065-30.61L30.565-30.61L30.565-34.86L34.815-34.86L34.815-39.11L41.065-39.11L41.065-43.36L47.44-43.36L47.44-47.48L51.69-47.48L51.69-74.98L55.94-74.98L55.94-79.23L89.815-79.23L89.815-74.98L93.94-74.98L93.94-55.98L72.815-55.98L72.815-51.73L85.565-51.73L85.565-47.48L68.565-47.48L68.565-39.11L77.065-39.11L77.065-30.61L72.815-30.61L72.815-34.86L68.565-34.86L68.565-19.98L64.44-19.98L64.44-13.73L60.19-13.73L60.19-9.48L55.94-9.48L55.94 7.52L60.19 7.52L60.19 11.64L51.69 11.64L51.69-0.98L47.44-0.98L47.44-5.23L43.19-5.23L43.19-0.98L38.94-0.98L38.94 3.27L34.815 3.27L34.815 7.52L38.94 7.52L38.94 11.64L30.565 11.64ZM60.19-68.73L64.44-68.73L64.44-72.98L60.19-72.98L60.19-68.73ZM5.19-0.98L22.065-0.98L22.065-3.11L5.19-3.11L5.19-0.98ZM64.44-0.98L98.19-0.98L98.19-3.11L64.44-3.11L64.44-0.98ZM17.815 7.52L26.315 7.52L26.315 5.39L17.815 5.39L17.815 7.52ZM5.19 13.77L13.565 13.77L13.565 11.64L5.19 11.64L5.19 13.77ZM91.94 11.64L93.94 11.64L93.94 9.52L91.94 9.52L91.94 11.64Z"/> + </g> + <g id="Ultralight-M" transform="matrix(1 0 0 1 510.836 1126)"> + <path d="M29.19 9.27L29.19-6.73L25.19-6.73L25.19-10.73L21.19-10.73L21.19-14.73L17.19-14.73L17.19-18.73L13.19-18.73L13.19-22.73L9.19-22.73L9.19-46.73L13.19-46.73L13.19-38.73L17.19-38.73L17.19-34.73L21.19-34.73L21.19-30.73L29.19-30.73L29.19-34.73L33.19-34.73L33.19-38.73L39.19-38.73L39.19-42.73L45.19-42.73L45.19-46.73L49.19-46.73L49.19-72.73L53.19-72.73L53.19-76.73L85.19-76.73L85.19-72.73L89.19-72.73L89.19-54.73L69.19-54.73L69.19-50.73L81.19-50.73L81.19-46.73L65.19-46.73L65.19-38.73L73.19-38.73L73.19-30.73L69.19-30.73L69.19-34.73L65.19-34.73L65.19-20.73L61.19-20.73L61.19-14.73L57.19-14.73L57.19-10.73L53.19-10.73L53.19 5.27L57.19 5.27L57.19 9.27L49.19 9.27L49.19-2.73L45.19-2.73L45.19-6.73L41.19-6.73L41.19-2.73L37.19-2.73L37.19 1.27L33.19 1.27L33.19 5.27L37.19 5.27L37.19 9.27L29.19 9.27ZM57.19-66.73L61.19-66.73L61.19-70.73L57.19-70.73L57.19-66.73ZM5.19-2.73L21.19-2.73L21.19-4.73L5.19-4.73L5.19-2.73ZM61.19-2.73L93.19-2.73L93.19-4.73L61.19-4.73L61.19-2.73ZM17.19 5.27L25.19 5.27L25.19 3.27L17.19 3.27L17.19 5.27ZM5.19 11.27L13.19 11.27L13.19 9.27L5.19 9.27L5.19 11.27ZM87.19 9.27L89.19 9.27L89.19 7.27L87.19 7.27L87.19 9.27Z"/> + </g> + <g id="Black-S" transform="matrix(1 0 0 1 2883.85 696)"> + <path d="M29.75 8.756L29.75-7.519L25.71-7.519L25.71-11.662L21.56-11.662L21.56-15.804L17.52-15.804L17.52-19.946L13.38-19.946L13.38-23.989L9.24-23.989L9.24-48.45L13.38-48.45L13.38-40.26L17.42-40.26L17.42-36.217L21.56-36.217L21.56-32.174L29.75-32.174L29.75-36.217L33.59-36.217L33.79-40.26L41.68-40.26L41.68-44.407L49.48-44.407L49.48-48.45L53.52-48.45L53.52-73.398L57.56-73.398L57.56-77.445L87.05-77.445L87.05-73.398L91.09-73.398L91.09-56.635L70.58-56.635L70.58-52.592L82.91-52.592L82.91-48.45L66.54-48.45L66.54-40.26L74.62-40.26L74.62-32.174L70.58-32.174L70.58-36.217L66.54-36.217L66.54-23.596L62.4-23.596L62.4-19.056L58.35-19.056L58.35-15.013L54.21-15.013L54.21 4.713L58.35 4.713L58.35 8.756L50.16 8.756L50.16-3.477L46.12-3.477L46.12-7.519L41.98-7.519L41.98-3.477L37.94-3.477L37.94 0.666L33.79 0.666L33.79 4.713L37.94 4.713L37.94 8.756L29.75 8.756ZM57.96-66.694L62-66.694L62-70.837L57.96-70.837L57.96-66.694ZM5.19-1.895L21.56-1.895L21.56-3.869L5.19-3.869L5.19-1.895ZM62.4-1.895L94.55-1.895L94.55-3.869L62.4-3.869L62.4-1.895ZM17.52 6.389L25.71 6.389L25.71 4.316L17.52 4.316L17.52 6.389ZM5.19 12.501L13.28 12.501L13.28 10.432L5.19 10.432L5.19 12.501ZM89.02 10.432L91.09 10.432L91.09 8.358L89.02 8.358L89.02 10.432Z"/> + </g> + <g id="Heavy-S" transform="matrix(1 0 0 1 2587.13 696)"> + <path d="M29.75 8.756L29.75-7.519L25.71-7.519L25.71-11.662L21.56-11.662L21.56-15.804L17.52-15.804L17.52-19.946L13.38-19.946L13.38-23.989L9.24-23.989L9.24-48.45L13.38-48.45L13.38-40.26L17.42-40.26L17.42-36.217L21.56-36.217L21.56-32.174L29.75-32.174L29.75-36.217L33.59-36.217L33.79-40.26L41.68-40.26L41.68-44.407L49.48-44.407L49.48-48.45L53.52-48.45L53.52-73.398L57.56-73.398L57.56-77.445L87.05-77.445L87.05-73.398L91.09-73.398L91.09-56.635L70.58-56.635L70.58-52.592L82.91-52.592L82.91-48.45L66.54-48.45L66.54-40.26L74.62-40.26L74.62-32.174L70.58-32.174L70.58-36.217L66.54-36.217L66.54-23.596L62.4-23.596L62.4-19.056L58.35-19.056L58.35-15.013L54.21-15.013L54.21 4.713L58.35 4.713L58.35 8.756L50.16 8.756L50.16-3.477L46.12-3.477L46.12-7.519L41.98-7.519L41.98-3.477L37.94-3.477L37.94 0.666L33.79 0.666L33.79 4.713L37.94 4.713L37.94 8.756L29.75 8.756ZM57.96-66.694L62-66.694L62-70.837L57.96-70.837L57.96-66.694ZM5.19-1.895L21.56-1.895L21.56-3.869L5.19-3.869L5.19-1.895ZM62.4-1.895L94.55-1.895L94.55-3.869L62.4-3.869L62.4-1.895ZM17.52 6.389L25.71 6.389L25.71 4.316L17.52 4.316L17.52 6.389ZM5.19 12.501L13.28 12.501L13.28 10.432L5.19 10.432L5.19 12.501ZM89.02 10.432L91.09 10.432L91.09 8.358L89.02 8.358L89.02 10.432Z"/> + </g> + <g id="Bold-S" transform="matrix(1 0 0 1 2290.42 696)"> + <path d="M29.75 8.756L29.75-7.519L25.71-7.519L25.71-11.662L21.57-11.662L21.57-15.804L17.52-15.804L17.52-19.946L13.38-19.946L13.38-23.989L9.24-23.989L9.24-48.45L13.38-48.45L13.38-40.26L17.42-40.26L17.42-36.217L21.57-36.217L21.57-32.174L29.75-32.174L29.75-36.217L33.59-36.217L33.79-40.26L41.68-40.26L41.68-44.407L49.48-44.407L49.48-48.45L53.52-48.45L53.52-73.398L57.56-73.398L57.56-77.445L87.05-77.445L87.05-73.398L91.09-73.398L91.09-56.635L70.58-56.635L70.58-52.592L82.91-52.592L82.91-48.45L66.54-48.45L66.54-40.26L74.62-40.26L74.62-32.174L70.58-32.174L70.58-36.217L66.54-36.217L66.54-23.596L62.4-23.596L62.4-19.056L58.35-19.056L58.35-15.013L54.21-15.013L54.21 4.713L58.35 4.713L58.35 8.756L50.16 8.756L50.16-3.477L46.12-3.477L46.12-7.519L41.98-7.519L41.98-3.477L37.94-3.477L37.94 0.666L33.79 0.666L33.79 4.713L37.94 4.713L37.94 8.756L29.75 8.756ZM57.96-66.694L62-66.694L62-70.837L57.96-70.837L57.96-66.694ZM5.19-1.895L21.57-1.895L21.57-3.869L5.19-3.869L5.19-1.895ZM62.4-1.895L94.55-1.895L94.55-3.869L62.4-3.869L62.4-1.895ZM17.52 6.389L25.71 6.389L25.71 4.316L17.52 4.316L17.52 6.389ZM5.19 12.501L13.28 12.501L13.28 10.432L5.19 10.432L5.19 12.501ZM89.02 10.432L91.09 10.432L91.09 8.358L89.02 8.358L89.02 10.432Z"/> + </g> + <g id="Semibold-S" transform="matrix(1 0 0 1 1994.99 696)"> + <path d="M28.96 8.167L28.96-7.616L25.02-7.616L25.02-11.659L21.08-11.659L21.08-15.602L17.13-15.602L17.13-19.551L13.09-19.551L13.09-23.494L9.14-23.494L9.14-47.164L13.09-47.164L13.09-39.272L17.13-39.272L17.13-35.329L21.08-35.329L21.08-31.48L28.96-31.48L28.96-35.329L32.81-35.329L32.91-39.272L39.81-39.272L39.81-43.221L46.72-43.221L46.72-47.164L50.66-47.164L50.66-72.018L54.61-72.018L54.61-75.961L84.39-75.961L84.39-72.018L88.34-72.018L88.34-55.15L68.51-55.15L68.51-51.207L80.45-51.207L80.45-47.164L64.57-47.164L64.57-39.272L72.46-39.272L72.46-31.386L68.51-31.386L68.51-35.329L64.57-35.329L64.57-22.505L60.62-22.505L60.62-17.477L56.68-17.477L56.68-13.534L52.63-13.534L52.63 4.219L56.68 4.219L56.68 8.167L48.79 8.167L48.79-3.668L44.84-3.668L44.84-7.616L40.9-7.616L40.9-3.668L36.85-3.668L36.85 0.275L32.91 0.275L32.91 4.219L36.95 4.219L36.95 8.167L28.96 8.167ZM56.48-65.707L60.43-65.707L60.43-69.75L56.48-69.75L56.48-65.707ZM5.19-2.783L21.08-2.783L21.08-4.752L5.19-4.752L5.19-2.783ZM60.62-2.783L91.99-2.783L91.99-4.752L60.62-4.752L60.62-2.783ZM17.13 5.208L25.12 5.208L25.12 3.234L17.13 3.234L17.13 5.208ZM5.19 11.126L13.09 11.126L13.09 9.152L5.19 9.152L5.19 11.126ZM86.36 9.152L88.34 9.152L88.34 7.083L86.36 7.083L86.36 9.152Z"/> + </g> + <g id="Medium-S" transform="matrix(1 0 0 1 1699.37 696)"> + <path d="M28.27 7.666L28.27-7.719L24.42-7.719L24.42-11.563L20.57-11.563L20.57-15.407L16.73-15.407L16.73-19.256L12.79-19.256L12.79-23.2L8.94-23.2L8.94-46.278L12.79-46.278L12.79-38.585L16.73-38.585L16.73-34.741L20.57-34.741L20.57-30.892L28.27-30.892L28.27-34.741L32.11-34.741L32.11-38.585L38.33-38.585L38.33-42.434L44.54-42.434L44.54-46.278L48.39-46.278L48.39-70.937L52.23-70.937L52.23-74.781L82.31-74.781L82.31-71.032L86.16-71.032L86.16-54.07L66.83-54.07L66.83-50.226L78.47-50.226L78.47-46.278L62.98-46.278L62.98-38.585L70.68-38.585L70.68-30.892L66.83-30.892L66.83-34.741L62.98-34.741L62.98-21.623L59.14-21.623L59.14-16.198L55.29-16.198L55.29-12.354L51.34-12.354L51.34 3.922L55.29 3.922L55.29 7.666L47.6 7.666L47.6-3.871L43.75-3.871L43.75-7.719L39.91-7.719L39.91-3.771L35.96-3.771L35.96 0.073L32.11 0.073L32.11 3.922L36.06 3.922L36.06 7.666L28.27 7.666ZM55.19-65.02L59.04-65.02L59.04-68.963L55.19-68.963L55.19-65.02ZM5.19-3.478L20.57-3.478L20.57-5.352L5.19-5.352L5.19-3.478ZM59.14-3.478L89.81-3.478L89.81-5.352L59.14-5.352L59.14-3.478ZM16.73 4.314L24.42 4.314L24.42 2.345L16.73 2.345L16.73 4.314ZM5.19 10.133L12.79 10.133L12.79 8.064L5.19 8.064L5.19 10.133ZM84.29 8.064L86.16 8.064L86.16 6.189L84.29 6.189L84.29 8.064Z"/> + </g> + <g id="Regular-S" transform="matrix(1 0 0 1 1403.35 696)"> + <path d="M27.87 7.376L27.87-7.716L24.12-7.716L24.12-11.56L20.28-11.56L20.28-15.31L16.53-15.31L16.53-19.154L12.69-19.154L12.69-22.903L8.94-22.903L8.94-45.588L12.69-45.588L12.69-37.99L16.53-37.99L16.53-34.246L20.28-34.246L20.28-30.496L27.87-30.496L27.87-34.246L31.62-34.246L31.62-37.99L37.34-37.99L37.34-41.839L43.06-41.839L43.06-45.588L46.81-45.588L46.81-70.243L50.56-70.243L50.56-73.992L80.93-73.992L80.93-70.243L84.68-70.243L84.68-53.182L65.75-53.182L65.75-49.432L77.09-49.432L77.09-45.588L62-45.588L62-37.99L69.5-37.99L69.5-30.496L65.75-30.496L65.75-34.246L62-34.246L62-21.028L58.15-21.028L58.15-15.31L54.4-15.31L54.4-11.56L50.56-11.56L50.56 3.626L54.4 3.626L54.4 7.376L46.81 7.376L46.81-3.967L43.06-3.967L43.06-7.716L39.22-7.716L39.22-3.967L35.47-3.967L35.47-0.118L31.62-0.118L31.62 3.626L35.47 3.626L35.47 7.376L27.87 7.376ZM54.4-64.524L58.15-64.524L58.15-68.368L54.4-68.368L54.4-64.524ZM5.19-3.967L20.28-3.967L20.28-5.842L5.19-5.842L5.19-3.967ZM58.15-3.967L88.43-3.967L88.43-5.842L58.15-5.842L58.15-3.967ZM16.53 3.626L24.12 3.626L24.12 1.752L16.53 1.752L16.53 3.626ZM5.19 9.35L12.69 9.35L12.69 7.376L5.19 7.376L5.19 9.35ZM82.81 7.376L84.68 7.376L84.68 5.501L82.81 5.501L82.81 7.376Z"/> + </g> + <g id="Light-S" transform="matrix(1 0 0 1 1109.99 696)"> + <path d="M25.99 4.213L25.99-9.69L22.54-9.69L22.54-13.241L19.09-13.241L19.09-16.692L15.64-16.692L15.64-20.143L12.09-20.143L12.09-23.694L8.64-23.694L8.64-44.504L12.09-44.504L12.09-37.602L15.54-37.602L15.54-34.052L19.09-34.052L19.09-30.7L25.99-30.7L25.99-34.052L29.54-34.052L29.54-37.503L34.77-37.503L34.77-41.053L40-41.053L40-44.504L43.45-44.504L43.45-67.19L46.9-67.19L46.9-70.641L74.81-70.641L74.81-67.19L78.26-67.19L78.26-51.511L60.91-51.511L60.91-48.055L71.26-48.055L71.26-44.504L57.36-44.504L57.36-37.602L64.36-37.602L64.36-30.6L60.91-30.6L60.91-34.052L57.36-34.052L57.36-21.923L53.9-21.923L53.9-16.692L50.45-16.692L50.45-13.241L46.9-13.241L46.9 0.762L50.45 0.762L50.45 4.213L43.45 4.213L43.45-6.239L40-6.239L40-9.69L36.45-9.69L36.45-6.239L33-6.239L33-2.689L29.45-2.689L29.45 0.762L33 0.762L33 4.213L25.99 4.213ZM50.45-61.963L53.9-61.963L53.9-65.514L50.45-65.514L50.45-61.963ZM5.19-6.239L19.09-6.239L19.09-8.015L5.19-8.015L5.19-6.239ZM53.9-6.239L81.72-6.239L81.72-8.015L53.9-8.015L53.9-6.239ZM15.64 0.762L22.54 0.762L22.54-1.013L15.64-1.013L15.64 0.762ZM5.19 5.989L12.09 5.989L12.09 4.213L5.19 4.213L5.19 5.989ZM76.59 4.213L78.26 4.213L78.26 2.438L76.59 2.438L76.59 4.213Z"/> + </g> + <g id="Thin-S" transform="matrix(1 0 0 1 816.634 696)"> + <path d="M24.225 0.961L24.225-11.665L21.068-11.665L21.068-14.917L17.815-14.917L17.815-18.074L14.658-18.074L14.658-21.232L11.406-21.232L11.406-24.385L8.347-24.385L8.347-43.42L11.5-43.42L11.5-37.11L14.658-37.11L14.658-33.952L17.815-33.952L17.815-30.795L24.225-30.795L24.225-33.952L27.378-33.952L27.378-37.11L32.112-37.11L32.112-40.263L36.946-40.263L36.946-43.42L40.103-43.42L40.103-64.131L43.261-64.131L43.261-67.289L68.706-67.289L68.706-64.131L71.859-64.131L71.859-49.929L55.981-49.929L55.981-46.677L65.548-46.677L65.548-43.42L52.828-43.42L52.828-37.11L59.139-37.11L59.139-30.795L55.981-30.795L55.981-33.952L52.828-33.952L52.828-22.808L49.671-22.808L49.671-18.074L46.513-18.074L46.513-14.917L43.261-14.917L43.261-2.097L46.513-2.097L46.513 0.961L40.103 0.961L40.103-8.507L36.946-8.507L36.946-11.665L33.793-11.665L33.793-8.507L30.536-8.507L30.536-5.354L27.378-5.354L27.378-2.097L30.536-2.097L30.536 0.961L24.225 0.961ZM46.513-59.397L49.671-59.397L49.671-62.654L46.513-62.654L46.513-59.397ZM5.19-8.507L17.815-8.507L17.815-10.088L5.19-10.088L5.19-8.507ZM49.671-8.507L75.016-8.507L75.016-10.088L49.671-10.088L49.671-8.507ZM14.658-2.097L21.068-2.097L21.068-3.674L14.658-3.674L14.658-2.097ZM5.19 2.637L11.406 2.637L11.406 0.961L5.19 0.961L5.19 2.637ZM70.382 0.961L71.859 0.961L71.859-0.62L70.382-0.62L70.382 0.961Z"/> + </g> + <g id="Ultralight-S" transform="matrix(1 0 0 1 521.897 696)"> + <path d="M23.136-0.911L23.136-12.846L20.178-12.846L20.178-15.904L17.12-15.904L17.12-18.862L14.166-18.862L14.166-21.921L11.107-21.921L11.107-24.875L8.149-24.875L8.149-42.826L11.107-42.826L11.107-36.809L14.166-36.809L14.166-33.85L17.12-33.85L17.12-30.892L23.136-30.892L23.136-33.85L26.095-33.85L26.095-36.809L30.635-36.809L30.635-39.867L35.17-39.867L35.17-42.826L38.129-42.826L38.129-62.354L41.088-62.354L41.088-65.312L65.151-65.312L65.151-62.354L68.109-62.354L68.109-48.843L53.122-48.843L53.122-45.884L62.093-45.884L62.093-42.826L50.163-42.826L50.163-36.809L56.08-36.809L56.08-30.892L53.122-30.892L53.122-33.85L50.163-33.85L50.163-23.398L47.105-23.398L47.105-18.862L44.146-18.862L44.146-15.904L41.088-15.904L41.088-3.87L44.146-3.87L44.146-0.911L38.129-0.911L38.129-9.887L35.17-9.887L35.17-12.846L32.112-12.846L32.112-9.887L29.153-9.887L29.153-6.829L26.095-6.829L26.095-3.87L29.153-3.87L29.153-0.911L23.136-0.911ZM44.146-57.819L47.105-57.819L47.105-60.877L44.146-60.877L44.146-57.819ZM5.19-9.887L17.12-9.887L17.12-11.364L5.19-11.364L5.19-9.887ZM47.105-9.887L71.068-9.887L71.068-11.364L47.105-11.364L47.105-9.887ZM14.166-3.87L20.178-3.87L20.178-5.352L14.166-5.352L14.166-3.87ZM5.19 0.665L11.107 0.665L11.107-0.911L5.19-0.911L5.19 0.665ZM66.632-0.911L68.109-0.911L68.109-2.388L66.632-2.388L66.632-0.911Z"/> + </g> + </g> +</svg>
diff --git a/ios/chrome/browser/ui/icons/symbol_names.h b/ios/chrome/browser/ui/icons/symbol_names.h index a5fe06a..96566ec 100644 --- a/ios/chrome/browser/ui/icons/symbol_names.h +++ b/ios/chrome/browser/ui/icons/symbol_names.h
@@ -49,6 +49,7 @@ extern NSString* const kShieldSymbol; extern NSString* const kCloudSlashSymbol; extern NSString* const kCloudAndArrowUpSymbol; +extern NSString* const kDinoSymbol; // Custom symbol names which can be configured with a color palette. iOS 15+ // only. @@ -94,6 +95,7 @@ extern NSString* const kCreditCardSymbol; extern NSString* const kMicrophoneFillSymbol; extern NSString* const kMicrophoneSymbol; +extern NSString* const kMagnifyingglassSymbol; extern NSString* const kEllipsisCircleFillSymbol; extern NSString* const kPinSlashSymbol; extern NSString* const kSettingsSymbol;
diff --git a/ios/chrome/browser/ui/icons/symbol_names.mm b/ios/chrome/browser/ui/icons/symbol_names.mm index 6ea8097..bed26e9 100644 --- a/ios/chrome/browser/ui/icons/symbol_names.mm +++ b/ios/chrome/browser/ui/icons/symbol_names.mm
@@ -46,6 +46,7 @@ NSString* const kShieldSymbol = @"shield"; NSString* const kCloudSlashSymbol = @"cloud_slash"; NSString* const kCloudAndArrowUpSymbol = @"cloud_and_arrow_up"; +NSString* const kDinoSymbol = @"dino"; // Custom symbol names which can be configured with a color palette. NSString* const kIncognitoCircleFillSymbol = @"incognito_circle_fill"; @@ -86,6 +87,7 @@ NSString* const kCreditCardSymbol = @"creditcard"; NSString* const kMicrophoneFillSymbol = @"mic.fill"; NSString* const kMicrophoneSymbol = @"mic"; +NSString* const kMagnifyingglassSymbol = @"magnifyingglass"; NSString* const kEllipsisCircleFillSymbol = @"ellipsis.circle.fill"; NSString* const kPinSlashSymbol = @"pin.slash"; NSString* const kSettingsSymbol = @"gearshape";
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn index 2333db2..3fb18af 100644 --- a/ios/chrome/browser/ui/location_bar/BUILD.gn +++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -28,6 +28,7 @@ "resources:location_bar_connection_info", "resources:location_bar_connection_offline", "resources:location_bar_connection_secure", + "resources:location_bar_magnifyingglass", "resources:location_bar_share", "resources:location_bar_voice", "//base",
diff --git a/ios/chrome/browser/ui/location_bar/resources/BUILD.gn b/ios/chrome/browser/ui/location_bar/resources/BUILD.gn index f44eacb0..955135e 100644 --- a/ios/chrome/browser/ui/location_bar/resources/BUILD.gn +++ b/ios/chrome/browser/ui/location_bar/resources/BUILD.gn
@@ -59,3 +59,11 @@ "location_bar_connection_info.imageset/location_bar_connection_info@3x.png", ] } + +imageset("location_bar_magnifyingglass") { + sources = [ + "location_bar_magnifyingglass.imageset/Contents.json", + "location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@2x.png", + "location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@3x.png", + ] +}
diff --git a/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/Contents.json b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/Contents.json new file mode 100644 index 0000000..7aa71c2 --- /dev/null +++ b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/Contents.json
@@ -0,0 +1,19 @@ +{ + "images": [ + { + "idiom": "universal", + "scale": "2x", + "filename": "location_bar_magnifyingglass@2x.png" + }, + { + "idiom": "universal", + "scale": "3x", + "filename": "location_bar_magnifyingglass@3x.png" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} +
diff --git a/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@2x.png b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@2x.png new file mode 100644 index 0000000..2e2b0c03 --- /dev/null +++ b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@2x.png Binary files differ
diff --git a/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@3x.png b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@3x.png new file mode 100644 index 0000000..733d36f53 --- /dev/null +++ b/ios/chrome/browser/ui/location_bar/resources/location_bar_magnifyingglass.imageset/location_bar_magnifyingglass@3x.png Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/metrics/BUILD.gn b/ios/chrome/browser/ui/ntp/metrics/BUILD.gn index f901c1f..8308bbf 100644 --- a/ios/chrome/browser/ui/ntp/metrics/BUILD.gn +++ b/ios/chrome/browser/ui/ntp/metrics/BUILD.gn
@@ -23,6 +23,7 @@ "//components/ntp_tiles", "//ios/chrome/browser/application_context", "//ios/chrome/browser/discover_feed:constants", + "//ios/chrome/browser/discover_feed:discover_feed_refresher", "//ios/chrome/browser/ntp:features", "//ios/chrome/browser/ui/content_suggestions:metrics", "//ios/chrome/browser/ui/favicon",
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.h b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.h index 48dba11..4fe8297 100644 --- a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.h +++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.h
@@ -11,6 +11,7 @@ #import "ios/chrome/browser/discover_feed/feed_constants.h" #import "ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h" +class DiscoverFeedRefresher; @protocol FeedControlDelegate; @protocol NewTabPageFollowDelegate; @@ -30,6 +31,9 @@ // Whether or not the feed is currently being shown on the Start Surface. @property(nonatomic, assign) BOOL isShownOnStartSurface; +// Object that can refresh the feed. +@property(nonatomic, assign) DiscoverFeedRefresher* feedRefresher; + // Records the trigger where a feed refresh is requested. + (void)recordFeedRefreshTrigger:(FeedRefreshTrigger)trigger;
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.mm b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.mm index 3ec6888f..fc7a611 100644 --- a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.mm +++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_recorder.mm
@@ -11,6 +11,7 @@ #import "base/metrics/user_metrics_action.h" #import "base/time/time.h" #import "components/feed/core/v2/public/common_enums.h" +#import "ios/chrome/browser/discover_feed/discover_feed_refresher.h" #import "ios/chrome/browser/ntp/features.h" #import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h" #import "ios/chrome/browser/ui/ntp/feed_control_delegate.h" @@ -70,6 +71,9 @@ @property(nonatomic, assign) NSTimeInterval discoverPreviousTimeInFeedGV; @property(nonatomic, assign) NSTimeInterval followingPreviousTimeInFeedGV; +// Timer to signal end of session. Set for `kMinutesBetweenSessions`. +@property(nonatomic, strong) NSTimer* sessionEndTimer; + @end @implementation FeedMetricsRecorder @@ -85,13 +89,16 @@ #pragma mark - Public +- (void)dealloc { + [self.sessionEndTimer invalidate]; + self.sessionEndTimer = nil; +} + + (void)recordFeedRefreshTrigger:(FeedRefreshTrigger)trigger { base::UmaHistogramEnumeration(kDiscoverFeedRefreshTrigger, trigger); } - (void)recordFeedScrolled:(int)scrollDistance { - [self recordEngagement:scrollDistance interacted:NO]; - if (IsGoodVisitsMetricEnabled()) { self.goodVisitScroll = YES; [self checkEngagementGoodVisitWithInteraction:NO]; @@ -118,6 +125,8 @@ FeedEngagementType::kFeedScrolled); self.scrolledReportedFollowing = YES; } + + [self recordEngagement:scrollDistance interacted:NO]; } - (void)recordDeviceOrientationChanged:(UIDeviceOrientation)orientation { @@ -137,6 +146,12 @@ } - (void)recordNTPDidChangeVisibility:(BOOL)visible { + // Invalidate the timer when the user returns to the feed since the feed + // should not be refreshed when the user is viewing it. + if (visible) { + [self.sessionEndTimer invalidate]; + } + if (!IsGoodVisitsMetricEnabled()) { return; } @@ -846,6 +861,13 @@ } [self.sessionRecorder recordUserInteractionOrScrolling]; + + // This must be called after memoizing if the current session has met + // engagement criteria. For example, setting `engagedSimpleReportedDiscover` + // must happen before this call. + if (IsFeedRefreshPostFeedSessionEnabled()) { + [self setOrExtendSessionEndTimer]; + } } // Checks if a Good Visit should be recorded. `interacted` is YES if it was @@ -1169,6 +1191,30 @@ } } +// Sets or extends the session end timer by `kMinutesBetweenSessions`. +- (void)setOrExtendSessionEndTimer { + [self.sessionEndTimer invalidate]; + __weak FeedMetricsRecorder* weakSelf = self; + self.sessionEndTimer = + [NSTimer scheduledTimerWithTimeInterval:kMinutesBetweenSessions * + 60 /*seconds per minute*/ + target:weakSelf + selector:@selector + (refreshFeedIfSessionConditionsAreMet) + userInfo:nil + repeats:NO]; +} + +// Refresh the feed if session conditions are met. See implementation for which +// specific conditions are used. +- (void)refreshFeedIfSessionConditionsAreMet { + [self.sessionEndTimer invalidate]; + self.sessionEndTimer = nil; + if (self.engagedSimpleReportedDiscover) { + self.feedRefresher->RefreshFeed(/*feed_visible=*/false); + } +} + #pragma mark - Converters // Converts a FollowingFeedSortType NSEnum into a FeedSortType enum.
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h index 705cb1d1..0eca0d7 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.h +++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.h
@@ -82,9 +82,14 @@ // Publisher. extern const char kFollowingFeedDefaultSortTypeGroupedByPublisher[]; -// A parameter value for the feed's refresh threshold. +// A parameter value for the feed's refresh threshold when the feed has already +// been seen by the user. extern const char kFeedSettingRefreshThresholdInSeconds[]; +// A parameter value for the feed's refresh threshold when the feed has not been +// seen by the user. +extern const char kFeedSettingUnseenRefreshThresholdInSeconds[]; + // A parameter value for the feed's maximum data cache age. extern const char kFeedSettingMaximumDataCacheAgeInSeconds[];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm index f36f91c..e6d81af 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_feature.mm
@@ -92,6 +92,8 @@ // Feature parameters for `kOverrideFeedSettings`. const char kFeedSettingRefreshThresholdInSeconds[] = "RefreshThresholdInSeconds"; +const char kFeedSettingUnseenRefreshThresholdInSeconds[] = + "UnseenRefreshThresholdInSeconds"; const char kFeedSettingMaximumDataCacheAgeInSeconds[] = "MaximumDataCacheAgeInSeconds"; const char kFeedSettingTimeoutThresholdAfterClearBrowsingData[] =
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h index 3e06665..5fe7594 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h +++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h
@@ -17,8 +17,6 @@ extern const CGFloat kScrolledToTopOmniboxBottomMargin; -extern const CGFloat kHintLabelSidePadding; - extern const CGFloat kHintLabelHeightMargin; // The margin added to the fake omnibox to have at the right position.
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm index 42e8fd2f..e296c68 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm
@@ -13,7 +13,6 @@ const CGFloat kAnimationDistance = 42; const CGFloat kFakeLocationBarTopConstraint = 4; const CGFloat kScrolledToTopOmniboxBottomMargin = 4; -const CGFloat kHintLabelSidePadding = 37; const CGFloat kHintLabelHeightMargin = 2; const CGFloat kMaxTopMarginDiff = 4; const CGFloat kFakeOmniboxScrolledToTopMargin = 14;
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn index 249921b5..0068095a 100644 --- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn +++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -83,6 +83,7 @@ "//ios/chrome/browser/ui/default_promo:utils", "//ios/chrome/browser/ui/favicon", "//ios/chrome/browser/ui/icons:symbols", + "//ios/chrome/browser/ui/icons:symbols_views", "//ios/chrome/browser/ui/main:default_browser_scene_agent", "//ios/chrome/browser/ui/main:layout_guide_util", "//ios/chrome/browser/ui/main:scene_state_header", @@ -109,6 +110,7 @@ "//ios/chrome/common/ui/colors", "//ios/chrome/common/ui/favicon", "//ios/chrome/common/ui/util", + "//ios/chrome/common/ui/util:image_util", "//ios/web/public:public", "//net", "//ui/base",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm index b0a2648..67524e96 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm
@@ -13,8 +13,12 @@ #import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/commands/omnibox_commands.h" #import "ios/chrome/browser/ui/commands/open_new_tab_command.h" +#import "ios/chrome/browser/ui/icons/colorful_background_symbol_view.h" +#import "ios/chrome/browser/ui/icons/symbols.h" #import "ios/chrome/browser/ui/omnibox/popup/popup_swift.h" #import "ios/chrome/browser/url/chrome_url_constants.h" +#import "ios/chrome/common/ui/colors/semantic_color_names.h" +#import "ios/chrome/common/ui/util/image_util.h" #import "ios/chrome/grit/ios_strings.h" #import "ui/base/l10n/l10n_util.h" @@ -58,7 +62,11 @@ NSInteger pedalType = static_cast<NSInteger>( static_cast<const OmniboxPedal*>(pedalAction)->GetMetricsId()); - switch (static_cast<OmniboxPedalId>(pedalAction->GetID())) { + OmniboxPedalId pedalId = static_cast<OmniboxPedalId>(pedalAction->GetID()); + + UIImage* image = [self pedalIconForPedalId:pedalId incognito:incognito]; + + switch (pedalId) { case OmniboxPedalId::PLAY_CHROME_DINO_GAME: { NSString* urlStr = [NSString stringWithFormat:@"%s://%s", kChromeUIScheme, kChromeUIDinoHost]; @@ -67,9 +75,8 @@ initWithTitle:hint subtitle:urlStr accessibilityHint:suggestionContents - imageName:@"pedal_dino" + image:image type:pedalType - incognito:incognito action:^{ OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:url @@ -84,9 +91,8 @@ l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_CLEAR_BROWSING_DATA) accessibilityHint:suggestionContents - imageName:@"pedal_clear_browsing_data" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint showClearBrowsingDataSettings]; @@ -106,9 +112,8 @@ subtitle:l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_DEFAULT_BROWSER) accessibilityHint:suggestionContents - imageName:@"pedal_default_browser" + image:image type:pedalType - incognito:incognito action:action]; } case OmniboxPedalId::MANAGE_PASSWORDS: { @@ -117,9 +122,8 @@ subtitle:l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_MANAGE_PASSWORDS) accessibilityHint:suggestionContents - imageName:@"pedal_passwords" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint @@ -134,9 +138,8 @@ l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_UPDATE_CREDIT_CARD) accessibilityHint:suggestionContents - imageName:@"pedal_payments" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint showCreditCardSettings]; @@ -148,9 +151,8 @@ subtitle:l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_LAUNCH_INCOGNITO) accessibilityHint:suggestionContents - imageName:@"pedal_incognito" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint @@ -165,9 +167,8 @@ initWithTitle:hint subtitle:subtitle accessibilityHint:suggestionContents - imageName:@"pedal_safety_check" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint @@ -181,9 +182,8 @@ initWithTitle:hint subtitle:subtitle accessibilityHint:suggestionContents - imageName:@"pedal_settings" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint showSettingsFromViewController:nil]; @@ -196,17 +196,151 @@ l10n_util::GetNSString( IDS_IOS_OMNIBOX_PEDAL_SUBTITLE_VIEW_CHROME_HISTORY) accessibilityHint:suggestionContents - imageName:@"pedal_history" + image:image type:pedalType - incognito:incognito action:^{ [omniboxCommandHandler cancelOmniboxEdit]; [pedalsEndpoint showHistory]; }]; } + // If a new case is added here, make sure to update the method returning + // the icon. default: return nil; } } +#pragma mark - Private + +// Returns the image associated with `pedalId`, for `incognito` or not. +- (UIImage*)pedalIconForPedalId:(OmniboxPedalId)pedalId + incognito:(BOOL)incognito { + ColorfulBackgroundSymbolView* symbolView = + [[ColorfulBackgroundSymbolView alloc] init]; + if (incognito) { + // Dark mode is set explicitly if incognito is enabled. + symbolView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark; + } + + // Dark mode is set explicitly if incognito is enabled. + UITraitCollection* traitCollection = + [UITraitCollection traitCollectionWithUserInterfaceStyle: + incognito ? UIUserInterfaceStyleDark + : UIUserInterfaceStyleUnspecified]; + + switch (pedalId) { + case OmniboxPedalId::PLAY_CHROME_DINO_GAME: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbol:CustomSymbolWithPointSize(kDinoSymbol, 22)]; + symbolView.backgroundColor = UIColor.whiteColor; + [symbolView setSymbolTintColor:UIColor.blackColor]; + symbolView.borderColor = [UIColor colorNamed:kGrey200Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_dino" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::CLEAR_BROWSING_DATA: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kTrashSymbol systemSymbol:YES]; + symbolView.backgroundColor = [UIColor colorNamed:kBlue500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_clear_browsing_data" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::SET_CHROME_AS_DEFAULT_BROWSER: { + if (UseSymbolsInOmnibox()) { +#if BUILDFLAG(IOS_USE_BRANDED_SYMBOLS) + symbolView.backgroundColor = UIColor.whiteColor; + [symbolView setSymbol:MakeSymbolMulticolor(CustomSymbolWithPointSize( + kGoogleIconSymbol, 22))]; + symbolView.borderColor = [UIColor colorNamed:kGrey200Color]; +#else + [symbolView setSymbolName:kDefaultBrowserSymbol systemSymbol:YES]; + symbolView.backgroundColor = [UIColor colorNamed:kPurple500Color]; +#endif // BUILDFLAG(IOS_USE_BRANDED_SYMBOLS) + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_default_browser" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::MANAGE_PASSWORDS: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kPasswordSymbol systemSymbol:NO]; + symbolView.backgroundColor = [UIColor colorNamed:kYellow500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_passwords" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::UPDATE_CREDIT_CARD: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kCreditCardSymbol systemSymbol:YES]; + symbolView.backgroundColor = [UIColor colorNamed:kYellow500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_payments" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::LAUNCH_INCOGNITO: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kIncognitoSymbol systemSymbol:NO]; + symbolView.backgroundColor = [UIColor colorNamed:kGrey800Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_incognito" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::RUN_CHROME_SAFETY_CHECK: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kSafetyCheckSymbol systemSymbol:NO]; + symbolView.backgroundColor = [UIColor colorNamed:kBlue500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_safety_check" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::MANAGE_CHROME_SETTINGS: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kSettingsSymbol systemSymbol:YES]; + symbolView.backgroundColor = [UIColor colorNamed:kGrey500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_settings" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + case OmniboxPedalId::VIEW_CHROME_HISTORY: { + if (UseSymbolsInOmnibox()) { + [symbolView setSymbolName:kHistorySymbol systemSymbol:YES]; + symbolView.backgroundColor = [UIColor colorNamed:kBlue500Color]; + return ImageFromView(symbolView, nil, UIEdgeInsetsZero); + } else { + return [UIImage imageNamed:@"pedal_history" + inBundle:nil + compatibleWithTraitCollection:traitCollection]; + } + } + default: + NOTREACHED(); + return nil; + } +} + @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/omnibox_pedal.swift b/ios/chrome/browser/ui/omnibox/popup/shared/omnibox_pedal.swift index 302ca03..c4ecef4 100644 --- a/ios/chrome/browser/ui/omnibox/popup/shared/omnibox_pedal.swift +++ b/ios/chrome/browser/ui/omnibox/popup/shared/omnibox_pedal.swift
@@ -13,10 +13,8 @@ public let subtitle: String /// Describes the action performed; can be used for voiceover. public let hint: String - /// Name of the image in the bundle. - public let imageName: String - /// Whether the pedal is displayed from an incognito session. - public let incognito: Bool + /// The image for the Pedal. + public let image: UIImage /// Action to run when the pedal is executed. public let action: () -> Void /// Action type for metrics collection. Int-casted OmniboxPedalId @@ -24,15 +22,14 @@ public init( title: String, subtitle: String, - accessibilityHint: String, imageName: String, type: Int, incognito: Bool, + accessibilityHint: String, image: UIImage, type: Int, action: @escaping () -> Void ) { self.title = title self.subtitle = subtitle self.hint = accessibilityHint - self.imageName = imageName + self.image = image self.type = type - self.incognito = incognito self.action = action } } @@ -44,12 +41,7 @@ } public var iconImage: UIImage? { - // Dark mode is set explicitly if incognito is enabled. - let userInterfaceStyle = - UITraitCollection(userInterfaceStyle: incognito ? .dark : .unspecified) - return UIImage( - named: self.imageName, in: nil, - compatibleWith: UITraitCollection(traitsFrom: [.current, userInterfaceStyle])) + return image } public var imageURL: CrURL? { return nil }
diff --git a/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.h b/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.h index 3c93e5fc..eaa5e1d 100644 --- a/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.h +++ b/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.h
@@ -18,6 +18,8 @@ // TrustedVaultClientBackend implementation. void AddObserver(Observer* observer) final; void RemoveObserver(Observer* observer) final; + void SetDeviceRegistrationPublicKeyVerifierForUMA( + VerifierCallback verifier) final; void FetchKeys(id<SystemIdentity> identity, KeyFetchedCallback callback) final; void MarkLocalKeysAsStale(id<SystemIdentity> identity, @@ -32,6 +34,8 @@ UIViewController* presenting_view_controller, CompletionBlock callback) final; void CancelDialog(BOOL animated, ProceduralBlock callback) final; + void ClearLocalData(id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback) final; // Simulates user cancelling the reauth dialog. void SimulateUserCancel();
diff --git a/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.mm b/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.mm index a9429f6f..a6ce15ae 100644 --- a/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.mm +++ b/ios/chrome/test/providers/signin/fake_trusted_vault_client_backend.mm
@@ -72,6 +72,11 @@ // Do nothing. } +void FakeTrustedVaultClientBackend:: + SetDeviceRegistrationPublicKeyVerifierForUMA(VerifierCallback verifier) { + // Do nothing. +} + void FakeTrustedVaultClientBackend::FetchKeys(id<SystemIdentity> identity, KeyFetchedCallback callback) { // Do nothing. @@ -108,6 +113,12 @@ // Do nothing. } +void FakeTrustedVaultClientBackend::ClearLocalData( + id<SystemIdentity> identity, + base::OnceCallback<void(bool)> callback) { + // Do nothing. +} + void FakeTrustedVaultClientBackend::CancelDialog(BOOL animated, ProceduralBlock callback) { DCHECK(view_controller_);
diff --git a/ios/web/js_features/context_menu/resources/all_frames_context_menu.js b/ios/web/js_features/context_menu/resources/all_frames_context_menu.js index 9fd3b6e..e16bfd46 100644 --- a/ios/web/js_features/context_menu/resources/all_frames_context_menu.js +++ b/ios/web/js_features/context_menu/resources/all_frames_context_menu.js
@@ -124,9 +124,17 @@ result.innerText = textNode.nodeValue; if (extractSurroundingText) { - var textAndStartPos = getSurroundingText(range); - result.surroundingText = textAndStartPos['text']; - result.surroundingTextOffset = textAndStartPos['pos']; + // TODO(crbug.com/1416910): getSurroundingText throws an exception + // when a node is null, a fix is planned to remove the exception + // handling. + try { + var textAndStartPos = getSurroundingText(range); + result.surroundingText = textAndStartPos['text']; + result.surroundingTextOffset = textAndStartPos['pos']; + } catch (err) { + result.surroundingText = ''; + result.surroundingTextOffset = 0; + } } } }
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm index 5b87c306..71f4de9 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.mm +++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -1863,10 +1863,6 @@ [[CRWErrorPageHelper alloc] initWithError:error]; WKBackForwardListItem* backForwardItem = webView.backForwardList.currentItem; GURL backForwardGURL = net::GURLWithNSURL(backForwardItem.URL); - if (web::wk_navigation_util::IsRestoreSessionUrl(backForwardGURL)) { - web::wk_navigation_util::ExtractTargetURL(backForwardGURL, - &backForwardGURL); - } GURL failedURL = [CRWErrorPageHelper failedNavigationURLFromErrorPageFileURL:backForwardGURL]; bool isSameURLFromWebClient = web::GetWebClient()->IsPointingToSameDocument( @@ -1875,20 +1871,21 @@ // 1. Current nav item is an error page for failed URL; // 2. Current nav item has a failed URL. This may happen when // back/forward/refresh on a loaded page; - // 3. Current nav item is a session restoration of the failed navigation. - // 4. Current nav item is an irrelevant page. - // For 1, 2 and 3, load an empty string to remove existing JS code. The URL is + // 3. Current nav item is an irrelevant page. + // 4. Current nav item is a session restoration. + // For 1, 2 and 4, load an empty string to remove existing JS code. The URL is // also updated to the URL of the page that failed to allow back/forward // navigations even on navigations originating from pushstate. See // crbug.com/1153261. - // For 4, load error page file to create a new nav item. + // For 3, load error page file to create a new nav item. // The actual error HTML will be loaded in didFinishNavigation callback. WKNavigation* errorNavigation = nil; if (provisionalLoad && ![errorPage isErrorPageFileURLForFailedNavigationURL:backForwardItem.URL] && !isSameURLFromWebClient && - backForwardGURL != net::GURLWithNSURL(errorPage.failedNavigationURL)) { + ![backForwardItem.URL isEqual:errorPage.failedNavigationURL] && + !web::wk_navigation_util::IsRestoreSessionUrl(backForwardItem.URL)) { errorNavigation = [webView loadFileURL:errorPage.errorPageFileURL allowingReadAccessToURL:errorPage.errorPageFileURL]; } else {
diff --git a/ios/web/web_state/error_page_inttest.mm b/ios/web/web_state/error_page_inttest.mm index 23e9f68..7939681 100644 --- a/ios/web/web_state/error_page_inttest.mm +++ b/ios/web/web_state/error_page_inttest.mm
@@ -599,53 +599,4 @@ restored_web_state->GetNavigationManager()->GetForwardItems().size()); } -// Tests that navigating to a page that fails during the provisional load while -// being on a session restoration page create a new navigation. -TEST_F(ErrorPageTest, LoadFailingPageAfterRestoration) { - server_responds_with_content_ = true; - - test::LoadUrl(web_state(), server_.GetURL("/echo-query?foo")); - ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo")); - - // Restore the session. - WebState::CreateParams params(GetBrowserState()); - auto restored_web_state = WebState::CreateWithStorageSession( - params, web_state()->BuildSessionStorage()); - NavigationManager* navigation_manager = - restored_web_state->GetNavigationManager(); - - navigation_manager->LoadIfNecessary(); - - ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo")); - // Check that there is no back/forward item. - ASSERT_EQ(0UL, navigation_manager->GetBackwardItems().size()); - ASSERT_EQ(0UL, navigation_manager->GetForwardItems().size()); - - // Load invalid URL (failing during the load). - std::string scheme = kTestWebUIScheme; - GURL invalid_webui = GURL(scheme + "://invalid"); - test::LoadUrl(restored_web_state.get(), invalid_webui); - - NSError* error = testing::CreateErrorWithUnderlyingErrorChain( - {{@"NSURLErrorDomain", NSURLErrorUnsupportedURL}, - {net::kNSErrorDomain, net::ERR_INVALID_URL}}); - ASSERT_TRUE(test::WaitForWebViewContainingText( - restored_web_state.get(), - testing::GetErrorText(restored_web_state.get(), invalid_webui, error, - /*is_post=*/false, /*is_otr=*/false, - /*cert_status=*/0))); - - // Check that there is one item in the back list and no forward item. - EXPECT_EQ(1UL, navigation_manager->GetBackwardItems().size()); - EXPECT_EQ(0UL, navigation_manager->GetForwardItems().size()); - - navigation_manager->GoBack(); - ASSERT_TRUE( - test::WaitForWebViewContainingText(restored_web_state.get(), "foo")); - - // Check that there is one item in the forward list and no back item. - EXPECT_EQ(0UL, navigation_manager->GetBackwardItems().size()); - EXPECT_EQ(1UL, navigation_manager->GetForwardItems().size()); -} - } // namespace web
diff --git a/media/mojo/services/stable_video_decoder_service.cc b/media/mojo/services/stable_video_decoder_service.cc index 450d23e..3811f553 100644 --- a/media/mojo/services/stable_video_decoder_service.cc +++ b/media/mojo/services/stable_video_decoder_service.cc
@@ -176,7 +176,6 @@ // The mojo traits have been coded assuming these conditions. CHECK(frame->metadata().allow_overlay); CHECK(!frame->metadata().end_of_stream); - CHECK(frame->metadata().read_lock_fences_enabled); CHECK(frame->metadata().power_efficient); stable_video_decoder_client_remote_->OnVideoFrameDecoded(
diff --git a/media/mojo/services/stable_video_decoder_service_unittest.cc b/media/mojo/services/stable_video_decoder_service_unittest.cc index 5e7e8a7..a65dcc51 100644 --- a/media/mojo/services/stable_video_decoder_service_unittest.cc +++ b/media/mojo/services/stable_video_decoder_service_unittest.cc
@@ -787,6 +787,9 @@ video_frame_to_send, kCanReadWithoutStalling, token_for_release); auxiliary_endpoints->video_decoder_client_remote.FlushForTesting(); ASSERT_TRUE(video_frame_received); + EXPECT_FALSE(video_frame_received->metadata().end_of_stream); + EXPECT_TRUE(video_frame_received->metadata().read_lock_fences_enabled); + EXPECT_TRUE(video_frame_received->metadata().power_efficient); EXPECT_TRUE(video_frame_received->metadata().allow_overlay); }
diff --git a/net/BUILD.gn b/net/BUILD.gn index 4e2aa15..d7ee4190 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -315,6 +315,8 @@ "cert/internal/revocation_checker.h", "cert/internal/system_trust_store.cc", "cert/internal/system_trust_store.h", + "cert/internal/trust_store_features.cc", + "cert/internal/trust_store_features.h", "cert/known_roots.cc", "cert/known_roots.h", "cert/merkle_audit_proof.cc",
diff --git a/net/cert/internal/system_trust_store_nss_unittest.cc b/net/cert/internal/system_trust_store_nss_unittest.cc index 926c1e9d..96ae434 100644 --- a/net/cert/internal/system_trust_store_nss_unittest.cc +++ b/net/cert/internal/system_trust_store_nss_unittest.cc
@@ -13,6 +13,7 @@ #include "crypto/scoped_nss_types.h" #include "crypto/scoped_test_nss_db.h" #include "net/cert/internal/system_trust_store_nss.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/test_root_certs.h" @@ -100,6 +101,28 @@ // Tests that SystemTrustStore created for NSS with a user-slot restriction // allows certificates stored on the specified user slot to be trusted. TEST_F(SystemTrustStoreNSSTest, UserSlotRestrictionAllows) { + ScopedLocalAnchorConstraintsEnforcementForTesting + scoped_enforce_local_anchor_constraints(true); + std::unique_ptr<SystemTrustStore> system_trust_store = + CreateSslSystemTrustStoreNSSWithUserSlotRestriction( + crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot()))); + + ASSERT_NO_FATAL_FAILURE(ImportRootCertAsTrusted(test_nssdb_.slot())); + + CertificateTrust trust = + system_trust_store->GetTrustStore()->GetTrust(parsed_root_cert_.get(), + /*debug_data=*/nullptr); + EXPECT_EQ(CertificateTrust::ForTrustAnchor() + .WithEnforceAnchorConstraints() + .WithEnforceAnchorExpiry() + .ToDebugString(), + trust.ToDebugString()); +} + +TEST_F(SystemTrustStoreNSSTest, + UserSlotRestrictionAllowsWithAnchorConstraintsDisabled) { + ScopedLocalAnchorConstraintsEnforcementForTesting + scoped_enforce_local_anchor_constraints(false); std::unique_ptr<SystemTrustStore> system_trust_store = CreateSslSystemTrustStoreNSSWithUserSlotRestriction( crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())));
diff --git a/net/cert/internal/trust_store_features.cc b/net/cert/internal/trust_store_features.cc new file mode 100644 index 0000000..a86231f --- /dev/null +++ b/net/cert/internal/trust_store_features.cc
@@ -0,0 +1,38 @@ +// 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. + +#include "net/cert/internal/trust_store_features.h" + +#include <atomic> + +#include "base/no_destructor.h" + +namespace net { + +namespace { +std::atomic_bool* GetLocalAnchorConstraintsEnforcementFlag() { + static std::atomic_bool flag = base::FeatureList::IsEnabled( + net::features::kEnforceLocalAnchorConstraints); + return &flag; +} + +} // namespace + +bool IsLocalAnchorConstraintsEnforcementEnabled() { + return GetLocalAnchorConstraintsEnforcementFlag()->load(); +} + +void SetLocalAnchorConstraintsEnforcementEnabled(bool enabled) { + GetLocalAnchorConstraintsEnforcementFlag()->store(enabled); +} + +namespace features { + +BASE_FEATURE(kEnforceLocalAnchorConstraints, + "EnforceLocalAnchorConstraints", + base::FEATURE_ENABLED_BY_DEFAULT); + +} // namespace features + +} // namespace net
diff --git a/net/cert/internal/trust_store_features.h b/net/cert/internal/trust_store_features.h new file mode 100644 index 0000000..02156a07 --- /dev/null +++ b/net/cert/internal/trust_store_features.h
@@ -0,0 +1,60 @@ +// 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. + +#ifndef NET_CERT_INTERNAL_TRUST_STORE_FEATURES_H_ +#define NET_CERT_INTERNAL_TRUST_STORE_FEATURES_H_ + +#include "base/feature_list.h" +#include "net/base/net_export.h" + +namespace net { + +// Returns true when platform TrustStore implementations should enforce +// constraints encoded into X.509 certificate trust anchors. +// When disabled, platform TrustStore implementations will not enforce anchor +// constraints (other than expiry). +// Has no effect if using a platform CertVerifyProc implementation. +// TODO(https://crbug.com/1406103): remove this a few milestones after the +// trust anchor constraints enforcement has been launched on all relevant +// platforms. +// Should only be called after base::Features have been resolved. Note that +// using ScopedFeatureList to override this won't work properly in unittests, +// use ScopedLocalAnchorConstraintsEnforcementForTesting instead. Using +// ScopedFeatureList in browser_tests is fine. +// It is safe to call this function on any thread. +NET_EXPORT bool IsLocalAnchorConstraintsEnforcementEnabled(); + +// Override the feature flag. Don't call this without consulting +// net/cert/OWNERS. +// It is safe to call this function on any thread. +NET_EXPORT void SetLocalAnchorConstraintsEnforcementEnabled(bool enabled); + +// Temporarily change the SetLocalAnchorConstraintsEnforcementEnabled value, +// resetting to the original value when destructed. +class NET_EXPORT ScopedLocalAnchorConstraintsEnforcementForTesting { + public: + explicit ScopedLocalAnchorConstraintsEnforcementForTesting(bool enabled) + : previous_value_(IsLocalAnchorConstraintsEnforcementEnabled()) { + SetLocalAnchorConstraintsEnforcementEnabled(enabled); + } + + ~ScopedLocalAnchorConstraintsEnforcementForTesting() { + SetLocalAnchorConstraintsEnforcementEnabled(previous_value_); + } + + private: + const bool previous_value_; +}; + +namespace features { + +// Most code should not check this feature flag directly, instead use +// IsLocalAnchorConstraintsEnforcementEnabled(). +NET_EXPORT BASE_DECLARE_FEATURE(kEnforceLocalAnchorConstraints); + +} // namespace features + +} // namespace net + +#endif // NET_CERT_INTERNAL_TRUST_STORE_FEATURES_H_
diff --git a/net/cert/internal/trust_store_mac.cc b/net/cert/internal/trust_store_mac.cc index ef8912a..1094656 100644 --- a/net/cert/internal/trust_store_mac.cc +++ b/net/cert/internal/trust_store_mac.cc
@@ -24,6 +24,7 @@ #include "net/base/features.h" #include "net/base/hash_value.h" #include "net/base/network_notification_thread_mac.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/cert_issuer_source_static.h" #include "net/cert/pki/extended_key_usage.h" @@ -1160,17 +1161,24 @@ base::SupportsUserData* debug_data) { TrustStatus trust_status = trust_cache_->IsCertTrusted(cert, debug_data); switch (trust_status) { - case TrustStatus::TRUSTED: + case TrustStatus::TRUSTED: { + CertificateTrust trust; if (base::FeatureList::IsEnabled( features::kTrustStoreTrustedLeafSupport)) { // Mac trust settings don't distinguish between trusted anchors and // trusted leafs, return a trust record valid for both, which will // depend on the context the certificate is encountered in. - return CertificateTrust::ForTrustAnchorOrLeaf() - .WithEnforceAnchorExpiry(); + trust = + CertificateTrust::ForTrustAnchorOrLeaf().WithEnforceAnchorExpiry(); } else { - return CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); + trust = CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); } + if (IsLocalAnchorConstraintsEnforcementEnabled()) { + trust = trust.WithEnforceAnchorConstraints() + .WithRequireAnchorBasicConstraints(); + } + return trust; + } case TrustStatus::DISTRUSTED: return CertificateTrust::ForDistrusted(); case TrustStatus::UNSPECIFIED:
diff --git a/net/cert/internal/trust_store_mac_unittest.cc b/net/cert/internal/trust_store_mac_unittest.cc index 9929e80..54bde0e 100644 --- a/net/cert/internal/trust_store_mac_unittest.cc +++ b/net/cert/internal/trust_store_mac_unittest.cc
@@ -22,10 +22,12 @@ #include "crypto/mac_security_services_lock.h" #include "crypto/sha2.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/pem.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/pki/test_helpers.h" +#include "net/cert/pki/trust_store.h" #include "net/cert/test_keychain_search_list_mac.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" @@ -121,10 +123,12 @@ class TrustStoreMacImplTest : public testing::TestWithParam< - std::tuple<TrustStoreMac::TrustImplType, bool>> { + std::tuple<TrustStoreMac::TrustImplType, bool, bool>> { public: - TrustStoreMacImplTest() { - if (std::get<1>(GetParam())) { + TrustStoreMacImplTest() + : scoped_enforce_local_anchor_constraints_( + ExpectedEnforceLocalAnchorConstraintsEnabled()) { + if (ExpectedTrustedLeafSupportEnabled()) { feature_list_.InitAndEnableFeature( features::kTrustStoreTrustedLeafSupport); } else { @@ -137,16 +141,36 @@ return std::get<0>(GetParam()); } + bool ExpectedTrustedLeafSupportEnabled() const { + return std::get<1>(GetParam()); + } + + bool ExpectedEnforceLocalAnchorConstraintsEnabled() const { + return std::get<2>(GetParam()); + } + CertificateTrust ExpectedTrustForAnchor() const { - if (std::get<1>(GetParam())) { - return CertificateTrust::ForTrustAnchorOrLeaf().WithEnforceAnchorExpiry(); + CertificateTrust trust; + + if (ExpectedTrustedLeafSupportEnabled()) { + trust = + CertificateTrust::ForTrustAnchorOrLeaf().WithEnforceAnchorExpiry(); } else { - return CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); + trust = CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); } + + if (ExpectedEnforceLocalAnchorConstraintsEnabled()) { + trust = trust.WithEnforceAnchorConstraints() + .WithRequireAnchorBasicConstraints(); + } + + return trust; } private: base::test::ScopedFeatureList feature_list_; + ScopedLocalAnchorConstraintsEnforcementForTesting + scoped_enforce_local_anchor_constraints_; }; // Much of the Keychain API was marked deprecated as of the macOS 13 SDK. @@ -451,11 +475,14 @@ testing::Values(TrustStoreMac::TrustImplType::kSimple, TrustStoreMac::TrustImplType::kDomainCacheFullCerts, TrustStoreMac::TrustImplType::kKeychainCacheFullCerts), - testing::Values(true, false)), + testing::Bool(), + testing::Bool()), [](const testing::TestParamInfo<TrustStoreMacImplTest::ParamType>& info) { - return base::StrCat({TrustImplTypeToString(std::get<0>(info.param)), - std::get<1>(info.param) ? "TrustedLeafSupported" - : "TrustAnchorOnly"}); + return base::StrCat( + {TrustImplTypeToString(std::get<0>(info.param)), + std::get<1>(info.param) ? "TrustedLeafSupported" : "TrustAnchorOnly", + std::get<2>(info.param) ? "EnforceLocalAnchorConstraints" + : "NoLocalAnchorConstraints"}); }); } // namespace net
diff --git a/net/cert/internal/trust_store_nss.cc b/net/cert/internal/trust_store_nss.cc index 4cf9659..53d8113e 100644 --- a/net/cert/internal/trust_store_nss.cc +++ b/net/cert/internal/trust_store_nss.cc
@@ -10,6 +10,7 @@ #include "base/logging.h" #include "crypto/nss_util.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/known_roots_nss.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/parsed_certificate.h" @@ -108,6 +109,8 @@ bool is_trusted_ca = false; bool is_trusted_leaf = false; + bool enforce_anchor_constraints = + IsLocalAnchorConstraintsEnforcementEnabled(); // Determine if the certificate is a trust anchor. // @@ -123,8 +126,20 @@ // objects from the builtin token (system trust settings). Properly // handling this may require iterating all the slots and manually computing // the trust settings directly, rather than CERT_GetCertTrust. - if (!ignore_system_trust_settings_ || !IsKnownRoot(nss_cert.get())) { + if (ignore_system_trust_settings_) { + // Only trust the user roots, and apply the value of + // enforce_anchor_constraints. + if (!IsKnownRoot(nss_cert.get())) { + is_trusted_ca = true; + } + } else { is_trusted_ca = true; + if (enforce_anchor_constraints && IsKnownRoot(nss_cert.get())) { + // Don't enforce anchor constraints on the builtin roots. Needing to + // check IsKnownRoot for this condition isn't ideal, but this should be + // good enough for now. + enforce_anchor_constraints = false; + } } } @@ -137,9 +152,13 @@ } if (is_trusted_ca && is_trusted_leaf) { - return CertificateTrust::ForTrustAnchorOrLeaf(); + return CertificateTrust::ForTrustAnchorOrLeaf() + .WithEnforceAnchorConstraints(enforce_anchor_constraints) + .WithEnforceAnchorExpiry(enforce_anchor_constraints); } else if (is_trusted_ca) { - return CertificateTrust::ForTrustAnchor(); + return CertificateTrust::ForTrustAnchor() + .WithEnforceAnchorConstraints(enforce_anchor_constraints) + .WithEnforceAnchorExpiry(enforce_anchor_constraints); } else if (is_trusted_leaf) { return CertificateTrust::ForTrustedLeaf(); }
diff --git a/net/cert/internal/trust_store_nss_unittest.cc b/net/cert/internal/trust_store_nss_unittest.cc index 6df62e4..1919c82 100644 --- a/net/cert/internal/trust_store_nss_unittest.cc +++ b/net/cert/internal/trust_store_nss_unittest.cc
@@ -15,10 +15,12 @@ #include "crypto/nss_util_internal.h" #include "crypto/scoped_test_nss_db.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/known_roots_nss.h" #include "net/cert/pki/cert_issuer_source_sync_unittest.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/pki/test_helpers.h" +#include "net/cert/pki/trust_store.h" #include "net/cert/scoped_nss_types.h" #include "net/cert/test_root_certs.h" #include "net/cert/x509_util.h" @@ -30,10 +32,6 @@ namespace { -constexpr CertificateTrust ExpectedTrustForAnchor() { - return CertificateTrust::ForTrustAnchor(); -} - // Returns true if the provided slot looks like a built-in root. bool IsBuiltInRootSlot(PK11SlotInfo* slot) { if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) @@ -105,8 +103,12 @@ class TrustStoreNSSTestBase : public ::testing::Test { public: - explicit TrustStoreNSSTestBase(bool trusted_leaf_support) - : trusted_leaf_support_(trusted_leaf_support) { + explicit TrustStoreNSSTestBase(bool trusted_leaf_support, + bool enforce_local_anchor_constraints) + : trusted_leaf_support_(trusted_leaf_support), + enforce_local_anchor_constraints_(enforce_local_anchor_constraints), + scoped_enforce_local_anchor_constraints_( + enforce_local_anchor_constraints) { if (trusted_leaf_support) { feature_list_.InitAndEnableFeature( features::kTrustStoreTrustedLeafSupport); @@ -116,20 +118,43 @@ } } - TrustStoreNSSTestBase() : TrustStoreNSSTestBase(true) {} + TrustStoreNSSTestBase() : TrustStoreNSSTestBase(true, true) {} - bool IsTrustedLeafSupportEnabled() const { return trusted_leaf_support_; } + bool ExpectedTrustedLeafSupportEnabled() const { + return trusted_leaf_support_; + } + + bool ExpectedEnforceLocalAnchorConstraintsEnabled() const { + return enforce_local_anchor_constraints_; + } + + CertificateTrust ExpectedTrustForBuiltinAnchor() const { + return CertificateTrust::ForTrustAnchor(); + } + + CertificateTrust ExpectedTrustForAnchor() const { + CertificateTrust trust = CertificateTrust::ForTrustAnchor(); + if (ExpectedEnforceLocalAnchorConstraintsEnabled()) { + trust = trust.WithEnforceAnchorConstraints().WithEnforceAnchorExpiry(); + } + return trust; + } CertificateTrust ExpectedTrustForAnchorOrLeaf() const { - if (IsTrustedLeafSupportEnabled()) { - return CertificateTrust::ForTrustAnchorOrLeaf(); + CertificateTrust trust; + if (ExpectedTrustedLeafSupportEnabled()) { + trust = CertificateTrust::ForTrustAnchorOrLeaf(); } else { - return CertificateTrust::ForTrustAnchor(); + trust = CertificateTrust::ForTrustAnchor(); } + if (ExpectedEnforceLocalAnchorConstraintsEnabled()) { + trust = trust.WithEnforceAnchorConstraints().WithEnforceAnchorExpiry(); + } + return trust; } CertificateTrust ExpectedTrustForLeaf() const { - if (IsTrustedLeafSupportEnabled()) { + if (ExpectedTrustedLeafSupportEnabled()) { return CertificateTrust::ForTrustedLeaf(); } else { return CertificateTrust::ForUnspecified(); @@ -313,6 +338,9 @@ base::test::ScopedFeatureList feature_list_; const bool trusted_leaf_support_; + const bool enforce_local_anchor_constraints_; + ScopedLocalAnchorConstraintsEnforcementForTesting + scoped_enforce_local_anchor_constraints_; std::shared_ptr<const ParsedCertificate> oldroot_; std::shared_ptr<const ParsedCertificate> newroot_; @@ -386,7 +414,7 @@ TEST_P(TrustStoreNSSTestWithSlotFilterType, TrustAllowedForBuiltinRootCerts) { auto builtin_root_cert = GetASSLTrustedBuiltinRoot(); ASSERT_TRUE(builtin_root_cert); - EXPECT_TRUE(HasTrust({builtin_root_cert}, ExpectedTrustForAnchor())); + EXPECT_TRUE(HasTrust({builtin_root_cert}, ExpectedTrustForBuiltinAnchor())); } INSTANTIATE_TEST_SUITE_P( @@ -399,9 +427,11 @@ // Tests a TrustStoreNSS that ignores system root certs. class TrustStoreNSSTestIgnoreSystemCerts : public TrustStoreNSSTestBase, - public testing::WithParamInterface<bool> { + public testing::WithParamInterface<std::tuple<bool, bool>> { public: - TrustStoreNSSTestIgnoreSystemCerts() : TrustStoreNSSTestBase(GetParam()) {} + TrustStoreNSSTestIgnoreSystemCerts() + : TrustStoreNSSTestBase(std::get<0>(GetParam()), + std::get<1>(GetParam())) {} ~TrustStoreNSSTestIgnoreSystemCerts() override = default; std::unique_ptr<TrustStoreNSS> CreateTrustStoreNSS() override { @@ -445,18 +475,23 @@ INSTANTIATE_TEST_SUITE_P( All, TrustStoreNSSTestIgnoreSystemCerts, - testing::Values(true, false), + testing::Combine(testing::Bool(), testing::Bool()), [](const testing::TestParamInfo< TrustStoreNSSTestIgnoreSystemCerts::ParamType>& info) { - return info.param ? "TrustedLeafSupported" : "TrustAnchorOnly"; + return std::string(std::get<0>(info.param) ? "TrustedLeafSupported" + : "TrustAnchorOnly") + + (std::get<1>(info.param) ? "EnforceLocalAnchorConstraints" + : "NoLocalAnchorConstraints"); }); // Tests a TrustStoreNSS that does not filter which certificates class TrustStoreNSSTestWithoutSlotFilter : public TrustStoreNSSTestBase, - public testing::WithParamInterface<bool> { + public testing::WithParamInterface<std::tuple<bool, bool>> { public: - TrustStoreNSSTestWithoutSlotFilter() : TrustStoreNSSTestBase(GetParam()) {} + TrustStoreNSSTestWithoutSlotFilter() + : TrustStoreNSSTestBase(std::get<0>(GetParam()), + std::get<1>(GetParam())) {} ~TrustStoreNSSTestWithoutSlotFilter() override = default; @@ -577,10 +612,13 @@ INSTANTIATE_TEST_SUITE_P( All, TrustStoreNSSTestWithoutSlotFilter, - testing::Values(true, false), + testing::Combine(testing::Bool(), testing::Bool()), [](const testing::TestParamInfo< TrustStoreNSSTestWithoutSlotFilter::ParamType>& info) { - return info.param ? "TrustedLeafSupported" : "TrustAnchorOnly"; + return std::string(std::get<0>(info.param) ? "TrustedLeafSupported" + : "TrustAnchorOnly") + + (std::get<1>(info.param) ? "EnforceLocalAnchorConstraints" + : "NoLocalAnchorConstraints"); }); // Tests for a TrustStoreNSS which does not allow certificates on user slots
diff --git a/net/cert/internal/trust_store_win.cc b/net/cert/internal/trust_store_win.cc index 1cb8d2d..ab059b7 100644 --- a/net/cert/internal/trust_store_win.cc +++ b/net/cert/internal/trust_store_win.cc
@@ -12,6 +12,7 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/scoped_blocking_call.h" #include "net/base/features.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/x509_util.h" @@ -329,9 +330,14 @@ // anchors or trusted leafs (if self-signed). return CertificateTrust::ForTrustAnchorOrLeaf() .WithEnforceAnchorExpiry() + .WithEnforceAnchorConstraints( + IsLocalAnchorConstraintsEnforcementEnabled()) .WithRequireLeafSelfSigned(); } else { - return CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); + return CertificateTrust::ForTrustAnchor() + .WithEnforceAnchorExpiry() + .WithEnforceAnchorConstraints( + IsLocalAnchorConstraintsEnforcementEnabled()); } } }
diff --git a/net/cert/internal/trust_store_win_unittest.cc b/net/cert/internal/trust_store_win_unittest.cc index 015d0189..284cfcd 100644 --- a/net/cert/internal/trust_store_win_unittest.cc +++ b/net/cert/internal/trust_store_win_unittest.cc
@@ -15,6 +15,7 @@ #include "crypto/scoped_capi_types.h" #include "net/base/features.h" #include "net/cert/cert_net_fetcher.h" +#include "net/cert/internal/trust_store_features.h" #include "net/cert/pki/cert_errors.h" #include "net/cert/pki/parsed_certificate.h" #include "net/cert/pki/test_helpers.h" @@ -52,10 +53,13 @@ return ::testing::AssertionSuccess(); } -class TrustStoreWinTest : public testing::TestWithParam<bool> { +class TrustStoreWinTest + : public testing::TestWithParam<std::tuple<bool, bool>> { public: - TrustStoreWinTest() { - if (IsTrustedLeafSupportEnabled()) { + TrustStoreWinTest() + : scoped_enforce_local_anchor_constraints_( + ExpectedEnforceLocalAnchorConstraintsEnabled()) { + if (ExpectedTrustedLeafSupportEnabled()) { feature_list_.InitAndEnableFeature( features::kTrustStoreTrustedLeafSupport); } else { @@ -75,20 +79,31 @@ ASSERT_TRUE(ParseCertFromFile("multi-root-F-by-E.pem", &f_by_e_)); } - bool IsTrustedLeafSupportEnabled() const { return GetParam(); } + bool ExpectedTrustedLeafSupportEnabled() const { + return std::get<0>(GetParam()); + } + + bool ExpectedEnforceLocalAnchorConstraintsEnabled() const { + return std::get<1>(GetParam()); + } CertificateTrust ExpectedTrustForAnchor() const { - if (IsTrustedLeafSupportEnabled()) { + if (ExpectedTrustedLeafSupportEnabled()) { return CertificateTrust::ForTrustAnchorOrLeaf() .WithEnforceAnchorExpiry() + .WithEnforceAnchorConstraints( + ExpectedEnforceLocalAnchorConstraintsEnabled()) .WithRequireLeafSelfSigned(); } else { - return CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry(); + return CertificateTrust::ForTrustAnchor() + .WithEnforceAnchorExpiry() + .WithEnforceAnchorConstraints( + ExpectedEnforceLocalAnchorConstraintsEnabled()); } } CertificateTrust ExpectedTrustForPeer() const { - if (IsTrustedLeafSupportEnabled()) { + if (ExpectedTrustedLeafSupportEnabled()) { return CertificateTrust::ForTrustedLeaf().WithRequireLeafSelfSigned(); } else { return CertificateTrust::ForUnspecified(); @@ -143,6 +158,8 @@ private: base::test::ScopedFeatureList feature_list_; + ScopedLocalAnchorConstraintsEnforcementForTesting + scoped_enforce_local_anchor_constraints_; }; TEST_P(TrustStoreWinTest, GetTrustInitializationError) { @@ -387,9 +404,12 @@ INSTANTIATE_TEST_SUITE_P( All, TrustStoreWinTest, - testing::Values(true, false), + testing::Combine(testing::Bool(), testing::Bool()), [](const testing::TestParamInfo<TrustStoreWinTest::ParamType>& info) { - return info.param ? "TrustedLeafSupported" : "TrustAnchorOnly"; + return std::string(std::get<0>(info.param) ? "TrustedLeafSupported" + : "TrustAnchorOnly") + + (std::get<1>(info.param) ? "EnforceLocalAnchorConstraints" + : "NoLocalAnchorConstraints"); }); } // namespace
diff --git a/net/data/ssl/certificates/crit-codeSigning-chain.pem b/net/data/ssl/certificates/crit-codeSigning-chain.pem index 857ba47..55d1e8c 100644 --- a/net/data/ssl/certificates/crit-codeSigning-chain.pem +++ b/net/data/ssl/certificates/crit-codeSigning-chain.pem
@@ -1,62 +1,63 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEArb3ubpJlolhcRduBpP7VEoyb0BbLuEdRBEN+WeuuUmFCUjcE -srXy9HtlfRRNk0fPFr931k1gmjuoqzz2zJLEd3KitsRo9itnimi2u27pIUr2C912 -4Gp4zq1tkXTQXiNID7VMpNdkpLeVSNHhgtNzZ7BjffOi3guU4SF74DuSwtorXnmE -CVcxh/TDPwniccahq8vnk3fsE2/eWMmPC2WJTKmMPtaoGis3M6S+gsuvQqnTfe7v -0IH8noDf56XB3+Efkz6NoxcAdPWkcvFG0IcwptEWkfbj3qMsmbuXEFJjbiDLFQGP -7gn1Qoa7xd5JfT7FNjPugCLwCntaxagE4gn6mQIDAQABAoIBAE67HMraaFfy9o1p -dQxRtjhbo1fenJ57Islt1FnnJO2LyUP8TpK1RM1pBYpyoty007EZPrLMCZSaqEpA -rA30DDecqQNtaarz7E2aOgQhtF8z2t9xkicL2Ia/rEEX0Dx4fHUx0PN57898qZeA -FFL2gvtfwRYYS5uXX/XM+JeaJ11IRCZTJCRkaoK6Ffyu0pi0yCg/WS852XlAuTWp -sguJRydFtt2xW8qF9Ua8mVVCVEo4olaopvGECu1cauj3Oxydk0vLc9lABPRQrS3u -0nTdovZiuk0EPy4s8pyFTo9isqSLq79HWi4v/ByXeCLRp6ZpjR7s3s/3SEyG2NG/ -6nMtmiECgYEA5uWN232qNv0hGgQLzTUPwk8VyGbpSAoETYyT8nwc7o4ZS/tghvXI -4JFxyQFEoMxeMmVWS8FgN6DGnDjjXQGD40ZO6UkwFX48E07jE8sh767cCL80BO36 -q8DSdicOUoYWvQ6WJw/RUNggIFzOZhjKlBw7Yjf5/buRkkOAJ3xk3nUCgYEAwKGd -Es0UjNU6/iYtNOlYlAxKQS8d+EwnxelWbBY6h89Uquqnt36+Bgv8ceDMx6JwjZGq -b6olv4f3HrmVwighEWUiiccniEuMx/50XInISHfHZV8OsQ9p5bUI0vHo3XNsegbg -JdeQqxC88JeCTrq7Fm09wKIZ8ft6WR0+DSAzbxUCgYAddei1usD/JykUErQWyNBr -8H9NBKR7Rpvp8SfnZqKiZYsgwMA+OBobXTNxfDHvemQCdh+eptvJ/T+aK0AHW+wi -EZR7+5ShCWxM4mHi4qY/2MXGb+8JOfwj8gRogu825Fj+YmASN9hzQkBHINBNApjG -cRu6mn3RPB+E1AwD/cE5CQKBgFDZA8XKUR+ytunIOB0G+uhYKConjlqSC/disaT3 -x2UMvapmhjHbfgGnsjJReWEoajjgtDndna4/cJZyqcotcYONgOt2rL7lhpbB0zCr -m2Xe7886ED58C6QfUS7H3UZklVi53gXD7bH+em44CLbmZHNLMinRXzZSp80TGuID -a9LpAoGAUjwIqx5+5xS5uTScPShuxTVh5kTWMhBLVRyh/evzmb9ALk6fUyid8Xm3 -q5E6l49iRsxHFbW2EQ9COp9CtZ3tgJbsivyeMazjlwpZhj1VotzrIHOSN0bup0KE -qX9WYiR6GU99/vFY8BKOBkVI6AdOfkPm5aWnMUzpceBtJHCJQdQ= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDo54gDQT1qecev +bDsUAnrPdTnIFp0Da4tLIr+UCTyfMoF5ZEVfH7PDaS5pydczRr3O5SR7+VUNOaQc +nU3E2IWgb0tD/AeI6x6Q/rjFMM7DsNvHjvzuX9xjhwGeM5KVNb9jv59g7TmwYvoF +y6QUwEpV+gySaAfGQi0GsviEIyZR3kWqzalHk8EetSsEql7BEVjeAEoz5raMLukF +qfJ9lOVkvzPMUXQVEROlCzWePHe7lXkep9LUNIdguu92Qt2458BUgNR703RsJxy3 +HU0dz1pe83Pu4MrQk65Di851HAOYykkZ0NugmWFZn4zHit3KDJZJZ4cBiaq8ftgh +xPvpH2m1AgMBAAECggEAATjJG+r0Wmlf1MOlZbIhLEQil0XvZ3jXxGuj0s2khllu +SZtReuyxsJzI/fiLD/h0nK/6gyKZmjOBU3lByP6KQYkrUQcaGfC90x3nSCp3zksV +a3bu6g+Hri7BA+tBukH4CmnOW9Dt2qmcG/CFNC+958iPr6FmKLv8hkC937Uf9cL6 +nD29wvDuzRibDRV/yDk/dhbc2QTpDp8kkjDBI/Hpibj6GeQra5YDDafqs78w0jQh +jOl6+E4sn79+X4ZccOCXfgcXsa8gZcskIXdVGhtWnYGWdxcyXDhJrZzKr2tEhzAw +QFhpHjlGL9l6P/lDyn0qMxp9IEFLcWJk2YTrcaV4wQKBgQD4VLNJj+l2x4O7qGpb +X5GRYLBCqI76iXLxUaEQJxZpd5qngWKaSJyS9IO82k7fcCgsbhQQnXC59UIlUQ6A +YLPO4Nj/D6V8ewttCIbiRtddTZBj9wIHJFhOfcRoDBSTskhaJ4kVB1Nh+YchQf+8 +P6/wwkTcn9FRohdeHikYDnpqIQKBgQDwGN6pGMbrnIKv/+HHC55QPshUYjKIjU98 +Bd7TU3Kj8tSpxiKKXY9RtkAfzoSrgPzu5SJ3bSgXBXz+xOtN9b9z5beQHJyufHaO +NaVQ4ZStMjo3afXMNj7UUd9ZbFuH2K+K1FjXy99Ab5wN/u5snRiErh2KS/w3mxze +6kOComAVFQKBgQC7vP0WHhB4VfmHg5l0ntmkOJ7IpjoBuqwFOJs1ZPeSoHNxM2Xi +EgcdKnH18m0yis40WLwem4g/beWl5JO8Bl+phV9H5QJNC5Dly059/uSOizcf+/uy +fo2sOXSk3I0p49zDG6SNG060gTrhr82w+cz/jT8WNFTBDHPyGYcjwr5VQQKBgEHU +f9BbU8csHYUGIrCBlgGohSLl3bclD6MQtPy6R5d+MCLwiW3oozAjSUevRx8C+dbC +ioW2LyTIw3HTKjUw6TJszLy9q5QH2jW5rb8UasBmIiIpclRwlx9950BMfngryE3H +VSit5GN1dpM7z8GF/T/7wWu208unQu43yxTZUoDVAoGBANzJZijbb2JYdvDSi5aX +Akv5droXiJ9DG7wew7xGJcGRdmpRw+mas2j5hdIXwfKeN7yr8KFaYxv/DDqlTsv+ +sZPysvWc4TZ8NrNOL9VyAD/3QCjo9uBdd2rSssYMKHI8RXD+Jiold59AW3oMDwVk +OW8+EuyR+eoq9HueaKbjlJ5O +-----END PRIVATE KEY----- Certificate: Data: - Version: 1 (0x0) + Version: 3 (0x2) Serial Number: 2 (0x2) - Signature Algorithm: sha256WithRSAEncryption + Signature Algorithm: sha256WithRSAEncryption Issuer: CN=2048 RSA Test Root CA Validity - Not Before: Aug 14 02:46:31 2014 GMT - Not After : Aug 11 02:46:31 2024 GMT + Not Before: Feb 17 17:08:51 2023 GMT + Not After : Feb 14 17:08:51 2033 GMT Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:ad:bd:ee:6e:92:65:a2:58:5c:45:db:81:a4:fe: - d5:12:8c:9b:d0:16:cb:b8:47:51:04:43:7e:59:eb: - ae:52:61:42:52:37:04:b2:b5:f2:f4:7b:65:7d:14: - 4d:93:47:cf:16:bf:77:d6:4d:60:9a:3b:a8:ab:3c: - f6:cc:92:c4:77:72:a2:b6:c4:68:f6:2b:67:8a:68: - b6:bb:6e:e9:21:4a:f6:0b:dd:76:e0:6a:78:ce:ad: - 6d:91:74:d0:5e:23:48:0f:b5:4c:a4:d7:64:a4:b7: - 95:48:d1:e1:82:d3:73:67:b0:63:7d:f3:a2:de:0b: - 94:e1:21:7b:e0:3b:92:c2:da:2b:5e:79:84:09:57: - 31:87:f4:c3:3f:09:e2:71:c6:a1:ab:cb:e7:93:77: - ec:13:6f:de:58:c9:8f:0b:65:89:4c:a9:8c:3e:d6: - a8:1a:2b:37:33:a4:be:82:cb:af:42:a9:d3:7d:ee: - ef:d0:81:fc:9e:80:df:e7:a5:c1:df:e1:1f:93:3e: - 8d:a3:17:00:74:f5:a4:72:f1:46:d0:87:30:a6:d1: - 16:91:f6:e3:de:a3:2c:99:bb:97:10:52:63:6e:20: - cb:15:01:8f:ee:09:f5:42:86:bb:c5:de:49:7d:3e: - c5:36:33:ee:80:22:f0:0a:7b:5a:c5:a8:04:e2:09: - fa:99 + 00:e8:e7:88:03:41:3d:6a:79:c7:af:6c:3b:14:02: + 7a:cf:75:39:c8:16:9d:03:6b:8b:4b:22:bf:94:09: + 3c:9f:32:81:79:64:45:5f:1f:b3:c3:69:2e:69:c9: + d7:33:46:bd:ce:e5:24:7b:f9:55:0d:39:a4:1c:9d: + 4d:c4:d8:85:a0:6f:4b:43:fc:07:88:eb:1e:90:fe: + b8:c5:30:ce:c3:b0:db:c7:8e:fc:ee:5f:dc:63:87: + 01:9e:33:92:95:35:bf:63:bf:9f:60:ed:39:b0:62: + fa:05:cb:a4:14:c0:4a:55:fa:0c:92:68:07:c6:42: + 2d:06:b2:f8:84:23:26:51:de:45:aa:cd:a9:47:93: + c1:1e:b5:2b:04:aa:5e:c1:11:58:de:00:4a:33:e6: + b6:8c:2e:e9:05:a9:f2:7d:94:e5:64:bf:33:cc:51: + 74:15:11:13:a5:0b:35:9e:3c:77:bb:95:79:1e:a7: + d2:d4:34:87:60:ba:ef:76:42:dd:b8:e7:c0:54:80: + d4:7b:d3:74:6c:27:1c:b7:1d:4d:1d:cf:5a:5e:f3: + 73:ee:e0:ca:d0:93:ae:43:8b:ce:75:1c:03:98:ca: + 49:19:d0:db:a0:99:61:59:9f:8c:c7:8a:dd:ca:0c: + 96:49:67:87:01:89:aa:bc:7e:d8:21:c4:fb:e9:1f: + 69:b5 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Alternative Name: @@ -64,42 +65,46 @@ X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: - C8:14:20:CB:A7:D1:AC:A1:7F:27:3E:72:47:1E:AF:50:EB:DF:CD:73 + CB:86:F3:FC:03:8D:82:4B:C6:63:57:8A:E6:9F:86:55:10:D9:00:B7 X509v3 Extended Key Usage: critical Code Signing + X509v3 Authority Key Identifier: + D5:28:55:87:C7:A3:BF:D7:C4:CE:BE:3D:01:D2:BE:8B:7C:E4:E2:E2 Signature Algorithm: sha256WithRSAEncryption - 18:5e:81:7e:31:f7:38:78:d2:2a:08:7f:57:60:a0:e1:b3:72: - 7e:7f:8a:6f:76:bd:cb:56:74:14:9a:65:96:0e:99:1b:59:90: - 4b:f1:d9:10:89:0a:5c:75:f3:01:1b:50:a5:00:b7:34:0f:d5: - 5a:24:df:10:79:83:17:9b:82:bf:66:b0:4f:eb:53:d1:0b:8c: - 01:b8:57:58:4f:31:f6:66:f3:89:ba:02:9c:fb:a4:1c:f8:e6: - 5d:14:44:13:d9:74:be:b3:1a:1d:6b:35:77:b7:08:28:d3:60: - 9f:ac:e5:72:57:20:03:2f:ce:5c:10:15:da:45:cd:d8:09:66: - 4c:a2:44:cb:9a:bf:b3:2e:1b:44:36:62:2f:e2:64:8f:72:b8: - da:9a:fb:ef:f5:0e:04:39:d1:2d:9a:1e:bd:48:56:90:d3:3c: - f4:d5:89:07:9f:f6:a6:c4:d3:ff:d6:fe:67:b8:29:f8:93:36: - e0:4d:69:a3:1a:38:a0:ce:39:02:63:73:3d:dd:13:98:80:36: - d0:1e:a0:23:55:8d:59:3f:dd:12:3c:ee:af:f7:a3:e4:f2:e3: - 57:b4:f5:f3:2a:e5:60:70:c1:88:89:f0:06:6d:de:c0:b1:cf: - a5:65:cc:a0:d3:41:f6:fb:4e:94:ba:17:0a:60:59:bb:ac:18: - d7:f6:9c:52 + Signature Value: + 64:ff:92:1d:66:c5:2b:6c:b8:d6:82:39:80:8e:ef:55:cd:bb: + 6f:1c:16:20:a5:6b:90:fa:f3:20:c7:37:0d:7d:4f:da:f5:1f: + 9c:dc:28:3e:39:56:a7:17:eb:3b:09:53:d7:1e:1b:a1:eb:24: + f7:7f:04:38:77:e1:e4:39:60:bf:24:13:37:05:8b:9f:36:94: + 91:e4:fc:43:97:d6:0e:11:cb:ee:a9:f9:c1:05:6e:2f:ea:76: + af:f5:69:ae:06:97:5a:2d:97:0b:cd:3a:1d:01:30:26:ac:da: + f6:03:e4:df:32:08:64:81:ea:36:85:0c:03:41:6a:ef:10:6b: + 2a:a4:cb:f6:59:9b:bf:fa:1b:5a:9e:05:33:b0:54:30:d1:79: + dd:77:6b:e0:c8:be:52:37:58:7e:b0:53:23:fe:62:ce:5c:bb: + b5:06:85:18:e0:08:08:cd:48:bc:1d:d7:cb:92:55:33:57:fe: + 42:4a:81:0d:d3:ee:02:0a:e1:4b:d2:f1:de:81:5e:bb:fa:8b: + 9f:3f:ec:0a:1a:30:0b:de:15:d3:75:5e:11:f8:a9:7e:4f:7d: + 40:03:06:e4:2f:3b:e5:5e:ff:42:f5:e6:99:02:f3:26:c0:f7: + e2:b2:62:53:d3:e2:5b:2a:17:e3:78:5c:84:cc:14:bf:fa:0d: + ac:d7:d0:49 -----BEGIN CERTIFICATE----- -MIIDTjCCAjYCAQIwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVMjA0OCBSU0Eg -VGVzdCBSb290IENBMB4XDTE0MDgxNDAyNDYzMVoXDTI0MDgxMTAyNDYzMVowYDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 -YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExEjAQBgNVBAMMCTEyNy4wLjAuMTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK297m6SZaJYXEXbgaT+1RKM -m9AWy7hHUQRDflnrrlJhQlI3BLK18vR7ZX0UTZNHzxa/d9ZNYJo7qKs89sySxHdy -orbEaPYrZ4potrtu6SFK9gvdduBqeM6tbZF00F4jSA+1TKTXZKS3lUjR4YLTc2ew -Y33zot4LlOEhe+A7ksLaK155hAlXMYf0wz8J4nHGoavL55N37BNv3ljJjwtliUyp -jD7WqBorNzOkvoLLr0Kp033u79CB/J6A3+elwd/hH5M+jaMXAHT1pHLxRtCHMKbR -FpH2496jLJm7lxBSY24gyxUBj+4J9UKGu8XeSX0+xTYz7oAi8Ap7WsWoBOIJ+pkC -AwEAAaNYMFYwDwYDVR0RBAgwBocEfwAAATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW -BBTIFCDLp9GsoX8nPnJHHq9Q69/NczAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAN -BgkqhkiG9w0BAQsFAAOCAQEAGF6BfjH3OHjSKgh/V2Cg4bNyfn+Kb3a9y1Z0FJpl -lg6ZG1mQS/HZEIkKXHXzARtQpQC3NA/VWiTfEHmDF5uCv2awT+tT0QuMAbhXWE8x -9mbziboCnPukHPjmXRREE9l0vrMaHWs1d7cIKNNgn6zlclcgAy/OXBAV2kXN2Alm -TKJEy5q/sy4bRDZiL+Jkj3K42pr77/UOBDnRLZoevUhWkNM89NWJB5/2psTT/9b+ -Z7gp+JM24E1poxo4oM45AmNzPd0TmIA20B6gI1WNWT/dEjzur/ej5PLjV7T18yrl -YHDBiInwBm3ewLHPpWXMoNNB9vtOlLoXCmBZu6wY1/acUg== +MIIDdDCCAlygAwIBAgIBAjANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBUyMDQ4 +IFJTQSBUZXN0IFJvb3QgQ0EwHhcNMjMwMjE3MTcwODUxWhcNMzMwMjE0MTcwODUx +WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN +TW91bnRhaW4gVmlldzEQMA4GA1UECgwHVGVzdCBDQTESMBAGA1UEAwwJMTI3LjAu +MC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6OeIA0E9annHr2w7 +FAJ6z3U5yBadA2uLSyK/lAk8nzKBeWRFXx+zw2kuacnXM0a9zuUke/lVDTmkHJ1N +xNiFoG9LQ/wHiOsekP64xTDOw7Dbx4787l/cY4cBnjOSlTW/Y7+fYO05sGL6Bcuk +FMBKVfoMkmgHxkItBrL4hCMmUd5Fqs2pR5PBHrUrBKpewRFY3gBKM+a2jC7pBany +fZTlZL8zzFF0FRETpQs1njx3u5V5HqfS1DSHYLrvdkLduOfAVIDUe9N0bCcctx1N +Hc9aXvNz7uDK0JOuQ4vOdRwDmMpJGdDboJlhWZ+Mx4rdygyWSWeHAYmqvH7YIcT7 +6R9ptQIDAQABo3kwdzAPBgNVHREECDAGhwR/AAABMAwGA1UdEwEB/wQCMAAwHQYD +VR0OBBYEFMuG8/wDjYJLxmNXiuafhlUQ2QC3MBYGA1UdJQEB/wQMMAoGCCsGAQUF +BwMDMB8GA1UdIwQYMBaAFNUoVYfHo7/XxM6+PQHSvot85OLiMA0GCSqGSIb3DQEB +CwUAA4IBAQBk/5IdZsUrbLjWgjmAju9VzbtvHBYgpWuQ+vMgxzcNfU/a9R+c3Cg+ +OVanF+s7CVPXHhuh6yT3fwQ4d+HkOWC/JBM3BYufNpSR5PxDl9YOEcvuqfnBBW4v +6nav9WmuBpdaLZcLzTodATAmrNr2A+TfMghkgeo2hQwDQWrvEGsqpMv2WZu/+hta +ngUzsFQw0Xndd2vgyL5SN1h+sFMj/mLOXLu1BoUY4AgIzUi8HdfLklUzV/5CSoEN +0+4CCuFL0vHegV67+oufP+wKGjAL3hXTdV4R+Kl+T31AAwbkLzvlXv9C9eaZAvMm +wPfismJT0+JbKhfjeFyEzBS/+g2s19BJ -----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/eku-test-root.pem b/net/data/ssl/certificates/eku-test-root.pem index 8b71c8c..c233e40 100644 --- a/net/data/ssl/certificates/eku-test-root.pem +++ b/net/data/ssl/certificates/eku-test-root.pem
@@ -1,75 +1,77 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 16790907397325450812 (0xe9054950da0fee3c) - Signature Algorithm: sha1WithRSAEncryption - Issuer: CN=2048 RSA Test Root CA + Serial Number: + 55:72:d4:d8:57:fc:9c:cf:51:b8:6c:e7:a7:d1:a6:f3:b3:f1:25:fa + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = 2048 RSA Test Root CA Validity - Not Before: Aug 14 02:46:30 2014 GMT - Not After : Aug 11 02:46:30 2024 GMT - Subject: CN=2048 RSA Test Root CA + Not Before: Feb 17 17:08:50 2023 GMT + Not After : Feb 14 17:08:50 2033 GMT + Subject: CN = 2048 RSA Test Root CA Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:9c:22:79:a9:f8:68:98:41:fc:84:fb:f3:1f:73: - 0f:e0:70:35:c6:04:bb:8d:c0:48:ca:2a:f1:d3:16: - 3b:8a:71:77:68:ba:c1:48:3c:77:12:ef:4c:d1:1d: - da:c1:98:dd:6d:46:50:b6:d7:a3:f3:7e:dc:8c:6b: - d2:db:05:bb:77:55:bf:bd:a6:f0:d8:9d:ff:2c:04: - 1e:24:25:c2:0b:b8:e7:42:a1:c2:42:83:70:16:f1: - 4a:ef:c0:68:d8:81:f4:41:51:dd:70:31:5a:5f:47: - 6a:55:a8:3e:53:9a:71:ef:8e:d4:a6:79:8f:e4:a2: - 79:34:d7:ad:b1:07:0f:21:90:a7:ff:12:c2:9d:8f: - 26:19:66:c5:34:15:c9:d9:3f:9a:62:9f:37:bb:79: - 1e:9e:24:16:a6:01:28:c6:99:54:75:91:f3:c2:5a: - 87:7d:99:9e:23:f0:e0:f2:97:50:b4:3f:99:21:e7: - b1:c4:87:58:5f:ba:9a:a5:4b:39:38:64:4b:ba:b8: - 67:fe:dd:c8:96:19:8a:7d:7c:44:fe:ff:39:15:61: - fb:7e:fa:e7:43:18:5a:cf:96:1d:f4:ff:9d:26:68: - 27:c8:67:80:9a:ca:a3:f5:45:5f:fa:93:6c:65:53: - e3:e8:84:c1:39:7c:e5:01:61:01:99:12:fd:02:5f: - 26:27 + 00:d0:bc:d5:52:0a:01:4c:04:65:37:00:73:17:af: + 1a:53:d5:0a:b1:9c:c0:5b:20:ff:c0:b4:bd:f2:d4: + b2:1d:2c:a5:c3:d7:eb:68:b7:f7:ab:2d:b3:6c:00: + c2:7a:b0:f7:5e:b1:8b:29:92:79:08:37:f6:ec:46: + 2d:66:29:7c:66:8c:d4:54:f0:35:4d:ad:1e:e6:ef: + 4f:43:9d:96:2f:78:49:66:eb:ea:4f:b9:e9:eb:27: + 33:6e:03:1f:3b:c1:e4:6d:bb:a9:68:62:1c:fd:78: + 0d:57:f2:58:89:16:4f:8d:1d:2d:94:2a:ea:b8:1a: + 1b:57:b8:ad:8f:67:44:23:04:7f:c9:1a:1c:4a:f3: + 19:15:1a:39:a6:fc:c9:17:ae:5e:40:97:e2:b3:ec: + 02:4c:1b:65:0e:99:6a:d4:fe:c7:04:56:14:0c:8e: + 0f:ee:e8:fb:4e:63:c4:cf:4a:36:ac:f5:a6:f7:1d: + 26:07:e9:33:a9:f3:31:ee:1d:27:fc:15:10:a5:83: + 7e:59:26:6e:24:32:87:af:19:31:6c:99:24:05:d9: + 55:71:03:b8:b3:b6:75:f8:ee:04:ff:04:7f:b0:b6: + 57:ad:24:e3:a6:f9:46:2c:f7:b5:53:31:d6:49:c6: + c3:1e:7f:a4:f0:6d:47:32:c9:8a:ff:f3:b6:7b:fd: + d3:7f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:TRUE X509v3 Subject Key Identifier: - 51:48:E1:39:42:1C:AC:B2:1D:E5:45:A1:D1:79:16:32:81:F8:FE:5E + D5:28:55:87:C7:A3:BF:D7:C4:CE:BE:3D:01:D2:BE:8B:7C:E4:E2:E2 X509v3 Key Usage: critical Certificate Sign, CRL Sign - Signature Algorithm: sha1WithRSAEncryption - 2f:65:1c:d8:d7:7d:2d:3d:a6:21:e6:18:cb:c5:2c:d1:b7:61: - 77:f6:dc:8e:c0:18:7b:55:f8:ad:09:43:47:c9:2a:c5:00:80: - ad:9c:61:77:d2:00:27:c2:2f:af:d8:60:30:a1:e1:0c:c3:f0: - fb:32:5d:33:be:0a:44:d7:fc:59:9e:18:58:9e:82:56:0a:84: - 64:8f:a6:15:a8:e8:7f:6e:7d:97:b0:7a:df:25:3c:98:ce:d8: - b6:de:72:3d:d9:a5:71:53:be:a6:a1:9d:f9:6c:10:16:ef:49: - 39:5c:58:0d:b7:40:63:6e:b9:71:41:5a:66:6e:fe:89:70:7b: - e5:6f:98:96:64:43:fe:37:90:c6:49:ba:1b:84:58:da:bc:31: - bd:c1:cd:ee:1a:53:62:44:d1:61:99:b9:06:43:2b:d4:75:4c: - 7f:af:6e:d3:2f:44:75:95:2c:0d:65:5e:a5:e8:d5:0d:9e:89: - cc:2c:0a:5e:96:4b:0a:95:56:bc:18:98:14:d2:6f:64:76:ff: - e6:9e:ce:ec:66:cb:26:fa:f3:8c:a4:e6:82:fb:24:cb:4d:38: - 9d:54:7b:12:0c:15:63:c0:75:4a:aa:cd:ac:6b:3e:86:d0:ab: - 2e:9c:48:55:e2:8d:9e:b4:fa:eb:e0:7f:a1:b5:d6:4c:13:9c: - 01:5a:03:1b + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 17:28:d3:4e:0f:70:db:86:b8:08:e2:0d:92:d1:4c:f6:32:70: + 52:6b:3f:1f:96:a0:bd:a4:d4:f8:ed:b5:a4:9e:0a:ef:45:09: + e4:1f:8a:af:50:2d:03:4e:1c:ed:46:71:3d:91:d6:a9:0f:8f: + bb:4f:38:a4:3c:18:2f:ff:1f:07:9e:17:06:a6:9a:f9:4d:01: + f6:79:c9:aa:fe:01:90:79:dd:d3:eb:6d:ea:0b:b9:6c:df:9c: + 1f:4f:31:25:70:71:58:9c:62:6a:5a:85:7d:8f:20:ae:97:d4: + e0:69:8a:79:4d:48:34:fe:c0:99:98:c3:33:ce:f3:07:80:c2: + 4b:95:a3:2e:ba:cb:ee:d7:4d:a3:e1:88:9e:71:8b:64:47:83: + 02:02:72:b0:46:ca:4f:e3:b1:f4:2c:f1:65:58:09:7a:81:18: + b7:35:55:50:6a:37:0a:0f:87:d3:d2:b2:2f:6f:93:e1:7d:e2: + ca:50:1f:a9:a4:6d:bc:40:1a:6e:d4:55:5e:99:e0:9c:77:7c: + b0:06:d3:7b:0b:cb:a2:9a:3a:9a:06:b6:59:3d:50:d5:d0:7b: + e0:51:c4:88:38:24:ba:80:3c:f0:42:07:24:c3:5c:e2:b5:6e: + bf:8b:37:b1:df:e9:7e:6e:33:d6:b2:af:d5:51:62:eb:b0:5a: + 52:54:52:a3 -----BEGIN CERTIFICATE----- -MIIDBTCCAe2gAwIBAgIJAOkFSVDaD+48MA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV -BAMMFTIwNDggUlNBIFRlc3QgUm9vdCBDQTAeFw0xNDA4MTQwMjQ2MzBaFw0yNDA4 -MTEwMjQ2MzBaMCAxHjAcBgNVBAMMFTIwNDggUlNBIFRlc3QgUm9vdCBDQTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJwiean4aJhB/IT78x9zD+BwNcYE -u43ASMoq8dMWO4pxd2i6wUg8dxLvTNEd2sGY3W1GULbXo/N+3Ixr0tsFu3dVv72m -8Nid/ywEHiQlwgu450KhwkKDcBbxSu/AaNiB9EFR3XAxWl9HalWoPlOace+O1KZ5 -j+SieTTXrbEHDyGQp/8Swp2PJhlmxTQVydk/mmKfN7t5Hp4kFqYBKMaZVHWR88Ja -h32ZniPw4PKXULQ/mSHnscSHWF+6mqVLOThkS7q4Z/7dyJYZin18RP7/ORVh+376 -50MYWs+WHfT/nSZoJ8hngJrKo/VFX/qTbGVT4+iEwTl85QFhAZkS/QJfJicCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUUUjhOUIcrLId5UWh0XkW -MoH4/l4wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAvZRzY130t -PaYh5hjLxSzRt2F39tyOwBh7VfitCUNHySrFAICtnGF30gAnwi+v2GAwoeEMw/D7 -Ml0zvgpE1/xZnhhYnoJWCoRkj6YVqOh/bn2XsHrfJTyYzti23nI92aVxU76moZ35 -bBAW70k5XFgNt0BjbrlxQVpmbv6JcHvlb5iWZEP+N5DGSbobhFjavDG9wc3uGlNi -RNFhmbkGQyvUdUx/r27TL0R1lSwNZV6l6NUNnonMLApelksKlVa8GJgU0m9kdv/m -ns7sZssm+vOMpOaC+yTLTTidVHsSDBVjwHVKqs2saz6G0KsunEhV4o2etPrr4H+h -tdZME5wBWgMb +MIIDEDCCAfigAwIBAgIUVXLU2Ff8nM9RuGznp9Gm87PxJfowDQYJKoZIhvcNAQEL +BQAwIDEeMBwGA1UEAwwVMjA0OCBSU0EgVGVzdCBSb290IENBMB4XDTIzMDIxNzE3 +MDg1MFoXDTMzMDIxNDE3MDg1MFowIDEeMBwGA1UEAwwVMjA0OCBSU0EgVGVzdCBS +b290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0LzVUgoBTARl +NwBzF68aU9UKsZzAWyD/wLS98tSyHSylw9fraLf3qy2zbADCerD3XrGLKZJ5CDf2 +7EYtZil8ZozUVPA1Ta0e5u9PQ52WL3hJZuvqT7np6yczbgMfO8HkbbupaGIc/XgN +V/JYiRZPjR0tlCrquBobV7itj2dEIwR/yRocSvMZFRo5pvzJF65eQJfis+wCTBtl +Dplq1P7HBFYUDI4P7uj7TmPEz0o2rPWm9x0mB+kzqfMx7h0n/BUQpYN+WSZuJDKH +rxkxbJkkBdlVcQO4s7Z1+O4E/wR/sLZXrSTjpvlGLPe1UzHWScbDHn+k8G1HMsmK +//O2e/3TfwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTVKFWH +x6O/18TOvj0B0r6LfOTi4jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQAD +ggEBABco004PcNuGuAjiDZLRTPYycFJrPx+WoL2k1PjttaSeCu9FCeQfiq9QLQNO +HO1GcT2R1qkPj7tPOKQ8GC//HweeFwammvlNAfZ5yar+AZB53dPrbeoLuWzfnB9P +MSVwcVicYmpahX2PIK6X1OBpinlNSDT+wJmYwzPO8weAwkuVoy66y+7XTaPhiJ5x +i2RHgwICcrBGyk/jsfQs8WVYCXqBGLc1VVBqNwoPh9PSsi9vk+F94spQH6mkbbxA +Gm7UVV6Z4Jx3fLAG03sLy6KaOpoGtlk9UNXQe+BRxIg4JLqAPPBCByTDXOK1br+L +N7Hf6X5uM9ayr9VRYuuwWlJUUqM= -----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/non-crit-codeSigning-chain.pem b/net/data/ssl/certificates/non-crit-codeSigning-chain.pem index d09f1cf9..6c539a6 100644 --- a/net/data/ssl/certificates/non-crit-codeSigning-chain.pem +++ b/net/data/ssl/certificates/non-crit-codeSigning-chain.pem
@@ -1,62 +1,63 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEA2f3ZUc459VPIfpNTUI40YfF1kwO+0B+mfKA74arroenNaykU -dR6azriRbYUzPtjLbIuu/NDXBc4Msv47X9EVTVs0ozjMgOOcTGVitrE/4Sww9anT -+VBWU9Tdp5ZRIRsZPvZzyEpb/MxOJKHX0JGsCPfpBJxRWQUNVChh0NVkOJyAMZiG -gmGkSeg6/d9xNy/2rNoIpNgfnLEhu7FxyB0940wQYA5prnyL/S/qfab4V40LuGZX -CrvWQ+twkx2rpQb4HRTx2YGtK/5YSh41hEoGThY/SZtXFhMtQyRSa6sKuhT2YaWH -MaEb9jbIeq3POXovhTSecA0Tw7bG0CtZEzwZVQIDAQABAoIBAQClUTsPLAuOPmTN -gSLs83tMT9avkGaT5XzYBJiFEp8yImJTg0rtazFR1m0Llrl/TuAuyFwDhMmcsF+3 -GtCiYKj8ClAH/JoyoOq2kSjkjdV5CY9zrsB/0Wo2lzcl0fxi0+84baTu312VgMc+ -RrKpjN/fyUqg4X9buFYcXaeYvUwNFVO7SWc1PIZO4AEqf1eYS8nfjShAiYFUj7cX -5rg8SBabVyrvCnvBWXc/DVnAUqbOWF3R17om3uRWY4tiVI9qpvi8RWQDTP3f/hLw -SqlLJEAr9w0x7VHNr5EN1HC+TcbQlW73dVTuHY8SQVRxzyde3BHB7/YOaqTt6YKK -wSxqxrG9AoGBAPrU0WZ4webJv/cTfUDvJfgrkWphBhnT8/w5wSH0zBuRONktT6oX -3y9Hjy+HR/+uRQO0Nywbb3+ZKkZeUUWylu1FCkwO/zLlRefNnHV2CRBoMnFgJtoY -TttUUNzzJmExjTEu3qPJ8TbzTA8vdk+387g9Rzbb3mjbcwvKxkjSuGhjAoGBAN57 -y5adtf551eyjRixEAQVFe5Iq0dUrp5T2bND4tHZzm6IYzbPEos8CHTs6tbHZGbws -w7zKxg0I7A/ZqRH3ngwdasaL7WrtTDNoAOkCvnqTjuxnoF0569Ko4sjiWVvcznZU -b5SInRfbHVT8g71DlahaRVQt0Rn8kmlvqEPeUvjnAoGBAJfz0R84zI6ZbfeqENkD -h4b+Lcu6F04SPt5vxnZhrDyPD1dRwc8TQxuLSEzMsWtNEXYa+Ml5nWQ5T4jtnmKQ -vCnlB0XoV+VnS6APyVbHONp9pQFV9HNvAmaQf6Q6kOeUcyp2cF3c+ooFffA9GnlU -wQq95KRxMh1nxBxCrTh0n05tAoGARo29l0r2PvgGFiAFDd6W8EQDluvLVS2d3Eh1 -Y6OrHvE0hqgU+5A9DSaffHv2yKqPVbRgcktfmRyeN7yPuCntTew6QzJ2nPUZuCeg -OkRrgVWv+lo2aboHheuW15uoONCCDNZj+BeGsd0DpULayDdZi2TtHW/WIsaM67DE -DJnBeDsCgYEAxWINst8mfuR2pKlVbQ8mMgo/LwKWqQgnpWpbf0gGMjdUpAoN5dh/ -l4VmO63Z/dAOyxQFAsqWS3pme2RfC2U94ZUOfFH58gLSNX7LWtBTZEhKLMs6Hfgr -aDJeuEEIrB+3snvqEWMztpxwHszx/YrDXwEbri557ve2UB2NtOcP3dc= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgluiYfh9lhgwx +d5rJhUpPoY+WJ67pPa1tdCTkeD3jowQ/MhNatUxMy/7O76pvBegUtrq29MPLcmvA +LeJYDcIJmh48pY/KrEUADZcDmrogQXQViCuaH/7ceKRxZu0FO7qBreyxgcu9nHkD +SP1X7ZefhEhOOKDsRXiQWpXTyDC/PeL/BMXkdoPGROEAx2RSyXETTCtby7HGsZ+w +nwXvdHUObpGmLErqiARhG39w7ZF40yV1hqy0DGw6CIV1q+JqLFMhZus+xSWkmFB4 +V06haO2D0Xgl7t2prN9c7OQHJ+fQP/D+cL2kzbdJO7MGq5biEoXMCsnpm0hCAu+v +Hl/kE5xJAgMBAAECggEAHjVtITx+evpdolORwE1s1nB5ooEqB9G0pVWXGAxfdgol +Ix8wIVzf8sVgFDzk5ngpuXXLR13hVAzc5JytCqvh360IeZeaZkGERFeKHCE32Jf/ +dop0S0ywBHjEVFnMhfK/qaPWVejo8uzznbDWCXuWDWmUsXQ5H2ENJjiIHTv4IY09 +0DXiySiaeI1iiyToiv6Dt9dVpPuleJbY0LlRfZ1WrkcEB9r1kQqzJOdqM5iuDYWa +dCO9u8fbWPGRacL3ystrZqB0Od3s9G6h09D5Iv8QXpYHWp4E/Oea3kYKyFUDgPpH +1wH6fE9fER6RKOKyG1HBZL12HlNZnPv1v033Rs6WRQKBgQC3LnaYo1TkHX0j4t1X +iQisRWtE8espt1ZsxIErqiz+fJlHhQCJq2Lqz/uLHmqaM6M2ffVuXoFM8vZzlyXw +v23EPc8HGEhvw6iTm3uCdTQ2nXZbb4Gbw5lYOF6PDJfAhnn3XwaUUVCL67PAZvwq +j4hWKzmrfrrv8KRgvso9NB6fHQKBgQDgbVxLGofqhoE/tuoCfZMCC0vI3PZ5503B +XvTXg/J9lKsycwx74qKWBDchqG+MF1GQOwufDvx44QAcW/gNsdd6SJ/yLlf69m6V +HSGh3S3pUfFwT4wzY/rKbh0g8DSXm6J7BrMwID+RAwMjKzst1tqjCBH6HnNEHIfP +HRrJAwgOHQKBgQCcFUkUckeJP987TrlPNwJe15+5VXENUJyhfSabMBu6lCx/FkMJ +CzHz5lftiHNJBSrS6azQ6FHAYV3BzE6VvmcnSYs3/mbqZIslitxIotlkl/Mbof2L +3bSxyQY5WX+MmokeUKfohQje0G2PSbEgCsEeuyIekJN0k1Vc4fStBdX5uQKBgAuG +0mSxGiX7fovtMxupo3FJbz0DzEz6ik3SOLUQ+9VjW1+d9RgvzbXyxXofEouZbwD/ +Z1tmA6WZuM28E4NwjOak8EIaCz7ChW93LZEIsSD4qnPgQg0pp2naOfjFHY5j2faD +o5RnM5yZEQIvaDy0ekpBUdsM0VLAPGFw1z1XwIQpAoGBALaN5OhHVZfy+RqTy+k/ +Sa+eO/vghkdapdZwfnFYhJzD+sZKRBUiNwzZrAcNfijN/sTIcnMWbMcD6TSdh1qZ +RLd2m/U0T9avw0/aog+IXldkm7VjKf7+V+RIzoGOxmtCI1zVP9uldDYJDKAMabiu +kdec1ozHgh6Pg2G2d5nkPgI9 +-----END PRIVATE KEY----- Certificate: Data: - Version: 1 (0x0) + Version: 3 (0x2) Serial Number: 1 (0x1) - Signature Algorithm: sha256WithRSAEncryption + Signature Algorithm: sha256WithRSAEncryption Issuer: CN=2048 RSA Test Root CA Validity - Not Before: Aug 14 02:46:30 2014 GMT - Not After : Aug 11 02:46:30 2024 GMT + Not Before: Feb 17 17:08:50 2023 GMT + Not After : Feb 14 17:08:50 2033 GMT Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:d9:fd:d9:51:ce:39:f5:53:c8:7e:93:53:50:8e: - 34:61:f1:75:93:03:be:d0:1f:a6:7c:a0:3b:e1:aa: - eb:a1:e9:cd:6b:29:14:75:1e:9a:ce:b8:91:6d:85: - 33:3e:d8:cb:6c:8b:ae:fc:d0:d7:05:ce:0c:b2:fe: - 3b:5f:d1:15:4d:5b:34:a3:38:cc:80:e3:9c:4c:65: - 62:b6:b1:3f:e1:2c:30:f5:a9:d3:f9:50:56:53:d4: - dd:a7:96:51:21:1b:19:3e:f6:73:c8:4a:5b:fc:cc: - 4e:24:a1:d7:d0:91:ac:08:f7:e9:04:9c:51:59:05: - 0d:54:28:61:d0:d5:64:38:9c:80:31:98:86:82:61: - a4:49:e8:3a:fd:df:71:37:2f:f6:ac:da:08:a4:d8: - 1f:9c:b1:21:bb:b1:71:c8:1d:3d:e3:4c:10:60:0e: - 69:ae:7c:8b:fd:2f:ea:7d:a6:f8:57:8d:0b:b8:66: - 57:0a:bb:d6:43:eb:70:93:1d:ab:a5:06:f8:1d:14: - f1:d9:81:ad:2b:fe:58:4a:1e:35:84:4a:06:4e:16: - 3f:49:9b:57:16:13:2d:43:24:52:6b:ab:0a:ba:14: - f6:61:a5:87:31:a1:1b:f6:36:c8:7a:ad:cf:39:7a: - 2f:85:34:9e:70:0d:13:c3:b6:c6:d0:2b:59:13:3c: - 19:55 + 00:a0:96:e8:98:7e:1f:65:86:0c:31:77:9a:c9:85: + 4a:4f:a1:8f:96:27:ae:e9:3d:ad:6d:74:24:e4:78: + 3d:e3:a3:04:3f:32:13:5a:b5:4c:4c:cb:fe:ce:ef: + aa:6f:05:e8:14:b6:ba:b6:f4:c3:cb:72:6b:c0:2d: + e2:58:0d:c2:09:9a:1e:3c:a5:8f:ca:ac:45:00:0d: + 97:03:9a:ba:20:41:74:15:88:2b:9a:1f:fe:dc:78: + a4:71:66:ed:05:3b:ba:81:ad:ec:b1:81:cb:bd:9c: + 79:03:48:fd:57:ed:97:9f:84:48:4e:38:a0:ec:45: + 78:90:5a:95:d3:c8:30:bf:3d:e2:ff:04:c5:e4:76: + 83:c6:44:e1:00:c7:64:52:c9:71:13:4c:2b:5b:cb: + b1:c6:b1:9f:b0:9f:05:ef:74:75:0e:6e:91:a6:2c: + 4a:ea:88:04:61:1b:7f:70:ed:91:78:d3:25:75:86: + ac:b4:0c:6c:3a:08:85:75:ab:e2:6a:2c:53:21:66: + eb:3e:c5:25:a4:98:50:78:57:4e:a1:68:ed:83:d1: + 78:25:ee:dd:a9:ac:df:5c:ec:e4:07:27:e7:d0:3f: + f0:fe:70:bd:a4:cd:b7:49:3b:b3:06:ab:96:e2:12: + 85:cc:0a:c9:e9:9b:48:42:02:ef:af:1e:5f:e4:13: + 9c:49 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Alternative Name: @@ -64,42 +65,46 @@ X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: - 47:76:6C:80:D1:4C:7A:B4:31:22:B8:50:62:FC:6B:D3:FD:B1:55:FA + 01:1C:A6:B0:CB:64:17:AB:8E:90:63:E0:B6:16:19:C6:06:C1:C2:C7 X509v3 Extended Key Usage: Code Signing + X509v3 Authority Key Identifier: + D5:28:55:87:C7:A3:BF:D7:C4:CE:BE:3D:01:D2:BE:8B:7C:E4:E2:E2 Signature Algorithm: sha256WithRSAEncryption - 37:61:d4:3b:d5:b6:77:53:54:17:d7:d7:78:4f:06:34:40:60: - dd:35:cf:fc:c5:07:c0:2c:86:72:c1:10:a1:3d:6e:af:53:8c: - de:b1:69:b9:00:8b:8b:d5:21:45:66:8f:53:98:78:d1:86:dc: - 8b:12:f3:b6:4a:e7:ae:19:b5:75:b1:7b:e6:27:dc:82:0d:44: - 8e:15:c7:fb:57:7b:78:4d:6d:2f:2a:c7:cc:ec:9e:15:be:47: - 2e:e3:7f:bd:09:a2:f5:4f:90:15:b5:6f:3a:ec:cc:a1:00:db: - 91:a8:8a:c2:d3:1f:d9:20:b0:8e:79:5f:2a:31:c6:48:45:6c: - f0:c3:f5:78:ea:92:21:49:82:fe:02:b6:e4:58:52:9b:98:31: - c0:a3:bd:9c:5d:a6:32:7d:e2:7f:8b:57:25:2a:5a:a4:64:64: - f9:20:4d:6d:be:95:81:6a:3f:ce:7a:a3:c8:e5:26:d2:c0:95: - ce:90:db:b0:90:7e:f9:fe:2b:96:06:ca:72:2d:c9:a3:c0:6c: - 84:1a:8a:f3:1a:93:09:2f:34:ec:a8:c7:88:79:eb:2e:a6:77: - 51:5d:79:00:db:8f:a4:5a:e5:58:7f:94:bb:2b:4c:24:16:56: - 4a:7a:49:3b:d9:b1:77:b0:21:c9:ba:73:96:68:8a:59:69:a5: - 96:4d:b7:a1 + Signature Value: + bc:03:2d:ad:04:e4:0e:64:20:1e:bd:fb:04:6a:63:d5:8c:51: + a0:d6:2d:4d:7d:97:f5:0f:42:56:09:58:d6:f4:df:46:01:01: + f3:62:78:a7:57:76:28:5a:85:fd:b1:34:37:06:7f:1f:7e:d1: + d9:1a:54:78:a2:4a:ec:90:69:bf:08:3e:c7:df:63:1c:89:d3: + ca:30:61:e3:f6:0a:92:66:68:8f:6b:6a:0b:01:4c:ca:90:e5: + 13:b4:89:92:2e:56:bd:84:6f:9c:ff:19:5b:75:27:7f:9e:cc: + 62:1c:06:e6:94:30:fe:d3:59:1b:2c:10:79:7c:0e:1a:29:be: + c9:14:02:56:f9:eb:cd:a6:33:10:75:fa:d7:61:23:d0:16:cf: + c9:e9:58:7b:51:8c:1f:84:3f:32:ca:89:91:90:da:5f:7d:35: + 0c:f5:1c:c3:b6:03:ad:f9:5f:49:79:54:55:86:98:fa:69:b5: + fb:47:5b:b1:99:c8:f0:5f:b8:af:a6:b4:63:1b:53:e6:47:78: + e3:40:df:63:21:f2:51:88:c4:24:b5:51:33:bc:b1:f5:7e:42: + 89:b3:ff:3a:d7:e4:02:e2:23:c4:82:98:74:d2:2c:ee:8e:71: + 58:80:b7:0f:24:dc:11:83:02:13:91:7d:0f:8f:b4:81:20:c5: + ee:55:8c:7b -----BEGIN CERTIFICATE----- -MIIDSzCCAjMCAQEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVMjA0OCBSU0Eg -VGVzdCBSb290IENBMB4XDTE0MDgxNDAyNDYzMFoXDTI0MDgxMTAyNDYzMFowYDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50 -YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExEjAQBgNVBAMMCTEyNy4wLjAuMTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANn92VHOOfVTyH6TU1CONGHx -dZMDvtAfpnygO+Gq66HpzWspFHUems64kW2FMz7Yy2yLrvzQ1wXODLL+O1/RFU1b -NKM4zIDjnExlYraxP+EsMPWp0/lQVlPU3aeWUSEbGT72c8hKW/zMTiSh19CRrAj3 -6QScUVkFDVQoYdDVZDicgDGYhoJhpEnoOv3fcTcv9qzaCKTYH5yxIbuxccgdPeNM -EGAOaa58i/0v6n2m+FeNC7hmVwq71kPrcJMdq6UG+B0U8dmBrSv+WEoeNYRKBk4W -P0mbVxYTLUMkUmurCroU9mGlhzGhG/Y2yHqtzzl6L4U0nnANE8O2xtArWRM8GVUC -AwEAAaNVMFMwDwYDVR0RBAgwBocEfwAAATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW -BBRHdmyA0Ux6tDEiuFBi/GvT/bFV+jATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkq -hkiG9w0BAQsFAAOCAQEAN2HUO9W2d1NUF9fXeE8GNEBg3TXP/MUHwCyGcsEQoT1u -r1OM3rFpuQCLi9UhRWaPU5h40YbcixLztkrnrhm1dbF75ifcgg1EjhXH+1d7eE1t -LyrHzOyeFb5HLuN/vQmi9U+QFbVvOuzMoQDbkaiKwtMf2SCwjnlfKjHGSEVs8MP1 -eOqSIUmC/gK25FhSm5gxwKO9nF2mMn3if4tXJSpapGRk+SBNbb6VgWo/znqjyOUm -0sCVzpDbsJB++f4rlgbKci3Jo8BshBqK8xqTCS807KjHiHnrLqZ3UV15ANuPpFrl -WH+UuytMJBZWSnpJO9mxd7AhybpzlmiKWWmllk23oQ== +MIIDcTCCAlmgAwIBAgIBATANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBUyMDQ4 +IFJTQSBUZXN0IFJvb3QgQ0EwHhcNMjMwMjE3MTcwODUwWhcNMzMwMjE0MTcwODUw +WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN +TW91bnRhaW4gVmlldzEQMA4GA1UECgwHVGVzdCBDQTESMBAGA1UEAwwJMTI3LjAu +MC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoJbomH4fZYYMMXea +yYVKT6GPlieu6T2tbXQk5Hg946MEPzITWrVMTMv+zu+qbwXoFLa6tvTDy3JrwC3i +WA3CCZoePKWPyqxFAA2XA5q6IEF0FYgrmh/+3HikcWbtBTu6ga3ssYHLvZx5A0j9 +V+2Xn4RITjig7EV4kFqV08gwvz3i/wTF5HaDxkThAMdkUslxE0wrW8uxxrGfsJ8F +73R1Dm6RpixK6ogEYRt/cO2ReNMldYastAxsOgiFdaviaixTIWbrPsUlpJhQeFdO +oWjtg9F4Je7dqazfXOzkByfn0D/w/nC9pM23STuzBquW4hKFzArJ6ZtIQgLvrx5f +5BOcSQIDAQABo3YwdDAPBgNVHREECDAGhwR/AAABMAwGA1UdEwEB/wQCMAAwHQYD +VR0OBBYEFAEcprDLZBerjpBj4LYWGcYGwcLHMBMGA1UdJQQMMAoGCCsGAQUFBwMD +MB8GA1UdIwQYMBaAFNUoVYfHo7/XxM6+PQHSvot85OLiMA0GCSqGSIb3DQEBCwUA +A4IBAQC8Ay2tBOQOZCAevfsEamPVjFGg1i1NfZf1D0JWCVjW9N9GAQHzYninV3Yo +WoX9sTQ3Bn8fftHZGlR4okrskGm/CD7H32McidPKMGHj9gqSZmiPa2oLAUzKkOUT +tImSLla9hG+c/xlbdSd/nsxiHAbmlDD+01kbLBB5fA4aKb7JFAJW+evNpjMQdfrX +YSPQFs/J6Vh7UYwfhD8yyomRkNpffTUM9RzDtgOt+V9JeVRVhpj6abX7R1uxmcjw +X7ivprRjG1PmR3jjQN9jIfJRiMQktVEzvLH1fkKJs/861+QC4iPEgph00izujnFY +gLcPJNwRgwITkX0Pj7SBIMXuVYx7 -----END CERTIFICATE-----
diff --git a/net/socket/udp_client_socket.cc b/net/socket/udp_client_socket.cc index 5203b05..5402c107 100644 --- a/net/socket/udp_client_socket.cc +++ b/net/socket/udp_client_socket.cc
@@ -178,4 +178,9 @@ #endif } +void UDPClientSocket::AdoptOpenedSocket(AddressFamily address_family, + SocketDescriptor socket) { + socket_.AdoptOpenedSocket(address_family, socket); +} + } // namespace net
diff --git a/net/socket/udp_client_socket.h b/net/socket/udp_client_socket.h index cdfadb5..8999d0e 100644 --- a/net/socket/udp_client_socket.h +++ b/net/socket/udp_client_socket.h
@@ -9,6 +9,7 @@ #include "net/base/net_export.h" #include "net/socket/datagram_client_socket.h" +#include "net/socket/socket_descriptor.h" #include "net/socket/udp_socket.h" #include "net/traffic_annotation/network_traffic_annotation.h" @@ -74,6 +75,9 @@ void SetIOSNetworkServiceType(int ios_network_service_type) override; void SetDontClose(bool dont_close) override; + // Takes ownership of an opened but unconnected and unbound `socket`. + void AdoptOpenedSocket(AddressFamily address_family, SocketDescriptor socket); + private: UDPSocket socket_; // The network the socket is currently bound to.
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc index 9dce5d5..a3149177e 100644 --- a/net/socket/udp_socket_posix.cc +++ b/net/socket/udp_socket_posix.cc
@@ -154,10 +154,33 @@ if (owned_socket_count.empty()) return ERR_INSUFFICIENT_RESOURCES; + owned_socket_count_ = std::move(owned_socket_count); addr_family_ = ConvertAddressFamily(address_family); socket_ = CreatePlatformSocket(addr_family_, SOCK_DGRAM, 0); - if (socket_ == kInvalidSocket) + if (socket_ == kInvalidSocket) { + owned_socket_count_.Reset(); return MapSystemError(errno); + } + + return ConfigureOpenedSocket(); +} + +int UDPSocketPosix::AdoptOpenedSocket(AddressFamily address_family, + int socket) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK_EQ(socket_, kInvalidSocket); + auto owned_socket_count = TryAcquireGlobalUDPSocketCount(); + if (owned_socket_count.empty()) { + return ERR_INSUFFICIENT_RESOURCES; + } + + owned_socket_count_ = std::move(owned_socket_count); + socket_ = socket; + addr_family_ = ConvertAddressFamily(address_family); + return ConfigureOpenedSocket(); +} + +int UDPSocketPosix::ConfigureOpenedSocket() { #if BUILDFLAG(IS_APPLE) && !BUILDFLAG(CRONET_BUILD) PCHECK(change_fdguard_np(socket_, nullptr, 0, &kSocketFdGuard, GUARD_CLOSE | GUARD_DUP, nullptr) == 0); @@ -171,7 +194,6 @@ if (tag_ != SocketTag()) tag_.Apply(socket_); - owned_socket_count_ = std::move(owned_socket_count); return OK; }
diff --git a/net/socket/udp_socket_posix.h b/net/socket/udp_socket_posix.h index 306feb8a..b6f3841 100644 --- a/net/socket/udp_socket_posix.h +++ b/net/socket/udp_socket_posix.h
@@ -275,6 +275,11 @@ // Sets "don't close" flag for the socket. void SetDontClose(bool dont_close); + // Takes ownership of `socket`, which should be a socket descriptor opened + // with the specified address family. The socket should only be created but + // not bound or connected to an address. + int AdoptOpenedSocket(AddressFamily address_family, int socket); + private: enum SocketOptions { SOCKET_OPTION_MULTICAST_LOOP = 1 << 0 @@ -368,6 +373,9 @@ // Binds to a random port on |address|. int RandomBind(const IPAddress& address); + // Sets `socket_hash_` and `tag_` on opened `socket_`. + int ConfigureOpenedSocket(); + int socket_; // Hash of |socket_| to verify that it is not corrupted when calling close().
diff --git a/net/socket/udp_socket_unittest.cc b/net/socket/udp_socket_unittest.cc index 33ccfe0..41828e3 100644 --- a/net/socket/udp_socket_unittest.cc +++ b/net/socket/udp_socket_unittest.cc
@@ -42,6 +42,13 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +#if !BUILDFLAG(IS_WIN) +#include <netinet/in.h> +#include <sys/socket.h> +#else +#include <winsock2.h> +#endif + #if BUILDFLAG(IS_ANDROID) #include "base/android/build_info.h" #include "net/android/network_change_notifier_factory_android.h" @@ -449,6 +456,57 @@ EXPECT_FALSE(socket.is_connected()); } +// Similar to ConnectFail but UDPSocket adopts an opened socket instead of +// opening one directly. +TEST_F(UDPSocketTest, AdoptedSocket) { + auto socketfd = + CreatePlatformSocket(ConvertAddressFamily(ADDRESS_FAMILY_IPV4), + SOCK_DGRAM, AF_UNIX ? 0 : IPPROTO_UDP); + UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource()); + + EXPECT_THAT(socket.AdoptOpenedSocket(ADDRESS_FAMILY_IPV4, socketfd), IsOk()); + + // Connect to an IPv6 address should fail since the socket was created for + // IPv4. + EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)), + Not(IsOk())); + + // Make sure that UDPSocket actually closed the socket. + EXPECT_FALSE(socket.is_connected()); +} + +// Tests that UDPSocket updates the global counter correctly. +TEST_F(UDPSocketTest, LimitAdoptSocket) { + ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting()); + { + // Creating a platform socket does not increase count. + auto socketfd = + CreatePlatformSocket(ConvertAddressFamily(ADDRESS_FAMILY_IPV4), + SOCK_DGRAM, AF_UNIX ? 0 : IPPROTO_UDP); + ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting()); + + // Simply allocating a UDPSocket does not increase count. + UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource()); + EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting()); + + // Calling AdoptOpenedSocket() allocates the socket and increases the global + // counter. + EXPECT_THAT(socket.AdoptOpenedSocket(ADDRESS_FAMILY_IPV4, socketfd), + IsOk()); + EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting()); + + // Connect to an IPv6 address should fail since the socket was created for + // IPv4. + EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)), + Not(IsOk())); + + // That Connect() failed doesn't change the global counter. + EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting()); + } + // Finally, destroying UDPSocket decrements the global counter. + EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting()); +} + // In this test, we verify that connect() on a socket will have the effect // of filtering reads on this socket only to data read from the destination // we connected to.
diff --git a/net/socket/udp_socket_win.cc b/net/socket/udp_socket_win.cc index bebf140a..8961503b 100644 --- a/net/socket/udp_socket_win.cc +++ b/net/socket/udp_socket_win.cc
@@ -263,19 +263,40 @@ if (owned_socket_count.empty()) return ERR_INSUFFICIENT_RESOURCES; + owned_socket_count_ = std::move(owned_socket_count); addr_family_ = ConvertAddressFamily(address_family); socket_ = CreatePlatformSocket(addr_family_, SOCK_DGRAM, IPPROTO_UDP); - if (socket_ == INVALID_SOCKET) + if (socket_ == INVALID_SOCKET) { + owned_socket_count_.Reset(); return MapSystemError(WSAGetLastError()); + } + ConfigureOpenedSocket(); + return OK; +} + +int UDPSocketWin::AdoptOpenedSocket(AddressFamily address_family, + SOCKET socket) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + auto owned_socket_count = TryAcquireGlobalUDPSocketCount(); + if (owned_socket_count.empty()) { + return ERR_INSUFFICIENT_RESOURCES; + } + + owned_socket_count_ = std::move(owned_socket_count); + addr_family_ = ConvertAddressFamily(address_family); + socket_ = socket; + ConfigureOpenedSocket(); + return OK; +} + +void UDPSocketWin::ConfigureOpenedSocket() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!use_non_blocking_io_) { core_ = base::MakeRefCounted<Core>(this); } else { read_write_event_.Set(WSACreateEvent()); WSAEventSelect(socket_, read_write_event_.Get(), FD_READ | FD_WRITE); } - - owned_socket_count_ = std::move(owned_socket_count); - return OK; } void UDPSocketWin::Close() {
diff --git a/net/socket/udp_socket_win.h b/net/socket/udp_socket_win.h index 4aa218c..5374c291 100644 --- a/net/socket/udp_socket_win.h +++ b/net/socket/udp_socket_win.h
@@ -350,6 +350,11 @@ // Apply |tag| to this socket. void ApplySocketTag(const SocketTag& tag); + // Takes ownership of `socket`, which should be a socket descriptor opened + // with the specified address family. The socket should only be created but + // not bound or connected to an address. + int AdoptOpenedSocket(AddressFamily address_family, SOCKET socket); + private: enum SocketOptions { SOCKET_OPTION_MULTICAST_LOOP = 1 << 0 @@ -406,6 +411,9 @@ int SetMulticastOptions(); int DoBind(const IPEndPoint& address); + // Configures opened `socket_` depending on whether it uses nonblocking IO. + void ConfigureOpenedSocket(); + // This is provided to allow QwaveApi mocking in tests. |UDPSocketWin| method // implementations should call |GetQwaveApi()| instead of // |QwaveApi::GetDefault()| directly.
diff --git a/services/network/public/cpp/network_switches.cc b/services/network/public/cpp/network_switches.cc index 718d5eb..f774c06a8 100644 --- a/services/network/public/cpp/network_switches.cc +++ b/services/network/public/cpp/network_switches.cc
@@ -17,8 +17,6 @@ // causing them to attempt an unauthenticated SSL/TLS session. This is intended // for use when testing various service URLs (eg: kPromoServerURL, kSbURLPrefix, // kSyncServiceURL, etc). -// TODO(crbug.com/1417189): Remove this flag if the alternative solution -// implemented for crbug.com/1221565 covers all needs. const char kIgnoreUrlFetcherCertRequests[] = "ignore-urlfetcher-cert-requests"; // A set of public key hashes for which to ignore certificate-related errors.
diff --git a/sql/recover_module/payload.cc b/sql/recover_module/payload.cc index f7e87da..ad3fac8 100644 --- a/sql/recover_module/payload.cc +++ b/sql/recover_module/payload.cc
@@ -203,9 +203,16 @@ return true; } +bool LeafPayloadReader::IsInitialized() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return page_id_ != DatabasePageReader::kInvalidPageId; +} + bool LeafPayloadReader::ReadPayload(int64_t offset, int64_t size, uint8_t* buffer) { + DCHECK(IsInitialized()) + << "Initialize() not called, or last call did not succeed"; DCHECK_GE(offset, 0); DCHECK_LT(offset, payload_size_); DCHECK_GT(size, 0); @@ -249,6 +256,11 @@ // The read is entirely in overflow pages. DCHECK_GE(offset, inline_payload_size_); + if (max_overflow_payload_size_ <= 0) { + // `max_overflow_payload_size_` should have been set in Initialize() if it's + // to be used here. See https://crbug.com/1417151. + return false; + } while (size > 0) { const int overflow_page_index = (offset - inline_payload_size_) / max_overflow_payload_size_; @@ -286,7 +298,7 @@ const uint8_t* LeafPayloadReader::ReadInlinePayload() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(page_id_ != DatabasePageReader::kInvalidPageId) + DCHECK(IsInitialized()) << "Initialize() not called, or last call did not succeed"; if (db_reader_->ReadPage(page_id_) != SQLITE_OK)
diff --git a/sql/recover_module/payload.h b/sql/recover_module/payload.h index dc6a329..3028af02 100644 --- a/sql/recover_module/payload.h +++ b/sql/recover_module/payload.h
@@ -51,6 +51,9 @@ // class can be called. bool Initialize(int64_t payload_size, int payload_offset); + // True if the last call to Initialize succeeded. + bool IsInitialized() const; + // The number of payload bytes that are stored on the B-tree page. // // The return value is guaranteed to be non-negative and at most @@ -109,29 +112,29 @@ const raw_ptr<DatabasePageReader> db_reader_; // Total size of the current payload. - int64_t payload_size_; + int64_t payload_size_ = 0; // The ID of the B-tree page containing the current payload's inline bytes. // // Set to kInvalidPageId if the reader wasn't successfully initialized. - int page_id_; + int page_id_ = DatabasePageReader::kInvalidPageId; // The start of the current payload's inline bytes on the B-tree page. // // Large payloads extend past the B-tree page containing the payload, via // overflow pages. - int inline_payload_offset_; + int inline_payload_offset_ = 0; // Number of bytes in the current payload stored in its B-tree page. // // The rest of the payload is stored on overflow pages. - int inline_payload_size_; + int inline_payload_size_ = 0; // Number of overflow pages used by the payload. - int overflow_page_count_; + int overflow_page_count_ = 0; // Number of bytes in each overflow page that stores the payload. - int max_overflow_payload_size_; + int max_overflow_payload_size_ = 0; // Page IDs for all the payload's overflow pages, in order. //
diff --git a/sql/recover_module/record.cc b/sql/recover_module/record.cc index 773cb2a9..109fb97 100644 --- a/sql/recover_module/record.cc +++ b/sql/recover_module/record.cc
@@ -154,6 +154,12 @@ return true; } +bool RecordReader::IsInitialized() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return value_headers_.size() == static_cast<size_t>(column_count_) && + payload_reader_->IsInitialized(); +} + ValueType RecordReader::GetValueType(int column_index) const { DCHECK(IsInitialized()); DCHECK_GE(column_index, 0);
diff --git a/sql/recover_module/record.h b/sql/recover_module/record.h index af437fe..c039ae669 100644 --- a/sql/recover_module/record.h +++ b/sql/recover_module/record.h
@@ -90,10 +90,7 @@ bool Initialize(); // True if the last call to Initialize succeeded. - bool IsInitialized() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return value_headers_.size() == static_cast<size_t>(column_count_); - } + bool IsInitialized() const; // The type of a value in the record. |column_index| is 0-based. ValueType GetValueType(int column_index) const;
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc index 4a78619..00652e0 100644 --- a/storage/browser/blob/blob_registry_impl_unittest.cc +++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -86,7 +86,7 @@ base::SequencedTaskRunner::GetCurrentDefault()); } auto delegate = std::make_unique<MockBlobRegistryDelegate>(); - delegate_ptr_ = delegate.get(); + delegate_ptr_ = delegate->AsWeakPtr(); registry_impl_->Bind(registry_.BindNewPipeAndPassReceiver(), std::move(delegate)); @@ -199,7 +199,7 @@ BlobUrlRegistry url_registry_; std::unique_ptr<BlobRegistryImpl> registry_impl_; mojo::Remote<blink::mojom::BlobRegistry> registry_; - raw_ptr<MockBlobRegistryDelegate> delegate_ptr_; + base::WeakPtr<MockBlobRegistryDelegate> delegate_ptr_; scoped_refptr<base::SequencedTaskRunner> bytes_provider_runner_; size_t reply_request_count_ = 0;
diff --git a/storage/browser/test/mock_blob_registry_delegate.h b/storage/browser/test/mock_blob_registry_delegate.h index 081bcad..87d0ed33 100644 --- a/storage/browser/test/mock_blob_registry_delegate.h +++ b/storage/browser/test/mock_blob_registry_delegate.h
@@ -5,11 +5,14 @@ #ifndef STORAGE_BROWSER_TEST_MOCK_BLOB_REGISTRY_DELEGATE_H_ #define STORAGE_BROWSER_TEST_MOCK_BLOB_REGISTRY_DELEGATE_H_ +#include "base/memory/weak_ptr.h" #include "storage/browser/blob/blob_registry_impl.h" namespace storage { -class MockBlobRegistryDelegate : public BlobRegistryImpl::Delegate { +class MockBlobRegistryDelegate + : public BlobRegistryImpl::Delegate, + public base::SupportsWeakPtr<MockBlobRegistryDelegate> { public: MockBlobRegistryDelegate() = default; ~MockBlobRegistryDelegate() override = default;
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 539cb81f..0b1848d 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5818,9 +5818,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -5832,8 +5832,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -5989,9 +5989,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6003,8 +6003,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -6141,9 +6141,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -6155,8 +6155,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json index afe23f0..4ae4cae 100644 --- a/testing/buildbot/chromium.coverage.json +++ b/testing/buildbot/chromium.coverage.json
@@ -20297,9 +20297,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -20311,8 +20311,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -20438,9 +20438,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -20452,8 +20452,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -20565,9 +20565,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -20579,8 +20579,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 89dddfc..d475b5d 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -7502,6 +7502,197 @@ } ] }, + "ios-blink-dbg-fyi": { + "additional_compile_targets": [ + "all" + ], + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPad Pro (12.9-inch) (3rd generation)", + "--version", + "16.2", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-build-version", + "14c18", + "--xctest" + ], + "isolate_name": "blink_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "blink_unittests iPad Pro (12.9-inch) (3rd generation) 16.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "named_caches": [ + { + "name": "xcode_ios_14c18", + "path": "Xcode.app" + }, + { + "name": "runtime_ios_16_2", + "path": "Runtime-ios-16.2" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/", + "variant_id": "iPad Pro (12.9-inch) (3rd generation) 16.2" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "16.2", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-build-version", + "14c18", + "--xctest" + ], + "isolate_name": "blink_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "blink_unittests iPhone X 16.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "named_caches": [ + { + "name": "xcode_ios_14c18", + "path": "Xcode.app" + }, + { + "name": "runtime_ios_16_2", + "path": "Runtime-ios-16.2" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/", + "variant_id": "iPhone X 16.2" + }, + { + "args": [ + "--platform", + "iPad Pro (12.9-inch) (3rd generation)", + "--version", + "16.2", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-build-version", + "14c18", + "--xctest" + ], + "isolate_name": "content_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "content_unittests iPad Pro (12.9-inch) (3rd generation) 16.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "named_caches": [ + { + "name": "xcode_ios_14c18", + "path": "Xcode.app" + }, + { + "name": "runtime_ios_16_2", + "path": "Runtime-ios-16.2" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://content/test:content_unittests/", + "variant_id": "iPad Pro (12.9-inch) (3rd generation) 16.2" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "16.2", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-build-version", + "14c18", + "--xctest" + ], + "isolate_name": "content_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "content_unittests iPhone X 16.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "named_caches": [ + { + "name": "xcode_ios_14c18", + "path": "Xcode.app" + }, + { + "name": "runtime_ios_16_2", + "path": "Runtime-ios-16.2" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://content/test:content_unittests/", + "variant_id": "iPhone X 16.2" + } + ] + }, "ios-fieldtrial-rel": { "isolated_scripts": [ { @@ -56985,9 +57176,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -56998,8 +57189,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -57156,9 +57347,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -57169,8 +57360,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -57308,9 +57499,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -57321,8 +57512,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -58846,9 +59037,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -58859,8 +59050,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -59017,9 +59208,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -59030,8 +59221,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -59169,9 +59360,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -59182,8 +59373,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -59955,9 +60146,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -59968,8 +60159,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index bbb153c..ed17daf 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -18533,12 +18533,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18550,8 +18550,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -18724,12 +18724,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18741,8 +18741,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [ @@ -18891,12 +18891,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 112.0.5600.0", + "description": "Run with ash-chrome version 112.0.5602.0", "isolate_profile_data": true, "merge": { "args": [], @@ -18908,8 +18908,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v112.0.5600.0", - "revision": "version:112.0.5600.0" + "location": "lacros_version_skew_tests_v112.0.5602.0", + "revision": "version:112.0.5602.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json index 08dc63cd..08798ff 100644 --- a/testing/buildbot/internal.chromeos.fyi.json +++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1099,7 +1099,6 @@ "args": [ "--magic-vm-cache=magic_cros_vm_cache" ], - "experiment_percentage": 100, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 80e6c5a4..0e66b454 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -612,7 +612,6 @@ 'mixins': [ 'has_native_resultdb_integration', ], - 'experiment_percentage': 100, }, }, @@ -3903,6 +3902,11 @@ 'headless_unittests': {}, }, + 'ios_blink_tests': { + 'blink_unittests': {}, + 'content_unittests': {}, + }, + 'ios_clang_tests': { 'absl_hardening_tests': {}, 'base_unittests': {}, @@ -6933,6 +6937,15 @@ } }, + 'ios_blink_dbg_tests': { + 'ios_blink_tests': { + 'variants': [ + 'SIM_IPAD_PRO_3RD_GEN_16_2', + 'SIM_IPHONE_X_16_2', + ] + }, + }, + 'ios_clang_tot_device_tests': { 'ios_clang_tests': { 'variants': [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 2ffda780..543cdb3 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5600.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5602.0/test_ash_chrome', ], - 'description': 'Run with ash-chrome version 112.0.5600.0', + 'description': 'Run with ash-chrome version 112.0.5602.0', 'identifier': 'Lacros version skew testing ash canary', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v112.0.5600.0', - 'revision': 'version:112.0.5600.0', + 'location': 'lacros_version_skew_tests_v112.0.5602.0', + 'revision': 'version:112.0.5602.0', }, ], },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index a23a0e84..5327958 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -3207,6 +3207,21 @@ 'isolated_scripts': 'chromeos_isolated_scripts', }, }, + 'ios-blink-dbg-fyi': { + 'additional_compile_targets': [ + 'all' + ], + 'mixins': [ + 'has_native_resultdb_integration', + 'mac_toolchain', + 'out_dir_arg', + 'xcode_14_main', + 'xctest', + ], + 'test_suites': { + 'isolated_scripts': 'ios_blink_dbg_tests' + }, + }, 'ios-fieldtrial-rel': { 'mixins': [ 'finch-chromium-swarming-pool',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 724f62ea..e4908eb 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -4658,7 +4658,9 @@ { "name": "Enabled", "enable_features": [ - "ShoppingList" + "IPH_PriceNotificationsWhileBrowsing", + "ShoppingList", + "kSmartSortingPriceTrackingDestination" ] } ]
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index 56b1540..1b147ad 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -708,6 +708,23 @@ "WebviewAccelerateSmallCanvases", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE( + kCanvas2DHibernation, + "Canvas2DHibernation", +#if BUILDFLAG(IS_MAC) + // Canvas hibernation is not always enabled on MacOS X due to a bug that + // causes content loss. TODO: Find a better fix for crbug.com/588434 + base::FeatureState::FEATURE_DISABLED_BY_DEFAULT +#else + base::FeatureState::FEATURE_ENABLED_BY_DEFAULT +#endif +); + +// Whether to losslessly compress the resulting image after canvas hibernation. +BASE_FEATURE(kCanvasCompressHibernatedImage, + "CanvasCompressHibernatedImage", + base::FEATURE_DISABLED_BY_DEFAULT); + // Whether to aggressively free resources for canvases in background pages. BASE_FEATURE(kCanvasFreeMemoryWhenHidden, "CanvasFreeMemoryWhenHidden", @@ -1492,7 +1509,7 @@ BASE_FEATURE(kSerializeAccessibilityPostLifecycle, "SerializeAccessibilityPostLifeycle", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kThreadedPreloadScanner, "ThreadedPreloadScanner",
diff --git a/third_party/blink/common/interest_group/auction_config.cc b/third_party/blink/common/interest_group/auction_config.cc index 45308cd..83e0cd8 100644 --- a/third_party/blink/common/interest_group/auction_config.cc +++ b/third_party/blink/common/interest_group/auction_config.cc
@@ -74,6 +74,9 @@ if (non_shared_params.buyer_timeouts.is_promise()) { ++total; } + if (non_shared_params.buyer_cumulative_timeouts.is_promise()) { + ++total; + } if (direct_from_seller_signals.is_promise()) { ++total; }
diff --git a/third_party/blink/common/interest_group/auction_config_mojom_traits.cc b/third_party/blink/common/interest_group/auction_config_mojom_traits.cc index df2f63f..9d598b7 100644 --- a/third_party/blink/common/interest_group/auction_config_mojom_traits.cc +++ b/third_party/blink/common/interest_group/auction_config_mojom_traits.cc
@@ -131,6 +131,7 @@ !data.ReadSellerTimeout(&out->seller_timeout) || !data.ReadPerBuyerSignals(&out->per_buyer_signals) || !data.ReadBuyerTimeouts(&out->buyer_timeouts) || + !data.ReadBuyerCumulativeTimeouts(&out->buyer_cumulative_timeouts) || !data.ReadPerBuyerGroupLimits(&out->per_buyer_group_limits) || !data.ReadPerBuyerPrioritySignals(&out->per_buyer_priority_signals) || !data.ReadAllBuyersPrioritySignals(&out->all_buyers_priority_signals) ||
diff --git a/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc index 60184e6..033d7f38 100644 --- a/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc +++ b/third_party/blink/common/interest_group/auction_config_mojom_traits_test.cc
@@ -54,16 +54,16 @@ const AuctionConfig::NonSharedParams& b) { return std::tie(a.interest_group_buyers, a.auction_signals, a.seller_signals, a.seller_timeout, a.per_buyer_signals, a.buyer_timeouts, - a.per_buyer_group_limits, a.all_buyers_group_limit, - a.per_buyer_priority_signals, a.all_buyers_priority_signals, - a.auction_report_buyer_keys, a.auction_report_buyers, - a.component_auctions) == + a.buyer_cumulative_timeouts, a.per_buyer_group_limits, + a.all_buyers_group_limit, a.per_buyer_priority_signals, + a.all_buyers_priority_signals, a.auction_report_buyer_keys, + a.auction_report_buyers, a.component_auctions) == std::tie(b.interest_group_buyers, b.auction_signals, b.seller_signals, b.seller_timeout, b.per_buyer_signals, b.buyer_timeouts, - b.per_buyer_group_limits, b.all_buyers_group_limit, - b.per_buyer_priority_signals, b.all_buyers_priority_signals, - b.auction_report_buyer_keys, b.auction_report_buyers, - b.component_auctions); + b.buyer_cumulative_timeouts, b.per_buyer_group_limits, + b.all_buyers_group_limit, b.per_buyer_priority_signals, + b.all_buyers_priority_signals, b.auction_report_buyer_keys, + b.auction_report_buyers, b.component_auctions); } bool operator==(const AuctionConfig& a, const AuctionConfig& b) { @@ -138,6 +138,14 @@ AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( std::move(buyer_timeouts)); + AuctionConfig::BuyerTimeouts buyer_cumulative_timeouts; + buyer_cumulative_timeouts.per_buyer_timeouts.emplace(); + (*buyer_cumulative_timeouts.per_buyer_timeouts)[buyer] = base::Seconds(432); + buyer_cumulative_timeouts.all_buyers_timeout = base::Seconds(234); + non_shared_params.buyer_cumulative_timeouts = + AuctionConfig::MaybePromiseBuyerTimeouts::FromValue( + std::move(buyer_cumulative_timeouts)); + non_shared_params.per_buyer_group_limits[buyer] = 10; non_shared_params.all_buyers_group_limit = 11; non_shared_params.per_buyer_priority_signals.emplace();
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index 30d3c51..6fddd69 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -270,6 +270,10 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebviewAccelerateSmallCanvases); +BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCanvas2DHibernation); + +BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCanvasCompressHibernatedImage); + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCanvasFreeMemoryWhenHidden); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCreateImageBitmapOrientationNone);
diff --git a/third_party/blink/public/common/interest_group/auction_config.h b/third_party/blink/public/common/interest_group/auction_config.h index 4770ad8..cd415a1 100644 --- a/third_party/blink/public/common/interest_group/auction_config.h +++ b/third_party/blink/public/common/interest_group/auction_config.h
@@ -193,6 +193,11 @@ // Values restrict the runtime of generateBid() scripts. MaybePromiseBuyerTimeouts buyer_timeouts; + // Collective timeouts for all interest groups with the same buyer. Includes + // launching worklet processes, loading scripts and signals, and running + // the buyer's generateBid() functions. + MaybePromiseBuyerTimeouts buyer_cumulative_timeouts; + // Values restrict the number of bidding interest groups for a particular // buyer that can participate in an auction. Values must be greater than 0. base::flat_map<url::Origin, std::uint16_t> per_buyer_group_limits;
diff --git a/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h b/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h index debd9e73..0049df90 100644 --- a/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h +++ b/third_party/blink/public/common/interest_group/auction_config_mojom_traits.h
@@ -211,6 +211,12 @@ return params.buyer_timeouts; } + static const blink::AuctionConfig::MaybePromiseBuyerTimeouts& + buyer_cumulative_timeouts( + const blink::AuctionConfig::NonSharedParams& params) { + return params.buyer_cumulative_timeouts; + } + static const base::flat_map<url::Origin, std::uint16_t>& per_buyer_group_limits(const blink::AuctionConfig::NonSharedParams& params) { return params.per_buyer_group_limits;
diff --git a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom index 4c7803a..6356fe2 100644 --- a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom +++ b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
@@ -31,6 +31,14 @@ kSellerSignals }; +// Represents which field that uses the AuctionAdConfigBuyerTimeouts struct is +// getting a concrete value provided after the promise that was originally +// passed in resolved. +enum AuctionAdConfigBuyerTimeoutField { + kPerBuyerTimeouts, + kPerBuyerCumulativeTimeouts +}; + // Used to provide a way of aborting a call to AdAuctionService.RunAdAuction interface AbortableAdAuction { // These methods should be called to provide a value for part of auction @@ -48,9 +56,11 @@ map<url.mojom.Origin, string>? per_buyer_signals); // Used to provide result of resolving a promise specifying - // `per_buyer_timeouts` field of an AuctionConfig. + // `per_buyer_timeouts` or `per_buyer_cumulative_timeouts` field of an + // AuctionConfig. ResolvedBuyerTimeoutsPromise( AuctionAdConfigAuctionId auction, + AuctionAdConfigBuyerTimeoutField field, AuctionAdConfigBuyerTimeouts buyer_timeouts); // Used to provide result of resolving a promise specifying
diff --git a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom index c8c0cf86..9b145b6b 100644 --- a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom +++ b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
@@ -307,8 +307,14 @@ AuctionAdConfigMaybePromisePerBuyerSignals per_buyer_signals; + // Timeouts for individual interest group worklet Javascript execution. AuctionAdConfigMaybePromiseBuyerTimeouts buyer_timeouts; + // Cumulative timeouts for all interest groups with the same buyer. Includes + // launching worklet processes, loading scripts and signals, and running + // the buyer's generateBid() functions. + AuctionAdConfigMaybePromiseBuyerTimeouts buyer_cumulative_timeouts; + // Keys of `per_buyer_group_limits` must be valid HTTPS origins. Values // restrict the number of bidding interest groups for a particular buyer // that can participate in an auction. Values must be greater than 0.
diff --git a/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist b/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist index fbbf3007..962ca26 100644 --- a/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist +++ b/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist
@@ -448,6 +448,313 @@ ../../../../media/test/data/webm_vorbis_track_entry ../../../../media/test/data/webm_vp8_track_entry ../../../../media/test/data/yellow_pink_gradient_lossless.webp +../../web_tests/images/resources/0colors.ico +../../web_tests/images/resources/0x0.bmp +../../web_tests/images/resources/12-55.jpg +../../web_tests/images/resources/182.jpg +../../web_tests/images/resources/1bit.ico +../../web_tests/images/resources/1xint32_min.bmp +../../web_tests/images/resources/2-comp.jpg +../../web_tests/images/resources/2-dht.jpg +../../web_tests/images/resources/23-55.jpg +../../web_tests/images/resources/2entries.ico +../../web_tests/images/resources/32bit.ico +../../web_tests/images/resources/5000x5000.png +../../web_tests/images/resources/55.jpg +../../web_tests/images/resources/57.jpg +../../web_tests/images/resources/58.jpg +../../web_tests/images/resources/59.jpg +../../web_tests/images/resources/8bit.ico +../../web_tests/images/resources/animated-10color-fast.gif +../../web_tests/images/resources/animated-10color.gif +../../web_tests/images/resources/animated-gif-with-offsets.gif +../../web_tests/images/resources/animated.gif +../../web_tests/images/resources/animated2.gif +../../web_tests/images/resources/apng00.png +../../web_tests/images/resources/apng01.png +../../web_tests/images/resources/apng08-ref.png +../../web_tests/images/resources/apng18.png +../../web_tests/images/resources/apng24-ref.png +../../web_tests/images/resources/apng24.png +../../web_tests/images/resources/apng26-ref.png +../../web_tests/images/resources/apng26.png +../../web_tests/images/resources/avif/README.md +../../web_tests/images/resources/avif/alpha-mask-full-range-10bpc.avif +../../web_tests/images/resources/avif/alpha-mask-full-range-12bpc.avif +../../web_tests/images/resources/avif/alpha-mask-full-range-8bpc.avif +../../web_tests/images/resources/avif/alpha-mask-limited-range-10bpc.avif +../../web_tests/images/resources/avif/alpha-mask-limited-range-12bpc.avif +../../web_tests/images/resources/avif/alpha-mask-limited-range-8bpc.avif +../../web_tests/images/resources/avif/dice_444_10b_grid4x3.avif +../../web_tests/images/resources/avif/gracehopper_422_12b_grid2x4.avif +../../web_tests/images/resources/avif/gray1024x704.avif +../../web_tests/images/resources/avif/green-no-alpha-ispe.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-10bpc.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-12bpc.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-8bpc.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-truncated.avif +../../web_tests/images/resources/avif/red-at-12-oclock-with-color-profile-with-wrong-frame-header.avif +../../web_tests/images/resources/avif/red-full-range-420-10bpc.avif +../../web_tests/images/resources/avif/red-full-range-420-12bpc.avif +../../web_tests/images/resources/avif/red-full-range-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-angle-1-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-angle-2-mode-0-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-angle-3-mode-1-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-bt2020-hlg-444-10bpc.avif +../../web_tests/images/resources/avif/red-full-range-bt2020-hlg-444-12bpc.avif +../../web_tests/images/resources/avif/red-full-range-bt2020-pq-444-10bpc.avif +../../web_tests/images/resources/avif/red-full-range-bt2020-pq-444-12bpc.avif +../../web_tests/images/resources/avif/red-full-range-bt709-444-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-mode-0-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-mode-1-420-8bpc.avif +../../web_tests/images/resources/avif/red-full-range-unspecified-420-8bpc.avif +../../web_tests/images/resources/avif/red-limited-range-420-10bpc.avif +../../web_tests/images/resources/avif/red-limited-range-420-12bpc.avif +../../web_tests/images/resources/avif/red-limited-range-420-8bpc.avif +../../web_tests/images/resources/avif/red-limited-range-422-10bpc.avif +../../web_tests/images/resources/avif/red-limited-range-422-12bpc.avif +../../web_tests/images/resources/avif/red-limited-range-422-8bpc.avif +../../web_tests/images/resources/avif/red-limited-range-444-10bpc.avif +../../web_tests/images/resources/avif/red-limited-range-444-12bpc.avif +../../web_tests/images/resources/avif/red-limited-range-444-8bpc.avif +../../web_tests/images/resources/avif/red-unsupported-transfer.avif +../../web_tests/images/resources/avif/red-with-alpha-10bpc.avif +../../web_tests/images/resources/avif/red-with-alpha-12bpc.avif +../../web_tests/images/resources/avif/red-with-alpha-8bpc.avif +../../web_tests/images/resources/avif/red-with-profile-10bpc.avif +../../web_tests/images/resources/avif/red-with-profile-12bpc.avif +../../web_tests/images/resources/avif/red-with-profile-8bpc.avif +../../web_tests/images/resources/avif/red.png +../../web_tests/images/resources/avif/silver-full-range-srgb-420-8bpc.avif +../../web_tests/images/resources/avif/silver.png +../../web_tests/images/resources/avif/star-animated-10bpc-with-alpha.avif +../../web_tests/images/resources/avif/star-animated-10bpc.avif +../../web_tests/images/resources/avif/star-animated-12bpc-with-alpha.avif +../../web_tests/images/resources/avif/star-animated-12bpc.avif +../../web_tests/images/resources/avif/star-animated-8bpc-with-alpha.avif +../../web_tests/images/resources/avif/star-animated-8bpc.avif +../../web_tests/images/resources/avif/tiger_3layer_1res.avif +../../web_tests/images/resources/avif/tiger_3layer_3res.avif +../../web_tests/images/resources/avif/tiger_420_8b_grid1x13.avif +../../web_tests/images/resources/bad-png.png +../../web_tests/images/resources/bad.ico +../../web_tests/images/resources/base-test-resources/success.png +../../web_tests/images/resources/blue-10.png +../../web_tests/images/resources/blue-100.png +../../web_tests/images/resources/blue-wheel-srgb-color-profile.jpg +../../web_tests/images/resources/blue-wheel-srgb-color-profile.png +../../web_tests/images/resources/blue-wheel-srgb-color-profile.webp +../../web_tests/images/resources/border-image-srgb-color-profile.png +../../web_tests/images/resources/border-image.png +../../web_tests/images/resources/boston.gif +../../web_tests/images/resources/broken-image-with-invalid-format.png +../../web_tests/images/resources/bug106024.jpg +../../web_tests/images/resources/bug653075.ico +../../web_tests/images/resources/busted-oval.png +../../web_tests/images/resources/cHRM_color_spin.png +../../web_tests/images/resources/cat.jpg +../../web_tests/images/resources/cicp_pq.png +../../web_tests/images/resources/cmyk-jpeg.jpg +../../web_tests/images/resources/color-checker-adobe-color-profile.png +../../web_tests/images/resources/color-checker-munsell-chart.js +../../web_tests/images/resources/color-checker-srgb-color-profile.png +../../web_tests/images/resources/color-profile-image-data-url.svg +../../web_tests/images/resources/color-profile-image-foreign-object.svg +../../web_tests/images/resources/color-profile-mask-image.svg +../../web_tests/images/resources/count-down-color-test.gif +../../web_tests/images/resources/count-down-color-test.png +../../web_tests/images/resources/count-down-color-test.webp +../../web_tests/images/resources/crbug.364830.webp +../../web_tests/images/resources/crbug702934.png +../../web_tests/images/resources/crbug752898.bmp +../../web_tests/images/resources/crbug779261.gif +../../web_tests/images/resources/crbug807324.png +../../web_tests/images/resources/crbug827754.png +../../web_tests/images/resources/cropped_mandrill.jpg +../../web_tests/images/resources/cs-uma-cmyk-jfif-marker.jpg +../../web_tests/images/resources/cs-uma-cmyk-no-jfif-or-adobe-markers.jpg +../../web_tests/images/resources/cs-uma-cmyk-unknown-transform.jpg +../../web_tests/images/resources/cs-uma-cmyk.jpg +../../web_tests/images/resources/cs-uma-grayscale.jpg +../../web_tests/images/resources/cs-uma-rgb-non-interleaved.jpg +../../web_tests/images/resources/cs-uma-rgb-unknown-transform.jpg +../../web_tests/images/resources/cs-uma-rgb.jpg +../../web_tests/images/resources/cs-uma-two-channels-jfif-marker.jpg +../../web_tests/images/resources/cs-uma-ycbcr-410.jpg +../../web_tests/images/resources/cs-uma-ycbcr-411.jpg +../../web_tests/images/resources/cs-uma-ycbcr-420-both-jfif-adobe.jpg +../../web_tests/images/resources/cs-uma-ycbcr-420-non-interleaved.jpg +../../web_tests/images/resources/cs-uma-ycbcr-420.jpg +../../web_tests/images/resources/cs-uma-ycbcr-422.jpg +../../web_tests/images/resources/cs-uma-ycbcr-440.jpg +../../web_tests/images/resources/cs-uma-ycbcr-444.jpg +../../web_tests/images/resources/cs-uma-ycbcr-other.jpg +../../web_tests/images/resources/cs-uma-ycck.jpg +../../web_tests/images/resources/dice.png +../../web_tests/images/resources/empty-frame.png +../../web_tests/images/resources/favicon.ico +../../web_tests/images/resources/flip.webp +../../web_tests/images/resources/flowchart.jpg +../../web_tests/images/resources/full2loop.gif +../../web_tests/images/resources/gif-loop-count.gif +../../web_tests/images/resources/gif-loop-count.png +../../web_tests/images/resources/gracehopper.bmp +../../web_tests/images/resources/gracehopper.jpg +../../web_tests/images/resources/gracehopper.png +../../web_tests/images/resources/green-10.png +../../web_tests/images/resources/green-100.png +../../web_tests/images/resources/green-24x24.jpg +../../web_tests/images/resources/green-256x256.jpg +../../web_tests/images/resources/green-circle.svg +../../web_tests/images/resources/green-half-stripe.png +../../web_tests/images/resources/green.jpg +../../web_tests/images/resources/green.png +../../web_tests/images/resources/green_rectangle.pdf +../../web_tests/images/resources/greenbox-3frames.cur +../../web_tests/images/resources/grid-large.png +../../web_tests/images/resources/grid-small.png +../../web_tests/images/resources/grid-transparent.png +../../web_tests/images/resources/half-circles.svg +../../web_tests/images/resources/half_scan-2.jpg +../../web_tests/images/resources/half_scan.jpg +../../web_tests/images/resources/icc-v2-gbr-420-height-not-whole-mcu.jpg +../../web_tests/images/resources/icc-v2-gbr-420-width-not-whole-mcu.jpg +../../web_tests/images/resources/icc-v2-gbr-422-whole-mcus.jpg +../../web_tests/images/resources/icc-v2-gbr.jpg +../../web_tests/images/resources/icon-without-and-bitmap.ico +../../web_tests/images/resources/invalid-animated-webp.webp +../../web_tests/images/resources/invalid-animated-webp2.webp +../../web_tests/images/resources/invalid-animated-webp3.webp +../../web_tests/images/resources/invalid-animated-webp4.webp +../../web_tests/images/resources/invalid.jpg +../../web_tests/images/resources/invalid_vp8_vp8x.webp +../../web_tests/images/resources/jpeg-height-exif-orientation.jpg +../../web_tests/images/resources/large-gif-checkerboard.gif +../../web_tests/images/resources/large-size-image-crash.jpeg +../../web_tests/images/resources/large.webp +../../web_tests/images/resources/missing-eoi.jpg +../../web_tests/images/resources/missing-plte-before-trns.png +../../web_tests/images/resources/motion-jpeg-single-frame.jpg +../../web_tests/images/resources/mu.png +../../web_tests/images/resources/non-interleaved_progressive-1.jpg +../../web_tests/images/resources/non-interleaved_progressive.jpg +../../web_tests/images/resources/oval.png +../../web_tests/images/resources/palatted-color-png-gamma-one-color-profile.png +../../web_tests/images/resources/pdf_test_landscape.pdf +../../web_tests/images/resources/pixel-crack-image-background-webkit-transform-scale.png +../../web_tests/images/resources/pixelated-hdpi.png +../../web_tests/images/resources/png-16bit/2x2_16bit_AdobeRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_AdobeRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_DisplayP3_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_DisplayP3_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_ProPhoto_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_ProPhoto_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_Rec2020_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_Rec2020_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_e-sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_e-sRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_AdobeRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_DisplayP3_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_ProPhoto_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_Rec2020_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_e-sRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_interlaced_sRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_16bit_sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_16bit_sRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_AdobeRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_AdobeRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_DisplayP3_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_DisplayP3_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_ProPhoto_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_ProPhoto_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_Rec2020_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_Rec2020_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_e-sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_e-sRGB_transparent.png +../../web_tests/images/resources/png-16bit/2x2_8bit_sRGB_opaque.png +../../web_tests/images/resources/png-16bit/2x2_8bit_sRGB_transparent.png +../../web_tests/images/resources/png-animated-idat-not-part-of-animation.png +../../web_tests/images/resources/png-animated-idat-part-of-animation.png +../../web_tests/images/resources/png-animated-three-independent-frames.png +../../web_tests/images/resources/png-color-profile-and-gamma-chunk.png +../../web_tests/images/resources/png-extra-row-crash.png +../../web_tests/images/resources/png-in-ico.ico +../../web_tests/images/resources/png-simple.png +../../web_tests/images/resources/png_per_row_alpha.png +../../web_tests/images/resources/pngfuzz/border-image.png +../../web_tests/images/resources/pngfuzz/oval.png +../../web_tests/images/resources/pngfuzz/png-animated-idat-not-part-of-animation.png +../../web_tests/images/resources/pngfuzz/png-animated-idat-part-of-animation.png +../../web_tests/images/resources/pngfuzz/png-animated-three-independent-frames.png +../../web_tests/images/resources/pngfuzz/png-simple.png +../../web_tests/images/resources/quicksort.gif +../../web_tests/images/resources/red-10.png +../../web_tests/images/resources/red-100.png +../../web_tests/images/resources/red-at-12-oclock-with-color-profile.jpg +../../web_tests/images/resources/red-at-12-oclock-with-color-profile.png +../../web_tests/images/resources/red-half-stripe.png +../../web_tests/images/resources/rgb-jpeg-blue.jpg +../../web_tests/images/resources/rgb-jpeg-green.jpg +../../web_tests/images/resources/rgb-jpeg-red.jpg +../../web_tests/images/resources/rgb-jpeg-with-adobe-marker-only.jpg +../../web_tests/images/resources/rgb-png-with-cmyk-color-profile.png +../../web_tests/images/resources/short-app-extension-string.gif +../../web_tests/images/resources/size-failure.b186640109.webp +../../web_tests/images/resources/size-failure.gif +../../web_tests/images/resources/small-square-with-colorspin-profile.jpg +../../web_tests/images/resources/sprite_all_fragments_same.png +../../web_tests/images/resources/sprite_alternate_fragments_same.png +../../web_tests/images/resources/stripes-large.png +../../web_tests/images/resources/stripes-small.png +../../web_tests/images/resources/test-load.jpg +../../web_tests/images/resources/test.svg +../../web_tests/images/resources/test.webp +../../web_tests/images/resources/test2.webp +../../web_tests/images/resources/test3.webp +../../web_tests/images/resources/truncated.webp +../../web_tests/images/resources/truncated2.webp +../../web_tests/images/resources/twitter_favicon.ico +../../web_tests/images/resources/webp-animated-icc-xmp.webp +../../web_tests/images/resources/webp-animated-large.webp +../../web_tests/images/resources/webp-animated-no-blend.webp +../../web_tests/images/resources/webp-animated-opaque.webp +../../web_tests/images/resources/webp-animated-semitransparent1.webp +../../web_tests/images/resources/webp-animated-semitransparent2.webp +../../web_tests/images/resources/webp-animated-semitransparent3.webp +../../web_tests/images/resources/webp-animated-semitransparent4.webp +../../web_tests/images/resources/webp-animated.webp +../../web_tests/images/resources/webp-color-no-profile-lossy.webp +../../web_tests/images/resources/webp-color-profile-crash.webp +../../web_tests/images/resources/webp-color-profile-lossless.webp +../../web_tests/images/resources/webp-color-profile-lossy-alpha.webp +../../web_tests/images/resources/webp-color-profile-lossy.webp +../../web_tests/images/resources/wrong-block-length.gif +../../web_tests/images/resources/wrong-frame-dimensions.ico +../../web_tests/images/resources/ycbcr-420-fast-int-progressive.jpg +../../web_tests/images/resources/ycbcr-420-float-progressive.jpg +../../web_tests/images/resources/ycbcr-420-float.jpg +../../web_tests/images/resources/ycbcr-420-progressive-smooth.jpg +../../web_tests/images/resources/ycbcr-420-progressive.jpg +../../web_tests/images/resources/ycbcr-420.jpg +../../web_tests/images/resources/ycbcr-422-float.jpg +../../web_tests/images/resources/ycbcr-422.jpg +../../web_tests/images/resources/ycbcr-440-float.jpg +../../web_tests/images/resources/ycbcr-440.jpg +../../web_tests/images/resources/ycbcr-444-float.jpg +../../web_tests/images/resources/ycbcr-444.jpg +../../web_tests/images/resources/ycbcr-progressive-000.jpg +../../web_tests/images/resources/ycbcr-progressive-001.jpg +../../web_tests/images/resources/ycbcr-progressive-002.jpg +../../web_tests/images/resources/ycbcr-progressive-003.jpg +../../web_tests/images/resources/ycbcr-with-cmyk-color-profile.jpg +../../web_tests/images/resources/ycbcr-with-no-color-profile.jpg ../core/animation/test_data/animation-in-main-frame.html ../core/animation/test_data/custom-property.html ../core/animation/test_data/inactive-animations.html
diff --git a/third_party/blink/renderer/controller/blink_unittests_bundle_data.globlist b/third_party/blink/renderer/controller/blink_unittests_bundle_data.globlist index 49b82dfe..b6122c53 100644 --- a/third_party/blink/renderer/controller/blink_unittests_bundle_data.globlist +++ b/third_party/blink/renderer/controller/blink_unittests_bundle_data.globlist
@@ -8,6 +8,6 @@ ../core/animation/test_data/* ../core/paint/test_data/* ../core/testing/data/** -../../web_tests/impages/resources/** +../../web_tests/images/resources/** ../../../../media/test/data/**
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc index 0904a4a..324c140 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -2138,14 +2138,14 @@ UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(IsBackdropInert(html)); - EXPECT_EQ(body->GetPseudoElement(kPseudoIdBackdrop), nullptr); + EXPECT_FALSE(IsBackdropInert(body)); EXPECT_EQ(dialog->GetPseudoElement(kPseudoIdBackdrop), nullptr); dialog->showModal(exception_state); UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(IsBackdropInert(html)); - EXPECT_EQ(body->GetPseudoElement(kPseudoIdBackdrop), nullptr); + EXPECT_FALSE(IsBackdropInert(body)); EXPECT_FALSE(IsBackdropInert(dialog)); }
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc index deb9dd1e..71fe817 100644 --- a/third_party/blink/renderer/core/css/selector_checker.cc +++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -1680,7 +1680,7 @@ case CSSSelector::kPseudoFullscreen: // fall through case CSSSelector::kPseudoFullScreen: - return Fullscreen::IsFullscreenElement(element); + return Fullscreen::IsFullscreenFlagSetFor(element); case CSSSelector::kPseudoFullScreenAncestor: return element.ContainsFullScreenElement(); case CSSSelector::kPseudoPaused: {
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni index 92b63c1..39520fb 100644 --- a/third_party/blink/renderer/core/dom/build.gni +++ b/third_party/blink/renderer/core/dom/build.gni
@@ -66,6 +66,8 @@ "css_toggle.h", "css_toggle_event.cc", "css_toggle_event.h", + "css_toggle_inference.cc", + "css_toggle_inference.h", "css_toggle_map.cc", "css_toggle_map.h", "css_toggle_traversal.h",
diff --git a/third_party/blink/renderer/core/dom/css_toggle.cc b/third_party/blink/renderer/core/dom/css_toggle.cc index 610bfc48..6b2819cd 100644 --- a/third_party/blink/renderer/core/dom/css_toggle.cc +++ b/third_party/blink/renderer/core/dom/css_toggle.cc
@@ -14,6 +14,7 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_union_stringsequence_unsignedlong.h" #include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/dom/css_toggle_event.h" +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" #include "third_party/blink/renderer/core/dom/css_toggle_map.h" #include "third_party/blink/renderer/core/dom/css_toggle_traversal.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -341,6 +342,8 @@ SetElementNeedsStyleRecalc(e, when, reason); } } + + toggle_element->GetDocument().EnsureCSSToggleInference().MarkNeedsRebuild(); } void CSSToggle::SetLaterSiblingsNeedStyleRecalc(Element* toggle_element, @@ -354,6 +357,8 @@ break; SetElementNeedsStyleRecalc(e, when, reason); } + + toggle_element->GetDocument().EnsureCSSToggleInference().MarkNeedsRebuild(); } const ToggleRoot* CSSToggle::FindToggleSpecifier() const {
diff --git a/third_party/blink/renderer/core/dom/css_toggle_inference.cc b/third_party/blink/renderer/core/dom/css_toggle_inference.cc new file mode 100644 index 0000000..85f2bc43 --- /dev/null +++ b/third_party/blink/renderer/core/dom/css_toggle_inference.cc
@@ -0,0 +1,594 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" + +#include <ostream> +#include <utility> + +#include "base/check.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/renderer/core/dom/css_toggle.h" +#include "third_party/blink/renderer/core/dom/css_toggle_map.h" +#include "third_party/blink/renderer/core/dom/css_toggle_traversal.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/element.h" +#include "third_party/blink/renderer/core/html/html_element.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" + +namespace blink { + +void CSSToggleInference::Trace(Visitor* visitor) const { + visitor->Trace(document_); + visitor->Trace(element_roles_); +} + +void CSSToggleInference::RebuildIfNeeded() { + if (needs_rebuild_) { + needs_rebuild_ = false; + Rebuild(); + } + DCHECK(!needs_rebuild_); +} + +namespace { + +// Check the root+trigger condition for whether something is tab-ish, +// but not the toggle-visibility on a sibling part. +bool MightBeTabIsh(Element* toggle_root) { + DCHECK(toggle_root->GetToggleMap()); + const ComputedStyle* root_style = toggle_root->GetComputedStyle(); + if (!root_style) { + return false; + } + const ToggleTriggerList* triggers = root_style->ToggleTrigger(); + if (!triggers) { + return false; + } + + for (const auto& [toggle_name, toggle] : + toggle_root->GetToggleMap()->Toggles()) { + for (const ToggleTrigger& trigger : triggers->Triggers()) { + if (trigger.Name() == toggle_name) { + return true; + } + } + } + return false; +} + +absl::optional<AtomicString> IsTabsIsh(Element* container) { + const ComputedStyle* container_style = container->GetComputedStyle(); + DCHECK(container_style); + const ToggleGroupList* container_groups = container_style->ToggleGroup(); + DCHECK(container_groups); + + for (const ToggleGroup& toggle_group : container_groups->Groups()) { + unsigned child_count = 0; + unsigned child_tab_count = 0; + unsigned child_panel_count = 0; + for (Element& child : ElementTraversal::ChildrenOf(*container)) { + const ComputedStyle* child_style = child.GetComputedStyle(); + if (!child_style) { + continue; + } + ++child_count; + + bool has_trigger = false; + const ToggleTriggerList* child_triggers = child_style->ToggleTrigger(); + const ToggleRootList* child_roots = child_style->ToggleRoot(); + if (child_triggers && child_roots) { + for (const ToggleTrigger& child_trigger : child_triggers->Triggers()) { + if (child_trigger.Name() == toggle_group.Name()) { + for (const ToggleRoot& child_root : child_roots->Roots()) { + if (child_root.Name() == toggle_group.Name() && + child_root.IsGroup()) { + has_trigger = true; + break; + } + } + if (has_trigger) { + break; + } + } + } + } + + bool has_visibility = + child_style->ToggleVisibility() == toggle_group.Name(); + + if (has_trigger ^ has_visibility) { + if (has_trigger) { + ++child_tab_count; + } else { + DCHECK(has_visibility); + ++child_panel_count; + } + } + } + // TODO(dbaron): Should we have a condition that the number of tabs + // and panels are similar or equal? + if (child_tab_count > 0 && child_panel_count > 0 && + (child_tab_count + child_panel_count) * 2 > child_count) { + return toggle_group.Name(); + } + } + return absl::nullopt; +} + +struct DisclosureIshInfo { + STACK_ALLOCATED(); + + public: + AtomicString toggle_name; + Element* trigger_element; +}; + +// Check whether something is disclosure-ish. Assumes that +// MightBeTabIsh was already called and false. Returns nullopt to +// indicate that it's not disclosure-ish, or info if it is. +absl::optional<DisclosureIshInfo> IsDisclosureIsh(Element* toggle_root) { + DCHECK(toggle_root->GetToggleMap()); + for (const auto& [toggle_name, toggle] : + toggle_root->GetToggleMap()->Toggles()) { + // We want to find out if we have exactly one descendant with + // toggle-trigger (for this name) and exactly one descendant with + // toggle-visibility (for this name), and those descendants are + // different. + Element* trigger_element = nullptr; + bool found_visibility = false; + bool found_duplicate_or_same_descendant = false; + + for (Element* e : + CSSToggleScopeRange(toggle_root, toggle_name, toggle->Scope())) { + const ComputedStyle* style = e->GetComputedStyle(); + if (!style) { + continue; + } + + bool e_has_trigger = false; + if (const ToggleTriggerList* triggers = style->ToggleTrigger()) { + for (const ToggleTrigger& trigger : triggers->Triggers()) { + if (trigger.Name() == toggle_name) { + e_has_trigger = true; + break; + } + } + } + bool e_has_visibility = style->ToggleVisibility() == toggle_name; + + if (e_has_trigger && e_has_visibility) { + found_duplicate_or_same_descendant = true; + break; + } + + if (e_has_trigger) { + if (trigger_element) { + found_duplicate_or_same_descendant = true; + break; + } + trigger_element = e; + } + + if (e_has_visibility) { + if (found_visibility) { + found_duplicate_or_same_descendant = true; + break; + } + found_visibility = true; + } + } + + if (found_duplicate_or_same_descendant) { + continue; + } + if (trigger_element && found_visibility) { + return DisclosureIshInfo({toggle_name, trigger_element}); + } + } + return absl::nullopt; +} + +bool IsAccordionIsh( + Element* container, + const HeapHashMap<Member<Element>, AtomicString>& disclosure_ish_elements) { + unsigned child_element_count = 0; + unsigned child_disclosure_ish_count = 0; + for (Element& child : ElementTraversal::ChildrenOf(*container)) { + ++child_element_count; + if (disclosure_ish_elements.Contains(&child)) { + ++child_disclosure_ish_count; + } + } + + // Define an element as accordion-ish if more than half of its + // children are disclosure-ish, and there are at least 2 + // disclosure-ish children. + return child_disclosure_ish_count >= 2 && + child_disclosure_ish_count * 2 > child_element_count; +} + +} // namespace + +void CSSToggleInference::Rebuild() { + DCHECK(!needs_rebuild_) << "Rebuild should be called via RebuildIfNeeded"; + + element_roles_.clear(); + + const HeapHashSet<WeakMember<Element>> elements_with_toggles = + document_->ElementsWithCSSToggles(); + + // TODO(dbaron): There are various things here that count siblings and + // have thresholds at 50%+1. We don't currently invalidate (i.e., + // set needs_rebuild_) for many of the cases that could change the + // result of these checks, since we only invalidate in response to + // changes on things that have toggles. + // + // In fact, there are probably other conditions that we don't properly + // invalidate for as well. Before this code is every used for + // something we ship, we should verify that all the conditions tested + // here are properly invalidated. + + // Build the set of disclosure-ish elements (and retain the toggle + // names associated with them, and the triggers, in two separate hash + // maps (for now) so that we don't have to make a new garbage + // collected struct). Simultaneously build a too-broad set of + // tabs-ish elements that we will reduce later. + HeapHashMap<Member<Element>, AtomicString> disclosure_ish_elements; + HeapHashMap<Member<Element>, Member<Element>> disclosure_ish_element_triggers; + HeapHashSet<Member<Element>> maybe_tabs_ish_elements; + for (Element* toggle_root : elements_with_toggles) { + DCHECK(!disclosure_ish_elements.Contains(toggle_root)); + if (MightBeTabIsh(toggle_root)) { + Element* parent = toggle_root->parentElement(); + if (parent) { + const ComputedStyle* parent_style = parent->GetComputedStyle(); + if (parent_style && parent_style->ToggleGroup()) { + maybe_tabs_ish_elements.insert(parent); + } + } + } else if (absl::optional<DisclosureIshInfo> info_option = + IsDisclosureIsh(toggle_root)) { + disclosure_ish_elements.insert(toggle_root, info_option->toggle_name); + disclosure_ish_element_triggers.insert(toggle_root, + info_option->trigger_element); + } + } + + // Build the set of accordion-ish elements by adding the parent of + // everything disclosure-ish, and then removing those that are not + // accordion-ish. (This is a convenient way to test each parent once, + // even when it has multiple disclosure-ish children.) + HeapHashSet<Member<Element>> accordion_ish_elements; + for (Element* disclosure_ish : disclosure_ish_elements.Keys()) { + Element* parent = disclosure_ish->parentElement(); + if (parent) { + accordion_ish_elements.insert(parent); + } + } + { + HeapHashSet<Member<Element>> trimmed_accordion_ish_elements; + for (Element* e : accordion_ish_elements) { + if (IsAccordionIsh(e, disclosure_ish_elements)) { + trimmed_accordion_ish_elements.insert(e); + } + } + std::swap(accordion_ish_elements, trimmed_accordion_ish_elements); + } + + // Reduce the set of tabs-ish elements and record the toggle names. + HeapHashMap<Member<Element>, AtomicString> tabs_ish_elements; + for (Element* e : maybe_tabs_ish_elements) { + absl::optional<AtomicString> tabs_toggle_name = IsTabsIsh(e); + if (tabs_toggle_name) { + tabs_ish_elements.insert(e, *tabs_toggle_name); + } + } + + // Now go through and assign the roles. + for (Element* toggle_root : elements_with_toggles) { + bool find_trigger_and_make_it_a_button = false; + + Element* parent = toggle_root->parentElement(); + const auto disclosure_ish_iter = disclosure_ish_elements.find(toggle_root); + if (disclosure_ish_iter != disclosure_ish_elements.end()) { + if (parent && accordion_ish_elements.Contains(parent)) { + CSSToggleRole parent_role; + { // scope for lifetime of add_result + auto add_result = element_roles_.insert(parent, CSSToggleRole::kNone); + // We might have already handled parent as the parent of a + // different one of its children. + if (add_result.is_new_entry) { + // Determine whether we're looking at accordion or tree. + // + // Do this by walking the scopes of the children that are + // disclosure-ish, and re-examining their panels (the elements + // with toggle-visibility). + unsigned panel_count = 0; + unsigned accordion_ish_panel_count = 0; + // For accordions, we want to check that the disclosure-ish + // child actually has the same toggle name as the group + // established by the accordion-ish element. So we maintain + // separate counts for each toggle name. + HashMap<AtomicString, unsigned> panel_with_toggle_group_counts; + for (Element& child : ElementTraversal::ChildrenOf(*parent)) { + if (disclosure_ish_elements.Contains(&child)) { + DCHECK(child.GetToggleMap()); + for (const auto& [toggle_name, toggle] : + child.GetToggleMap()->Toggles()) { + for (Element* e : CSSToggleScopeRange(&child, toggle_name, + toggle->Scope())) { + const ComputedStyle* e_style = e->GetComputedStyle(); + if (!e_style) { + continue; + } + if (e_style->ToggleVisibility() == toggle_name) { + ++panel_count; + if (toggle->IsGroup()) { + auto count_add_result = + panel_with_toggle_group_counts.insert(toggle_name, + 0); + DCHECK(count_add_result.is_new_entry == + (count_add_result.stored_value->value == 0)); + ++count_add_result.stored_value->value; + } else { + if (accordion_ish_elements.Contains(e)) { + ++accordion_ish_panel_count; + } + } + } + } + } + } + } + DCHECK_GE(panel_count, 2u) << "unexpect IsAccordionIsh result"; + bool enough_accordions = + accordion_ish_panel_count * 2 > panel_count; + bool enough_toggle_groups = false; + if (const ComputedStyle* parent_style = + parent->GetComputedStyle()) { + if (const ToggleGroupList* parent_groups = + parent_style->ToggleGroup()) { + for (const ToggleGroup& group : parent_groups->Groups()) { + auto iter = panel_with_toggle_group_counts.find(group.Name()); + if (iter != panel_with_toggle_group_counts.end()) { + if (iter->value * 2 > panel_count) { + enough_toggle_groups = true; + } + } + } + } + } + if (enough_accordions ^ enough_toggle_groups) { + if (enough_accordions) { + DCHECK(!enough_toggle_groups); + add_result.stored_value->value = CSSToggleRole::kTree; + } else { + DCHECK(enough_toggle_groups); + add_result.stored_value->value = CSSToggleRole::kAccordion; + } + } else { + // TODO(dbaron): For now, we don't know what this is, but + // maybe we could do better. + add_result.stored_value->value = CSSToggleRole::kNone; + } + } + parent_role = add_result.stored_value->value; + } + switch (parent_role) { + case CSSToggleRole::kTree: + case CSSToggleRole::kTreeGroup: { + // TODO(dbaron): This role assignment is likely wrong! Need to + // examine closely! + Element* trigger_element = + disclosure_ish_element_triggers.at(toggle_root); + element_roles_.insert(toggle_root, CSSToggleRole::kTreeGroup); + // TODO(dbaron): What if the trigger is just part of the tree item? + element_roles_.insert(trigger_element, CSSToggleRole::kTreeItem); + break; + } + case CSSToggleRole::kAccordion: { + Element* trigger_element = + disclosure_ish_element_triggers.at(toggle_root); + element_roles_.insert(toggle_root, CSSToggleRole::kAccordionItem); + element_roles_.insert(trigger_element, + CSSToggleRole::kAccordionItemButton); + break; + } + case CSSToggleRole::kNone: + find_trigger_and_make_it_a_button = true; + break; + default: + // This could happen if some other part of the assignment + // logic touches it. + // TODO(dbaron): Do we want to do this? + find_trigger_and_make_it_a_button = true; + break; + } + } else { + const AtomicString& toggle_name = disclosure_ish_iter->value; + DCHECK(toggle_root->GetToggleMap()); + CSSToggle* toggle = + toggle_root->GetToggleMap()->Toggles().at(toggle_name); + DCHECK(toggle); + if (toggle->IsGroup()) { + // This is not a detectable pattern. + find_trigger_and_make_it_a_button = true; + } else { + // Find the panel (which we previously found when marking the + // element as disclosure-ish) to determine whether this is a + // popup or disclosure. + for (Element* e : + CSSToggleScopeRange(toggle_root, toggle_name, toggle->Scope())) { + const ComputedStyle* e_style = e->GetComputedStyle(); + if (e_style && e_style->ToggleVisibility() == toggle_name) { + bool positioned_or_popover = e_style->HasOutOfFlowPosition(); + if (!positioned_or_popover) { + HTMLElement* e_html = DynamicTo<HTMLElement>(e); + positioned_or_popover = + e_html && e_html->PopoverType() != PopoverValueType::kNone; + } + Element* trigger_element = + disclosure_ish_element_triggers.at(toggle_root); + if (positioned_or_popover) { + element_roles_.insert(trigger_element, + CSSToggleRole::kButtonWithPopup); + } else { + element_roles_.insert(toggle_root, CSSToggleRole::kDisclosure); + element_roles_.insert(trigger_element, + CSSToggleRole::kDisclosureButton); + } + break; + } + } + } + } + } else if (CSSToggle* tabs_ish_toggle = [&]() -> CSSToggle* { + if (!parent) { + return nullptr; + } + auto elements_iter = tabs_ish_elements.find(parent); + if (elements_iter == tabs_ish_elements.end()) { + return nullptr; + } + AtomicString toggle_name = elements_iter->value; + DCHECK(toggle_root->GetToggleMap()); + ToggleMap& map = toggle_root->GetToggleMap()->Toggles(); + auto toggles_iter = map.find(toggle_name); + if (toggles_iter == map.end()) { + return nullptr; + } + CSSToggle* toggle = toggles_iter->value; + DCHECK(toggle); + DCHECK_EQ(toggle->Name(), toggle_name); + return toggle; + }()) { + element_roles_.insert(parent, CSSToggleRole::kTabContainer); + element_roles_.insert(toggle_root, CSSToggleRole::kTab); + + for (Element* e : + CSSToggleScopeRange(toggle_root, tabs_ish_toggle->Name(), + tabs_ish_toggle->Scope())) { + const ComputedStyle* e_style = e->GetComputedStyle(); + if (e_style && e_style->ToggleVisibility() == tabs_ish_toggle->Name()) { + element_roles_.insert(e, CSSToggleRole::kTabPanel); + } + } + } else { + CSSToggleMap* toggle_map = toggle_root->GetToggleMap(); + DCHECK(toggle_map); + const CSSToggle* toggle_with_trigger = nullptr; + if (const ComputedStyle* style = toggle_root->GetComputedStyle()) { + for (const auto& [toggle_name, toggle] : toggle_map->Toggles()) { + if (const ToggleTriggerList* triggers = style->ToggleTrigger()) { + for (const ToggleTrigger& trigger : triggers->Triggers()) { + if (toggle_name == trigger.Name()) { + DCHECK_EQ(toggle_name, toggle->Name()); + toggle_with_trigger = toggle; + break; + } + } + } + if (toggle_with_trigger) { + break; + } + } + } + + if (toggle_with_trigger) { + bool found_visibility = false; + const AtomicString& toggle_name = toggle_with_trigger->Name(); + for (Element* e : CSSToggleScopeRange(toggle_root, toggle_name, + toggle_with_trigger->Scope())) { + const ComputedStyle* e_style = e->GetComputedStyle(); + if (e_style && e_style->ToggleVisibility() == toggle_name) { + found_visibility = true; + break; + } + } + + CSSToggleRole parent_role = CSSToggleRole::kNone; + if (found_visibility) { + // TODO(dbaron): The current spec draft says that this should + // be "no pattern detected", but it seems bad to fail to + // report something. I'm going to report button instead. + element_roles_.insert(toggle_root, CSSToggleRole::kButton); + } else if (toggle_with_trigger->IsGroup()) { + element_roles_.insert(toggle_root, CSSToggleRole::kRadioItem); + if (const ComputedStyle* parent_style = parent->GetComputedStyle()) { + if (const ToggleGroupList* parent_groups = + parent_style->ToggleGroup()) { + for (const ToggleGroup& group : parent_groups->Groups()) { + if (toggle_with_trigger->Name() == group.Name()) { + parent_role = CSSToggleRole::kRadioGroup; + break; + } + } + } + } + } else { + // TODO(dbaron): Maybe this should be Switch, depending on + // device. + element_roles_.insert(toggle_root, CSSToggleRole::kCheckbox); + // TODO(dbaron): We should only set parent_role here when + // there are multiple checkbox siblings with few non-checkbox + // siblings! + parent_role = CSSToggleRole::kCheckboxGroup; + } + + // TODO(dbaron): Figure out if we need to exclude additional + // cases where the parent would be assigned a different role (in + // order to ensure that hash map processing order doesn't affect + // the result). + if (parent_role != CSSToggleRole::kNone && parent && + !accordion_ish_elements.Contains(parent)) { + auto parent_add_result = element_roles_.insert(parent, parent_role); + // prefer checkbox group to radio group if some children + // lead to either + if (parent_add_result.stored_value->value != parent_role && + parent_add_result.stored_value->value == + CSSToggleRole::kRadioGroup) { + parent_add_result.stored_value->value = parent_role; + } + } + } else { + find_trigger_and_make_it_a_button = true; + } + } + + if (find_trigger_and_make_it_a_button) { + CSSToggleMap* toggle_map = toggle_root->GetToggleMap(); + DCHECK(toggle_map); + for (const auto& [toggle_name, toggle] : toggle_map->Toggles()) { + for (Element* e : + CSSToggleScopeRange(toggle_root, toggle_name, toggle->Scope())) { + if (const ComputedStyle* e_style = e->GetComputedStyle()) { + if (const ToggleTriggerList* triggers = e_style->ToggleTrigger()) { + for (const ToggleTrigger& trigger : triggers->Triggers()) { + if (trigger.Name() == toggle_name) { + element_roles_.insert(e, CSSToggleRole::kButton); + break; + } + } + } + } + } + } + } + } +} + +CSSToggleRole CSSToggleInference::RoleForElement(blink::Element* element) { + RebuildIfNeeded(); + + auto iter = element_roles_.find(element); + if (iter == element_roles_.end()) { + return CSSToggleRole::kNone; + } + + return iter->value; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/dom/css_toggle_inference.h b/third_party/blink/renderer/core/dom/css_toggle_inference.h new file mode 100644 index 0000000..ac679a31 --- /dev/null +++ b/third_party/blink/renderer/core/dom/css_toggle_inference.h
@@ -0,0 +1,104 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_INFERENCE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_INFERENCE_H_ + +#include <cstdint> + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/heap/member.h" + +namespace blink { + +class Document; +class Element; + +enum class CSSToggleRole : uint8_t { + kNone, + kAccordion, + kAccordionItem, + kAccordionItemButton, + kButton, + kButtonWithPopup, + kCheckbox, + kCheckboxGroup, + kDisclosure, + kDisclosureButton, + kListbox, + kListboxItem, + kRadioGroup, + kRadioItem, + kTab, + kTabContainer, + kTabPanel, + kTree, + kTreeGroup, + kTreeItem, +}; + +/** + * This is a prototype of the inference algorithm being designed in + * https://github.com/tabatkins/css-toggle/issues/41 . Its purpose is + * to infer information that can be relatively easily mapped to ARIA + * roles and properties based on CSS Toggle properties (toggle-root, + * toggle-trigger, toggle-group, and toggle-visibility) and the DOM tree + * relationships of the elements with those properties. It can infer + * roles on elements that lack those properties; for example, it might + * infer a role for an element that is the parent of a set of elements + * with those properties. + * + * The output of the algorithm should be a mapping from DOM element to + * its inferred role and properties (if any). (When toggles are not + * used, inferred roles and properties should not be produced.) + * + * These outputs are intended to be used both for ARIA roles/properties + * and for default keyboard behaviors given to these elements. + * + * This implementation is intended as a prototype and its performance + * characteristics are not intended to be usable in a shipping + * implementation. A sufficiently performant implementation may need to + * have the toggles code maintain data structures that represent the + * relationships between the toggles (rather than the current code that + * searches the tree when necessary). + */ + +class CORE_EXPORT CSSToggleInference final + : public GarbageCollected<CSSToggleInference> { + public: + explicit CSSToggleInference(Document* document) : document_(document) {} + + void Trace(Visitor* visitor) const; + + void MarkNeedsRebuild() { needs_rebuild_ = true; } + + // This returns role information for the element that is inferred from + // the patterns of using CSS toggles (see comments above describing + // this class). + // + // These roles have some relationship to ARIA roles but are not the + // same. + // + // This role information is somewhat expensive to rebuild, and is + // information that does *not* change when toggle state changes. + CSSToggleRole RoleForElement(blink::Element* element); + + // TODO(dbaron): Add a separate API here for property (e.g., state) + // information that *does* sometimes change when toggle state is + // changed. + + private: + void RebuildIfNeeded(); + void Rebuild(); + + bool needs_rebuild_ = true; + Member<Document> document_; + HeapHashMap<Member<Element>, CSSToggleRole> element_roles_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_INFERENCE_H_
diff --git a/third_party/blink/renderer/core/dom/css_toggle_map.cc b/third_party/blink/renderer/core/dom/css_toggle_map.cc index e975e17..5fc02e7 100644 --- a/third_party/blink/renderer/core/dom/css_toggle_map.cc +++ b/third_party/blink/renderer/core/dom/css_toggle_map.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/dom/css_toggle_map.h" +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element_rare_data_field.h" @@ -39,10 +40,12 @@ // ElementsWithCSSToggles() from things that can happen mid-move. DCHECK(old_document.ElementsWithCSSToggles().Contains(element)); old_document.ElementsWithCSSToggles().erase(element); + old_document.EnsureCSSToggleInference().MarkNeedsRebuild(); - auto add_result = - element->GetDocument().ElementsWithCSSToggles().insert(element); + Document& new_document = element->GetDocument(); + auto add_result = new_document.ElementsWithCSSToggles().insert(element); DCHECK(add_result.is_new_entry); + new_document.EnsureCSSToggleInference().MarkNeedsRebuild(); } void CSSToggleMap::CreateToggles(const ToggleRootList* toggle_roots) {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 4eb4b6b..a6e4aa6 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -124,6 +124,7 @@ #include "third_party/blink/renderer/core/dom/cdata_section.h" #include "third_party/blink/renderer/core/dom/comment.h" #include "third_party/blink/renderer/core/dom/context_features.h" +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" #include "third_party/blink/renderer/core/dom/document_data.h" #include "third_party/blink/renderer/core/dom/document_fragment.h" #include "third_party/blink/renderer/core/dom/document_init.h" @@ -2442,6 +2443,13 @@ return true; } +CSSToggleInference& Document::EnsureCSSToggleInference() { + if (!css_toggle_inference_) { + css_toggle_inference_ = MakeGarbageCollected<CSSToggleInference>(this); + } + return *css_toggle_inference_; +} + void Document::ApplyScrollRestorationLogic() { DCHECK(View()); // This function is not re-entrant. However, the places that invoke this are @@ -8608,6 +8616,7 @@ visitor->Trace(popovers_waiting_to_hide_); visitor->Trace(elements_with_css_toggles_); visitor->Trace(elements_needing_style_recalc_for_toggle_); + visitor->Trace(css_toggle_inference_); visitor->Trace(load_event_delay_timer_); visitor->Trace(plugin_loading_timer_); visitor->Trace(elem_sheet_);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h index 255c64e3..11ba2d1 100644 --- a/third_party/blink/renderer/core/dom/document.h +++ b/third_party/blink/renderer/core/dom/document.h
@@ -131,6 +131,7 @@ class BeforeUnloadEventListener; class CDATASection; class CSSStyleSheet; +class CSSToggleInference; class CanvasFontCache; class ChromeClient; class Comment; @@ -1567,6 +1568,8 @@ // Call SetNeedsStyleRecalc for elements from AddToRecalcStyleForToggle; // return whether any calls were made. bool SetNeedsStyleRecalcForToggles(); + CSSToggleInference* GetCSSToggleInference() { return css_toggle_inference_; } + CSSToggleInference& EnsureCSSToggleInference(); // A non-null template_document_host_ implies that |this| was created by // EnsureTemplateDocument(). @@ -2414,6 +2417,8 @@ // Elements that need to be restyled because a toggle was created on them, // or a prior sibling, during the previous restyle. HeapHashSet<Member<Element>> elements_needing_style_recalc_for_toggle_; + // The inference engine for CSS toggles. + Member<CSSToggleInference> css_toggle_inference_; int load_event_delay_count_;
diff --git a/third_party/blink/renderer/core/events/toggle_event.h b/third_party/blink/renderer/core/events/toggle_event.h index b51ea83..a5fa756 100644 --- a/third_party/blink/renderer/core/events/toggle_event.h +++ b/third_party/blink/renderer/core/events/toggle_event.h
@@ -15,18 +15,17 @@ DEFINE_WRAPPERTYPEINFO(); public: - static ToggleEvent* Create() { return MakeGarbageCollected<ToggleEvent>(); } static ToggleEvent* Create(const AtomicString& type, const ToggleEventInit* initializer) { return MakeGarbageCollected<ToggleEvent>(type, initializer); } - static ToggleEvent* CreateBubble(const AtomicString& type, - Event::Cancelable cancelable, - const String& old_state, - const String& new_state) { + static ToggleEvent* Create(const AtomicString& type, + Event::Cancelable cancelable, + const String& old_state, + const String& new_state) { auto* event = MakeGarbageCollected<ToggleEvent>(type, cancelable, old_state, new_state); - event->SetBubbles(true); + DCHECK(!event->bubbles()); return event; }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h index e04ea0a2..cef49d1 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.h +++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -34,6 +34,7 @@ #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/mojom/content_security_policy.mojom-blink.h" #include "third_party/blink/public/common/frame/fullscreen_request_token.h" +#include "third_party/blink/public/common/frame/history_user_activation_state.h" #include "third_party/blink/public/common/frame/payment_request_token.h" #include "third_party/blink/public/common/metrics/post_message_counter.h" #include "third_party/blink/public/common/scheduler/task_attribution_id.h"
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc index 255328f..0ffd6827 100644 --- a/third_party/blink/renderer/core/frame/visual_viewport.cc +++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -169,7 +169,7 @@ GetChromeClient()->GetDeviceEmulationTransform(); if (!device_emulation_transform.IsIdentity()) { TransformPaintPropertyNode::State state{{device_emulation_transform}}; - state.in_subtree_of_page_scale = false; + state.flags.in_subtree_of_page_scale = false; if (!device_emulation_transform_node_) { device_emulation_transform_node_ = TransformPaintPropertyNode::Create( *transform_parent, std::move(state)); @@ -189,7 +189,7 @@ DCHECK(!transform_parent->Unalias().IsInSubtreeOfPageScale()); TransformPaintPropertyNode::State state; - state.in_subtree_of_page_scale = false; + state.flags.in_subtree_of_page_scale = false; // TODO(crbug.com/877794) Should create overscroll elasticity transform node // based on settings. if (!overscroll_elasticity_transform_node_) { @@ -214,7 +214,7 @@ TransformPaintPropertyNode::State state; if (scale_ != 1.f) state.transform_and_origin.matrix = gfx::Transform::MakeScale(scale_); - state.in_subtree_of_page_scale = false; + state.flags.in_subtree_of_page_scale = false; state.direct_compositing_reasons = CompositingReason::kViewport; state.compositor_element_id = page_scale_element_id_;
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc index c91a612..f075acd 100644 --- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc +++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -159,7 +159,7 @@ }; using ElementMetaParamsMap = - HeapHashMap<WeakMember<Element>, Member<const MetaParams>>; + HeapHashMap<WeakMember<const Element>, Member<const MetaParams>>; ElementMetaParamsMap& FullscreenParamsMap() { DEFINE_STATIC_LOCAL(Persistent<ElementMetaParamsMap>, map, @@ -167,22 +167,22 @@ return *map; } -bool HasFullscreenFlag(Element& element) { +bool HasFullscreenFlag(const Element& element) { return FullscreenParamsMap().Contains(&element); } -void SetFullscreenFlag(Element& element, +void SetFullscreenFlag(const Element& element, FullscreenRequestType request_type, const FullscreenOptions* options) { FullscreenParamsMap().insert( &element, MakeGarbageCollected<MetaParams>(request_type, options)); } -void UnsetFullscreenFlag(Element& element) { +void UnsetFullscreenFlag(const Element& element) { FullscreenParamsMap().erase(&element); } -FullscreenRequestType GetRequestType(Element& element) { +FullscreenRequestType GetRequestType(const Element& element) { return FullscreenParamsMap().find(&element)->value->request_type(); } @@ -1153,6 +1153,10 @@ // layer. This is done in Element::RemovedFrom. } +bool Fullscreen::IsFullscreenFlagSetFor(const Element& element) { + return HasFullscreenFlag(element); +} + void Fullscreen::Trace(Visitor* visitor) const { visitor->Trace(pending_requests_); visitor->Trace(pending_exits_);
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.h b/third_party/blink/renderer/core/fullscreen/fullscreen.h index a529d3f..b1a3790 100644 --- a/third_party/blink/renderer/core/fullscreen/fullscreen.h +++ b/third_party/blink/renderer/core/fullscreen/fullscreen.h
@@ -63,9 +63,14 @@ static Element* FullscreenElementFrom(Document&); static Element* FullscreenElementForBindingFrom(TreeScope&); + // Returns true if the Element is the topmost element in its document's top + // layer whose fullscreen flag is set. static bool IsFullscreenElement(const Element&); static bool IsInFullscreenElementStack(const Element&); static bool HasFullscreenElements(); + // Returns true if the Element's fullscreen flag is set. A Document may have + // multiple elements with the fullscreen flag set. + static bool IsFullscreenFlagSetFor(const Element&); static void RequestFullscreen(Element&); static ScriptPromise RequestFullscreen(
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc index 4743bfe6..08c286b 100644 --- a/third_party/blink/renderer/core/html/html_element.cc +++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1376,10 +1376,10 @@ } // Fire the "opening" beforetoggle event. - auto* event = ToggleEvent::CreateBubble( + auto* event = ToggleEvent::Create( event_type_names::kBeforetoggle, Event::Cancelable::kYes, /*old_state*/ "closed", /*new_state*/ "open"); - DCHECK(event->bubbles()); + DCHECK(!event->bubbles()); DCHECK(event->cancelable()); DCHECK_EQ(event->oldState(), "closed"); DCHECK_EQ(event->newState(), "open"); @@ -1467,12 +1467,12 @@ } else { GetPopoverData()->setPendingToggleEventStartedClosed(true); } - after_event = ToggleEvent::CreateBubble(event_type_names::kToggle, - Event::Cancelable::kNo, old_state, - /*new_state*/ "open"); + after_event = ToggleEvent::Create(event_type_names::kToggle, + Event::Cancelable::kNo, old_state, + /*new_state*/ "open"); DCHECK_EQ(after_event->newState(), "open"); DCHECK_EQ(after_event->oldState(), old_state); - DCHECK(after_event->bubbles()); + DCHECK(!after_event->bubbles()); DCHECK(!after_event->cancelable()); after_event->SetTarget(this); GetPopoverData()->setPendingToggleEventTask(PostCancellableTask( @@ -1624,10 +1624,10 @@ } // Fire the "closing" beforetoggle event. - auto* event = ToggleEvent::CreateBubble( + auto* event = ToggleEvent::Create( event_type_names::kBeforetoggle, Event::Cancelable::kNo, /*old_state*/ "open", /*new_state*/ "closed"); - DCHECK(event->bubbles()); + DCHECK(!event->bubbles()); DCHECK(!event->cancelable()); DCHECK_EQ(event->oldState(), "open"); DCHECK_EQ(event->newState(), "closed"); @@ -1677,12 +1677,12 @@ } else { GetPopoverData()->setPendingToggleEventStartedClosed(false); } - after_event = ToggleEvent::CreateBubble(event_type_names::kToggle, - Event::Cancelable::kNo, old_state, - /*new_state*/ "closed"); + after_event = ToggleEvent::Create(event_type_names::kToggle, + Event::Cancelable::kNo, old_state, + /*new_state*/ "closed"); DCHECK_EQ(after_event->newState(), "closed"); DCHECK_EQ(after_event->oldState(), old_state); - DCHECK(after_event->bubbles()); + DCHECK(!after_event->bubbles()); DCHECK(!after_event->cancelable()); after_event->SetTarget(this); GetPopoverData()->setPendingToggleEventTask(PostCancellableTask(
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 17a39e0..d7a5ad0 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -42,6 +42,7 @@ #include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" #include "third_party/blink/renderer/core/dom/css_toggle_map.h" #include "third_party/blink/renderer/core/dom/element_traversal.h" #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h" @@ -3114,6 +3115,12 @@ } } + if (old_style && + (old_style->ToggleTrigger() != StyleRef().ToggleTrigger() || + old_style->ToggleVisibility() != StyleRef().ToggleVisibility())) { + GetDocument().EnsureCSSToggleInference().MarkNeedsRebuild(); + } + if (StyleRef().AnchorName()) MarkMayHaveAnchorQuery(); }
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc index 9133670..61ed0bb 100644 --- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc +++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -756,8 +756,8 @@ return DispatchResult::kContinue; } - auto* script_state = ToScriptStateForMainWorld(window_->GetFrame()); - DCHECK(script_state); + LocalFrame* frame = window_->GetFrame(); + auto* script_state = ToScriptStateForMainWorld(frame); ScriptState::Scope scope(script_state); if (params->frame_load_type == WebFrameLoadType::kBackForward && @@ -796,8 +796,15 @@ } init->setDestination(destination); + bool should_allow_traversal_cancellation = + RuntimeEnabledFeatures::NavigateEventCancelableTraversalsEnabled() && + params->frame_load_type == WebFrameLoadType::kBackForward && + params->event_type != NavigateEventType::kCrossDocument && + frame->IsMainFrame() && + (!params->is_browser_initiated || frame->IsHistoryUserActivationActive()); init->setCancelable(params->frame_load_type != - WebFrameLoadType::kBackForward); + WebFrameLoadType::kBackForward || + should_allow_traversal_cancellation); init->setCanIntercept( CanChangeToUrlForHistoryApi(params->url, window_->GetSecurityOrigin(), current_url) && @@ -840,8 +847,13 @@ DispatchEvent(*navigate_event); if (navigate_event->defaultPrevented()) { - if (!navigate_event->signal()->aborted()) + if (!navigate_event->signal()->aborted()) { + if (params->frame_load_type == WebFrameLoadType::kBackForward && + window_->GetFrame()) { + window_->GetFrame()->ConsumeHistoryUserActivation(); + } FinalizeWithAbortedNavigationError(script_state, ongoing_navigation_); + } return DispatchResult::kAbort; }
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.h b/third_party/blink/renderer/core/navigation_api/navigation_api.h index b438c7da..01b0447 100644 --- a/third_party/blink/renderer/core/navigation_api/navigation_api.h +++ b/third_party/blink/renderer/core/navigation_api/navigation_api.h
@@ -23,6 +23,8 @@ namespace blink { +class DOMException; +class HistoryItem; class NavigationApiNavigation; class NavigationUpdateCurrentEntryOptions; class NavigationHistoryEntry; @@ -32,8 +34,7 @@ class NavigationResult; class NavigationOptions; class NavigationTransition; -class DOMException; -class HistoryItem; +class RegisteredEventListener; class SerializedScriptValue; class CORE_EXPORT NavigationApi final
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api_test.cc b/third_party/blink/renderer/core/navigation_api/navigation_api_test.cc index bef09df..c6a2667 100644 --- a/third_party/blink/renderer/core/navigation_api/navigation_api_test.cc +++ b/third_party/blink/renderer/core/navigation_api/navigation_api_test.cc
@@ -6,6 +6,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/commit_result/commit_result.mojom-blink.h" +#include "third_party/blink/public/platform/web_runtime_features.h" #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/loader/frame_load_request.h" @@ -14,6 +15,17 @@ namespace blink { +// static +HistoryItem* MakeHistoryItemFor(const KURL& url, const String& key) { + HistoryItem* item = MakeGarbageCollected<HistoryItem>(); + item->SetURL(url); + item->SetDocumentSequenceNumber(1234); + item->SetNavigationApiKey(key); + // The |item| has a unique default item sequence number. Reusing an item + // sequence number will suppress the naivgate event, so don't overwrite it. + return item; +} + class NavigationApiTest : public testing::Test { public: void TearDown() override { @@ -59,7 +71,7 @@ EXPECT_TRUE(client.BeginNavigationCalled()); } -TEST_F(NavigationApiTest, BrowserInitiatedSameDocumentBackForwardUncancelable) { +TEST_F(NavigationApiTest, BrowserInitiatedSameDocumentBackForward) { url_test_helpers::RegisterMockedURLLoad( url_test_helpers::ToKURL( "https://example.com/navigation-api/onnavigate-preventDefault.html"), @@ -69,12 +81,75 @@ web_view_helper.InitializeAndLoad( "https://example.com/navigation-api/onnavigate-preventDefault.html"); + LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame(); + DocumentLoader* document_loader = frame->Loader().GetDocumentLoader(); + const KURL& url = document_loader->Url(); + const String& key = document_loader->GetHistoryItem()->GetNavigationApiKey(); + // Emulate a same-document back-forward navigation initiated by browser UI. // It should be uncancelable, even though the onnavigate handler will try. - auto& frame_loader = web_view_helper.LocalMainFrame()->GetFrame()->Loader(); - HistoryItem* item = frame_loader.GetDocumentLoader()->GetHistoryItem(); - auto result = frame_loader.GetDocumentLoader()->CommitSameDocumentNavigation( - item->Url(), WebFrameLoadType::kBackForward, item, + auto result1 = document_loader->CommitSameDocumentNavigation( + url, WebFrameLoadType::kBackForward, MakeHistoryItemFor(url, key), + ClientRedirectPolicy::kNotClientRedirect, + false /* has_transient_user_activation */, nullptr /* initiator_origin */, + false /* is_synchronously_committed */, + mojom::blink::TriggeringEventInfo::kNotFromEvent, + true /* is_browser_initiated */, absl::nullopt); + EXPECT_EQ(result1, mojom::blink::CommitResult::Ok); + + // Now that there's been a user activation, the onnavigate handler should be + // able to cancel the navigation (which will consume the user activation). + LocalFrame::NotifyUserActivation( + frame, mojom::UserActivationNotificationType::kTest); + auto result2 = document_loader->CommitSameDocumentNavigation( + url, WebFrameLoadType::kBackForward, MakeHistoryItemFor(url, key), + ClientRedirectPolicy::kNotClientRedirect, + false /* has_transient_user_activation */, nullptr /* initiator_origin */, + false /* is_synchronously_committed */, + mojom::blink::TriggeringEventInfo::kNotFromEvent, + true /* is_browser_initiated */, absl::nullopt); + EXPECT_EQ(result2, mojom::blink::CommitResult::Aborted); + + // Having consumed the user activation, the onnavigate handler should not be + // able to cancel the next navigation. + auto result3 = document_loader->CommitSameDocumentNavigation( + url, WebFrameLoadType::kBackForward, MakeHistoryItemFor(url, key), + ClientRedirectPolicy::kNotClientRedirect, + false /* has_transient_user_activation */, nullptr /* initiator_origin */, + false /* is_synchronously_committed */, + mojom::blink::TriggeringEventInfo::kNotFromEvent, + true /* is_browser_initiated */, absl::nullopt); + EXPECT_EQ(result3, mojom::blink::CommitResult::Ok); +} + +TEST_F(NavigationApiTest, BrowserInitiatedSameDocumentBackForwardUncancelable) { + // Disable the feature that allows navigate events to cancel traversals, and + // ensure that the cancellation fails. + WebRuntimeFeatures::EnableFeatureFromString( + "NavigateEventCancelableTraversals", false); + + url_test_helpers::RegisterMockedURLLoad( + url_test_helpers::ToKURL( + "https://example.com/navigation-api/onnavigate-preventDefault.html"), + test::CoreTestDataPath("navigation-api/onnavigate-preventDefault.html")); + + frame_test_helpers::WebViewHelper web_view_helper; + web_view_helper.InitializeAndLoad( + "https://example.com/navigation-api/onnavigate-preventDefault.html"); + + LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame(); + DocumentLoader* document_loader = frame->Loader().GetDocumentLoader(); + const KURL& url = document_loader->Url(); + const String& key = document_loader->GetHistoryItem()->GetNavigationApiKey(); + + // Emulate a same-document back-forward navigation initiated by browser UI and + // with user activation. The navigate event would be cancelable if + // kNavigateEventCancelableTraversals were enabled, but since we disabled it, + // the cancel should fail and the navigation should proceed. + LocalFrame::NotifyUserActivation( + frame, mojom::UserActivationNotificationType::kTest); + auto result = document_loader->CommitSameDocumentNavigation( + url, WebFrameLoadType::kBackForward, MakeHistoryItemFor(url, key), ClientRedirectPolicy::kNotClientRedirect, /*has_transient_user_activation=*/false, /*initiator_origin=*/nullptr, /*is_synchronously_committed=*/false,
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 6e361af..f3d5c565 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -644,7 +644,7 @@ if (paint_offset_translation) { TransformPaintPropertyNode::State state{ {gfx::Transform::MakeTranslation(*paint_offset_translation)}}; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.rendering_context_id = context_.rendering_context_id; state.direct_compositing_reasons = @@ -662,7 +662,7 @@ if (IsA<LayoutView>(object_)) { DCHECK(object_.GetFrame()); - state.is_frame_paint_offset_translation = true; + state.flags.is_frame_paint_offset_translation = true; state.visible_frame_element_id = object_.GetFrame()->GetVisibleToHitTesting() ? CompositorElementIdFromUniqueObjectId( @@ -707,7 +707,7 @@ box_model.UniqueId(), CompositorElementIdNamespace::kStickyTranslation); state.rendering_context_id = context_.rendering_context_id; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; if (state.direct_compositing_reasons) { @@ -804,7 +804,7 @@ box.UniqueId(), CompositorElementIdNamespace::kAnchorScrollTranslation); state.rendering_context_id = context_.rendering_context_id; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.anchor_scroll_containers_data = @@ -944,10 +944,10 @@ // be included here, such as setting animation_is_axis_aligned. state.direct_compositing_reasons = direct_compositing_reasons & CompositingReasonsForTransformProperty(); - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.rendering_context_id = context_.rendering_context_id; - state.is_for_svg_child = true; + state.flags.is_for_svg_child = true; state.compositor_element_id = GetCompositorElementId( CompositorElementIdNamespace::kPrimaryTransform); @@ -1164,7 +1164,7 @@ // TODO(crbug.com/1185254): Make this work correctly for block // fragmentation. It's the size of each individual NGPhysicalBoxFragment // that's interesting, not the total LayoutBox size. - state.animation_is_axis_aligned = + state.flags.animation_is_axis_aligned = UpdateBoxSizeAndCheckActiveAnimationAxisAlignment( box, full_context_.direct_compositing_reasons); } @@ -1173,7 +1173,7 @@ full_context_.direct_compositing_reasons & compositing_reasons_for_property; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; if (running_on_compositor_test) { state.compositor_element_id = @@ -2264,7 +2264,7 @@ {matrix, gfx::Point3F(PerspectiveOrigin(To<LayoutBox>(object_)) + gfx::Vector2dF(context_.current.paint_offset))}}; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.rendering_context_id = context_.rendering_context_id; OnUpdateTransform(properties_->UpdatePerspective( @@ -2298,7 +2298,7 @@ if (!content_to_parent_space.IsIdentity()) { TransformPaintPropertyNode::State state; state.transform_and_origin = {content_to_parent_space.ToTransform()}; - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.rendering_context_id = context_.rendering_context_id; OnUpdateTransform(properties_->UpdateReplacedContentTransform( @@ -2435,7 +2435,7 @@ box.GetScrollableArea()->PendingScrollAnchorAdjustment(); box.GetScrollableArea()->ClearPendingScrollAnchorAdjustment(); } - state.flattens_inherited_transform = + state.flags.flattens_inherited_transform = context_.should_flatten_inherited_transform; state.rendering_context_id = context_.rendering_context_id; state.direct_compositing_reasons = @@ -2447,7 +2447,7 @@ // scrolling contents. if (object_.HasTransform() && object_.StyleRef().BackfaceVisibility() == EBackfaceVisibility::kHidden) { - state.delegates_to_parent_for_backface = true; + state.flags.delegates_to_parent_for_backface = true; } auto effective_change_type = properties_->UpdateScrollTranslation(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc index 138cb79..0dfb3069 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.cc
@@ -80,11 +80,6 @@ return ax::mojom::blink::Role::kGenericContainer; } -bool AXImageMapLink::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - return AccessibilityIsIgnoredByDefault(ignored_reasons); -} - Element* AXImageMapLink::ActionElement() const { return AnchorElement(); }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h index f47e92aa..eb9b5a75 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h +++ b/third_party/blink/renderer/modules/accessibility/ax_image_map_link.h
@@ -54,7 +54,6 @@ HTMLMapElement* MapElement() const; ax::mojom::blink::Role NativeRoleIgnoringAria() const override; - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; bool CanHaveChildren() const override { // If the area has child nodes, those will be rendered, and the combination // of Role::kGenericContainer and CanHaveChildren() = true allows for those
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc index 0bfd1de..8a14915 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -373,28 +373,6 @@ // Whether objects are ignored, i.e. not included in the tree. // -AXObjectInclusion AXLayoutObject::DefaultObjectInclusion( - IgnoredReasons* ignored_reasons) const { - if (!layout_object_) { - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); - return kIgnoreObject; - } - - if (layout_object_->Style()->Visibility() != EVisibility::kVisible) { - // aria-hidden is meant to override visibility as the determinant in AX - // hierarchy inclusion. - if (AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty::kHidden)) - return kDefaultBehavior; - - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXNotVisible)); - return kIgnoreObject; - } - - return AXObject::DefaultObjectInclusion(ignored_reasons); -} - // Is this the anonymous placeholder for a text control? bool AXLayoutObject::IsPlaceholder() const { AXObject* parent_object = ParentObject(); @@ -419,34 +397,13 @@ DCHECK(initialized_); #endif - // All nodes must have an unignored parent within their tree under the root - // node of the main web area, so force that node to always be unignored. - // The web area for a <select>'s' popup document is ignored, because the - // popup object hierarchy is constructed without the document root. - if (IsA<Document>(GetNode())) { - return CachedParentObject() && CachedParentObject()->IsMenuList(); - } - - const Node* node = GetNode(); - if (IsA<HTMLHtmlElement>(node)) - return true; - - if (!layout_object_) { - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); - return true; - } - // Ignore continuations, they're duplicate copies of inline nodes with blocks // inside. AXObjects are no longer created for these. DCHECK(!layout_object_->IsElementContinuation()); - // Check first if any of the common reasons cause this element to be ignored. - AXObjectInclusion default_inclusion = DefaultObjectInclusion(ignored_reasons); - if (default_inclusion == kIncludeObject) - return false; - if (default_inclusion == kIgnoreObject) + if (AXObject::ShouldIgnoreForHiddenOrInert(ignored_reasons)) { return true; + } AXObjectInclusion semantic_inclusion = ShouldIncludeBasedOnSemantics(ignored_reasons); @@ -458,6 +415,7 @@ // Inner editor element of editable area with empty text provides bounds // used to compute the character extent for index 0. This is the same as // what the caret's bounds would be if the editable area is focused. + Node* node = GetNode(); if (node) { const TextControlElement* text_control = EnclosingTextControl(node); if (text_control) { @@ -488,16 +446,12 @@ } } - // The SVG-AAM says the foreignObject element is normally presentational. - if (layout_object_->IsSVGForeignObjectIncludingNG()) { - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXPresentational)); - return true; - } - - // Make sure renderers with layers stay in the tree. - if (GetLayoutObject() && GetLayoutObject()->HasLayer() && node && - node->hasChildren()) { + // Layers are used on objects that have styles where Blink is likely to + // attempt to optimize them in for the GPU, such as animations, z-indexing and + // hidden overflow. Ensure layered objects are unignored, except for <html>. + // TODO(accessibility) There is no clear reason to specifically include these, + // consider removal of this special case. + if (layout_object_->HasLayer() && node && node->hasChildren()) { return false; } @@ -505,6 +459,7 @@ if (CanvasHasFallbackContent()) return false; + // A 1x1 canvas is too small for the user to see and thus ignored. const auto* canvas = DynamicTo<LayoutHTMLCanvas>(GetLayoutObject()); if (canvas && (canvas->Size().Height() <= 1 || canvas->Size().Width() <= 1)) { @@ -598,14 +553,6 @@ } } - // If setting enabled, do not ignore SVG grouping (<g>) elements. - if (IsA<SVGGElement>(node)) { - Settings* settings = GetDocument()->GetSettings(); - if (settings->GetAccessibilityIncludeSvgGElement()) { - return false; - } - } - // By default, objects should be ignored so that the AX hierarchy is not // filled with unnecessary items. if (ignored_reasons) @@ -748,18 +695,35 @@ : result->DeepestLastChildIncludingIgnored(); } -// Note: |NextOnLineInternalNG()| returns null when fragment for |layout_object| -// is culled as legacy layout version since |LayoutInline::LastLineBox()| -// returns null when it is culled. -// See also |PreviousOnLineInternalNG()| which is identical except for using -// "next" and |back()| instead of "previous" and |front()|. -static AXObject* NextOnLineInternalNG(const AXObject& ax_object) { - DCHECK(!ax_object.IsDetached()); - const LayoutObject* layout_object = ax_object.GetLayoutObject(); +AXObject* AXLayoutObject::NextOnLine() const { + // If this is the last object on the line, nullptr is returned. Otherwise, all + // AXLayoutObjects, regardless of role and tree depth, are connected to the + // next inline text box on the same line. If there is no inline text box, they + // are connected to the next leaf AXObject. + DCHECK(!IsDetached()); + + const LayoutObject* layout_object = GetLayoutObject(); DCHECK(layout_object); - DCHECK(ShouldUseLayoutNG(*layout_object)) << layout_object; - if (layout_object->IsBoxListMarkerIncludingNG() || - !layout_object->IsInLayoutNGInlineFormattingContext()) { + + if (DisplayLockUtilities::LockedAncestorPreventingPaint(*layout_object)) { + return nullptr; + } + + if (layout_object->IsBoxListMarkerIncludingNG()) { + // A list marker should be followed by a list item on the same line. + // Note that pseudo content is always included in the tree, so + // NextSiblingIncludingIgnored() will succeed. + if (AccessibilityIsIncludedInTree()) { + return GetDeepestAXChildInLayoutTree(NextSiblingIncludingIgnored(), true); + } + return nullptr; + } + + if (!ShouldUseLayoutNG(*layout_object)) { + return nullptr; + } + + if (!layout_object->IsInLayoutNGInlineFormattingContext()) { return nullptr; } @@ -785,8 +749,7 @@ if (cursor) { LayoutObject* runner_layout_object = cursor.CurrentMutableLayoutObject(); DCHECK(runner_layout_object); - AXObject* result = - ax_object.AXObjectCache().GetOrCreate(runner_layout_object); + AXObject* result = AXObjectCache().GetOrCreate(runner_layout_object); result = GetDeepestAXChildInLayoutTree(result, true); if (result) return result; @@ -798,19 +761,14 @@ // line as its first line. if (layout_object->NextSibling()) return nullptr; // Not at end of parent layout object. - if (!ax_object.ParentObject()) { - NOTREACHED(); - return nullptr; - } - // Fallback: Use AX parent's next on line. - AXObject* ax_parent = ax_object.ParentObject(); + AXObject* ax_parent = ParentObject(); + DCHECK(ax_parent); AXObject* ax_result = ax_parent->NextOnLine(); if (!ax_result) return nullptr; - if (!ax_object.AXObjectCache().IsAriaOwned(&ax_object) && - ax_result->ParentObject() == &ax_object) { + if (!AXObjectCache().IsAriaOwned(this) && ax_result->ParentObject() == this) { // NextOnLine() must not point to a child of the current object. // Because inline objects try to return a result from their // parents, using a descendant can cause a previous position to be @@ -822,96 +780,31 @@ return ax_result; } -AXObject* AXLayoutObject::NextOnLine() const { - // If this is the last object on the line, nullptr is returned. Otherwise, all - // AXLayoutObjects, regardless of role and tree depth, are connected to the - // next inline text box on the same line. If there is no inline text box, they - // are connected to the next leaf AXObject. - if (IsDetached()) { - NOTREACHED(); - return nullptr; - } +AXObject* AXLayoutObject::PreviousOnLine() const { + // If this is the first object on the line, nullptr is returned. Otherwise, + // all AXLayoutObjects, regardless of role and tree depth, are connected to + // the previous inline text box on the same line. If there is no inline text + // box, they are connected to the previous leaf AXObject. + DCHECK(!IsDetached()); - DCHECK(GetLayoutObject()); - - if (DisplayLockUtilities::LockedAncestorPreventingPaint(*GetLayoutObject())) { - return nullptr; - } - - if (GetLayoutObject()->IsBoxListMarkerIncludingNG()) { - // A list marker should be followed by a list item on the same line. - // Note that pseudo content is always included in the tree, so - // NextSiblingIncludingIgnored() will succeed. - return GetDeepestAXChildInLayoutTree(NextSiblingIncludingIgnored(), true); - } - - if (ShouldUseLayoutNG(*GetLayoutObject())) { - return NextOnLineInternalNG(*this); - } - - InlineBox* inline_box = nullptr; - if (GetLayoutObject()->IsBox()) { - inline_box = To<LayoutBox>(GetLayoutObject())->InlineBoxWrapper(); - } else if (GetLayoutObject()->IsLayoutInline()) { - // For performance and memory consumption, LayoutInline may ignore some - // inline-boxes during line layout because they don't actually impact - // layout. This is known as "culled inline". We have to recursively look - // to the LayoutInline's children via "LastLineBoxIncludingCulling". - inline_box = - To<LayoutInline>(GetLayoutObject())->LastLineBoxIncludingCulling(); - } else if (GetLayoutObject()->IsText()) { - inline_box = To<LayoutText>(GetLayoutObject())->LastTextBox(); - } - - if (!inline_box) - return nullptr; - - for (InlineBox* next = inline_box->NextOnLine(); next; - next = next->NextOnLine()) { - LayoutObject* layout_object = - LineLayoutAPIShim::LayoutObjectFrom(next->GetLineLayoutItem()); - AXObject* result = AXObjectCache().GetOrCreate(layout_object); - result = GetDeepestAXChildInLayoutTree(result, true); - if (result) - return result; - } - - // Our parent object could have been created based on an ignored inline or - // inline block spanning multiple lines. We need to ensure that we are - // really at the end of our parent before attempting to connect to the - // next AXObject that is on the same line as its last line. - // - // For example, look at the following layout tree: - // LayoutBlockFlow - // ++LayoutInline - // ++++LayoutText "Beginning of line one " - // ++++AnonymousLayoutInline - // ++++++LayoutText "end of line one" - // ++++++LayoutBR - // ++++++LayoutText "Beginning of line two " - // ++++LayoutText "End of line two" - // - // If we are on kStaticText "End of line one", and retrieve the parent - // AXObject, it will be the anonymous layout inline which actually ends - // somewhere in the second line, not the first line. Its "NextOnLine" - // AXObject will be kStaticText "End of line two", which is obviously - // wrong. - if (!GetLayoutObject()->NextSibling()) - return ParentObject()->NextOnLine(); - - return nullptr; -} - -// Note: |PreviousOnLineInlineNG()| returns null when fragment for -// |layout_object| is culled as legacy layout version since -// |LayoutInline::FirstLineBox()| returns null when it is culled. See also -// |NextOnLineNG()| which is identical except for using "previous" and |front()| -// instead of "next" and |back()|. -static AXObject* PreviousOnLineInlineNG(const AXObject& ax_object) { - DCHECK(!ax_object.IsDetached()); - const LayoutObject* layout_object = ax_object.GetLayoutObject(); + const LayoutObject* layout_object = GetLayoutObject(); DCHECK(layout_object); - DCHECK(ShouldUseLayoutNG(*layout_object)) << layout_object; + if (!ShouldUseLayoutNG(*layout_object)) { + return nullptr; + } + + if (DisplayLockUtilities::LockedAncestorPreventingPaint(*layout_object)) { + return nullptr; + } + + AXObject* previous_sibling = AccessibilityIsIncludedInTree() + ? PreviousSiblingIncludingIgnored() + : nullptr; + if (previous_sibling && previous_sibling->GetLayoutObject() && + previous_sibling->GetLayoutObject()->IsLayoutNGOutsideListMarker()) { + // A list item should be preceded by a list marker on the same line. + return GetDeepestAXChildInLayoutTree(previous_sibling, false); + } if (layout_object->IsBoxListMarkerIncludingNG() || !layout_object->IsInLayoutNGInlineFormattingContext()) { @@ -940,8 +833,7 @@ if (cursor) { LayoutObject* runner_layout_object = cursor.CurrentMutableLayoutObject(); DCHECK(runner_layout_object); - AXObject* result = - ax_object.AXObjectCache().GetOrCreate(runner_layout_object); + AXObject* result = AXObjectCache().GetOrCreate(runner_layout_object); result = GetDeepestAXChildInLayoutTree(result, false); if (result) return result; @@ -954,19 +846,14 @@ if (layout_object->PreviousSibling()) return nullptr; // Not at start of parent layout object. - if (!ax_object.ParentObject()) { - NOTREACHED(); - return nullptr; - } - // Fallback: Use AX parent's previous on line. - AXObject* ax_parent = ax_object.ParentObject(); + AXObject* ax_parent = ParentObject(); + DCHECK(ax_parent); AXObject* ax_result = ax_parent->PreviousOnLine(); if (!ax_result) return nullptr; - if (!ax_object.AXObjectCache().IsAriaOwned(&ax_object) && - ax_result->ParentObject() == &ax_object) { + if (!AXObjectCache().IsAriaOwned(this) && ax_result->ParentObject() == this) { // PreviousOnLine() must not point to a child of the current object. // Because inline objects without try to return a result from their // parents, using a descendant can cause a previous position to be @@ -978,85 +865,6 @@ return ax_result; } -AXObject* AXLayoutObject::PreviousOnLine() const { - // If this is the first object on the line, nullptr is returned. Otherwise, - // all AXLayoutObjects, regardless of role and tree depth, are connected to - // the previous inline text box on the same line. If there is no inline text - // box, they are connected to the previous leaf AXObject. - if (IsDetached()) { - NOTREACHED(); - return nullptr; - } - - DCHECK(GetLayoutObject()); - - if (DisplayLockUtilities::LockedAncestorPreventingPaint(*GetLayoutObject())) { - return nullptr; - } - - AXObject* previous_sibling = AccessibilityIsIncludedInTree() - ? PreviousSiblingIncludingIgnored() - : nullptr; - if (previous_sibling && previous_sibling->GetLayoutObject() && - previous_sibling->GetLayoutObject()->IsLayoutNGOutsideListMarker()) { - // A list item should be preceded by a list marker on the same line. - return GetDeepestAXChildInLayoutTree(previous_sibling, false); - } - - if (ShouldUseLayoutNG(*GetLayoutObject())) - return PreviousOnLineInlineNG(*this); - - InlineBox* inline_box = nullptr; - if (GetLayoutObject()->IsBox()) { - inline_box = To<LayoutBox>(GetLayoutObject())->InlineBoxWrapper(); - } else if (GetLayoutObject()->IsLayoutInline()) { - // For performance and memory consumption, LayoutInline may ignore some - // inline-boxes during line layout because they don't actually impact - // layout. This is known as "culled inline". We have to recursively look - // to the LayoutInline's children via "FirstLineBoxIncludingCulling". - inline_box = - To<LayoutInline>(GetLayoutObject())->FirstLineBoxIncludingCulling(); - } else if (GetLayoutObject()->IsText()) { - inline_box = To<LayoutText>(GetLayoutObject())->FirstTextBox(); - } - - if (!inline_box) - return nullptr; - - for (InlineBox* prev = inline_box->PrevOnLine(); prev; - prev = prev->PrevOnLine()) { - LayoutObject* layout_object = - LineLayoutAPIShim::LayoutObjectFrom(prev->GetLineLayoutItem()); - AXObject* result = AXObjectCache().GetOrCreate(layout_object); - result = GetDeepestAXChildInLayoutTree(result, false); - if (result) - return result; - } - - // Our parent object could have been created based on an ignored inline or - // inline block spanning multiple lines. We need to ensure that we are - // at the start of our parent layout object before attempting to connect - // to the previous AXObject that is on the same line as its first line. - // - // For example, fook at the following layout tree: - // LayoutBlockFlow - // ++LayoutInline - // ++++LayoutText "Beginning of line one " - // ++++AnonymousLayoutInline - // ++++++LayoutText "end of line one" - // ++++++LayoutBR - // ++++++LayoutText "Line two" - // - // If we are on kStaticText "Line two", and retrieve the parent AXObject, - // it will be the anonymous layout inline which actually started somewhere - // in the first line, not the second line. Its "PreviousOnLine" AXObject - // will be kStaticText "Start of line one", which is obviously wrong. - if (!GetLayoutObject()->PreviousSibling()) - return ParentObject()->PreviousOnLine(); - - return nullptr; -} - // // Properties of interactive elements. //
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h index c3954e4..4d356ff 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -83,8 +83,6 @@ bool IsNotUserSelectable() const override; // Whether objects are ignored, i.e. not included in the tree. - AXObjectInclusion DefaultObjectInclusion( - IgnoredReasons* = nullptr) const override; bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; // Properties of static elements.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_list_box_option.cc b/third_party/blink/renderer/modules/accessibility/ax_list_box_option.cc index 9b40a67..95ccf343 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_list_box_option.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_list_box_option.cc
@@ -63,17 +63,6 @@ return list_box_parent_node->ActiveSelectionEnd() == GetNode(); } -bool AXListBoxOption::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - if (!GetNode()) - return true; - - if (AccessibilityIsIgnoredByDefault(ignored_reasons)) - return true; - - return false; -} - String AXListBoxOption::TextAlternative( bool recursive, const AXObject* aria_label_or_description_root,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_list_box_option.h b/third_party/blink/renderer/modules/accessibility/ax_list_box_option.h index 202b37d6..9de61dd8 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_list_box_option.h +++ b/third_party/blink/renderer/modules/accessibility/ax_list_box_option.h
@@ -61,7 +61,6 @@ private: bool CanHaveChildren() const override { return false; } - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; HTMLSelectElement* ListBoxOptionParentNode() const; };
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc index c0e02fbd..b1f8800 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_media_element.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.cc
@@ -48,11 +48,6 @@ return true; } -bool AccessibilityMediaElement::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - return false; -} - AXRestriction AccessibilityMediaElement::Restriction() const { if (IsUnplayable()) return kRestrictionDisabled;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_element.h b/third_party/blink/renderer/modules/accessibility/ax_media_element.h index a243426..2f80450 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_media_element.h +++ b/third_party/blink/renderer/modules/accessibility/ax_media_element.h
@@ -34,7 +34,6 @@ // AXNodeObject overrides. bool CanHaveChildren() const override; - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; AXRestriction Restriction() const override; protected:
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc index b548ad3..4f2e025 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_option.cc
@@ -137,6 +137,9 @@ return true; } +// TODO(aleventhal) This override could go away, but it will cause a lot of +// test changes, as invisible options inside of a collapsed <select> will become +// ignored since they have no layout object. bool AXMenuListOption::ComputeAccessibilityIsIgnored( IgnoredReasons* ignored_reasons) const { if (IsDetached()) { @@ -148,7 +151,14 @@ html_names::kHiddenAttr)) return true; - return AccessibilityIsIgnoredByDefault(ignored_reasons); + if (IsAriaHidden()) { + if (ignored_reasons) { + ComputeIsAriaHidden(ignored_reasons); + } + return true; + } + + return ParentObject()->ComputeAccessibilityIsIgnored(ignored_reasons); } void AXMenuListOption::GetRelativeBounds(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_mock_object.cc b/third_party/blink/renderer/modules/accessibility/ax_mock_object.cc index 1b70b6d2..4fa4c2f 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_mock_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_mock_object.cc
@@ -34,11 +34,6 @@ AXMockObject::~AXMockObject() = default; -bool AXMockObject::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - return AccessibilityIsIgnoredByDefault(ignored_reasons); -} - Document* AXMockObject::GetDocument() const { return ParentObject() ? ParentObject()->GetDocument() : nullptr; }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_mock_object.h b/third_party/blink/renderer/modules/accessibility/ax_mock_object.h index c5c2afe4..6dd49c4e 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_mock_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_mock_object.h
@@ -53,9 +53,6 @@ bool IsMockObject() const final { return true; } Document* GetDocument() const override; ax::mojom::blink::Role NativeRoleIgnoringAria() const override; - - private: - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; }; 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 e741aa7..bb34258 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -46,6 +46,7 @@ #include "third_party/blink/renderer/core/css/css_resolution_units.h" #include "third_party/blink/renderer/core/css/properties/longhands.h" #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" +#include "third_party/blink/renderer/core/dom/css_toggle_inference.h" #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h" #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" @@ -453,6 +454,12 @@ IgnoredReasons* ignored_reasons) const { DCHECK(GetDocument()); + // All nodes must have an unignored parent within their tree under + // the root node of the web area, so force that node to always be unignored. + if (IsA<Document>(GetNode())) { + return kIncludeObject; + } + if (IsPresentational()) { if (ignored_reasons) ignored_reasons->push_back(IgnoredReason(kAXPresentational)); @@ -472,6 +479,7 @@ // alt text. This can allow auto alt to be applied to them. if (IsImage()) return kIncludeObject; + return kDefaultBehavior; } @@ -487,8 +495,9 @@ } Element* element = GetElement(); - if (!element) + if (!element) { return kDefaultBehavior; + } if (IsA<SVGElement>(node)) { // The symbol element is used to define graphical templates which can be @@ -515,6 +524,14 @@ return kIncludeObject; } + // If setting enabled, do not ignore SVG grouping (<g>) elements. + if (IsA<SVGGElement>(node)) { + Settings* settings = GetDocument()->GetSettings(); + if (settings->GetAccessibilityIncludeSvgGElement()) { + return kIncludeObject; + } + } + // If we return kDefaultBehavior here, the logic related to inclusion of // clickable objects, links, controls, etc. will not be reached. We handle // SVG elements early to ensure properties in a <symbol> subtree do not @@ -524,7 +541,14 @@ if (IsTableLikeRole() || IsTableRowLikeRole() || IsTableCellLikeRole()) return kIncludeObject; - // All focusable elements except the <body> are included. + if (IsA<HTMLHtmlElement>(node)) { + if (ignored_reasons) { + ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); + } + return kIgnoreObject; + } + + // All focusable elements except the <body> and <html> are included. if (!IsA<HTMLBodyElement>(node) && CanSetFocusAttribute()) return kIncludeObject; @@ -579,6 +603,7 @@ ax::mojom::blink::Role::kAbbr, ax::mojom::blink::Role::kApplication, ax::mojom::blink::Role::kArticle, + ax::mojom::blink::Role::kAudio, ax::mojom::blink::Role::kBanner, ax::mojom::blink::Role::kBlockquote, ax::mojom::blink::Role::kComplementary, @@ -654,6 +679,7 @@ ax::mojom::blink::Role::kSubscript, ax::mojom::blink::Role::kSuperscript, ax::mojom::blink::Role::kTime, + ax::mojom::blink::Role::kVideo, }; if (always_included_computed_roles.find(RoleValue()) != @@ -705,9 +731,44 @@ return kIncludeObject; } + // The SVG-AAM says the foreignObject element is normally presentational. + if (IsA<SVGForeignObjectElement>(node)) { + if (ignored_reasons) { + ignored_reasons->push_back(IgnoredReason(kAXPresentational)); + } + return kIgnoreObject; + } + return kDefaultBehavior; } +bool AXNodeObject::ComputeAccessibilityIsIgnored( + IgnoredReasons* ignored_reasons) const { + if (AXObject::ComputeAccessibilityIsIgnored(ignored_reasons)) { + // Fallback elements inside of a <canvas> are invisible, but are not ignored + // if they are semantic and not aria-hidden or hidden via style. + if (IsAriaHidden() || IsHiddenViaStyle() || !GetNode()->parentElement() || + !GetNode()->parentElement()->IsInCanvasSubtree()) { + return true; + } + } + + // Handle content that is either visible or in a canvas subtree. + AXObjectInclusion include = ShouldIncludeBasedOnSemantics(ignored_reasons); + if (include == kIgnoreObject) { + return true; + } + + if (include == kDefaultBehavior && !IsA<Text>(GetNode())) { + if (ignored_reasons) { + ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); + } + return true; + } + + return false; +} + // static absl::optional<String> AXNodeObject::GetCSSAltText(const Node* node) { // CSS alt text rules allow text to be assigned to ::before/::after content. @@ -743,65 +804,6 @@ return absl::nullopt; } -bool AXNodeObject::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { -#if DCHECK_IS_ON() - // Double-check that an AXObject is never accessed before - // it's been initialized. - DCHECK(initialized_); -#endif - - // If we don't have a node, then ignore the node object. - // TODO(vmpstr/aleventhal): Investigate how this can happen. - if (!GetNode()) { - NOTREACHED(); - return true; - } - - // All nodes must have an unignored parent within their tree under - // the root node of the web area, so force that node to always be unignored. - if (IsA<Document>(GetNode())) { - return false; - } - - DCHECK_NE(role_, ax::mojom::blink::Role::kUnknown); - // Use AXLayoutObject::ComputeAccessibilityIsIgnored(). - DCHECK(!GetLayoutObject()); - - if (DisplayLockUtilities::IsDisplayLockedPreventingPaint(GetNode())) { - if (IsAriaHidden() || - DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock( - *GetNode(), DisplayLockActivationReason::kAccessibility)) { - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); - return true; - } - return ShouldIncludeBasedOnSemantics(ignored_reasons) == kIgnoreObject; - } - - auto* element = DynamicTo<Element>(GetNode()); - if (!element) - element = GetNode()->parentElement(); - - if (!element) - return true; - - if (element->IsInCanvasSubtree()) - return ShouldIncludeBasedOnSemantics(ignored_reasons) == kIgnoreObject; - - if (AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty::kHidden)) - return false; - - if (element->HasDisplayContentsStyle()) { - if (ShouldIncludeBasedOnSemantics(ignored_reasons) == kIncludeObject) - return false; - } - - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); - return true; -} - // The following lists are for deciding whether the tags aside, // header and footer can be interpreted as roles complementary, banner and // contentInfo or if they should be interpreted as generic. This list @@ -1342,6 +1344,75 @@ return RoleFromLayoutObjectOrNode(); } +namespace { + +ax::mojom::blink::Role InferredCSSToggleRole(Node* node) { + Element* element = DynamicTo<Element>(node); + if (!element) { + return ax::mojom::blink::Role::kUnknown; + } + + // toggle_inference is null when CSS toggles are not used in the document. + CSSToggleInference* toggle_inference = + element->GetDocument().GetCSSToggleInference(); + if (!toggle_inference) { + return ax::mojom::blink::Role::kUnknown; + } + + DCHECK(RuntimeEnabledFeatures::CSSTogglesEnabled()); + + switch (toggle_inference->RoleForElement(element)) { + case CSSToggleRole::kNone: + break; + case CSSToggleRole::kButtonWithPopup: + return ax::mojom::blink::Role::kPopUpButton; + case CSSToggleRole::kDisclosure: + break; + case CSSToggleRole::kDisclosureButton: + return ax::mojom::blink::Role::kButton; + case CSSToggleRole::kTree: + return ax::mojom::blink::Role::kTree; + case CSSToggleRole::kTreeGroup: + return ax::mojom::blink::Role::kGroup; + case CSSToggleRole::kTreeItem: + return ax::mojom::blink::Role::kTreeItem; + case CSSToggleRole::kAccordion: + break; + case CSSToggleRole::kAccordionItem: + return ax::mojom::blink::Role::kRegion; + case CSSToggleRole::kAccordionItemButton: + return ax::mojom::blink::Role::kButton; + case CSSToggleRole::kTabContainer: + // TODO(dbaron): We should verify that using kTabList really + // works here, since this is a container that has both the tab + // list *and* the tab panels. We should also make sure that + // posinset/setsize work correctly for the tabs. + return ax::mojom::blink::Role::kTabList; + case CSSToggleRole::kTab: + return ax::mojom::blink::Role::kTab; + case CSSToggleRole::kTabPanel: + return ax::mojom::blink::Role::kTabPanel; + case CSSToggleRole::kRadioGroup: + return ax::mojom::blink::Role::kRadioGroup; + case CSSToggleRole::kRadioItem: + return ax::mojom::blink::Role::kRadioButton; + case CSSToggleRole::kCheckboxGroup: + break; + case CSSToggleRole::kCheckbox: + return ax::mojom::blink::Role::kCheckBox; + case CSSToggleRole::kListbox: + return ax::mojom::blink::Role::kListBox; + case CSSToggleRole::kListboxItem: + return ax::mojom::blink::Role::kListBoxOption; + case CSSToggleRole::kButton: + return ax::mojom::blink::Role::kButton; + } + + return ax::mojom::blink::Role::kUnknown; +} + +} // namespace + ax::mojom::blink::Role AXNodeObject::DetermineAccessibilityRole() { #if DCHECK_IS_ON() base::AutoReset<bool> reentrancy_protector(&is_computing_role_, true); @@ -1356,8 +1427,25 @@ aria_role_ = DetermineAriaRoleAttribute(); - return aria_role_ == ax::mojom::blink::Role::kUnknown ? native_role_ - : aria_role_; + // Order of precedence is currently: + // 1. ARIA role + // 2. Inferred role from CSS Toggle inference engine + // 3. Native markup role + // but we may decide to change how the CSS Toggle inference fits in. + // + // TODO(dbaron): Perhaps revisit whether there are types of elements + // where toggles should not work. + + if (aria_role_ != ax::mojom::blink::Role::kUnknown) { + return aria_role_; + } + + ax::mojom::blink::Role css_toggle_role = InferredCSSToggleRole(GetNode()); + if (css_toggle_role != ax::mojom::blink::Role::kUnknown) { + return css_toggle_role; + } + + return native_role_; } void AXNodeObject::AccessibilityChildrenFromAOMProperty( @@ -1416,7 +1504,8 @@ #endif AXObject::Init(parent); - DCHECK(role_ == native_role_ || role_ == aria_role_) + DCHECK(role_ == native_role_ || role_ == aria_role_ || + GetNode()->GetDocument().GetCSSToggleInference()) << "Role must be either the cached native role or cached aria role: " << "\n* Final role: " << role_ << "\n* Native role: " << native_role_ << "\n* Aria role: " << aria_role_ << "\n* Node: " << GetNode();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index da11b71..4e5236b9 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2837,6 +2837,10 @@ << "\nThe Detach() method sets cached_is_ignored_ to true, but " "something has recomputed it."; } + if (!cached_is_ignored_ && IsA<Document>(GetNode()) && CachedParentObject() && + CachedParentObject()->IsMenuList()) { + NOTREACHED() << "The menulist popup's document must be ignored."; + } #endif return cached_is_ignored_; } @@ -2888,7 +2892,7 @@ const ComputedStyle* style = GetComputedStyle(); - cached_is_hidden_via_style = ComputeIsHiddenViaStyle(style); + cached_is_hidden_via_style_ = ComputeIsHiddenViaStyle(style); // Decisions in what subtree descendants are included (each descendant's // cached children_) depends on the ARIA hidden state. When it changes, @@ -2973,30 +2977,64 @@ } } -bool AXObject::AccessibilityIsIgnoredByDefault( +bool AXObject::ComputeAccessibilityIsIgnored( IgnoredReasons* ignored_reasons) const { - return DefaultObjectInclusion(ignored_reasons) == kIgnoreObject; + return ShouldIgnoreForHiddenOrInert(ignored_reasons); } -AXObjectInclusion AXObject::DefaultObjectInclusion( +bool AXObject::ShouldIgnoreForHiddenOrInert( IgnoredReasons* ignored_reasons) const { - if (IsAriaHidden()) { + DCHECK(AXObjectCache().ModificationCount() == last_modification_count_) + << "Hidden values must be computed before ignored."; + + // All nodes must have an unignored parent within their tree under + // the root node of the web area, so force that node to always be unignored. + if (IsA<Document>(GetNode())) { + return false; + } + + if (cached_is_aria_hidden_) { // Keep keyboard focusable elements that are aria-hidden in tree, so that // they can still fire events such as focus and value changes. if (!IsKeyboardFocusable()) { if (ignored_reasons) ComputeIsAriaHidden(ignored_reasons); - return kIgnoreObject; + return true; } } - if (IsInert()) { - if (ignored_reasons) + if (cached_is_inert_) { + if (ignored_reasons) { ComputeIsInert(ignored_reasons); - return kIgnoreObject; + } + return true; } - return kDefaultBehavior; + // aria-hidden=false is meant to override visibility as the determinant in + // AX hierarchy inclusion, but only for the element it is specified, and not + // the entire subtree. See https://w3c.github.io/aria/#aria-hidden. + if (AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty::kHidden)) { + return false; + } + + if (cached_is_hidden_via_style_) { + if (ignored_reasons) { + ignored_reasons->push_back( + IgnoredReason(GetLayoutObject() ? kAXNotVisible : kAXNotRendered)); + } + return true; + } + + // Hide nodes that are whitespace or are occluded by CSS alt text. + if (!GetLayoutObject() && GetNode() && !IsA<HTMLAreaElement>(GetNode()) && + !DisplayLockUtilities::IsDisplayLockedPreventingPaint(GetNode())) { + if (ignored_reasons) { + ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); + } + return true; + } + + return false; } // Note: do not rely on the value of this inside of display:none. @@ -4000,7 +4038,7 @@ bool AXObject::IsHiddenViaStyle() const { UpdateCachedAttributeValuesIfNeeded(); - return cached_is_hidden_via_style; + return cached_is_hidden_via_style_; } // Return true if this should be removed from accessible name computations. @@ -6962,8 +7000,9 @@ } else if (AriaHiddenRoot()) { string_builder = string_builder + " ariaHiddenRootExtra"; } - if (cached_values_only ? cached_is_hidden_via_style : IsHiddenViaStyle()) + if (cached_values_only ? cached_is_hidden_via_style_ : IsHiddenViaStyle()) { string_builder = string_builder + " isHiddenViaCSS"; + } if (cached_values_only ? cached_is_inert_ : IsInert()) string_builder = string_builder + " isInert"; if (IsMissingParent())
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h index b14b18ba..511f940d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -486,12 +486,8 @@ // elements are, etc. bool AccessibilityIsIncludedInTree() const; typedef HeapVector<IgnoredReason> IgnoredReasons; - virtual bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const { - return true; - } - bool AccessibilityIsIgnoredByDefault(IgnoredReasons* = nullptr) const; - virtual AXObjectInclusion DefaultObjectInclusion( - IgnoredReasons* = nullptr) const; + virtual bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const; + bool ShouldIgnoreForHiddenOrInert(IgnoredReasons* = nullptr) const; bool IsInert() const; bool IsAriaHidden() const; bool CachedIsAriaHidden() { return cached_is_aria_hidden_; } @@ -1468,7 +1464,7 @@ mutable bool cached_is_ignored_but_included_in_tree_ : 1; mutable bool cached_is_inert_ : 1; mutable bool cached_is_aria_hidden_ : 1; - mutable bool cached_is_hidden_via_style : 1; + mutable bool cached_is_hidden_via_style_ : 1; mutable bool cached_is_descendant_of_disabled_node_ : 1; mutable bool cached_can_set_focus_attribute_ : 1;
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 e1c0107..c29af7d5 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
@@ -887,10 +887,12 @@ if (result->IsMissingParent()) { AXObject* computed_parent = AXObject::ComputeNonARIAParent(*this, layout_object->GetNode()); - NOTREACHED() << "Had AXObject but was missing parent: " << layout_object - << " " << result->ToString(true, true) << "\nComputed parent: " - << (computed_parent ? computed_parent->ToString(true, true) - : "null"); + // TODO(https://crbug.com/1413932) resolve and restore to NOTREACHED(). + DCHECK(false) << "Had AXObject but was missing parent: " << layout_object + << " " << result->ToString(true, true) + << "\nComputed parent: " + << (computed_parent ? computed_parent->ToString(true, true) + : "null"); } #endif
diff --git a/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.cc b/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.cc index f85dd5c..4107770e 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.cc
@@ -44,11 +44,6 @@ return ax::mojom::blink::Role::kProgressIndicator; } -bool AXProgressIndicator::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - return AccessibilityIsIgnoredByDefault(ignored_reasons); -} - bool AXProgressIndicator::ValueForRange(float* out_value) const { float value_now; if (HasAOMPropertyOrARIAAttribute(AOMFloatProperty::kValueNow, value_now)) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.h b/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.h index a651512..ef56f0e2d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.h +++ b/third_party/blink/renderer/modules/accessibility/ax_progress_indicator.h
@@ -45,7 +45,6 @@ bool MinValueForRange(float* out_value) const override; HTMLProgressElement* GetProgressElement() const; - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc index 84c43e6..d80edcd 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
@@ -35,11 +35,6 @@ return GetAccessibleNode() ? GetAccessibleNode()->GetDocument() : nullptr; } -bool AXVirtualObject::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignoredReasons) const { - return AccessibilityIsIgnoredByDefault(ignoredReasons); -} - void AXVirtualObject::AddChildren() { #if defined(AX_FAIL_FAST_BUILD) DCHECK(!IsDetached());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h index e50c159..db2236c3 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.h
@@ -41,8 +41,6 @@ ax::mojom::blink::Role AriaRoleAttribute() const override; private: - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; - Member<AccessibleNode> accessible_node_; ax::mojom::blink::Role aria_role_;
diff --git a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc index 3aad1328..300ad40 100644 --- a/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc +++ b/third_party/blink/renderer/modules/accessibility/blink_ax_tree_source.cc
@@ -306,9 +306,10 @@ } if (!child->AccessibilityIsIncludedInTree()) { - NOTREACHED() << "Should not receive unincluded child." - << "\nChild: " << child->ToString(true).Utf8() - << "\nParent: " << parent->ToString(true).Utf8(); + // TODO(https://crbug.com/1407396) resolve and restore to NOTREACHED(). + DCHECK(false) << "Should not receive unincluded child." + << "\nChild: " << child->ToString(true).Utf8() + << "\nParent: " << parent->ToString(true).Utf8(); continue; }
diff --git a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl index e0348e2..3ab8969 100644 --- a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl +++ b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
@@ -25,21 +25,27 @@ unsigned long long sellerTimeout; unsigned short sellerExperimentGroupId; + // Really (record<USVString, any> or Promise<record<USVString, any>>) any perBuyerSignals; + // Really (record<USVString, unsigned long long> or // Promise<record<USVString, unsigned long long>>) any perBuyerTimeouts; + any perBuyerCumulativeTimeouts; + record<USVString, unsigned short> perBuyerGroupLimits; record<USVString, unsigned short> perBuyerExperimentGroupIds; record<USVString, record<USVString, double>> perBuyerPrioritySignals; + // `auctionReportBuyerKeys` and `auctionReportBuyers` are described in: // https://github.com/WICG/turtledove/blob/main/FLEDGE_extended_PA_reporting.md - + // // TODO(crbug.com/1402467): Use `bigint` instead of `any` when supported by // WebIDL parser. sequence<any> auctionReportBuyerKeys; record<USVString, AuctionReportBuyersConfig> auctionReportBuyers; + sequence<AuctionAdConfig> componentAuctions; AbortSignal? signal; boolean resolveToConfig;
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc index de5ef5c43..bd3cc9f 100644 --- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc +++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -61,6 +61,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/text/string_operators.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "v8/include/v8-primitive.h" @@ -109,10 +110,13 @@ const String seller_name_; }; + // This is used for perBuyerTimeouts and perBuyerCumulativeTimeouts, with + // `field` indicating which of the two fields an object is being used for. class BuyerTimeoutsResolved : public ScriptFunction::Callable { public: BuyerTimeoutsResolved(AuctionHandle* auction_handle, mojom::blink::AuctionAdConfigAuctionIdPtr auction_id, + mojom::blink::AuctionAdConfigBuyerTimeoutField field, const String& seller_name); ScriptValue Call(ScriptState* script_state, ScriptValue value) override; @@ -121,6 +125,7 @@ private: Member<AuctionHandle> auction_handle_; const mojom::blink::AuctionAdConfigAuctionIdPtr auction_id_; + const mojom::blink::AuctionAdConfigBuyerTimeoutField field_; const String seller_name_; }; @@ -185,9 +190,10 @@ void ResolvedBuyerTimeoutsPromise( mojom::blink::AuctionAdConfigAuctionIdPtr auction, + mojom::blink::AuctionAdConfigBuyerTimeoutField field, mojom::blink::AuctionAdConfigBuyerTimeoutsPtr buyer_timeouts) { abortable_ad_auction_->ResolvedBuyerTimeoutsPromise( - std::move(auction), std::move(buyer_timeouts)); + std::move(auction), field, std::move(buyer_timeouts)); } void ResolvedDirectFromSellerSignalsPromise( @@ -1260,11 +1266,17 @@ } // Returns nullptr + sets exception on failure, or returns a concrete value. +// +// This is shared logic for `perBuyerTimeouts` and `perBuyerCumulativeTimeouts`, +// with `field` indicating which name to use in error messages. The logic is +// identical in both cases. mojom::blink::AuctionAdConfigBuyerTimeoutsPtr -ConvertNonPromisePerBuyerTimeoutsFromV8ToMojo(const ScriptState& script_state, - ExceptionState& exception_state, - const String& seller_name, - v8::Local<v8::Value> value) { +ConvertNonPromisePerBuyerTimeoutsFromV8ToMojo( + const ScriptState& script_state, + ExceptionState& exception_state, + const String& seller_name, + v8::Local<v8::Value> value, + mojom::blink::AuctionAdConfigBuyerTimeoutField field) { Vector<std::pair<String, uint64_t>> decoded = NativeValueTraits<IDLRecord<IDLUSVString, IDLUnsignedLongLong>>:: NativeValue(script_state.GetIsolate(), value, exception_state); @@ -1284,8 +1296,18 @@ scoped_refptr<const SecurityOrigin> buyer = ParseOrigin(per_buyer_timeout.first); if (!buyer) { + String field_name; + switch (field) { + case mojom::blink::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts: + field_name = "perBuyerTimeouts buyer"; + break; + case mojom::blink::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts: + field_name = "perBuyerCumulativeTimeouts buyer"; + break; + } exception_state.ThrowTypeError(ErrorInvalidAuctionConfigSeller( - seller_name, "perBuyerTimeouts buyer", per_buyer_timeout.first, + seller_name, field_name, per_buyer_timeout.first, "must be \"*\" (wildcard) or a valid https origin.")); return nullptr; } @@ -1318,7 +1340,10 @@ &script_state, MakeGarbageCollected< NavigatorAuction::AuctionHandle::BuyerTimeoutsResolved>( - auction_handle, auction_id->Clone(), input.seller())), + auction_handle, auction_id->Clone(), + mojom::blink::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerTimeouts, + input.seller())), MakeGarbageCollected<ScriptFunction>( &script_state, MakeGarbageCollected<NavigatorAuction::AuctionHandle::Rejected>( @@ -1330,7 +1355,8 @@ mojom::blink::AuctionAdConfigBuyerTimeoutsPtr buyer_timeouts = ConvertNonPromisePerBuyerTimeoutsFromV8ToMojo( - script_state, exception_state, input.seller(), value); + script_state, exception_state, input.seller(), value, + mojom::blink::AuctionAdConfigBuyerTimeoutField::kPerBuyerTimeouts); if (buyer_timeouts) { output.auction_ad_config_non_shared_params->buyer_timeouts = mojom::blink::AuctionAdConfigMaybePromiseBuyerTimeouts::NewValue( @@ -1340,6 +1366,55 @@ return false; } +bool CopyPerBuyerCumulativeTimeoutsFromIdlToMojo( + NavigatorAuction::AuctionHandle* auction_handle, + const mojom::blink::AuctionAdConfigAuctionId* auction_id, + ScriptState& script_state, + ExceptionState& exception_state, + const AuctionAdConfig& input, + mojom::blink::AuctionAdConfig& output) { + if (!input.hasPerBuyerCumulativeTimeouts()) { + output.auction_ad_config_non_shared_params->buyer_cumulative_timeouts = + mojom::blink::AuctionAdConfigMaybePromiseBuyerTimeouts::NewValue( + mojom::blink::AuctionAdConfigBuyerTimeouts::New()); + return true; + } + + v8::Local<v8::Value> value = input.perBuyerCumulativeTimeouts().V8Value(); + if (auction_handle && value->IsPromise()) { + ScriptPromise promise(&script_state, value); + promise.Then( + MakeGarbageCollected<ScriptFunction>( + &script_state, + MakeGarbageCollected< + NavigatorAuction::AuctionHandle::BuyerTimeoutsResolved>( + auction_handle, auction_id->Clone(), + mojom::blink::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts, + input.seller())), + MakeGarbageCollected<ScriptFunction>( + &script_state, + MakeGarbageCollected<NavigatorAuction::AuctionHandle::Rejected>( + auction_handle))); + output.auction_ad_config_non_shared_params->buyer_cumulative_timeouts = + mojom::blink::AuctionAdConfigMaybePromiseBuyerTimeouts::NewPromise(0); + return true; + } + + mojom::blink::AuctionAdConfigBuyerTimeoutsPtr buyer_cumulative_timeouts = + ConvertNonPromisePerBuyerTimeoutsFromV8ToMojo( + script_state, exception_state, input.seller(), value, + mojom::blink::AuctionAdConfigBuyerTimeoutField:: + kPerBuyerCumulativeTimeouts); + if (buyer_cumulative_timeouts) { + output.auction_ad_config_non_shared_params->buyer_cumulative_timeouts = + mojom::blink::AuctionAdConfigMaybePromiseBuyerTimeouts::NewValue( + std::move(buyer_cumulative_timeouts)); + return true; + } + return false; +} + bool CopyPerBuyerExperimentIdsFromIdlToMojo( const ScriptState& script_state, ExceptionState& exception_state, @@ -1575,6 +1650,9 @@ !CopyPerBuyerTimeoutsFromIdlToMojo(auction_handle, auction_id.get(), script_state, exception_state, config, *mojo_config) || + !CopyPerBuyerCumulativeTimeoutsFromIdlToMojo( + auction_handle, auction_id.get(), script_state, exception_state, + config, *mojo_config) || !CopyPerBuyerExperimentIdsFromIdlToMojo(script_state, exception_state, config, *mojo_config) || !CopyPerBuyerGroupLimitsFromIdlToMojo(script_state, exception_state, @@ -1800,9 +1878,11 @@ NavigatorAuction::AuctionHandle::BuyerTimeoutsResolved::BuyerTimeoutsResolved( AuctionHandle* auction_handle, mojom::blink::AuctionAdConfigAuctionIdPtr auction_id, + mojom::blink::AuctionAdConfigBuyerTimeoutField field, const String& seller_name) : auction_handle_(auction_handle), auction_id_(std::move(auction_id)), + field_(field), seller_name_(seller_name) {} ScriptValue NavigatorAuction::AuctionHandle::BuyerTimeoutsResolved::Call( @@ -1816,7 +1896,7 @@ v8::Local<v8::Value> v8_value = value.V8Value(); if (!v8_value->IsUndefined() && !v8_value->IsNull()) { buyer_timeouts = ConvertNonPromisePerBuyerTimeoutsFromV8ToMojo( - *script_state, exception_state, seller_name_, v8_value); + *script_state, exception_state, seller_name_, v8_value, field_); } } @@ -1825,7 +1905,7 @@ } if (!exception_state.HadException()) { - auction_handle_->ResolvedBuyerTimeoutsPromise(auction_id_->Clone(), + auction_handle_->ResolvedBuyerTimeoutsPromise(auction_id_->Clone(), field_, std::move(buyer_timeouts)); } else { auction_handle_->Abort();
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc index d040dbc4..da118564 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -341,7 +341,6 @@ break; } - case WGPUCreatePipelineAsyncStatus_Error: case WGPUCreatePipelineAsyncStatus_InternalError: case WGPUCreatePipelineAsyncStatus_DeviceLost: case WGPUCreatePipelineAsyncStatus_DeviceDestroyed:
diff --git a/third_party/blink/renderer/platform/graphics/DEPS b/third_party/blink/renderer/platform/graphics/DEPS index a987189..d24aa11 100644 --- a/third_party/blink/renderer/platform/graphics/DEPS +++ b/third_party/blink/renderer/platform/graphics/DEPS
@@ -10,6 +10,7 @@ "+base/strings/string_number_conversions.h", "+base/strings/string_split.h", "+base/strings/string_util.h", + "+base/task/task_traits.h", "+base/threading/sequenced_task_runner_handle.h", "+base/threading/thread_restrictions.h", "+base/barrier_closure.h",
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc index 7d6f4379..020c25e 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -32,6 +32,8 @@ #include "base/location.h" #include "base/rand_util.h" #include "base/task/single_thread_task_runner.h" +#include "base/task/task_traits.h" +#include "base/time/time.h" #include "base/timer/elapsed_timer.h" #include "cc/layers/texture_layer.h" #include "components/viz/common/resources/transferable_resource.h" @@ -52,31 +54,171 @@ #include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/scheduler/public/main_thread.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/skia/include/core/SkAlphaType.h" #include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkEncodedImageFormat.h" +#include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSurface.h" namespace blink { -namespace { - -BASE_FEATURE( - kCanvas2DHibernation, - "Canvas2DHibernation", -#if BUILDFLAG(IS_MAC) - // Canvas hibernation is not always enabled on MacOS X due to a bug that - // causes content loss. TODO: Find a better fix for crbug.com/588434 - base::FeatureState::FEATURE_DISABLED_BY_DEFAULT -#else - base::FeatureState::FEATURE_ENABLED_BY_DEFAULT -#endif -); -} - // static bool Canvas2DLayerBridge::IsHibernationEnabled() { - return base::FeatureList::IsEnabled(kCanvas2DHibernation); + return base::FeatureList::IsEnabled(features::kCanvas2DHibernation); +} + +void HibernationHandler::TakeHibernationImage(sk_sp<SkImage>&& image) { + DCheckInvariant(); + epoch_++; + image_ = image; + + if (!base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)) { + return; + } + + width_ = image_->width(); + height_ = image_->height(); + bytes_per_pixel_ = image_->imageInfo().bytesPerPixel(); + + // If we had an encoded version, discard it. + encoded_.reset(); + + // Don't bother compressing very small canvases. + size_t memory_size = image_->height() * static_cast<size_t>(image_->width()) * + static_cast<size_t>(image_->imageInfo().bytesPerPixel()); + if (memory_size < 16 * 1024) { + return; + } + + // Don't post the compression task to the thread pool with a delay right away. + // The task increases the reference count on the SkImage. In the case of rapid + // foreground / background transitions, each transition allocates a new + // SkImage. If we post a compression task right away with a sk_sp<SkImage> as + // a parameter, this takes a reference on the underlying SkImage, keeping it + // alive until the task runs. This means that posting the compression task + // right away would increase memory usage by a lot in these cases. + // + // Rather, post a main thread task later that will check whether we are still + // in hibernation mode, and in the same hibernation "epoch" as last time. If + // this is the case, then compress. + // + // This simplifies tracking of background / foreground cycles, at the cost of + // running one extra trivial task for each cycle. + // + // Note: not using a delayed idle tasks, because idle tasks do not run when + // the renderer is idle. In other words, a delayed idle task would not execute + // as long as the renderer is in background, which completely defeats the + // purpose. + GetMainThreadTaskRunner()->PostDelayedTask( + FROM_HERE, + base::BindOnce(&HibernationHandler::OnAfterHibernation, + weak_ptr_factory_.GetWeakPtr(), epoch_), + kBeforeCompressionDelay); +} + +void HibernationHandler::OnAfterHibernation(uint64_t epoch) { + DCheckInvariant(); + DCHECK( + base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)); + // Either we no longer have the image (because we are not hibernating), or we + // went through another visible / not visible cycle (in which case it is too + // early to compress). + if (epoch_ != epoch || !image_) { + return; + } + auto task_runner = GetMainThreadTaskRunner(); + auto params = std::make_unique<BackgroundTaskParams>( + image_, epoch_, weak_ptr_factory_.GetWeakPtr(), task_runner); + + if (background_thread_task_runner_for_testing_) { + background_thread_task_runner_for_testing_->PostTask( + FROM_HERE, + base::BindOnce(&HibernationHandler::Encode, std::move(params))); + } else { + worker_pool::PostTask( + FROM_HERE, {base::TaskPriority::BEST_EFFORT}, + CrossThreadBindOnce(&HibernationHandler::Encode, std::move(params))); + } +} + +void HibernationHandler::OnEncoded( + std::unique_ptr<HibernationHandler::BackgroundTaskParams> params, + sk_sp<SkData> encoded) { + DCheckInvariant(); + DCHECK( + base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)); + // Discard the compressed image, it is no longer current. + if (params->epoch != epoch_ || !IsHibernating()) { + return; + } + + DCHECK_EQ(image_.get(), params->image.get()); + encoded_ = encoded; + image_ = nullptr; +} + +scoped_refptr<base::SingleThreadTaskRunner> +HibernationHandler::GetMainThreadTaskRunner() const { + return main_thread_task_runner_for_testing_ + ? main_thread_task_runner_for_testing_ + : Thread::MainThread()->GetTaskRunner( + MainThreadTaskRunnerRestricted()); +} + +void HibernationHandler::Encode( + std::unique_ptr<HibernationHandler::BackgroundTaskParams> params) { + TRACE_EVENT0("blink", __PRETTY_FUNCTION__); + DCHECK( + base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)); + sk_sp<SkData> encoded = + params->image->encodeToData(SkEncodedImageFormat::kPNG, 100); + + auto* reply_task_runner = params->reply_task_runner.get(); + reply_task_runner->PostTask( + FROM_HERE, + base::BindOnce(&HibernationHandler::OnEncoded, params->weak_instance, + std::move(params), encoded)); +} + +sk_sp<SkImage> HibernationHandler::GetImage() { + TRACE_EVENT0("blink", __PRETTY_FUNCTION__); + DCheckInvariant(); + if (image_) { + return image_; + } + + DCHECK(encoded_); + DCHECK( + base::FeatureList::IsEnabled(features::kCanvasCompressHibernatedImage)); + + // Note: not discarding the encoded image. + return SkImage::MakeFromEncoded(encoded_)->makeRasterImage(); +} + +void HibernationHandler::Clear() { + DCheckInvariant(); + encoded_ = nullptr; + image_ = nullptr; +} + +size_t HibernationHandler::memory_size() const { + DCheckInvariant(); + DCHECK(IsHibernating()); + if (is_encoded()) { + return encoded_->size(); + } else { + return original_memory_size(); + } +} + +size_t HibernationHandler::original_memory_size() const { + return static_cast<size_t>(width_) * height_ * bytes_per_pixel_; } Canvas2DLayerBridge::Canvas2DLayerBridge(const gfx::Size& size, @@ -166,11 +308,13 @@ static void LoseContextInBackgroundWrapper( base::WeakPtr<Canvas2DLayerBridge> bridge, base::TimeTicks /*idleDeadline*/) { - if (bridge) + if (bridge) { bridge->LoseContext(); + } } void Canvas2DLayerBridge::Hibernate() { + TRACE_EVENT0("blink", __PRETTY_FUNCTION__); DCHECK(!IsHibernating()); DCHECK(hibernation_scheduled_); @@ -215,14 +359,18 @@ logger_->ReportHibernationEvent(kHibernationAbortedDueSnapshotFailure); return; } - hibernation_image_ = snapshot->PaintImageForCurrentFrame().GetSwSkImage(); + hibernation_handler_.TakeHibernationImage( + snapshot->PaintImageForCurrentFrame().GetSwSkImage()); + ResetResourceProvider(); - if (layer_) + if (layer_) { layer_->ClearTexture(); + } // shouldBeDirectComposited() may have changed. - if (resource_host_) + if (resource_host_) { resource_host_->SetNeedsCompositingUpdate(); + } logger_->DidStartHibernating(); } @@ -340,10 +488,13 @@ } PaintImageBuilder builder = PaintImageBuilder::WithDefault(); - builder.set_image(hibernation_image_, PaintImage::GetNextContentId()); + builder.set_image(hibernation_handler_.GetImage(), + PaintImage::GetNextContentId()); builder.set_id(PaintImage::GetNextId()); resource_provider->RestoreBackBuffer(builder.TakePaintImage()); - hibernation_image_.reset(); + // The hibernation image is no longer valid, clear it. + hibernation_handler_.Clear(); + DCHECK(!IsHibernating()); if (resource_host_) { // shouldBeDirectComposited() may have changed. @@ -607,19 +758,23 @@ } bool Canvas2DLayerBridge::CheckResourceProviderValid() { - if (IsHibernating()) + if (IsHibernating()) { return true; - if (!layer_ || raster_mode_ == RasterMode::kCPU) + } + if (!layer_ || raster_mode_ == RasterMode::kCPU) { return true; - if (context_lost_) + } + if (context_lost_) { return false; + } if (ResourceProvider() && IsAccelerated() && ResourceProvider()->IsGpuContextLost()) { context_lost_ = true; ClearPendingRasterTimers(); ResetResourceProvider(); - if (resource_host_) + if (resource_host_) { resource_host_->NotifyGpuContextLost(); + } return false; } return !!GetOrCreateResourceProvider(); @@ -772,8 +927,10 @@ scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot() { if (snapshot_state_ == kInitialSnapshotState) snapshot_state_ = kDidAcquireSnapshot; - if (IsHibernating()) - return UnacceleratedStaticBitmapImage::Create(hibernation_image_); + if (IsHibernating()) { + return UnacceleratedStaticBitmapImage::Create( + hibernation_handler_.GetImage()); + } if (!IsValid()) return nullptr; // GetOrCreateResourceProvider needs to be called before FlushRecording, to
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h index ed344ceb..f134d141 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -33,6 +33,7 @@ #include "base/memory/weak_ptr.h" #include "base/numerics/checked_math.h" #include "base/rand_util.h" +#include "base/task/single_thread_task_runner.h" #include "base/time/time.h" #include "base/types/optional_util.h" #include "build/build_config.h" @@ -46,6 +47,7 @@ #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/deque.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" +#include "third_party/blink/renderer/platform/wtf/wtf.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/color_space.h" @@ -65,6 +67,94 @@ class SharedContextRateLimiter; class StaticBitmapImage; +// All the fields are main-thread only. See DCheckInvariant() for invariants. +class PLATFORM_EXPORT HibernationHandler { + public: + // Semi-arbitrary threshold. Some past experiments (e.g. tile discard) have + // shown that taking action after 5 minutes has a positive impact on memory, + // and a minimal impact on tab switching latency (and on needless + // compression). + static constexpr base::TimeDelta kBeforeCompressionDelay = base::Minutes(5); + + void TakeHibernationImage(sk_sp<SkImage>&& image); + // Returns the uncompressed image for this hibernation image. Does not + // invalidate the hibernated image. Must call `Clear()` if invalidation is + // required. + sk_sp<SkImage> GetImage(); + // Invalidate the hibernated image. + void Clear(); + + bool IsHibernating() const { + DCheckInvariant(); + return image_ != nullptr || encoded_ != nullptr; + } + bool is_encoded() const { + DCheckInvariant(); + return encoded_ != nullptr; + } + size_t memory_size() const; + size_t original_memory_size() const; + int width() const { return width_; } + int height() const { return height_; } + + void SetTaskRunnersForTesting( + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> + background_thread_task_runner) { + main_thread_task_runner_for_testing_ = main_thread_task_runner; + background_thread_task_runner_for_testing_ = background_thread_task_runner; + } + + private: + struct BackgroundTaskParams final { + BackgroundTaskParams( + sk_sp<SkImage> image, + uint64_t epoch, + base::WeakPtr<HibernationHandler> weak_instance, + scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner) + : image(image), + epoch(epoch), + weak_instance(weak_instance), + reply_task_runner(reply_task_runner) {} + + BackgroundTaskParams(const BackgroundTaskParams&) = delete; + BackgroundTaskParams& operator=(const BackgroundTaskParams&) = delete; + ~BackgroundTaskParams() { DCHECK(IsMainThread()); } + + const sk_sp<SkImage> image; + const uint64_t epoch; + const base::WeakPtr<HibernationHandler> weak_instance; + const scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner; + }; + + void DCheckInvariant() const { + DCHECK(IsMainThread()); + DCHECK(!((image_ != nullptr) && (encoded_ != nullptr))); + } + void OnAfterHibernation(uint64_t initial_epoch); + static void Encode(std::unique_ptr<BackgroundTaskParams> params); + void OnEncoded( + std::unique_ptr<HibernationHandler::BackgroundTaskParams> params, + sk_sp<SkData> encoded); + scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner() const; + + // Incremented each time the canvas is hibernated. + uint64_t epoch_ = 0; + // Uncompressed hibernation image. + sk_sp<SkImage> image_ = nullptr; + // Compressed hibernation image. + sk_sp<SkData> encoded_ = nullptr; + scoped_refptr<base::SingleThreadTaskRunner> + main_thread_task_runner_for_testing_; + scoped_refptr<base::SingleThreadTaskRunner> + background_thread_task_runner_for_testing_; + int width_; + int height_; + int bytes_per_pixel_; + + base::WeakPtrFactory<HibernationHandler> weak_ptr_factory_{this}; +}; + class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { public: Canvas2DLayerBridge(const gfx::Size&, RasterMode, OpacityMode opacity_mode); @@ -111,7 +201,7 @@ // This is used for a memory usage experiment: frees canvas resource when // canvas is in an invisible tab. void LoseContext(); - bool IsHibernating() const { return hibernation_image_ != nullptr; } + bool IsHibernating() const { return hibernation_handler_.IsHibernating(); } bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; } @@ -162,6 +252,10 @@ static bool IsHibernationEnabled(); + HibernationHandler& GetHibernationHandlerForTesting() { + return hibernation_handler_; + } + private: friend class Canvas2DLayerBridgeTest; friend class CanvasRenderingContext2DTest; @@ -176,7 +270,8 @@ // Check if the Raster Mode is GPU and if the GPU context is not lost bool ShouldAccelerate() const; - sk_sp<SkImage> hibernation_image_; + HibernationHandler hibernation_handler_; + scoped_refptr<cc::TextureLayer> layer_; std::unique_ptr<SharedContextRateLimiter> rate_limiter_; std::unique_ptr<Logger> logger_;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc index 0442563f..58fc337 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -25,12 +25,18 @@ #include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h" +#include <list> +#include <memory> #include <utility> +#include "base/functional/callback_forward.h" #include "base/location.h" #include "base/memory/scoped_refptr.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" #include "build/build_config.h" #include "cc/layers/texture_layer.h" #include "cc/paint/paint_flags.h" @@ -48,6 +54,7 @@ #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" @@ -67,8 +74,6 @@ #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" -#include <memory> - namespace blink { namespace { @@ -82,6 +87,53 @@ using testing::SetArgPointee; using testing::Test; +class TestSingleThreadTaskRunner : public base::SingleThreadTaskRunner { + public: + bool PostDelayedTask(const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) override { + if (delay.is_zero()) { + immediate_.push_back(std::move(task)); + } else { + delayed_.push_back(std::move(task)); + } + + return true; + } + bool PostNonNestableDelayedTask(const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) override { + return false; + } + bool RunsTasksInCurrentSequence() const override { return false; } + + static size_t RunAll(std::list<base::OnceClosure>& tasks) { + size_t count = 0; + while (!tasks.empty()) { + std::move(tasks.front()).Run(); + tasks.pop_front(); + count++; + } + return count; + } + + static bool RunOne(std::list<base::OnceClosure>& tasks) { + if (tasks.empty()) { + return false; + } + std::move(tasks.front()).Run(); + tasks.pop_front(); + return true; + } + + std::list<base::OnceClosure>& delayed() { return delayed_; } + std::list<base::OnceClosure>& immediate() { return immediate_; } + + private: + std::list<base::OnceClosure> delayed_; + std::list<base::OnceClosure> immediate_; +}; + class ImageTrackingDecodeCache : public cc::StubDecodeCache { public: ImageTrackingDecodeCache() = default; @@ -1017,4 +1069,347 @@ EXPECT_FALSE(bridge->HasRateLimiterForTesting()); } +namespace { +void SetIsInHiddenPage( + Canvas2DLayerBridge* bridge, + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>& platform, + bool hidden) { + bridge->SetIsInHiddenPage(hidden); + // Make sure that idle tasks run when hidden. + if (hidden) { + scheduler::RunIdleTasksForTesting( + scheduler::WebThreadScheduler::MainThreadScheduler(), + base::DoNothing()); + platform->RunUntilIdle(); + EXPECT_TRUE(bridge->IsHibernating()); + } +} +} // namespace + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerSimpleTest) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 200), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + + EXPECT_TRUE(bridge->IsHibernating()); + // Triggers a delayed task for encoding. + EXPECT_FALSE(task_runner->delayed().empty()); + EXPECT_TRUE(task_runner->immediate().empty()); + + TestSingleThreadTaskRunner::RunAll(task_runner->delayed()); + // Posted the background compression task. + EXPECT_FALSE(task_runner->immediate().empty()); + + size_t uncompressed_size = 300u * 200 * 4; + EXPECT_EQ(handler.width(), 300); + EXPECT_EQ(handler.height(), 200); + EXPECT_EQ(uncompressed_size, handler.memory_size()); + + // Runs the encoding task, but also the callback one. + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + EXPECT_TRUE(handler.is_encoded()); + EXPECT_LT(handler.memory_size(), uncompressed_size); + EXPECT_EQ(handler.original_memory_size(), uncompressed_size); + + SetIsInHiddenPage(bridge.get(), platform, false); + EXPECT_FALSE(handler.is_encoded()); + + EXPECT_TRUE(bridge->IsAccelerated()); + EXPECT_FALSE(bridge->IsHibernating()); + EXPECT_TRUE(bridge->IsValid()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerDisabledTest) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation}, + {features::kCanvasCompressHibernatedImage}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 200), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + + EXPECT_TRUE(bridge->IsHibernating()); + + // No posted tasks. + EXPECT_TRUE(task_runner->delayed().empty()); + EXPECT_TRUE(task_runner->immediate().empty()); + + // But still hibernating. + EXPECT_TRUE(bridge->IsHibernating()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerForegroundTooEarly) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + SetIsInHiddenPage(bridge.get(), platform, true); + + // Triggers a delayed task for encoding. + EXPECT_FALSE(task_runner->delayed().empty()); + + EXPECT_TRUE(bridge->IsHibernating()); + SetIsInHiddenPage(bridge.get(), platform, false); + + // Nothing happens, because the page came to foreground in-between. + TestSingleThreadTaskRunner::RunAll(task_runner->delayed()); + EXPECT_TRUE(task_runner->immediate().empty()); + EXPECT_FALSE(handler.is_encoded()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerBackgroundForeground) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + // Background -> Foreground -> Background + SetIsInHiddenPage(bridge.get(), platform, true); + SetIsInHiddenPage(bridge.get(), platform, false); + SetIsInHiddenPage(bridge.get(), platform, true); + + // 2 delayed task that will potentially trigger encoding. + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + // But a single encoding task (plus the main thread callback). + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + EXPECT_TRUE(handler.is_encoded()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerForegroundAfterEncoding) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the encoding task to be posted. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + EXPECT_TRUE(TestSingleThreadTaskRunner::RunOne(task_runner->immediate())); + // Come back to foreground after (or during) compression, but before the + // callback. + SetIsInHiddenPage(bridge.get(), platform, false); + + // The callback is still pending. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + // But the encoded version is dropped. + EXPECT_FALSE(handler.is_encoded()); + EXPECT_FALSE(bridge->IsHibernating()); +} + +TEST_F(Canvas2DLayerBridgeTest, + HibernationHandlerForegroundFlipForAfterEncoding) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the encoding task to be posted. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + EXPECT_TRUE(TestSingleThreadTaskRunner::RunOne(task_runner->immediate())); + // Come back to foreground after (or during) compression, but before the + // callback. + SetIsInHiddenPage(bridge.get(), platform, false); + // And back to background. + SetIsInHiddenPage(bridge.get(), platform, true); + EXPECT_TRUE(bridge->IsHibernating()); + + // The callback is still pending. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + // But the encoded version is dropped (epoch mismatch). + EXPECT_FALSE(handler.is_encoded()); + // Yet we are hibernating (since the bridge is in background). + EXPECT_TRUE(bridge->IsHibernating()); + + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + EXPECT_TRUE(handler.is_encoded()); + // Yet we are hibernating (since the bridge is in background). + EXPECT_TRUE(bridge->IsHibernating()); +} + +TEST_F(Canvas2DLayerBridgeTest, + HibernationHandlerForegroundFlipForBeforeEncoding) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the encoding task to be posted. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + // Come back to foreground before compression. + SetIsInHiddenPage(bridge.get(), platform, false); + // And back to background. + SetIsInHiddenPage(bridge.get(), platform, true); + EXPECT_TRUE(bridge->IsHibernating()); + // Compression still happens, since it's a static task, doesn't look at the + // epoch before compressing. + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + + // But the encoded version is dropped (epoch mismatch). + EXPECT_FALSE(handler.is_encoded()); + // Yet we are hibernating (since the bridge is in background). + EXPECT_TRUE(bridge->IsHibernating()); +} + +TEST_F(Canvas2DLayerBridgeTest, + HibernationHandlerCanvasSnapshottedInBackground) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the canvas to be encoded. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + EXPECT_TRUE(handler.is_encoded()); + + EXPECT_TRUE(bridge->IsHibernating()); + auto image = bridge->NewImageSnapshot(); + EXPECT_TRUE(bridge->IsHibernating()); + // Do not discard the encoded representation. + EXPECT_TRUE(handler.is_encoded()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerCanvasWriteInBackground) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the canvas to be encoded. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + EXPECT_EQ(2u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + EXPECT_TRUE(handler.is_encoded()); + + bridge->WritePixels(SkImageInfo::MakeN32Premul(10, 10), nullptr, 10, 0, 0); + + EXPECT_FALSE(bridge->IsHibernating()); + EXPECT_FALSE(handler.is_encoded()); +} + +TEST_F(Canvas2DLayerBridgeTest, HibernationHandlerCanvasWriteWhileCompressing) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + {features::kCanvas2DHibernation, + features::kCanvasCompressHibernatedImage}, + {}); + + auto task_runner = base::MakeRefCounted<TestSingleThreadTaskRunner>(); + ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform; + std::unique_ptr<Canvas2DLayerBridge> bridge = + MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque); + DrawSomething(bridge.get()); + + auto& handler = bridge->GetHibernationHandlerForTesting(); + handler.SetTaskRunnersForTesting(task_runner, task_runner); + + SetIsInHiddenPage(bridge.get(), platform, true); + // Wait for the canvas to be encoded. + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->delayed())); + // Run the compression task, not the callback. + EXPECT_TRUE(TestSingleThreadTaskRunner::RunOne(task_runner->immediate())); + + bridge->WritePixels(SkImageInfo::MakeN32Premul(10, 10), nullptr, 10, 0, 0); + EXPECT_EQ(1u, TestSingleThreadTaskRunner::RunAll(task_runner->immediate())); + + // No hibernation, read happened in-between. + EXPECT_FALSE(bridge->IsHibernating()); + EXPECT_FALSE(handler.is_encoded()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index 928a0e1..b974c74 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -451,7 +451,8 @@ CreateTransform(*transform1, MakeRotationMatrix(0, 45, 0)); TransformPaintPropertyNode::State transform3_state{ {MakeRotationMatrix(0, 45, 0)}}; - transform3_state.flattens_inherited_transform = transform_is_flattened; + transform3_state.flags.flattens_inherited_transform = + transform_is_flattened; auto transform3 = TransformPaintPropertyNode::Create( *transform2, std::move(transform3_state)); @@ -504,7 +505,8 @@ auto transform2 = TransformPaintPropertyNodeAlias::Create(*real_transform2); TransformPaintPropertyNode::State transform3_state{ {MakeRotationMatrix(0, 45, 0)}}; - transform3_state.flattens_inherited_transform = transform_is_flattened; + transform3_state.flags.flattens_inherited_transform = + transform_is_flattened; auto real_transform3 = TransformPaintPropertyNode::Create( *transform2, std::move(transform3_state)); auto transform3 = TransformPaintPropertyNodeAlias::Create(*real_transform3); @@ -3757,7 +3759,7 @@ TEST_P(PaintArtifactCompositorTest, CreatesViewportNodes) { auto matrix = MakeScaleMatrix(2); TransformPaintPropertyNode::State transform_state{{matrix}}; - transform_state.in_subtree_of_page_scale = false; + transform_state.flags.in_subtree_of_page_scale = false; const CompositorElementId compositor_element_id = CompositorElementIdFromUniqueObjectId(1); transform_state.compositor_element_id = compositor_element_id; @@ -3782,12 +3784,12 @@ // the page scale transform node or ancestors, and is set on descendants. TEST_P(PaintArtifactCompositorTest, InSubtreeOfPageScale) { TransformPaintPropertyNode::State ancestor_transform_state; - ancestor_transform_state.in_subtree_of_page_scale = false; + ancestor_transform_state.flags.in_subtree_of_page_scale = false; auto ancestor_transform = TransformPaintPropertyNode::Create( TransformPaintPropertyNode::Root(), std::move(ancestor_transform_state)); TransformPaintPropertyNode::State page_scale_transform_state; - page_scale_transform_state.in_subtree_of_page_scale = false; + page_scale_transform_state.flags.in_subtree_of_page_scale = false; const CompositorElementId page_scale_compositor_element_id = CompositorElementIdFromUniqueObjectId(1); page_scale_transform_state.compositor_element_id = @@ -3800,7 +3802,7 @@ CompositorElementIdFromUniqueObjectId(2); descendant_transform_state.compositor_element_id = descendant_compositor_element_id; - descendant_transform_state.in_subtree_of_page_scale = true; + descendant_transform_state.flags.in_subtree_of_page_scale = true; descendant_transform_state.direct_compositing_reasons = CompositingReason::kWillChangeTransform; auto descendant_transform = TransformPaintPropertyNode::Create( @@ -3837,7 +3839,7 @@ TEST_P(PaintArtifactCompositorTest, ViewportPageScale) { // Create a page scale transform node with a page scale factor of 2.0. TransformPaintPropertyNode::State transform_state{{MakeScaleMatrix(2)}}; - transform_state.in_subtree_of_page_scale = false; + transform_state.flags.in_subtree_of_page_scale = false; transform_state.compositor_element_id = CompositorElementIdFromUniqueObjectId(1); auto scale_transform_node = TransformPaintPropertyNode::Create(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index f07c1ba..cb71308 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -985,7 +985,7 @@ clip_id = EnsureCompositorClipNode(*clip); } // For non-trivial clip, isolation_effect.stable_id will be assigned later - // when the effect is closed. For now the default value kInvalidStableId + // when the effect is closed. For now the default value INVALID_STABLE_ID // is used. See PropertyTreeManager::EmitClipMaskLayer(). if (SupportsShaderBasedRoundedCorner(*pending_clip.clip, pending_clip.type, next_effect)) { @@ -1116,7 +1116,7 @@ } if (has_multiple_groups) { - if (effect_node.stable_id != cc::EffectNode::kInvalidStableId) { + if (effect_node.stable_id != cc::EffectNode::INVALID_STABLE_ID) { // We are creating more than one cc effect nodes for one blink effect. // Give the extra cc effect node a unique stable id. effect_node.stable_id =
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc index 28b64a7..f7f30ff 100644 --- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc +++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
@@ -356,7 +356,7 @@ auto inverse_rotate_transform = MakeRotationMatrix(-45, 0, 0); TransformPaintPropertyNode::State inverse_state{{inverse_rotate_transform}}; - inverse_state.flattens_inherited_transform = true; + inverse_state.flags.flattens_inherited_transform = true; auto transform2 = TransformPaintPropertyNode::Create(*transform1, std::move(inverse_state)); local_state.SetTransform(*transform2);
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc index 6661a9e..ff7fe6e 100644 --- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc +++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -68,18 +68,20 @@ const State& other, const AnimationState& animation_state) const { // Whether or not a node is considered a frame root should be invariant. - DCHECK_EQ(is_frame_paint_offset_translation, - other.is_frame_paint_offset_translation); + DCHECK_EQ(flags.is_frame_paint_offset_translation, + other.flags.is_frame_paint_offset_translation); // Changes other than compositing reason and the transform are not simple. - if (flattens_inherited_transform != other.flattens_inherited_transform || - in_subtree_of_page_scale != other.in_subtree_of_page_scale || - animation_is_axis_aligned != other.animation_is_axis_aligned || - delegates_to_parent_for_backface != - other.delegates_to_parent_for_backface || - is_frame_paint_offset_translation != - other.is_frame_paint_offset_translation || - is_for_svg_child != other.is_for_svg_child || + if (flags.flattens_inherited_transform != + other.flags.flattens_inherited_transform || + flags.in_subtree_of_page_scale != other.flags.in_subtree_of_page_scale || + flags.animation_is_axis_aligned != + other.flags.animation_is_axis_aligned || + flags.delegates_to_parent_for_backface != + other.flags.delegates_to_parent_for_backface || + flags.is_frame_paint_offset_translation != + other.flags.is_frame_paint_offset_translation || + flags.is_for_svg_child != other.flags.is_for_svg_child || backface_visibility != other.backface_visibility || rendering_context_id != other.rendering_context_id || compositor_element_id != other.compositor_element_id || @@ -129,13 +131,14 @@ // The root of the transform tree. The root transform node references the root // scroll node. const TransformPaintPropertyNode& TransformPaintPropertyNode::Root() { - DEFINE_STATIC_REF(TransformPaintPropertyNode, root, - base::AdoptRef(new TransformPaintPropertyNode( - nullptr, State{{}, - &ScrollPaintPropertyNode::Root(), - nullptr, - false /* flattens_inherited_transform */, - false /* in_subtree_of_page_scale */}))); + DEFINE_STATIC_REF( + TransformPaintPropertyNode, root, + base::AdoptRef(new TransformPaintPropertyNode( + nullptr, State{{}, + &ScrollPaintPropertyNode::Root(), + nullptr, + State::Flags{false /* flattens_inherited_transform */, + false /* in_subtree_of_page_scale */}}))); return *root; } @@ -166,12 +169,10 @@ json->SetString("matrix", matrix.Replace("\n", ", ")); json->SetString("origin", String(Origin().ToString())); } - if (!state_.flattens_inherited_transform) { + if (!state_.flags.flattens_inherited_transform) json->SetBoolean("flattensInheritedTransform", false); - } - if (!state_.in_subtree_of_page_scale) { + if (!state_.flags.in_subtree_of_page_scale) json->SetBoolean("in_subtree_of_page_scale", false); - } if (state_.backface_visibility != BackfaceVisibility::kInherited) { json->SetString("backface", state_.backface_visibility == BackfaceVisibility::kVisible
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h index bb7ab651..a2a609c 100644 --- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h +++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -103,13 +103,21 @@ scoped_refptr<const ScrollPaintPropertyNode> scroll; scoped_refptr<const TransformPaintPropertyNode> scroll_translation_for_fixed; - bool flattens_inherited_transform : 1 = false; - bool in_subtree_of_page_scale : 1 = true; - bool animation_is_axis_aligned : 1 = false; - bool delegates_to_parent_for_backface : 1 = false; - // Set if a frame is rooted at this node. - bool is_frame_paint_offset_translation : 1 = false; - bool is_for_svg_child : 1 = false; + + // Use bitfield packing instead of separate bools to save space. + struct Flags { + DISALLOW_NEW(); + + public: + bool flattens_inherited_transform : 1; + bool in_subtree_of_page_scale : 1; + bool animation_is_axis_aligned : 1; + bool delegates_to_parent_for_backface : 1; + // Set if a frame is rooted at this node. + bool is_frame_paint_offset_translation : 1; + bool is_for_svg_child : 1; + } flags = {false, true, false, false, false, false}; + BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited; unsigned rendering_context_id = 0; CompositingReasons direct_compositing_reasons = CompositingReason::kNone; @@ -214,7 +222,7 @@ // If true, this node is a descendant of the page scale transform. This is // important for avoiding raster during pinch-zoom (see: crbug.com/951861). bool IsInSubtreeOfPageScale() const { - return state_.in_subtree_of_page_scale; + return state_.flags.in_subtree_of_page_scale; } const CompositorStickyConstraint* GetStickyConstraint() const { @@ -242,7 +250,7 @@ // the plane of its parent. This is implemented by flattening the total // accumulated transform from its ancestors. bool FlattensInheritedTransform() const { - return state_.flattens_inherited_transform; + return state_.flags.flattens_inherited_transform; } // Returns the local BackfaceVisibility value set on this node. To be used @@ -274,8 +282,8 @@ bool FlattensInheritedTransformSameAsParent() const { if (IsRoot()) return true; - return state_.flattens_inherited_transform == - Parent()->Unalias().state_.flattens_inherited_transform; + return state_.flags.flattens_inherited_transform == + Parent()->Unalias().state_.flags.flattens_inherited_transform; } bool HasDirectCompositingReasons() const { @@ -314,7 +322,7 @@ } bool TransformAnimationIsAxisAligned() const { - return state_.animation_is_axis_aligned; + return state_.flags.animation_is_axis_aligned; } bool RequiresCompositingForRootScroller() const { @@ -344,11 +352,11 @@ } bool IsFramePaintOffsetTranslation() const { - return state_.is_frame_paint_offset_translation; + return state_.flags.is_frame_paint_offset_translation; } bool DelegatesToParentForBackface() const { - return state_.delegates_to_parent_for_backface; + return state_.flags.delegates_to_parent_for_backface; } // Content whose transform nodes have a common rendering context ID are 3D @@ -356,7 +364,7 @@ unsigned RenderingContextId() const { return state_.rendering_context_id; } bool HasRenderingContext() const { return state_.rendering_context_id; } - bool IsForSVGChild() const { return state_.is_for_svg_child; } + bool IsForSVGChild() const { return state_.flags.is_for_svg_child; } std::unique_ptr<JSONObject> ToJSON() const;
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc index 338c01d..03d26352 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
@@ -400,6 +400,8 @@ dest->do_not_prompt_for_login = true; dest->load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY; } + + dest->has_storage_access = src.GetHasStorageAccess(); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index fdf6f1a..a0c915e 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2109,6 +2109,12 @@ public: true, }, { + name: "NavigateEventCancelableTraversals", + status: "experimental", + base_feature: "NavigateEventCancelableTraversals", + base_feature_status: "enabled", + }, + { name: "NavigationId", status: "experimental", origin_trial_feature_name: "SoftNavigationHeuristics",
diff --git a/third_party/blink/renderer/platform/scheduler/public/main_thread.h b/third_party/blink/renderer/platform/scheduler/public/main_thread.h index f6a6ac4..ad2ae3f0 100644 --- a/third_party/blink/renderer/platform/scheduler/public/main_thread.h +++ b/third_party/blink/renderer/platform/scheduler/public/main_thread.h
@@ -33,6 +33,7 @@ friend class WebGLWebCodecsVideoFrame; friend class WebRtcVideoFrameAdapter; friend class WorkerGlobalScope; + friend class HibernationHandler; friend MainThreadTaskRunnerRestricted AccessMainThreadForGpuFactories(); friend MainThreadTaskRunnerRestricted AccessMainThreadForWebGraphicsContext3DProvider();
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc index 04b03d4..ed5b2d6 100644 --- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc +++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -3179,6 +3179,11 @@ } TEST_F(InputHandlerProxyEventQueueTest, CoalescedEventSwitchToMainThread) { + // After scroll unification, we do not handle scrolls on the main thread. + if (base::FeatureList::IsEnabled(features::kScrollUnification)) { + return; + } + cc::InputHandlerScrollResult scroll_result_did_scroll_; cc::InputHandlerScrollResult scroll_result_did_not_scroll_; scroll_result_did_scroll_.did_scroll = true;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index de2e7da9..522270c 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -915,6 +915,9 @@ crbug.com/922249 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure Pass ] +# Fractional paint offsets lead to 1px bleed with position sticky. +crbug.com/1136079 external/wpt/css/css-position/sticky/position-sticky-fractional-offset.html [ Failure ] + # ====== Layout team owned tests from here ====== crbug.com/711704 external/wpt/css/CSS2/floats/floats-rule3-outside-left-002.xht [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/aria-hidden-hides-all-elements.html b/third_party/blink/web_tests/accessibility/aria-hidden-hides-all-elements.html index 07ba441..b31d4ce 100644 --- a/third_party/blink/web_tests/accessibility/aria-hidden-hides-all-elements.html +++ b/third_party/blink/web_tests/accessibility/aria-hidden-hides-all-elements.html
@@ -43,19 +43,19 @@ var content = accessibilityController.accessibleElementById("main"); assert_equals(content.childrenCount, 8); assert_true(content.isIgnored); - expectUnignoredChildren(content, [0, 1, 2, 3, 6]); + expectUnignoredChildren(content, [1, 2, 3, 6]); document.getElementById("ul").tabIndex = 0; assert_equals(content.childrenCount, 8, "Making list focusable should make it unignored"); assert_true(content.isIgnored); - expectUnignoredChildren(content, [0, 1, 2, 3, 5, 6]); + expectUnignoredChildren(content, [1, 2, 3, 5, 6]); document.getElementById("ul").removeAttribute("tabindex"); assert_equals(content.childrenCount, 8, "Making list unfocusable should make it ignored again"); assert_true(content.isIgnored); - expectUnignoredChildren(content, [0, 1, 2, 3, 6]); + expectUnignoredChildren(content, [1, 2, 3, 6]); }); </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset-ref.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset-ref.html new file mode 100644 index 0000000..8b7a1f8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset-ref.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<style> + .container { + width: 100px; + height: 100px; + overflow-y: scroll; + background: lightgreen; + display: inline-block; + } +</style> + +<div class="container"> + <div style="height: calc(300px + 50px + 10.10px);"></div> +</div> + +<div class="container"> + <div style="height: calc(300px + 50px + 10.25px);"></div> +</div> + +<div class="container"> + <div style="height: calc(300px + 50px + 10.50px);"></div> +</div> + +<div class="container"> + <div style="height: calc(300px + 50px + 10.75px);"></div> +</div> + +<div class="container"> + <div style="height: calc(300px + 50px + 10.90px);"></div> +</div> + +<script> + window.onload = function() { + var containers = document.getElementsByClassName('container'); + for (let i = 0; i < containers.length; i++) { + containers[i].scrollTo(0, 20); + } + }; +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset.html new file mode 100644 index 0000000..79c29f4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-fractional-offset.html
@@ -0,0 +1,80 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" /> +<meta name="assert" content="Position sticky with a fractional offset should not show a gap" /> +<link rel="match" href="position-sticky-fractional-offset-ref.html" /> + +<style> + .sticky-container { + width: 100px; + height: 100px; + overflow-y: scroll; + background: red; + display: inline-block; + } + + .sticky { + position: sticky; + top: 0; + height: 50px; + background: lightgreen; + } + + .force-scroll { + height: 300px; + background: lightgreen; + } +</style> + +<div class="sticky-container"> + <div style="height: 10.10px;"></div> + <div class="sticky"></div> + <div class="force-scroll"></div> +</div> + +<div class="sticky-container"> + <div style="height: 10.25px;"></div> + <div class="sticky"></div> + <div class="force-scroll"></div> +</div> + +<div class="sticky-container"> + <div style="height: 10.50px;"></div> + <div class="sticky"></div> + <div class="force-scroll"></div> +</div> + +<div class="sticky-container"> + <div style="height: 10.75px;"></div> + <div class="sticky"></div> + <div class="force-scroll"></div> +</div> + +<div class="sticky-container"> + <div style="height: 10.90px;"></div> + <div class="sticky"></div> + <div class="force-scroll"></div> +</div> + +<script> + window.onload = function() { + // Start with all containers scrolled to the top. + var containers = document.getElementsByClassName('sticky-container'); + for (let i = 0; i < containers.length; i++) { + containers[i].scrollTo(0, 0); + } + + // Wait for a full frame, then scroll all containers down so the sticky + // elements are stuck to the container. There should be no visible gap + // where the container's red background color is visible. + requestAnimationFrame(() => { + requestAnimationFrame(() => { + for (let i = 0; i < containers.length; i++) { + containers[i].scrollTo(0, 20); + } + document.documentElement.classList.remove('reftest-wait'); + }); + }); + }; +</script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html new file mode 100644 index 0000000..7c500fd --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-toggle/toggle-aria-roles.tentative.html
@@ -0,0 +1,354 @@ +<!DOCTYPE HTML> +<meta charset="UTF-8"> +<title>CSS Toggles: ARIA roles</title> +<link rel="author" title="L. David Baron" href="https://dbaron.org/"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link rel="help" href="https://tabatkins.github.io/css-toggle/"> +<link rel="help" href="https://github.com/tabatkins/css-toggle/issues/41"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/toggle-helpers.js"></script> +<style id="style"></style> + +<body> + +<div id="container"></div> +<script> + +let aria_role_tests = [ + // Markup to create the test assertions: + // data-expected-role: The expected aria role for this element. + // + // Helper markup to create more markup: + // class=group: group the group with the toggle-group property + // class=group-self: same, but with the self keyword (narrow scope) + // class=root: create a test-role toggle with the toggle-root property + // class=root-group: same, but with the 'group' keyword + // class=root-self: same, but with the 'self' keyword + // class=trigger: toggle-trigger to activate test-role toggle + // class=visibility: toggle-visibility connected to test-role toggle + ` + <div></div> + `, + ` + <div class="root"> + <div></div> + </div> + `, + ` + <div class="root trigger" data-expected-role="checkbox"></div> + `, + // Test that ARIA attributes override the toggle inference: + ` + <div class="root trigger" role="link" data-expected-role="link"></div> + `, + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + </div> + `, + + // Radios and radio groups: + ` + <div class="group" data-expected-role="radiogroup"> + <div class="root-group trigger" data-expected-role="radio"></div> + </div> + `, + ` + <div class="group" data-expected-role="radiogroup"> + <div class="root-group trigger" data-expected-role="radio"></div> + <div class="root-group trigger" data-expected-role="radio"></div> + </div> + `, + ` + <div> + <div class="root-group trigger" data-expected-role="radio"></div> + </div> + `, + ` + <div style="toggle-group: another-group"> + <div class="root-group trigger" data-expected-role="radio"></div> + </div> + `, + ` + <div style="toggle-group: another-group, test-role, third-group" data-expected-role="radiogroup"> + <div class="root-group trigger" data-expected-role="radio"></div> + </div> + `, + + + // Checkboxes and checkbox groups: + ` + <div> + <div class="root trigger" data-expected-role="checkbox"></div> + </div> + `, + // TODO(dbaron): This is a checkbox group... but we can't distinguish + // that with current ARIA roles. + ` + <div> + <div class="root trigger" data-expected-role="checkbox"></div> + <div class="root trigger" data-expected-role="checkbox"></div> + </div> + `, + + // Disclosure: + // TODO(dbaron): This is a disclosure... but how is it possible to + // distinguish with ARIA roles (compare to next test!)? + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + `, + // This is not a disclosure because it has a toggle-group. + ` + <div class="root-group"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + `, + // This is button with popup (absolute positioning) + // TODO(dbaron): This test doesn't actually distinguish this from + // disclosure because the internal kPopUpButton role maps to "button" + // in kReverseRoles in ax_object.cc. + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility" style="position: absolute"></div> + </div> + `, + // This is button with popup (fixed positioning) + // TODO(dbaron): This test doesn't actually distinguish this from + // disclosure because the internal kPopUpButton role maps to "button" + // in kReverseRoles in ax_object.cc. + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility" style="position: fixed"></div> + </div> + `, + // This is button with popup (popover) + // TODO(dbaron): This test doesn't actually distinguish this from + // disclosure because the internal kPopUpButton role maps to "button" + // in kReverseRoles in ax_object.cc. + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility" popover="auto"></div> + </div> + `, + // This is disclosure (NOT button with popup) (sticky positioning) + ` + <div class="root"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility" style="position: sticky"></div> + </div> + `, + + // Accordion: + ` + <div class="group"> + <div class="root-group" data-expected-role="region"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + <div class="root-group" data-expected-role="region"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + </div> + `, + // Not accordion because of other siblings: + ` + <div class="group"> + <div class="root-group"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + <div class="root-group"> + <div class="trigger" data-expected-role="button"></div> + <div class="visibility"></div> + </div> + <div></div> + <div></div> + <div></div> + </div> + `, + + // Tree: + // TODO(dbaron): This should probably also work with the toggles on + // the <button>! + // TODO(dbaron): This should probably mark the non-interactive items + // as treeitem as well! + // TODO(dbaron): Do the elements getting the roles here make sense? + // TODO(dbaron): The requirement for having multiple disclosure-ish + // children to qualify as accordion-ish probably doesn't make sense + // here. The test below is basically the minimal example that gets + // detected as a tree, but simpler things definitely should be! + // TODO(dbaron): The inner parts of the tree should also be getting + // tree roles! + ` + <ul data-expected-role="tree"> + <li class="root-self" data-expected-role="group"> + <button class="trigger" data-expected-role="treeitem"></button> + <ul class="visibility" data-expected-role="list"> + <li>item</li> + <li class="root-self"> + <button class="trigger" data-expected-role="button"></button> + <ul class="visibility" data-expected-role="list"> + <li>item</li> + <li>item</li> + </ul> + </li> + <li class="root-self"> + <button class="trigger" data-expected-role="button"></button> + <ul class="visibility" data-expected-role="list"> + <li>item</li> + <li>item</li> + </ul> + </li> + </ul> + </li> + <li class="root-self" data-expected-role="group"> + <button class="trigger" data-expected-role="treeitem"></button> + <ul class="visibility" data-expected-role="list"> + <li class="root-self"> + <button class="trigger" data-expected-role="button"></button> + <ul class="visibility" data-expected-role="list"> + <li>item</li> + <li>item</li> + </ul> + </li> + <li class="root-self"> + <button class="trigger" data-expected-role="button"></button> + <ul class="visibility" data-expected-role="list"> + <li>item</li> + <li>item</li> + </ul> + </li> + </ul> + </li> + </ul> + `, + + // Tabs: + ` + <section class="group" data-expected-role="tablist"> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + </section> + `, + ` + <section class="group" data-expected-role="tablist"> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <div></div> + </section> + `, + ` + <section class="group" data-expected-role="tablist"> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 style="toggle-root: other-toggle; toggle-trigger: other-toggle" data-expected-role="checkbox"></h1> + </section> + `, + ` + <section class="group" data-expected-role="tablist"> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 class="root-group trigger" data-expected-role="tab"></h1> + <div class="visibility" data-expected-role="tabpanel"></div> + <h1 style="toggle-root: other-toggle; toggle-trigger: other-toggle" data-expected-role="button"></h1> + <div style="toggle-visibility: toggle other-toggle"></div> + </section> + `, + // TODO(https://crbug.com/758089): The expected role for the <section> + // should be generic rather than null! + ` + <section class="group" data-expected-role="null"> + <h1 class="root-group trigger" data-expected-role="button"></h1> + <div class="visibility"></div> + <h1 class="root-group trigger" data-expected-role="button"></h1> + <div class="visibility"></div> + <div></div> + <div></div> + <div></div> + <div></div> + </section> + `, + ` + <section class="group" data-expected-role="radiogroup"> + <h1 class="root-group trigger" data-expected-role="radio"></h1> + <h1 class="root-group trigger" data-expected-role="radio"></h1> + <div></div> + <div></div> + <div></div> + <div></div> + </section> + `, +]; + +for (let t of aria_role_tests) { + promise_test(async function() { + container.innerHTML = t; + + for (let e of container.querySelectorAll('.group')) { + e.style.toggleGroup = "test-role"; + } + for (let e of container.querySelectorAll('.group-self')) { + e.style.toggleGroup = "test-role self"; + } + for (let e of container.querySelectorAll('.root')) { + e.style.toggleRoot = "test-role"; + } + for (let e of container.querySelectorAll('.root-group')) { + e.style.toggleRoot = "test-role group"; + } + for (let e of container.querySelectorAll('.root-self')) { + e.style.toggleRoot = "test-role self"; + } + for (let e of container.querySelectorAll('.trigger')) { + e.style.toggleTrigger = "test-role"; + } + for (let e of container.querySelectorAll('.visibility')) { + e.style.toggleVisibility = "toggle test-role"; + } + + for (let e of container.querySelectorAll('.root, .root-nogroup')) { + await wait_for_toggle_creation(e); + } + + let count = 0; + for (let e of container.querySelectorAll("*")) { + if (e == container) + continue; + + let expected_role = "generic"; + if (e.hasAttribute("data-expected-role")) { + expected_role = e.getAttribute("data-expected-role"); + // TODO(https://crbug.com/758089): See above regarding <section>; + // this null handling should eventually be removed. + if (expected_role === "null") { + expected_role = null; + } + } + ++count; + // NOTE: This relies on Element.computedRole, which is an + // experimental feature behind the ComputedAccessibilityInfo flag + // in blink. + assert_equals(e.computedRole, expected_role, `role on ${e.tagName} element (#${count})`); + } + + }, `aria role test: ${t}`); +} + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/rendering/fullscreen-pseudo-class-expected.txt b/third_party/blink/web_tests/external/wpt/fullscreen/rendering/fullscreen-pseudo-class-expected.txt deleted file mode 100644 index ff07788..0000000 --- a/third_party/blink/web_tests/external/wpt/fullscreen/rendering/fullscreen-pseudo-class-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL :fullscreen pseudo-class assert_true: outer:fullscreen in nested fullscreen expected true got false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.html index b2994245..b0df2f2 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-events.html
@@ -20,7 +20,7 @@ window.onload = () => { for(const method of ["listener","attribute"]) { promise_test(async t => { - const popover = document.querySelector('[popover]'); + const {popover,signal} = getPopoverAndSignal(t); assert_false(popover.matches(':open')); let showCount = 0; let afterShowCount = 0; @@ -59,10 +59,9 @@ }; switch (method) { case "listener": - const {signal} = getPopoverAndSignal(t); - // These events bubble. - document.addEventListener('beforetoggle', listener, {signal}); - document.addEventListener('toggle', listener, {signal}); + // These events do *not* bubble. + popover.addEventListener('beforetoggle', listener, {signal}); + popover.addEventListener('toggle', listener, {signal}); break; case "attribute": assert_false(popover.hasAttribute('onbeforetoggle'));
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-top-layer-interactions.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-top-layer-interactions.html index 50a21be..5cd4d4c 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-top-layer-interactions.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-top-layer-interactions.html
@@ -35,10 +35,10 @@ }, { type: types.fullscreen, - closes: [types.popover, types.fullscreen], + closes: [types.popover], createElement: () => document.createElement('div'), trigger: async function(visibleElement) {assert_false(this.isTopLayer());await blessTopLayer(visibleElement);await this.element.requestFullscreen();}, - close: function() {assert_equals(this.element,document.fullscreenElement); document.exitFullscreen();}, + close: function() {document.exitFullscreen();}, isTopLayer: function() {return this.element.matches(':fullscreen');}, }, ];
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-fragment.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-fragment.html index 976754f..57a30c8 100644 --- a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-fragment.html +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-fragment.html
@@ -12,7 +12,7 @@ navigation.onnavigate = t.step_func_done(e => { assert_equals(e.navigationType, "traverse"); - assert_false(e.cancelable); + assert_true(e.cancelable); assert_true(e.canIntercept); assert_false(e.userInitiated); assert_true(e.hashChange);
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-pushState.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-pushState.html index 4d870fb2..bf2e6e4 100644 --- a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-pushState.html +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-history-back-after-pushState.html
@@ -12,7 +12,7 @@ navigation.onnavigate = t.step_func_done(e => { assert_equals(e.navigationType, "traverse"); - assert_false(e.cancelable); + assert_true(e.cancelable); assert_true(e.canIntercept); assert_false(e.userInitiated); assert_false(e.hashChange);
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-cross-document.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-cross-document.html index 214644066..2e1adbe 100644 --- a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-cross-document.html +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-cross-document.html
@@ -9,7 +9,9 @@ let target_id = i.contentWindow.navigation.currentEntry.id; i.contentWindow.navigation.navigate("?foo"); i.onload = t.step_func(() => { + let beforeunload_called = false; i.contentWindow.navigation.onnavigate = t.step_func_done(e => { + assert_true(beforeunload_called); assert_equals(e.navigationType, "traverse"); assert_false(e.cancelable); assert_false(e.canIntercept); @@ -24,6 +26,7 @@ assert_equals(e.formData, null); assert_equals(e.info, "hi"); }); + i.contentWindow.onbeforeunload = () => beforeunload_called = true; assert_true(i.contentWindow.navigation.canGoBack); i.contentWindow.navigation.back({ info: "hi" }); })
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html new file mode 100644 index 0000000..cebd2f36 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html
@@ -0,0 +1,33 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + let target_key = i.contentWindow.navigation.currentEntry.key; + let target_id = i.contentWindow.navigation.currentEntry.id; + await i.contentWindow.navigation.navigate("#").finished; + assert_true(i.contentWindow.navigation.canGoBack); + + i.contentWindow.navigation.onnavigate = e => { + assert_equals(e.navigationType, "traverse"); + assert_false(e.cancelable, "traversals in iframes should never be cancelable"); + assert_true(e.canIntercept); + assert_false(e.userInitiated); + assert_true(e.hashChange); + assert_equals(e.downloadRequest, null); + assert_equals(new URL(e.destination.url).hash, ""); + assert_true(e.destination.sameDocument); + assert_equals(e.destination.key, target_key); + assert_equals(e.destination.id, target_id); + assert_equals(e.destination.index, 0); + assert_equals(e.formData, null); + assert_equals(e.info, "hi"); + } + await i.contentWindow.navigation.back({ info: "hi" }).finished; +}, "navigate event for navigation.back() - same-document in an iframe"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document.html index 8753e6b1..431d384 100644 --- a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document.html +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-navigation-back-same-document.html
@@ -11,7 +11,7 @@ navigation.navigate("#foo").committed.then(t.step_func(() => { navigation.onnavigate = t.step_func_done(e => { assert_equals(e.navigationType, "traverse"); - assert_false(e.cancelable); + assert_true(e.cancelable); assert_true(e.canIntercept); assert_false(e.userInitiated); assert_true(e.hashChange);
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html new file mode 100644 index 0000000..0b5b750 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html
@@ -0,0 +1,33 @@ + +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../navigation-methods/return-value/resources/helpers.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + let w = window.open("resources/opener-postMessage-onload.html"); + await new Promise(resolve => window.onmessage = resolve); + // Navigate to a url that will notify us when the navigation is complete. + w.navigation.navigate("opener-postMessage-onload.html?1"); + + await new Promise(resolve => window.onmessage = resolve); + assert_equals(w.navigation.entries().length, 2); + assert_equals(w.navigation.currentEntry.index, 1); + let navigate_called = false; + w.navigation.onnavigate = t.step_func(e => { + navigate_called = true; + assert_false(e.destination.sameDocument); + assert_false(e.cancelable); + // Should do nothing. + e.preventDefault(); + }); + w.navigation.back(); + await new Promise(resolve => window.onmessage = resolve); + assert_equals(w.navigation.currentEntry.index, 0); + assert_true(navigate_called); +}, "navigation.back() cross-document cannot be cancelled with the navigate event"); +</script> +
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html new file mode 100644 index 0000000..7edb18882 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html
@@ -0,0 +1,26 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../navigation-methods/return-value/resources/helpers.js"></script> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + await navigation.navigate("#").finished; + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + + navigation.onnavigate = e => e.preventDefault(); + + navigation.onnavigateerror = t.step_func(e => { + assert_equals(e.constructor, ErrorEvent); + assert_equals(e.filename, location.href); + navigateerror_called = true; + }); + await assertBothRejectDOM(t, navigation.back(), "AbortError"); + assert_equals(navigation.currentEntry.index, 1); + assert_true(navigateerror_called); +}, "navigation.back() same-document preventDefault"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html new file mode 100644 index 0000000..d68b11f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html
@@ -0,0 +1,47 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../navigation-methods/return-value/resources/helpers.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + // Navigate the iframe, then the top window, so that when the iframe goes back + // to its initial entry, the top window navigates as well. + await i.contentWindow.navigation.navigate("#").finished; + await navigation.navigate("#").finished; + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + + // Ensure the top window, which is allowed to cancel the traversal, does so. + navigation.onnavigate = e => e.preventDefault(); + + let top_navigateerror_fired = false; + navigation.onnavigateerror = t.step_func(e => { + assert_equals(e.constructor, ErrorEvent); + assert_equals(e.filename, location.href); + top_navigateerror_fired = true; + }); + let iframe_navigateerror_fired = false; + i.contentWindow.navigation.onnavigateerror = t.step_func(e => { + assert_equals(e.constructor, i.contentWindow.ErrorEvent); + assert_equals(e.filename, i.contentWindow.location.href); + iframe_navigateerror_fired = true; + }); + + // When the top window blocks the traversal, it should be blocked in the + // iframe as well, and the traversal promises in the iframe should be rejected. + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + await assertBothRejectDOM(t, i.contentWindow.navigation.traverseTo(i.contentWindow.navigation.entries()[0].key), "AbortError", iWindow, iDOMException); + assert_true(top_navigateerror_fired); + assert_true(iframe_navigateerror_fired); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); +}, "navigation.traverseTo() in an iframe with same-document preventDefault in its parent"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html new file mode 100644 index 0000000..31cb54f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html
@@ -0,0 +1,49 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i1" src="/common/blank.html"></iframe> +<iframe id="i2" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + await navigation.navigate("#").finished; + await i1.contentWindow.navigation.navigate("#").finished; + i2.contentWindow.navigation.navigate("?"); + await new Promise(resolve => i2.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(navigation.entries().length, 2); + assert_equals(i1.contentWindow.navigation.entries().length, 2); + assert_equals(i2.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i1.contentWindow.navigation.currentEntry.index, 1); + assert_equals(i2.contentWindow.navigation.currentEntry.index, 1); + + let navigate_event_count = 0; + navigation.onnavigate = t.step_func(e => { + assert_equals(navigate_event_count, 0); + navigate_event_count++; + assert_true(e.cancelable); + }); + i1.contentWindow.navigation.onnavigate = t.step_func(e => { + assert_true(navigate_event_count > 0); + navigate_event_count++; + assert_false(e.cancelable); + }); + i2.contentWindow.navigation.onnavigate = t.step_func(e => { + assert_true(navigate_event_count > 0); + navigate_event_count++; + assert_false(e.cancelable); + }); + + await navigation.traverseTo(navigation.entries()[0].key).finished; + // The top window will finish quickly, becuase it is same-document traversal. + // i2 will be slower because it is cross-document, so wait for its onload. + await new Promise(resolve => i2.onload = () => t.step_timeout(resolve, 0)); + assert_equals(navigate_event_count, 3); + assert_equals(navigation.currentEntry.index, 0); + assert_equals(i1.contentWindow.navigation.currentEntry.index, 0); + assert_equals(i2.contentWindow.navigation.currentEntry.index, 0); +}, "navigation.traverseTo() can navigate 3 frames of different types with correct navigate event cancelable values"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html new file mode 100644 index 0000000..9bb64fb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html
@@ -0,0 +1,23 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + await navigation.navigate("#").finished; + await i.contentWindow.navigation.navigate("#").finished; + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + + navigation.onnavigate = e => e.preventDefault(); + i.contentWindow.navigation.onnavigate = t.unreached_func("navigate event should not fire in the iframe, because the traversal was cancelled in the top window"); + await promise_rejects_dom(t, "AbortError", navigation.traverseTo(navigation.entries()[0].key).finished); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); +}, "navigation.traverseTo() - if a top window cancels the traversal, any iframes should not fire navigate"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html new file mode 100644 index 0000000..11f07af --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html
@@ -0,0 +1,26 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../navigation-methods/return-value/resources/helpers.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + await navigation.navigate("#").finished; + i.contentWindow.navigation.navigate("?"); + await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + + navigation.onnavigate = t.step_func(e => e.preventDefault()); + i.contentWindow.navigation.onnavigate = t.unreached_func("navigation should be cancelled before iframe fires navigate event"); + await assertBothRejectDOM(t, navigation.traverseTo(navigation.entries()[0].key), "AbortError"); + // Give the iframe time to navigate in case it was incorrectly permitted. + await new Promise(resolve => t.step_timeout(resolve, 50)); +}, "navigate.traverseTo() cancelled by top frame cancels cross-document iframe"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html index a0a29188..43bde9a 100644 --- a/third_party/blink/web_tests/external/wpt/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html +++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html
@@ -20,8 +20,16 @@ let result; i.contentWindow.navigation.onnavigate = t.step_func(e => { - e.intercept({ handler: () => new Promise(resolve => t.step_timeout(resolve, 2)) }); - t.step_timeout(() => i.remove(), 1); + // 1. The intercept handler runs. + // 2. "t.step_timeout(handlerRunResolve, 0)" executes handlerRunResolve in a macro task. + // 3. In the next microtask, the iframe is removed. + // 4. "t.step_timeout(resolve, 5)" executes and the intercept handler promise resolves. + let handlerRunResolve; + new Promise(r => handlerRunResolve = r).then(() => i.remove()); + e.intercept({ handler() { + t.step_timeout(handlerRunResolve, 0); + return new Promise(resolve => t.step_timeout(resolve, 5)); + }}); }); i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("navigatesuccess must not fire");
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-getAXNodeAndAncestors-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-getAXNodeAndAncestors-expected.txt index a1ad8e6..5563ebd8 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-getAXNodeAndAncestors-expected.txt +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-getAXNodeAndAncestors-expected.txt
@@ -137,6 +137,13 @@ } ignored : true ignoredReasons : [ + [0] : { + name : uninteresting + value : { + type : boolean + value : true + } + } ] nodeId : <string> parentId : <string>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt index 1cc9a89..cf947a2 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/accessibility/accessibility-ignoredNodes-expected.txt
@@ -12,6 +12,13 @@ domNode : html ignored : true ignoredReasons : [ + [0] : { + name : uninteresting + value : { + type : boolean + value : true + } + } ] nodeId : <string> parentId : <string>
diff --git a/third_party/glide/gif_encoder/BUILD.gn b/third_party/glide/gif_encoder/BUILD.gn deleted file mode 100644 index 1e2abbc8..0000000 --- a/third_party/glide/gif_encoder/BUILD.gn +++ /dev/null
@@ -1,18 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/android/rules.gni") - -assert(is_android) - -android_library("gif_encoder_java") { - sources = [ - "java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java", - "java/com/bumptech/glide/gifencoder/LZWEncoder.java", - "java/com/bumptech/glide/gifencoder/NeuQuant.java", - ] - deps = [ - "//third_party/androidx:androidx_annotation_annotation_java", - ] -}
diff --git a/third_party/glide/gif_encoder/LICENSE b/third_party/glide/gif_encoder/LICENSE deleted file mode 100644 index 0bdc525..0000000 --- a/third_party/glide/gif_encoder/LICENSE +++ /dev/null
@@ -1,25 +0,0 @@ -License for AnimatedGifEncoder.java and LZWEncoder.java - -No copyright asserted on the source code of this class. May be used for any -purpose, however, refer to the Unisys LZW patent for restrictions on use of -the associated LZWEncoder class. Please forward any corrections to -kweiner@fmsware.com. - ------------------------------------------------------------------------------ -License for NeuQuant.java - -Copyright (c) 1994 Anthony Dekker - -NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See -"Kohonen neural networks for optimal colour quantization" in "Network: -Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of -the algorithm. - -Any party obtaining a copy of these files from the author, directly or -indirectly, is granted, free of charge, a full and unrestricted irrevocable, -world-wide, paid up, royalty-free, nonexclusive right and license to deal in -this software and documentation files (the "Software"), including without -limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons who -receive copies from any such party to do so, with the only requirement being -that this copyright notice remain intact. \ No newline at end of file
diff --git a/third_party/glide/gif_encoder/OWNERS b/third_party/glide/gif_encoder/OWNERS deleted file mode 100644 index a60ab48..0000000 --- a/third_party/glide/gif_encoder/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -gujen@google.com -sebsg@chromium.org \ No newline at end of file
diff --git a/third_party/glide/gif_encoder/README.chromium b/third_party/glide/gif_encoder/README.chromium deleted file mode 100644 index 43ce333..0000000 --- a/third_party/glide/gif_encoder/README.chromium +++ /dev/null
@@ -1,17 +0,0 @@ -Name: Android GIF Encoder -URL: https://github.com/bumptech/glide/tree/master/third_party/gif_encoder -Version: a7b254c42888db691b7339b2165abcd462473fcf -License: Notice -License File: LICENSE -License Android Compatible: yes -Security Critical: yes -CPEPrefix: unknown - -Description: - This contains the GIF encoder classes from the Glide library, which were themselves based on the - GIF encoding logic found here: http://java2s.com/Code/Java/2D-Graphics-GUI/AnimatedGifEncoder.htm. - The README.third_party file in the Glide repo has more info. - -Local Modifications: -- Changed the name of the top-level package from "com.bumptech.glide.gifencoder" to - "org.chromium.third_party.glide.gif_encoder" to avoid potential conflicts with the Glide library. \ No newline at end of file
diff --git a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java b/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java deleted file mode 100644 index fd8c313..0000000 --- a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java +++ /dev/null
@@ -1,580 +0,0 @@ -package org.chromium.third_party.glide.gif_encoder; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more - * frames. - * - * <pre> - * Example: - * AnimatedGifEncoder e = new AnimatedGifEncoder(); - * e.start(outputFileName); - * e.setDelay(1000); // 1 frame per sec - * e.addFrame(image1); - * e.addFrame(image2); - * e.addFrame(image3, 100, 100); // set position of the frame - * e.finish(); - * </pre> - * - * No copyright asserted on the source code of this class. May be used for any - * purpose, however, refer to the Unisys LZW patent for restrictions on use of - * the associated LZWEncoder class. Please forward any corrections to - * kweiner@fmsware.com. - * - * @author Kevin Weiner, FM Software - * @version 1.03 November 2003 - * - */ - -public class AnimatedGifEncoder { - private static final String TAG = "AnimatedGifEncoder"; - - // The minimum % of an images pixels that must be transparent for us to set a transparent index - // automatically. - private static final double MIN_TRANSPARENT_PERCENTAGE = 4d; - - private int width; // image size - - private int height; - - private int fixedWidth; // set by setSize() - - private int fixedHeight; - - private Integer transparent = null; // transparent color if given - - private int transIndex; // transparent index in color table - - private int repeat = -1; // no repeat - - private int delay = 0; // frame delay (hundredths) - - private boolean started = false; // ready to output frames - - private OutputStream out; - - private Bitmap image; // current frame - - private byte[] pixels; // BGR byte array from frame - - private byte[] indexedPixels; // converted frame indexed to palette - - private int colorDepth; // number of bit planes - - private byte[] colorTab; // RGB palette - - private boolean[] usedEntry = new boolean[256]; // active palette entries - - private int palSize = 7; // color table size (bits-1) - - private int dispose = -1; // disposal code (-1 = use default) - - private boolean closeStream = false; // close stream when finished - - private boolean firstFrame = true; - - private boolean sizeSet = false; // if false, get size from first frame - - private int sample = 10; // default sample interval for quantizer - - private boolean hasTransparentPixels; - - /** - * Sets the delay time between each frame, or changes it for subsequent frames - * (applies to last frame added). - * - * @param ms - * int delay time in milliseconds - */ - public void setDelay(int ms) { - delay = Math.round(ms / 10.0f); - } - - /** - * Sets the GIF frame disposal code for the last added frame and any - * subsequent frames. Default is 0 if no transparent color has been set, - * otherwise 2. - * - * @param code - * int disposal code. - */ - public void setDispose(int code) { - if (code >= 0) { - dispose = code; - } - } - - /** - * Sets the number of times the set of GIF frames should be played. Default is - * 1; 0 means play indefinitely. Must be invoked before the first image is - * added. - * - * @param iter - * int number of iterations. - */ - public void setRepeat(int iter) { - if (iter >= 0) { - repeat = iter; - } - } - - /** - * Sets the transparent color for the last added frame and any subsequent - * frames. Since all colors are subject to modification in the quantization - * process, the color in the final palette for each frame closest to the given - * color becomes the transparent color for that frame. May be set to null to - * indicate no transparent color. - * - * @param color - * Color to be treated as transparent on display. - */ - public void setTransparent(int color) { - transparent = color; - } - - /** - * Adds next GIF frame. The frame is not written immediately, but is actually - * deferred until the next frame is received so that timing data can be - * inserted. Invoking <code>finish()</code> flushes all frames. If - * <code>setSize</code> was invoked, the size is used for all subsequent frames. - * Otherwise, the actual size of the image is used for each frames. - * - * @param im - * BufferedImage containing frame to write. - * @return true if successful. - */ - public boolean addFrame(@Nullable Bitmap im) { - return addFrame(im, 0, 0); - } - - /** - * Adds next GIF frame to the specified position. The frame is not written immediately, but is - * actually deferred until the next frame is received so that timing data can be inserted. - * Invoking <code>finish()</code> flushes all frames. If <code>setSize</code> was invoked, the - * size is used for all subsequent frames. Otherwise, the actual size of the image is used for - * each frame. - * - * See page 11 of http://giflib.sourceforge.net/gif89.txt for the position of the frame - * - * @param im - * BufferedImage containing frame to write. - * @param x - * Column number, in pixels, of the left edge of the image, with respect to the left - * edge of the Logical Screen. - * @param y - * Row number, in pixels, of the top edge of the image with respect to the top edge of - * the Logical Screen. - * @return true if successful. - */ - public boolean addFrame(@Nullable Bitmap im, int x, int y) { - if ((im == null) || !started) { - return false; - } - boolean ok = true; - try { - if (sizeSet) { - setFrameSize(fixedWidth, fixedHeight); - } else { - setFrameSize(im.getWidth(), im.getHeight()); - } - image = im; - getImagePixels(); // convert to correct format if necessary - analyzePixels(); // build color table & map pixels - if (firstFrame) { - writeLSD(); // logical screen descriptor - writePalette(); // global color table - if (repeat >= 0) { - // use NS app extension to indicate reps - writeNetscapeExt(); - } - } - writeGraphicCtrlExt(); // write graphic control extension - writeImageDesc(x, y); // image descriptor - if (!firstFrame) { - writePalette(); // local color table - } - writePixels(); // encode and write pixel data - firstFrame = false; - } catch (IOException e) { - ok = false; - } - - return ok; - } - - /** - * Flushes any pending data and closes output file. If writing to an - * OutputStream, the stream is not closed. - */ - public boolean finish() { - if (!started) - return false; - boolean ok = true; - started = false; - try { - out.write(0x3b); // GIF trailer - out.flush(); - if (closeStream) { - out.close(); - } - } catch (IOException e) { - ok = false; - } - - // reset for subsequent use - transIndex = 0; - out = null; - image = null; - pixels = null; - indexedPixels = null; - colorTab = null; - closeStream = false; - firstFrame = true; - - return ok; - } - - /** - * Sets frame rate in frames per second. Equivalent to - * <code>setDelay(1000/fps)</code>. - * - * @param fps - * float frame rate (frames per second) - */ - public void setFrameRate(float fps) { - if (fps != 0f) { - delay = Math.round(100f / fps); - } - } - - /** - * Sets quality of color quantization (conversion of images to the maximum 256 - * colors allowed by the GIF specification). Lower values (minimum = 1) - * produce better colors, but slow processing significantly. 10 is the - * default, and produces good color mapping at reasonable speeds. Values - * greater than 20 do not yield significant improvements in speed. - * - * @param quality int greater than 0. - */ - public void setQuality(int quality) { - if (quality < 1) - quality = 1; - sample = quality; - } - - /** - * Sets the fixed GIF frame size for all the frames. - * This should be called before start. - * - * @param w - * int frame width. - * @param h - * int frame width. - */ - public void setSize(int w, int h) { - if (started) { - return; - } - - fixedWidth = w; - fixedHeight = h; - if (fixedWidth < 1) { - fixedWidth = 320; - } - if (fixedHeight < 1) { - fixedHeight = 240; - } - - sizeSet = true; - } - - /** - * Sets current GIF frame size. - * - * @param w - * int frame width. - * @param h - * int frame width. - */ - private void setFrameSize(int w, int h) { - width = w; - height = h; - } - - /** - * Initiates GIF file creation on the given stream. The stream is not closed - * automatically. - * - * @param os - * OutputStream on which GIF images are written. - * @return false if initial write failed. - */ - public boolean start(@Nullable OutputStream os) { - if (os == null) - return false; - boolean ok = true; - closeStream = false; - out = os; - try { - writeString("GIF89a"); // header - } catch (IOException e) { - ok = false; - } - return started = ok; - } - - /** - * Initiates writing of a GIF file with the specified name. - * - * @param file - * String containing output file name. - * @return false if open or initial write failed. - */ - public boolean start(@NonNull String file) { - boolean ok; - try { - out = new BufferedOutputStream(new FileOutputStream(file)); - ok = start(out); - closeStream = true; - } catch (IOException e) { - ok = false; - } - return started = ok; - } - - /** - * Analyzes image colors and creates color map. - */ - private void analyzePixels() { - int len = pixels.length; - int nPix = len / 3; - indexedPixels = new byte[nPix]; - NeuQuant nq = new NeuQuant(pixels, len, sample); - // initialize quantizer - colorTab = nq.process(); // create reduced palette - // convert map from BGR to RGB - for (int i = 0; i < colorTab.length; i += 3) { - byte temp = colorTab[i]; - colorTab[i] = colorTab[i + 2]; - colorTab[i + 2] = temp; - usedEntry[i / 3] = false; - } - // map image pixels to new palette - int k = 0; - for (int i = 0; i < nPix; i++) { - int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff); - usedEntry[index] = true; - indexedPixels[i] = (byte) index; - } - pixels = null; - colorDepth = 8; - palSize = 7; - // get closest match to transparent color if specified - if (transparent != null) { - transIndex = findClosest(transparent); - } else if (hasTransparentPixels) { - transIndex = findClosest(Color.TRANSPARENT); - } - } - - /** - * Returns index of palette color closest to c - * - */ - private int findClosest(int color) { - if (colorTab == null) - return -1; - int r = Color.red(color); - int g = Color.green(color); - int b = Color.blue(color); - int minpos = 0; - int dmin = 256 * 256 * 256; - int len = colorTab.length; - for (int i = 0; i < len;) { - int dr = r - (colorTab[i++] & 0xff); - int dg = g - (colorTab[i++] & 0xff); - int db = b - (colorTab[i] & 0xff); - int d = dr * dr + dg * dg + db * db; - int index = i / 3; - if (usedEntry[index] && (d < dmin)) { - dmin = d; - minpos = index; - } - i++; - } - return minpos; - } - - /** - * Extracts image pixels into byte array "pixels" - */ - private void getImagePixels() { - int w = image.getWidth(); - int h = image.getHeight(); - - if ((w != width) || (h != height)) { - // create new image with right size/format - Bitmap temp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(temp); - canvas.drawBitmap(temp, 0, 0, null); - image = temp; - } - int[] pixelsInt = new int[w * h]; - image.getPixels(pixelsInt, 0, w, 0, 0, w, h); - - // The algorithm requires 3 bytes per pixel as RGB. - pixels = new byte[pixelsInt.length * 3]; - - int pixelsIndex = 0; - hasTransparentPixels = false; - int totalTransparentPixels = 0; - for (final int pixel : pixelsInt) { - if (pixel == Color.TRANSPARENT) { - totalTransparentPixels++; - } - pixels[pixelsIndex++] = (byte) (pixel & 0xFF); - pixels[pixelsIndex++] = (byte) ((pixel >> 8) & 0xFF); - pixels[pixelsIndex++] = (byte) ((pixel >> 16) & 0xFF); - } - - double transparentPercentage = 100 * totalTransparentPixels / (double) pixelsInt.length; - // Assume images with greater where more than n% of the pixels are transparent actually have - // transparency. See issue #214. - hasTransparentPixels = transparentPercentage > MIN_TRANSPARENT_PERCENTAGE; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "got pixels for frame with " + transparentPercentage - + "% transparent pixels"); - } - } - - /** - * Writes Graphic Control Extension - */ - private void writeGraphicCtrlExt() throws IOException { - out.write(0x21); // extension introducer - out.write(0xf9); // GCE label - out.write(4); // data block size - int transp, disp; - if (transparent == null && !hasTransparentPixels) { - transp = 0; - disp = 0; // dispose = no action - } else { - transp = 1; - disp = 2; // force clear if using transparent color - } - if (dispose >= 0) { - disp = dispose & 7; // user override - } - disp <<= 2; - - // packed fields - out.write(0 | // 1:3 reserved - disp | // 4:6 disposal - 0 | // 7 user input - 0 = none - transp); // 8 transparency flag - - writeShort(delay); // delay x 1/100 sec - out.write(transIndex); // transparent color index - out.write(0); // block terminator - } - - /** - * Writes Image Descriptor - */ - private void writeImageDesc(int x, int y) throws IOException { - out.write(0x2c); // image separator - writeShort(x); // image position - writeShort(y); - writeShort(width); // image size - writeShort(height); - // packed fields - if (firstFrame) { - // no LCT - GCT is used for first (or only) frame - out.write(0); - } else { - // specify normal LCT - out.write(0x80 | // 1 local color table 1=yes - 0 | // 2 interlace - 0=no - 0 | // 3 sorted - 0=no - 0 | // 4-5 reserved - palSize); // 6-8 size of color table - } - } - - /** - * Writes Logical Screen Descriptor - */ - private void writeLSD() throws IOException { - // logical screen size - writeShort(width); - writeShort(height); - // packed fields - out.write((0x80 | // 1 : global color table flag = 1 (gct used) - 0x70 | // 2-4 : color resolution = 7 - 0x00 | // 5 : gct sort flag = 0 - palSize)); // 6-8 : gct size - - out.write(0); // background color index - out.write(0); // pixel aspect ratio - assume 1:1 - } - - /** - * Writes Netscape application extension to define repeat count. - */ - private void writeNetscapeExt() throws IOException { - out.write(0x21); // extension introducer - out.write(0xff); // app extension label - out.write(11); // block size - writeString("NETSCAPE" + "2.0"); // app id + auth code - out.write(3); // sub-block size - out.write(1); // loop sub-block id - writeShort(repeat); // loop count (extra iterations, 0=repeat forever) - out.write(0); // block terminator - } - - /** - * Writes color table - */ - private void writePalette() throws IOException { - out.write(colorTab, 0, colorTab.length); - int n = (3 * 256) - colorTab.length; - for (int i = 0; i < n; i++) { - out.write(0); - } - } - - /** - * Encodes and writes pixel data - */ - private void writePixels() throws IOException { - LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth); - encoder.encode(out); - } - - /** - * Write 16-bit value to output stream, LSB first - */ - private void writeShort(int value) throws IOException { - out.write(value & 0xff); - out.write((value >> 8) & 0xff); - } - - /** - * Writes string to output stream - */ - private void writeString(String s) throws IOException { - for (int i = 0; i < s.length(); i++) { - out.write((byte) s.charAt(i)); - } - } -} \ No newline at end of file
diff --git a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/LZWEncoder.java b/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/LZWEncoder.java deleted file mode 100644 index fed3330..0000000 --- a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/LZWEncoder.java +++ /dev/null
@@ -1,296 +0,0 @@ -package org.chromium.third_party.glide.gif_encoder; - -import java.io.IOException; -import java.io.OutputStream; - -// ============================================================================== -// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. -// K Weiner 12/00 -class LZWEncoder { - - private static final int EOF = -1; - - private int imgW, imgH; - - private byte[] pixAry; - - private int initCodeSize; - - private int remaining; - - private int curPixel; - - // GIFCOMPR.C - GIF Image compression routines - // - // Lempel-Ziv compression based on 'compress'. GIF modifications by - // David Rowley (mgardi@watdcsu.waterloo.edu) - - // General DEFINEs - - static final int BITS = 12; - - static final int HSIZE = 5003; // 80% occupancy - - // GIF Image compression - modified 'compress' - // - // Based on: compress.c - File compression ala IEEE Computer, June 1984. - // - // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) - // Jim McKie (decvax!mcvax!jim) - // Steve Davies (decvax!vax135!petsd!peora!srd) - // Ken Turkowski (decvax!decwrl!turtlevax!ken) - // James A. Woods (decvax!ihnp4!ames!jaw) - // Joe Orost (decvax!vax135!petsd!joe) - - int n_bits; // number of bits/code - - int maxbits = BITS; // user settable max # bits/code - - int maxcode; // maximum code, given n_bits - - int maxmaxcode = 1 << BITS; // should NEVER generate this code - - int[] htab = new int[HSIZE]; - - int[] codetab = new int[HSIZE]; - - int hsize = HSIZE; // for dynamic table sizing - - int free_ent = 0; // first unused entry - - // block compression parameters -- after all codes are used up, - // and compression rate changes, start over. - boolean clear_flg = false; - - // Algorithm: use open addressing double hashing (no chaining) on the - // prefix code / next character combination. We do a variant of Knuth's - // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - // secondary probe. Here, the modular division first probe is gives way - // to a faster exclusive-or manipulation. Also do block compression with - // an adaptive reset, whereby the code table is cleared when the compression - // ratio decreases, but after the table fills. The variable-length output - // codes are re-sized at this point, and a special CLEAR code is generated - // for the decompressor. Late addition: construct the table according to - // file size for noticeable speed improvement on small files. Please direct - // questions about this implementation to ames!jaw. - - int g_init_bits; - - int ClearCode; - - int EOFCode; - - // output - // - // Output the given code. - // Inputs: - // code: A n_bits-bit integer. If == -1, then EOF. This assumes - // that n_bits =< wordsize - 1. - // Outputs: - // Outputs code to the file. - // Assumptions: - // Chars are 8 bits long. - // Algorithm: - // Maintain a BITS character long buffer (so that 8 codes will - // fit in it exactly). Use the VAX insv instruction to insert each - // code in turn. When the buffer fills up empty it and start over. - - int cur_accum = 0; - - int cur_bits = 0; - - int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, - 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; - - // Number of characters so far in this 'packet' - int a_count; - - // Define the storage for the packet accumulator - byte[] accum = new byte[256]; - - // ---------------------------------------------------------------------------- - LZWEncoder(int width, int height, byte[] pixels, int color_depth) { - imgW = width; - imgH = height; - pixAry = pixels; - initCodeSize = Math.max(2, color_depth); - } - - // Add a character to the end of the current packet, and if it is 254 - // characters, flush the packet to disk. - void char_out(byte c, OutputStream outs) throws IOException { - accum[a_count++] = c; - if (a_count >= 254) - flush_char(outs); - } - - // Clear out the hash table - - // table clear for block compress - void cl_block(OutputStream outs) throws IOException { - cl_hash(hsize); - free_ent = ClearCode + 2; - clear_flg = true; - - output(ClearCode, outs); - } - - // reset code table - void cl_hash(int hsize) { - for (int i = 0; i < hsize; ++i) - htab[i] = -1; - } - - void compress(int init_bits, OutputStream outs) throws IOException { - int fcode; - int i /* = 0 */; - int c; - int ent; - int disp; - int hsize_reg; - int hshift; - - // Set up the globals: g_init_bits - initial number of bits - g_init_bits = init_bits; - - // Set up the necessary values - clear_flg = false; - n_bits = g_init_bits; - maxcode = MAXCODE(n_bits); - - ClearCode = 1 << (init_bits - 1); - EOFCode = ClearCode + 1; - free_ent = ClearCode + 2; - - a_count = 0; // clear packet - - ent = nextPixel(); - - hshift = 0; - for (fcode = hsize; fcode < 65536; fcode *= 2) - ++hshift; - hshift = 8 - hshift; // set hash code range bound - - hsize_reg = hsize; - cl_hash(hsize_reg); // clear hash table - - output(ClearCode, outs); - - outer_loop: - while ((c = nextPixel()) != EOF) { - fcode = (c << maxbits) + ent; - i = (c << hshift) ^ ent; // xor hashing - - if (htab[i] == fcode) { - ent = codetab[i]; - continue; - } else if (htab[i] >= 0) // non-empty slot - { - disp = hsize_reg - i; // secondary hash (after G. Knott) - if (i == 0) - disp = 1; - do { - if ((i -= disp) < 0) - i += hsize_reg; - - if (htab[i] == fcode) { - ent = codetab[i]; - continue outer_loop; - } - } while (htab[i] >= 0); - } - output(ent, outs); - ent = c; - if (free_ent < maxmaxcode) { - codetab[i] = free_ent++; // code -> hashtable - htab[i] = fcode; - } else - cl_block(outs); - } - // Put out the final code. - output(ent, outs); - output(EOFCode, outs); - } - - // ---------------------------------------------------------------------------- - void encode(OutputStream os) throws IOException { - os.write(initCodeSize); // write "initial code size" byte - - remaining = imgW * imgH; // reset navigation variables - curPixel = 0; - - compress(initCodeSize + 1, os); // compress and write the pixel data - - os.write(0); // write block terminator - } - - // Flush the packet to disk, and reset the accumulator - void flush_char(OutputStream outs) throws IOException { - if (a_count > 0) { - outs.write(a_count); - outs.write(accum, 0, a_count); - a_count = 0; - } - } - - final int MAXCODE(int n_bits) { - return (1 << n_bits) - 1; - } - - // ---------------------------------------------------------------------------- - // Return the next pixel from the image - // ---------------------------------------------------------------------------- - private int nextPixel() { - if (remaining == 0) - return EOF; - - --remaining; - - byte pix = pixAry[curPixel++]; - - return pix & 0xff; - } - - void output(int code, OutputStream outs) throws IOException { - cur_accum &= masks[cur_bits]; - - if (cur_bits > 0) - cur_accum |= (code << cur_bits); - else - cur_accum = code; - - cur_bits += n_bits; - - while (cur_bits >= 8) { - char_out((byte) (cur_accum & 0xff), outs); - cur_accum >>= 8; - cur_bits -= 8; - } - - // If the next entry is going to be too big for the code size, - // then increase it, if possible. - if (free_ent > maxcode || clear_flg) { - if (clear_flg) { - maxcode = MAXCODE(n_bits = g_init_bits); - clear_flg = false; - } else { - ++n_bits; - if (n_bits == maxbits) - maxcode = maxmaxcode; - else - maxcode = MAXCODE(n_bits); - } - } - - if (code == EOFCode) { - // At EOF, write the rest of the buffer. - while (cur_bits > 0) { - char_out((byte) (cur_accum & 0xff), outs); - cur_accum >>= 8; - cur_bits -= 8; - } - - flush_char(outs); - } - } -} \ No newline at end of file
diff --git a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/NeuQuant.java b/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/NeuQuant.java deleted file mode 100644 index 3d2c2cd..0000000 --- a/third_party/glide/gif_encoder/java/com/bumptech/glide/gifencoder/NeuQuant.java +++ /dev/null
@@ -1,506 +0,0 @@ -package org.chromium.third_party.glide.gif_encoder; - -/* - * NeuQuant Neural-Net Quantization Algorithm - * ------------------------------------------ - * - * Copyright (c) 1994 Anthony Dekker - * - * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See - * "Kohonen neural networks for optimal colour quantization" in "Network: - * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of - * the algorithm. - * - * Any party obtaining a copy of these files from the author, directly or - * indirectly, is granted, free of charge, a full and unrestricted irrevocable, - * world-wide, paid up, royalty-free, nonexclusive right and license to deal in - * this software and documentation files (the "Software"), including without - * limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons who - * receive copies from any such party to do so, with the only requirement being - * that this copyright notice remain intact. - */ - -// Ported to Java 12/00 K Weiner -class NeuQuant { - - protected static final int netsize = 256; /* number of colours used */ - - /* four primes near 500 - assume no image has a length so large */ - /* that it is divisible by all four primes */ - protected static final int prime1 = 499; - - protected static final int prime2 = 491; - - protected static final int prime3 = 487; - - protected static final int prime4 = 503; - - protected static final int minpicturebytes = (3 * prime4); - - /* minimum size for input image */ - - /* - * Program Skeleton ---------------- [select samplefac in range 1..30] [read - * image from input file] pic = (unsigned char*) malloc(3*width*height); - * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output - * image header, using writecolourmap(f)] inxbuild(); write output image using - * inxsearch(b,g,r) - */ - - /* - * Network Definitions ------------------- - */ - - protected static final int maxnetpos = (netsize - 1); - - protected static final int netbiasshift = 4; /* bias for colour values */ - - protected static final int ncycles = 100; /* no. of learning cycles */ - - /* defs for freq and bias */ - protected static final int intbiasshift = 16; /* bias for fractions */ - - protected static final int intbias = (((int) 1) << intbiasshift); - - protected static final int gammashift = 10; /* gamma = 1024 */ - - protected static final int gamma = (((int) 1) << gammashift); - - protected static final int betashift = 10; - - protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */ - - protected static final int betagamma = (intbias << (gammashift - betashift)); - - /* defs for decreasing radius factor */ - protected static final int initrad = (netsize >> 3); /* - * for 256 cols, radius - * starts - */ - - protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ - - protected static final int radiusbias = (((int) 1) << radiusbiasshift); - - protected static final int initradius = (initrad * radiusbias); /* - * and - * decreases - * by a - */ - - protected static final int radiusdec = 30; /* factor of 1/30 each cycle */ - - /* defs for decreasing alpha factor */ - protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */ - - protected static final int initalpha = (((int) 1) << alphabiasshift); - - protected int alphadec; /* biased by 10 bits */ - - /* radbias and alpharadbias used for radpower calculation */ - protected static final int radbiasshift = 8; - - protected static final int radbias = (((int) 1) << radbiasshift); - - protected static final int alpharadbshift = (alphabiasshift + radbiasshift); - - protected static final int alpharadbias = (((int) 1) << alpharadbshift); - - /* - * Types and Global Variables -------------------------- - */ - - protected byte[] thepicture; /* the input image itself */ - - protected int lengthcount; /* lengthcount = H*W*3 */ - - protected int samplefac; /* sampling factor 1..30 */ - - // typedef int pixel[4]; /* BGRc */ - protected int[][] network; /* the network itself - [netsize][4] */ - - protected int[] netindex = new int[256]; - - /* for network lookup - really 256 */ - - protected int[] bias = new int[netsize]; - - /* bias and freq arrays for learning */ - protected int[] freq = new int[netsize]; - - protected int[] radpower = new int[initrad]; - - /* radpower for precomputation */ - - /* - * Initialise network in range (0,0,0) to (255,255,255) and set parameters - * ----------------------------------------------------------------------- - */ - public NeuQuant(byte[] thepic, int len, int sample) { - - int i; - int[] p; - - thepicture = thepic; - lengthcount = len; - samplefac = sample; - - network = new int[netsize][]; - for (i = 0; i < netsize; i++) { - network[i] = new int[4]; - p = network[i]; - p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize; - freq[i] = intbias / netsize; /* 1/netsize */ - bias[i] = 0; - } - } - - public byte[] colorMap() { - byte[] map = new byte[3 * netsize]; - int[] index = new int[netsize]; - for (int i = 0; i < netsize; i++) - index[network[i][3]] = i; - int k = 0; - for (int i = 0; i < netsize; i++) { - int j = index[i]; - map[k++] = (byte) (network[j][0]); - map[k++] = (byte) (network[j][1]); - map[k++] = (byte) (network[j][2]); - } - return map; - } - - /* - * Insertion sort of network and building of netindex[0..255] (to do after - * unbias) - * ------------------------------------------------------------------------------- - */ - public void inxbuild() { - - int i, j, smallpos, smallval; - int[] p; - int[] q; - int previouscol, startpos; - - previouscol = 0; - startpos = 0; - for (i = 0; i < netsize; i++) { - p = network[i]; - smallpos = i; - smallval = p[1]; /* index on g */ - /* find smallest in i..netsize-1 */ - for (j = i + 1; j < netsize; j++) { - q = network[j]; - if (q[1] < smallval) { /* index on g */ - smallpos = j; - smallval = q[1]; /* index on g */ - } - } - q = network[smallpos]; - /* swap p (i) and q (smallpos) entries */ - if (i != smallpos) { - j = q[0]; - q[0] = p[0]; - p[0] = j; - j = q[1]; - q[1] = p[1]; - p[1] = j; - j = q[2]; - q[2] = p[2]; - p[2] = j; - j = q[3]; - q[3] = p[3]; - p[3] = j; - } - /* smallval entry is now in position i */ - if (smallval != previouscol) { - netindex[previouscol] = (startpos + i) >> 1; - for (j = previouscol + 1; j < smallval; j++) - netindex[j] = i; - previouscol = smallval; - startpos = i; - } - } - netindex[previouscol] = (startpos + maxnetpos) >> 1; - for (j = previouscol + 1; j < 256; j++) - netindex[j] = maxnetpos; /* really 256 */ - } - - /* - * Main Learning Loop ------------------ - */ - public void learn() { - - int i, j, b, g, r; - int radius, rad, alpha, step, delta, samplepixels; - byte[] p; - int pix, lim; - - if (lengthcount < minpicturebytes) - samplefac = 1; - alphadec = 30 + ((samplefac - 1) / 3); - p = thepicture; - pix = 0; - lim = lengthcount; - samplepixels = lengthcount / (3 * samplefac); - delta = samplepixels / ncycles; - alpha = initalpha; - radius = initradius; - - rad = radius >> radiusbiasshift; - if (rad <= 1) - rad = 0; - for (i = 0; i < rad; i++) - radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); - - // fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad); - - if (lengthcount < minpicturebytes) - step = 3; - else if ((lengthcount % prime1) != 0) - step = 3 * prime1; - else { - if ((lengthcount % prime2) != 0) - step = 3 * prime2; - else { - if ((lengthcount % prime3) != 0) - step = 3 * prime3; - else - step = 3 * prime4; - } - } - - i = 0; - while (i < samplepixels) { - b = (p[pix + 0] & 0xff) << netbiasshift; - g = (p[pix + 1] & 0xff) << netbiasshift; - r = (p[pix + 2] & 0xff) << netbiasshift; - j = contest(b, g, r); - - altersingle(alpha, j, b, g, r); - if (rad != 0) - alterneigh(rad, j, b, g, r); /* alter neighbours */ - - pix += step; - if (pix >= lim) - pix -= lengthcount; - - i++; - if (delta == 0) - delta = 1; - if (i % delta == 0) { - alpha -= alpha / alphadec; - radius -= radius / radiusdec; - rad = radius >> radiusbiasshift; - if (rad <= 1) - rad = 0; - for (j = 0; j < rad; j++) - radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); - } - } - // fprintf(stderr,"finished 1D learning: final alpha=%f - // !\n",((float)alpha)/initalpha); - } - - /* - * Search for BGR values 0..255 (after net is unbiased) and return colour - * index - * ---------------------------------------------------------------------------- - */ - public int map(int b, int g, int r) { - - int i, j, dist, a, bestd; - int[] p; - int best; - - bestd = 1000; /* biggest possible dist is 256*3 */ - best = -1; - i = netindex[g]; /* index on g */ - j = i - 1; /* start at netindex[g] and work outwards */ - - while ((i < netsize) || (j >= 0)) { - if (i < netsize) { - p = network[i]; - dist = p[1] - g; /* inx key */ - if (dist >= bestd) - i = netsize; /* stop iter */ - else { - i++; - if (dist < 0) - dist = -dist; - a = p[0] - b; - if (a < 0) - a = -a; - dist += a; - if (dist < bestd) { - a = p[2] - r; - if (a < 0) - a = -a; - dist += a; - if (dist < bestd) { - bestd = dist; - best = p[3]; - } - } - } - } - if (j >= 0) { - p = network[j]; - dist = g - p[1]; /* inx key - reverse dif */ - if (dist >= bestd) - j = -1; /* stop iter */ - else { - j--; - if (dist < 0) - dist = -dist; - a = p[0] - b; - if (a < 0) - a = -a; - dist += a; - if (dist < bestd) { - a = p[2] - r; - if (a < 0) - a = -a; - dist += a; - if (dist < bestd) { - bestd = dist; - best = p[3]; - } - } - } - } - } - return (best); - } - - public byte[] process() { - learn(); - unbiasnet(); - inxbuild(); - return colorMap(); - } - - /* - * Unbias network to give byte values 0..255 and record position i to prepare - * for sort - * ----------------------------------------------------------------------------------- - */ - public void unbiasnet() { - - int i, j; - - for (i = 0; i < netsize; i++) { - network[i][0] >>= netbiasshift; - network[i][1] >>= netbiasshift; - network[i][2] >>= netbiasshift; - network[i][3] = i; /* record colour no */ - } - } - - /* - * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in - * radpower[|i-j|] - * --------------------------------------------------------------------------------- - */ - protected void alterneigh(int rad, int i, int b, int g, int r) { - - int j, k, lo, hi, a, m; - int[] p; - - lo = i - rad; - if (lo < -1) - lo = -1; - hi = i + rad; - if (hi > netsize) - hi = netsize; - - j = i + 1; - k = i - 1; - m = 1; - while ((j < hi) || (k > lo)) { - a = radpower[m++]; - if (j < hi) { - p = network[j++]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } // prevents 1.3 miscompilation - } - if (k > lo) { - p = network[k--]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } - } - } - } - - /* - * Move neuron i towards biased (b,g,r) by factor alpha - * ---------------------------------------------------- - */ - protected void altersingle(int alpha, int i, int b, int g, int r) { - - /* alter hit neuron */ - int[] n = network[i]; - n[0] -= (alpha * (n[0] - b)) / initalpha; - n[1] -= (alpha * (n[1] - g)) / initalpha; - n[2] -= (alpha * (n[2] - r)) / initalpha; - } - - /* - * Search for biased BGR values ---------------------------- - */ - protected int contest(int b, int g, int r) { - - /* finds closest neuron (min dist) and updates freq */ - /* finds best neuron (min dist-bias) and returns position */ - /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ - /* bias[i] = gamma*((1/netsize)-freq[i]) */ - - int i, dist, a, biasdist, betafreq; - int bestpos, bestbiaspos, bestd, bestbiasd; - int[] n; - - bestd = ~(((int) 1) << 31); - bestbiasd = bestd; - bestpos = -1; - bestbiaspos = bestpos; - - for (i = 0; i < netsize; i++) { - n = network[i]; - dist = n[0] - b; - if (dist < 0) - dist = -dist; - a = n[1] - g; - if (a < 0) - a = -a; - dist += a; - a = n[2] - r; - if (a < 0) - a = -a; - dist += a; - if (dist < bestd) { - bestd = dist; - bestpos = i; - } - biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); - if (biasdist < bestbiasd) { - bestbiasd = biasdist; - bestbiaspos = i; - } - betafreq = (freq[i] >> betashift); - freq[i] -= betafreq; - bias[i] += (betafreq << gammashift); - } - freq[bestpos] += beta; - bias[bestpos] -= betagamma; - return (bestbiaspos); - } -} \ No newline at end of file
diff --git a/tools/binary_size/libsupersize/dir_metadata.py b/tools/binary_size/libsupersize/dir_metadata.py index b74634a0..717b1e2 100644 --- a/tools/binary_size/libsupersize/dir_metadata.py +++ b/tools/binary_size/libsupersize/dir_metadata.py
@@ -3,6 +3,7 @@ # found in the LICENSE file. """Helpers for determining Component of directories.""" +import functools import os import re @@ -29,6 +30,7 @@ return '' +@functools.lru_cache class _ComponentLookupContext: def __init__(self, source_directory): self._mixins_cache = {} @@ -104,6 +106,11 @@ """ context = _ComponentLookupContext(source_directory) for symbol in raw_symbols: + found_component = '' if symbol.source_path: found_component = context.ComponentForSourcePath(symbol.source_path) - symbol.component = found_component or default_component + if not found_component and symbol.object_path: + # Some generated files and not put under their target_gen_dir (common + # grit _resources_maps.cc files). So also look at object path. + found_component = context.ComponentForSourcePath(symbol.object_path) + symbol.component = found_component or default_component
diff --git a/tools/binary_size/libsupersize/testdata/Archive.golden b/tools/binary_size/libsupersize/testdata/Archive.golden index 06fd0ab4..857ca80 100644 --- a/tools/binary_size/libsupersize/testdata/Archive.golden +++ b/tools/binary_size/libsupersize/testdata/Archive.golden
@@ -4,7 +4,7 @@ Section .text: has 100.0% of 35982248 bytes accounted for from 17 symbols. 0 bytes are unaccounted for. * Padding accounts for 13808 bytes (0.0%) * 0 have source paths. Accounts for 0 bytes (0.0%). -* 0 have a component assigned. Accounts for 0 bytes (0.0%). +* 13 have a component assigned. Accounts for 73978 bytes (0.2%). * 5 placeholders exist (symbols that start with **). Accounts for 35912296 bytes (99.8%). * 0 symbols have shared ownership. * 1 symbols are marked as "hot". Accounts for 12 bytes (0.0%). @@ -14,29 +14,29 @@ Section .rodata: has 100.0% of 5927652 bytes accounted for from 10 symbols. 0 bytes are unaccounted for. * Padding accounts for 2637935 bytes (44.5%) * 0 have source paths. Accounts for 0 bytes (0.0%). -* 0 have a component assigned. Accounts for 0 bytes (0.0%). +* 5 have a component assigned. Accounts for 676128 bytes (11.4%). * 5 placeholders exist (symbols that start with **). Accounts for 5251524 bytes (88.6%). * 0 string literals exist. Accounts for 0 bytes (0.0%) padding is 0 bytes. * 0 symbols have shared ownership. Large padding of 675992 between: - A) .rodata@284e398(size_without_padding=32,padding=0,full_name=chrome::mojom::FilePatcher::Name_,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) - B) .rodata@28f3450(size_without_padding=48,padding=675992,full_name=kAnimationFrameTimeHistogramClassPath,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=) + A) .rodata@284e398(size_without_padding=32,padding=0,full_name=chrome::mojom::FilePatcher::Name_,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) + B) .rodata@28f3450(size_without_padding=48,padding=675992,full_name=kAnimationFrameTimeHistogramClassPath,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) Section .data.rel.ro: has 100.0% of 1065224 bytes accounted for from 4 symbols. 0 bytes are unaccounted for. * Padding accounts for 0 bytes (0.0%) * 0 have source paths. Accounts for 0 bytes (0.0%). -* 0 have a component assigned. Accounts for 0 bytes (0.0%). +* 3 have a component assigned. Accounts for 92 bytes (0.0%). * 1 placeholders exist (symbols that start with **). Accounts for 1065132 bytes (100.0%). * 0 symbols have shared ownership. Section .data: has 100.0% of 101768 bytes accounted for from 6 symbols. 0 bytes are unaccounted for. * Padding accounts for 0 bytes (0.0%) * 0 have source paths. Accounts for 0 bytes (0.0%). -* 0 have a component assigned. Accounts for 0 bytes (0.0%). +* 5 have a component assigned. Accounts for 168 bytes (0.2%). * 1 placeholders exist (symbols that start with **). Accounts for 101600 bytes (99.8%). * 0 symbols have shared ownership. Section .bss: has 40.3% of 524520 bytes accounted for from 6 symbols. 775936 bytes are unaccounted for. * Padding accounts for 196 bytes (0.0%) * 0 have source paths. Accounts for 0 bytes (0.0%). -* 0 have a component assigned. Accounts for 0 bytes (0.0%). +* 6 have a component assigned. Accounts for 524520 bytes (100.0%). * 0 symbols have shared ownership. Section .dex: 0 bytes from 0 symbols. * Padding accounts for 0 bytes (0.0%) @@ -64,54 +64,54 @@ * 0 have a component assigned. Accounts for 0 bytes (0.0%). * 22 placeholders exist (symbols that start with **). Accounts for 56448494 bytes (100.0%). * 0 symbols have shared ownership. -.text@28d900(size_without_padding=16,padding=0,full_name=_GLOBAL__sub_I_page_allocator.cc,object_path=base/base/page_allocator.o,source_path=,flags={startup},num_aliases=1,component=) +.text@28d900(size_without_padding=16,padding=0,full_name=_GLOBAL__sub_I_page_allocator.cc,object_path=base/base/page_allocator.o,source_path=,flags={startup},num_aliases=1,component=Blink>Internal) .text@28d910(size_without_padding=56,padding=0,full_name=_GLOBAL__sub_I_bbr_sender.cc,object_path=$SYSTEM/path.a/foo.o,source_path=,flags={startup},num_aliases=1,component=) -.text@28d948(size_without_padding=28,padding=0,full_name=_GLOBAL__sub_I_pacing_sender.cc,object_path=base/base/page_allocator.o,source_path=,flags={startup},num_aliases=1,component=) -.text@28d964(size_without_padding=38,padding=0,full_name=extFromUUseMapping(signed char, unsigned int, int),object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) -.text@28d98a(size_without_padding=32,padding=0,full_name=extFromUUseMapping(aj, int),object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) +.text@28d948(size_without_padding=28,padding=0,full_name=_GLOBAL__sub_I_pacing_sender.cc,object_path=base/base/page_allocator.o,source_path=,flags={startup},num_aliases=1,component=Blink>Internal) +.text@28d964(size_without_padding=38,padding=0,full_name=extFromUUseMapping(signed char, unsigned int, int),object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) +.text@28d98a(size_without_padding=32,padding=0,full_name=extFromUUseMapping(aj, int),object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) .text@28f000(size_without_padding=0,padding=5718,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=) -.text@28f000(size_without_padding=448,padding=0,full_name=ucnv_extMatchFromU(int const*, int, unsigned short const*, int, unsigned short const*, int, unsigned int*, signed char, signed char),object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=) -.text@28f1c8(size_without_padding=20,padding=8,full_name=_GLOBAL__sub_I_SkDeviceProfile.cpp,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={startup},num_aliases=1,component=) -.text@28f1e0(size_without_padding=69120,padding=4,full_name=foo_bar,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={unlikely},num_aliases=1,component=) -.text@2a0000(size_without_padding=16,padding=32,full_name=blink::ContiguousContainerBase::shrinkToFit(),object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={},num_aliases=1,component=) -.text@2a0010(size_without_padding=12,padding=0,full_name=blink::ContiguousContainerBase::shrinkToFit(),object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={clone,hot},num_aliases=1,component=) -.text@2a0020(size_without_padding=24,padding=4,full_name=blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&),object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) +.text@28f000(size_without_padding=448,padding=0,full_name=ucnv_extMatchFromU(int const*, int, unsigned short const*, int, unsigned short const*, int, unsigned int*, signed char, signed char),object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.text@28f1c8(size_without_padding=20,padding=8,full_name=_GLOBAL__sub_I_SkDeviceProfile.cpp,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={startup},num_aliases=1,component=Internal>Android) +.text@28f1e0(size_without_padding=69120,padding=4,full_name=foo_bar,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={unlikely},num_aliases=1,component=Internal>Android) +.text@2a0000(size_without_padding=16,padding=32,full_name=blink::ContiguousContainerBase::shrinkToFit(),object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.text@2a0010(size_without_padding=12,padding=0,full_name=blink::ContiguousContainerBase::shrinkToFit(),object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={clone,hot},num_aliases=1,component=Internal>Android) +.text@2a0020(size_without_padding=24,padding=4,full_name=blink::ContiguousContainerBase::ContiguousContainerBase(blink::ContiguousContainerBase&&),object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) .text@2a1000(size_without_padding=0,padding=4040,full_name=** symbol gap 1,object_path=,source_path=,flags={},num_aliases=1,component=) -.text@2a1000(size_without_padding=94,padding=0,full_name=blink::PaintChunker::releasePaintChunks(),object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon,clone},num_aliases=1,component=) -.text@2a2000(size_without_padding=32,padding=4002,full_name=** outlined function,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) -.text@2a2020(size_without_padding=48,padding=0,full_name=** outlined function,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) +.text@2a1000(size_without_padding=94,padding=0,full_name=blink::PaintChunker::releasePaintChunks(),object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon,clone},num_aliases=1,component=Internal>Android) +.text@2a2000(size_without_padding=32,padding=4002,full_name=** outlined function,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.text@2a2020(size_without_padding=48,padding=0,full_name=** outlined function,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) .text@2a2050(size_without_padding=35898456,padding=0,full_name=** .text (unattributed),object_path=,source_path=,flags={},num_aliases=1,component=) .rodata@266e600(size_without_padding=32,padding=0,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=) .rodata@266e630(size_without_padding=16,padding=16,full_name=** merge strings,object_path=,source_path=,flags={},num_aliases=1,component=) .rodata@284d600(size_without_padding=3425,padding=1961920,full_name=** merge constants,object_path=,source_path=,flags={},num_aliases=1,component=) .rodata@284e364(size_without_padding=0,padding=3,full_name=** symbol gap 0,object_path=,source_path=,flags={},num_aliases=1,component=) -.rodata@284e364(size_without_padding=8,padding=0,full_name=,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) -.rodata@284e370(size_without_padding=40,padding=4,full_name=Name,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) -.rodata@284e398(size_without_padding=32,padding=0,full_name=chrome::mojom::FilePatcher::Name_,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) -.rodata@28f3450(size_without_padding=48,padding=675992,full_name=kAnimationFrameTimeHistogramClassPath,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=) -.rodata@28f3480(size_without_padding=4,padding=0,full_name=blink::CSSValueKeywordsHash::findValueImpl(char const*, unsigned int)::value_word_list,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=) +.rodata@284e364(size_without_padding=8,padding=0,full_name=,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) +.rodata@284e370(size_without_padding=40,padding=4,full_name=Name,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) +.rodata@284e398(size_without_padding=32,padding=0,full_name=chrome::mojom::FilePatcher::Name_,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.rodata@28f3450(size_without_padding=48,padding=675992,full_name=kAnimationFrameTimeHistogramClassPath,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) +.rodata@28f3480(size_without_padding=4,padding=0,full_name=blink::CSSValueKeywordsHash::findValueImpl(char const*, unsigned int)::value_word_list,object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) .rodata@28f3484(size_without_padding=3286112,padding=0,full_name=** .rodata (unattributed),object_path=,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) -.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) +.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) +.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/WebKit.a/./../third_party/sub/PaintChunker.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) +.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) .data.rel.ro@2cd855c(size_without_padding=1065132,padding=0,full_name=** .data.rel.ro (unattributed),object_path=,source_path=,flags={},num_aliases=1,component=) -.data@2de7000(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelCmpxchg,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=) -.data@2de7004(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelMemoryBarrier,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=) -.data@2de7008(size_without_padding=152,padding=0,full_name=base::android::kBaseRegisteredMethods,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={rel},num_aliases=1,component=) -.data@2de70a0(size_without_padding=4,padding=0,full_name=base::android::g_renderer_histogram_code,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon},num_aliases=1,component=) -.data@2de70a4(size_without_padding=4,padding=0,full_name=base::android::g_library_version_number,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon,rel.loc},num_aliases=1,component=) +.data@2de7000(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelCmpxchg,object_path=base/base/page_allocator.o,source_path=,flags={},num_aliases=1,component=Blink>Internal) +.data@2de7004(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelMemoryBarrier,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data@2de7008(size_without_padding=152,padding=0,full_name=base::android::kBaseRegisteredMethods,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={rel},num_aliases=1,component=Internal>Android) +.data@2de70a0(size_without_padding=4,padding=0,full_name=base::android::g_renderer_histogram_code,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) +.data@2de70a4(size_without_padding=4,padding=0,full_name=base::android::g_library_version_number,object_path=third_party/WebKit.a/sub/ContiguousContainer.o,source_path=,flags={anon,rel.loc},num_aliases=1,component=Internal>Android) .data@2de70a8(size_without_padding=101600,padding=0,full_name=** .data (unattributed),object_path=,source_path=,flags={},num_aliases=1,component=) -.bss@0(size_without_padding=262144,padding=0,full_name=ff_cos_131072,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o,source_path=,flags={},num_aliases=1,component=) -.bss@0(size_without_padding=131072,padding=0,full_name=ff_cos_131072_fixed,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_fixed.o,source_path=,flags={},num_aliases=1,component=) -.bss@0(size_without_padding=131072,padding=0,full_name=ff_cos_65536,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o,source_path=,flags={},num_aliases=1,component=) -.bss@2dffda0(size_without_padding=28,padding=0,full_name=g_chrome_content_browser_client,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=) -.bss@2dffe80(size_without_padding=4,padding=196,full_name=SaveHistogram(_JNIEnv*, base::android::JavaParamRef<_jobject*> const&, base::android::JavaParamRef<_jstring*> const&, base::android::JavaParamRef<_jlongArray*> const&, int)::atomic_histogram_pointer,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=) -.bss@2dffe84(size_without_padding=4,padding=0,full_name=g_AnimationFrameTimeHistogram_clazz,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={anon},num_aliases=1,component=) +.bss@0(size_without_padding=262144,padding=0,full_name=ff_cos_131072,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.bss@0(size_without_padding=131072,padding=0,full_name=ff_cos_131072_fixed,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_fixed.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.bss@0(size_without_padding=131072,padding=0,full_name=ff_cos_65536,object_path=third_party/ffmpeg/libffmpeg_internal.a/fft_float.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.bss@2dffda0(size_without_padding=28,padding=0,full_name=g_chrome_content_browser_client,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.bss@2dffe80(size_without_padding=4,padding=196,full_name=SaveHistogram(_JNIEnv*, base::android::JavaParamRef<_jobject*> const&, base::android::JavaParamRef<_jstring*> const&, base::android::JavaParamRef<_jlongArray*> const&, int)::atomic_histogram_pointer,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.bss@2dffe84(size_without_padding=4,padding=0,full_name=g_AnimationFrameTimeHistogram_clazz,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) .other@0(size_without_padding=60,padding=0,full_name=** ELF Section: .ARM.attributes,object_path=,source_path=,flags={},num_aliases=1,component=) .other@0(size_without_padding=1536456,padding=0,full_name=** ELF Section: .ARM.exidx,object_path=,source_path=,flags={},num_aliases=1,component=) .other@0(size_without_padding=183632,padding=0,full_name=** ELF Section: .ARM.extab,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/ArchiveContainers.golden b/tools/binary_size/libsupersize/testdata/ArchiveContainers.golden index 85b2dc6..37c5736 100644 --- a/tools/binary_size/libsupersize/testdata/ArchiveContainers.golden +++ b/tools/binary_size/libsupersize/testdata/ArchiveContainers.golden
@@ -71,8 +71,8 @@ <Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -<Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -<Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +<Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +<Container1:/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) <Container1:/test.so (armeabi-v7a)>.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) <Container1:/test.so (armeabi-v7a)>.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) <Container1:/test.so (armeabi-v7a)>.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) @@ -150,8 +150,8 @@ <Container2:>.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Container2:>.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Container2:>.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -<Container2:>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -<Container2:>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +<Container2:>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +<Container2:>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) <Container2:>.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) <Container2:>.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) <Container2:>.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden index 38ed6df..3b47a86 100644 --- a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden +++ b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
@@ -235,8 +235,8 @@ <test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -<test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -<test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +<test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +<test.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) <test.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) <test.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) <test.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden index afa097c..7a1d306 100644 --- a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden +++ b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
@@ -113,8 +113,8 @@ .data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) .data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden index 4c359a91..76e1762 100644 --- a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden +++ b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
@@ -372,7 +372,7 @@ Section .other: has 100.0% of 695 bytes accounted for from 2 symbols. 0 bytes are unaccounted for. * Padding accounts for 136 bytes (19.6%) * 1 have source paths. Accounts for 559 bytes (80.4%). -* 1 have a component assigned. Accounts for 559 bytes (80.4%). +* 2 have a component assigned. Accounts for 695 bytes (100.0%). * 0 symbols have shared ownership. <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.text@28d900(size_without_padding=16,padding=0,full_name=_GLOBAL__sub_I_page_allocator.cc,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={startup},num_aliases=1,component=Blink>Internal) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.text@28d910(size_without_padding=56,padding=0,full_name=_GLOBAL__sub_I_bbr_sender.cc,object_path=$SYSTEM/path.a/foo.o,source_path=,flags={startup},num_aliases=2,component=) @@ -412,8 +412,8 @@ <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -<Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -<Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +<Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +<Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) <Bundle.minimal.apks/base.apk/test.so (armeabi-v7a)>.data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) @@ -711,4 +711,4 @@ <Bundle.minimal.apks/not_on_demand.apk>.other@0(size_without_padding=560,padding=0,full_name=AndroidManifest.xml,object_path=,source_path=$APK/AndroidManifest.xml,flags={},num_aliases=1,component=) <Bundle.minimal.apks/not_on_demand.apk>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=) <Bundle.minimal.apks/on_demand.apk?>.other@0(size_without_padding=559,padding=0,full_name=AndroidManifest.xml,object_path=,source_path=$APK/AndroidManifest.xml,flags={},num_aliases=1,component=DEFAULT) -<Bundle.minimal.apks/on_demand.apk?>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=) +<Bundle.minimal.apks/on_demand.apk?>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=DEFAULT)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_OutputDirectory.golden b/tools/binary_size/libsupersize/testdata/Archive_OutputDirectory.golden index cfdbf666..edb21f5 100644 --- a/tools/binary_size/libsupersize/testdata/Archive_OutputDirectory.golden +++ b/tools/binary_size/libsupersize/testdata/Archive_OutputDirectory.golden
@@ -98,8 +98,8 @@ .data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) .data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden b/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden index 99ac06d..3ee3cfc 100644 --- a/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden +++ b/tools/binary_size/libsupersize/testdata/Archive_Pak_Files.golden
@@ -115,8 +115,8 @@ .data.rel.ro.local@2c176f0(size_without_padding=56,padding=0,full_name=ChromeMainDelegate [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17728(size_without_padding=24,padding=0,full_name=chrome::mojom::FieldTrialRecorder [vtable],object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1,component=Internal>Android) .data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/sub/ContiguousContainer.o,source_path=third_party/container/container.c,flags={},num_aliases=1,component=UI>Browser) -.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=) -.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=) +.data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1,component=Internal>Android) +.data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8500(size_without_padding=56,padding=0,full_name=ChromeMainDelegateAndroid [vtable],object_path=third_party/sub/PaintChunker.o,source_path=third_party/paint.cc,flags={},num_aliases=1,component=Internal>Android) .data.rel.ro@2cd8538(size_without_padding=24,padding=0,full_name=mojo::MessageReceiver [vtable],object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal) .data.rel.ro@2cd8550(size_without_padding=12,padding=0,full_name=kMethodsAnimationFrameTimeHistogram,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1,component=Blink>Internal)
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version index 1317a249..440c24e 100644 --- a/tools/cast3p/cast_core.version +++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@ -cast_20230205_2231_RC00 +cast_20230217_0600_RC00
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version index 626e403..c00952a 100644 --- a/tools/cast3p/runtime.version +++ b/tools/cast3p/runtime.version
@@ -1 +1 @@ -344536 +344759
diff --git a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.gni b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.gni index 9262f9a..1a2f1cc 100644 --- a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.gni +++ b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.gni
@@ -18,7 +18,11 @@ args = [ "--manifest-files" ] + rebase_path(invoker.manifest_files, root_out_dir) + [ "--sources" ] + rebase_path(invoker.sources, root_out_dir) + [ "--outputs" ] + - rebase_path(invoker.outputs, root_out_dir) + rebase_path(invoker.outputs, root_out_dir) + + [ + "--out-dir", + rebase_path(invoker.out_dir, root_build_dir), + ] inputs = [ "//tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.js" ] foreach(manifest, invoker.manifest_files) { outputs += [ get_path_info(manifest, "dir") + "/" +
diff --git a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.js b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.js index 8af05e81..76b69ee1 100644 --- a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.js +++ b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.js
@@ -134,6 +134,8 @@ const parser = new ArgumentParser({description: 'Merge multiple inlined sourcemaps'}); + parser.add_argument( + '--out-dir', {help: 'Dir where all output files reside', required: true}); parser.add_argument('--sources', {help: 'Input files', nargs: '*'}); parser.add_argument('--outputs', {help: 'Output files', nargs: '*'}); parser.add_argument( @@ -143,15 +145,12 @@ await processFiles(argv.sources, argv.outputs); if (argv.manifest_files) { - // TODO(crbug/1337530): Currently we just remove the final directory of the - // `base_dir` key. This is definitely brittle and also subject to changes - // made to the output directory. Consider updating this to be more robust. for (const manifestFile of argv.manifest_files) { try { const manifestFileContents = fs.readFileSync(manifestFile).toString('utf-8'); const manifest = JSON.parse(manifestFileContents); - manifest.base_dir = path.parse(manifest.base_dir).dir; + manifest.base_dir = argv.out_dir; const parsedPath = path.parse(manifestFile); fs.writeFileSync( path.join( @@ -165,4 +164,4 @@ } } -(async () => main())(); \ No newline at end of file +(async () => main())();
diff --git a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.py b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.py index a5725dd..bc97bad 100755 --- a/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.py +++ b/tools/code_coverage/js_source_maps/merge_js_source_maps/merge_js_source_maps.py
@@ -18,14 +18,22 @@ def main(argv): parser = argparse.ArgumentParser() + parser.add_argument('--out-dir', required=True) parser.add_argument('--sources', required=True, nargs="*") parser.add_argument('--outputs', required=True, nargs="*") parser.add_argument('--manifest-files', required=True, nargs="*") args = parser.parse_args(argv) node.RunNode([ - str(_SOURCE_MAP_MERGER), '--manifest-files', *args.manifest_files, - '--sources', *args.sources, '--outputs', *args.outputs + str(_SOURCE_MAP_MERGER), + '--manifest-files', + *args.manifest_files, + '--sources', + *args.sources, + '--outputs', + *args.outputs, + '--out-dir', + args.out_dir, ])
diff --git a/tools/code_coverage/js_source_maps/merge_js_source_maps/test/merge_js_source_maps_test.py b/tools/code_coverage/js_source_maps/merge_js_source_maps/test/merge_js_source_maps_test.py index f97cc9a..8965ea1 100755 --- a/tools/code_coverage/js_source_maps/merge_js_source_maps/test/merge_js_source_maps_test.py +++ b/tools/code_coverage/js_source_maps/merge_js_source_maps/test/merge_js_source_maps_test.py
@@ -74,6 +74,8 @@ str(input_file_name), '--outputs', str(output_file_name), + '--out-dir', + 'tsc', ]) source_map = None @@ -173,6 +175,8 @@ str(output_file_name), '--manifest-files', str(manifest_file), + '--out-dir', + 'tsc', ]) manifest_file_contents = '{"base_dir":"tsc"}'
diff --git a/tools/crates/gnrt/BUILD.gn b/tools/crates/gnrt/BUILD.gn index 0fc3f6a..ea6c441 100644 --- a/tools/crates/gnrt/BUILD.gn +++ b/tools/crates/gnrt/BUILD.gn
@@ -17,6 +17,7 @@ crate_name = "gnrt_lib" crate_root = "lib.rs" sources = [ + "config.rs", "crates.rs", "deps.rs", "download.rs",
diff --git a/tools/crates/gnrt/config.rs b/tools/crates/gnrt/config.rs new file mode 100644 index 0000000..ea18692b --- /dev/null +++ b/tools/crates/gnrt/config.rs
@@ -0,0 +1,28 @@ +// 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. + +//! Parsing configuration file that customizes gnrt BUILD.gn output. Currently +//! only used for std bindings. + +use std::collections::BTreeMap; + +use serde::Deserialize; + +/// Extra GN configuration for targets. Contains one entry for each crate with +/// custom config. +#[derive(Clone, Debug, Deserialize)] +pub struct ConfigFile { + #[serde(flatten)] + pub per_lib_config: BTreeMap<String, PerLibConfig>, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct PerLibConfig { + /// List of `cfg(...)` options for building this crate. + #[serde(default)] + pub cfg: Vec<String>, + /// List of compile-time environment variables for this crate. + #[serde(default)] + pub env: Vec<String>, +}
diff --git a/tools/crates/gnrt/lib.rs b/tools/crates/gnrt/lib.rs index 53368c4..445d305 100644 --- a/tools/crates/gnrt/lib.rs +++ b/tools/crates/gnrt/lib.rs
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +pub mod config; pub mod crates; pub mod deps; pub mod download;
diff --git a/tools/crates/gnrt/main.rs b/tools/crates/gnrt/main.rs index 032a293..e88189c4 100644 --- a/tools/crates/gnrt/main.rs +++ b/tools/crates/gnrt/main.rs
@@ -297,11 +297,26 @@ } fn generate_for_std(_args: &clap::ArgMatches, paths: &paths::ChromiumPaths) -> ExitCode { + // Load config file, which applies rustenv and cfg flags to some std crates. + let config_file_contents = std::fs::read_to_string(paths.std_config_file).unwrap(); + let config: config::ConfigFile = toml::de::from_str(&config_file_contents).unwrap(); + // Run `cargo metadata` from the std package in the Rust source tree (which // is a workspace). let mut command = cargo_metadata::MetadataCommand::new(); command.current_dir(paths.std_fake_root); + // Delete the Cargo.lock if it exists. + let mut std_fake_root_cargo_lock = paths.std_fake_root.to_path_buf(); + std_fake_root_cargo_lock.push("Cargo.lock"); + if let Err(e) = std::fs::remove_file(std_fake_root_cargo_lock) { + match e.kind() { + // Ignore if it already doesn't exist. + std::io::ErrorKind::NotFound => (), + _ => panic!("io error while deleting Cargo.lock: {e}"), + } + } + // Use offline to constrain dependency resolution to those in the Rust src // tree and vendored crates. Ideally, we'd use "--locked" and use the // upstream Cargo.lock, but this is not straightforward since the rust-src @@ -374,22 +389,36 @@ } } + // Load extra cfg flags and environment variables needed while building std + // crates. + // // TODO(crbug.com/1368806): - // * Find a more sustainable way to supply these extra args, preferably in a - // config file somewhere. // * Supply `-Zforce-unstable-if-unmarked` to all std crates, which ensures deps // of std aren't visible to consumers. let mut extra_gn = HashMap::new(); - // std requires: - // * cfg(backtrace_in_libstd) because it directly includes .rs files from the - // backtrace code rather than including it as a dependency. backtrace's - // implementation has special-purpose code to handle this. - // * STD_ENV_ARCH is referenced in architecture-dependent code. - extra_gn.insert("std".to_string(), "rustflags = [\"--cfg=backtrace_in_libstd\"]\nrustenv = [\"STD_ENV_ARCH=$rust_target_arch\"]".to_string()); - // libc requires: - // * cfg(libc_align) for new enough rustc, which is normally provided by - // build.rs but we don't run build scripts for std crates. - extra_gn.insert("libc".to_string(), "rustflags = [\"--cfg=libc_align\"]".to_string()); + for (lib, config) in config.per_lib_config { + let mut rustflags = String::new(); + let mut rustenv = String::new(); + if !config.cfg.is_empty() { + rustflags = "rustflags = [".to_string(); + rustflags.extend(config.cfg.into_iter().map(|cfg| format!("\"--cfg={cfg}\","))); + rustflags.push(']'); + } + if !config.env.is_empty() { + rustenv = "rustenv = [".to_string(); + rustenv.extend(config.env.into_iter().map(|env| format!("\"{env}\","))); + rustenv.push(']'); + } + + let strs = [rustflags.as_str(), rustenv.as_str()]; + let extra = strs.join("\n"); + + assert!( + !extra.is_empty(), + "if a config entry was present, we should've generated something..." + ); + extra_gn.insert(lib, extra); + } let build_file = gn::build_file_from_std_deps(dependencies.iter(), paths, &extra_gn); write_build_file(&paths.std_build.join("BUILD.gn"), &build_file).unwrap();
diff --git a/tools/crates/gnrt/paths.rs b/tools/crates/gnrt/paths.rs index 08525d3b..d7b24ac 100644 --- a/tools/crates/gnrt/paths.rs +++ b/tools/crates/gnrt/paths.rs
@@ -20,6 +20,7 @@ pub rust_src: &'static Path, pub rust_src_vendor: &'static Path, pub rust_std: &'static Path, + pub std_config_file: &'static Path, pub std_build: &'static Path, pub std_fake_root: &'static Path, } @@ -37,6 +38,7 @@ rust_src: check_path(&cur_dir, RUST_SRC_DIR)?, rust_src_vendor: check_path(&cur_dir, RUST_SRC_VENDOR_DIR)?, rust_std: check_path(&cur_dir, RUST_STD_DIR)?, + std_config_file: check_path(&cur_dir, STD_CONFIG_FILE)?, std_build: check_path(&cur_dir, STD_BUILD_DIR)?, std_fake_root: check_path(&cur_dir, STD_FAKE_ROOT)?, }) @@ -54,7 +56,7 @@ fn check_path<'a>(root: &Path, p_str: &'a str) -> io::Result<&'a Path> { let p = Path::new(p_str); - if !root.join(p).is_dir() { + if !root.join(p).exists() { return Err(io::Error::new( io::ErrorKind::Other, format!( @@ -71,5 +73,6 @@ static RUST_SRC_DIR: &str = "third_party/rust-toolchain/lib/rustlib/src/rust"; static RUST_SRC_VENDOR_DIR: &str = "third_party/rust-toolchain/lib/rustlib/src/rust/vendor"; static RUST_STD_DIR: &str = "third_party/rust-toolchain/lib/rustlib/src/rust/library/std"; +static STD_CONFIG_FILE: &str = "build/rust/std/gnrt_config.toml"; static STD_BUILD_DIR: &str = "build/rust/std/rules"; static STD_FAKE_ROOT: &str = "build/rust/std/fake_root";
diff --git a/tools/mac/show_mod_init_func.py b/tools/mac/show_mod_init_func.py index b71881d..0886cd78 100755 --- a/tools/mac/show_mod_init_func.py +++ b/tools/mac/show_mod_init_func.py
@@ -1,11 +1,13 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # 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. """ -Prints the contents of the __DATA,__mod_init_func section of a Mach-O image. +Prints the contents of the module initialization functions stored in sections +matching the flag S_MOD_INIT_FUNC_POINTERS or S_INIT_FUNC_OFFSETS of a Mach-O +image. Usage: tools/mac/show_mod_init_func.py out/gn/Chromium\ Framework.unstripped @@ -14,52 +16,162 @@ dump-static-initializers.py instead. """ -from __future__ import print_function - -import optparse +import argparse import os +import platform +import re import subprocess import sys +# From <mach-o/loader.h> +# Section flag with only function pointers for initializers. +S_MOD_INIT_FUNC_POINTERS = 0x9 +# Section flag with only 32-bit offsets to initializers. +S_INIT_FUNC_OFFSETS = 0x16 + + +def GetArchitecture(binary, xcode_path): + """If the binary is a fat file with multiple architectures, return its + architecture that matches the host. If such an architecture is not present in + the fat file print an error and exit. If the binary is a thin file or a + single-architecture fat file, return the single architecture.""" + if xcode_path: + lipo_path = os.path.join(xcode_path, 'Contents', 'Developer', 'Toolchains', + 'XcodeDefault.xctoolchain', 'usr', 'bin', 'lipo') + else: + lipo_path = 'lipo' + + architectures = subprocess.check_output([lipo_path, '-archs', binary], + encoding='utf-8').strip().split(' ') + if len(architectures) == 1: + return architectures[0] + + host_arch = platform.machine() + if host_arch in architectures: + return host_arch + + raise Exception('Host architecture ' + host_arch + + ' not present in fat binary') + + +def GetTextBase(load_commands): + """Returns the base address of the __TEXT segment.""" + return int( + re.search('segname __TEXT\n.*vmaddr (0x[0-9a-f]+)', load_commands, + re.MULTILINE).group(1), 16) + + def ShowModuleInitializers(binary, xcode_path): """Gathers the module initializers for |binary| and symbolizes the addresses. """ - initializers = GetModuleInitializers(binary, xcode_path) + # Get the architecture to operate on. + architecture = GetArchitecture(binary, xcode_path) + + initializers = GetModuleInitializers(binary, architecture, xcode_path) if not initializers: # atos will do work even if there are no addresses, so bail early. return - symbols = SymbolizeAddresses(binary, initializers, xcode_path) + symbols = SymbolizeAddresses(binary, architecture, initializers, xcode_path) print(binary) for initializer in zip(initializers, symbols): print('%s @ %s' % initializer) -def GetModuleInitializers(binary, xcode_path): +def GetStaticInitializerSection(load_commands): + """Returns the static initializer location based on the binary load commands. + Static initializers are stored in sections with flag S_MOD_INIT_FUNC_POINTERS + or S_INIT_FUNC_OFFSETS. Below are some expected names of the the (sectname, + segname,flags) that ld64 and lld would use: + - deployment target macOS < 10.15 or iOS 14: + (__mod_init_func,__DATA,S_MOD_INIT_FUNC_POINTERS) + - deployment target macOS >= 10.15 or iOS 14: + (__mod_init_func,__DATA_CONST,S_MOD_INIT_FUNC_POINTERS) + - ld64 with a deployment target macOS >= 12 or iOS >= 16 or lldb with + `-fixup_chains`: + (__init_offsets,__TEXT,S_INIT_FUNC_OFFSETS)""" + matches = re.findall( + r'sectname (.*)\n\s+segname (.*)\n(?:.|\n)*?flags (0x[0-9a-f]*)\n', + load_commands, re.MULTILINE) + sections = [] + for sectname, segname, flags in matches: + flags = int(flags, 16) + if flags in (S_MOD_INIT_FUNC_POINTERS, S_INIT_FUNC_OFFSETS): + sections.append((sectname, segname, flags)) + return sections + + +def GetModuleInitializers(binary, architecture, xcode_path): """Parses the __DATA,__mod_init_func segment of |binary| and returns a list of string hexadecimal addresses of the module initializers. """ if xcode_path: - otool_path = os.path.join(xcode_path, 'Contents', 'Developer', - 'Toolchains', 'XcodeDefault.xctoolchain', 'usr', 'bin', 'otool') + otool_path = os.path.join(xcode_path, 'Contents', 'Developer', 'Toolchains', + 'XcodeDefault.xctoolchain', 'usr', 'bin', 'otool') else: otool_path = 'otool' - # The -v flag will display the addresses in a usable form (as opposed to - # just its on-disk little-endian byte representation). - otool = [otool_path, '-v', '-s', '__DATA', '__mod_init_func', binary] - lines = subprocess.check_output(otool).strip().split('\n') - # Skip the first two header lines and then get the address of the - # initializer in the second column. The first address is the address - # of the initializer pointer. - # out/gn/Chromium Framework.unstripped: - # Contents of (__DATA,__mod_init_func) section - # 0x0000000008761498 0x000000000385d120 - return [line.split(' ')[1] for line in lines[2:]] + load_commands = subprocess.check_output( + [otool_path, '-l', '-arch', architecture, binary], encoding='utf-8') + + static_initializer_sections = GetStaticInitializerSection(load_commands) + addresses = [] + for sectname, segname, flags in static_initializer_sections: + # The -v flag will display the addresses in a usable form (as opposed to + # just its on-disk little-endian byte representation). + otool = [ + otool_path, '-arch', architecture, '-v', '-s', segname, sectname, binary + ] + lines = subprocess.check_output(otool, encoding='utf-8').splitlines() + # Skip the first two header lines and then get the address of the + # initializer in the second column. The first address is the address of the + # initializer pointer. + # out/gn/Chromium Framework.unstripped: + # Contents of (__DATA,__mod_init_func) section + # 0x0000000008761498 0x000000000385d120 + if flags == S_MOD_INIT_FUNC_POINTERS: + sect_address = [line.split(' ')[1] for line in lines[2:]] + addresses.extend(sect_address) + continue + + # If otool adds a proper implementation for S_INIT_FUNC_OFFSETS the + # sections below building `sect_address` can be removed. The logic to add + # the __TEXT base address will remain. + if architecture not in ('arm64', 'x86_64'): + raise Exception( + "Parsing otool's S_INIT_FUNC_OFFSETS output on architectures other " + "than arm64 on x86_64 is unsupported.") + + # Trim the warning that otool doesn't understand S_INIT_FUNC_OFFSETS. + lines = [i for i in lines if not i.startswith('Unknown section')] + + # From https://github.com/apple-oss-distributions/cctools/blob/cctools-973.0.1/otool/ofile_print.c#L9553 + if architecture == 'arm64': + # For arm64 otool hex dumps as 4-byte words. Since the offsets + # in S_INIT_FUNC_OFFSETS arm64 are 32 bits simply trim the first column + sect_address = [line.split('\t')[1].strip() for line in lines[2:]] + sect_address = (' '.join(sect_address)).split(' ') + + if architecture == 'x86_64': + # For x86_64 otool dumps as byte-oriented output. Here, trim the first + # column and recreate each 32 bit address from the 8 bit groups. + octets = [line.split('\t')[1].strip() for line in lines[2:]] + octets = (' '.join(octets)).split(' ') + sect_address = [] + for i in range(0, len(octets), 4): + # Take four octets and interpret as little-endian. + sect_address.append(''.join(octets[i:i + 4][::-1])) + + # S_INIT_FUNC_OFFSETS are __TEXT relative. Add the __TEXT base + # address to each initializer offset. + text_base = GetTextBase(load_commands) + sect_address = [hex(int(x, 16) + text_base) for x in sect_address] + addresses.extend(sect_address) + return addresses -def SymbolizeAddresses(binary, addresses, xcode_path): +def SymbolizeAddresses(binary, architecture, addresses, xcode_path): """Given a |binary| and a list of |addresses|, symbolizes them using atos. """ if xcode_path: @@ -68,26 +180,24 @@ else: atos_path = 'atos' - atos = [atos_path, '-o', binary] + addresses - lines = subprocess.check_output(atos).strip().split('\n') + atos = [atos_path, '-arch', architecture, '-o', binary] + addresses + lines = subprocess.check_output(atos, encoding='utf-8').splitlines() return lines -def Main(): - parser = optparse.OptionParser(usage='%prog filename') - parser.add_option( +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument( '--xcode-path', default=None, help='Optional custom path to xcode binaries. By default, commands such ' 'as `otool` will be run as `/usr/bin/otool` which only works ' 'if there is a system-wide install of Xcode.') - opts, args = parser.parse_args() - if len(args) != 1: - parser.error('missing binary filename') - return 1 + parser.add_argument('filename', nargs=1) + options = parser.parse_args(args) - ShowModuleInitializers(args[0], opts.xcode_path) + ShowModuleInitializers(options.filename[0], options.xcode_path) return 0 if __name__ == '__main__': - sys.exit(Main()) + sys.exit(main(sys.argv[1:]))
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 07ffb29..9983ef3 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -406,6 +406,7 @@ # TODO(crbug.com/1235218): remove after the migration. 'chromeos-amd64-generic-rel (reclient compare)': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient', 'chromeos-amd64-generic-rel (reclient)': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient', + 'ios-blink-dbg-fyi': 'ios_simulator_blink_xctest', 'ios-fieldtrial-rel': 'ios_simulator_debug_static_bot_xctest_arm64_reclient', 'ios-m1-simulator': 'ios_simulator_debug_static_bot_xctest_arm64_reclient', 'ios-m1-simulator-cronet': 'ios_cronet_xctest_arm64_reclient', @@ -2884,6 +2885,10 @@ 'ios', 'ios_device', 'ios_cpu_arm64', 'ios_disable_code_signing', 'release_bot_reclient', 'xctest', ], + 'ios_simulator_blink_xctest': [ + 'ios', 'ios_simulator', 'debug', 'use_blink', 'xctest' + ], + 'ios_simulator_code_coverage_partial_instrumentation_xctest': [ 'use_clang_coverage', 'debug_static_bot', 'x64', 'ios', 'ios_simulator', 'partial_code_coverage_instrumentation', 'xctest', ], @@ -4695,6 +4700,10 @@ 'gn_args': 'use_chromium_rust_toolchain=false', }, + 'use_blink': { + 'gn_args': 'use_blink=true' + }, + 'use_clang_coverage': { 'gn_args': 'use_clang_coverage=true', },
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index f35a774b..2276838 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -511,6 +511,15 @@ "use_remoteexec": true } }, + "ios-blink-dbg-fyi": { + "gn_args": { + "enable_run_ios_unittests_with_xctest": true, + "is_debug": true, + "target_environment": "simulator", + "target_os": "ios", + "use_blink": true + } + }, "ios-fieldtrial-rel": { "gn_args": { "enable_run_ios_unittests_with_xctest": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index cd2ae33..c0e995d 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -24244,6 +24244,8 @@ <int value="48" label="Secondary toolbar swipe-up pending intent EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION"/> + <int value="49" + label="Decoration type EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE"/> </enum> <enum name="CustomTabsParallelRequestStatusOnStart"> @@ -32777,6 +32779,7 @@ <int value="1076" label="DeviceActivityHeartbeatCollectionRateMs"/> <int value="1077" label="WallpaperGooglePhotosIntegrationEnabled"/> <int value="1078" label="WebRtcTextLogCollectionAllowed"/> + <int value="1079" label="EnforceLocalAnchorConstraintsEnabled"/> </enum> <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index e786cd0..af36f04a 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -2110,7 +2110,7 @@ </histogram> <histogram name="Arc.Session.HasWebViewUsage" enum="Boolean" - expires_after="2023-02-01"> + expires_after="2023-04-15"> <owner>hungmn@google.com</owner> <owner>khmel@google.com</owner> <owner>arc-performance@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 9810d0d..00618cd4 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -4423,13 +4423,14 @@ <owner>cros-status-area-eng@google.com</owner> <summary> Recorded when an action is done on any FeaturePod in the quick settings - view. {FeaturePodBehavior} can be toggled to enable the feature, toggle to - disable this feature, or go to the feature's detailed page. + view. {FeaturePodBehavior} can be a toggle to enable the feature, toggle to + disable this feature, going to the feature's detailed page, or making the + button visible. </summary> <token key="FeaturePodBehavior"> - <variant name="DiveIn" summary="Go to the feature detailed page"/> - <variant name="ToggleOff" summary="Toggle to disable the feature"/> - <variant name="ToggleOn" summary="Toggle to enable the feature"/> + <variant name="DiveIn" summary="Go to the feature's detailed page"/> + <variant name="ToggledOff" summary="Toggle to disable the feature"/> + <variant name="ToggledOn" summary="Toggle to enable the feature"/> <variant name="Visible" summary="The feature pod is shown"/> </token> </histogram> @@ -5483,14 +5484,14 @@ <owner>cros-status-area-eng@google.com</owner> <summary> Recorded when an action is done on any FeaturePod in the old quick settings - view. {FeaturePodBehavior} can be toggled to enable the feature, toggle to - disable this feature, go to the feature's detailed page, or the feature pod - is shown (visible). + view. {FeaturePodBehavior} can be a toggle to enable the feature, toggle to + disable this feature, going to the feature's detailed page, or making the + button visible. </summary> <token key="FeaturePodBehavior"> - <variant name="DiveIn" summary="Go to the feature detailed page"/> - <variant name="ToggleOff" summary="Toggle to disable the feature"/> - <variant name="ToggleOn" summary="Toggle to enable the feature"/> + <variant name="DiveIn" summary="Go to the feature's detailed page"/> + <variant name="ToggledOff" summary="Toggle to disable the feature"/> + <variant name="ToggledOn" summary="Toggle to enable the feature"/> <variant name="Visible" summary="The feature pod is shown"/> </token> </histogram>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml index 0a8a9ec9..42e54fac 100644 --- a/tools/metrics/histograms/metadata/history/histograms.xml +++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -1224,6 +1224,8 @@ <variant name="PersistClustersLatency" summary="receiving acknowledgement for completion of persisting the clusters to the history DB"/> + <variant name="Total" + summary="the main thread being notified that the work was completed"/> </token> </histogram> @@ -1314,6 +1316,8 @@ summary="receiving acknowledgement for completion of persisting the context clusters formed from unclustered visits to the history DB"/> + <variant name="Total" + summary="the main thread being notified that the work was completed"/> </token> </histogram>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml index b20d046..c3c9ead3 100644 --- a/tools/metrics/histograms/metadata/ios/histograms.xml +++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1368,7 +1368,7 @@ </histogram> <histogram name="IOS.PasswordsInOtherApps.AutoFillStatusChange" - enum="PasswordAutoFillEnrollmentStatus" expires_after="2023-03-11"> + enum="PasswordAutoFillEnrollmentStatus" expires_after="2023-09-01"> <owner>ginnyhuang@chromium.org</owner> <owner>bling-team@google.com</owner> <summary> @@ -1379,7 +1379,7 @@ </histogram> <histogram name="IOS.PasswordsInOtherApps.Dismiss" - enum="PasswordAutoFillEnrollmentStatus" expires_after="2023-03-11"> + enum="PasswordAutoFillEnrollmentStatus" expires_after="2023-09-01"> <owner>ginnyhuang@chromium.org</owner> <owner>bling-team@google.com</owner> <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index cbbc68c4..79909a0d 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -13410,8 +13410,7 @@ </summary> <metric name="AnchorIndex"> <summary> - The index of the clicked-on anchor element in NavigationPredictor's - |top_urls_|. + The index of the clicked-on anchor element in the tracked list. </summary> </metric> <metric name="BucketedPathHash"> @@ -13515,6 +13514,13 @@ the same as the href when the element was created. 0 if the href changed. </summary> </metric> + <metric name="NavigationStartToLinkClickedMs"> + <summary> + The time in milliseconds between the moment an entry for the anchor + element was logged to UKM and the moment the user clicked on it. + Exponentially bucketed, 1.3 factor. + </summary> + </metric> </event> <event name="NavigationPredictorPageLinkMetrics" singular="True"> @@ -13621,6 +13627,50 @@ </metric> </event> +<event name="NavigationPredictorUserInteractions"> + <owner>ryansturm@chromium.org</owner> + <owner>isaboori@google.com</owner> + <summary> + Metrics that describe user interaction with an anchor element. This event is + emitted once per randomly selected anchor elements. + </summary> + <metric name="AnchorIndex"> + <summary> + The index of the anchor element in NavigationPredictor's |top_urls_|. + </summary> + </metric> + <metric name="IsInViewport"> + <summary> + 1 if anchor element is in viewport, otherwise 0. + </summary> + </metric> + <metric name="IsPointerHoveringOver"> + <summary> + 1 if the pointer is hovering over the anchor element, otherwise 0. + </summary> + </metric> + <metric name="MaxEnteredViewportToLeftViewportMs"> + <summary> + Maximum time between the moment the anchor element entered the viewport + and the moment it left the viewport in milliseconds. Exponentially + bucketed using GetExponentialBucketMin a value of 1.3. + </summary> + </metric> + <metric name="MaxHoverDwellTimeMs"> + <summary> + Maximum dwelltime of pointer hovering over the anchor element in + milliseconds. Exponentially bucketed using GetExponentialBucketMin a value + of 1.3. + </summary> + </metric> + <metric name="PointerHoveringOverCount"> + <summary> + How many times the pointer was hovering over the anchor element. + Exponentially bucketed using GetExponentialBucketMin a value of 1.3. + </summary> + </metric> +</event> + <event name="NavigationThrottleDeferredTime"> <owner>ryansturm@chromium.org</owner> <owner>chrome-brapp-loading@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index cb317dd0..ec4beae 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/3b59f000c939bfe4d05267fd68d282ef0b541334/linux-arm64/trace_processor_shell" }, "win": { - "hash": "5804b6d194c554726237f00739e8c468efbe28c0", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/1046bf928b4441cb79549691d76eeabec48e7274/trace_processor_shell.exe" + "hash": "5952b645b8e655e24391ee5ca52e0f767c46a6dc", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/58600633967db2ab113cf205eb2be6b76aef97d4/trace_processor_shell.exe" }, "linux_arm": { "hash": "e945a99da7a66211f847b8049627bbec846d2d1d", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/ea816328b746af31c8b7441db2ae0c3f561e629d/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "524701dfeed6fa1f92c9e91822b57d95482f0526", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/1046bf928b4441cb79549691d76eeabec48e7274/trace_processor_shell" + "hash": "15df64f8578f9b3f9c96fa89d636fd32d4814942", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/0d180f46481a96cbe8340734fa5cdce3bba636c8/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/typescript/ts_library.py b/tools/typescript/ts_library.py index df490887..b99bbfa 100644 --- a/tools/typescript/ts_library.py +++ b/tools/typescript/ts_library.py
@@ -19,7 +19,7 @@ import node_modules from path_mappings import GetDepToPathMappings -from validate_tsconfig import validateTsconfigJson, validateJavaScriptAllowed +from validate_tsconfig import validateTsconfigJson, validateJavaScriptAllowed, validateRootDir def _write_tsconfig_json(gen_dir, tsconfig, tsconfig_file): @@ -54,6 +54,12 @@ root_dir = os.path.relpath(args.root_dir, args.gen_dir) out_dir = os.path.relpath(args.out_dir, args.gen_dir) + + is_root_dir_valid, error = validateRootDir(args.root_dir, args.gen_dir, + args.root_gen_dir, args.is_ios) + if not is_root_dir_valid: + raise AssertionError(error) + TSCONFIG_BASE_PATH = os.path.join(_HERE_DIR, 'tsconfig_base.json') tsconfig = collections.OrderedDict()
diff --git a/tools/typescript/ts_library_test.py b/tools/typescript/ts_library_test.py index e455f1c..0a23828e 100755 --- a/tools/typescript/ts_library_test.py +++ b/tools/typescript/ts_library_test.py
@@ -12,6 +12,7 @@ import unittest _HERE_DIR = os.path.dirname(__file__) +_CWD = os.getcwd() class TsLibraryTest(unittest.TestCase): @@ -24,7 +25,8 @@ shutil.rmtree(self._out_folder) def _build_project1(self, enable_source_maps=False): - gen_dir = os.path.join(self._out_folder, 'project1') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project1') # Generate definition .d.ts file for legacy JS file. ts_definitions.main([ @@ -45,11 +47,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project1'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project1'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'foo.ts', '--definitions', @@ -86,7 +88,8 @@ def _build_project2(self, project1_gen_dir, project3_gen_dir, project6_gen_dir): root_dir = os.path.join(_HERE_DIR, 'tests', 'project2') - gen_dir = os.path.join(self._out_folder, 'project2') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project2') project1_gen_dir = os.path.relpath(project1_gen_dir, gen_dir) project3_gen_dir = os.path.relpath(project3_gen_dir, gen_dir) project6_gen_dir = os.path.relpath(project6_gen_dir, gen_dir) @@ -99,11 +102,11 @@ '--raw_deps', '//ui/webui/resources/js:build_ts', '--root_dir', - root_dir, + os.path.relpath(root_dir, _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'bar.ts', '--deps', @@ -135,7 +138,8 @@ # Builds project3, which includes only definition files. def _build_project3(self): - gen_dir = os.path.join(self._out_folder, 'project3') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project3') ts_library.main([ '--output_suffix', @@ -143,13 +147,14 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project3'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project3'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--definitions', - '../../tests/project3/baz.d.ts', + os.path.relpath( + os.path.join(_HERE_DIR, 'tests', 'project3', 'baz.d.ts'), gen_dir), '--composite', ]) return gen_dir @@ -162,7 +167,8 @@ self.assertFalse(os.path.exists(os.path.join(gen_dir, 'build_ts.manifest'))) def _build_project4(self): - gen_dir = os.path.join(self._out_folder, 'project4') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project4') # Build project4, which includes multiple TS files, only one of which should # be included in the manifest. @@ -172,11 +178,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project4'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project4'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'include.ts', 'exclude.ts', @@ -205,7 +211,8 @@ self.assertEqual(data['files'], expected_files) def _build_project5(self): - gen_dir = os.path.join(self._out_folder, 'project5') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project5') out_dir_test = os.path.join(self._out_folder, 'project5_test') # Build project5, which includes 2 TS projects one for prod and one for @@ -218,11 +225,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project5'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project5'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'bar.ts', ]) @@ -237,11 +244,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project5'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project5'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'bar_test.ts', ]) @@ -264,7 +271,8 @@ self._assert_manifest_files(manifest_test, ['bar_test.js']) def _build_project6(self): - gen_dir = os.path.join(self._out_folder, 'ui', 'webui', 'resources', 'js') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'ui', 'webui', 'resources', 'js') out_dir = os.path.join(self._out_folder, 'ui', 'webui', 'resources', 'tsc', 'js') @@ -277,11 +285,13 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'ui', 'webui', 'resources', 'js'), + os.path.relpath( + os.path.join(_HERE_DIR, 'tests', 'ui', 'webui', 'resources', 'js'), + _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - out_dir, + os.path.relpath(out_dir, _CWD), '--in_files', 'assert.ts', '--composite', @@ -313,7 +323,7 @@ # Test success case where both project1 and project2 are compiled successfully # and no errors are thrown. def testSuccess(self): - self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) + self._out_folder = tempfile.mkdtemp(dir=_CWD) project1_gen_dir = self._build_project1() self._assert_project1_output(project1_gen_dir) @@ -336,8 +346,9 @@ # Test error case where a type violation exists, ensure that an error is # thrown. def testError(self): - self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) - gen_dir = os.path.join(self._out_folder, 'project1') + self._out_folder = tempfile.mkdtemp(dir=_CWD) + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project1') try: ts_library.main([ '--output_suffix', @@ -345,11 +356,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - os.path.join(_HERE_DIR, 'tests', 'project1'), + os.path.relpath(os.path.join(_HERE_DIR, 'tests', 'project1'), _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'errors.ts', '--composite', @@ -365,9 +376,10 @@ # Test error case where the project's tsconfig file is failing validation. def testTsConfigValidationError(self): - self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) + self._out_folder = tempfile.mkdtemp(dir=_CWD) root_dir = os.path.join(_HERE_DIR, 'tests', 'project5') - gen_dir = os.path.join(self._out_folder, 'project5') + gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests', + 'project5') try: ts_library.main([ '--output_suffix', @@ -375,11 +387,11 @@ '--root_gen_dir', os.path.relpath(self._out_folder, gen_dir), '--root_dir', - root_dir, + os.path.relpath(root_dir, _CWD), '--gen_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--out_dir', - gen_dir, + os.path.relpath(gen_dir, _CWD), '--in_files', 'bar.ts', '--tsconfig_base', @@ -394,7 +406,7 @@ # Test case where |enable_source_maps| is specified. def testEnableSourceMaps(self): - self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) + self._out_folder = tempfile.mkdtemp(dir=_CWD) expectations_dir = os.path.join(_HERE_DIR, 'tests', 'expected', 'project1')
diff --git a/tools/typescript/validate_tsconfig.py b/tools/typescript/validate_tsconfig.py index 43ba234..e84d4ba 100644 --- a/tools/typescript/validate_tsconfig.py +++ b/tools/typescript/validate_tsconfig.py
@@ -10,6 +10,13 @@ # args corresponding to config options to limit the possibility of unsupported # configurations proliferating in the codebase. +import os + +_CWD = os.getcwd().replace('\\', '/') +_HERE_DIR = os.path.dirname(__file__) +_SRC_DIR = os.path.normpath(os.path.join(_HERE_DIR, '..', + '..')).replace('\\', '/') + # Options configured by the ts_library should not be set separately. _tsconfig_compiler_options_mappings = { 'allowJs': 'allow_js=true', @@ -132,3 +139,55 @@ return False, 'Invalid allow_js detected for input directory ' + \ f'{source_dir} and output directory {out_dir}' + + +# |root_dir| shouldn't refer to any parent directories. Specifically it should +# be either: +# - within the folder tree starting at the ts_library() target's location +# - within the folder tree starting at the ts_library() target's corresponding +# target_gen_dir location. +def validateRootDir(root_dir, gen_dir, root_gen_dir, is_ios): + root_gen_dir_from_build = os.path.normpath(os.path.join( + gen_dir, root_gen_dir)).replace('\\', '/') + target_path = os.path.relpath(gen_dir, + root_gen_dir_from_build).replace('\\', '/') + + # Broadly special casing ios/ for now, since compile_ts.gni relies on + # unsupported behavior of setting the root_dir to src/. + # TODO (https://www.crbug.com/1412158): Make iOS TypeScript build tools use + # ts_library in a supported way, or change them to not rely on ts_library. + if (is_ios and (target_path.startswith('ios') or '/ios/' in target_path)): + return True, None + + # Legacy cases supported for backward-compatibility. Do not add new targets + # here. The existing exceptions should be removed over time. + exceptions = [ + # TODO (https://www.crbug.com/1412158): Update this folder to copy files + # instead of setting $root_gen_dir/mojom-webui as the root_dir. + 'ui/webui/resources/mojo', + + # ChromeOS cases + 'ash/webui/camera_app_ui/resources/js', + 'ash/webui/color_internals/mojom', + 'ash/webui/face_ml_app_ui/mojom', + 'ash/webui/sample_system_web_app_ui/mojom', + 'chrome/browser/resources/chromeos/accessibility/select_to_speak', + ] + + if target_path in exceptions: + return True, None + + target_path_src = os.path.relpath(os.path.join(_SRC_DIR, target_path), + _CWD).replace('\\', '/') + root_path_from_gen = os.path.relpath(root_dir, + root_gen_dir_from_build).replace( + '\\', '/') + root_path_from_src = os.path.relpath(os.path.join(_CWD, root_dir), + _SRC_DIR).replace('\\', '/') + + if (root_path_from_gen.startswith(target_path) + or root_path_from_src.startswith(target_path)): + return True, None + + return False, f'Error: root_dir ({root_dir}) should be within {gen_dir} ' + \ + f'or {target_path_src}.'
diff --git a/ui/android/resources/resource_manager.h b/ui/android/resources/resource_manager.h index bff2565..80fa5d5 100644 --- a/ui/android/resources/resource_manager.h +++ b/ui/android/resources/resource_manager.h
@@ -62,6 +62,8 @@ : 0; } + virtual void MarkTintNonDiscardable(SkColor tint_color) = 0; + // A notification that all updates have finished for the current frame. virtual void OnFrameUpdatesFinished() = 0;
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc index ac65593..171d2c85 100644 --- a/ui/android/resources/resource_manager_impl.cc +++ b/ui/android/resources/resource_manager_impl.cc
@@ -129,6 +129,10 @@ } } +void ResourceManagerImpl::MarkTintNonDiscardable(SkColor tint_color) { + used_tints_.insert(tint_color); +} + void ResourceManagerImpl::OnFrameUpdatesFinished() { RemoveUnusedTints(); used_tints_.clear();
diff --git a/ui/android/resources/resource_manager_impl.h b/ui/android/resources/resource_manager_impl.h index d5a1bb8..30eba34 100644 --- a/ui/android/resources/resource_manager_impl.h +++ b/ui/android/resources/resource_manager_impl.h
@@ -49,6 +49,7 @@ SkColor tint_color, bool preserve_color_alpha) override; void PreloadResource(AndroidResourceType res_type, int res_id) override; + void MarkTintNonDiscardable(SkColor tint_color) override; void OnFrameUpdatesFinished() override; // Called from Java @@ -96,7 +97,7 @@ TintedResourceMap tinted_resources_; // The set of tints that are used for resources in the current frame. - std::unordered_set<int> used_tints_; + std::unordered_set<SkColor> used_tints_; base::android::ScopedJavaGlobalRef<jobject> java_obj_; };
diff --git a/ui/color/chromeos/native_color_mixers_chromeos.cc b/ui/color/chromeos/native_color_mixers_chromeos.cc index 891e67b..df6e202 100644 --- a/ui/color/chromeos/native_color_mixers_chromeos.cc +++ b/ui/color/chromeos/native_color_mixers_chromeos.cc
@@ -23,6 +23,8 @@ mixer[kColorAshSystemUIMenuItemBackgroundSelected] = { kColorMenuItemBackgroundSelected}; mixer[kColorAshSystemUIMenuSeparator] = {kColorMenuSeparator}; + mixer[kColorMultitaskMenuNudgePulse] = {kColorEndpointForeground}; + bool dark_mode = key.color_mode == ColorProviderManager::ColorMode::kDark; // Add color initializations for highlight border.
diff --git a/ui/color/color_id.h b/ui/color/color_id.h index cc98f7c..5196cf2 100644 --- a/ui/color/color_id.h +++ b/ui/color/color_id.h
@@ -221,6 +221,7 @@ E_CPONLY(kColorButtonForegroundUnchecked) \ E_CPONLY(kColorMultitaskFeedbackButtonLabelBackground) \ E_CPONLY(kColorMultitaskFeedbackButtonLabelForeground) \ + E_CPONLY(kColorMultitaskMenuNudgePulse) \ E_CPONLY(kColorComboboxBackground) \ E_CPONLY(kColorComboboxBackgroundDisabled) \ E_CPONLY(kColorCustomFrameCaptionForeground) \
diff --git a/ui/file_manager/file_manager/widgets/xf_search_options.html b/ui/file_manager/file_manager/widgets/xf_search_options.html index 2933ba26..54e70f3 100644 --- a/ui/file_manager/file_manager/widgets/xf_search_options.html +++ b/ui/file_manager/file_manager/widgets/xf_search_options.html
@@ -10,9 +10,9 @@ } </style> -<xf-select id="location-selector" icon="select-location"> +<xf-select id="location-selector" icon="select-location" menuAlignment="start"> </xf-select> -<xf-select id="recency-selector" icon="select-time"> +<xf-select id="recency-selector" icon="select-time" menuAlignment="start"> </xf-select> -<xf-select id="type-selector" icon="select-filetype"> +<xf-select id="type-selector" icon="select-filetype" menuAlignment="start"> </xf-select>
diff --git a/ui/file_manager/file_manager/widgets/xf_select.ts b/ui/file_manager/file_manager/widgets/xf_select.ts index 0b9e5fa9..fdf7a81 100644 --- a/ui/file_manager/file_manager/widgets/xf_select.ts +++ b/ui/file_manager/file_manager/widgets/xf_select.ts
@@ -77,6 +77,12 @@ */ @property({type: String, reflect: true}) value: string = ''; + /** + * The alignment of items in the dropdown menu. Can be one of + * 'start', 'center', 'end'. + */ + @property({type: String, reflect: true}) menuAlignment: string = 'center'; + static get events() { return { /** emits when the currently selected option changed. */ @@ -161,10 +167,11 @@ * Returns a template of the dropdown which shows available choices. */ private renderDropdown_() { + const alignment = this.menuAlignment || 'center'; return html`<cr-action-menu> ${this.options.map((option, index) => html` <cr-button - class="dropdown-item" + class="dropdown-item dropdown-item-${alignment}" role="menuitem" @click=${() => this.onOptionSelected_(index)} ?selected=${this.selectedOption_!.value === option.value}> @@ -352,6 +359,15 @@ cr-button.dropdown-item { --focus-shadow-color: none; } + cr-button.dropdown-item-center { + justify-content: center; + } + cr-button.dropdown-item-start { + justify-content: start; + } + cr-button.dropdown-item-end { + justify-content: end; + } `; }
diff --git a/ui/ozone/platform/drm/common/drm_wrapper.h b/ui/ozone/platform/drm/common/drm_wrapper.h index 6247fa0..297177e 100644 --- a/ui/ozone/platform/drm/common/drm_wrapper.h +++ b/ui/ozone/platform/drm/common/drm_wrapper.h
@@ -13,7 +13,6 @@ #include "base/files/file.h" #include "base/files/file_path.h" #include "base/functional/callback.h" -#include "base/memory/ref_counted.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" @@ -54,13 +53,14 @@ // Wraps DRM calls into a tight interface. Used to provide different // implementations of the DRM calls. For the actual implementation the DRM API // would be called. In unit tests this interface would be stubbed. -class DrmWrapper : public base::RefCountedThreadSafe<DrmWrapper> { +class DrmWrapper { public: DrmWrapper(const base::FilePath& device_path, base::File file, bool is_primary_device); DrmWrapper(const DrmWrapper&) = delete; DrmWrapper& operator=(const DrmWrapper&) = delete; + virtual ~DrmWrapper(); // Open device. virtual bool Initialize(); @@ -252,10 +252,6 @@ bool is_primary_device() const { return is_primary_device_; } protected: - friend class base::RefCountedThreadSafe<DrmWrapper>; - - virtual ~DrmWrapper(); - // Path to the DRM device (in sysfs). const base::FilePath device_path_; // DRM device.
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h index 23d2daf..f3bdf5b 100644 --- a/ui/ozone/platform/drm/gpu/drm_device.h +++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -24,7 +24,8 @@ class HardwareDisplayPlaneManager; class GbmDevice; -class DrmDevice : public DrmWrapper { +class DrmDevice : public DrmWrapper, + public base::RefCountedThreadSafe<DrmDevice> { public: using PageFlipCallback = base::OnceCallback<void(unsigned int /* frame */, @@ -76,6 +77,8 @@ GbmDevice* gbm_device() const { return gbm_.get(); } protected: + friend class base::RefCountedThreadSafe<DrmDevice>; + friend class DrmDisplayTest; ~DrmDevice() override;
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc index 7f65886..32de82bb 100644 --- a/ui/views/controls/button/image_button.cc +++ b/ui/views/controls/button/image_button.cc
@@ -141,6 +141,14 @@ return views::PaintInfo::ScaleType::kUniformScaling; } +void ImageButton::OnThemeChanged() { + Button::OnThemeChanged(); + + // If we have any `ImageModel`s, they may need repaint upon a `ColorProvider` + // change. + SchedulePaint(); +} + void ImageButton::PaintButtonContents(gfx::Canvas* canvas) { // TODO(estade|tdanderson|bruthig): The ink drop layer should be positioned // behind the button's image which means the image needs to be painted to its
diff --git a/ui/views/controls/button/image_button.h b/ui/views/controls/button/image_button.h index 8eb1f73b..a3aea3f 100644 --- a/ui/views/controls/button/image_button.h +++ b/ui/views/controls/button/image_button.h
@@ -73,6 +73,7 @@ // Overridden from View: gfx::Size CalculatePreferredSize() const override; views::PaintInfo::ScaleType GetPaintScaleType() const override; + void OnThemeChanged() override; protected: // Overridden from Button:
diff --git a/weblayer/browser/translate_compact_infobar.cc b/weblayer/browser/translate_compact_infobar.cc index 1747423..23f55c0 100644 --- a/weblayer/browser/translate_compact_infobar.cc +++ b/weblayer/browser/translate_compact_infobar.cc
@@ -118,13 +118,11 @@ if (option == translate::TranslateUtils::OPTION_SOURCE_CODE) { std::string source_code = base::android::ConvertJavaStringToUTF8(env, value); - if (delegate->source_language_code().compare(source_code) != 0) - delegate->UpdateSourceLanguage(source_code); + delegate->UpdateSourceLanguage(source_code); } else if (option == translate::TranslateUtils::OPTION_TARGET_CODE) { std::string target_code = base::android::ConvertJavaStringToUTF8(env, value); - if (delegate->target_language_code().compare(target_code) != 0) - delegate->UpdateTargetLanguage(target_code); + delegate->UpdateTargetLanguage(target_code); } else { DCHECK(false); }