diff --git a/DEPS b/DEPS index ce42510..c7ffc60 100644 --- a/DEPS +++ b/DEPS
@@ -253,15 +253,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': '447845a906add31e196f9665fde406ab230c866b', + 'skia_revision': '631e9e00fc341ce67e576cfed74cf5ed934cd44d', # 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': 'f6ce14f28a6a2a6e43c2eaacba19b14689e69eba', + 'v8_revision': 'bbc18c9de614169da2a3d784177efeec33748538', # 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': '37bfc40d1bfc28a67f31200c71ff88a4aeece68c', + 'angle_revision': '1fd544a4adc994d7e0b2f4e0d713d17c614e3fe6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -269,7 +269,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '3fa9afc042f113c75e79d8d7966ef03ceea1fca5', + 'pdfium_revision': '52cd2170b9ad1f550764629d5820e07438627213', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -328,7 +328,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'f98d5bdccf3be601afbcf9fdc3bd7e585b6b0239', + 'devtools_frontend_revision': '776472583c6cc432c4f2c7028f361edbf033a900', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -368,7 +368,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': '5ba6ae779a8052202369962d67d5476ea4155fd9', + 'dawn_revision': '44f039d3c209efc99315990938056f9157f1b42a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -392,7 +392,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libavif # and whatever else without interference from each other. - 'libavif_revision': '7a6d13be831da40859c6b61fb513b7a7a654a58b', + 'libavif_revision': '6244bccbea5d96b08094ea1aed540c0d5a7cc31b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nearby # and whatever else without interference from each other. @@ -704,7 +704,7 @@ Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248', 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + 'c5fc33d1a41991a0f64b42169220a57ffdaf7b54', + 'url': Var('chromium_git') + '/website.git' + '@' + '45ff4c5d75b9182d81d8a1bea92005acd2178e14', }, 'src/ios/third_party/earl_grey2/src': { @@ -1126,12 +1126,12 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'bd903f09a68a2c4404c617014924e73db994a230', + 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '1af27b73d59bab46733bd0817be2008561c8288b', 'condition': 'checkout_linux', }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a255e4064ae55e05e306aa595b12c047638e6b8c', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1c22c511d02b4c4e02173297bcb8f2df3d3b4f66', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1514,7 +1514,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '54ba566cdda23803c888afe5b664005be30d06e0', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd041e6e3ffe64223b6c9bbb3b066f8ab59873c02', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1592,7 +1592,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/android/aemu/release/linux-amd64', - 'version': '3qEVy-mxJWvizWcxmbgwzKDpK9LqsU4AiDZ1frAqUIIC' + 'version': '16Xb5ZlqfZdoyBTPFYjV_SP1MSdQ7iYzIxZZT7_pHbkC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -1653,7 +1653,7 @@ Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + '65dc7b383985eb4f63cd3e752136db8d9b4be8c0', 'src/third_party/sqlite/src': - Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + 'bae060174325d9f8bbab105e9f807977bb1d3cf9', + Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + '803a31044a01ca3984f7c321c8821974e4100d07', 'src/third_party/sqlite4java': { 'packages': [ @@ -1735,7 +1735,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '008969e4d83211e112f83143bd7932f30e4ef549', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'c1ed7ef491f83c83b90bafae2f1b72b0f68673a7', + Var('webrtc_git') + '/src.git' + '@' + '5823c55b17bb531bf0f5b11edcc747a189cc1922', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1805,7 +1805,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@db5ecce14d9077cf71fdbcc283f4f891f2e969f6', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@334e4232a3303239d0a505cd77c146207fa765e5', 'condition': 'checkout_src_internal', },
diff --git a/ash/app_list/app_list_badge_controller.cc b/ash/app_list/app_list_badge_controller.cc index bfc2754..9c927a1 100644 --- a/ash/app_list/app_list_badge_controller.cc +++ b/ash/app_list/app_list_badge_controller.cc
@@ -47,8 +47,7 @@ // Update the notification badge indicator for the newly added app list // item. cache_->ForOneApp(item->id(), [item](const apps::AppUpdate& update) { - item->UpdateNotificationBadge(update.HasBadge() == - apps::mojom::OptionalBool::kTrue); + item->UpdateNotificationBadge(update.HasBadge().value_or(false)); }); } } @@ -80,7 +79,8 @@ void AppListBadgeController::OnAppUpdate(const apps::AppUpdate& update) { if (update.HasBadgeChanged() && notification_badging_pref_enabled_.value_or(false)) { - UpdateItemNotificationBadge(update.AppId(), update.HasBadge()); + UpdateItemNotificationBadge(update.AppId(), + update.HasBadge().value_or(false)); } } @@ -91,13 +91,13 @@ void AppListBadgeController::UpdateItemNotificationBadge( const std::string& app_id, - apps::mojom::OptionalBool has_badge) { + bool has_badge) { if (!model_) return; AppListItem* item = model_->FindItem(app_id); if (!item) return; - item->UpdateNotificationBadge(has_badge == apps::mojom::OptionalBool::kTrue); + item->UpdateNotificationBadge(has_badge); } void AppListBadgeController::UpdateAppNotificationBadging() { @@ -115,11 +115,8 @@ if (cache_) { cache_->ForEachApp([this](const apps::AppUpdate& update) { // Set the app notification badge hidden when the pref is disabled. - apps::mojom::OptionalBool has_badge = - notification_badging_pref_enabled_.value() && - (update.HasBadge() == apps::mojom::OptionalBool::kTrue) - ? apps::mojom::OptionalBool::kTrue - : apps::mojom::OptionalBool::kFalse; + bool has_badge = notification_badging_pref_enabled_.value() && + (update.HasBadge().value_or(false)); UpdateItemNotificationBadge(update.AppId(), has_badge); }); }
diff --git a/ash/app_list/app_list_badge_controller.h b/ash/app_list/app_list_badge_controller.h index 1b6fb0ca..2861787a 100644 --- a/ash/app_list/app_list_badge_controller.h +++ b/ash/app_list/app_list_badge_controller.h
@@ -55,8 +55,7 @@ private: // Updates whether a notification badge is shown for the AppListItemView // corresponding with the |app_id|. - void UpdateItemNotificationBadge(const std::string& app_id, - apps::mojom::OptionalBool has_badge); + void UpdateItemNotificationBadge(const std::string& app_id, bool has_badge); // Checks the notification badging pref and then updates whether a // notification badge is shown for each AppListItem.
diff --git a/ash/app_list/app_list_bubble_presenter.cc b/ash/app_list/app_list_bubble_presenter.cc index 4732631..fcdbdda 100644 --- a/ash/app_list/app_list_bubble_presenter.cc +++ b/ash/app_list/app_list_bubble_presenter.cc
@@ -382,15 +382,9 @@ bubble_widget_->GetNativeWindow()->parent(); // If the bubble or one of its children (e.g. an uninstall dialog) gained - // focus, the bubble should stay open. Likewise, certain other containers are - // allowed to gain focus without closing the launcher (e.g. power menu). - // Allowing the other containers is a speculative fix for a bug where the - // launcher closes spontaneously. - if (gained_focus) { - aura::Window* container = ash::GetContainerForWindow(gained_focus); - if (container && !ShouldCloseAppListForFocusInContainer(container->GetId())) - return; - } + // focus, the bubble should stay open. + if (gained_focus && app_list_container->Contains(gained_focus)) + return; // Otherwise, if the bubble or one of its children lost focus, the bubble // should close.
diff --git a/ash/app_list/app_list_bubble_presenter_unittest.cc b/ash/app_list/app_list_bubble_presenter_unittest.cc index fbd04ac3..f41f3a9 100644 --- a/ash/app_list/app_list_bubble_presenter_unittest.cc +++ b/ash/app_list/app_list_bubble_presenter_unittest.cc
@@ -330,21 +330,6 @@ EXPECT_TRUE(presenter->bubble_widget_for_test()); } -TEST_F(AppListBubblePresenterTest, DismissOnFocusLoss) { - AppListBubblePresenter* presenter = GetBubblePresenter(); - presenter->Show(GetPrimaryDisplay().id()); - - // Creating a window in these containers should not dismiss the launcher. - for (int id : kContainersThatWontHideAppListOnFocus) { - std::unique_ptr<views::Widget> widget = CreateTestWidget(nullptr, id); - EXPECT_TRUE(presenter->IsShowing()); - } - - // Creating a window in the default window container dismisses the launcher. - std::unique_ptr<views::Widget> widget = CreateTestWidget(); - EXPECT_FALSE(presenter->IsShowing()); -} - // Regression test for https://crbug.com/1275755 TEST_F(AppListBubblePresenterTest, AssistantKeyOpensToAssistantPage) { // Simulate production behavior for animations, assistant, and zero-state
diff --git a/ash/app_list/app_list_presenter_impl.cc b/ash/app_list/app_list_presenter_impl.cc index 2a5e649..a6867dea 100644 --- a/ash/app_list/app_list_presenter_impl.cc +++ b/ash/app_list/app_list_presenter_impl.cc
@@ -26,6 +26,7 @@ #include "ash/wm/container_finder.h" #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" #include "chromeos/services/assistant/public/cpp/assistant_enums.h" @@ -125,6 +126,9 @@ } // namespace +constexpr std::array<int, 7> + AppListPresenterImpl::kIdsOfContainersThatWontHideAppList; + AppListPresenterImpl::AppListPresenterImpl(AppListControllerImpl* controller) : controller_(controller) { DCHECK(controller_); @@ -549,7 +553,8 @@ // change since the app list is still visible for the most part. const bool gained_focus_hides_app_list = gained_focus_container_id != kShellWindowId_Invalid && - ShouldCloseAppListForFocusInContainer(gained_focus_container_id); + !base::Contains(kIdsOfContainersThatWontHideAppList, + gained_focus_container_id); const bool app_list_gained_focus = applist_window->Contains(gained_focus) || applist_container->Contains(gained_focus);
diff --git a/ash/app_list/app_list_presenter_impl.h b/ash/app_list/app_list_presenter_impl.h index 125b3105..21af0018 100644 --- a/ash/app_list/app_list_presenter_impl.h +++ b/ash/app_list/app_list_presenter_impl.h
@@ -14,6 +14,7 @@ #include "ash/ash_export.h" #include "ash/public/cpp/pagination/pagination_model_observer.h" #include "ash/public/cpp/shelf_types.h" +#include "ash/public/cpp/shell_window_ids.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_layout_manager_observer.h" #include "base/callback.h" @@ -46,6 +47,15 @@ public display::DisplayObserver, public ShelfLayoutManagerObserver { public: + static constexpr std::array<int, 7> kIdsOfContainersThatWontHideAppList = { + kShellWindowId_AppListContainer, + kShellWindowId_HomeScreenContainer, + kShellWindowId_MenuContainer, + kShellWindowId_PowerMenuContainer, + kShellWindowId_SettingBubbleContainer, + kShellWindowId_ShelfBubbleContainer, + kShellWindowId_ShelfContainer}; + // Callback which fills out the passed settings object. Used by // UpdateYPositionAndOpacityForHomeLauncher so different callers can do // similar animations with different settings.
diff --git a/ash/app_list/app_list_presenter_impl_unittest.cc b/ash/app_list/app_list_presenter_impl_unittest.cc index 14549d0..8e2512a 100644 --- a/ash/app_list/app_list_presenter_impl_unittest.cc +++ b/ash/app_list/app_list_presenter_impl_unittest.cc
@@ -177,7 +177,7 @@ shelf_layout_manager->GetShelfBackgroundType()); HotseatWidget* hotseat = GetPrimaryShelf()->hotseat_widget(); - for (int id : kContainersThatWontHideAppListOnFocus) { + for (int id : AppListPresenterImpl::kIdsOfContainersThatWontHideAppList) { // Create a widget with a specific container id and make sure that the // kHomeLauncher background is still shown. std::unique_ptr<views::Widget> widget = CreateTestWidget(nullptr, id);
diff --git a/ash/app_list/app_list_test_api.cc b/ash/app_list/app_list_test_api.cc index 1cd795b9..d4e3c53e 100644 --- a/ash/app_list/app_list_test_api.cc +++ b/ash/app_list/app_list_test_api.cc
@@ -29,7 +29,6 @@ #include "ash/app_list/views/paged_apps_grid_view.h" #include "ash/app_list/views/scrollable_apps_grid_view.h" #include "ash/constants/ash_features.h" -#include "ash/public/cpp/shell_window_ids.h" #include "ash/shell.h" #include "ash/test/layer_animation_stopped_waiter.h" #include "base/callback.h"
diff --git a/ash/components/arc/mojom/file_system.mojom b/ash/components/arc/mojom/file_system.mojom index e4a67617..d1ba40a 100644 --- a/ash/components/arc/mojom/file_system.mojom +++ b/ash/components/arc/mojom/file_system.mojom
@@ -476,7 +476,8 @@ [MinVersion=14] RequestFileRemovalScan@20(array<string> directory_paths); // DEPRECATED. Use OpenUrlsWithPermissionAndWindowInfo() instead. - [MinVersion=8] OpenUrlsWithPermission@11(OpenUrlsRequest request) => (); + [MinVersion=8] DEPRECATED_OpenUrlsWithPermission@11(OpenUrlsRequest request) + => (); // Opens URLs by sending an intent to the specified activity with a specific // launch window info.
diff --git a/ash/components/arc/test/fake_file_system_instance.cc b/ash/components/arc/test/fake_file_system_instance.cc index f5c1f8a..1a3f80e 100644 --- a/ash/components/arc/test/fake_file_system_instance.cc +++ b/ash/components/arc/test/fake_file_system_instance.cc
@@ -714,9 +714,9 @@ RequestMediaScan(paths); } -void FakeFileSystemInstance::OpenUrlsWithPermission( +void FakeFileSystemInstance::DEPRECATED_OpenUrlsWithPermission( mojom::OpenUrlsRequestPtr request, - OpenUrlsWithPermissionCallback callback) { + DEPRECATED_OpenUrlsWithPermissionCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); handled_url_requests_.emplace_back(std::move(request)); } @@ -724,7 +724,7 @@ void FakeFileSystemInstance::OpenUrlsWithPermissionAndWindowInfo( mojom::OpenUrlsRequestPtr request, mojom::WindowInfoPtr window_info, - OpenUrlsWithPermissionCallback callback) { + DEPRECATED_OpenUrlsWithPermissionCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); handled_url_requests_.emplace_back(std::move(request)); }
diff --git a/ash/components/arc/test/fake_file_system_instance.h b/ash/components/arc/test/fake_file_system_instance.h index e39b1ec..3d62231 100644 --- a/ash/components/arc/test/fake_file_system_instance.h +++ b/ash/components/arc/test/fake_file_system_instance.h
@@ -354,12 +354,13 @@ void RequestFileRemovalScan( const std::vector<std::string>& directory_paths) override; void ReindexDirectory(const std::string& directory_path) override; - void OpenUrlsWithPermission(mojom::OpenUrlsRequestPtr request, - OpenUrlsWithPermissionCallback callback) override; + void DEPRECATED_OpenUrlsWithPermission( + mojom::OpenUrlsRequestPtr request, + DEPRECATED_OpenUrlsWithPermissionCallback callback) override; void OpenUrlsWithPermissionAndWindowInfo( mojom::OpenUrlsRequestPtr request, mojom::WindowInfoPtr window_info, - OpenUrlsWithPermissionCallback callback) override; + DEPRECATED_OpenUrlsWithPermissionCallback callback) override; private: // A pair of an authority and a document ID which identifies the location
diff --git a/ash/projector/projector_annotation_tray.cc b/ash/projector/projector_annotation_tray.cc index 918c58d..6fd1b16 100644 --- a/ash/projector/projector_annotation_tray.cc +++ b/ash/projector/projector_annotation_tray.cc
@@ -96,19 +96,29 @@ ProjectorAnnotationTray::~ProjectorAnnotationTray() = default; bool ProjectorAnnotationTray::PerformAction(const ui::Event& event) { - if (bubble_) { - CloseBubble(); - } else { - if (GetCurrentTool() == kToolNone) { - ShowBubble(); - } else { - DeactivateActiveTool(); - } - } - + ToggleAnnotator(); return true; } +void ProjectorAnnotationTray::OnMouseEvent(ui::MouseEvent* event) { + if (event->type() != ui::ET_MOUSE_PRESSED) { + return; + } + if (event->IsRightMouseButton()) { + ShowBubble(); + } else if (event->IsLeftMouseButton()) { + ToggleAnnotator(); + } +} + +void ProjectorAnnotationTray::OnGestureEvent(ui::GestureEvent* event) { + if (event->details().type() == ui::ET_GESTURE_LONG_PRESS) { + ShowBubble(); + } else if (event->details().type() == ui::ET_GESTURE_TAP) { + ToggleAnnotator(); + } +} + void ProjectorAnnotationTray::ClickedOutsideBubble() { CloseBubble(); } @@ -139,10 +149,6 @@ DCHECK(tray_container()); - // There may still be an active tool if show bubble was called from an - // accelerator. - DeactivateActiveTool(); - TrayBubbleView::InitParams init_params; init_params.delegate = this; init_params.parent_window = GetBubbleWindowContainer(); @@ -230,11 +236,28 @@ UpdateIcon(); } +void ProjectorAnnotationTray::ToggleAnnotator() { + if (GetCurrentTool() == kToolNone) { + EnableAnnotatorTool(); + } else { + DeactivateActiveTool(); + } + if (bubble_) { + CloseBubble(); + } + UpdateIcon(); +} + +void ProjectorAnnotationTray::EnableAnnotatorTool() { + auto* controller = Shell::Get()->projector_controller(); + DCHECK(controller); + controller->OnMarkerPressed(); +} + void ProjectorAnnotationTray::DeactivateActiveTool() { auto* controller = Shell::Get()->projector_controller(); DCHECK(controller); controller->ResetTools(); - UpdateIcon(); } void ProjectorAnnotationTray::UpdateIcon() { @@ -243,14 +266,13 @@ GetIconForTool(tool), AshColorProvider::Get()->GetContentLayerColor( AshColorProvider::ContentLayerType::kIconColorPrimary))); + SetIsActive(GetCurrentTool() == kToolNone); } void ProjectorAnnotationTray::OnPenColorPressed(SkColor color) { // TODO(b/201664243) Pass the color for the marker. - auto* projector_controller = ProjectorControllerImpl::Get(); - DCHECK(projector_controller); - projector_controller->OnMarkerPressed(); CloseBubble(); + UpdateIcon(); } } // namespace ash
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h index cc32db7..2019b196 100644 --- a/ash/projector/projector_annotation_tray.h +++ b/ash/projector/projector_annotation_tray.h
@@ -32,10 +32,15 @@ void ShowBubble() override; TrayBubbleView* GetBubbleView() override; views::Widget* GetBubbleWidget() const override; + void OnMouseEvent(ui::MouseEvent* event) override; + void OnGestureEvent(ui::GestureEvent* event) override; void OnThemeChanged() override; private: - // Deactives any annotation tool that is currently enabled and update the UI. + void ToggleAnnotator(); + void EnableAnnotatorTool(); + // Deactivates any annotation tool that is currently enabled and updates the + // UI. void DeactivateActiveTool(); // Updates the icon in the status area.
diff --git a/ash/public/cpp/shell_window_ids.cc b/ash/public/cpp/shell_window_ids.cc index 8fa18c2..fb5a564 100644 --- a/ash/public/cpp/shell_window_ids.cc +++ b/ash/public/cpp/shell_window_ids.cc
@@ -46,10 +46,6 @@ } // namespace -bool ShouldCloseAppListForFocusInContainer(int id) { - return !base::Contains(kContainersThatWontHideAppListOnFocus, id); -} - std::vector<int> GetActivatableShellWindowIds() { std::vector<int> ids(kPreDesksActivatableContainersIds.begin(), kPreDesksActivatableContainersIds.end());
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h index b287371..a5c387b 100644 --- a/ash/public/cpp/shell_window_ids.h +++ b/ash/public/cpp/shell_window_ids.h
@@ -220,22 +220,10 @@ // A list of system modal container IDs. The order of the list is important that // the more restrictive container appears before the less restrictive ones. -inline constexpr int kSystemModalContainerIds[] = { +constexpr int kSystemModalContainerIds[] = { kShellWindowId_LockSystemModalContainer, kShellWindowId_SystemModalContainer}; -// Normally if a window gains focus the app list will be closed. Windows in -// these containers are exceptions to that rule. -inline constexpr int kContainersThatWontHideAppListOnFocus[] = { - kShellWindowId_AppListContainer, kShellWindowId_HomeScreenContainer, - kShellWindowId_MenuContainer, kShellWindowId_PowerMenuContainer, - kShellWindowId_SettingBubbleContainer, kShellWindowId_ShelfBubbleContainer, - kShellWindowId_ShelfContainer}; - -// Returns true if `id` is a shell window container id such that a window in -// that container gaining focus should close the app list. -ASH_PUBLIC_EXPORT bool ShouldCloseAppListForFocusInContainer(int id); - // Returns the list of container ids of containers which may contain windows // that need to be activated. this list is ordered by the activation order; that // is, windows in containers appearing earlier in the list are activated before
diff --git a/ash/search_box/search_box_view_base.cc b/ash/search_box/search_box_view_base.cc index d1c96146..355609d 100644 --- a/ash/search_box/search_box_view_base.cc +++ b/ash/search_box/search_box_view_base.cc
@@ -107,6 +107,13 @@ // OnPaintBackground(); SetInstallFocusRingOnFocus(false); + // Inkdrop only on click. + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); + SetHasInkDropActionOnClick(true); + views::InkDrop::UseInkDropForFloodFillRipple(views::InkDrop::Get(this), + /*highlight_on_hover=*/false); + UpdateInkDropColors(); + SetPaintToLayer(); layer()->SetFillsBoundsOpaquely(false); @@ -144,6 +151,11 @@ SchedulePaint(); } + void OnThemeChanged() override { + views::View::OnThemeChanged(); + UpdateInkDropColors(); + } + void set_is_showing(bool is_showing) { is_showing_ = is_showing; } bool is_showing() { return is_showing_; } @@ -153,6 +165,18 @@ // Whether the button is showing/shown or hiding/hidden. bool is_showing_ = false; + void UpdateInkDropColors() { + SkColor search_box_card_background_color = + AppListColorProvider::Get()->GetSearchBoxCardBackgroundColor(); + + views::InkDrop::Get(this)->SetBaseColor( + AppListColorProvider::Get()->GetInkDropBaseColor( + search_box_card_background_color)); + views::InkDrop::Get(this)->SetVisibleOpacity( + AppListColorProvider::Get()->GetInkDropOpacity( + search_box_card_background_color)); + } + // views::View overrides: void OnPaintBackground(gfx::Canvas* canvas) override { if (HasFocus()) {
diff --git a/ash/shelf/shelf_controller.cc b/ash/shelf/shelf_controller.cc index c3824b9..65c7481 100644 --- a/ash/shelf/shelf_controller.cc +++ b/ash/shelf/shelf_controller.cc
@@ -238,7 +238,7 @@ void ShelfController::OnAppUpdate(const apps::AppUpdate& update) { if (update.HasBadgeChanged() && notification_badging_pref_enabled_.value_or(false)) { - bool has_badge = update.HasBadge() == apps::mojom::OptionalBool::kTrue; + bool has_badge = update.HasBadge().value_or(false); model_.UpdateItemNotification(update.AppId(), has_badge); } } @@ -256,7 +256,7 @@ // Update the notification badge indicator for the newly added shelf item. cache_->ForOneApp(app_id, [this](const apps::AppUpdate& update) { - bool has_badge = update.HasBadge() == apps::mojom::OptionalBool::kTrue; + bool has_badge = update.HasBadge().value_or(false); model_.UpdateItemNotification(update.AppId(), has_badge); }); } @@ -276,10 +276,9 @@ if (cache_) { cache_->ForEachApp([this](const apps::AppUpdate& update) { // Set the app notification badge hidden when the pref is disabled. - bool has_badge = - notification_badging_pref_enabled_.value() - ? (update.HasBadge() == apps::mojom::OptionalBool::kTrue) - : false; + bool has_badge = notification_badging_pref_enabled_.value() + ? update.HasBadge().value_or(false) + : false; model_.UpdateItemNotification(update.AppId(), has_badge); });
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc index e6d6fee5..84ea3e0 100644 --- a/ash/system/accessibility/tray_accessibility.cc +++ b/ash/system/accessibility/tray_accessibility.cc
@@ -603,14 +603,15 @@ } } -void AccessibilityDetailedView::OnSodaInstallSucceeded() { - speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance(); - if (!soda_installer->IsSodaInstalled(GetDictationLocale())) +// SodaInstaller::Observer: +void AccessibilityDetailedView::OnSodaInstalled( + speech::LanguageCode language_code) { + if (language_code != GetDictationLocale()) return; - // Only show the success message if both the SODA binary and the language pack + // Show the success message if both the SODA binary and the language pack // matching the Dictation locale have been downloaded. - soda_installer->RemoveObserver(this); + speech::SodaInstaller::GetInstance()->RemoveObserver(this); AccessibilityControllerImpl* controller = Shell::Get()->accessibility_controller(); if (dictation_view_ && controller->IsDictationSettingVisibleInTray()) { @@ -619,16 +620,34 @@ } } -void AccessibilityDetailedView::OnSodaInstallProgress( - int progress, +void AccessibilityDetailedView::OnSodaError( speech::LanguageCode language_code) { - // TODO(https://crbug.com/1266491): Ensure we use combined progress instead - // of just the language pack progress. - if (language_code != GetDictationLocale()) + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLocale()) { return; + } - // Only show the progress message if this applies to the language pack - // matching the Dictation locale. + // Show the failed message if either the Dictation locale failed or the SODA + // binary failed (encoded by LanguageCode::kNone). + speech::SodaInstaller::GetInstance()->RemoveObserver(this); + AccessibilityControllerImpl* controller = + Shell::Get()->accessibility_controller(); + if (dictation_view_ && controller->IsDictationSettingVisibleInTray()) { + dictation_view_->SetSubText(l10n_util::GetStringUTF16( + IDS_ASH_ACCESSIBILITY_DICTATION_SETTING_SUBTITLE_SODA_DOWNLOAD_ERROR)); + } +} + +void AccessibilityDetailedView::OnSodaProgress( + speech::LanguageCode language_code, + int progress) { + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLocale()) { + return; + } + + // Only show the progress message if this applies to the SODA binary (encoded + // by LanguageCode::kNone) or the language pack matching the Dictation locale. AccessibilityControllerImpl* controller = Shell::Get()->accessibility_controller(); if (dictation_view_ && controller->IsDictationSettingVisibleInTray()) { @@ -638,47 +657,6 @@ } } -void AccessibilityDetailedView::OnSodaInstallFailed( - speech::LanguageCode language_code) { - if (language_code == speech::LanguageCode::kNone || - language_code == GetDictationLocale()) { - // Show the failed message if either the Dictation locale failed or the SODA - // binary failed (encoded by LanguageCode::kNone). - speech::SodaInstaller::GetInstance()->RemoveObserver(this); - AccessibilityControllerImpl* controller = - Shell::Get()->accessibility_controller(); - if (dictation_view_ && controller->IsDictationSettingVisibleInTray()) { - dictation_view_->SetSubText(l10n_util::GetStringUTF16( - IDS_ASH_ACCESSIBILITY_DICTATION_SETTING_SUBTITLE_SODA_DOWNLOAD_ERROR)); - } - } -} - -// SodaInstaller::Observer: -void AccessibilityDetailedView::OnSodaInstalled() { - OnSodaInstallSucceeded(); -} - -void AccessibilityDetailedView::OnSodaLanguagePackInstalled( - speech::LanguageCode language_code) { - OnSodaInstallSucceeded(); -} - -void AccessibilityDetailedView::OnSodaError() { - OnSodaInstallFailed(speech::LanguageCode::kNone); -} - -void AccessibilityDetailedView::OnSodaLanguagePackError( - speech::LanguageCode language_code) { - OnSodaInstallFailed(language_code); -} - -void AccessibilityDetailedView::OnSodaLanguagePackProgress( - int language_progress, - speech::LanguageCode language_code) { - OnSodaInstallProgress(language_progress, language_code); -} - void AccessibilityDetailedView::SetDictationViewSubtitleTextForTesting( std::u16string text) { dictation_view_->SetSubText(text);
diff --git a/ash/system/accessibility/tray_accessibility.h b/ash/system/accessibility/tray_accessibility.h index f5f4cb8e..b280c56 100644 --- a/ash/system/accessibility/tray_accessibility.h +++ b/ash/system/accessibility/tray_accessibility.h
@@ -75,18 +75,12 @@ void AppendAccessibilityList(); void UpdateSodaInstallerObserverStatus(); - void OnSodaInstallSucceeded(); - void OnSodaInstallProgress(int progress, speech::LanguageCode language_code); - void OnSodaInstallFailed(speech::LanguageCode language_code); // SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaLanguagePackInstalled(speech::LanguageCode language_code) override; - void OnSodaError() override; - void OnSodaLanguagePackError(speech::LanguageCode language_code) override; - void OnSodaProgress(int combined_progress) override {} - void OnSodaLanguagePackProgress(int language_progress, - speech::LanguageCode language_code) override; + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaError(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int combined_progress) override; void SetDictationViewSubtitleTextForTesting(std::u16string text); std::u16string GetDictationViewSubtitleTextForTesting();
diff --git a/ash/system/accessibility/tray_accessibility_unittest.cc b/ash/system/accessibility/tray_accessibility_unittest.cc index 3fcdd27..8439b19 100644 --- a/ash/system/accessibility/tray_accessibility_unittest.cc +++ b/ash/system/accessibility/tray_accessibility_unittest.cc
@@ -715,6 +715,7 @@ } speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; } + speech::LanguageCode fr_fr() { return speech::LanguageCode::kFrFr; } void SetDictationViewSubtitleText(std::u16string text) { detailed_menu()->SetDictationViewSubtitleTextForTesting(text); @@ -738,23 +739,20 @@ // correct language pack before doing anything. soda_installer()->NotifySodaInstalledForTesting(); EXPECT_EQ(kInitialDictationViewSubtitleText, GetDictationViewSubtitleText()); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_EQ(kInitialDictationViewSubtitleText, GetDictationViewSubtitleText()); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting( - speech::LanguageCode::kFrFr); + soda_installer()->NotifySodaInstalledForTesting(fr_fr()); EXPECT_EQ(kSodaDownloaded, GetDictationViewSubtitleText()); } // Ensures we only notify the user of progress for the language pack matching // the Dictation locale. TEST_F(TrayAccessibilitySodaTest, OnSodaProgressNotification) { - // Do not give updates for the SODA binary. - soda_installer()->NotifySodaDownloadProgressForTesting(50); + soda_installer()->NotifySodaProgressForTesting(50, fr_fr()); EXPECT_EQ(kInitialDictationViewSubtitleText, GetDictationViewSubtitleText()); - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting( - 50, speech::LanguageCode::kFrFr); - EXPECT_EQ(kInitialDictationViewSubtitleText, GetDictationViewSubtitleText()); - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(50, en_us()); + soda_installer()->NotifySodaProgressForTesting(50); + EXPECT_EQ(kSodaInProgress, GetDictationViewSubtitleText()); + soda_installer()->NotifySodaProgressForTesting(50, en_us()); EXPECT_EQ(kSodaInProgress, GetDictationViewSubtitleText()); } @@ -768,10 +766,9 @@ TEST_F(TrayAccessibilitySodaTest, SodaLanguageErrorNotification) { // Do nothing if the failed language pack is different than the Dictation // locale. - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting( - speech::LanguageCode::kFrFr); + soda_installer()->NotifySodaErrorForTesting(fr_fr()); EXPECT_EQ(kInitialDictationViewSubtitleText, GetDictationViewSubtitleText()); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); EXPECT_EQ(kSodaFailed, GetDictationViewSubtitleText()); }
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc index 48d9477..c1fda179 100644 --- a/ash/system/eche/eche_tray.cc +++ b/ash/system/eche/eche_tray.cc
@@ -102,44 +102,7 @@ return; } - TrayBubbleView::InitParams init_params; - init_params.delegate = this; - init_params.parent_window = GetBubbleWindowContainer(); - init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect; - init_params.anchor_rect = GetBubbleAnchor()->GetAnchorBoundsInScreen(); - init_params.insets = GetTrayBubbleInsets(); - init_params.shelf_alignment = shelf()->alignment(); - // TODO(nayebi): get the width relative to the screen size - init_params.preferred_width = 400; - init_params.close_on_deactivate = false; - init_params.has_shadow = false; - init_params.translucent = true; - init_params.reroute_event_handler = false; - init_params.corner_radius = kTrayItemCornerRadius; - - auto bubble_view = std::make_unique<TrayBubbleView>(init_params); - bubble_view->SetCanActivate(true); - bubble_view->SetBorder(views::CreateEmptyBorder(kBubblePadding)); - - auto* header_view = bubble_view->AddChildView(CreateBubbleHeaderView()); - // The layer is needed to draw the header non-opaquely that is needed to - // match the phone hub behavior. - header_view->SetPaintToLayer(); - header_view->layer()->SetFillsBoundsOpaquely(false); - - AshWebView::InitParams params; - auto web_view = AshWebViewFactory::Get()->Create(params); - // TODO(nayebi): Use GetDefaultBoundsForEche() - web_view->SetPreferredSize(gfx::Size(400, 600)); - if (!url_.is_empty()) - web_view->Navigate(url_); - bubble_view->AddChildView(std::move(web_view)); - - bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view.release(), - /*event_handling=*/false); - - SetIsActive(true); - bubble_->GetBubbleView()->UpdateBubble(); + InitBubble(); // TODO(nayebi): Add metric updates. } @@ -218,6 +181,46 @@ bubble_->GetBubbleWidget()->Hide(); } +void EcheTray::InitBubble() { + TrayBubbleView::InitParams init_params; + init_params.delegate = this; + init_params.parent_window = GetBubbleWindowContainer(); + init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect; + init_params.anchor_rect = GetBubbleAnchor()->GetAnchorBoundsInScreen(); + init_params.insets = GetTrayBubbleInsets(); + init_params.shelf_alignment = shelf()->alignment(); + // TODO(nayebi): get the width relative to the screen size + init_params.preferred_width = 400; + init_params.close_on_deactivate = false; + init_params.has_shadow = false; + init_params.translucent = true; + init_params.reroute_event_handler = false; + init_params.corner_radius = kTrayItemCornerRadius; + + auto bubble_view = std::make_unique<TrayBubbleView>(init_params); + bubble_view->SetCanActivate(true); + bubble_view->SetBorder(views::CreateEmptyBorder(kBubblePadding)); + + auto* header_view = bubble_view->AddChildView(CreateBubbleHeaderView()); + // The layer is needed to draw the header non-opaquely that is needed to + // match the phone hub behavior. + header_view->SetPaintToLayer(); + header_view->layer()->SetFillsBoundsOpaquely(false); + + auto web_view = AshWebViewFactory::Get()->Create(AshWebView::InitParams()); + // TODO(nayebi): Use GetDefaultBoundsForEche() + web_view->SetPreferredSize(gfx::Size(400, 600)); + if (!url_.is_empty()) + web_view->Navigate(url_); + bubble_view->AddChildView(std::move(web_view)); + + bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view.release(), + /*event_handling=*/false); + + SetIsActive(true); + bubble_->GetBubbleView()->UpdateBubble(); +} + void EcheTray::UpdateVisibility() { SetVisiblePreferred(true); }
diff --git a/ash/system/eche/eche_tray.h b/ash/system/eche/eche_tray.h index 394e710f..3151de0 100644 --- a/ash/system/eche/eche_tray.h +++ b/ash/system/eche/eche_tray.h
@@ -75,6 +75,11 @@ void HideBubble(); + // Set up the params and init the bubble. + // Note: This function makes the bubble active and makes the + // TrayBackgroundView's background inkdrop activate. + void InitBubble(); + // Test helpers TrayBubbleWrapper* get_bubble_wrapper_for_test() { return bubble_.get(); }
diff --git a/ash/system/eche/eche_tray_unittest.cc b/ash/system/eche/eche_tray_unittest.cc index 5d4b700e..b4d6660 100644 --- a/ash/system/eche/eche_tray_unittest.cc +++ b/ash/system/eche/eche_tray_unittest.cc
@@ -95,4 +95,31 @@ eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); } +TEST_F(EcheTrayTest, EcheTrayCreatesBubbleButHideFirst) { + // Verify the eche tray button is not active, and the eche tray bubble + // is not shown initially. + EXPECT_FALSE(eche_tray()->is_active()); + EXPECT_FALSE(eche_tray()->get_bubble_wrapper_for_test()); + + // Allow us to create the bubble but it is not visible until we need this + // bubble to show up. + eche_tray()->SetUrl(GURL("http://google.com")); + eche_tray()->InitBubble(); + eche_tray()->HideBubble(); + + EXPECT_FALSE(eche_tray()->is_active()); + EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test()); + EXPECT_FALSE( + eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); + + // Request this bubble to show up. + eche_tray()->ShowBubble(); + // Wait for the tray bubble widget to open. + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(eche_tray()->is_active()); + EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test()); + EXPECT_TRUE( + eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); +} + } // namespace ash
diff --git a/ash/system/message_center/ash_notification_view_unittest.cc b/ash/system/message_center/ash_notification_view_unittest.cc index 518f2ac..8ae1675d 100644 --- a/ash/system/message_center/ash_notification_view_unittest.cc +++ b/ash/system/message_center/ash_notification_view_unittest.cc
@@ -20,6 +20,7 @@ #include "ash/test/ash_test_base.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "base/time/time.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" @@ -192,10 +193,11 @@ EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor)); } - // Ensure there is one more frame presented after animation finishes to - // allow animation throughput data to be passed from cc to ui. - std::ignore = - ui::WaitForNextFrameToBePresented(compositor, base::Milliseconds(200)); + // Force a frame then wait, ensuring there is one more frame presented after + // animation finishes to allow animation throughput data to be passed from + // cc to ui. + compositor->ScheduleFullRedraw(); + EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor)); // Smoothness should be recorded. histograms.ExpectTotalCount(animation_histogram_name, data_point_count); @@ -665,8 +667,7 @@ "Ash.NotificationView.ActionsRow.FadeIn.AnimationSmoothness"); } -TEST_F(AshNotificationViewTest, - DISABLED_ImageExpandCollapseAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, ImageExpandCollapseAnimationsRecordSmoothness) { // Enable animations. ui::ScopedAnimationDurationScaleMode duration( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); @@ -731,8 +732,7 @@ "ScaleAndTranslate.AnimationSmoothness"); } -TEST_F(AshNotificationViewTest, - DISABLED_GroupExpandCollapseAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, GroupExpandCollapseAnimationsRecordSmoothness) { base::HistogramTester histograms; // Enable animations. @@ -839,8 +839,7 @@ "Ash.NotificationView.InlineReply.FadeOut.AnimationSmoothness"); } -TEST_F(AshNotificationViewTest, - DISABLED_InlineSettingsAnimationsRecordSmoothness) { +TEST_F(AshNotificationViewTest, InlineSettingsAnimationsRecordSmoothness) { base::HistogramTester histograms; // Enable animations.
diff --git a/ash/webui/camera_app_ui/resources/.eslintrc.js b/ash/webui/camera_app_ui/resources/.eslintrc.js index 5e2227b..fece873d 100644 --- a/ash/webui/camera_app_ui/resources/.eslintrc.js +++ b/ash/webui/camera_app_ui/resources/.eslintrc.js
@@ -534,6 +534,11 @@ message: 'Don\'t use "Interface" as identifier suffix. ' + '(go/tsstyle#naming-style)', }, + // Disallow forEach. (go/tsstyle#iterating-containers) + { + selector: 'CallExpression[callee.property.name="forEach"]', + message: 'forEach are not allowed. (go/tsstyle#iterating-containers)', + }, ], '@typescript-eslint/naming-convention': [
diff --git a/ash/webui/camera_app_ui/resources/js/animation.ts b/ash/webui/camera_app_ui/resources/js/animation.ts index f762e86..2f32d14 100644 --- a/ash/webui/camera_app_ui/resources/js/animation.ts +++ b/ash/webui/camera_app_ui/resources/js/animation.ts
@@ -39,7 +39,9 @@ */ async function doCancel({el, onChild}: {el: HTMLElement, onChild: boolean}): Promise<void> { - getAnimations({el, onChild}).forEach((a) => a.cancel()); + for (const a of getAnimations({el, onChild})) { + a.cancel(); + } await getQueueFor(el).flush(); }
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts b/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts index ff1f874..d47d6724 100644 --- a/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts +++ b/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts
@@ -18,16 +18,16 @@ export class Camera3DeviceInfo { readonly deviceId: string; - readonly videoResols: ResolutionList = []; + readonly videoResolutions: ResolutionList = []; readonly videoMaxFps: MaxFpsInfo = {}; /** * @param deviceInfo Information of the video device. * @param facing Camera facing of the video device. - * @param photoResols Supported available photo resolutions + * @param photoResolutions Supported available photo resolutions * of the video device. - * @param videoResolFpses Supported available video + * @param videoResolutionFpses Supported available video * resolutions and maximal capture fps of the video device. * @param fpsRanges Supported fps ranges of the video device. * @param supportPTZ Is supported PTZ controls. @@ -35,18 +35,20 @@ constructor( deviceInfo: MediaDeviceInfo, readonly facing: Facing, - readonly photoResols: ResolutionList, - videoResolFpses: VideoConfig[], + readonly photoResolutions: ResolutionList, + videoResolutionFpses: VideoConfig[], readonly fpsRanges: FpsRangeList, readonly supportPTZ: boolean, ) { this.deviceId = deviceInfo.deviceId; - videoResolFpses.filter(({maxFps}) => maxFps >= 24) - .forEach(({width, height, maxFps}) => { - const r = new Resolution(width, height); - this.videoResols.push(r); - this.videoMaxFps[r.toString()] = maxFps; - }); + for (const {width, height, maxFps} of videoResolutionFpses) { + if (maxFps < 24) { + continue; + } + const r = new Resolution(width, height); + this.videoResolutions.push(r); + this.videoMaxFps[r.toString()] = maxFps; + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts index 1b662b98..4898e79 100644 --- a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts +++ b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts
@@ -3,6 +3,7 @@ // found in the LICENSE file. import { + assert, assertExists, assertInstanceof, assertString, @@ -101,7 +102,7 @@ constructor( private readonly perfLogger: PerfLogger, - defaultFacing: Facing, + defaultFacing: Facing|null, modeConstraints: ModeConstraints, ) { this.preview = new Preview(async () => { @@ -143,7 +144,7 @@ } private getDeviceId(): string { - return assertString(this.scheduler.reconfigurer.config.deviceId); + return assertString(this.scheduler.reconfigurer.config?.deviceId); } getPreviewVideo(): PreviewVideo { @@ -299,6 +300,7 @@ } if (devices.length > 0) { index = (index + 1) % devices.length; + assert(this.scheduler.reconfigurer.config !== null); this.scheduler.reconfigurer.config.deviceId = devices[index].deviceId; } }); @@ -312,6 +314,7 @@ switchMode(mode: Mode): Promise<boolean>|null { return this.tryReconfigure(() => { + assert(this.scheduler.reconfigurer.config !== null); this.scheduler.reconfigurer.config.mode = mode; }); } @@ -326,6 +329,7 @@ // Changing the configure of the camera not currently opened, thus no // reconfiguration are required. preferer.changePreferredResolution(deviceId, resolution); + assert(this.scheduler.reconfigurer.config !== null); return this.onUpdateConfig(this.scheduler.reconfigurer.config) .then(() => true); }
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts b/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts index ac21311..ee0c1e4 100644 --- a/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts +++ b/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts
@@ -32,6 +32,7 @@ import {StreamManager} from './stream_manager.js'; import { CameraConfig, + CameraConfigCandidate, CameraInfo, CameraViewUI, ModeConstraints, @@ -47,7 +48,7 @@ } export interface EventListener { - onTryingNewConfig(config: CameraConfig): void; + onTryingNewConfig(config: CameraConfigCandidate): void; onUpdateConfig(config: CameraConfig): Promise<void>; onUpdateCapability(cameraInfo: CameraInfo): void; } @@ -59,7 +60,11 @@ /** * Preferred configuration. */ - config: CameraConfig; + config: CameraConfig|null = null; + + private readonly initialFacing: Facing|null; + + private readonly initialMode: Mode; private shouldSuspend = false; @@ -68,11 +73,11 @@ private readonly modes: Modes, private readonly listener: EventListener, private readonly modeConstraints: ModeConstraints, - facing: Facing, + facing: Facing|null, ) { - const mode = util.assertEnumVariant( + this.initialMode = util.assertEnumVariant( Mode, this.modeConstraints.exact ?? this.modeConstraints.default); - this.config = {deviceId: null, facing, mode}; + this.initialFacing = facing; } setShouldSuspend(value: boolean) { @@ -100,17 +105,15 @@ devices = cameraInfo.devicesInfo; } - const preferredFacing = this.config.facing === Facing.NOT_SET ? - util.getDefaultFacing() : - this.config.facing; + const preferredFacing = + this.config?.facing ?? this.initialFacing ?? util.getDefaultFacing(); // Put the selected video device id first. const sorted = devices.map((device) => device.deviceId).sort((a, b) => { if (a === b) { return 0; } - if (this.config.deviceId !== null ? - a === this.config.deviceId : - (facings && facings[a] === preferredFacing)) { + if (this.config !== null ? a === this.config.deviceId : + (facings && facings[a] === preferredFacing)) { return -1; } return 1; @@ -124,7 +127,8 @@ await this.modes.isSupported(this.modeConstraints.exact, deviceId)); return [this.modeConstraints.exact]; } - return this.modes.getModeCandidates(deviceId, this.config.mode); + return this.modes.getModeCandidates( + deviceId, this.config?.mode ?? this.initialMode); } private async * @@ -134,23 +138,24 @@ for (const deviceId of this.getDeviceIdCandidates(cameraInfo)) { for (const mode of await this.getModeCandidates(deviceId)) { - let resolCandidates; - let photoRs; + let resolutionCandidates; + let photoResolutions; if (deviceOperator !== null) { - resolCandidates = this.modes.getResolutionCandidates(mode, deviceId); - photoRs = await deviceOperator.getPhotoResolutions(deviceId); + resolutionCandidates = + this.modes.getResolutionCandidates(mode, deviceId); + photoResolutions = await deviceOperator.getPhotoResolutions(deviceId); } else { - resolCandidates = + resolutionCandidates = this.modes.getFakeResolutionCandidates(mode, deviceId); - photoRs = resolCandidates.map((c) => c.resolution); + photoResolutions = resolutionCandidates.map((c) => c.resolution); } - const maxResolution = photoRs.reduce( + const maxResolution = photoResolutions.reduce( (maxR, r) => r !== null && (maxR === null || r.area > maxR.area) ? r : maxR); for (const { resolution: captureResolution, previewCandidates, - } of resolCandidates) { + } of resolutionCandidates) { const videoSnapshotResolution = state.get(state.State.ENABLE_FULL_SIZED_VIDEO_SNAPSHOT) ? maxResolution : @@ -214,15 +219,13 @@ return false; } - const nextConfig: CameraConfig = { + this.listener.onTryingNewConfig({ deviceId: c.deviceId, - facing: (c.deviceId !== null ? - cameraInfo.getCamera3DeviceInfo(c.deviceId)?.facing : - null) ?? - Facing.NOT_SET, + facing: c.deviceId !== null ? + cameraInfo.getCamera3DeviceInfo(c.deviceId)?.facing ?? null : + null, mode: c.mode, - }; - this.listener.onTryingNewConfig(nextConfig); + }); this.modes.setCaptureParams( c.mode, c.constraints, c.captureResolution, c.videoSnapshotResolution); @@ -230,18 +233,20 @@ await this.modes.prepareDevice(); const factory = this.modes.getModeFactory(c.mode); const stream = await this.preview.open(c.constraints); - // For legacy linux VCD, the facing and device id can only be known + // For non-ChromeOS VCD, the facing and device id can only be known // after preview is actually opened. const facing = this.preview.getFacing(); - nextConfig.facing = facing; const deviceId = assertString(this.preview.getDeviceId()); - nextConfig.deviceId = deviceId; await this.checkEnablePTZ(c); factory.setPreviewVideo(this.preview.getVideo()); factory.setFacing(facing); await this.modes.updateMode(factory, stream, facing, deviceId); - this.config = nextConfig; + this.config = { + deviceId, + facing, + mode: c.mode, + }; await this.listener.onUpdateConfig(this.config); return true; @@ -262,7 +267,7 @@ // We cannot get the camera facing from stream since it might // not be successfully opened. Therefore, we asked the camera // facing via Mojo API. - let facing = Facing.NOT_SET; + let facing: Facing|null = null; if (deviceOperator !== null) { facing = await deviceOperator.getCameraFacing(c.deviceId); } @@ -344,7 +349,7 @@ private readonly infoUpdater: DeviceInfoUpdater, private readonly listener: EventListener, preview: Preview, - defaultFacing: Facing, + defaultFacing: Facing|null, modeConstraints: ModeConstraints, ) { this.modes = new Modes(this.photoPreferrer, this.videoPreferrer);
diff --git a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts index 27d180f..175f398 100644 --- a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts +++ b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts
@@ -177,11 +177,11 @@ /** * Sorts prefer resolutions. * - * @param prefR Preferred resolution. + * @param prefResolution Preferred resolution. * @return Return compare function for comparing based on preferred * resolution. */ - protected getPreferResolutionSort(prefR: Resolution): + protected getPreferResolutionSort(prefResolution: Resolution): (c0: CaptureCandidate, c1: CaptureCandidate) => number { return ({resolution: r1}, {resolution: r2}) => { if (r1 === null || r2 === null) { @@ -191,19 +191,19 @@ return 0; } // Exactly the preferred resolution. - if (r1.equals(prefR)) { + if (r1.equals(prefResolution)) { return -1; } - if (r2.equals(prefR)) { + if (r2.equals(prefResolution)) { return 1; } // Aspect ratio same as preferred resolution. if (!r1.aspectRatioEquals(r2)) { - if (r1.aspectRatioEquals(prefR)) { + if (r1.aspectRatioEquals(prefResolution)) { return -1; } - if (r2.aspectRatioEquals(prefR)) { + if (r2.aspectRatioEquals(prefResolution)) { return 1; } } @@ -216,7 +216,7 @@ * * @return Ratio as key, all resolutions with that ratio as value. */ - protected groupResolutionRatio(rs: ResolutionList): + protected groupResolutionRatio(resolutions: ResolutionList): Map<number, ResolutionList> { const toSupportedPreviewRatio = (r: Resolution): number => { // Special aspect ratio mapping rule, see http://b/147986763. @@ -227,7 +227,7 @@ }; const result = new Map<number, ResolutionList>(); - for (const r of rs) { + for (const r of resolutions) { const ratio = toSupportedPreviewRatio(r); const ratios = result.get(ratio) ?? []; ratios.push(r); @@ -242,6 +242,11 @@ */ const SUPPORTED_CONSTANT_FPS = [30, 60]; +interface VideoPreviewResolutions { + videoResolutions: ResolutionList; + previewResolutions: ResolutionList; +} + /** * Controller for handling video resolution preference. */ @@ -269,8 +274,8 @@ * Maps from device id as key to video and preview resolutions of * same aspect ratio supported by that video device as value. */ - private deviceVideoPreviewResolutionMap = new Map< - string, Array<{videoRs: ResolutionList, previewRs: ResolutionList}>>(); + private deviceVideoPreviewResolutionMap = + new Map<string, VideoPreviewResolutions[]>(); constructor() { super(); @@ -311,8 +316,9 @@ if (!SUPPORTED_CONSTANT_FPS.includes(prefFps)) { return; } - SUPPORTED_CONSTANT_FPS.forEach( - (fps) => state.set(state.assertState(`fps-${fps}`), fps === prefFps)); + for (const fps of SUPPORTED_CONSTANT_FPS) { + state.set(state.assertState(`fps-${fps}`), fps === prefFps); + } const resolutionFpses = this.prefFpses[deviceId] || {}; resolutionFpses[resolution.toString()] = prefFps; this.prefFpses[deviceId] = resolutionFpses; @@ -343,35 +349,40 @@ this.supportedResolutions = new Map(); this.constFpsInfo = {}; - for (const {deviceId, videoResols, videoMaxFps, fpsRanges} of devices) { + for (const {deviceId, videoResolutions, videoMaxFps, fpsRanges} of + devices) { this.supportedResolutions.set( - deviceId, [...videoResols].sort((r1, r2) => r2.area - r1.area)); + deviceId, [...videoResolutions].sort((r1, r2) => r2.area - r1.area)); // Filter out preview resolution greater than 1920x1080 and 1600x1200. - const previewRatios = this.groupResolutionRatio(videoResols.filter( + const previewRatios = this.groupResolutionRatio(videoResolutions.filter( ({width, height}) => width <= 1920 && height <= 1200)); - const videoRatios = this.groupResolutionRatio(videoResols); - const pairedResolutions: - Array<{videoRs: ResolutionList, previewRs: ResolutionList}> = []; - for (const [ratio, videoRs] of videoRatios) { - const previewRs = previewRatios.get(ratio); - if (previewRs === undefined) { + const videoRatios = this.groupResolutionRatio(videoResolutions); + const pairedResolutions: VideoPreviewResolutions[] = []; + for (const [ratio, videoResolutions] of videoRatios) { + const previewResolutions = previewRatios.get(ratio); + if (previewResolutions === undefined) { continue; } - pairedResolutions.push({videoRs, previewRs}); + pairedResolutions.push({videoResolutions, previewResolutions}); } this.deviceVideoPreviewResolutionMap.set(deviceId, pairedResolutions); - const findResol = (width: number, height: number): Resolution|undefined => - videoResols.find((r) => r.width === width && r.height === height); - let prefR = this.getPrefResolution(deviceId) ?? findResol(1920, 1080) ?? - findResol(1280, 720) ?? new Resolution(0, -1); - if (findResol(prefR.width, prefR.height) === undefined) { - prefR = videoResols.reduce( + const findResolution = (width: number, height: number): Resolution| + undefined => videoResolutions.find( + (r) => r.width === width && r.height === height); + + let prefResolution = this.getPrefResolution(deviceId) ?? + findResolution(1920, 1080) ?? findResolution(1280, 720) ?? + new Resolution(0, -1); + + if (findResolution(prefResolution.width, prefResolution.height) === + undefined) { + prefResolution = videoResolutions.reduce( (maxR, r) => (maxR.area < r.area ? r : maxR), new Resolution(0, -1)); } - this.prefResolution.set(deviceId, prefR); + this.prefResolution.set(deviceId, prefResolution); const constFpses = fpsRanges.filter(({minFps, maxFps}) => minFps === maxFps) @@ -408,7 +419,8 @@ } private getMultiStreamSortedCandidates(deviceId: string): CaptureCandidate[] { - const prefR = this.getPrefResolution(deviceId) ?? new Resolution(0, -1); + const prefResolution = + this.getPrefResolution(deviceId) ?? new Resolution(0, -1); /** * Maps specified video resolution to object of resolution and all supported @@ -436,36 +448,35 @@ }; const toVideoCandidate = - ({videoRs, - previewRs}: {videoRs: ResolutionList, previewRs: ResolutionList}): - CaptureCandidate[] => { - let videoR = prefR; - if (!videoRs.some((r) => r.equals(prefR))) { - videoR = videoRs.reduce( - (videoR, r) => (r.width > videoR.width ? r : videoR)); - } + ({videoResolutions, + previewResolutions}: VideoPreviewResolutions): CaptureCandidate[] => { + let videoResolution = prefResolution; + if (!videoResolutions.some((r) => r.equals(prefResolution))) { + videoResolution = videoResolutions.reduce( + (videoR, r) => (r.width > videoR.width ? r : videoR)); + } - return getFpses(videoR).map( - ({fps}) => ({ - resolution: videoR, - previewCandidates: - this.sortPreview(previewRs, videoR) - .map(({width, height}) => ({ - deviceId, - audio: true, - video: { - frameRate: fps ? {exact: fps} : - {min: 20, ideal: 30}, - width, - height, - }, - })), - })); - }; + return getFpses(videoResolution) + .map(({fps}) => ({ + resolution: videoResolution, + previewCandidates: + this.sortPreview(previewResolutions, videoResolution) + .map(({width, height}) => ({ + deviceId, + audio: true, + video: { + frameRate: fps ? {exact: fps} : + {min: 20, ideal: 30}, + width, + height, + }, + })), + })); + }; return assertExists(this.deviceVideoPreviewResolutionMap.get(deviceId)) .flatMap(toVideoCandidate) - .sort(this.getPreferResolutionSort(prefR)); + .sort(this.getPreferResolutionSort(prefResolution)); } private getSortedCandidatesInternal(deviceId: string): CaptureCandidate[] { @@ -505,7 +516,8 @@ }, }); - const prefR = this.getPrefResolution(deviceId) ?? new Resolution(0, -1); + const prefResolution = + this.getPrefResolution(deviceId) ?? new Resolution(0, -1); return [...assertExists(this.supportedResolutions.get(deviceId))] .flatMap(getFpses) .map(({r, fps}) => ({ @@ -514,7 +526,7 @@ // to do video recording. previewCandidates: [toPreivewConstraints(r, fps)], })) - .sort(this.getPreferResolutionSort(prefR)); + .sort(this.getPreferResolutionSort(prefResolution)); } getSortedCandidates(deviceId: string): CaptureCandidate[] { @@ -525,6 +537,11 @@ } } +interface CapturePreviewResolutions { + captureResolutions: ResolutionList; + previewResolutions: ResolutionList; +} + /** * Controller for handling photo resolution preference. */ @@ -533,8 +550,8 @@ * Maps from device id as key to capture and preview resolutions of * same aspect ratio supported by that video device as value. */ - private deviceCapturePreviewResolutionMap = new Map< - string, Array<{captureRs: ResolutionList, previewRs: ResolutionList}>>(); + private deviceCapturePreviewResolutionMap = + new Map<string, CapturePreviewResolutions[]>(); /** * Maps from device id as key to whether PTZ is support from device level. @@ -558,35 +575,40 @@ this.devicePTZSupportMap = new Map( devices.map(({deviceId, supportPTZ}) => [deviceId, supportPTZ])); - devices.forEach(({deviceId, photoResols, videoResols: previewResols}) => { - const previewRatios = this.groupResolutionRatio(previewResols); - const captureRatios = this.groupResolutionRatio(photoResols); - const pairedResolutions: - Array<{captureRs: ResolutionList, previewRs: ResolutionList}> = []; - for (const [ratio, captureRs] of captureRatios) { - const previewRs = previewRatios.get(ratio); - if (previewRs === undefined) { + for (const { + deviceId, + photoResolutions, + videoResolutions: previewResolutions, + } of devices) { + const previewRatios = this.groupResolutionRatio(previewResolutions); + const captureRatios = this.groupResolutionRatio(photoResolutions); + const pairedResolutions: CapturePreviewResolutions[] = []; + for (const [ratio, captureResolutions] of captureRatios) { + const previewResolutions = previewRatios.get(ratio); + if (previewResolutions === undefined) { continue; } - pairedResolutions.push({captureRs, previewRs}); + pairedResolutions.push({captureResolutions, previewResolutions}); } this.deviceCapturePreviewResolutionMap.set(deviceId, pairedResolutions); this.supportedResolutions.set( deviceId, - pairedResolutions.flatMap(({captureRs}) => captureRs) + pairedResolutions + .flatMap(({captureResolutions}) => captureResolutions) .sort((r1, r2) => r2.area - r1.area)); - let prefR = this.getPrefResolution(deviceId) ?? new Resolution(0, -1); - const captureRs = this.supportedResolutions.get(deviceId); - assert(captureRs !== undefined); - if (!captureRs.some((r) => r.equals(prefR))) { - prefR = captureRs.reduce( + let prefResolution = + this.getPrefResolution(deviceId) ?? new Resolution(0, -1); + const captureResolutions = this.supportedResolutions.get(deviceId); + assert(captureResolutions !== undefined); + if (!captureResolutions.some((r) => r.equals(prefResolution))) { + prefResolution = captureResolutions.reduce( (maxR, r) => (maxR.area < r.area ? r : maxR), new Resolution(0, -1)); } - this.prefResolution.set(deviceId, prefR); - }); + this.prefResolution.set(deviceId, prefResolution); + } this.saveResolutionPreference('devicePhotoResolution'); } @@ -599,40 +621,42 @@ } getSortedCandidates(deviceId: string): CaptureCandidate[] { - const prefR = this.getPrefResolution(deviceId) ?? new Resolution(0, -1); + const prefResolution = + this.getPrefResolution(deviceId) ?? new Resolution(0, -1); const supportPTZ = this.devicePTZSupportMap.get(deviceId) ?? false; const toCaptureCandidate = - ({captureRs, previewRs}: - {captureRs: ResolutionList, - previewRs: ResolutionList}): CaptureCandidate => { - let captureR = prefR; - if (!captureRs.some((r) => r.equals(prefR))) { - captureR = captureRs.reduce( + ({captureResolutions, + previewResolutions}: CapturePreviewResolutions): CaptureCandidate => { + let captureResolution = prefResolution; + if (!captureResolutions.some((r) => r.equals(prefResolution))) { + captureResolution = captureResolutions.reduce( (captureR, r) => (r.width > captureR.width ? r : captureR)); } // Use workaround for b/184089334 on PTZ camera to use preview frame // as photo result. if (supportPTZ && - previewRs.find((r) => captureR.equals(r)) !== undefined) { - previewRs = [captureR]; + previewResolutions.find((r) => captureResolution.equals(r)) !== + undefined) { + previewResolutions = [captureResolution]; } const previewCandidates = - this.sortPreview(previewRs, captureR).map(({width, height}) => ({ - deviceId, - audio: false, - video: { - width, - height, - }, - })); - return {resolution: captureR, previewCandidates}; + this.sortPreview(previewResolutions, captureResolution) + .map(({width, height}) => ({ + deviceId, + audio: false, + video: { + width, + height, + }, + })); + return {resolution: captureResolution, previewCandidates}; }; return assertExists(this.deviceCapturePreviewResolutionMap.get(deviceId)) .map(toCaptureCandidate) - .sort(this.getPreferResolutionSort(prefR)); + .sort(this.getPreferResolutionSort(prefResolution)); } }
diff --git a/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts b/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts index aa7e8a90..920abc2 100644 --- a/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts +++ b/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts
@@ -106,7 +106,9 @@ } else { this.camera3DevicesInfo = null; } - this.deviceChangeListeners.forEach((l) => l(this)); + for (const listener of this.deviceChangeListeners) { + listener(this); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts index 95171d5b..37681ad 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
@@ -320,11 +320,11 @@ }, }; - [state.State.EXPERT, state.State.SAVE_METADATA].forEach((s) => { + for (const s of [state.State.EXPERT, state.State.SAVE_METADATA]) { state.addObserver(s, () => { this.updateSaveMetadata(); }); - }); + } } initialize(handler: CaptureHandler): void {
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/mode_base.ts b/ash/webui/camera_app_ui/resources/js/device/mode/mode_base.ts index 7407dce..d1dc421 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/mode_base.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/mode_base.ts
@@ -138,7 +138,7 @@ /** * Camera facing of current mode. */ - protected facing = Facing.NOT_SET; + protected facing: Facing|null = null; /** * @param constraints Constraints for preview stream.
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/photo.ts b/ash/webui/camera_app_ui/resources/js/device/mode/photo.ts index 0e1a5373..c2c98ad0 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/photo.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/photo.ts
@@ -166,6 +166,7 @@ produce(): ModeBase { assert(this.previewVideo !== null); + assert(this.facing !== null); return new Photo( this.previewVideo, this.facing, this.captureResolution, this.handler); }
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts b/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts index 3cdad97..7f2b657 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts
@@ -102,6 +102,7 @@ produce(): ModeBase { assert(this.previewVideo !== null); + assert(this.facing !== null); return new Portrait( this.previewVideo, this.facing, this.captureResolution, this.portraitHandler);
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/scan.ts b/ash/webui/camera_app_ui/resources/js/device/mode/scan.ts index c3b7c7d..145be327 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/scan.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/scan.ts
@@ -91,6 +91,7 @@ produce(): ModeBase { assert(this.previewVideo !== null); + assert(this.facing !== null); return new Scan( this.previewVideo, this.facing,
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/square.ts b/ash/webui/camera_app_ui/resources/js/device/mode/square.ts index a4c11c7..bd748a82 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/square.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/square.ts
@@ -88,6 +88,7 @@ export class SquareFactory extends PhotoFactory { produce(): ModeBase { assert(this.previewVideo !== null); + assert(this.facing !== null); return new Square( this.previewVideo, this.facing, this.captureResolution, this.handler); }
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts index fb2e127f..fef84b32 100644 --- a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts +++ b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
@@ -649,6 +649,7 @@ }; } assert(this.previewVideo !== null); + assert(this.facing !== null); return new Video( this.previewVideo, captureConstraints, this.captureResolution, this.snapshotResolution, this.facing, this.handler);
diff --git a/ash/webui/camera_app_ui/resources/js/device/preview.ts b/ash/webui/camera_app_ui/resources/js/device/preview.ts index 37448d27..37e2107 100644 --- a/ash/webui/camera_app_ui/resources/js/device/preview.ts +++ b/ash/webui/camera_app_ui/resources/js/device/preview.ts
@@ -79,7 +79,7 @@ */ private focusMarker: symbol|null = null; - private facing = Facing.NOT_SET; + private facing: Facing|null = null; private deviceId: string|null = null; @@ -133,7 +133,7 @@ } getFacing(): Facing { - return this.facing; + return util.assertEnumVariant(Facing, this.facing); } getDeviceId(): string|null { @@ -155,15 +155,7 @@ } private async updateFacing() { - if (!(await DeviceOperator.isSupported())) { - this.facing = Facing.NOT_SET; - return; - } const {facingMode} = this.getVideoTrack().getSettings(); - if (facingMode === undefined) { - this.facing = Facing.EXTERNAL; - return; - } switch (facingMode) { case 'user': this.facing = Facing.USER; @@ -172,7 +164,8 @@ this.facing = Facing.ENVIRONMENT; return; default: - throw new Error('Unknown facing: ' + facingMode); + this.facing = Facing.EXTERNAL; + return; } } @@ -416,9 +409,9 @@ return; } - dom.getAll('.metadata.value', HTMLElement).forEach((element) => { + for (const element of dom.getAll('.metadata.value', HTMLElement)) { element.style.display = 'none'; - }); + } const displayCategory = (selector: string, enabled: boolean) => { dom.get(selector, HTMLElement).classList.toggle('mode-on', enabled);
diff --git a/ash/webui/camera_app_ui/resources/js/device/type.ts b/ash/webui/camera_app_ui/resources/js/device/type.ts index a092f87..5af41c2 100644 --- a/ash/webui/camera_app_ui/resources/js/device/type.ts +++ b/ash/webui/camera_app_ui/resources/js/device/type.ts
@@ -53,21 +53,31 @@ * camera will be opened with. */ export interface CameraConfig { + deviceId: string; + facing: Facing; + mode: Mode; +} + +/** + * The next |CameraConfig| to be tried. + */ +export interface CameraConfigCandidate { /** - * May be null for device using legacy linux VCD. + * The only null case is for opening the default facing camera on non-ChromeOS + * VCD. */ deviceId: string|null; - /** - * May be Facing.NOT_SET for device using legacy linux VCD. + * On device using non-ChromeOS VCD, camera facing is unknown before opening + * the camera. */ - facing: Facing; + facing: Facing|null; mode: Mode; } export interface CameraUI { onUpdateCapability?(cameraInfo: CameraInfo): void; - onTryingNewConfig?(config: CameraConfig): void; + onTryingNewConfig?(config: CameraConfigCandidate): void; onUpdateConfig?(config: CameraConfig): Promise<void>|void; onCameraUnavailable?(): void; onCameraAvailble?(): void;
diff --git a/ash/webui/camera_app_ui/resources/js/focus_ring.ts b/ash/webui/camera_app_ui/resources/js/focus_ring.ts index f6e0c70d..3605410 100644 --- a/ash/webui/camera_app_ui/resources/js/focus_ring.ts +++ b/ash/webui/camera_app_ui/resources/js/focus_ring.ts
@@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { - assert, - assertInstanceof, -} from './assert.js'; +import {assert} from './assert.js'; import {cssStyle} from './css.js'; import * as dom from './dom.js'; import {getStyleValueInPx} from './util.js'; @@ -77,9 +74,11 @@ } }; - dom.getAll('[tabindex]', HTMLElement).forEach(setup); + for (const el of dom.getAll('[tabindex]', HTMLElement)) { + setup(el); + } const observer = new MutationObserver((mutationList) => { - mutationList.forEach((mutation) => { + for (const mutation of mutationList) { assert(mutation.type === 'childList'); // Only the newly added nodes with [tabindex] are considered here. So // simply adding class attribute on existing element will not work. @@ -87,12 +86,11 @@ if (!(node instanceof HTMLElement)) { continue; } - const el = assertInstanceof(node, HTMLElement); - if (el.hasAttribute('tabindex')) { - setup(el); + if (node.hasAttribute('tabindex')) { + setup(node); } } - }); + } }); observer.observe(document.body, { subtree: true,
diff --git a/ash/webui/camera_app_ui/resources/js/main.ts b/ash/webui/camera_app_ui/resources/js/main.ts index 4940f6203..b69c41d 100644 --- a/ash/webui/camera_app_ui/resources/js/main.ts +++ b/ash/webui/camera_app_ui/resources/js/main.ts
@@ -65,7 +65,7 @@ constructor({perfLogger, intent, facing, mode: defaultMode}: { perfLogger: PerfLogger, intent: Intent|null, - facing: Facing, + facing: Facing|null, mode: Mode|null, }) { this.perfLogger = perfLogger; @@ -125,7 +125,7 @@ * Sets up toggles (checkbox and radio) by data attributes. */ private setupToggles() { - dom.getAll('input', HTMLInputElement).forEach((element) => { + for (const element of dom.getAll('input', HTMLInputElement)) { element.addEventListener('keypress', (event) => { const e = assertInstanceof(event, KeyboardEvent); if (util.getShortcutIdentifier(e) === 'Enter') { @@ -171,18 +171,19 @@ localStorage.getBool(element.dataset['key'], element.checked); util.toggleChecked(element, value); } - }); + } } /** * Sets up visual effect for all applicable elements. */ private setupEffect() { - dom.getAll('.inkdrop', HTMLElement) - .forEach((el) => util.setInkdropEffect(el)); + for (const el of dom.getAll('.inkdrop', HTMLElement)) { + util.setInkdropEffect(el); + } const observer = new MutationObserver((mutationList) => { - mutationList.forEach((mutation) => { + for (const mutation of mutationList) { assert(mutation.type === 'childList'); // Only the newly added nodes with inkdrop class are considered here. So // simply adding class attribute on existing element will not work. @@ -190,12 +191,11 @@ if (!(node instanceof HTMLElement)) { continue; } - const el = assertInstanceof(node, HTMLElement); - if (el.classList.contains('inkdrop')) { - util.setInkdropEffect(el); + if (node.classList.contains('inkdrop')) { + util.setInkdropEffect(node); } } - }); + } }); observer.observe(document.body, { subtree: true, @@ -350,7 +350,7 @@ */ function parseSearchParams(): { intent: Intent|null, - facing: Facing, + facing: Facing|null, mode: Mode|null, openFrom: string|null, autoTake: boolean, @@ -358,8 +358,7 @@ const url = new URL(window.location.href); const params = url.searchParams; - const facing = - checkEnumVariant(Facing, params.get('facing')) ?? Facing.NOT_SET; + const facing = checkEnumVariant(Facing, params.get('facing')); const mode = checkEnumVariant(Mode, params.get('mode'));
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts index 2a22344..3f18a42 100644 --- a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts +++ b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts
@@ -154,12 +154,12 @@ } const cameraMetadataTagInverseLookup: Record<number, string> = {}; - Object.entries(CameraMetadataTag).forEach(([key, value]) => { + for (const [key, value] of Object.entries(CameraMetadataTag)) { if (key === 'MIN_VALUE' || key === 'MAX_VALUE') { return; } cameraMetadataTagInverseLookup[value] = key; - }); + } const callback = (metadata: CameraMetadata) => { const parsedMetadata: Record<string, unknown> = {};
diff --git a/ash/webui/camera_app_ui/resources/js/nav.ts b/ash/webui/camera_app_ui/resources/js/nav.ts index e350560..a4e8ce08 100644 --- a/ash/webui/camera_app_ui/resources/js/nav.ts +++ b/ash/webui/camera_app_ui/resources/js/nav.ts
@@ -60,14 +60,14 @@ // Restore the view's child elements' tabindex and then focus the view. const view = allViews[index]; view.root.setAttribute('aria-hidden', 'false'); - dom.getAllFrom(view.root, '[tabindex]', HTMLElement).forEach((element) => { + for (const element of dom.getAllFrom(view.root, '[tabindex]', HTMLElement)) { if (element.dataset['tabindex'] === undefined) { // First activation, no need to restore tabindex from data-tabindex. - return; + continue; } element.setAttribute('tabindex', element.dataset['tabindex']); element.removeAttribute('data-tabindex'); - }); + } view.focus(); } @@ -79,11 +79,11 @@ function deactivate(index: number) { const view = allViews[index]; view.root.setAttribute('aria-hidden', 'true'); - dom.getAllFrom(view.root, '[tabindex]', HTMLElement).forEach((element) => { + for (const element of dom.getAllFrom(view.root, '[tabindex]', HTMLElement)) { element.dataset['tabindex'] = assertExists(element.getAttribute('tabindex')); element.setAttribute('tabindex', '-1'); - }); + } const activeElement = document.activeElement; if (activeElement instanceof HTMLElement) { activeElement.blur();
diff --git a/ash/webui/camera_app_ui/resources/js/perf.ts b/ash/webui/camera_app_ui/resources/js/perf.ts index 534f53d..f1d58433 100644 --- a/ash/webui/camera_app_ui/resources/js/perf.ts +++ b/ash/webui/camera_app_ui/resources/js/perf.ts
@@ -101,7 +101,9 @@ const duration = performance.now() - startTime; ChromeHelper.getInstance().stopTracing(event); - this.listeners.forEach((listener) => listener({event, duration, perfInfo})); + for (const listener of this.listeners) { + listener({event, duration, perfInfo}); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/tooltip.ts b/ash/webui/camera_app_ui/resources/js/tooltip.ts index 56dab01..7a5bc01 100644 --- a/ash/webui/camera_app_ui/resources/js/tooltip.ts +++ b/ash/webui/camera_app_ui/resources/js/tooltip.ts
@@ -94,7 +94,7 @@ export function setup(elements: NodeListOf<HTMLElement>): NodeListOf<HTMLElement> { wrapper = dom.get('#tooltip', HTMLElement); - elements.forEach((el) => { + for (const el of elements) { const handler = () => { // Handler hides tooltip only when it's for the element. if (el === hovered) { @@ -104,6 +104,6 @@ el.addEventListener('mouseout', handler); el.addEventListener('click', handler); el.addEventListener('mouseover', () => show(el)); - }); + } return elements; }
diff --git a/ash/webui/camera_app_ui/resources/js/type.ts b/ash/webui/camera_app_ui/resources/js/type.ts index b4076c2..e8a965c 100644 --- a/ash/webui/camera_app_ui/resources/js/type.ts +++ b/ash/webui/camera_app_ui/resources/js/type.ts
@@ -93,7 +93,7 @@ VIRTUAL_USER = 'virtual_user', VIRTUAL_ENV = 'virtual_environment', VIRTUAL_EXT = 'virtual_external', - NOT_SET = '(not set)', + UNKNOWN = 'unknown', } export enum ViewName {
diff --git a/ash/webui/camera_app_ui/resources/js/util.ts b/ash/webui/camera_app_ui/resources/js/util.ts index e473cc51..e9e9ee9 100644 --- a/ash/webui/camera_app_ui/resources/js/util.ts +++ b/ash/webui/camera_app_ui/resources/js/util.ts
@@ -109,21 +109,23 @@ const setAriaLabel = (element: HTMLElement, attr: string) => element.setAttribute('aria-label', getMessage(element, attr)); - getElements('i18n-text') - .forEach( - (element) => element.textContent = getMessage(element, 'i18n-text')); - getElements('i18n-tooltip-true') - .forEach( - (element) => element.setAttribute( - 'tooltip-true', getMessage(element, 'i18n-tooltip-true'))); - getElements('i18n-tooltip-false') - .forEach( - (element) => element.setAttribute( - 'tooltip-false', getMessage(element, 'i18n-tooltip-false'))); - getElements('i18n-aria') - .forEach((element) => setAriaLabel(element, 'i18n-aria')); - tooltip.setup(getElements('i18n-label')) - .forEach((element) => setAriaLabel(element, 'i18n-label')); + for (const element of getElements('i18n-text')) { + element.textContent = getMessage(element, 'i18n-text'); + } + for (const element of getElements('i18n-tooltip-true')) { + element.setAttribute( + 'tooltip-true', getMessage(element, 'i18n-tooltip-true')); + } + for (const element of getElements('i18n-tooltip-false')) { + element.setAttribute( + 'tooltip-false', getMessage(element, 'i18n-tooltip-false')); + } + for (const element of getElements('i18n-aria')) { + setAriaLabel(element, 'i18n-aria'); + } + for (const element of tooltip.setup(getElements('i18n-label'))) { + setAriaLabel(element, 'i18n-label'); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.ts b/ash/webui/camera_app_ui/resources/js/views/camera.ts index 26568de5..8546027 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera.ts +++ b/ash/webui/camera_app_ui/resources/js/views/camera.ts
@@ -102,7 +102,7 @@ protected readonly review = new review.Review(); - protected facing = Facing.NOT_SET; + protected facing: Facing|null = null; protected shutterType = metrics.ShutterType.UNKNOWN; @@ -275,6 +275,13 @@ await this.initScanMode(); } + /** + * Gets current facing after |initialize()|. + */ + protected getFacing(): Facing { + return util.assertEnumVariant(Facing, this.facing); + } + private updateModeUI(mode: Mode) { for (const m of Object.values(Mode)) { state.set(m, m === mode); @@ -397,8 +404,11 @@ if (!state.get(state.State.CAMERA_CONFIGURING) && state.get(Mode.SCAN) && this.scanOptions.isDocumentModeEanbled() && nav.isTopMostView(this.name)) { - dom.getAll('button.shutter', HTMLButtonElement) - .forEach((btn) => btn.offsetParent && btn.focus()); + for (const btn of dom.getAll('button.shutter', HTMLButtonElement)) { + if (btn.offsetParent !== null) { + btn.focus(); + } + } } }; state.addObserver(state.State.CAMERA_CONFIGURING, checkRefocus); @@ -424,8 +434,11 @@ } // Avoid focusing invisible shutters. - dom.getAll('button.shutter', HTMLButtonElement) - .forEach((btn) => btn.offsetParent && btn.focus()); + for (const btn of dom.getAll('button.shutter', HTMLButtonElement)) { + if (btn.offsetParent !== null) { + btn.focus(); + } + } })(); } @@ -474,7 +487,10 @@ assertInstanceof(e, Error)); } finally { this.take = null; - state.set(state.State.TAKING, false, {hasError, facing: this.facing}); + state.set(state.State.TAKING, false, { + hasError, + facing: this.getFacing(), + }); this.focus(); // Refocus the visible shutter button for ChromeVox. } })(); @@ -505,7 +521,7 @@ async handleVideoSnapshot({resolution, blob, timestamp, metadata}: PhotoResult): Promise<void> { metrics.sendCaptureEvent({ - facing: this.facing, + facing: this.getFacing(), resolution, shutterType: this.shutterType, isVideoSnapshot: true, @@ -535,7 +551,7 @@ await this.checkPhotoResult(pendingPhotoResult); metrics.sendCaptureEvent({ - facing: this.facing, + facing: this.getFacing(), resolution, shutterType: this.shutterType, isVideoSnapshot: false, @@ -550,7 +566,7 @@ } state.set( PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, - {resolution, facing: this.facing}); + {resolution, facing: this.getFacing()}); } catch (e) { state.set( PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true}); @@ -568,7 +584,7 @@ await this.checkPhotoResult(pendingReference); metrics.sendCaptureEvent({ - facing: this.facing, + facing: this.getFacing(), resolution, shutterType: this.shutterType, isVideoSnapshot: false, @@ -607,7 +623,7 @@ } finally { state.set( PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false, - {hasError, facing: this.facing}); + {hasError, facing: this.getFacing()}); } } @@ -685,7 +701,7 @@ let fixType = metrics.DocFixType.NONE; const sendEvent = (docResult: metrics.DocResultType) => { metrics.sendCaptureEvent({ - facing: this.facing, + facing: this.getFacing(), resolution: originImage.resolution, shutterType: this.shutterType, docResult, @@ -818,7 +834,7 @@ const sendEvent = (gifResult: metrics.GifResultType) => { metrics.sendCaptureEvent({ recordType: metrics.RecordType.GIF, - facing: this.facing, + facing: this.getFacing(), resolution, duration, shutterType: this.shutterType, @@ -863,7 +879,7 @@ try { metrics.sendCaptureEvent({ recordType: metrics.RecordType.NORMAL_VIDEO, - facing: this.facing, + facing: this.getFacing(), duration, resolution, shutterType: this.shutterType, @@ -872,7 +888,7 @@ await this.resultSaver.finishSaveVideo(videoSaver); state.set( PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, - {resolution, facing: this.facing}); + {resolution, facing: this.getFacing()}); } catch (e) { state.set( PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true});
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts index 44597e1e..76b65f6 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts +++ b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
@@ -356,14 +356,14 @@ }); // Set start of dot transition. - starts.forEach((corn, idx) => { + for (const [idx, corn] of starts.entries()) { const prevIdx = (idx + 3) % 4; const nextIdx = (idx + 1) % 4; this.corners[idx].place(corn, starts[prevIdx], starts[nextIdx]); - }); + } // Set start of line transition. - this.sides.forEach((line, i) => { + for (const [i, line] of this.sides.entries()) { const startCorn = starts[i]; const startCorn2 = starts[(i + 1) % 4]; const startSide = vectorFromPoints(startCorn2, startCorn); @@ -372,18 +372,18 @@ angle: startSide.cssRotateAngle(), length: startSide.length(), }); - }); + } void this.cornerContainer.offsetParent; // Force start state of transition. // Set end of dot transition. - corners.forEach((corn, i) => { + for (const [i, corn] of corners.entries()) { const prevIdx = (i + 3) % 4; const nextIdx = (i + 1) % 4; this.corners[i].place(corn, corners[prevIdx], corners[nextIdx]); - }); + } - this.sides.forEach((line, i) => { + for (const [i, line] of this.sides.entries()) { const endCorn = corners[i]; const endCorn2 = corners[(i + 1) % 4]; const endSide = vectorFromPoints(endCorn2, endCorn); @@ -392,19 +392,19 @@ angle: endSide.cssRotateAngle(), length: endSide.length(), }); - }); + } } /** * Place first 4 corners on the overlay and play settle animation. */ private updateCorners(corners: Point[]) { - corners.forEach((corn, i) => { + for (const [i, corn] of corners.entries()) { const prevIdx = (i + 3) % 4; const nextIdx = (i + 1) % 4; this.corners[i].place(corn, corners[prevIdx], corners[nextIdx]); - }); - this.sides.forEach((line, i) => { + } + for (const [i, line] of this.sides.entries()) { const corn = corners[i]; const corn2 = corners[(i + 1) % 4]; const side = vectorFromPoints(corn2, corn); @@ -413,7 +413,7 @@ angle: side.cssRotateAngle(), length: side.length(), }); - }); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/options.ts b/ash/webui/camera_app_ui/resources/js/views/camera/options.ts index 123ce81..b6402e6 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/options.ts +++ b/ash/webui/camera_app_ui/resources/js/views/camera/options.ts
@@ -110,8 +110,9 @@ private updateVideoConstFpsOption(prefFps: number|null) { this.toggleFps.checked = prefFps === 60; - SUPPORTED_CONSTANT_FPS.forEach( - (fps) => state.set(state.assertState(`fps-${fps}`), fps === prefFps)); + for (const fps of SUPPORTED_CONSTANT_FPS) { + state.set(state.assertState(`fps-${fps}`), fps === prefFps); + } } onUpdateCapability(cameraInfo: CameraInfo): void {
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts index a85a090..28b71f7 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts +++ b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
@@ -75,24 +75,24 @@ this.documentCornerOverylay = new DocumentCornerOverlay( (p) => this.cameraManager.setPointOfInterest(p)); - [this.photoBarcodeOption, ...this.scanOptions].forEach((opt) => { - opt.addEventListener('click', (evt) => { + for (const option of [this.photoBarcodeOption, ...this.scanOptions]) { + option.addEventListener('click', (evt) => { if (state.get(state.State.CAMERA_CONFIGURING)) { evt.preventDefault(); } }); - }); + } this.photoBarcodeOption.addEventListener('change', () => { this.updateOption( this.photoBarcodeOption.checked ? ScanType.BARCODE : null); }); - this.scanOptions.forEach((opt) => { - opt.addEventListener('change', () => { - if (opt.checked) { + for (const option of this.scanOptions) { + option.addEventListener('change', () => { + if (option.checked) { this.updateOption(this.getToggledScanOption()); } }); - }); + } } /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera_intent.ts b/ash/webui/camera_app_ui/resources/js/views/camera_intent.ts index d2fb8ac7..adcdc40 100644 --- a/ash/webui/camera_app_ui/resources/js/views/camera_intent.ts +++ b/ash/webui/camera_app_ui/resources/js/views/camera_intent.ts
@@ -87,7 +87,7 @@ ], })); metrics.sendCaptureEvent({ - facing: this.facing, + facing: this.getFacing(), ...metricArgs, intentResult: confirmed ? metrics.IntentResultType.CONFIRMED : metrics.IntentResultType.CANCELED,
diff --git a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts index 08348984..e241704 100644 --- a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts +++ b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
@@ -204,7 +204,7 @@ return new Size(width, height); })(); - this.corners.forEach((corn) => { + for (const corn of this.corners) { // Start dragging on one corner. corn.el.addEventListener('pointerdown', (e) => { e.preventDefault(); @@ -286,7 +286,7 @@ clearKeydown(); } }); - }); + } // Stop dragging. for (const eventName of ['pointerup', 'pointerleave', 'pointercancel']) { @@ -486,23 +486,24 @@ private updateCornerEl() { const cords = this.corners.map(({pt: {x, y}}) => `${x},${y}`).join(' '); this.cropArea.setAttribute('points', cords); - this.corners.forEach((corn) => { + for (const corn of this.corners) { const style = corn.el.attributeStyleMap; style.set('left', CSS.px(corn.pt.x)); style.set('top', CSS.px(corn.pt.y)); - }); + } } private updateCornerElAriaLabel() { - [I18nString.LABEL_DOCUMENT_TOP_LEFT_CORNER, - I18nString.LABEL_DOCUMENT_BOTTOM_LEFT_CORNER, - I18nString.LABEL_DOCUMENT_BOTTOM_RIGHT_CORNER, - I18nString.LABEL_DOCUMENT_TOP_RIGHT_CORNER, - ].forEach((label, index) => { + for (const [index, label] of + [I18nString.LABEL_DOCUMENT_TOP_LEFT_CORNER, + I18nString.LABEL_DOCUMENT_BOTTOM_LEFT_CORNER, + I18nString.LABEL_DOCUMENT_BOTTOM_RIGHT_CORNER, + I18nString.LABEL_DOCUMENT_TOP_RIGHT_CORNER, + ].entries()) { const cornEl = this.corners[(this.rotation + index) % this.corners.length].el; cornEl.setAttribute('i18n-aria', label); - }); + } util.setupI18nElements(this.root); } @@ -532,18 +533,18 @@ // Update corner space. if (this.cornerSpaceSize === null) { - this.initialCorners.forEach(({x, y}, idx) => { + for (const [idx, {x, y}] of this.initialCorners.entries()) { this.corners[idx].pt = new Point(x * newImageW, y * newImageH); - }); + } this.initialCorners = []; } else { const oldImageW = this.cornerSpaceSize?.width || newImageW; const oldImageH = this.cornerSpaceSize?.height || newImageH; - this.corners.forEach((corn) => { + for (const corn of this.corners) { corn.pt = new Point( corn.pt.x / oldImageW * newImageW, corn.pt.y / oldImageH * newImageH); - }); + } } this.cornerSpaceSize = new Size(newImageW, newImageH);
diff --git a/ash/webui/camera_app_ui/resources/js/views/settings.ts b/ash/webui/camera_app_ui/resources/js/views/settings.ts index e93f20e..b02cf59 100644 --- a/ash/webui/camera_app_ui/resources/js/views/settings.ts +++ b/ash/webui/camera_app_ui/resources/js/views/settings.ts
@@ -30,8 +30,8 @@ * available resolutions for a particular video device. */ interface ResolutionConfig { - prefResol: Resolution; - resols: ResolutionList; + prefResolution: Resolution; + resolutions: ResolutionList; } /** @@ -69,12 +69,13 @@ dom.getFrom(this.root, '.menu-header button', HTMLButtonElement) .addEventListener('click', () => this.leave()); - dom.getAllFrom(this.root, '.menu-item', HTMLElement).forEach((element) => { + for (const element of dom.getAllFrom( + this.root, '.menu-item', HTMLElement)) { const handler = itemHandlers[element.id]; if (handler !== undefined) { element.addEventListener('click', handler); } - }); + } this.defaultFocus = dom.getFrom(this.root, '[tabindex]', HTMLElement); @@ -223,11 +224,11 @@ readonly videoResolutionSettings = new BaseSettings(ViewName.VIDEO_RESOLUTION_SETTINGS); - private readonly resMenu: HTMLDivElement; + private readonly resolutionMenu: HTMLDivElement; - private readonly videoResMenu: HTMLDivElement; + private readonly videoResolutionMenu: HTMLDivElement; - private readonly photoResMenu: HTMLDivElement; + private readonly photoResolutionMenu: HTMLDivElement; private cameraAvailble = false; @@ -281,12 +282,12 @@ })(), ); - this.resMenu = dom.getFrom(this.root, 'div.menu', HTMLDivElement); + this.resolutionMenu = dom.getFrom(this.root, 'div.menu', HTMLDivElement); - this.videoResMenu = dom.getFrom( + this.videoResolutionMenu = dom.getFrom( this.videoResolutionSettings.root, 'div.menu', HTMLDivElement); - this.photoResMenu = dom.getFrom( + this.photoResolutionMenu = dom.getFrom( this.photoResolutionSettings.root, 'div.menu', HTMLDivElement); state.addObserver(state.State.TAKING, () => { @@ -318,21 +319,22 @@ this.frontSetting = this.backSetting = null; this.externalSettings = []; - devices.forEach(({deviceId, facing, photoResols, videoResols}) => { + for (const {deviceId, facing, photoResolutions, videoResolutions} of + devices) { const deviceSetting = { deviceId, photo: { - prefResol: assertInstanceof( + prefResolution: assertInstanceof( cameraManager.getPrefPhotoResolution(deviceId), Resolution), - resols: - /* Filter out resolutions of megapixels < 0.1 i.e. megapixels - * 0.0 */ - photoResols.filter((r) => r.area >= 100000), + resolutions: + /* Filter out resolutions of megapixels < 0.1 i.e. + * megapixels 0.0 */ + photoResolutions.filter((r) => r.area >= 100000), }, video: { - prefResol: assertInstanceof( + prefResolution: assertInstanceof( cameraManager.getPrefVideoResolution(deviceId), Resolution), - resols: videoResols, + resolutions: videoResolutions, }, }; switch (facing) { @@ -350,7 +352,7 @@ ErrorType.UNKNOWN_FACING, ErrorLevel.ERROR, new Error(`Ignore device of unknown facing: ${facing}`)); } - }); + } this.updateResolutions(); }, onUpdateConfig: (config: CameraConfig) => { @@ -362,14 +364,14 @@ return; } if (config.mode === Mode.VIDEO) { - const prefResol = cameraManager.getPrefVideoResolution(deviceId); - if (prefResol !== null) { - this.updateSelectedVideoResolution(deviceId, prefResol); + const prefResolution = cameraManager.getPrefVideoResolution(deviceId); + if (prefResolution !== null) { + this.updateSelectedVideoResolution(deviceId, prefResolution); } } else { - const prefResol = cameraManager.getPrefPhotoResolution(deviceId); - if (prefResol !== null) { - this.updateSelectedPhotoResolution(deviceId, prefResol); + const prefResolution = cameraManager.getPrefPhotoResolution(deviceId); + if (prefResolution !== null) { + this.updateSelectedPhotoResolution(deviceId, prefResolution); } } }, @@ -377,9 +379,9 @@ } private updateOptionAvailability(): void { - dom.getAll('.resolution-option>input', HTMLInputElement).forEach((e) => { + for (const e of dom.getAll('.resolution-option>input', HTMLInputElement)) { e.disabled = !this.cameraAvailble || state.get(state.State.TAKING); - }); + } } @@ -393,38 +395,40 @@ /** * Template for generating option text from photo resolution width and height. * - * @param r Resolution of text to be generated. + * @param resolution Resolution of text to be generated. * @param resolutions All available resolutions. * @return Text shown on resolution option item. */ - private photoOptTextTempl(r: Resolution, resolutions: ResolutionList): - string { + private photoOptionTextTemplate( + resolution: Resolution, resolutions: ResolutionList): string { const gcd = (a: number, b: number): number => (a === 0 ? b : gcd(b % a, a)); const toMegapixel = ({area}: Resolution): number => area >= 1e6 ? Math.round(area / 1e6) : Math.round(area / 1e5) / 10; - const d = gcd(r.width, r.height); + const d = gcd(resolution.width, resolution.height); + if (resolutions.some( - (findR) => !findR.equals(r) && r.aspectRatioEquals(findR) && - toMegapixel(r) === toMegapixel(findR))) { + (r) => !r.equals(resolution) && resolution.aspectRatioEquals(r) && + toMegapixel(resolution) === toMegapixel(r))) { return loadTimeData.getI18nMessage( - I18nString.LABEL_DETAIL_PHOTO_RESOLUTION, r.width / d, r.height / d, - r.width, r.height, toMegapixel(r)); + I18nString.LABEL_DETAIL_PHOTO_RESOLUTION, resolution.width / d, + resolution.height / d, resolution.width, resolution.height, + toMegapixel(resolution)); } else { return loadTimeData.getI18nMessage( - I18nString.LABEL_PHOTO_RESOLUTION, r.width / d, r.height / d, - toMegapixel(r)); + I18nString.LABEL_PHOTO_RESOLUTION, resolution.width / d, + resolution.height / d, toMegapixel(resolution)); } } /** * Template for generating option text from video resolution width and height. * - * @param r Resolution of text to be generated. + * @param resolution Resolution of text to be generated. * @return Text shown on resolution option item. */ - private videoOptTextTempl(r: Resolution): string { + private videoOptionTextTemplate(resolution: Resolution): string { return loadTimeData.getI18nMessage( - I18nString.LABEL_VIDEO_RESOLUTION, r.height, r.width); + I18nString.LABEL_VIDEO_RESOLUTION, resolution.height, resolution.width); } /** @@ -444,30 +448,36 @@ * Updates resolution information of front, back camera and external cameras. */ private updateResolutions() { - const prepItem = - (item: HTMLElement, id: string, {prefResol, resols}: ResolutionConfig, - optTextTempl: (prefResol: Resolution, resols: ResolutionList) => - string) => { + const prepareItem = + (item: HTMLElement, id: string, + {prefResolution, resolutions}: ResolutionConfig, + optionTextTemplate: + (prefResolutions: Resolution, resolutions: ResolutionList) => + string) => { item.dataset['deviceId'] = id; - item.classList.toggle('multi-option', resols.length > 1); + item.classList.toggle('multi-option', resolutions.length > 1); dom.getFrom(item, '.description>span', HTMLSpanElement).textContent = - optTextTempl(prefResol, resols); + optionTextTemplate(prefResolution, resolutions); }; // Update front camera setting state.set(state.State.HAS_FRONT_CAMERA, this.frontSetting !== null); if (this.frontSetting) { const {deviceId, photo, video} = this.frontSetting; - prepItem(this.frontPhotoItem, deviceId, photo, this.photoOptTextTempl); - prepItem(this.frontVideoItem, deviceId, video, this.videoOptTextTempl); + prepareItem( + this.frontPhotoItem, deviceId, photo, this.photoOptionTextTemplate); + prepareItem( + this.frontVideoItem, deviceId, video, this.videoOptionTextTemplate); } // Update back camera setting state.set(state.State.HAS_BACK_CAMERA, this.backSetting !== null); if (this.backSetting) { const {deviceId, photo, video} = this.backSetting; - prepItem(this.backPhotoItem, deviceId, photo, this.photoOptTextTempl); - prepItem(this.backVideoItem, deviceId, video, this.videoOptTextTempl); + prepareItem( + this.backPhotoItem, deviceId, photo, this.photoOptionTextTemplate); + prepareItem( + this.backVideoItem, deviceId, video, this.videoOptionTextTemplate); } // Update external camera settings @@ -475,23 +485,23 @@ // focused item in both previous and current list, pop out all items in // previous list except those having same deviceId as focused one and // recreate all other items from current list. - const prevFocus = this.resMenu.querySelector<HTMLElement>( + const prevFocused = this.resolutionMenu.querySelector<HTMLElement>( '.menu-item.external-camera:focus'); - const prevFId = prevFocus?.dataset['deviceId'] ?? null; - const focusIdx = - this.externalSettings.findIndex(({deviceId}) => deviceId === prevFId); - const fTitle = this.resMenu.querySelector<HTMLElement>( - `.external-camera.title-item[data-device-id="${prevFId}"]`); - const focusedId = focusIdx === -1 ? null : prevFId; + const prevFocusedId = prevFocused?.dataset['deviceId'] ?? null; + const focusedIdx = this.externalSettings.findIndex( + ({deviceId}) => deviceId === prevFocusedId); + const prevFocusedTitle = this.resolutionMenu.querySelector<HTMLElement>( + `.external-camera.title-item[data-device-id="${prevFocusedId}"]`); + const focusedId = focusedIdx === -1 ? null : prevFocusedId; for (const element of dom.getAllFrom( - this.resMenu, '.menu-item.external-camera', HTMLElement)) { + this.resolutionMenu, '.menu-item.external-camera', HTMLElement)) { if (element.dataset['deviceId'] !== focusedId) { assertExists(element.parentNode).removeChild(element); } } - this.externalSettings.forEach((config, index) => { + for (const [index, config] of this.externalSettings.entries()) { const {deviceId} = config; let titleItem: HTMLElement; let photoItem: HTMLElement; @@ -518,21 +528,24 @@ videoItem.setAttribute('aria-describedby', `${deviceId}-videores-desc`); dom.getFrom(videoItem, '.description', HTMLElement).id = `${deviceId}-videores-desc`; - if (index < focusIdx) { - this.resMenu.insertBefore(extItem, fTitle); + if (index < focusedIdx) { + this.resolutionMenu.insertBefore(extItem, prevFocusedTitle); } else { - this.resMenu.appendChild(extItem); + this.resolutionMenu.appendChild(extItem); } } else { - assert(fTitle !== null); - titleItem = fTitle; - photoItem = assertInstanceof(fTitle.nextElementSibling, HTMLElement); + assert(prevFocusedTitle !== null); + titleItem = prevFocusedTitle; + photoItem = + assertInstanceof(prevFocusedTitle.nextElementSibling, HTMLElement); videoItem = assertInstanceof(photoItem.nextElementSibling, HTMLElement); } titleItem.dataset['deviceId'] = deviceId; - prepItem(photoItem, deviceId, config.photo, this.photoOptTextTempl); - prepItem(videoItem, deviceId, config.video, this.videoOptTextTempl); - }); + prepareItem( + photoItem, deviceId, config.photo, this.photoOptionTextTemplate); + prepareItem( + videoItem, deviceId, config.video, this.videoOptionTextTemplate); + } // Force closing opened setting of unplugged device. if ((state.get(ViewName.PHOTO_RESOLUTION_SETTINGS) || state.get(ViewName.VIDEO_RESOLUTION_SETTINGS)) && @@ -554,7 +567,7 @@ private updateSelectedPhotoResolution( deviceId: string, resolution: Resolution) { const {photo} = assertExists(this.getDeviceSetting(deviceId)); - photo.prefResol = resolution; + photo.prefResolution = resolution; let photoItem: HTMLElement; if (this.frontSetting && this.frontSetting.deviceId === deviceId) { photoItem = this.frontPhotoItem; @@ -562,17 +575,17 @@ photoItem = this.backPhotoItem; } else { photoItem = dom.getFrom( - this.resMenu, `.menu-item.photo-item[data-device-id="${deviceId}"]`, - HTMLElement); + this.resolutionMenu, + `.menu-item.photo-item[data-device-id="${deviceId}"]`, HTMLElement); } dom.getFrom(photoItem, '.description>span', HTMLSpanElement).textContent = - this.photoOptTextTempl(photo.prefResol, photo.resols); + this.photoOptionTextTemplate(photo.prefResolution, photo.resolutions); // Update setting option if it's opened. if (state.get(ViewName.PHOTO_RESOLUTION_SETTINGS) && this.openedSettingDeviceId === deviceId) { const input = dom.getFrom( - this.photoResMenu, + this.photoResolutionMenu, 'input' + `[data-width="${resolution.width}"]` + `[data-height="${resolution.height}"]`, @@ -590,7 +603,7 @@ private updateSelectedVideoResolution( deviceId: string, resolution: Resolution) { const {video} = assertExists(this.getDeviceSetting(deviceId)); - video.prefResol = resolution; + video.prefResolution = resolution; let videoItem: HTMLElement; if (this.frontSetting && this.frontSetting.deviceId === deviceId) { videoItem = this.frontVideoItem; @@ -598,17 +611,17 @@ videoItem = this.backVideoItem; } else { videoItem = dom.getFrom( - this.resMenu, `.menu-item.video-item[data-device-id="${deviceId}"]`, - HTMLElement); + this.resolutionMenu, + `.menu-item.video-item[data-device-id="${deviceId}"]`, HTMLElement); } dom.getFrom(videoItem, '.description>span', HTMLSpanElement).textContent = - this.videoOptTextTempl(video.prefResol); + this.videoOptionTextTemplate(video.prefResolution); // Update setting option if it's opened. if (state.get(ViewName.VIDEO_RESOLUTION_SETTINGS) && this.openedSettingDeviceId === deviceId) { const input = dom.getFrom( - this.videoResMenu, + this.videoResolutionMenu, 'input' + `[data-width="${resolution.width}"]` + `[data-height="${resolution.height}"]`, @@ -621,83 +634,85 @@ * Opens photo resolution setting view. * * @param setting Setting of video device to be opened. - * @param resolItem Dom element from upper layer menu item showing title of - * the selected resolution. + * @param resolutionItem Dom element from upper layer menu item showing title + * of the selected resolution. */ - private openPhotoResSettings(setting: DeviceSetting, resolItem: HTMLElement) { + private openPhotoResSettings( + setting: DeviceSetting, resolutionItem: HTMLElement) { const {deviceId, photo} = setting; this.openedSettingDeviceId = deviceId; this.updateMenu( - resolItem, this.photoResMenu, this.photoOptTextTempl, + resolutionItem, this.photoResolutionMenu, this.photoOptionTextTemplate, (r) => this.cameraManager.setPrefPhotoResolution(deviceId, r), - photo.resols, photo.prefResol); - this.openSubSettings(resolItem, ViewName.PHOTO_RESOLUTION_SETTINGS); + photo.resolutions, photo.prefResolution); + this.openSubSettings(resolutionItem, ViewName.PHOTO_RESOLUTION_SETTINGS); } /** * Opens video resolution setting view. * * @param setting Setting of video device to be opened. - * @param resolItem Dom element from upper layer menu item showing title of - * the selected resolution. + * @param resolutionItem Dom element from upper layer menu item showing title + * of the selected resolution. */ - private openVideoResSettings(setting: DeviceSetting, resolItem: HTMLElement) { + private openVideoResSettings( + setting: DeviceSetting, resolutionItem: HTMLElement) { const {deviceId, video} = setting; this.openedSettingDeviceId = deviceId; this.updateMenu( - resolItem, this.videoResMenu, this.videoOptTextTempl, + resolutionItem, this.videoResolutionMenu, this.videoOptionTextTemplate, (r) => this.cameraManager.setPrefVideoResolution(deviceId, r), - video.resols, video.prefResol); - this.openSubSettings(resolItem, ViewName.VIDEO_RESOLUTION_SETTINGS); + video.resolutions, video.prefResolution); + this.openSubSettings(resolutionItem, ViewName.VIDEO_RESOLUTION_SETTINGS); } /** * Updates resolution menu with specified resolutions. * - * @param resolItem DOM element holding selected resolution. + * @param resolutionItem DOM element holding selected resolution. * @param menu Menu holding all resolution option elements. - * @param optTextTempl Template generating text content for each resolution - * option from its width and height. + * @param optionTextTemplate Template generating text content for each + * resolution option from its width and height. * @param onChange Called when selected option changed with resolution of * newly selected option. * @param resolutions Resolutions of its width and height to be updated with. - * @param selectedR Selected resolution. + * @param selectedResolution Selected resolution. */ private updateMenu( - resolItem: HTMLElement, + resolutionItem: HTMLElement, menu: HTMLElement, - optTextTempl: + optionTextTemplate: (resolution: Resolution, resolutions: ResolutionList) => string, onChange: (resolution: Resolution) => void, resolutions: ResolutionList, - selectedR: Resolution, + selectedResolution: Resolution, ) { const captionText = - dom.getFrom(resolItem, '.description>span', HTMLSpanElement); + dom.getFrom(resolutionItem, '.description>span', HTMLSpanElement); captionText.textContent = ''; for (const element of dom.getAllFrom( menu, '.menu-item', HTMLLabelElement)) { assertExists(element.parentNode).removeChild(element); } - for (const r of resolutions) { + for (const resolution of resolutions) { const item = util.instantiateTemplate('#resolution-item-template'); const input = dom.getFrom(item, 'input', HTMLInputElement); dom.getFrom(item, 'span', HTMLSpanElement).textContent = - optTextTempl(r, resolutions); + optionTextTemplate(resolution, resolutions); input.name = assertExists(menu.dataset[I18nString.NAME]); - input.dataset['width'] = r.width.toString(); - input.dataset['height'] = r.height.toString(); - if (r.equals(selectedR)) { - captionText.textContent = optTextTempl(r, resolutions); + input.dataset['width'] = resolution.width.toString(); + input.dataset['height'] = resolution.height.toString(); + if (resolution.equals(selectedResolution)) { + captionText.textContent = optionTextTemplate(resolution, resolutions); input.checked = true; } input.disabled = state.get(state.State.CAMERA_CONFIGURING) || state.get(state.State.TAKING); input.addEventListener('change', () => { if (input.checked) { - captionText.textContent = optTextTempl(r, resolutions); - onChange(r); + captionText.textContent = optionTextTemplate(resolution, resolutions); + onChange(resolution); } }); menu.appendChild(item);
diff --git a/ash/webui/camera_app_ui/resources/js/window_controller.ts b/ash/webui/camera_app_ui/resources/js/window_controller.ts index 37a62eeb..eb22763 100644 --- a/ash/webui/camera_app_ui/resources/js/window_controller.ts +++ b/ash/webui/camera_app_ui/resources/js/window_controller.ts
@@ -42,7 +42,9 @@ windowMonitorCallbackRouter.onWindowStateChanged.addListener( (states: WindowStateType[]) => { this.windowStates = states; - this.listeners.forEach((listener) => listener(states)); + for (const listener of this.listeners) { + listener(states); + } }); const {states} = await this.windowStateController.addMonitor( windowMonitorCallbackRouter.$.bindNewPipeAndPassRemote());
diff --git a/ash/webui/eche_app_ui/BUILD.gn b/ash/webui/eche_app_ui/BUILD.gn index 4b223329..d5f6319 100644 --- a/ash/webui/eche_app_ui/BUILD.gn +++ b/ash/webui/eche_app_ui/BUILD.gn
@@ -28,6 +28,8 @@ "eche_connector.h", "eche_connector_impl.cc", "eche_connector_impl.h", + "eche_display_stream_handler.cc", + "eche_display_stream_handler.h", "eche_feature_status_provider.cc", "eche_feature_status_provider.h", "eche_message_receiver.cc", @@ -114,10 +116,12 @@ "apps_access_setup_operation_unittest.cc", "eche_app_manager_unittest.cc", "eche_connector_impl_unittest.cc", + "eche_display_stream_handler_unittest.cc", "eche_feature_status_provider_unittest.cc", "eche_message_receiver_impl_unittest.cc", "eche_notification_click_handler_unittest.cc", "eche_notification_generator_unittest.cc", + "eche_presence_manager_unittest.cc", "eche_recent_app_click_handler_unittest.cc", "eche_signaler_unittest.cc", "eche_uid_provider_unittest.cc",
diff --git a/ash/webui/eche_app_ui/eche_app_manager.cc b/ash/webui/eche_app_ui/eche_app_manager.cc index 397a5bbc..142b2dc 100644 --- a/ash/webui/eche_app_ui/eche_app_manager.cc +++ b/ash/webui/eche_app_ui/eche_app_manager.cc
@@ -9,6 +9,7 @@ #include "ash/services/secure_channel/public/cpp/client/connection_manager_impl.h" #include "ash/webui/eche_app_ui/apps_access_manager_impl.h" #include "ash/webui/eche_app_ui/eche_connector_impl.h" +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" #include "ash/webui/eche_app_ui/eche_message_receiver_impl.h" #include "ash/webui/eche_app_ui/eche_notification_generator.h" #include "ash/webui/eche_app_ui/eche_presence_manager.h" @@ -59,11 +60,13 @@ launch_eche_app_function, close_eche_app_function, launch_notification_function)), + display_stream_handler_(std::make_unique<EcheDisplayStreamHandler>()), eche_notification_click_handler_( std::make_unique<EcheNotificationClickHandler>( phone_hub_manager, feature_status_provider_.get(), - launch_app_helper_.get())), + launch_app_helper_.get(), + display_stream_handler_.get())), eche_connector_( std::make_unique<EcheConnectorImpl>(feature_status_provider_.get(), connection_manager_.get())), @@ -83,7 +86,8 @@ std::make_unique<EcheRecentAppClickHandler>( phone_hub_manager, feature_status_provider_.get(), - launch_app_helper_.get())), + launch_app_helper_.get(), + display_stream_handler_.get())), notification_generator_(std::make_unique<EcheNotificationGenerator>( launch_app_helper_.get())), apps_access_manager_(std::make_unique<AppsAccessManagerImpl>( @@ -119,6 +123,11 @@ notification_generator_->Bind(std::move(receiver)); } +void EcheAppManager::BindDisplayStreamHandlerInterface( + mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver) { + display_stream_handler_->Bind(std::move(receiver)); +} + AppsAccessManager* EcheAppManager::GetAppsAccessManager() { return apps_access_manager_.get(); } @@ -136,6 +145,7 @@ signaler_.reset(); eche_connector_.reset(); eche_notification_click_handler_.reset(); + display_stream_handler_.reset(); launch_app_helper_.reset(); feature_status_provider_.reset(); connection_manager_.reset();
diff --git a/ash/webui/eche_app_ui/eche_app_manager.h b/ash/webui/eche_app_ui/eche_app_manager.h index f911b64..a839bf5 100644 --- a/ash/webui/eche_app_ui/eche_app_manager.h +++ b/ash/webui/eche_app_ui/eche_app_manager.h
@@ -41,6 +41,7 @@ class SystemInfo; class SystemInfoProvider; class AppsAccessManager; +class EcheDisplayStreamHandler; // Implements the core logic of the EcheApp and exposes interfaces via its // public API. Implemented as a KeyedService since it depends on other @@ -75,6 +76,9 @@ void BindNotificationGeneratorInterface( mojo::PendingReceiver<mojom::NotificationGenerator> receiver); + void BindDisplayStreamHandlerInterface( + mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver); + AppsAccessManager* GetAppsAccessManager(); // KeyedService: @@ -84,6 +88,7 @@ std::unique_ptr<secure_channel::ConnectionManager> connection_manager_; std::unique_ptr<EcheFeatureStatusProvider> feature_status_provider_; std::unique_ptr<LaunchAppHelper> launch_app_helper_; + std::unique_ptr<EcheDisplayStreamHandler> display_stream_handler_; std::unique_ptr<EcheNotificationClickHandler> eche_notification_click_handler_; std::unique_ptr<EcheConnector> eche_connector_;
diff --git a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc index 332ddd01..ddff94f 100644 --- a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc +++ b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc
@@ -12,6 +12,7 @@ #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" #include "ash/services/secure_channel/public/cpp/client/presence_monitor_client.h" #include "ash/services/secure_channel/public/cpp/client/presence_monitor_client_impl.h" +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" #include "ash/webui/eche_app_ui/system_info.h" #include "base/bind.h" @@ -134,6 +135,10 @@ return notification_generator_remote_; } + mojo::Remote<mojom::DisplayStreamHandler>& display_stream_handler_remote() { + return display_stream_handler_remote_; + } + void Bind() { manager_->BindSignalingMessageExchangerInterface( signaling_message_exchanger_remote_.BindNewPipeAndPassReceiver()); @@ -143,6 +148,8 @@ uid_generator_remote_.BindNewPipeAndPassReceiver()); manager_->BindNotificationGeneratorInterface( notification_generator_remote_.BindNewPipeAndPassReceiver()); + manager_->BindDisplayStreamHandlerInterface( + display_stream_handler_remote_.BindNewPipeAndPassReceiver()); } private: @@ -163,6 +170,7 @@ mojo::Remote<mojom::SystemInfoProvider> system_info_provider_remote_; mojo::Remote<mojom::UidGenerator> uid_generator_remote_; mojo::Remote<mojom::NotificationGenerator> notification_generator_remote_; + mojo::Remote<mojom::DisplayStreamHandler> display_stream_handler_remote_; }; TEST_F(EcheAppManagerTest, BindCheck) { @@ -170,6 +178,7 @@ EXPECT_FALSE(system_info_provider_remote()); EXPECT_FALSE(uid_generator_remote()); EXPECT_FALSE(notification_generator_remote()); + EXPECT_FALSE(display_stream_handler_remote()); Bind(); @@ -177,6 +186,7 @@ EXPECT_TRUE(system_info_provider_remote()); EXPECT_TRUE(uid_generator_remote()); EXPECT_TRUE(notification_generator_remote()); + EXPECT_TRUE(display_stream_handler_remote()); } } // namespace eche_app
diff --git a/ash/webui/eche_app_ui/eche_app_ui.cc b/ash/webui/eche_app_ui/eche_app_ui.cc index 32d39a4..fab04df 100644 --- a/ash/webui/eche_app_ui/eche_app_ui.cc +++ b/ash/webui/eche_app_ui/eche_app_ui.cc
@@ -24,12 +24,14 @@ BindSignalingMessageExchangerCallback exchanger_callback, BindSystemInfoProviderCallback system_info_callback, BindUidGeneratorCallback generator_callback, - BindNotificationGeneratorCallback notification_callback) + BindNotificationGeneratorCallback notification_callback, + BindDisplayStreamHandlerCallback stream_handler_callback) : ui::MojoWebUIController(web_ui), bind_exchanger_callback_(std::move(exchanger_callback)), bind_system_info_callback_(std::move(system_info_callback)), bind_generator_callback_(std::move(generator_callback)), - bind_notification_callback_(std::move(notification_callback)) { + bind_notification_callback_(std::move(notification_callback)), + bind_stream_handler_callback_(std::move(stream_handler_callback)) { auto* browser_context = web_ui->GetWebContents()->GetBrowserContext(); content::WebUIDataSource* html_source = content::WebUIDataSource::CreateAndAdd(browser_context, @@ -111,6 +113,11 @@ bind_notification_callback_.Run(std::move(receiver)); } +void EcheAppUI::BindInterface( + mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver) { + bind_stream_handler_callback_.Run(std::move(receiver)); +} + WEB_UI_CONTROLLER_TYPE_IMPL(EcheAppUI) } // namespace eche_app
diff --git a/ash/webui/eche_app_ui/eche_app_ui.h b/ash/webui/eche_app_ui/eche_app_ui.h index f06b3117..30387785 100644 --- a/ash/webui/eche_app_ui/eche_app_ui.h +++ b/ash/webui/eche_app_ui/eche_app_ui.h
@@ -22,12 +22,15 @@ base::RepeatingCallback<void(mojo::PendingReceiver<mojom::UidGenerator>)>; using BindNotificationGeneratorCallback = base::RepeatingCallback<void( mojo::PendingReceiver<mojom::NotificationGenerator>)>; + using BindDisplayStreamHandlerCallback = base::RepeatingCallback<void( + mojo::PendingReceiver<mojom::DisplayStreamHandler>)>; EcheAppUI(content::WebUI* web_ui, BindSignalingMessageExchangerCallback exchanger_callback, BindSystemInfoProviderCallback system_info_callback, BindUidGeneratorCallback generator_callback, - BindNotificationGeneratorCallback notification_callback); + BindNotificationGeneratorCallback notification_callback, + BindDisplayStreamHandlerCallback stream_handler_callback); EcheAppUI(const EcheAppUI&) = delete; EcheAppUI& operator=(const EcheAppUI&) = delete; ~EcheAppUI() override; @@ -42,11 +45,15 @@ void BindInterface( mojo::PendingReceiver<mojom::NotificationGenerator> receiver); + void BindInterface( + mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver); + private: const BindSignalingMessageExchangerCallback bind_exchanger_callback_; const BindSystemInfoProviderCallback bind_system_info_callback_; const BindUidGeneratorCallback bind_generator_callback_; const BindNotificationGeneratorCallback bind_notification_callback_; + const BindDisplayStreamHandlerCallback bind_stream_handler_callback_; WEB_UI_CONTROLLER_TYPE_DECL(); };
diff --git a/ash/webui/eche_app_ui/eche_display_stream_handler.cc b/ash/webui/eche_app_ui/eche_display_stream_handler.cc new file mode 100644 index 0000000..d1b9f86a9 --- /dev/null +++ b/ash/webui/eche_app_ui/eche_display_stream_handler.cc
@@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" + +#include "ash/webui/eche_app_ui/launch_app_helper.h" +#include "chromeos/components/multidevice/logging/logging.h" + +namespace ash { +namespace eche_app { + +EcheDisplayStreamHandler::EcheDisplayStreamHandler() = default; + +EcheDisplayStreamHandler::~EcheDisplayStreamHandler() = default; + +void EcheDisplayStreamHandler::StartStreaming() { + PA_LOG(INFO) << "echeapi EcheDisplayStreamHandler StartStreaming"; + NotifyStartStreaming(); +} + +void EcheDisplayStreamHandler::Bind( + mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver) { + display_stream_receiver_.reset(); + display_stream_receiver_.Bind(std::move(receiver)); +} + +void EcheDisplayStreamHandler::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void EcheDisplayStreamHandler::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void EcheDisplayStreamHandler::NotifyStartStreaming() { + for (auto& observer : observer_list_) + observer.OnStartStreaming(); +} + +} // namespace eche_app +} // namespace ash
diff --git a/ash/webui/eche_app_ui/eche_display_stream_handler.h b/ash/webui/eche_app_ui/eche_display_stream_handler.h new file mode 100644 index 0000000..339b1b2 --- /dev/null +++ b/ash/webui/eche_app_ui/eche_display_stream_handler.h
@@ -0,0 +1,60 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WEBUI_ECHE_APP_UI_ECHE_DISPLAY_STREAM_HANDLER_H_ +#define ASH_WEBUI_ECHE_APP_UI_ECHE_DISPLAY_STREAM_HANDLER_H_ + +#include "ash/webui/eche_app_ui/mojom/eche_app.mojom.h" +#include "base/observer_list.h" +#include "base/observer_list_types.h" +#include "mojo/public/cpp/bindings/receiver.h" + +namespace ash { +namespace eche_app { + +// Implements the EcheDisplayStreamHandler interface to allow the WebUI to sync +// the status of the display stream for Eche, e.g. When the display stream is +// started in the Eche Web, we can register `Observer` and get this status via +// `OnStartStreaming` event. +// TODO(paulzchen): Consider using `DisplayStreamEventHandler` to replace +// `DisplayStreamHandler`. +class EcheDisplayStreamHandler : public mojom::DisplayStreamHandler { + public: + class Observer : public base::CheckedObserver { + public: + ~Observer() override = default; + + // Called when the streaming is ready. About another status: + // OnStopStreaming, we prefer to listen to the stop signal when the bubble + // is really closed. + // TODO(paulzchen): Using generic method `OnStreamStatusChanged`. + virtual void OnStartStreaming() = 0; + }; + + EcheDisplayStreamHandler(); + ~EcheDisplayStreamHandler() override; + + EcheDisplayStreamHandler(const EcheDisplayStreamHandler&) = delete; + EcheDisplayStreamHandler& operator=(const EcheDisplayStreamHandler&) = delete; + + // mojom::DisplayStreamHandler: + void StartStreaming() override; + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + void Bind(mojo::PendingReceiver<mojom::DisplayStreamHandler> receiver); + + protected: + void NotifyStartStreaming(); + + private: + mojo::Receiver<mojom::DisplayStreamHandler> display_stream_receiver_{this}; + base::ObserverList<Observer> observer_list_; +}; + +} // namespace eche_app +} // namespace ash + +#endif // ASH_WEBUI_ECHE_APP_UI_ECHE_DISPLAY_STREAM_HANDLER_H_
diff --git a/ash/webui/eche_app_ui/eche_display_stream_handler_unittest.cc b/ash/webui/eche_app_ui/eche_display_stream_handler_unittest.cc new file mode 100644 index 0000000..fec38a0 --- /dev/null +++ b/ash/webui/eche_app_ui/eche_display_stream_handler_unittest.cc
@@ -0,0 +1,63 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { +namespace eche_app { +namespace { + +class FakeObserver : public EcheDisplayStreamHandler::Observer { + public: + FakeObserver() = default; + ~FakeObserver() override = default; + + size_t num_calls() const { return num_calls_; } + + // EcheDisplayStreamHandler::Observer: + void OnStartStreaming() override { ++num_calls_; } + + private: + size_t num_calls_ = 0; +}; + +} // namespace + +class EcheDisplayStreamHandlerTest : public testing::Test { + protected: + EcheDisplayStreamHandlerTest() = default; + EcheDisplayStreamHandlerTest(const EcheDisplayStreamHandlerTest&) = delete; + EcheDisplayStreamHandlerTest& operator=(const EcheDisplayStreamHandlerTest&) = + delete; + ~EcheDisplayStreamHandlerTest() override = default; + + // testing::Test: + void SetUp() override { + handler_ = std::make_unique<EcheDisplayStreamHandler>(); + handler_->AddObserver(&fake_observer_); + } + + void TearDown() override { + handler_->RemoveObserver(&fake_observer_); + handler_.reset(); + } + + void StartStreaming() { handler_->StartStreaming(); } + + size_t GetNumObserverCalls() const { return fake_observer_.num_calls(); } + + private: + FakeObserver fake_observer_; + std::unique_ptr<EcheDisplayStreamHandler> handler_; +}; + +TEST_F(EcheDisplayStreamHandlerTest, StartStreaming) { + StartStreaming(); + EXPECT_EQ(1u, GetNumObserverCalls()); +} + +} // namespace eche_app +} // namespace ash \ No newline at end of file
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler.cc b/ash/webui/eche_app_ui/eche_notification_click_handler.cc index 3a81369a..2f8a4e9d 100644 --- a/ash/webui/eche_app_ui/eche_notification_click_handler.cc +++ b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
@@ -6,6 +6,9 @@ #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/constants/ash_features.h" +#include "ash/root_window_controller.h" +#include "ash/shell.h" +#include "ash/system/eche/eche_tray.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" #include "chromeos/components/multidevice/logging/logging.h" @@ -15,9 +18,11 @@ EcheNotificationClickHandler::EcheNotificationClickHandler( phonehub::PhoneHubManager* phone_hub_manager, FeatureStatusProvider* feature_status_provider, - LaunchAppHelper* launch_app_helper) + LaunchAppHelper* launch_app_helper, + EcheDisplayStreamHandler* display_stream_handler) : feature_status_provider_(feature_status_provider), - launch_app_helper_(launch_app_helper) { + launch_app_helper_(launch_app_helper), + display_stream_handler_(display_stream_handler) { handler_ = phone_hub_manager->GetNotificationInteractionHandler(); feature_status_provider_->AddObserver(this); if (handler_ && IsClickable(feature_status_provider_->GetStatus())) { @@ -27,12 +32,18 @@ PA_LOG(INFO) << "No Phone Hub interaction handler to set Eche click handler"; } + + if (features::IsEcheSWAInBackgroundEnabled()) + display_stream_handler_->AddObserver(this); } EcheNotificationClickHandler::~EcheNotificationClickHandler() { feature_status_provider_->RemoveObserver(this); if (is_click_handler_set_ && handler_) handler_->RemoveNotificationClickHandler(this); + + if (features::IsEcheSWAInBackgroundEnabled()) + display_stream_handler_->RemoveObserver(this); } void EcheNotificationClickHandler::HandleNotificationClick( @@ -46,6 +57,7 @@ launch_app_helper_->LaunchEcheApp( notification_id, app_metadata.package_name, app_metadata.visible_app_name, app_metadata.user_id); + is_waiting_for_streaming_to_show_ = true; break; case LaunchAppHelper::AppLaunchProhibitedReason::kDisabledByScreenLock: launch_app_helper_->ShowNotification( @@ -80,15 +92,30 @@ } else if (is_click_handler_set_ && !clickable) { handler_->RemoveNotificationClickHandler(this); is_click_handler_set_ = false; + is_waiting_for_streaming_to_show_ = false; } if (NeedClose(feature_status_provider_->GetStatus()) && !base::FeatureList::IsEnabled(features::kEcheSWADebugMode)) { PA_LOG(INFO) << "Close Eche app window"; + is_waiting_for_streaming_to_show_ = false; launch_app_helper_->CloseEcheApp(); } } +void EcheNotificationClickHandler::OnStartStreaming() { + if (features::IsEcheCustomWidgetEnabled()) { + // TODO(paulzchen): Move the eche tray control to factory. + auto* eche_tray = Shell::GetPrimaryRootWindowController() + ->GetStatusAreaWidget() + ->eche_tray(); + if (eche_tray && is_waiting_for_streaming_to_show_) { + eche_tray->ShowBubble(); + is_waiting_for_streaming_to_show_ = false; + } + } +} + bool EcheNotificationClickHandler::IsClickable(FeatureStatus status) { return status == FeatureStatus::kDisconnected || status == FeatureStatus::kConnecting ||
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler.h b/ash/webui/eche_app_ui/eche_notification_click_handler.h index 5fe0c234..c46cc41 100644 --- a/ash/webui/eche_app_ui/eche_notification_click_handler.h +++ b/ash/webui/eche_app_ui/eche_notification_click_handler.h
@@ -10,6 +10,7 @@ #include "ash/components/phonehub/notification_interaction_handler.h" // TODO(https://crbug.com/1164001): move to forward declaration. #include "ash/components/phonehub/phone_hub_manager.h" +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" #include "ash/webui/eche_app_ui/feature_status_provider.h" #include "base/callback.h" @@ -20,11 +21,14 @@ // Handles notification clicks originating from Phone Hub notifications. class EcheNotificationClickHandler : public phonehub::NotificationClickHandler, - public FeatureStatusProvider::Observer { + public FeatureStatusProvider::Observer, + public EcheDisplayStreamHandler::Observer { public: - EcheNotificationClickHandler(phonehub::PhoneHubManager* phone_hub_manager, - FeatureStatusProvider* feature_status_provider, - LaunchAppHelper* launch_app_helper); + EcheNotificationClickHandler( + phonehub::PhoneHubManager* phone_hub_manager, + FeatureStatusProvider* feature_status_provider, + LaunchAppHelper* launch_app_helper, + EcheDisplayStreamHandler* display_stream_handler); ~EcheNotificationClickHandler() override; EcheNotificationClickHandler(const EcheNotificationClickHandler&) = delete; @@ -39,6 +43,14 @@ // FeatureStatusProvider::Observer: void OnFeatureStatusChanged() override; + // EcheDisplayStreamHandler::Observer: + void OnStartStreaming() override; + + // Test helpers, we need this to confirm the streaming will work as expected. + bool waiting_for_streaming_to_show() { + return is_waiting_for_streaming_to_show_; + } + private: bool IsClickable(FeatureStatus status); @@ -47,7 +59,9 @@ phonehub::NotificationInteractionHandler* handler_; FeatureStatusProvider* feature_status_provider_; LaunchAppHelper* launch_app_helper_; + EcheDisplayStreamHandler* display_stream_handler_; bool is_click_handler_set_ = false; + bool is_waiting_for_streaming_to_show_ = false; }; } // namespace eche_app
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler_unittest.cc b/ash/webui/eche_app_ui/eche_notification_click_handler_unittest.cc index fd49284..33be6b5 100644 --- a/ash/webui/eche_app_ui/eche_notification_click_handler_unittest.cc +++ b/ash/webui/eche_app_ui/eche_notification_click_handler_unittest.cc
@@ -8,6 +8,12 @@ #include "ash/components/phonehub/fake_phone_hub_manager.h" #include "ash/constants/ash_features.h" +#include "ash/system/eche/eche_tray.h" +#include "ash/system/status_area_widget_test_helper.h" +#include "ash/system/tray/tray_bubble_wrapper.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/ash_test_suite.h" +#include "ash/test/test_ash_web_view_factory.h" #include "ash/webui/eche_app_ui/fake_feature_status_provider.h" #include "ash/webui/eche_app_ui/fake_launch_app_helper.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" @@ -15,11 +21,12 @@ #include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/base/resource/resource_bundle.h" namespace ash { namespace eche_app { -class EcheNotificationClickHandlerTest : public testing::Test { +class EcheNotificationClickHandlerTest : public AshTestBase { protected: EcheNotificationClickHandlerTest() = default; EcheNotificationClickHandlerTest(const EcheNotificationClickHandlerTest&) = @@ -28,12 +35,23 @@ const EcheNotificationClickHandlerTest&) = delete; ~EcheNotificationClickHandlerTest() override = default; - // testing::Test: + // AshTestBase::Test: void SetUp() override { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kEcheSWA, features::kEcheCustomWidget}, + /*disabled_features=*/{}); + + DCHECK(test_web_view_factory_.get()); + + ui::ResourceBundle::CleanupSharedInstance(); + AshTestSuite::LoadTestResources(); + AshTestBase::SetUp(); + eche_tray_ = + ash::StatusAreaWidgetTestHelper::GetStatusAreaWidget()->eche_tray(); + fake_phone_hub_manager_.fake_feature_status_provider()->SetStatus( phonehub::FeatureStatus::kEnabledAndConnected); fake_feature_status_provider_.SetStatus(FeatureStatus::kIneligible); - scoped_feature_list_.InitWithFeatures({features::kEcheSWA}, {}); launch_app_helper_ = std::make_unique<FakeLaunchAppHelper>( &fake_phone_hub_manager_, base::BindRepeating( @@ -45,13 +63,16 @@ base::BindRepeating( &EcheNotificationClickHandlerTest::FakeLaunchNotificationFunction, base::Unretained(this))); + display_stream_handler_ = std::make_unique<EcheDisplayStreamHandler>(); handler_ = std::make_unique<EcheNotificationClickHandler>( &fake_phone_hub_manager_, &fake_feature_status_provider_, - launch_app_helper_.get()); + launch_app_helper_.get(), display_stream_handler_.get()); } void TearDown() override { + AshTestBase::TearDown(); launch_app_helper_.reset(); + display_stream_handler_.reset(); handler_.reset(); } @@ -91,12 +112,20 @@ ->notification_click_handler_count(); } + void StartStreaming() { handler_->OnStartStreaming(); } + bool close_eche_is_called() { return close_eche_is_called_; } size_t num_notifications_shown() { return num_notifications_shown_; } size_t num_app_launch() { return num_app_launch_; } + bool waiting_for_streaming_to_show() { + return handler_->waiting_for_streaming_to_show(); + } + + EcheTray* eche_tray() { return eche_tray_; } + void reset() { close_eche_is_called_ = false; num_notifications_shown_ = 0; @@ -110,9 +139,15 @@ base::test::ScopedFeatureList scoped_feature_list_; FakeFeatureStatusProvider fake_feature_status_provider_; std::unique_ptr<FakeLaunchAppHelper> launch_app_helper_; + std::unique_ptr<EcheDisplayStreamHandler> display_stream_handler_; bool close_eche_is_called_; size_t num_notifications_shown_ = 0; size_t num_app_launch_ = 0; + EcheTray* eche_tray_ = nullptr; // Not owned + + // Calling the factory constructor is enough to set it up. + std::unique_ptr<TestAshWebViewFactory> test_web_view_factory_ = + std::make_unique<TestAshWebViewFactory>(); }; TEST_F(EcheNotificationClickHandlerTest, StatusChangeTransitions) { @@ -199,5 +234,28 @@ EXPECT_EQ(num_notifications_shown(), 1u); } +TEST_F(EcheNotificationClickHandlerTest, StartStreaming) { + EXPECT_FALSE(waiting_for_streaming_to_show()); + + const int64_t notification_id = 0; + const char16_t app_name[] = u"Test App"; + const char package_name[] = "com.google.testapp"; + const int64_t user_id = 0; + phonehub::Notification::AppMetadata app_meta_data = + phonehub::Notification::AppMetadata(app_name, package_name, + /*icon=*/gfx::Image(), + /*icon_color=*/absl::nullopt, + /*icon_is_monochrome=*/true, user_id); + HandleNotificationClick(notification_id, app_meta_data); + + EXPECT_TRUE(waiting_for_streaming_to_show()); + + StartStreaming(); + + EXPECT_TRUE( + eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); + EXPECT_FALSE(waiting_for_streaming_to_show()); +} + } // namespace eche_app } // namespace ash
diff --git a/ash/webui/eche_app_ui/eche_presence_manager.cc b/ash/webui/eche_app_ui/eche_presence_manager.cc index f305aa47..14882050 100644 --- a/ash/webui/eche_app_ui/eche_presence_manager.cc +++ b/ash/webui/eche_app_ui/eche_presence_manager.cc
@@ -25,7 +25,7 @@ } // namespace EchePresenceManager::EchePresenceManager( - EcheFeatureStatusProvider* eche_feature_status_provider, + FeatureStatusProvider* eche_feature_status_provider, device_sync::DeviceSyncClient* device_sync_client, multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client, std::unique_ptr<secure_channel::PresenceMonitorClient>
diff --git a/ash/webui/eche_app_ui/eche_presence_manager.h b/ash/webui/eche_app_ui/eche_presence_manager.h index 99e447f6..93dfb9d 100644 --- a/ash/webui/eche_app_ui/eche_presence_manager.h +++ b/ash/webui/eche_app_ui/eche_presence_manager.h
@@ -29,7 +29,7 @@ public EcheMessageReceiver::Observer { public: EchePresenceManager( - EcheFeatureStatusProvider* eche_feature_status_provider, + FeatureStatusProvider* eche_feature_status_provider, device_sync::DeviceSyncClient* device_sync_client, multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client, std::unique_ptr<secure_channel::PresenceMonitorClient> @@ -59,7 +59,7 @@ void StopMonitoring(); void OnTimerExpired(); - EcheFeatureStatusProvider* eche_feature_status_provider_; + FeatureStatusProvider* eche_feature_status_provider_; device_sync::DeviceSyncClient* device_sync_client_; multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_; std::unique_ptr<secure_channel::PresenceMonitorClient>
diff --git a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc new file mode 100644 index 0000000..0f80bd0 --- /dev/null +++ b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
@@ -0,0 +1,196 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/webui/eche_app_ui/eche_presence_manager.h" + +#include "ash/constants/ash_features.h" +#include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" +#include "ash/services/secure_channel/public/cpp/client/presence_monitor_client_impl.h" +#include "ash/webui/eche_app_ui/fake_eche_connector.h" +#include "ash/webui/eche_app_ui/fake_eche_message_receiver.h" +#include "ash/webui/eche_app_ui/fake_feature_status_provider.h" +#include "ash/webui/eche_app_ui/proto/exo_messages.pb.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "chromeos/components/multidevice/remote_device_test_util.h" +#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { +namespace eche_app { + +static size_t num_start_monitor_calls_ = 0; +static size_t num_stop_monitor_calls_ = 0; + +namespace { + +class FakePresenceMonitorClient : public secure_channel::PresenceMonitorClient { + public: + FakePresenceMonitorClient() = default; + ~FakePresenceMonitorClient() override = default; + + private: + // secure_channel::PresenceMonitorClient: + void SetPresenceMonitorCallbacks( + chromeos::secure_channel::PresenceMonitor::ReadyCallback ready_callback, + chromeos::secure_channel::PresenceMonitor::DeviceSeenCallback + device_seen_callback) override {} + void StartMonitoring( + const multidevice::RemoteDeviceRef& remote_device_ref, + const multidevice::RemoteDeviceRef& local_device_ref) override { + num_start_monitor_calls_++; + } + void StopMonitoring() override { num_stop_monitor_calls_++; } +}; +} // namespace + +class EchePresenceManagerTest : public testing::Test { + protected: + EchePresenceManagerTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + test_remote_device_( + chromeos::multidevice::CreateRemoteDeviceRefForTest()), + test_devices_( + chromeos::multidevice::CreateRemoteDeviceRefListForTest(1)) {} + EchePresenceManagerTest(const EchePresenceManagerTest&) = delete; + EchePresenceManagerTest& operator=(const EchePresenceManagerTest&) = delete; + ~EchePresenceManagerTest() override = default; + + void SetUp() override { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{chromeos::features::kEcheSWA}, + /*disabled_features=*/{}); + fake_multidevice_setup_client_.SetHostStatusWithDevice(std::make_pair( + chromeos::multidevice_setup::mojom::HostStatus::kHostVerified, + test_remote_device_)); + fake_device_sync_client_.set_local_device_metadata(test_devices_[0]); + fake_device_sync_client_.NotifyReady(); + fake_eche_connector_ = std::make_unique<FakeEcheConnector>(); + fake_eche_message_receiver_ = std::make_unique<FakeEcheMessageReceiver>(); + fake_feature_status_provider_ = std::make_unique<FakeFeatureStatusProvider>( + FeatureStatus::kDependentFeature); + fake_presence_monitor_client_ = + std::make_unique<FakePresenceMonitorClient>(); + eche_presence_manager_ = std::make_unique<EchePresenceManager>( + fake_feature_status_provider_.get(), &fake_device_sync_client_, + &fake_multidevice_setup_client_, + std::move(fake_presence_monitor_client_), fake_eche_connector_.get(), + fake_eche_message_receiver_.get()); + } + + void TearDown() override { + eche_presence_manager_.reset(); + fake_eche_connector_.reset(); + fake_eche_message_receiver_.reset(); + fake_feature_status_provider_.reset(); + fake_presence_monitor_client_.reset(); + } + + void SetFeatureStatus(FeatureStatus status) { + fake_feature_status_provider_->SetStatus(status); + } + + FeatureStatus GetFeatureStatus() { + return fake_feature_status_provider_->GetStatus(); + } + + void SetStreamStatus(proto::StatusChangeType type) { + fake_eche_message_receiver_->FakeStatusChange(type); + } + + void Reset() { + num_start_monitor_calls_ = 0; + num_stop_monitor_calls_ = 0; + } + + base::test::TaskEnvironment task_environment_; + + private: + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<FakeEcheConnector> fake_eche_connector_; + std::unique_ptr<FakeEcheMessageReceiver> fake_eche_message_receiver_; + std::unique_ptr<FakeFeatureStatusProvider> fake_feature_status_provider_; + const chromeos::multidevice::RemoteDeviceRef test_remote_device_; + multidevice_setup::FakeMultiDeviceSetupClient fake_multidevice_setup_client_; + device_sync::FakeDeviceSyncClient fake_device_sync_client_; + const multidevice::RemoteDeviceRefList test_devices_; + std::unique_ptr<FakePresenceMonitorClient> fake_presence_monitor_client_; + std::unique_ptr<EchePresenceManager> eche_presence_manager_; +}; + +TEST_F(EchePresenceManagerTest, StopMonitoring) { + // Test feature status change to kNotEnabledByPhone + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kNotEnabledByPhone); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kIneligible + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kIneligible); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kDisabled + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kDisabled); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kDependentFeature + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kDependentFeature); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kDependentFeaturePending + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kDependentFeaturePending); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kDisconnected. + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kDisconnected); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test feature status change to kConnecting. + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetFeatureStatus(FeatureStatus::kConnecting); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test stream status change to stop. + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_STOP); + EXPECT_EQ(1u, num_stop_monitor_calls_); + + // Test 5 minutes not see device. + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + task_environment_.FastForwardBy(base::Minutes(5)); + EXPECT_EQ(1u, num_stop_monitor_calls_); +} + +TEST_F(EchePresenceManagerTest, StartMonitoring) { + Reset(); + SetFeatureStatus(FeatureStatus::kConnected); + SetStreamStatus(proto::StatusChangeType::TYPE_STREAM_START); + EXPECT_EQ(1u, num_start_monitor_calls_); +} + +} // namespace eche_app +} // namespace ash
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc index b23adca..42cca0e 100644 --- a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc +++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
@@ -5,6 +5,9 @@ #include "ash/webui/eche_app_ui/eche_recent_app_click_handler.h" #include "ash/components/phonehub/phone_hub_manager.h" +#include "ash/root_window_controller.h" +#include "ash/shell.h" +#include "ash/system/eche/eche_tray.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" #include "chromeos/components/multidevice/logging/logging.h" @@ -14,9 +17,11 @@ EcheRecentAppClickHandler::EcheRecentAppClickHandler( phonehub::PhoneHubManager* phone_hub_manager, FeatureStatusProvider* feature_status_provider, - LaunchAppHelper* launch_app_helper) + LaunchAppHelper* launch_app_helper, + EcheDisplayStreamHandler* display_stream_handler) : feature_status_provider_(feature_status_provider), - launch_app_helper_(launch_app_helper) { + launch_app_helper_(launch_app_helper), + display_stream_handler_(display_stream_handler) { notification_handler_ = phone_hub_manager->GetNotificationInteractionHandler(); recent_apps_handler_ = phone_hub_manager->GetRecentAppsInteractionHandler(); @@ -28,6 +33,9 @@ recent_apps_handler_->AddRecentAppClickObserver(this); is_click_handler_set_ = true; } + + if (features::IsEcheSWAInBackgroundEnabled()) + display_stream_handler_->AddObserver(this); } EcheRecentAppClickHandler::~EcheRecentAppClickHandler() { @@ -36,6 +44,9 @@ notification_handler_->RemoveNotificationClickHandler(this); if (recent_apps_handler_) recent_apps_handler_->RemoveRecentAppClickObserver(this); + + if (features::IsEcheSWAInBackgroundEnabled()) + display_stream_handler_->RemoveObserver(this); } void EcheRecentAppClickHandler::HandleNotificationClick( @@ -62,6 +73,7 @@ launch_app_helper_->LaunchEcheApp( /*notification_id=*/absl::nullopt, app_metadata.package_name, app_metadata.visible_app_name, app_metadata.user_id); + is_waiting_for_streaming_to_show_ = true; break; case LaunchAppHelper::AppLaunchProhibitedReason::kDisabledByScreenLock: launch_app_helper_->ShowNotification( @@ -97,6 +109,20 @@ notification_handler_->RemoveNotificationClickHandler(this); recent_apps_handler_->RemoveRecentAppClickObserver(this); is_click_handler_set_ = false; + is_waiting_for_streaming_to_show_ = false; + } +} + +void EcheRecentAppClickHandler::OnStartStreaming() { + if (features::IsEcheCustomWidgetEnabled()) { + // TODO(paulzchen): Move the eche tray control to factory. + auto* eche_tray = Shell::GetPrimaryRootWindowController() + ->GetStatusAreaWidget() + ->eche_tray(); + if (eche_tray && is_waiting_for_streaming_to_show_) { + eche_tray->ShowBubble(); + is_waiting_for_streaming_to_show_ = false; + } } }
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler.h b/ash/webui/eche_app_ui/eche_recent_app_click_handler.h index f5297a1..efdb6aae 100644 --- a/ash/webui/eche_app_ui/eche_recent_app_click_handler.h +++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler.h
@@ -12,6 +12,7 @@ #include "ash/components/phonehub/phone_hub_manager.h" #include "ash/components/phonehub/recent_app_click_observer.h" #include "ash/components/phonehub/recent_apps_interaction_handler.h" +#include "ash/webui/eche_app_ui/eche_display_stream_handler.h" #include "ash/webui/eche_app_ui/feature_status_provider.h" #include "base/callback.h" @@ -23,11 +24,13 @@ // Handles recent app clicks originating from Phone Hub recent apps. class EcheRecentAppClickHandler : public phonehub::NotificationClickHandler, public FeatureStatusProvider::Observer, - public phonehub::RecentAppClickObserver { + public phonehub::RecentAppClickObserver, + public EcheDisplayStreamHandler::Observer { public: EcheRecentAppClickHandler(phonehub::PhoneHubManager* phone_hub_manager, FeatureStatusProvider* feature_status_provider, - LaunchAppHelper* launch_app_helper); + LaunchAppHelper* launch_app_helper, + EcheDisplayStreamHandler* display_stream_handler); ~EcheRecentAppClickHandler() override; EcheRecentAppClickHandler(const EcheRecentAppClickHandler&) = delete; @@ -46,6 +49,14 @@ // FeatureStatusProvider::Observer: void OnFeatureStatusChanged() override; + // EcheDisplayStreamHandler::Observer: + void OnStartStreaming() override; + + // Test helpers + bool waiting_for_streaming_to_show() { + return is_waiting_for_streaming_to_show_; + } + private: bool IsClickable(FeatureStatus status); @@ -53,7 +64,9 @@ phonehub::RecentAppsInteractionHandler* recent_apps_handler_; FeatureStatusProvider* feature_status_provider_; LaunchAppHelper* launch_app_helper_; + EcheDisplayStreamHandler* display_stream_handler_; bool is_click_handler_set_ = false; + bool is_waiting_for_streaming_to_show_ = false; }; } // namespace eche_app
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc index 62c24678..1f4f7d4 100644 --- a/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc +++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler_unittest.cc
@@ -8,6 +8,12 @@ #include "ash/components/phonehub/fake_phone_hub_manager.h" #include "ash/constants/ash_features.h" +#include "ash/system/eche/eche_tray.h" +#include "ash/system/status_area_widget_test_helper.h" +#include "ash/system/tray/tray_bubble_wrapper.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/ash_test_suite.h" +#include "ash/test/test_ash_web_view_factory.h" #include "ash/webui/eche_app_ui/fake_feature_status_provider.h" #include "ash/webui/eche_app_ui/fake_launch_app_helper.h" #include "ash/webui/eche_app_ui/launch_app_helper.h" @@ -15,11 +21,12 @@ #include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/base/resource/resource_bundle.h" namespace ash { namespace eche_app { -class EcheRecentAppClickHandlerTest : public testing::Test { +class EcheRecentAppClickHandlerTest : public AshTestBase { protected: EcheRecentAppClickHandlerTest() = default; EcheRecentAppClickHandlerTest(const EcheRecentAppClickHandlerTest&) = delete; @@ -27,15 +34,25 @@ const EcheRecentAppClickHandlerTest&) = delete; ~EcheRecentAppClickHandlerTest() override = default; - // testing::Test: + // AshTestBase::Test: void SetUp() override { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kEcheSWA, features::kPhoneHubRecentApps, + features::kEcheCustomWidget}, + /*disabled_features=*/{}); + + DCHECK(test_web_view_factory_.get()); + + ui::ResourceBundle::CleanupSharedInstance(); + AshTestSuite::LoadTestResources(); + AshTestBase::SetUp(); + eche_tray_ = + ash::StatusAreaWidgetTestHelper::GetStatusAreaWidget()->eche_tray(); + fake_phone_hub_manager_.fake_feature_status_provider()->SetStatus( phonehub::FeatureStatus::kEnabledAndConnected); fake_feature_status_provider_.SetStatus(FeatureStatus::kIneligible); - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{features::kEcheSWA, - features::kPhoneHubRecentApps}, - /*disabled_features=*/{}); + launch_app_helper_ = std::make_unique<FakeLaunchAppHelper>( &fake_phone_hub_manager_, base::BindRepeating( @@ -47,13 +64,16 @@ base::BindRepeating( &EcheRecentAppClickHandlerTest::FakeLaunchNotificationFunction, base::Unretained(this))); + display_stream_handler_ = std::make_unique<EcheDisplayStreamHandler>(); handler_ = std::make_unique<EcheRecentAppClickHandler>( &fake_phone_hub_manager_, &fake_feature_status_provider_, - launch_app_helper_.get()); + launch_app_helper_.get(), display_stream_handler_.get()); } void TearDown() override { + AshTestBase::TearDown(); launch_app_helper_.reset(); + display_stream_handler_.reset(); handler_.reset(); } @@ -103,21 +123,35 @@ ->FetchRecentAppMetadataList(); } + void StartStreaming() { handler_->OnStartStreaming(); } + const std::string& get_package_name() { return package_name_; } const std::u16string& get_visible_name() { return visible_name_; } int64_t get_user_id() { return user_id_; } + bool waiting_for_streaming_to_show() { + return handler_->waiting_for_streaming_to_show(); + } + + EcheTray* eche_tray() { return eche_tray_; } + private: phonehub::FakePhoneHubManager fake_phone_hub_manager_; base::test::ScopedFeatureList scoped_feature_list_; FakeFeatureStatusProvider fake_feature_status_provider_; std::unique_ptr<LaunchAppHelper> launch_app_helper_; std::unique_ptr<EcheRecentAppClickHandler> handler_; + std::unique_ptr<EcheDisplayStreamHandler> display_stream_handler_; std::string package_name_; std::u16string visible_name_; int64_t user_id_; + EcheTray* eche_tray_ = nullptr; // Not owned + + // Calling the factory constructor is enough to set it up. + std::unique_ptr<TestAshWebViewFactory> test_web_view_factory_ = + std::make_unique<TestAshWebViewFactory>(); }; TEST_F(EcheRecentAppClickHandlerTest, StatusChangeTransitions) { @@ -182,5 +216,25 @@ EXPECT_EQ(fake_app_metadata.user_id, app_metadata[0].user_id); } +TEST_F(EcheRecentAppClickHandlerTest, StartStreaming) { + EXPECT_FALSE(waiting_for_streaming_to_show()); + + const int64_t user_id = 1; + const char16_t app_visible_name[] = u"Fake App"; + const char package_name[] = "com.fakeapp"; + auto fake_app_metadata = phonehub::Notification::AppMetadata( + app_visible_name, package_name, gfx::Image(), + /*icon_color=*/absl::nullopt, /*icon_is_monochrome=*/true, user_id); + RecentAppClicked(fake_app_metadata); + + EXPECT_TRUE(waiting_for_streaming_to_show()); + + StartStreaming(); + + EXPECT_TRUE( + eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); + EXPECT_FALSE(waiting_for_streaming_to_show()); +} + } // namespace eche_app } // namespace ash
diff --git a/ash/webui/eche_app_ui/eche_uid_provider.h b/ash/webui/eche_app_ui/eche_uid_provider.h index b01264b..5c3ba34 100644 --- a/ash/webui/eche_app_ui/eche_uid_provider.h +++ b/ash/webui/eche_app_ui/eche_uid_provider.h
@@ -37,6 +37,8 @@ void Bind(mojo::PendingReceiver<mojom::UidGenerator> receiver); private: + friend class EcheUidProviderTest; + std::string ConvertBinaryToString(base::span<const uint8_t> src); absl::optional<std::vector<uint8_t>> ConvertStringToBinary( base::StringPiece str,
diff --git a/ash/webui/eche_app_ui/eche_uid_provider_unittest.cc b/ash/webui/eche_app_ui/eche_uid_provider_unittest.cc index 0eff021..e2f2a0dd 100644 --- a/ash/webui/eche_app_ui/eche_uid_provider_unittest.cc +++ b/ash/webui/eche_app_ui/eche_uid_provider_unittest.cc
@@ -5,7 +5,8 @@ #include "ash/webui/eche_app_ui/eche_uid_provider.h" #include <base/base64.h> - +#include "base/task/single_thread_task_runner.h" +#include "base/test/task_environment.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" @@ -13,16 +14,62 @@ namespace ash { namespace eche_app { +class TaskRunner { + public: + TaskRunner() = default; + ~TaskRunner() = default; + + void WaitForResult() { run_loop_.Run(); } + + void Finish() { run_loop_.Quit(); } + + private: + base::test::SingleThreadTaskEnvironment task_environment_; + base::RunLoop run_loop_; +}; + +class EcheUidProviderTest; + class Callback { public: - static void GetUidCallback(const std::string& uid) { uid_ = uid; } + static void GetUidCallback(const std::string& uid) { + uid_ = uid; + if (task_runner_) { + task_runner_->Finish(); + } + } + + static void setTaskRunner(TaskRunner* task_runner) { + task_runner_ = task_runner; + } + static std::string GetUid() { return uid_; } static void ResetUid() { uid_ = ""; } private: + static TaskRunner* task_runner_; static std::string uid_; }; +class FakeExchangerClient : public mojom::UidGenerator { + public: + FakeExchangerClient() = default; + ~FakeExchangerClient() override = default; + + mojo::PendingReceiver<mojom::UidGenerator> CreatePendingReceiver() { + return remote_.BindNewPipeAndPassReceiver(); + } + + // mojom::UidGenerator: + void GetUid(base::OnceCallback<void(const std::string&)> callback) override { + remote_->GetUid(base::BindOnce(std::move(callback))); + } + + private: + mojo::Remote<mojom::UidGenerator> remote_; +}; + +ash::eche_app::TaskRunner* ash::eche_app::Callback::task_runner_ = nullptr; std::string ash::eche_app::Callback::uid_ = ""; class EcheUidProviderTest : public testing::Test { @@ -53,10 +100,17 @@ void GetUid() { uid_provider_->GetUid(base::BindOnce(&Callback::GetUidCallback)); } + absl::optional<std::vector<uint8_t>> DecodeStringWithSeed( + size_t expected_len) { + std::string pref_seed = pref_service_.GetString(kEcheAppSeedPref); + return uid_provider_->ConvertStringToBinary(pref_seed, expected_len); + } + + TaskRunner task_runner_; + std::unique_ptr<EcheUidProvider> uid_provider_; private: TestingPrefServiceSimple pref_service_; - std::unique_ptr<EcheUidProvider> uid_provider_; }; TEST_F(EcheUidProviderTest, GetUidHasValue) { @@ -87,5 +141,30 @@ EXPECT_NE(Callback::GetUid(), uid); } +TEST_F(EcheUidProviderTest, BindPendingReceiverCanGetUid) { + Callback::setTaskRunner(&task_runner_); + FakeExchangerClient fake_exchanger_client; + uid_provider_->Bind(fake_exchanger_client.CreatePendingReceiver()); + + fake_exchanger_client.GetUid(base::BindOnce(&Callback::GetUidCallback)); + task_runner_.WaitForResult(); + + EXPECT_NE(Callback::GetUid(), ""); +} + +TEST_F(EcheUidProviderTest, GetBinaryWhenSeedSizeCorrect) { + GetUid(); + ResetUidProvider(); + + EXPECT_NE(DecodeStringWithSeed(kSeedSizeInByte), absl::nullopt); +} + +TEST_F(EcheUidProviderTest, GetNulloptWhenSeedSizeIncorrect) { + GetUid(); + ResetUidProvider(); + + EXPECT_EQ(DecodeStringWithSeed(kSeedSizeInByte - 1), absl::nullopt); +} + } // namespace eche_app } // namespace ash
diff --git a/ash/webui/eche_app_ui/mojom/eche_app.mojom b/ash/webui/eche_app_ui/mojom/eche_app.mojom index c05d75c..3bfbaa1 100644 --- a/ash/webui/eche_app_ui/mojom/eche_app.mojom +++ b/ash/webui/eche_app_ui/mojom/eche_app.mojom
@@ -88,3 +88,10 @@ ShowNotification(mojo_base.mojom.String16 title, mojo_base.mojom.String16 message, WebNotificationType type); }; + +// Interface for streaming a display video with which the connection is +// established. TODO(paulzchen): Using generic method `OnStreamStatusChanged`. +interface DisplayStreamHandler { + // Stream a display video for Eche. + StartStreaming(); +};
diff --git a/ash/webui/eche_app_ui/resources/browser_proxy.js b/ash/webui/eche_app_ui/resources/browser_proxy.js index d8bd8e4..f1ede4e 100644 --- a/ash/webui/eche_app_ui/resources/browser_proxy.js +++ b/ash/webui/eche_app_ui/resources/browser_proxy.js
@@ -43,6 +43,8 @@ const notificationGenerator = ash.echeApp.mojom.NotificationGenerator.getRemote(); +const displayStreamHandler = ash.echeApp.mojom.DisplayStreamHandler.getRemote(); + /** * A pipe through which we can send messages to the guest frame. * Use an undefined `target` to find the <iframe> automatically. @@ -148,6 +150,12 @@ histogramData.maxValue); }); + // Register START_STREAMING pipes. + guestMessagePipe.registerHandler(Message.START_STREAMING, async () => { + console.log('echeapi browser_proxy.js startStreaming'); + displayStreamHandler.startStreaming(); + }); + // We can't access hash change event inside iframe so parse the notification // info from the anchor part of the url when hash is changed and send them to // untrusted section via message pipes.
diff --git a/ash/webui/eche_app_ui/resources/message_types.js b/ash/webui/eche_app_ui/resources/message_types.js index ef67b8b..73259ea 100644 --- a/ash/webui/eche_app_ui/resources/message_types.js +++ b/ash/webui/eche_app_ui/resources/message_types.js
@@ -89,4 +89,6 @@ TIME_HISTOGRAM_MESSAGE: 'time_histagram_message', // Message for sending metrics data for recording enum histogram. ENUM_HISTOGRAM_MESSAGE: 'enum_histagram_message', + // Message for starting the display video of Eche. + START_STREAMING: 'start_streaming', };
diff --git a/ash/webui/eche_app_ui/resources/receiver.js b/ash/webui/eche_app_ui/resources/receiver.js index fd17229..f598738 100644 --- a/ash/webui/eche_app_ui/resources/receiver.js +++ b/ash/webui/eche_app_ui/resources/receiver.js
@@ -93,6 +93,11 @@ Message.SHOW_NOTIFICATION, {title, message, notificationType}); } + startStreaming() { + console.log('echeapi receiver.js startStreaming'); + parentMessagePipe.sendMessage(Message.START_STREAMING); + } + sendTimeHistogram(histogram, value) { console.log('echeapi receiver.js sendTimeHistogram'); parentMessagePipe.sendMessage( @@ -131,6 +136,8 @@ EcheApiBindingImpl.onReceivedNotification.bind(EcheApiBindingImpl); echeapi.system.showCrOSNotification = EcheApiBindingImpl.showNotification.bind(EcheApiBindingImpl); +echeapi.system.startStreaming = + EcheApiBindingImpl.startStreaming.bind(EcheApiBindingImpl); echeapi.system.sendTimeHistogram = EcheApiBindingImpl.sendTimeHistogram.bind(EcheApiBindingImpl); echeapi.system.sendEnumHistogram =
diff --git a/ash/webui/system_extensions_internals_ui/BUILD.gn b/ash/webui/system_extensions_internals_ui/BUILD.gn index d260625a..7b0a72a 100644 --- a/ash/webui/system_extensions_internals_ui/BUILD.gn +++ b/ash/webui/system_extensions_internals_ui/BUILD.gn
@@ -19,6 +19,7 @@ deps = [ "//ash/webui/resources:system_extensions_internals_resources", + "//ash/webui/system_extensions_internals_ui/mojom", "//content/public/browser", "//ui/webui", ] @@ -30,7 +31,13 @@ } js_library("system_extensions_internals") { - sources = [ "resources/index.js" ] + sources = [ + "resources/index.js", + "resources/page_handler.js", + ] + externs_list = + [ "//ash/webui/web_applications/externs/file_handling.externs.js" ] + deps = [ "//ash/webui/system_extensions_internals_ui/mojom:mojom_webui_js" ] } js2gtest("browser_tests_js") { @@ -43,13 +50,32 @@ grd_prefix = "ash_system_extensions_internals" +mojo_grdp = "$target_gen_dir/system_extensions_internals_mojo_resources.grdp" + +generate_grd("build_mojo_grdp") { + grd_prefix = grd_prefix + out_grd = mojo_grdp + + deps = [ "//ash/webui/system_extensions_internals_ui/mojom:mojom_webui_js" ] + + # Flatten out the dependency tree of your mojom and add generated bindings + # file here. + input_files = [ "ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom-webui.js" ] + + input_files_base_dir = + rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir") +} + generate_grd("build_grd") { input_files_base_dir = rebase_path("resources", "//") input_files = [ "index.html", "index.js", + "page_handler.js", ] grd_prefix = grd_prefix out_grd = "$target_gen_dir/${grd_prefix}_resources.grd" + deps = [ ":build_mojo_grdp" ] + grdp_files = [ mojo_grdp ] }
diff --git a/ash/webui/system_extensions_internals_ui/mojom/BUILD.gn b/ash/webui/system_extensions_internals_ui/mojom/BUILD.gn new file mode 100644 index 0000000..0755d379 --- /dev/null +++ b/ash/webui/system_extensions_internals_ui/mojom/BUILD.gn
@@ -0,0 +1,15 @@ +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/chromeos/ui_mode.gni") +import("//mojo/public/tools/bindings/mojom.gni") + +assert(is_chromeos_ash, "System Extensions Internals is ash-chrome only") + +mojom("mojom") { + sources = [ "system_extensions_internals_ui.mojom" ] + + public_deps = [ "//mojo/public/mojom/base" ] + webui_module_path = "/ash/webui/system_extensions_internals_ui/mojom/" +}
diff --git a/ash/webui/system_extensions_internals_ui/mojom/OWNERS b/ash/webui/system_extensions_internals_ui/mojom/OWNERS new file mode 100644 index 0000000..08850f4 --- /dev/null +++ b/ash/webui/system_extensions_internals_ui/mojom/OWNERS
@@ -0,0 +1,2 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom b/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom new file mode 100644 index 0000000..5af20556 --- /dev/null +++ b/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom
@@ -0,0 +1,15 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module ash.mojom.system_extensions_internals; + +import "mojo/public/mojom/base/safe_base_name.mojom"; + +// Interface for installing an unpacked System Extension. +interface PageHandler { + // Installs a system extension from `system_extension_dir_name`, which must be + // a folder located at the top level of the default Downloads directory. + InstallSystemExtensionFromDownloadsDir( + mojo_base.mojom.SafeBaseName system_extension_dir_name) => (bool success); +};
diff --git a/ash/webui/system_extensions_internals_ui/resources/index.js b/ash/webui/system_extensions_internals_ui/resources/index.js index 1441a844..b1dc57b 100644 --- a/ash/webui/system_extensions_internals_ui/resources/index.js +++ b/ash/webui/system_extensions_internals_ui/resources/index.js
@@ -2,9 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {pageHandler} from './page_handler.js'; + const chooseDirButton = document.querySelector('#choose-directory'); const resultDialog = document.querySelector('#result-dialog'); chooseDirButton.addEventListener('click', async event => { + const directory = await window.showDirectoryPicker({startIn: 'downloads'}); + const {success} = await pageHandler.installSystemExtensionFromDownloadsDir( + {path: {path: directory.name}}); + if (success) { + resultDialog.textContent = + `System Extension in '${directory.name}' was successfully installed.`; + } else { + resultDialog.textContent = + `System Extension in '${directory.name}' failed to be installed.`; + } resultDialog.showModal(); });
diff --git a/ash/webui/system_extensions_internals_ui/resources/page_handler.js b/ash/webui/system_extensions_internals_ui/resources/page_handler.js new file mode 100644 index 0000000..74fd29d --- /dev/null +++ b/ash/webui/system_extensions_internals_ui/resources/page_handler.js
@@ -0,0 +1,11 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Convenience module to bind to initialize a PageHandler + * remote i.e. a PageHandler that we can use to talk to the browser. + */ +import {PageHandler, PageHandlerRemote} from '/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom-webui.js'; + +export const pageHandler = PageHandler.getRemote();
diff --git a/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h b/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h index c1dcb1c..5a757713 100644 --- a/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h +++ b/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h
@@ -5,6 +5,7 @@ #ifndef ASH_WEBUI_SYSTEM_EXTENSIONS_INTERNALS_UI_SYSTEM_EXTENSIONS_INTERNALS_UI_H_ #define ASH_WEBUI_SYSTEM_EXTENSIONS_INTERNALS_UI_SYSTEM_EXTENSIONS_INTERNALS_UI_H_ +#include "ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom.h" #include "ui/webui/mojo_web_ui_controller.h" namespace ash { @@ -18,6 +19,12 @@ delete; ~SystemExtensionsInternalsUI() override; + // Implemented in //chrome/browser/chrome_browser_interface_binders.cc + // because PageHandler is implemented in //chrome/browser. + void BindInterface( + mojo::PendingReceiver<mojom::system_extensions_internals::PageHandler> + page_handler); + private: WEB_UI_CONTROLLER_TYPE_DECL(); };
diff --git a/base/power_monitor/power_monitor_device_source_stub.cc b/base/power_monitor/power_monitor_device_source_stub.cc index 96a9ffd..8aeadc9 100644 --- a/base/power_monitor/power_monitor_device_source_stub.cc +++ b/base/power_monitor/power_monitor_device_source_stub.cc
@@ -8,7 +8,6 @@ namespace base { bool PowerMonitorDeviceSource::IsOnBatteryPower() { - NOTIMPLEMENTED(); return false; }
diff --git a/base/power_monitor/power_monitor_source.h b/base/power_monitor/power_monitor_source.h index 891dcb6..40f6e5c 100644 --- a/base/power_monitor/power_monitor_source.h +++ b/base/power_monitor/power_monitor_source.h
@@ -36,8 +36,8 @@ // Reads the initial operating system CPU speed limit, if available on the // platform. Otherwise returns PowerThermalObserver::kSpeedLimitMax. - // Only called on the main thead in PowerMonitor::Initialize(). - // The actual speed limit value will be updated asynchronosulsy via the + // Only called on the main thread in PowerMonitor::Initialize(). + // The actual speed limit value will be updated asynchronously via the // ProcessSpeedLimitEvent() if/when the value changes. virtual int GetInitialSpeedLimit();
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1 index d489e93..ac95ece6 100644 --- a/build/fuchsia/linux_internal.sdk.sha1 +++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@ -7.20220301.2.2 +7.20220301.3.1
diff --git a/cc/document_transition/document_transition_request.cc b/cc/document_transition/document_transition_request.cc index 0783540c..3d2eb1df 100644 --- a/cc/document_transition/document_transition_request.cc +++ b/cc/document_transition/document_transition_request.cc
@@ -35,45 +35,117 @@ return "<unknown>"; } +std::string EffectToString( + viz::CompositorFrameTransitionDirective::Effect effect) { + switch (effect) { + case viz::CompositorFrameTransitionDirective::Effect::kNone: + return "kNone"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverDown: + return "kCoverDown"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverLeft: + return "kCoverLeft"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverRight: + return "kCoverRight"; + case viz::CompositorFrameTransitionDirective::Effect::kCoverUp: + return "kCoverUp"; + case viz::CompositorFrameTransitionDirective::Effect::kExplode: + return "kExplode"; + case viz::CompositorFrameTransitionDirective::Effect::kFade: + return "kFade"; + case viz::CompositorFrameTransitionDirective::Effect::kImplode: + return "kImplode"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealDown: + return "kRevealDown"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealLeft: + return "kRevealLeft"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealRight: + return "kRevealRight"; + case viz::CompositorFrameTransitionDirective::Effect::kRevealUp: + return "kRevealUp"; + } + return "<unknown>"; +} + } // namespace uint32_t DocumentTransitionRequest::s_next_sequence_id_ = 1; // static std::unique_ptr<DocumentTransitionRequest> -DocumentTransitionRequest::CreateCapture(uint32_t document_tag, - uint32_t shared_element_count, - base::OnceClosure commit_callback) { +DocumentTransitionRequest::CreatePrepare( + Effect effect, + uint32_t document_tag, + TransitionConfig root_config, + std::vector<TransitionConfig> shared_element_config, + base::OnceClosure commit_callback, + bool is_renderer_driven_animation) { return base::WrapUnique(new DocumentTransitionRequest( - Type::kSave, document_tag, shared_element_count, - std::move(commit_callback))); + effect, document_tag, root_config, shared_element_config, + std::move(commit_callback), is_renderer_driven_animation)); +} + +// static +std::unique_ptr<DocumentTransitionRequest> +DocumentTransitionRequest::CreateStart(uint32_t document_tag, + uint32_t shared_element_count, + base::OnceClosure commit_callback) { + return base::WrapUnique(new DocumentTransitionRequest( + document_tag, shared_element_count, std::move(commit_callback))); } // static std::unique_ptr<DocumentTransitionRequest> DocumentTransitionRequest::CreateAnimateRenderer(uint32_t document_tag) { - return base::WrapUnique(new DocumentTransitionRequest( - Type::kAnimateRenderer, document_tag, 0u, base::DoNothing())); + return base::WrapUnique( + new DocumentTransitionRequest(Type::kAnimateRenderer, document_tag)); } // static std::unique_ptr<DocumentTransitionRequest> DocumentTransitionRequest::CreateRelease(uint32_t document_tag) { - return base::WrapUnique(new DocumentTransitionRequest( - Type::kRelease, document_tag, 0u, base::DoNothing())); + return base::WrapUnique( + new DocumentTransitionRequest(Type::kRelease, document_tag)); } DocumentTransitionRequest::DocumentTransitionRequest( - Type type, + Effect effect, + uint32_t document_tag, + TransitionConfig root_config, + std::vector<TransitionConfig> shared_element_config, + base::OnceClosure commit_callback, + bool is_renderer_driven_animation) + : type_(Type::kSave), + effect_(effect), + root_config_(root_config), + document_tag_(document_tag), + shared_element_count_(shared_element_config.size()), + shared_element_config_(std::move(shared_element_config)), + commit_callback_(std::move(commit_callback)), + is_renderer_driven_animation_(is_renderer_driven_animation), + sequence_id_(s_next_sequence_id_++) {} + +DocumentTransitionRequest::DocumentTransitionRequest( uint32_t document_tag, uint32_t shared_element_count, base::OnceClosure commit_callback) - : type_(type), + : type_(Type::kAnimate), document_tag_(document_tag), shared_element_count_(shared_element_count), commit_callback_(std::move(commit_callback)), + is_renderer_driven_animation_(false), sequence_id_(s_next_sequence_id_++) {} +DocumentTransitionRequest::DocumentTransitionRequest(Type type, + uint32_t document_tag) + : type_(type), + document_tag_(document_tag), + shared_element_count_(0u), + commit_callback_(base::DoNothing()), + is_renderer_driven_animation_(true), + sequence_id_(s_next_sequence_id_++) { + DCHECK(type_ == Type::kAnimateRenderer || type_ == Type::kRelease); +} + DocumentTransitionRequest::~DocumentTransitionRequest() = default; viz::CompositorFrameTransitionDirective @@ -82,7 +154,15 @@ shared_element_render_pass_id_map) const { std::vector<viz::CompositorFrameTransitionDirective::SharedElement> shared_elements(shared_element_count_); + DCHECK(shared_element_config_.empty() || + shared_element_config_.size() == shared_elements.size()); for (uint32_t i = 0; i < shared_elements.size(); ++i) { + // For transitions with a null element on the source page, we won't find a + // render pass below. But we still need to propagate the configuration + // params. + if (!shared_element_config_.empty()) + shared_elements[i].config = shared_element_config_[i]; + auto it = std::find_if( shared_element_render_pass_id_map.begin(), shared_element_render_pass_id_map.end(), @@ -95,17 +175,16 @@ shared_elements[i].render_pass_id = it->second.render_pass_id; shared_elements[i].shared_element_resource_id = it->second.resource_id; } - // TODO(vmpstr): Clean up the directive parameters. return viz::CompositorFrameTransitionDirective( - sequence_id_, type_, /*is_renderer_driven_animation=*/true, - viz::CompositorFrameTransitionDirective::Effect::kNone, {}, + sequence_id_, type_, is_renderer_driven_animation_, effect_, root_config_, std::move(shared_elements)); } std::string DocumentTransitionRequest::ToString() const { std::ostringstream str; - str << "[type: " << TypeToString(type_) << " sequence_id: " << sequence_id_ - << "]"; + str << "[type: " << TypeToString(type_) + << " effect: " << EffectToString(effect_) + << " sequence_id: " << sequence_id_ << "]"; return str.str(); }
diff --git a/cc/document_transition/document_transition_request.h b/cc/document_transition/document_transition_request.h index 6bda2402..a16c3a4 100644 --- a/cc/document_transition/document_transition_request.h +++ b/cc/document_transition/document_transition_request.h
@@ -24,8 +24,21 @@ // transition to occur. class CC_EXPORT DocumentTransitionRequest { public: - // Creates a Type::kCapture type of request. - static std::unique_ptr<DocumentTransitionRequest> CreateCapture( + using Effect = viz::CompositorFrameTransitionDirective::Effect; + using TransitionConfig = + viz::CompositorFrameTransitionDirective::TransitionConfig; + + // Creates a Type::kPrepare type of request. + static std::unique_ptr<DocumentTransitionRequest> CreatePrepare( + Effect effect, + uint32_t document_tag, + TransitionConfig root_config, + std::vector<TransitionConfig> shared_element_config, + base::OnceClosure commit_callback, + bool is_renderer_driven_animation); + + // Creates a Type::kSave type of request. + static std::unique_ptr<DocumentTransitionRequest> CreateStart( uint32_t document_tag, uint32_t shared_element_count, base::OnceClosure commit_callback); @@ -74,15 +87,25 @@ private: using Type = viz::CompositorFrameTransitionDirective::Type; - DocumentTransitionRequest(Type type, + DocumentTransitionRequest(Effect effect, uint32_t document_tag, + TransitionConfig root_config, + std::vector<TransitionConfig> shared_element_config, + base::OnceClosure commit_callback, + bool is_renderer_driven_animation); + DocumentTransitionRequest(uint32_t document_tag, uint32_t shared_element_count, base::OnceClosure commit_callback); + DocumentTransitionRequest(Type type, uint32_t document_tag); const Type type_; + const Effect effect_ = Effect::kNone; + const TransitionConfig root_config_; const uint32_t document_tag_; const uint32_t shared_element_count_; + const std::vector<TransitionConfig> shared_element_config_; base::OnceClosure commit_callback_; + const bool is_renderer_driven_animation_; const uint32_t sequence_id_; static uint32_t s_next_sequence_id_;
diff --git a/cc/document_transition/document_transition_request_unittest.cc b/cc/document_transition/document_transition_request_unittest.cc index 40f6af6e..a1f3b553 100644 --- a/cc/document_transition/document_transition_request_unittest.cc +++ b/cc/document_transition/document_transition_request_unittest.cc
@@ -15,8 +15,10 @@ bool called = false; auto callback = base::BindLambdaForTesting([&called]() { called = true; }); - auto request = DocumentTransitionRequest::CreateCapture( - /*document_tag=*/0, /*shared_element_count=*/0, std::move(callback)); + auto request = DocumentTransitionRequest::CreatePrepare( + DocumentTransitionRequest::Effect::kRevealLeft, + /*document_tag=*/0, DocumentTransitionRequest::TransitionConfig(), + /*shared_element_config=*/{}, std::move(callback), false); EXPECT_FALSE(called); request->TakeFinishedCallback().Run(); @@ -25,29 +27,36 @@ auto directive = request->ConstructDirective({}); EXPECT_GT(directive.sequence_id(), 0u); + EXPECT_EQ(DocumentTransitionRequest::Effect::kRevealLeft, directive.effect()); EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kSave, directive.type()); - EXPECT_TRUE(directive.is_renderer_driven_animation()); + EXPECT_FALSE(directive.is_renderer_driven_animation()); auto duplicate = request->ConstructDirective({}); EXPECT_EQ(duplicate.sequence_id(), directive.sequence_id()); + EXPECT_EQ(duplicate.effect(), directive.effect()); EXPECT_EQ(duplicate.type(), directive.type()); EXPECT_EQ(duplicate.is_renderer_driven_animation(), directive.is_renderer_driven_animation()); } TEST(DocumentTransitionRequestTest, StartRequest) { - auto request = DocumentTransitionRequest::CreateAnimateRenderer( - /*document_tag=*/0); + bool called = false; + auto callback = base::BindLambdaForTesting([&called]() { called = true; }); + auto request = DocumentTransitionRequest::CreateStart( + /*document_tag=*/0, /*shared_element_transition=*/0, std::move(callback)); + + EXPECT_FALSE(called); request->TakeFinishedCallback().Run(); + EXPECT_TRUE(called); EXPECT_TRUE(request->TakeFinishedCallback().is_null()); auto directive = request->ConstructDirective({}); EXPECT_GT(directive.sequence_id(), 0u); - EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kAnimateRenderer, + EXPECT_EQ(viz::CompositorFrameTransitionDirective::Type::kAnimate, directive.type()); - EXPECT_TRUE(directive.is_renderer_driven_animation()); + EXPECT_FALSE(directive.is_renderer_driven_animation()); } } // namespace cc
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index d6422192..fc42c4e 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -18200,8 +18200,8 @@ // Adding a transition effect should cause us to redraw. host_impl_->active_tree()->AddDocumentTransitionRequest( - DocumentTransitionRequest::CreateAnimateRenderer( - /*document_tag=*/0)); + DocumentTransitionRequest::CreateStart( + /*document_tag=*/0, /*shared_element_count=*/0, base::OnceClosure())); // Ensure there is damage and we requested a redraw. host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw,
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 158478c..cb9d5c6 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -9865,7 +9865,14 @@ void BeginTest() override { layer_tree_host()->AddDocumentTransitionRequest( - DocumentTransitionRequest::CreateCapture( + DocumentTransitionRequest::CreatePrepare( + DocumentTransitionRequest::Effect::kExplode, + /*document_tag=*/0, DocumentTransitionRequest::TransitionConfig(), + /*shared_element_config=*/{}, + base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }), + /*is_renderer_driven_animation=*/false)); + layer_tree_host()->AddDocumentTransitionRequest( + DocumentTransitionRequest::CreateStart( /*document_tag=*/0, /*shared_element_count=*/0, base::BindLambdaForTesting([this]() { CommitLambdaCalled(); }))); } @@ -9874,12 +9881,20 @@ void DisplayReceivedCompositorFrameOnThread( const viz::CompositorFrame& frame) override { - ASSERT_EQ(1u, frame.metadata.transition_directives.size()); + ASSERT_EQ(2u, frame.metadata.transition_directives.size()); const auto& save = frame.metadata.transition_directives[0]; submitted_sequence_ids_.push_back(save.sequence_id()); EXPECT_EQ(save.type(), viz::CompositorFrameTransitionDirective::Type::kSave); + EXPECT_EQ(save.effect(), + viz::CompositorFrameTransitionDirective::Effect::kExplode); + + const auto& animate = frame.metadata.transition_directives[1]; + EXPECT_GT(animate.sequence_id(), save.sequence_id()); + EXPECT_EQ(animate.type(), + viz::CompositorFrameTransitionDirective::Type::kAnimate); + submitted_sequence_ids_.push_back(animate.sequence_id()); } void DidReceiveCompositorFrameAck() override { @@ -9888,7 +9903,7 @@ EndTest(); } - void AfterTest() override { EXPECT_EQ(1, num_lambda_calls_); } + void AfterTest() override { EXPECT_EQ(2, num_lambda_calls_); } std::vector<uint32_t> submitted_sequence_ids_; int num_lambda_calls_ = 0;
diff --git a/chrome/VERSION b/chrome/VERSION index 5019a18..a700d10b3 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=101 MINOR=0 -BUILD=4919 +BUILD=4920 PATCH=0
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index b989877..828c202 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -136,9 +136,11 @@ "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchObserverTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicyTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPreferenceFragmentTest.java", + "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRequestTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSystemTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTest.java", + "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUnbatchedTest.java", "javatests/src/org/chromium/chrome/browser/contextualsearch/MockContextualSearchPolicy.java", "javatests/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchFullUiTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java index a2b083ef..b084395 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -17,6 +17,7 @@ import android.graphics.Point; import android.os.SystemClock; import android.support.test.InstrumentationRegistry; +import android.text.TextUtils; import android.view.View; import android.view.ViewConfiguration; import android.widget.LinearLayout; @@ -191,12 +192,22 @@ /** * A SelectionPopupController that has some methods stubbed out for testing. */ - private static final class StubbedSelectionPopupController + protected static final class StubbedSelectionPopupController extends TestSelectionPopupController { private String mCurrentText; + private boolean mIsFocusedNodeEditable; public StubbedSelectionPopupController() {} + public void setIsFocusedNodeEditableForTest(boolean isFocusedNodeEditable) { + mIsFocusedNodeEditable = isFocusedNodeEditable; + } + + @Override + public boolean isFocusedNodeEditable() { + return mIsFocusedNodeEditable; + } + @Override public String getSelectedText() { return mCurrentText; @@ -280,9 +291,19 @@ } } + /** + * The DOM node for the word "search" on the test page, which causes a plain search response + * with the Search Term "Search" from the Fake server. + */ protected static final String SEARCH_NODE = "search"; protected static final String SEARCH_NODE_TERM = "Search"; + /** + * The DOM node for the word "intelligence" on the test page, which causes a search response + * for the Search Term "Intelligence" and also includes Related Searches suggestions. + */ + protected static final String RELATED_SEARCHES_NODE = "intelligence"; + private static final String TAG = "CSIBase"; private static final int TEST_TIMEOUT = 15000; private static final int TEST_EXPECTED_FAILURE_TIMEOUT = 1000; @@ -353,7 +374,7 @@ private LayoutManagerImpl mLayoutManager; private ActivityMonitor mActivityMonitor; - private ContextualSearchSelectionController mSelectionController; + protected ContextualSearchSelectionController mSelectionController; private ContextualSearchInstrumentationTestHost mTestHost; private float mDpToPx; @@ -665,6 +686,14 @@ } /** + * Waits for the selected text string to be the given string, and asserts. + * @param text The string to wait for the selection to become. + */ + protected void waitForSelectionToBe(final String text) { + mTestHost.waitForSelectionToBe(text); + } + + /** * Asserts that the action bar does or does not become visible in response to a selection. * @param visible Whether the Action Bar must become visible or not. */ @@ -1145,7 +1174,7 @@ /** * Waits for the Search Panel to expand, and asserts that it did expand. */ - private void waitForPanelToExpand() { + protected void waitForPanelToExpand() { waitForPanelToEnterState(PanelState.EXPANDED); } @@ -1177,6 +1206,42 @@ } /** + * Asserts that the panel is still in the given state and continues to stay that way + * for a while. + * Waits for a reasonable amount of time for the panel to change to a different state, + * and verifies that it did not change state while this method is executing. + * Note that it's quite possible for the panel to transition through some other state and + * back to the initial state before this method is called without that being detected, + * because this method only monitors state during its own execution. + * @param initialState The initial state of the panel at the beginning of an operation that + * should not change the panel state. + * @throws InterruptedException + */ + protected void assertPanelStillInState(final @PanelState int initialState) + throws InterruptedException { + boolean didChangeState = false; + long startTime = SystemClock.uptimeMillis(); + while (!didChangeState + && SystemClock.uptimeMillis() - startTime < TEST_EXPECTED_FAILURE_TIMEOUT) { + Thread.sleep(DEFAULT_POLLING_INTERVAL); + didChangeState = mPanel.getPanelState() != initialState; + } + Assert.assertFalse(didChangeState); + } + + /** + * Shorthand for a common sequence: + * 1) Waits for gesture processing, + * 2) Waits for the panel to close, + * 3) Asserts that there is no selection and that the panel closed. + */ + protected void waitForGestureToClosePanelAndAssertNoSelection() { + waitForPanelToClose(); + assertPanelClosedOrUndefined(); + Assert.assertTrue(TextUtils.isEmpty(getSelectedText())); + } + + /** * Waits for the selection to be empty. Use this method any time a test repeatedly establishes * and dissolves a selection to ensure that the selection has been completely dissolved before * simulating the next selection event. This is needed because the renderer's notification of a @@ -1192,7 +1257,7 @@ /** * Waits for the panel to close and then waits for the selection to dissolve. */ - private void waitForPanelToCloseAndSelectionEmpty() { + protected void waitForPanelToCloseAndSelectionEmpty() { waitForPanelToClose(); waitForSelectionEmpty(); } @@ -1258,7 +1323,7 @@ /** * Scrolls the base page. */ - private void scrollBasePage() { + protected void scrollBasePage() { fling(0.f, 0.75f, 0.f, 0.7f, 100); } @@ -1384,4 +1449,34 @@ private Object loggedToRanker(@ContextualSearchInteractionRecorder.Feature int feature) { return getRankerLogger().getFeaturesLogged().get(feature); } + + /** Asserts that all the expected features have been logged to Ranker. **/ + protected void assertLoggedAllExpectedFeaturesToRanker() { + for (int feature = 0; feature < ContextualSearchInteractionRecorder.Feature.NUM_ENTRIES; + feature++) { + if (expectedFeatureName(feature) != null) Assert.assertNotNull(loggedToRanker(feature)); + } + } + + /** Asserts that all the expected outcomes have been logged to Ranker. **/ + protected void assertLoggedAllExpectedOutcomesToRanker() { + for (int feature = 0; feature < ContextualSearchInteractionRecorder.Feature.NUM_ENTRIES; + feature++) { + if (expectedOutcomeName(feature) != null) { + Assert.assertNotNull("Expected this outcome to be logged: " + feature, + getRankerLogger().getOutcomesLogged().get(feature)); + } + } + } + + /** + * Returns whether all the supported gestures for opted-in users trigger a Resolve request, + * aka intelligent search. + */ + protected boolean isConfigurationForResolvingGesturesOnly() { + // The current interpretation of the ability to resolve Longpress (which is forced by the + // Translations Feature as well as the LongpressResolve Feature) preserves a resolving Tap + // so there is no non-resolving gesture for opted-in users. + return mPolicy.canResolveLongpress(); + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java index 397e72a..316b72e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -96,14 +96,11 @@ import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.chrome.test.util.FullscreenTestUtils; import org.chromium.chrome.test.util.MenuUtils; -import org.chromium.components.browser_ui.widget.chips.ChipProperties; import org.chromium.components.external_intents.ExternalNavigationHandler; import org.chromium.content_public.browser.NavigationHandle; -import org.chromium.content_public.browser.SelectionClient; import org.chromium.content_public.browser.SelectionPopupController; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.test.util.DOMUtils; -import org.chromium.content_public.browser.test.util.TestSelectionPopupController; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TouchCommon; import org.chromium.net.test.EmbeddedTestServer; @@ -115,21 +112,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeoutException; // TODO(donnd): Create class with limited API to encapsulate the internals of simulations. -// TODO(donnd): Separate tests into different classes grouped by type of tests. Examples: -// Gestures (Tap, Long-press), Search Term Resolution (resolves, expand selection, prevent preload, -// translation), Panel interaction (tap, fling up/down, close), Content (creation, loading, -// visibility, history, delayed load), Tab Promotion, Policy (add tests to check if policies -// affect the behavior correctly), General (remaining tests), etc. /** * Tests the Contextual Search Manager using instrumentation tests. @@ -781,25 +771,6 @@ mManager.getOverlayContentDelegate().onMainFrameNavigation(url, false, isFailure, false); } - /** - * A SelectionPopupController that has some methods stubbed out for testing. - */ - private static final class StubbedSelectionPopupController - extends TestSelectionPopupController { - private boolean mIsFocusedNodeEditable; - - public StubbedSelectionPopupController() {} - - public void setIsFocusedNodeEditableForTest(boolean isFocusedNodeEditable) { - mIsFocusedNodeEditable = isFocusedNodeEditable; - } - - @Override - public boolean isFocusedNodeEditable() { - return mIsFocusedNodeEditable; - } - } - //============================================================================================ // Other Helpers // TODO(donnd): organize into sections. @@ -1362,56 +1333,6 @@ //============================================================================================ /** - * Tests the doesContainAWord method. - * TODO(donnd): Change to a unit test. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testDoesContainAWord() { - Assert.assertTrue(mSelectionController.doesContainAWord("word")); - Assert.assertTrue(mSelectionController.doesContainAWord("word ")); - Assert.assertFalse("Emtpy string should not be considered a word!", - mSelectionController.doesContainAWord("")); - Assert.assertFalse("Special symbols should not be considered a word!", - mSelectionController.doesContainAWord("@")); - Assert.assertFalse("White space should not be considered a word", - mSelectionController.doesContainAWord(" ")); - Assert.assertTrue(mSelectionController.doesContainAWord("Q2")); - Assert.assertTrue(mSelectionController.doesContainAWord("123")); - } - - /** - * Tests the isValidSelection method. - * TODO(donnd): Change to a unit test. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testIsValidSelection() { - StubbedSelectionPopupController c = new StubbedSelectionPopupController(); - Assert.assertTrue(mSelectionController.isValidSelection("valid", c)); - Assert.assertFalse(mSelectionController.isValidSelection(" ", c)); - c.setIsFocusedNodeEditableForTest(true); - Assert.assertFalse(mSelectionController.isValidSelection("editable", c)); - c.setIsFocusedNodeEditableForTest(false); - String numberString = "0123456789"; - Assert.assertTrue(mSelectionController.isValidSelection(numberString, c)); - StringBuilder longStringBuilder = new StringBuilder().append(numberString); - for (int i = 0; i < 10; i++) { - longStringBuilder.append(longStringBuilder.toString()); - if (longStringBuilder.toString().length() < 1000) { - Assert.assertTrue( - mSelectionController.isValidSelection(longStringBuilder.toString(), c)); - } else { - Assert.assertFalse( - mSelectionController.isValidSelection(longStringBuilder.toString(), c)); - break; - } - } - } - - /** * Tests Ranker logging for a simple trigger that resolves. */ @Test @@ -1437,23 +1358,6 @@ } /** - * Tests a simple non-resolving gesture, without opening the panel. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class) - public void testNonResolveTrigger(@EnabledFeature int enabledFeature) throws Exception { - if (isConfigurationForResolvingGesturesOnly()) return; - triggerNonResolve("states"); - - Assert.assertNull(mFakeServer.getSearchTermRequested()); - waitForPanelToPeek(); - assertLoadedNoUrl(); - assertNoWebContents(); - } - - /** * Tests swiping the overlay open, after an initial trigger that activates the peeking card. */ @Test @@ -1585,174 +1489,6 @@ } //============================================================================================ - // Tap=gesture Tests - //============================================================================================ - - /** - * Tests that a Tap gesture on a special character does not select or show the panel. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - // Previously flaky and disabled 4/2021. https://crbug.com/1180304 - public void testTapGestureOnSpecialCharacterDoesntSelect() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickNode("question-mark"); - Assert.assertNull(getSelectedText()); - assertPanelClosedOrUndefined(); - assertLoadedNoUrl(); - } - - /** - * Tests that a Tap gesture followed by scrolling clears the selection. - */ - @Test - @DisableIf. - Build(sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/841017") - @SmallTest - @Feature({"ContextualSearch"}) - public void testTapGestureFollowedByScrollClearsSelection() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickWordNode("intelligence"); - fakeResponse(false, 200, "Intelligence", "Intelligence", "alternate-term", false); - assertContainsParameters("Intelligence", "alternate-term"); - waitForPanelToPeek(); - assertLoadedLowPriorityUrl(); - scrollBasePage(); - assertPanelClosedOrUndefined(); - Assert.assertTrue(TextUtils.isEmpty(mSelectionController.getSelectedText())); - } - - /** - * Tests that a Tap gesture followed by tapping an invalid character doesn't select. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - // Previously flaky and disabled 4/2021. https://crbug.com/1192285 - public void testTapGestureFollowedByInvalidTextTapCloses() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickWordNode("states-far"); - waitForPanelToPeek(); - clickNode("question-mark"); - waitForPanelToClose(); - Assert.assertNull(mSelectionController.getSelectedText()); - } - - /** - * Tests that a Tap gesture followed by tapping a non-text character doesn't select. - * @SmallTest - * @Feature({"ContextualSearch"}) - * crbug.com/665633 - */ - @Test - @DisabledTest - public void testTapGestureFollowedByNonTextTap() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickWordNode("states-far"); - waitForPanelToPeek(); - clickNode("button"); - waitForPanelToCloseAndSelectionEmpty(); - } - - /** - * Tests that a Tap gesture far away toggles selecting text. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testTapGestureFarAwayTogglesSelecting() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickWordNode("states"); - Assert.assertEquals("States", getSelectedText()); - waitForPanelToPeek(); - clickNode("states-far"); - waitForPanelToClose(); - Assert.assertNull(getSelectedText()); - clickNode("states-far"); - waitForPanelToPeek(); - Assert.assertEquals("States", getSelectedText()); - } - - /** - * Tests a "retap" -- that sequential Tap gestures nearby keep selecting. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @DisabledTest(message = "https://crbug.com/1075895") - public void testTapGesturesNearbyKeepSelecting() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - clickWordNode("states"); - Assert.assertEquals("States", getSelectedText()); - waitForPanelToPeek(); - assertLoggedAllExpectedFeaturesToRanker(); - // Avoid issues with double-tap detection by ensuring sequential taps - // aren't treated as such. Double-tapping can also select words much as - // longpress, in turn showing the pins and preventing contextual tap - // refinement from nearby taps. The double-tap timeout is sufficiently - // short that this shouldn't conflict with tap refinement by the user. - Thread.sleep(ViewConfiguration.getDoubleTapTimeout()); - // Because sequential taps never hide the bar, we we can't wait for it to peek. - // Instead we use clickNode (which doesn't wait) instead of clickWordNode and wait - // for the selection to change. - clickNode("states-near"); - waitForSelectionToBe("StatesNear"); - assertLoggedAllExpectedOutcomesToRanker(); - assertLoggedAllExpectedFeaturesToRanker(); - Thread.sleep(ViewConfiguration.getDoubleTapTimeout()); - clickNode("states"); - waitForSelectionToBe("States"); - assertLoggedAllExpectedOutcomesToRanker(); - } - - //============================================================================================ - // Long-press non-triggering gesture tests. - //============================================================================================ - - /** - * Tests that a long-press gesture followed by scrolling does not clear the selection. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class) - @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.O, message = "crbug.com/1071080") - public void testLongPressGestureFollowedByScrollMaintainsSelection( - @EnabledFeature int enabledFeature) throws Exception { - longPressNode("intelligence"); - waitForPanelToPeek(); - scrollBasePage(); - assertPanelClosedOrUndefined(); - Assert.assertEquals("Intelligence", getSelectedText()); - assertLoadedNoUrl(); - } - - /** - * Tests that a long-press gesture followed by a tap does not select. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) - @DisabledTest(message = "See https://crbug.com/837998") - public void testLongPressGestureFollowedByTapDoesntSelect() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - longPressNode("intelligence"); - waitForPanelToPeek(); - clickWordNode("states-far"); - waitForGestureToClosePanelAndAssertNoSelection(); - assertLoadedNoUrl(); - } - - //============================================================================================ // Various Tests //============================================================================================ @@ -1814,88 +1550,6 @@ } //============================================================================================ - // Tap-non-triggering when ARIA annotated as interactive. - //============================================================================================ - - /** - * Tests that a Tap gesture on an element with an ARIA role does not trigger. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @DisabledTest(message = "http://crbug.com/1296677") - public void testTapOnRoleIgnored() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - @PanelState - int initialState = mPanel.getPanelState(); - clickNode("role"); - assertPanelStillInState(initialState); - } - - /** - * Tests that a Tap gesture on an element with an ARIA attribute does not trigger. - * http://crbug.com/542874 - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - // Previously flaky and disabled 4/2021. https://crbug.com/1192285 - @DisabledTest(message = "https://crbug.com/1291558") - public void testTapOnARIAIgnored() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - @PanelState - int initialState = mPanel.getPanelState(); - clickNode("aria"); - assertPanelStillInState(initialState); - } - - /** - * Tests that a Tap gesture on an element that is focusable does not trigger. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testTapOnFocusableIgnored() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - @PanelState - int initialState = mPanel.getPanelState(); - clickNode("focusable"); - assertPanelStillInState(initialState); - } - - //============================================================================================ - // Search-term resolution (server request to determine a search). - //============================================================================================ - - /** - * Tests expanding the panel before the search term has resolved, verifies that nothing - * loads until the resolve completes and that it's now a normal priority URL. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class) - public void testExpandBeforeSearchTermResolution(@EnabledFeature int enabledFeature) - throws Exception { - simulateSlowResolveSearch("states"); - assertNoWebContents(); - - // Expanding before the search term resolves should not load anything. - tapPeekingBarToExpandAndAssert(); - assertLoadedNoUrl(); - - // Once the response comes in, it should load. - simulateSlowResolveFinished(); - assertContainsParameters("States"); - assertLoadedNormalPriorityUrl(); - assertWebContentsCreated(); - assertWebContentsVisible(); - } - - //============================================================================================ // Undecided/Decided users. //============================================================================================ @@ -2024,40 +1678,6 @@ } /** - * Tests that the Contextual Search panel does not reappear when a long-press selection is - * modified after the user has taken an action to explicitly dismiss the panel. Also tests - * that the panel reappears when a new selection is made. - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - // Previously flaky, disabled 4/2021. https://crbug.com/1192285 - @DisabledTest(message = "https://crbug.com/1291558") - public void testPreventHandlingCurrentSelectionModification() throws Exception { - FeatureList.setTestFeatures(ENABLE_NONE); - - simulateNonResolveSearch("search"); - - // Dismiss the Contextual Search panel. - closePanel(); - Assert.assertEquals("Search", getSelectedText()); - - // Simulate a selection change event and assert that the panel has not reappeared. - TestThreadUtils.runOnUiThreadBlocking(() -> { - SelectionClient selectionClient = mManager.getContextualSearchSelectionClient(); - selectionClient.onSelectionEvent( - SelectionEventType.SELECTION_HANDLE_DRAG_STARTED, 333, 450); - selectionClient.onSelectionEvent( - SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED, 303, 450); - }); - assertPanelClosedOrUndefined(); - - // Select a different word and assert that the panel has appeared. - simulateNonResolveSearch("resolution"); - // The simulateNonResolveSearch call will verify that the panel peeks. - } - - /** * Tests ContextualSearchManager#shouldInterceptNavigation for a case that an external * navigation has a user gesture. */ @@ -2183,26 +1803,6 @@ Assert.assertEquals(0, mActivityMonitor.getHits()); } - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class) - // Previously flaky and disabled 4/2021. https://crbug.com/1180304 - public void testSelectionExpansionOnSearchTermResolution(@EnabledFeature int enabledFeature) - throws Exception { - mFakeServer.reset(); - triggerResolve("intelligence"); - waitForPanelToPeek(); - - ResolvedSearchTerm resolvedSearchTerm = - new ResolvedSearchTerm - .Builder(false, 200, "Intelligence", "United States Intelligence") - .setSelectionStartAdjust(-14) - .build(); - fakeResponse(resolvedSearchTerm); - waitForSelectionToBe("United States Intelligence"); - } - //============================================================================================ // Translate Tests //============================================================================================ @@ -2907,224 +2507,4 @@ Assert.assertEquals(2, userActionMonitor.get("ContextualSearch.ManualRefine")); Assert.assertEquals(2, userActionMonitor.get("ContextualSearch.SelectionEstablished")); } - - // -------------------------------------------------------------------------------------------- - // Related Searches Feature tests: base feature enables requests, UI feature allows results. - // -------------------------------------------------------------------------------------------- - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testRelatedSearchesInBar() throws Exception { - FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); - mFakeServer.reset(); - FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence"); - ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); - Assert.assertTrue("Related Searches results should have been returned but were not!", - !resolvedSearchTerm.relatedSearchesJson().isEmpty()); - // Select a chip in the Bar, which should expand the panel. - final int chipToSelect = 1; - TestThreadUtils.runOnUiThreadBlocking( - () -> mPanel.getRelatedSearchesInBarControl().selectChipForTest(chipToSelect)); - waitForPanelToExpand(); - - // Close the panel - closePanel(); - // TODO(donnd): Validate UMA metrics once we log in-bar selections. - } - - /** - * Tests that the offset of the SERP is unaffected by whether we are showing Related Searches - * in the Bar or not. See https://crbug.com/1250546. - * @throws Exception - */ - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testRelatedSearchesInBarSerpOffset() throws Exception { - FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); - mFakeServer.reset(); - simulateResolveSearch(SIMPLE_SEARCH_NODE_ID); - float plainSearchBarHeight = mPanel.getBarHeight(); - float plainSearchContentY = mPanel.getContentY(); - closePanel(); - - // Bring up a panel with Related Searches in order to expand the Bar - simulateResolveSearch(RELATED_SEARCHES_NODE_ID); - // Wait for the animation to start growing the Bar. - CriteriaHelper.pollUiThread(() -> { - Criteria.checkThat( - mPanel.getInBarRelatedSearchesAnimatedHeightDps(), Matchers.greaterThan(0f)); - }); - // We should have a taller Bar, but that should not affect the Y offset of the content. - Assert.assertNotEquals( - "Test code failure - unable to open panels with differing Bar heights!", - plainSearchBarHeight, mPanel.getBarHeight(), 0.1f); - Assert.assertEquals("SERP content offsets with and without Related Searches should match!", - plainSearchContentY, mPanel.getContentY(), 0.1f); - } - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testRelatedSearchesInBarWithDefaultQuery() throws Exception { - FeatureList.TestValues testValues = new FeatureList.TestValues(); - testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); - testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, - ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, - "true"); - FeatureList.setTestValues(testValues); - mFakeServer.reset(); - - FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence"); - ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); - Assert.assertTrue("Related Searches results should have been returned but were not!", - !resolvedSearchTerm.relatedSearchesJson().isEmpty()); - // Select a chip in the Bar, which should expand the panel. - final int chipToSelect = 0; - TestThreadUtils.runOnUiThreadBlocking( - () -> mPanel.getRelatedSearchesInBarControl().selectChipForTest(chipToSelect)); - waitForPanelToExpand(); - - CriteriaHelper.pollUiThread(() -> { - Criteria.checkThat( - mPanel.getSearchBarControl().getSearchTerm(), Matchers.is("Intelligence")); - }); - - // Close the panel - closePanel(); - // TODO(donnd): Validate UMA metrics once we log in-bar selections. - } - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @DisabledTest(message = "https://crbug.com/1244089") - public void testRelatedSearchesInBarWithDefaultQuery_HighlightDefaultQuery() throws Exception { - FeatureList.TestValues testValues = new FeatureList.TestValues(); - testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); - testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, - ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, - "true"); - FeatureList.setTestValues(testValues); - mFakeServer.reset(); - - FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence"); - ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); - Assert.assertTrue("Related Searches results should have been returned but were not!", - !resolvedSearchTerm.relatedSearchesJson().isEmpty()); - // Select a chip in the Bar, which should expand the panel. - tapPeekingBarToExpandAndAssert(); - - CriteriaHelper.pollUiThread(() -> { - Criteria.checkThat( - mPanel.getSearchBarControl().getSearchTerm(), Matchers.is("Intelligence")); - Criteria.checkThat(mPanel.getRelatedSearchesInBarControl().getSelectedChipForTest(), - Matchers.is(0)); - }); - - // Close the panel - closePanel(); - // TODO(donnd): Validate UMA metrics once we log in-bar selections. - } - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testRelatedSearchesInBarWithDefaultQuery_Ellipsize() throws Exception { - FeatureList.TestValues testValues = new FeatureList.TestValues(); - testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); - testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, - ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, - "true"); - testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, - ContextualSearchFieldTrial - .RELATED_SEARCHES_DEFAULT_QUERY_CHIP_MAX_WIDTH_SP_PARAM_NAME, - "60"); - FeatureList.setTestValues(testValues); - mFakeServer.reset(); - - FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence"); - ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); - Assert.assertTrue("Related Searches results should have been returned but were not!", - !resolvedSearchTerm.relatedSearchesJson().isEmpty()); - // Select a chip in the Bar, which should expand the panel. - tapPeekingBarToExpandAndAssert(); - - CriteriaHelper.pollUiThread(() -> { - Criteria.checkThat( - mPanel.getRelatedSearchesInBarControl().getChipsForTest().get(0).model.get( - ChipProperties.TEXT_MAX_WIDTH_PX), - Matchers.not(ChipProperties.SHOW_WHOLE_TEXT)); - }); - - // Close the panel - closePanel(); - // TODO(donnd): Validate UMA metrics once we log in-bar selections. - } - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - public void testRelatedSearchesInBarForDefinitionCard() throws Exception { - CompositorAnimationHandler.setTestingMode(true); - FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); - mFakeServer.reset(); - // Do a normal search without Related Searches or Definition cards. - simulateResolveSearch("search"); - float normalHeight = mPanel.getHeight(); - - // Simulate a response that includes both a definition and Related Searches - List<String> inBarSuggestions = new ArrayList<String>(); - inBarSuggestions.add("Related Suggestion 1"); - inBarSuggestions.add("Related Suggestion 2"); - TestThreadUtils.runOnUiThreadBlocking( - () - -> mPanel.onSearchTermResolved("obscure · əbˈskyo͝or", null, null, - QuickActionCategory.NONE, CardTag.CT_DEFINITION, inBarSuggestions, - false /* showDefaultSearchInBar */, - null /* relatedSearchesInContent */, - false /* showDefaultSearchInContent */)); - boolean didPanelGetTaller = mPanel.getHeight() > normalHeight; - Assert.assertTrue( - "Related Searches should show in a taller Bar when there's a definition card, " - + "but they did not!", - didPanelGetTaller); - // Clean up - closePanel(); - CompositorAnimationHandler.setTestingMode(false); - } - - @Test - @SmallTest - @Feature({"ContextualSearch"}) - @DisabledTest(message = "https://crbug.com/1251774") - public void testRelatedSearchesDismissDuringAnimation() throws Exception { - FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); - mFakeServer.reset(); - // Use the "intelligence" node to generate Related Searches suggestions. - simulateResolveSearch("intelligence"); - - // Wait for the animation to start growing the Bar. - CriteriaHelper.pollUiThread(() -> { - Criteria.checkThat( - mPanel.getInBarRelatedSearchesAnimatedHeightDps(), Matchers.greaterThan(0f)); - }); - - // Wait for the animation to change to make sure that doesn't bring the Bar back - final boolean[] didAnimationChange = {false}; - mPanel.getSearchBarControl().setInBarAnimationTestNotifier( - () -> { didAnimationChange[0] = true; }); - CriteriaHelper.pollUiThread( - () -> { Criteria.checkThat(didAnimationChange[0], Matchers.is(true)); }); - // Repeatedly closing the panel should not bring it back even during ongoing animation. - closePanel(); - Assert.assertFalse("The panel is showing again due to Animation!", mPanel.isShowing()); - // Another scroll might try to close the panel when it thinks it's already closed, which - // could fail due to inconsistencies in internal logic, so test that too. - closePanel(); - Assert.assertFalse("Expected the panel to not be showing after a close! " - + "Animation of the Bar height is the likely cause.", - mPanel.isShowing()); - } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java new file mode 100644 index 0000000..1bc4fbf --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRelatedSearchesTest.java
@@ -0,0 +1,275 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.contextualsearch; + +import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.FeatureList; +import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.Batch; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Criteria; +import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.Restriction; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler; +import org.chromium.components.browser_ui.widget.chips.ChipProperties; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests the Related Searches Feature of Contextual Search using instrumentation tests. + */ +@RunWith(BaseJUnit4ClassRunner.class) +// NOTE: Disable online detection so we we'll default to online on test bots with no network. +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + ContextualSearchFieldTrial.ONLINE_DETECTION_DISABLED}) +@Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) +@Batch(Batch.PER_CLASS) +public class ContextualSearchRelatedSearchesTest extends ContextualSearchInstrumentationBase { + @Override + @Before + public void setUp() throws Exception { + mTestPage = "/chrome/test/data/android/contextualsearch/tap_test.html"; + super.setUp(); + } + + // -------------------------------------------------------------------------------------------- + // Related Searches Feature tests: base feature enables requests, UI feature allows results. + // -------------------------------------------------------------------------------------------- + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testRelatedSearchesInBar() throws Exception { + FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); + mFakeServer.reset(); + ContextualSearchFakeServer.FakeResolveSearch fakeSearch = + simulateResolveSearch("intelligence"); + ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); + Assert.assertTrue("Related Searches results should have been returned but were not!", + !resolvedSearchTerm.relatedSearchesJson().isEmpty()); + // Select a chip in the Bar, which should expand the panel. + final int chipToSelect = 1; + TestThreadUtils.runOnUiThreadBlocking( + () -> mPanel.getRelatedSearchesInBarControl().selectChipForTest(chipToSelect)); + waitForPanelToExpand(); + + // Close the panel + closePanel(); + // TODO(donnd): Validate UMA metrics once we log in-bar selections. + } + + /** + * Tests that the offset of the SERP is unaffected by whether we are showing Related Searches + * in the Bar or not. See https://crbug.com/1250546. + * @throws Exception + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testRelatedSearchesInBarSerpOffset() throws Exception { + FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); + mFakeServer.reset(); + simulateResolveSearch(SEARCH_NODE); + float plainSearchBarHeight = mPanel.getBarHeight(); + float plainSearchContentY = mPanel.getContentY(); + closePanel(); + + // Bring up a panel with Related Searches in order to expand the Bar + simulateResolveSearch(RELATED_SEARCHES_NODE); + // Wait for the animation to start growing the Bar. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + mPanel.getInBarRelatedSearchesAnimatedHeightDps(), Matchers.greaterThan(0f)); + }); + // We should have a taller Bar, but that should not affect the Y offset of the content. + Assert.assertNotEquals( + "Test code failure - unable to open panels with differing Bar heights!", + plainSearchBarHeight, mPanel.getBarHeight(), 0.1f); + Assert.assertEquals("SERP content offsets with and without Related Searches should match!", + plainSearchContentY, mPanel.getContentY(), 0.1f); + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testRelatedSearchesInBarWithDefaultQuery() throws Exception { + FeatureList.TestValues testValues = new FeatureList.TestValues(); + testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); + testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, + ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, + "true"); + FeatureList.setTestValues(testValues); + mFakeServer.reset(); + + ContextualSearchFakeServer.FakeResolveSearch fakeSearch = + simulateResolveSearch("intelligence"); + ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); + Assert.assertTrue("Related Searches results should have been returned but were not!", + !resolvedSearchTerm.relatedSearchesJson().isEmpty()); + // Select a chip in the Bar, which should expand the panel. + final int chipToSelect = 0; + TestThreadUtils.runOnUiThreadBlocking( + () -> mPanel.getRelatedSearchesInBarControl().selectChipForTest(chipToSelect)); + waitForPanelToExpand(); + + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + mPanel.getSearchBarControl().getSearchTerm(), Matchers.is("Intelligence")); + }); + + // Close the panel + closePanel(); + // TODO(donnd): Validate UMA metrics once we log in-bar selections. + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @DisabledTest(message = "https://crbug.com/1244089") + public void testRelatedSearchesInBarWithDefaultQuery_HighlightDefaultQuery() throws Exception { + FeatureList.TestValues testValues = new FeatureList.TestValues(); + testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); + testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, + ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, + "true"); + FeatureList.setTestValues(testValues); + mFakeServer.reset(); + + ContextualSearchFakeServer.FakeResolveSearch fakeSearch = + simulateResolveSearch("intelligence"); + ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); + Assert.assertTrue("Related Searches results should have been returned but were not!", + !resolvedSearchTerm.relatedSearchesJson().isEmpty()); + // Select a chip in the Bar, which should expand the panel. + tapPeekingBarToExpandAndAssert(); + + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + mPanel.getSearchBarControl().getSearchTerm(), Matchers.is("Intelligence")); + Criteria.checkThat(mPanel.getRelatedSearchesInBarControl().getSelectedChipForTest(), + Matchers.is(0)); + }); + + // Close the panel + closePanel(); + // TODO(donnd): Validate UMA metrics once we log in-bar selections. + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testRelatedSearchesInBarWithDefaultQuery_Ellipsize() throws Exception { + FeatureList.TestValues testValues = new FeatureList.TestValues(); + testValues.setFeatureFlagsOverride(ENABLE_RELATED_SEARCHES_IN_BAR); + testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, + ContextualSearchFieldTrial.RELATED_SEARCHES_SHOW_DEFAULT_QUERY_CHIP_PARAM_NAME, + "true"); + testValues.addFieldTrialParamOverride(ChromeFeatureList.RELATED_SEARCHES_IN_BAR, + ContextualSearchFieldTrial + .RELATED_SEARCHES_DEFAULT_QUERY_CHIP_MAX_WIDTH_SP_PARAM_NAME, + "60"); + FeatureList.setTestValues(testValues); + mFakeServer.reset(); + + ContextualSearchFakeServer.FakeResolveSearch fakeSearch = + simulateResolveSearch("intelligence"); + ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm(); + Assert.assertTrue("Related Searches results should have been returned but were not!", + !resolvedSearchTerm.relatedSearchesJson().isEmpty()); + // Select a chip in the Bar, which should expand the panel. + tapPeekingBarToExpandAndAssert(); + + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + mPanel.getRelatedSearchesInBarControl().getChipsForTest().get(0).model.get( + ChipProperties.TEXT_MAX_WIDTH_PX), + Matchers.not(ChipProperties.SHOW_WHOLE_TEXT)); + }); + + // Close the panel + closePanel(); + // TODO(donnd): Validate UMA metrics once we log in-bar selections. + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testRelatedSearchesInBarForDefinitionCard() throws Exception { + CompositorAnimationHandler.setTestingMode(true); + FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); + mFakeServer.reset(); + // Do a normal search without Related Searches or Definition cards. + simulateResolveSearch("search"); + float normalHeight = mPanel.getHeight(); + + // Simulate a response that includes both a definition and Related Searches + List<String> inBarSuggestions = new ArrayList<String>(); + inBarSuggestions.add("Related Suggestion 1"); + inBarSuggestions.add("Related Suggestion 2"); + TestThreadUtils.runOnUiThreadBlocking( + () + -> mPanel.onSearchTermResolved("obscure · əbˈskyo͝or", null, null, + QuickActionCategory.NONE, ResolvedSearchTerm.CardTag.CT_DEFINITION, + inBarSuggestions, false /* showDefaultSearchInBar */, + null /* relatedSearchesInContent */, + false /* showDefaultSearchInContent */)); + boolean didPanelGetTaller = mPanel.getHeight() > normalHeight; + Assert.assertTrue( + "Related Searches should show in a taller Bar when there's a definition card, " + + "but they did not!", + didPanelGetTaller); + // Clean up + closePanel(); + CompositorAnimationHandler.setTestingMode(false); + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @DisabledTest(message = "https://crbug.com/1251774") + public void testRelatedSearchesDismissDuringAnimation() throws Exception { + FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_BAR); + mFakeServer.reset(); + // Use the "intelligence" node to generate Related Searches suggestions. + simulateResolveSearch("intelligence"); + + // Wait for the animation to start growing the Bar. + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat( + mPanel.getInBarRelatedSearchesAnimatedHeightDps(), Matchers.greaterThan(0f)); + }); + + // Wait for the animation to change to make sure that doesn't bring the Bar back + final boolean[] didAnimationChange = {false}; + mPanel.getSearchBarControl().setInBarAnimationTestNotifier( + () -> { didAnimationChange[0] = true; }); + CriteriaHelper.pollUiThread( + () -> { Criteria.checkThat(didAnimationChange[0], Matchers.is(true)); }); + // Repeatedly closing the panel should not bring it back even during ongoing animation. + closePanel(); + Assert.assertFalse("The panel is showing again due to Animation!", mPanel.isShowing()); + // Another scroll might try to close the panel when it thinks it's already closed, which + // could fail due to inconsistencies in internal logic, so test that too. + closePanel(); + Assert.assertFalse("Expected the panel to not be showing after a close! " + + "Animation of the Bar height is the likely cause.", + mPanel.isShowing()); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java new file mode 100644 index 0000000..260f6c33 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
@@ -0,0 +1,430 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.contextualsearch; + +import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; + +import android.os.Build; +import android.text.TextUtils; +import android.view.ViewConfiguration; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.FeatureList; +import org.chromium.base.test.params.ParameterAnnotations; +import org.chromium.base.test.params.ParameterizedRunner; +import org.chromium.base.test.util.Batch; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.Restriction; +import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; +import org.chromium.content_public.browser.SelectionClient; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.ui.test.util.UiRestriction; + +/** + * Tests the Related Searches Feature of Contextual Search using instrumentation tests. + */ +@RunWith(ParameterizedRunner.class) +@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) +// NOTE: Disable online detection so we we'll default to online on test bots with no network. +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + ContextualSearchFieldTrial.ONLINE_DETECTION_DISABLED}) +@Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) +@Batch(Batch.PER_CLASS) +public class ContextualSearchTriggerTest extends ContextualSearchInstrumentationBase { + @Override + @Before + public void setUp() throws Exception { + mTestPage = "/chrome/test/data/android/contextualsearch/tap_test.html"; + super.setUp(); + } + + //============================================================================================ + // Test Cases + //============================================================================================ + + /** + * Tests the doesContainAWord method. + * TODO(donnd): Change to a unit test. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testDoesContainAWord() { + Assert.assertTrue(mSelectionController.doesContainAWord("word")); + Assert.assertTrue(mSelectionController.doesContainAWord("word ")); + Assert.assertFalse("Emtpy string should not be considered a word!", + mSelectionController.doesContainAWord("")); + Assert.assertFalse("Special symbols should not be considered a word!", + mSelectionController.doesContainAWord("@")); + Assert.assertFalse("White space should not be considered a word", + mSelectionController.doesContainAWord(" ")); + Assert.assertTrue(mSelectionController.doesContainAWord("Q2")); + Assert.assertTrue(mSelectionController.doesContainAWord("123")); + } + + /** + * Tests the isValidSelection method. + * TODO(donnd): Change to a unit test. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testIsValidSelection() { + StubbedSelectionPopupController c = new StubbedSelectionPopupController(); + Assert.assertTrue(mSelectionController.isValidSelection("valid", c)); + Assert.assertFalse(mSelectionController.isValidSelection(" ", c)); + c.setIsFocusedNodeEditableForTest(true); + Assert.assertFalse(mSelectionController.isValidSelection("editable", c)); + c.setIsFocusedNodeEditableForTest(false); + String numberString = "0123456789"; + Assert.assertTrue(mSelectionController.isValidSelection(numberString, c)); + StringBuilder longStringBuilder = new StringBuilder().append(numberString); + for (int i = 0; i < 10; i++) { + longStringBuilder.append(longStringBuilder.toString()); + if (longStringBuilder.toString().length() < 1000) { + Assert.assertTrue( + mSelectionController.isValidSelection(longStringBuilder.toString(), c)); + } else { + Assert.assertFalse( + mSelectionController.isValidSelection(longStringBuilder.toString(), c)); + break; + } + } + } + + /** + * Tests a simple non-resolving gesture, without opening the panel. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class) + public void testNonResolveTrigger(@EnabledFeature int enabledFeature) throws Exception { + if (isConfigurationForResolvingGesturesOnly()) return; + triggerNonResolve("states"); + + Assert.assertNull(mFakeServer.getSearchTermRequested()); + waitForPanelToPeek(); + assertLoadedNoUrl(); + assertNoWebContents(); + } + + //============================================================================================ + // Tap=gesture Tests + //============================================================================================ + + /** + * Tests that a Tap gesture on a special character does not select or show the panel. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + // Previously flaky and disabled 4/2021. https://crbug.com/1180304 + public void testTapGestureOnSpecialCharacterDoesntSelect() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickNode("question-mark"); + Assert.assertNull(getSelectedText()); + assertPanelClosedOrUndefined(); + assertLoadedNoUrl(); + } + + /** + * Tests that a Tap gesture followed by scrolling clears the selection. + */ + @Test + @DisableIf. + Build(sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/841017") + @SmallTest + @Feature({"ContextualSearch"}) + public void testTapGestureFollowedByScrollClearsSelection() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickWordNode("intelligence"); + fakeResponse(false, 200, "Intelligence", "Intelligence", "alternate-term", false); + assertContainsParameters("Intelligence", "alternate-term"); + waitForPanelToPeek(); + assertLoadedLowPriorityUrl(); + scrollBasePage(); + assertPanelClosedOrUndefined(); + Assert.assertTrue(TextUtils.isEmpty(mSelectionController.getSelectedText())); + } + + /** + * Tests that a Tap gesture followed by tapping an invalid character doesn't select. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + // Previously flaky and disabled 4/2021. https://crbug.com/1192285 + public void testTapGestureFollowedByInvalidTextTapCloses() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickWordNode("states-far"); + waitForPanelToPeek(); + clickNode("question-mark"); + waitForPanelToClose(); + Assert.assertNull(mSelectionController.getSelectedText()); + } + + /** + * Tests that a Tap gesture followed by tapping a non-text character doesn't select. + * @SmallTest + * @Feature({"ContextualSearch"}) + * crbug.com/665633 + */ + @Test + @DisabledTest + public void testTapGestureFollowedByNonTextTap() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickWordNode("states-far"); + waitForPanelToPeek(); + clickNode("button"); + waitForPanelToCloseAndSelectionEmpty(); + } + + /** + * Tests that a Tap gesture far away toggles selecting text. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testTapGestureFarAwayTogglesSelecting() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickWordNode("states"); + Assert.assertEquals("States", getSelectedText()); + waitForPanelToPeek(); + clickNode("states-far"); + waitForPanelToClose(); + Assert.assertNull(getSelectedText()); + clickNode("states-far"); + waitForPanelToPeek(); + Assert.assertEquals("States", getSelectedText()); + } + + /** + * Tests a "retap" -- that sequential Tap gestures nearby keep selecting. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @DisabledTest(message = "https://crbug.com/1075895") + public void testTapGesturesNearbyKeepSelecting() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + clickWordNode("states"); + Assert.assertEquals("States", getSelectedText()); + waitForPanelToPeek(); + assertLoggedAllExpectedFeaturesToRanker(); + // Avoid issues with double-tap detection by ensuring sequential taps + // aren't treated as such. Double-tapping can also select words much as + // longpress, in turn showing the pins and preventing contextual tap + // refinement from nearby taps. The double-tap timeout is sufficiently + // short that this shouldn't conflict with tap refinement by the user. + Thread.sleep(ViewConfiguration.getDoubleTapTimeout()); + // Because sequential taps never hide the bar, we we can't wait for it to peek. + // Instead we use clickNode (which doesn't wait) instead of clickWordNode and wait + // for the selection to change. + clickNode("states-near"); + waitForSelectionToBe("StatesNear"); + assertLoggedAllExpectedOutcomesToRanker(); + assertLoggedAllExpectedFeaturesToRanker(); + Thread.sleep(ViewConfiguration.getDoubleTapTimeout()); + clickNode("states"); + waitForSelectionToBe("States"); + assertLoggedAllExpectedOutcomesToRanker(); + } + + //============================================================================================ + // Long-press non-triggering gesture tests. + //============================================================================================ + + /** + * Tests that a long-press gesture followed by scrolling does not clear the selection. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class) + @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.O, message = "crbug.com/1071080") + public void testLongPressGestureFollowedByScrollMaintainsSelection( + @EnabledFeature int enabledFeature) throws Exception { + longPressNode("intelligence"); + waitForPanelToPeek(); + scrollBasePage(); + assertPanelClosedOrUndefined(); + Assert.assertEquals("Intelligence", getSelectedText()); + assertLoadedNoUrl(); + } + + /** + * Tests that a long-press gesture followed by a tap does not select. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) + @DisabledTest(message = "See https://crbug.com/837998") + public void testLongPressGestureFollowedByTapDoesntSelect() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + longPressNode("intelligence"); + waitForPanelToPeek(); + clickWordNode("states-far"); + waitForGestureToClosePanelAndAssertNoSelection(); + assertLoadedNoUrl(); + } + + //============================================================================================ + // Tap-non-triggering when ARIA annotated as interactive. + //============================================================================================ + + /** + * Tests that a Tap gesture on an element with an ARIA role does not trigger. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @DisabledTest(message = "http://crbug.com/1296677") + public void testTapOnRoleIgnored() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + @OverlayPanel.PanelState + int initialState = mPanel.getPanelState(); + clickNode("role"); + assertPanelStillInState(initialState); + } + + /** + * Tests that a Tap gesture on an element with an ARIA attribute does not trigger. + * http://crbug.com/542874 + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + // Previously flaky and disabled 4/2021. https://crbug.com/1192285 + @DisabledTest(message = "https://crbug.com/1291558") + public void testTapOnARIAIgnored() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + @OverlayPanel.PanelState + int initialState = mPanel.getPanelState(); + clickNode("aria"); + assertPanelStillInState(initialState); + } + + /** + * Tests that a Tap gesture on an element that is focusable does not trigger. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + public void testTapOnFocusableIgnored() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + @OverlayPanel.PanelState + int initialState = mPanel.getPanelState(); + clickNode("focusable"); + assertPanelStillInState(initialState); + } + + //============================================================================================ + // Search-term resolution (server request to determine a search). + //============================================================================================ + + /** + * Tests expanding the panel before the search term has resolved, verifies that nothing + * loads until the resolve completes and that it's now a normal priority URL. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class) + public void testExpandBeforeSearchTermResolution(@EnabledFeature int enabledFeature) + throws Exception { + simulateSlowResolveSearch("states"); + assertNoWebContents(); + + // Expanding before the search term resolves should not load anything. + tapPeekingBarToExpandAndAssert(); + assertLoadedNoUrl(); + + // Once the response comes in, it should load. + simulateSlowResolveFinished(); + assertContainsParameters("States"); + assertLoadedNormalPriorityUrl(); + assertWebContentsCreated(); + assertWebContentsVisible(); + } + + /** + * Tests that the Contextual Search panel does not reappear when a long-press selection is + * modified after the user has taken an action to explicitly dismiss the panel. Also tests + * that the panel reappears when a new selection is made. + */ + @Test + @SmallTest + @Feature({"ContextualSearch"}) + // Previously flaky, disabled 4/2021. https://crbug.com/1192285 + @DisabledTest(message = "https://crbug.com/1291558") + public void testPreventHandlingCurrentSelectionModification() throws Exception { + FeatureList.setTestFeatures(ENABLE_NONE); + + simulateNonResolveSearch("search"); + + // Dismiss the Contextual Search panel. + closePanel(); + Assert.assertEquals("Search", getSelectedText()); + + // Simulate a selection change event and assert that the panel has not reappeared. + TestThreadUtils.runOnUiThreadBlocking(() -> { + SelectionClient selectionClient = mManager.getContextualSearchSelectionClient(); + selectionClient.onSelectionEvent(org.chromium.ui.touch_selection.SelectionEventType + .SELECTION_HANDLE_DRAG_STARTED, + 333, 450); + selectionClient.onSelectionEvent(org.chromium.ui.touch_selection.SelectionEventType + .SELECTION_HANDLE_DRAG_STOPPED, + 303, 450); + }); + assertPanelClosedOrUndefined(); + + // Select a different word and assert that the panel has appeared. + simulateNonResolveSearch("resolution"); + // The simulateNonResolveSearch call will verify that the panel peeks. + } + + @Test + @SmallTest + @Feature({"ContextualSearch"}) + @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class) + // Previously flaky and disabled 4/2021. https://crbug.com/1180304 + public void testSelectionExpansionOnSearchTermResolution(@EnabledFeature int enabledFeature) + throws Exception { + mFakeServer.reset(); + triggerResolve("intelligence"); + waitForPanelToPeek(); + + ResolvedSearchTerm resolvedSearchTerm = + new ResolvedSearchTerm + .Builder(false, 200, "Intelligence", "United States Intelligence") + .setSelectionStartAdjust(-14) + .build(); + fakeResponse(resolvedSearchTerm); + waitForSelectionToBe("United States Intelligence"); + } +}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 14d1bbf..a4e30d7 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2440,6 +2440,8 @@ "//ash/webui/shimless_rma", "//ash/webui/shimless_rma/mojom", "//ash/webui/shortcut_customization_ui", + "//ash/webui/system_extensions_internals_ui", + "//ash/webui/system_extensions_internals_ui/mojom", "//chrome/app/theme:chrome_unscaled_resources_grit", "//chrome/browser/ash/system_extensions", "//chrome/browser/ash/system_extensions/api/hid",
diff --git a/chrome/browser/accessibility/live_caption_controller_browsertest.cc b/chrome/browser/accessibility/live_caption_controller_browsertest.cc index 2d166dc9..121db42 100644 --- a/chrome/browser/accessibility/live_caption_controller_browsertest.cc +++ b/chrome/browser/accessibility/live_caption_controller_browsertest.cc
@@ -83,14 +83,20 @@ void SetLiveCaptionEnabled(bool enabled) { browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, enabled); - if (enabled) + if (enabled) { + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + en_us()); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + } } void SetLiveCaptionEnabledOnProfile(bool enabled, Profile* profile) { profile->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, enabled); - if (enabled) + if (enabled) { + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + en_us()); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + } } LiveCaptionController* GetController() { @@ -181,6 +187,8 @@ #endif } + speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; } + private: base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<CaptionBubbleContextBrowser> caption_bubble_context_; @@ -263,6 +271,7 @@ EXPECT_FALSE(HasBubbleController()); // The UI is only created after SODA is installed. + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us()); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); EXPECT_TRUE(HasBubbleController()); }
diff --git a/chrome/browser/accessibility/live_caption_speech_recognition_host_browsertest.cc b/chrome/browser/accessibility/live_caption_speech_recognition_host_browsertest.cc index 5ea48bc..d4f977f 100644 --- a/chrome/browser/accessibility/live_caption_speech_recognition_host_browsertest.cc +++ b/chrome/browser/accessibility/live_caption_speech_recognition_host_browsertest.cc
@@ -125,8 +125,11 @@ void SetLiveCaptionEnabled(bool enabled) { browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, enabled); - if (enabled) + if (enabled) { + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + speech::LanguageCode::kEnUs); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + } } bool HasBubbleController() {
diff --git a/chrome/browser/accessibility/soda_installer_impl.cc b/chrome/browser/accessibility/soda_installer_impl.cc index aa9a2868..e663d94 100644 --- a/chrome/browser/accessibility/soda_installer_impl.cc +++ b/chrome/browser/accessibility/soda_installer_impl.cc
@@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/check_op.h" +#include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/cxx17_backports.h" #include "base/feature_list.h" @@ -25,32 +26,6 @@ #include "media/base/media_switches.h" #include "ui/base/l10n/l10n_util.h" -namespace { - -int GetDownloadProgress( - const std::map<std::string, update_client::CrxUpdateItem>& - downloading_components) { - int total_bytes = 0; - int downloaded_bytes = 0; - - for (auto component : downloading_components) { - if (component.second.downloaded_bytes >= 0 && - component.second.total_bytes > 0) { - downloaded_bytes += component.second.downloaded_bytes; - total_bytes += component.second.total_bytes; - } - } - - if (total_bytes == 0) - return -1; - - DCHECK_LE(downloaded_bytes, total_bytes); - return 100 * base::clamp(static_cast<double>(downloaded_bytes) / total_bytes, - 0.0, 1.0); -} - -} // namespace - namespace speech { SodaInstallerImpl::SodaInstallerImpl() = default; @@ -151,33 +126,22 @@ case Events::COMPONENT_UPDATE_UPDATING: { update_client::CrxUpdateItem item; g_browser_process->component_updater()->GetComponentDetails(id, &item); - downloading_components_[id] = item; - const int combined_progress = - GetDownloadProgress(downloading_components_); + downloading_components_[language_code] = item; - // When GetDownloadProgress returns -1, do nothing. It returns -1 when the - // downloaded or total bytes is unknown. - if (combined_progress != -1) { - NotifyOnSodaProgress(combined_progress); - } - - if (language_code != LanguageCode::kNone) { - const int language_progress = GetDownloadProgress( - std::map<std::string, update_client::CrxUpdateItem>{{id, item}}); - if (language_progress != -1) { - language_pack_progress_[language_code] = language_progress; - NotifyOnSodaLanguagePackProgress(language_progress, language_code); + if (language_code == LanguageCode::kNone && + !language_pack_progress_.empty()) { + for (auto language : language_pack_progress_) { + UpdateAndNotifyOnSodaProgress(language.first); } + } else { + UpdateAndNotifyOnSodaProgress(language_code); } - } break; case Events::COMPONENT_UPDATE_ERROR: is_soda_downloading_ = false; if (language_code != LanguageCode::kNone) { language_pack_progress_.erase(language_code); - NotifyOnSodaLanguagePackError(language_code); - base::UmaHistogramTimes( GetInstallationFailureTimeMetricForLanguagePack(language_code), base::Time::Now() - @@ -185,7 +149,6 @@ base::UmaHistogramBoolean( GetInstallationResultMetricForLanguagePack(language_code), false); - } else { base::UmaHistogramTimes( kSodaBinaryInstallationFailureTimeTaken, @@ -194,7 +157,7 @@ base::UmaHistogramBoolean(kSodaBinaryInstallationResult, false); } - NotifyOnSodaError(); + NotifyOnSodaError(language_code); break; case Events::COMPONENT_CHECKING_FOR_UPDATES: case Events::COMPONENT_UPDATED: @@ -207,8 +170,8 @@ void SodaInstallerImpl::OnSodaBinaryInstalled() { soda_binary_installed_ = true; is_soda_downloading_ = false; - if (IsAnyLanguagePackInstalled()) { - NotifyOnSodaInstalled(); + for (LanguageCode language : installed_languages_) { + NotifyOnSodaInstalled(language); } base::UmaHistogramTimes(kSodaBinaryInstallationSuccessTimeTaken, @@ -220,10 +183,9 @@ speech::LanguageCode language_code) { installed_languages_.insert(language_code); language_pack_progress_.erase(language_code); - NotifyOnSodaLanguagePackInstalled(language_code); if (soda_binary_installed_) { - NotifyOnSodaInstalled(); + NotifyOnSodaInstalled(language_code); } base::UmaHistogramTimes( @@ -233,4 +195,32 @@ GetInstallationResultMetricForLanguagePack(language_code), true); } +void SodaInstallerImpl::UpdateAndNotifyOnSodaProgress( + speech::LanguageCode language_code) { + int total_bytes = 0; + int downloaded_bytes = 0; + speech::LanguageCode soda_code = speech::LanguageCode::kNone; + + if (base::Contains(downloading_components_, soda_code)) { + total_bytes += downloading_components_[soda_code].total_bytes; + downloaded_bytes += downloading_components_[soda_code].downloaded_bytes; + } + + if (language_code != soda_code) { + total_bytes += downloading_components_[language_code].total_bytes; + downloaded_bytes += downloading_components_[language_code].downloaded_bytes; + } + + if (total_bytes == 0) + return; + + DCHECK_LE(downloaded_bytes, total_bytes); + int progress = + 100 * base::clamp(static_cast<double>(downloaded_bytes) / total_bytes, + 0.0, 1.0); + if (language_code != soda_code) + language_pack_progress_[language_code] = progress; + NotifyOnSodaProgress(language_code, progress); +} + } // namespace speech
diff --git a/chrome/browser/accessibility/soda_installer_impl.h b/chrome/browser/accessibility/soda_installer_impl.h index 8e694e7c..fc63790f 100644 --- a/chrome/browser/accessibility/soda_installer_impl.h +++ b/chrome/browser/accessibility/soda_installer_impl.h
@@ -57,7 +57,10 @@ void OnSodaLanguagePackInstalled(speech::LanguageCode language_code); private: - std::map<std::string, update_client::CrxUpdateItem> downloading_components_; + void UpdateAndNotifyOnSodaProgress(speech::LanguageCode language_code); + + std::map<speech::LanguageCode, update_client::CrxUpdateItem> + downloading_components_; base::Time soda_binary_install_start_time_; base::flat_map<LanguageCode, base::Time> language_pack_install_start_time_;
diff --git a/chrome/browser/apps/app_service/notifications_browsertest.cc b/chrome/browser/apps/app_service/notifications_browsertest.cc index de15794b..7409982a 100644 --- a/chrome/browser/apps/app_service/notifications_browsertest.cc +++ b/chrome/browser/apps/app_service/notifications_browsertest.cc
@@ -94,7 +94,7 @@ } absl::optional<bool> HasBadge(Profile* profile, const std::string& app_id) { - auto mojom_has_badge = OptionalBool::kUnknown; + absl::optional<bool> mojom_has_badge; auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile); proxy->FlushMojoCallsForTesting(); proxy->AppRegistryCache().ForOneApp( @@ -105,13 +105,13 @@ absl::optional<bool> has_badge; proxy->AppRegistryCache().ForApp(app_id, [&has_badge](const apps::AppUpdate& update) { - has_badge = update.GetHasBadge(); + has_badge = update.HasBadge(); }); - if (has_badge.has_value()) { - if (has_badge.value() && mojom_has_badge == OptionalBool::kTrue) + if (has_badge.has_value() && has_badge == mojom_has_badge) { + if (has_badge.value()) return true; - if (!has_badge.value() && mojom_has_badge == OptionalBool::kFalse) + if (!has_badge.value()) return false; } return absl::nullopt;
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc index 1b97dbe..3c97ece 100644 --- a/chrome/browser/apps/app_service/publishers/arc_apps.cc +++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -395,7 +395,7 @@ } // Constructs an OpenUrlsRequest to be passed to -// FileSystemInstance.OpenUrlsWithPermission. +// FileSystemInstance.DEPRECATED_OpenUrlsWithPermission. arc::mojom::OpenUrlsRequestPtr ConstructOpenUrlsRequest( const apps::mojom::IntentPtr& intent, const arc::mojom::ActivityNamePtr& activity, @@ -449,12 +449,12 @@ } else { arc_file_system = ARC_GET_INSTANCE_FOR_METHOD( arc_service_manager->arc_bridge_service()->file_system(), - OpenUrlsWithPermission); + DEPRECATED_OpenUrlsWithPermission); if (!arc_file_system) { return; } - arc_file_system->OpenUrlsWithPermission( + arc_file_system->DEPRECATED_OpenUrlsWithPermission( ConstructOpenUrlsRequest(intent, activity, content_urls), base::DoNothing()); }
diff --git a/chrome/browser/apps/app_service/webapk/webapk_install_task.cc b/chrome/browser/apps/app_service/webapk/webapk_install_task.cc index 4330115..ee35f1c 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_install_task.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_install_task.cc
@@ -462,19 +462,13 @@ std::unique_ptr<std::string> response_body) { timer_.Stop(); - int response_or_error_code = -1; + int response_code = -1; if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers) { - response_or_error_code = - url_loader_->ResponseInfo()->headers->response_code(); - } else { - response_or_error_code = url_loader_->NetError(); + response_code = url_loader_->ResponseInfo()->headers->response_code(); } - base::UmaHistogramSparse(kWebApkMinterErrorCodeHistogram, - response_or_error_code); - if (!response_body || response_or_error_code != net::HTTP_OK) { - LOG(WARNING) << "WebAPK server request returned error " - << response_or_error_code; + if (!response_body || response_code != net::HTTP_OK) { + LOG(WARNING) << "WebAPK server returned response code " << response_code; DeliverResult(WebApkInstallStatus::kNetworkError); return; }
diff --git a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc index 4cd28c0..fba00c95 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
@@ -223,8 +223,6 @@ apps::WebApkInstallStatus::kSuccess, 1); histograms.ExpectBucketCount(apps::kWebApkArcInstallResultHistogram, arc::mojom::WebApkInstallResult::kSuccess, 1); - histograms.ExpectBucketCount(apps::kWebApkMinterErrorCodeHistogram, - net::HTTP_OK, 1); } TEST_F(WebApkInstallTaskTest, ShareTarget) { @@ -291,8 +289,6 @@ ASSERT_EQ(apps::webapk_prefs::GetWebApkAppIds(profile()).size(), 0u); histograms.ExpectBucketCount(apps::kWebApkInstallResultHistogram, apps::WebApkInstallStatus::kNetworkError, 1); - histograms.ExpectBucketCount(apps::kWebApkMinterErrorCodeHistogram, - net::HTTP_BAD_REQUEST, 1); } TEST_F(WebApkInstallTaskTest, FailedArcInstall) {
diff --git a/chrome/browser/apps/app_service/webapk/webapk_manager.cc b/chrome/browser/apps/app_service/webapk/webapk_manager.cc index 63d121c..ffbcfc5 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_manager.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_manager.cc
@@ -203,7 +203,6 @@ // If an installed WebAPK is not listed in WebAPK prefs, then we will generate // and install a new WebAPK automatically, possibly resulting in duplicate // apps visible to the user. - int uninstall_count = 0; std::vector<std::string> installed_packages = app_list_prefs_->GetPackagesFromPrefs(); base::flat_set<std::string> installed_webapk_packages = @@ -211,7 +210,6 @@ for (const auto& package_name : installed_packages) { if (base::StartsWith(package_name, kGeneratedWebApkPackagePrefix) && !installed_webapk_packages.contains(package_name)) { - uninstall_count++; auto* instance = ARC_GET_INSTANCE_FOR_METHOD( app_list_prefs_->app_connection_holder(), UninstallPackage); if (!instance) { @@ -220,14 +218,6 @@ instance->UninstallPackage(package_name); } } - - if (uninstall_count > 0) { - // Record the number of instances of this issue so we can determine whether - // further investigation/prevention is warranted. - base::UmaHistogramCustomCounts("ChromeOS.WebAPK.UnlinkedWebAPKCount", - uninstall_count, /*min=*/1, /*max=*/20, - /*buckets=*/10); - } } void WebApkManager::OnPackageRemoved(const std::string& package_name, @@ -248,26 +238,7 @@ // TODO(crbug.com/1200199): Remove the web app as well, if it is still // installed and eligible, and WebAPKs are not disabled by policy. - absl::optional<std::string> app_id = - webapk_prefs::RemoveWebApkByPackageName(profile_, package_name); - - if (!uninstalled || !app_id.has_value()) { - return; - } - - bool is_installed_and_eligible = false; - proxy_->AppRegistryCache().ForOneApp( - app_id.value(), [&](const AppUpdate& update) { - is_installed_and_eligible = IsAppEligibleForWebApk(update); - }); - - // Record a metric so we can determine how often WebAPKs are uninstalled from - // Android settings. - WebApkUninstallSource uninstall_source = is_installed_and_eligible - ? WebApkUninstallSource::kArc - : WebApkUninstallSource::kAsh; - base::UmaHistogramEnumeration(kWebApkUninstallSourceHistogram, - uninstall_source); + webapk_prefs::RemoveWebApkByPackageName(profile_, package_name); } void WebApkManager::OnArcPlayStoreEnabledChanged(bool enabled) {
diff --git a/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc b/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc index 3d5d322..80fc4dd 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc
@@ -213,15 +213,12 @@ apps::webapk_prefs::AddWebApk(profile(), app_id, kTestWebApkPackageName); StartWebApkManager(); arc_test()->app_instance()->SendRefreshPackageList({}); - base::HistogramTester histograms; app_service_proxy()->UninstallSilently( app_id, apps::mojom::UninstallSource::kUnknown); app_service_test()->FlushMojoCalls(); ASSERT_FALSE(apps::webapk_prefs::GetWebApkPackageName(profile(), app_id)); - histograms.ExpectBucketCount(apps::kWebApkUninstallSourceHistogram, - apps::WebApkUninstallSource::kAsh, 1); } TEST_F(WebApkManagerTest, QueuesUpdatedApp) { @@ -327,12 +324,10 @@ "org.chromium.webapk.package1"); StartWebApkManager(); - base::HistogramTester histograms; arc_test()->app_instance()->SendRefreshPackageList(std::move(packages)); ASSERT_TRUE(ArcAppListPrefs::Get(profile())->GetPackage( "org.chromium.webapk.package1")); ASSERT_FALSE(ArcAppListPrefs::Get(profile())->GetPackage( "org.chromium.webapk.package2")); - histograms.ExpectUniqueSample("ChromeOS.WebAPK.UnlinkedWebAPKCount", 1, 1); }
diff --git a/chrome/browser/apps/app_service/webapk/webapk_metrics.cc b/chrome/browser/apps/app_service/webapk/webapk_metrics.cc index 59be6da..39e6ef7 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_metrics.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_metrics.cc
@@ -15,10 +15,6 @@ "ChromeOS.WebAPK.Install.ArcInstallResult"; const char kWebApkArcUpdateResultHistogram[] = "ChromeOS.WebAPK.Update.ArcInstallResult"; -const char kWebApkMinterErrorCodeHistogram[] = - "ChromeOS.WebAPK.MinterResponseOrErrorCode"; -const char kWebApkUninstallSourceHistogram[] = - "ChromeOS.WebApk.UninstallSource"; void RecordWebApkInstallResult(bool is_update, WebApkInstallStatus result) { const char* histogram =
diff --git a/chrome/browser/apps/app_service/webapk/webapk_metrics.h b/chrome/browser/apps/app_service/webapk/webapk_metrics.h index 2d09b33..c6d4c85 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_metrics.h +++ b/chrome/browser/apps/app_service/webapk/webapk_metrics.h
@@ -49,8 +49,6 @@ extern const char kWebApkUpdateResultHistogram[]; extern const char kWebApkArcInstallResultHistogram[]; extern const char kWebApkArcUpdateResultHistogram[]; -extern const char kWebApkMinterErrorCodeHistogram[]; -extern const char kWebApkUninstallSourceHistogram[]; // Records the overall result of installing/updating a WebAPK to UMA. void RecordWebApkInstallResult(bool is_update, WebApkInstallStatus result);
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc index 573f9a8d85..56e8fd2a 100644 --- a/chrome/browser/ash/accessibility/accessibility_manager.cc +++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -946,7 +946,7 @@ // nudge hasn't yet been shown to the user. if (!offline_nudge || !offline_nudge.value()) { if (speech::SodaInstaller::GetInstance()->IsSodaInstalled( - speech::GetLanguageCode(dictation_locale))) { + GetDictationLanguageCode())) { // The locale is already installed on device, show the nudge // immediately. ShowDictationLanguageUpgradedNudge(dictation_locale); @@ -2027,16 +2027,11 @@ if (!::features::IsDictationOfflineAvailable()) return true; + // Show the dialog for languages not supported by SODA. speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance(); std::vector<std::string> supported_languages = soda_installer->GetAvailableLanguages(); - if (std::find(supported_languages.begin(), supported_languages.end(), - locale) == supported_languages.end()) { - // Show the dialog for languages not supported by SODA. - return true; - } - - return false; + return !base::Contains(supported_languages, locale); } void AccessibilityManager::ShowNetworkDictationDialog() { @@ -2098,19 +2093,6 @@ soda_failed_notification_shown_ = false; } -void AccessibilityManager::OnSodaInstallSucceeded() { - if (ShouldShowSodaSucceededNotificationForDictation()) - ShowSodaDownloadNotificationForDictation(true); - OnSodaInstallUpdated(100); -} - -void AccessibilityManager::OnSodaInstallError( - speech::LanguageCode language_code) { - if (ShouldShowSodaFailedNotificationForDictation(language_code)) - ShowSodaDownloadNotificationForDictation(false); - OnSodaInstallUpdated(0); -} - void AccessibilityManager::OnSodaInstallUpdated(int progress) { if (!::features::IsDictationOfflineAvailable()) return; @@ -2118,15 +2100,13 @@ speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance(); const std::string dictation_locale = profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale); - speech::LanguageCode dictation_language_code = - speech::GetLanguageCode(dictation_locale); // Update the Dictation button tray. // TODO(https://crbug.com/1266491): Ensure we use combined progress instead // of just the language pack progress. AccessibilityController::Get() ->UpdateDictationButtonOnSpeechRecognitionDownloadChanged(progress); - if (soda_installer->IsSodaDownloading(dictation_language_code)) + if (soda_installer->IsSodaDownloading(GetDictationLanguageCode())) return; const absl::optional<bool> offline_nudge = @@ -2135,41 +2115,42 @@ // shown to the user (the key is in kAccessibilityDictationLocale but the // value is false). if (offline_nudge && !offline_nudge.value() && - soda_installer->IsSodaInstalled( - speech::GetLanguageCode(dictation_locale))) { + soda_installer->IsSodaInstalled(GetDictationLanguageCode())) { ShowDictationLanguageUpgradedNudge(dictation_locale); } } // SodaInstaller::Observer: -void AccessibilityManager::OnSodaInstalled() { - OnSodaInstallSucceeded(); -} - -void AccessibilityManager::OnSodaError() { - OnSodaInstallError(speech::LanguageCode::kNone); -} - -void AccessibilityManager::OnSodaLanguagePackInstalled( - speech::LanguageCode language_code) { - OnSodaInstallSucceeded(); -} - -void AccessibilityManager::OnSodaLanguagePackProgress( - int language_progress, - speech::LanguageCode language_code) { - const std::string locale = - profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale); - - if (language_code != speech::GetLanguageCode(locale)) +void AccessibilityManager::OnSodaInstalled(speech::LanguageCode language_code) { + if (language_code != GetDictationLanguageCode()) return; - OnSodaInstallUpdated(language_progress); + if (ShouldShowSodaSucceededNotificationForDictation()) + ShowSodaDownloadNotificationForDictation(true); + OnSodaInstallUpdated(100); } -void AccessibilityManager::OnSodaLanguagePackError( - speech::LanguageCode language_code) { - OnSodaInstallError(language_code); +void AccessibilityManager::OnSodaError(speech::LanguageCode language_code) { + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLanguageCode()) { + return; + } + + // Show the failed message if either the Dictation locale failed or the SODA + // binary failed (encoded by LanguageCode::kNone). + if (ShouldShowSodaFailedNotificationForDictation(language_code)) + ShowSodaDownloadNotificationForDictation(false); + OnSodaInstallUpdated(0); +} + +void AccessibilityManager::OnSodaProgress(speech::LanguageCode language_code, + int progress) { + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLanguageCode()) { + return; + } + + OnSodaInstallUpdated(progress); } bool AccessibilityManager::ShouldShowSodaSucceededNotificationForDictation() { @@ -2182,14 +2163,8 @@ // download, either for the SODA binary or a language pack. // Both the SODA binary and the language pack matching the Dictation locale // need to be downloaded to return true. - const std::string locale = - profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale); - if (speech::SodaInstaller::GetInstance()->IsSodaInstalled( - speech::GetLanguageCode(locale))) { - return true; - } - - return false; + return speech::SodaInstaller::GetInstance()->IsSodaInstalled( + GetDictationLanguageCode()); } bool AccessibilityManager::ShouldShowSodaFailedNotificationForDictation( @@ -2207,13 +2182,8 @@ // 1. |language_code| == kNone (encodes that this was an error for the SODA // binary), or // 2. |language_code| matches the Dictation locale. - const std::string locale = - profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale); - if (language_code == speech::LanguageCode::kNone || - language_code == speech::GetLanguageCode(locale)) - return true; - - return false; + return language_code == speech::LanguageCode::kNone || + language_code == GetDictationLanguageCode(); } void AccessibilityManager::ShowSodaDownloadNotificationForDictation( @@ -2236,4 +2206,10 @@ soda_failed_notification_shown_ = true; } +speech::LanguageCode AccessibilityManager::GetDictationLanguageCode() { + DCHECK(profile_); + return speech::GetLanguageCode( + profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale)); +} + } // namespace ash
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.h b/chrome/browser/ash/accessibility/accessibility_manager.h index 0376a59..868f6172 100644 --- a/chrome/browser/ash/accessibility/accessibility_manager.h +++ b/chrome/browser/ash/accessibility/accessibility_manager.h
@@ -365,13 +365,10 @@ double value); // SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaLanguagePackInstalled(speech::LanguageCode language_code) override; - void OnSodaError() override; - void OnSodaLanguagePackError(speech::LanguageCode language_code) override; - void OnSodaProgress(int combined_progress) override {} - void OnSodaLanguagePackProgress(int language_progress, - speech::LanguageCode language_code) override; + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaError(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override; // Test helpers: void SetProfileForTest(Profile* profile); @@ -494,8 +491,6 @@ // SODA-related methods. void MaybeInstallSoda(const std::string& locale); - void OnSodaInstallSucceeded(); - void OnSodaInstallError(speech::LanguageCode language_code); void OnSodaInstallUpdated(int progress); bool ShouldShowSodaSucceededNotificationForDictation(); bool ShouldShowSodaFailedNotificationForDictation( @@ -503,6 +498,7 @@ void ShowSodaDownloadNotificationForDictation(bool succeeded); void ShowDictationLanguageUpgradedNudge(const std::string& locale); + speech::LanguageCode GetDictationLanguageCode(); void CreateChromeVoxPanel();
diff --git a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc index b787a60..15ca45d 100644 --- a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc +++ b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
@@ -801,6 +801,7 @@ } speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; } + speech::LanguageCode fr_fr() { return speech::LanguageCode::kFrFr; } const std::u16string en_us_display_name() { return u"English (United States)"; @@ -821,7 +822,8 @@ // The nudge should not be requested to be shown because this was a // user-initiated change. EXPECT_FALSE(GetDictationOfflineNudgePref("en-US")); - speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_FALSE(IsSodaDownloading()); // The nudge was never shown. EXPECT_FALSE(GetDictationOfflineNudgePref("en-US")); @@ -841,9 +843,7 @@ // The nudge should be shown when SODA download finishes. EXPECT_FALSE(GetDictationOfflineNudgePref("en-US").value()); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); - speech::SodaInstaller::GetInstance() - ->NotifyOnSodaLanguagePackInstalledForTesting( - speech::LanguageCode::kEnUs); + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us()); EXPECT_FALSE(IsSodaDownloading()); EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value()); // No notifications were shown. @@ -876,7 +876,8 @@ ClearDictationOfflineNudgePref("en-US"); EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/true); EXPECT_TRUE(IsSodaDownloading()); - speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_FALSE(IsSodaDownloading()); EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value()); UninstallSodaForTesting(); @@ -885,7 +886,8 @@ // The second time the same language downloads, the nudge is not shown again. EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false); EXPECT_TRUE(IsSodaDownloading()); - speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_FALSE(IsSodaDownloading()); // Unchanged. EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value()); @@ -893,7 +895,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, SodaInstalledBeforeDictationEnabled) { - speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); ClearDictationOfflineNudgePref("en-US"); EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false); @@ -920,7 +923,8 @@ // enabled. This mocks selecting a new locale from settings. SetDictationLocale("en-US"); EXPECT_TRUE(IsSodaDownloading()); - speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_FALSE(IsSodaDownloading()); // The nudge was never shown because this was a user-initiated change. EXPECT_FALSE(GetDictationOfflineNudgePref("en-US")); @@ -936,11 +940,11 @@ SucceededNotificationCase1) { // For this test, pretend that the Dictation locale is fr-FR. g_browser_process->SetApplicationLocale("fr-FR"); - speech::LanguageCode fr_fr = speech::LanguageCode::kFrFr; SetDictationEnabled(true); soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(fr_fr); + soda_installer()->NotifySodaInstalledForTesting(fr_fr()); AssertSodaNotificationShownForDictation(u"français (France)", /*success=*/true); } @@ -950,7 +954,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, SucceededNotificationCase2) { SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); soda_installer()->NotifySodaInstalledForTesting(); AssertSodaNotificationShownForDictation(en_us_display_name(), @@ -972,7 +976,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, SodaFailedNotificationLanguageError) { SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertSodaNotificationShownForDictation(en_us_display_name(), /*success=*/false); } @@ -982,7 +986,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, LanguageInstalledBinaryFails) { SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); soda_installer()->NotifySodaErrorForTesting(); AssertSodaNotificationShownForDictation(en_us_display_name(), @@ -995,11 +999,11 @@ BinaryInstalledLanguageFails) { // For this test, pretend that the Dictation locale is fr-FR. g_browser_process->SetApplicationLocale("fr-FR"); - speech::LanguageCode fr_fr = speech::LanguageCode::kFrFr; SetDictationEnabled(true); soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(fr_fr); + soda_installer()->NotifySodaErrorForTesting(fr_fr()); AssertSodaNotificationShownForDictation(u"français (France)", /*success=*/false); } @@ -1009,13 +1013,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, SodaFailedNotificationNotShownTwice) { SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertSodaNotificationShownForDictation(en_us_display_name(), /*success=*/false); ClearMessageCenter(); // No second message is shown on additional failures. - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertMessageCenterEmpty(); soda_installer()->NotifySodaErrorForTesting(); AssertMessageCenterEmpty(); @@ -1026,7 +1030,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, SodaFailedNotificationShownOncePerDownload) { SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertSodaNotificationShownForDictation(en_us_display_name(), /*success=*/false); SetDictationEnabled(false); @@ -1037,7 +1041,7 @@ // A fresh attempt at Dictation means another chance to show an error message. SetDictationEnabled(true); - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertSodaNotificationShownForDictation(en_us_display_name(), /*success=*/false); } @@ -1047,7 +1051,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, NotTriggeredByUser) { EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false); soda_installer()->NotifySodaInstalledForTesting(); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); } @@ -1059,7 +1063,7 @@ SetDictationEnabled(true); soda_installer()->NotifySodaInstalledForTesting(); AssertMessageCenterEmpty(); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertMessageCenterEmpty(); } @@ -1074,7 +1078,7 @@ // enabled. This mocks selecting a new locale from settings. SetDictationLocale("en-US"); soda_installer()->NotifySodaInstalledForTesting(); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); // The notification should have been shown. AssertSodaNotificationShownForDictation(en_us_display_name(), @@ -1095,22 +1099,20 @@ // The API will not be called if the language pack differs from the Dictation // locale. - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting( - 30, speech::LanguageCode::kFrFr); + soda_installer()->NotifySodaProgressForTesting(30, fr_fr()); EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress()); // The API will be called if the language pack matches the Dictation locale. - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting( - 50, speech::LanguageCode::kEnUs); + soda_installer()->NotifySodaProgressForTesting(50, en_us()); EXPECT_EQ(50, test_api->GetDictationSodaDownloadProgress()); // If SODA download fails, the API will be called with a value of 0. soda_installer()->NotifySodaErrorForTesting(); EXPECT_EQ(0, test_api->GetDictationSodaDownloadProgress()); // Reset to a non-zero value. - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting( - 70, speech::LanguageCode::kEnUs); + soda_installer()->NotifySodaProgressForTesting(70, en_us()); EXPECT_EQ(70, test_api->GetDictationSodaDownloadProgress()); // If SODA download succeeds, the API will be called with a value of 100. soda_installer()->NotifySodaInstalledForTesting(); + soda_installer()->NotifySodaInstalledForTesting(en_us()); EXPECT_EQ(100, test_api->GetDictationSodaDownloadProgress()); }
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc index 384b4f47..a3cc56f5 100644 --- a/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc +++ b/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc
@@ -217,7 +217,7 @@ void ReadWindowTitle() { extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait( browser()->profile(), extension_misc::kChromeVoxExtensionId, - "CommandHandler.onCommand('readCurrentTitle');"); + "CommandHandlerInterface.instance.onCommand('readCurrentTitle');"); } AppListItem* FindItemByName(const std::string& name, int* index) {
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc index f6add6037..04d566e 100644 --- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc +++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -138,7 +138,7 @@ // To avoid flakes in sending keys, execute the command directly in js. extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait( browser()->profile(), extension_misc::kChromeVoxExtensionId, - "CommandHandler.onCommand('toggleStickyMode');"); + "CommandHandlerInterface.instance.onCommand('toggleStickyMode');"); } void LoggedInSpokenFeedbackTest::SendMouseMoveTo(const gfx::Point& location) { @@ -270,7 +270,7 @@ sm_.Call([this]() { extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait( browser()->profile(), extension_misc::kChromeVoxExtensionId, - "CommandHandler.onCommand('showKbExplorerPage');"); + "CommandHandlerInterface.instance.onCommand('showKbExplorerPage');"); }); sm_.ExpectSpeechPattern( "Press a qwerty key, refreshable braille key, or touch gesture to learn " @@ -307,7 +307,7 @@ sm_.Call([this]() { extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait( browser()->profile(), extension_misc::kChromeVoxExtensionId, - "CommandHandler.onCommand('showKbExplorerPage');"); + "CommandHandlerInterface.instance.onCommand('showKbExplorerPage');"); }); sm_.ExpectSpeechPattern( "Press a qwerty key, refreshable braille key, or touch gesture to learn "
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc index d5e7c60..93e75eac 100644 --- a/chrome/browser/ash/crosapi/browser_util.cc +++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -800,5 +800,17 @@ return base::StringPiece(); } +bool IsAshBrowserSyncEnabled() { + // Turn off sync from Ash if Lacros is enabled and Ash web browser is + // disabled. + // TODO(crbug.com/1293250): We must check whether profile migration is + // completed or not here. Currently that is checked inside `IsLacrosEnabled()` + // but it is planned to be decoupled with the function in the future. + if (IsLacrosEnabled() && !IsAshWebBrowserEnabled()) + return false; + + return true; +} + } // namespace browser_util } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h index 8726d99..468e7824 100644 --- a/chrome/browser/ash/crosapi/browser_util.h +++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -292,6 +292,13 @@ // g_browser_process->local_state() etc. void SetProfileMigrationCompletedForTest(bool is_completed); +// Indicate whether sync on Ash should be enabled for browser data. Sync should +// stop syncing browser items from Ash if Lacros is enabled and once browser +// data is migrated to Lacros making it safe to turn off web browser on +// Ash and sync for browser data. Only use after the primary user profile is set +// on UserManager since it calls `IsLacrosEnabled()`. +bool IsAshBrowserSyncEnabled(); + // Returns who decided how Lacros should be used - or not: The User, the policy // or another edge case. LacrosLaunchSwitchSource GetLacrosLaunchSwitchSource();
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc index ca1e77b..2d0558b 100644 --- a/chrome/browser/ash/crosapi/browser_util_unittest.cc +++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -687,4 +687,44 @@ user_id_hash)); } +TEST_F(BrowserUtilTest, IsAshBrowserSyncEnabled) { + { + browser_util::SetLacrosEnabledForTest(false); + EXPECT_FALSE(browser_util::IsLacrosEnabled()); + EXPECT_TRUE(browser_util::IsAshWebBrowserEnabled()); + EXPECT_TRUE(browser_util::IsAshBrowserSyncEnabled()); + } + + { + browser_util::SetLacrosEnabledForTest(true); + EXPECT_TRUE(browser_util::IsLacrosEnabled()); + EXPECT_TRUE(browser_util::IsAshWebBrowserEnabled()); + EXPECT_TRUE(browser_util::IsAshBrowserSyncEnabled()); + } + + { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {chromeos::features::kLacrosOnly, chromeos::features::kLacrosPrimary, + chromeos::features::kLacrosSupport}, + {}); + browser_util::SetLacrosEnabledForTest(false); + EXPECT_FALSE(browser_util::IsLacrosEnabled()); + EXPECT_TRUE(browser_util::IsAshWebBrowserEnabled()); + EXPECT_TRUE(browser_util::IsAshBrowserSyncEnabled()); + } + + { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {chromeos::features::kLacrosOnly, chromeos::features::kLacrosPrimary, + chromeos::features::kLacrosSupport}, + {}); + browser_util::SetLacrosEnabledForTest(true); + EXPECT_TRUE(browser_util::IsLacrosEnabled()); + EXPECT_FALSE(browser_util::IsAshWebBrowserEnabled()); + EXPECT_FALSE(browser_util::IsAshBrowserSyncEnabled()); + } +} + } // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/message_center_ash.cc b/chrome/browser/ash/crosapi/message_center_ash.cc index 9b62700..950aaef 100644 --- a/chrome/browser/ash/crosapi/message_center_ash.cc +++ b/chrome/browser/ash/crosapi/message_center_ash.cc
@@ -84,6 +84,7 @@ rich_data.accessible_name = notification->accessible_name; rich_data.fullscreen_visibility = FromMojo(notification->fullscreen_visibility); + rich_data.accent_color = notification->accent_color; gfx::Image icon; if (!notification->icon.isNull())
diff --git a/chrome/browser/ash/eche_app/eche_app_manager_factory.cc b/chrome/browser/ash/eche_app/eche_app_manager_factory.cc index 4e0e706..baf1171 100644 --- a/chrome/browser/ash/eche_app/eche_app_manager_factory.cc +++ b/chrome/browser/ash/eche_app/eche_app_manager_factory.cc
@@ -96,7 +96,14 @@ // TODO(nayebi): if it is null log an error? Dcheck? if (eche_tray) { eche_tray->SetUrl(url); - eche_tray->ShowBubble(); + if (!features::IsEcheSWAInBackgroundEnabled()) { + eche_tray->ShowBubble(); + } else { + eche_tray->InitBubble(); + + // Hide bubble first until the streaming is ready. + eche_tray->HideBubble(); + } } }
diff --git a/chrome/browser/ash/file_manager/arc_file_tasks.cc b/chrome/browser/ash/file_manager/arc_file_tasks.cc index 6c3657d..b8e17c4 100644 --- a/chrome/browser/ash/file_manager/arc_file_tasks.cc +++ b/chrome/browser/ash/file_manager/arc_file_tasks.cc
@@ -95,7 +95,7 @@ } // Constructs an OpenUrlsRequest to be passed to -// FileSystemInstance.OpenUrlsWithPermission. +// FileSystemInstance.DEPRECATED_OpenUrlsWithPermission. arc::mojom::OpenUrlsRequestPtr ConstructOpenUrlsRequest( const TaskDescriptor& task, const std::vector<GURL>& content_urls, @@ -286,7 +286,7 @@ arc::mojom::FileSystemInstance* arc_file_system = ARC_GET_INSTANCE_FOR_METHOD( arc_service_manager->arc_bridge_service()->file_system(), - OpenUrlsWithPermission); + DEPRECATED_OpenUrlsWithPermission); if (!arc_file_system) { std::move(done).Run( extensions::api::file_manager_private::TASK_RESULT_FAILED, @@ -296,8 +296,8 @@ arc::mojom::OpenUrlsRequestPtr request = ConstructOpenUrlsRequest(task, content_urls, mime_types); - arc_file_system->OpenUrlsWithPermission(std::move(request), - base::DoNothing()); + arc_file_system->DEPRECATED_OpenUrlsWithPermission(std::move(request), + base::DoNothing()); // TODO(benwells): return the correct code here, depending on how the app // will be opened in multiprofile. std::move(done).Run(
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc index 26987946..0eea693 100644 --- a/chrome/browser/ash/file_manager/extract_io_task.cc +++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/ash/file_manager/extract_io_task.h" +#include "chrome/browser/chromeos/fileapi/file_system_backend.h" +#include "components/services/unzip/content/unzip_service.h" + namespace file_manager { namespace io_task { @@ -11,19 +14,44 @@ std::vector<storage::FileSystemURL> source_urls, storage::FileSystemURL parent_folder, scoped_refptr<storage::FileSystemContext> file_system_context) - : file_system_context_(file_system_context) { + : source_urls_(std::move(source_urls)), + parent_folder_(std::move(parent_folder)), + file_system_context_(std::move(file_system_context)) { progress_.type = OperationType::kExtract; + progress_.state = State::kSuccess; } ExtractIOTask::~ExtractIOTask() {} +void ExtractIOTask::ZipExtractCallback(bool success) { + progress_.state = success ? State::kSuccess : State::kError; +} + void ExtractIOTask::Execute(IOTask::ProgressCallback progress_callback, IOTask::CompleteCallback complete_callback) { progress_callback_ = std::move(progress_callback); complete_callback_ = std::move(complete_callback); VLOG(1) << "Executing EXTRACT_ARCHIVE IO task"; - Complete(State::kSuccess); + // TODO(crbug.com/953256) Generalize for multiple files. + if (source_urls_.size() == 1) { + if (!chromeos::FileSystemBackend::CanHandleURL(source_urls_[0])) { + progress_.state = State::kError; + } else { + base::FilePath source_file = source_urls_[0].path(); + if (chromeos::FileSystemBackend::CanHandleURL(parent_folder_)) { + base::FilePath destination_directory = parent_folder_.path(); + unzip::Unzip(unzip::LaunchUnzipper(), source_file, + destination_directory, + base::BindOnce(&ExtractIOTask::ZipExtractCallback, + weak_ptr_factory_.GetWeakPtr())); + } else { + progress_.state = State::kError; + } + } + } + + Complete(); } void ExtractIOTask::Cancel() { @@ -33,8 +61,7 @@ // Calls the completion callback for the task. |progress_| should not be // accessed after calling this. -void ExtractIOTask::Complete(State state) { - progress_.state = state; +void ExtractIOTask::Complete() { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(complete_callback_), std::move(progress_)));
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h index fc34943f..8d876b5 100644 --- a/chrome/browser/ash/file_manager/extract_io_task.h +++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -12,6 +12,7 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ash/file_manager/io_task.h" +#include "components/services/unzip/public/cpp/unzip.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_url.h" @@ -34,7 +35,15 @@ void Cancel() override; private: - void Complete(State state); + void Complete(); + + void ZipExtractCallback(bool success); + + // URLs of the files that have archives in them for extraction. + const std::vector<storage::FileSystemURL> source_urls_; + + // Parent folder of the files in 'source_urls_'. + const storage::FileSystemURL parent_folder_; const scoped_refptr<storage::FileSystemContext> file_system_context_;
diff --git a/chrome/browser/ash/note_taking_helper.cc b/chrome/browser/ash/note_taking_helper.cc index c74a192..a105051 100644 --- a/chrome/browser/ash/note_taking_helper.cc +++ b/chrome/browser/ash/note_taking_helper.cc
@@ -768,11 +768,11 @@ arc::mojom::FileSystemInstance* arc_file_system = ARC_GET_INSTANCE_FOR_METHOD( arc::ArcServiceManager::Get()->arc_bridge_service()->file_system(), - OpenUrlsWithPermission); + DEPRECATED_OpenUrlsWithPermission); if (!arc_file_system) return LaunchResult::ANDROID_NOT_RUNNING; - arc_file_system->OpenUrlsWithPermission(std::move(request), - base::DoNothing()); + arc_file_system->DEPRECATED_OpenUrlsWithPermission(std::move(request), + base::DoNothing()); arc::ArcMetricsService::RecordArcUserInteraction( profile, arc::UserInteractionType::APP_STARTED_FROM_STYLUS_TOOLS);
diff --git a/chrome/browser/ash/system_extensions/BUILD.gn b/chrome/browser/ash/system_extensions/BUILD.gn index 27f8bf9..5c23430 100644 --- a/chrome/browser/ash/system_extensions/BUILD.gn +++ b/chrome/browser/ash/system_extensions/BUILD.gn
@@ -23,6 +23,8 @@ "system_extensions_install_manager.cc", "system_extensions_install_manager.h", "system_extensions_install_status.h", + "system_extensions_internals_page_handler.cc", + "system_extensions_internals_page_handler.h", "system_extensions_profile_utils.cc", "system_extensions_profile_utils.h", "system_extensions_provider.cc", @@ -40,6 +42,7 @@ deps = [ ":system_extensions_group", "//ash/constants", + "//ash/webui/system_extensions_internals_ui/mojom", "//base", "//chrome/browser/chromeos", "//chrome/browser/profiles",
diff --git a/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn b/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn index 57b14a04..f2e1027e 100644 --- a/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn +++ b/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
@@ -2,6 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") + +assert(is_chromeos_ash) + source_set("window_management") { sources = [ "window_management_impl.cc", @@ -12,6 +16,7 @@ "//components/services/app_service/public/cpp:instance_update", "//components/services/app_service/public/mojom", "//third_party/blink/public/mojom:mojom_platform", + "//ui/aura", "//ui/views", "//ui/webui", ]
diff --git a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc index ff4ca8b6..395f8c201 100644 --- a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc +++ b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc
@@ -54,6 +54,15 @@ int32_t y, int32_t width, int32_t height) { + aura::Window* target = GetWindow(id); + // TODO(crbug.com/1253318): Ensure this works with multiple screens. + if (target) { + target->SetBounds(gfx::Rect(x, y, width, height)); + } +} + +aura::Window* WindowManagementImpl::GetWindow( + const base::UnguessableToken& id) { aura::Window* target = nullptr; apps::AppServiceProxy* proxy = apps::AppServiceProxyFactory::GetForProfile( Profile::FromBrowserContext(browser_context_)); @@ -63,10 +72,8 @@ target = update.Window(); } }); - // TODO(crbug.com/1253318): Ensure this works with multiple screens. - if (target) { - target->SetBounds(gfx::Rect(x, y, width, height)); - } + + return target; } } // namespace ash
diff --git a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h index 3b12a121..7e866c43 100644 --- a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h +++ b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h
@@ -7,6 +7,7 @@ #include "base/unguessable_token.h" #include "third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom.h" +#include "ui/aura/window.h" namespace content { class BrowserContext; @@ -28,6 +29,8 @@ int32_t height) override; private: + aura::Window* GetWindow(const base::UnguessableToken& id); + content::BrowserContext* browser_context_; };
diff --git a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc new file mode 100644 index 0000000..a704db82 --- /dev/null +++ b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc
@@ -0,0 +1,45 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h" + +#include "base/debug/stack_trace.h" + +SystemExtensionsInternalsPageHandler::SystemExtensionsInternalsPageHandler( + Profile* profile) + : profile_(profile) {} + +SystemExtensionsInternalsPageHandler::~SystemExtensionsInternalsPageHandler() = + default; + +void SystemExtensionsInternalsPageHandler:: + InstallSystemExtensionFromDownloadsDir( + const base::SafeBaseName& system_extension_dir_name, + InstallSystemExtensionFromDownloadsDirCallback callback) { + base::FilePath downloads_path; + if (!base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) { + std::move(callback).Run(false); + return; + } + + auto& install_manager = + SystemExtensionsProvider::Get(profile_)->install_manager(); + base::FilePath system_extension_dir = + downloads_path.Append(system_extension_dir_name); + + install_manager.InstallUnpackedExtensionFromDir( + system_extension_dir, + base::BindOnce(&SystemExtensionsInternalsPageHandler::OnInstallFinished, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void SystemExtensionsInternalsPageHandler::OnInstallFinished( + InstallSystemExtensionFromDownloadsDirCallback callback, + InstallStatusOrSystemExtensionId result) { + if (!result.ok()) { + LOG(ERROR) << "failed with: " << static_cast<int32_t>(result.status()); + } + + std::move(callback).Run(result.ok()); +}
diff --git a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h new file mode 100644 index 0000000..b286c5b --- /dev/null +++ b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h
@@ -0,0 +1,40 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_INTERNALS_PAGE_HANDLER_H_ +#define CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_INTERNALS_PAGE_HANDLER_H_ + +#include "ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom.h" +#include "base/files/file_path.h" +#include "base/files/safe_base_name.h" +#include "base/memory/weak_ptr.h" +#include "base/path_service.h" +#include "chrome/browser/ash/system_extensions/system_extensions_install_status.h" +#include "chrome/browser/ash/system_extensions/system_extensions_provider.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_paths.h" + +class SystemExtensionsInternalsPageHandler + : public ash::mojom::system_extensions_internals::PageHandler { + public: + explicit SystemExtensionsInternalsPageHandler(Profile* profile); + ~SystemExtensionsInternalsPageHandler() override; + + // mojom::system_extensions_internals::PageHandler + void InstallSystemExtensionFromDownloadsDir( + const base::SafeBaseName& system_extension_dir_name, + InstallSystemExtensionFromDownloadsDirCallback callback) override; + + private: + void OnInstallFinished( + InstallSystemExtensionFromDownloadsDirCallback callback, + InstallStatusOrSystemExtensionId result); + + raw_ptr<Profile> profile_; + + base::WeakPtrFactory<SystemExtensionsInternalsPageHandler> weak_ptr_factory_{ + this}; +}; + +#endif // CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_INTERNALS_PAGE_HANDLER_H_
diff --git a/chrome/browser/ash/web_applications/eche_app_info.cc b/chrome/browser/ash/web_applications/eche_app_info.cc index fe742456..c6e01aee 100644 --- a/chrome/browser/ash/web_applications/eche_app_info.cc +++ b/chrome/browser/ash/web_applications/eche_app_info.cc
@@ -93,7 +93,8 @@ // than half of the windows. gfx::Rect bounds = display::Screen::GetScreen()->GetDisplayForNewWindows().work_area(); - const float bounds_aspect_ratio = bounds.width() / bounds.height(); + const float bounds_aspect_ratio = + static_cast<float>(bounds.width()) / bounds.height(); const bool is_landscape = (bounds_aspect_ratio >= 1); auto new_width = is_landscape ? (bounds.height() / 2) : bounds.width() / 2; if (kMinimumEcheSize.width() > new_width) {
diff --git a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc index 0b470e92..f519acf 100644 --- a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc +++ b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
@@ -57,11 +57,11 @@ gfx::Rect work_area = display::Screen::GetScreen()->GetDisplayForNewWindows().work_area(); int expected_width = work_area.height() / 2; - int expected_hight = work_area.height() / 2 * aspect_ratio; + int expected_height = work_area.height() * aspect_ratio / 2; int x = (work_area.width() - expected_width) / 2; - int y = (work_area.height() - expected_hight) / 2; + int y = (work_area.height() - expected_height) / 2; EXPECT_EQ(browser->window()->GetBounds(), - gfx::Rect(x, y, expected_width, expected_hight)); + gfx::Rect(x, y, expected_width, expected_height)); } IN_PROC_BROWSER_TEST_P(EcheAppIntegrationTest, @@ -78,11 +78,11 @@ gfx::Rect work_area = display::Screen::GetScreen()->GetDisplayForNewWindows().work_area(); int expected_width = work_area.width() / 2; - int expected_hight = work_area.width() / 2 * aspect_ratio; + int expected_height = work_area.width() * aspect_ratio / 2; int x = (work_area.width() - expected_width) / 2; - int y = (work_area.height() - expected_hight) / 2; + int y = (work_area.height() - expected_height) / 2; EXPECT_EQ(browser->window()->GetBounds(), - gfx::Rect(x, y, expected_width, expected_hight)); + gfx::Rect(x, y, expected_width, expected_height)); } IN_PROC_BROWSER_TEST_P(EcheAppIntegrationTest,
diff --git a/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc index 7e152ff..e20a77e 100644 --- a/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc +++ b/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc
@@ -94,11 +94,11 @@ display::Screen::GetScreen()->GetDisplayForNewWindows().work_area(); int expected_width = 600; - int expected_hight = 640; + int expected_height = 640; int x = (work_area.width() - expected_width) / 2; - int y = (work_area.height() - expected_hight) / 2; + int y = (work_area.height() - expected_height) / 2; EXPECT_EQ(browser->window()->GetBounds(), - gfx::Rect(x, y, expected_width, expected_hight)); + gfx::Rect(x, y, expected_width, expected_height)); } // Test that the Feedback App
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index 315507e..10db96e 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -74,6 +74,7 @@ #include "content/public/common/url_constants.h" #include "extensions/buildflags/buildflags.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/image_annotation/public/mojom/image_annotation.mojom.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/credentialmanager/credential_manager.mojom.h" @@ -219,7 +220,10 @@ #include "ash/webui/scanning/mojom/scanning.mojom.h" #include "ash/webui/scanning/scanning_ui.h" #include "ash/webui/shimless_rma/shimless_rma.h" +#include "ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom.h" +#include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h" #include "chrome/browser/apps/digital_goods/digital_goods_factory_impl.h" +#include "chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h" #include "chrome/browser/nearby_sharing/common/nearby_share_features.h" #include "chrome/browser/speech/cros_speech_recognition_service_factory.h" #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom.h" @@ -304,6 +308,16 @@ #include "components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.h" #endif +#if BUILDFLAG(IS_CHROMEOS_ASH) +void ash::SystemExtensionsInternalsUI::BindInterface( + mojo::PendingReceiver<ash::mojom::system_extensions_internals::PageHandler> + receiver) { + auto page_handler = std::make_unique<SystemExtensionsInternalsPageHandler>( + Profile::FromWebUI(web_ui())); + mojo::MakeSelfOwnedReceiver(std::move(page_handler), std::move(receiver)); +} +#endif + namespace chrome { namespace internal { @@ -977,6 +991,10 @@ map); RegisterWebUIControllerInterfaceBinder< + ash::eche_app::mojom::DisplayStreamHandler, ash::eche_app::EcheAppUI>( + map); + + RegisterWebUIControllerInterfaceBinder< ash::media_app_ui::mojom::PageHandlerFactory, ash::MediaAppUI>(map); RegisterWebUIControllerInterfaceBinder< @@ -1139,6 +1157,13 @@ .Add<ash::mojom::sample_swa::PageHandlerFactory>(); #endif // BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD) +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (base::FeatureList::IsEnabled(ash::features::kSystemExtensions)) { + registry.ForWebUI<ash::SystemExtensionsInternalsUI>() + .Add<ash::mojom::system_extensions_internals::PageHandler>(); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + // --- Section 2: chrome-untrusted:// WebUIs: #if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc index ba97b2d..6a16b9c8 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -453,69 +453,25 @@ ExtensionFunction::ResponseAction WebNavigationGetFrameFunction::Run() { std::unique_ptr<GetFrame::Params> params(GetFrame::Params::Create(args())); EXTENSION_FUNCTION_VALIDATE(params.get()); + int tab_id = params->details.tab_id; + int frame_id = params->details.frame_id; - int tab_id = api::tabs::TAB_ID_NONE; - int frame_id = -1; - - content::RenderFrameHost* render_frame_host = nullptr; - if (params->details.document_id) { - ExtensionApiFrameIdMap::DocumentId document_id = - ExtensionApiFrameIdMap::DocumentIdFromString( - *params->details.document_id); - if (!document_id) - return RespondNow(Error("Invalid documentId.")); - - // Note that we will globally find a RenderFrameHost but validate that - // we are in the right context still as we may be in the wrong profile - // or in incognito mode. - render_frame_host = - ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId( - document_id); - - if (!render_frame_host) - return RespondNow(OneArgument(base::Value())); - - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(render_frame_host); - // We found the RenderFrameHost through a generic lookup so we must test to - // see if the WebContents is actually in our BrowserContext. - if (!ExtensionTabUtil::IsWebContentsInContext( - web_contents, browser_context(), include_incognito_information())) { - return RespondNow(OneArgument(base::Value())); - } - - tab_id = ExtensionTabUtil::GetTabId(web_contents); - frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host); - - // If the provided tab_id and frame_id do not match the calculated ones - // return. - if ((params->details.tab_id && *params->details.tab_id != tab_id) || - (params->details.frame_id && *params->details.frame_id != frame_id)) { - return RespondNow(OneArgument(base::Value())); - } - } else { - // If documentId is not provided, tab_id and frame_id must be. Return early - // if not. - if (!params->details.tab_id || !params->details.frame_id) { - return RespondNow(Error( - "Either documentId or both tabId and frameId must be specified.")); - } - - tab_id = *params->details.tab_id; - frame_id = *params->details.frame_id; - - content::WebContents* web_contents = nullptr; - if (!ExtensionTabUtil::GetTabById(tab_id, browser_context(), - include_incognito_information(), - &web_contents) || - !web_contents) { - return RespondNow(OneArgument(base::Value())); - } - - render_frame_host = ExtensionApiFrameIdMap::Get()->GetRenderFrameHostById( - web_contents, frame_id); + content::WebContents* web_contents; + if (!ExtensionTabUtil::GetTabById(tab_id, browser_context(), + include_incognito_information(), + &web_contents) || + !web_contents) { + return RespondNow(OneArgument(base::Value())); } + WebNavigationTabObserver* observer = + WebNavigationTabObserver::Get(web_contents); + DCHECK(observer); + + content::RenderFrameHost* render_frame_host = + ExtensionApiFrameIdMap::Get()->GetRenderFrameHostById(web_contents, + frame_id); + auto* frame_navigation_state = render_frame_host ? FrameNavigationState::GetForCurrentDocument(render_frame_host) @@ -555,7 +511,7 @@ EXTENSION_FUNCTION_VALIDATE(params.get()); int tab_id = params->details.tab_id; - content::WebContents* web_contents = nullptr; + content::WebContents* web_contents; if (!ExtensionTabUtil::GetTabById(tab_id, browser_context(), include_incognito_information(), &web_contents) || @@ -563,6 +519,10 @@ return RespondNow(OneArgument(base::Value())); } + WebNavigationTabObserver* observer = + WebNavigationTabObserver::Get(web_contents); + DCHECK(observer); + std::vector<GetAllFrames::Results::DetailsType> result_list; // We only iterate the frames in the active page. We currently do not
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index 253ea51..e81672c 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -769,24 +769,6 @@ return active_contents; } -// static -bool ExtensionTabUtil::IsWebContentsInContext( - content::WebContents* web_contents, - content::BrowserContext* browser_context, - bool include_incognito) { - // Look at the WebContents BrowserContext and see if it is the same. - content::BrowserContext* web_contents_browser_context = - web_contents->GetBrowserContext(); - if (web_contents_browser_context == browser_context) - return true; - - // If not it might be to include the incongito mode, so we if the profiles - // are the same or the parent. - return include_incognito && Profile::FromBrowserContext(browser_context) - ->IsSameOrParent(Profile::FromBrowserContext( - web_contents_browser_context)); -} - GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string, const Extension* extension) { GURL url = GURL(url_string);
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h index a4faba975..0b196e5 100644 --- a/chrome/browser/extensions/extension_tab_util.h +++ b/chrome/browser/extensions/extension_tab_util.h
@@ -202,12 +202,6 @@ content::BrowserContext* browser_context, bool include_incognito); - // Determines if the |web_contents| is in |browser_context| or it's OTR - // BrowserContext if |include_incognito| is true. - static bool IsWebContentsInContext(content::WebContents* web_contents, - content::BrowserContext* browser_context, - bool include_incognito); - // Takes |url_string| and returns a GURL which is either valid and absolute // or invalid. If |url_string| is not directly interpretable as a valid (it is // likely a relative URL) an attempt is made to resolve it. When |extension|
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 3d94c19..381f396a 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1072,7 +1072,7 @@ { "name": "dark-light-mode", "owners": [ "minch", "tclaiborne" ], - "expiry_milestone": 100 + "expiry_milestone": 110 }, { "name": "darken-websites-checkbox-in-themes-setting",
diff --git a/chrome/browser/notifications/notification_platform_bridge_lacros.cc b/chrome/browser/notifications/notification_platform_bridge_lacros.cc index 854ca97..e6e03edb 100644 --- a/chrome/browser/notifications/notification_platform_bridge_lacros.cc +++ b/chrome/browser/notifications/notification_platform_bridge_lacros.cc
@@ -90,6 +90,7 @@ mojo_note->accessible_name = notification.accessible_name(); mojo_note->fullscreen_visibility = ToMojo(notification.fullscreen_visibility()); + mojo_note->accent_color = notification.accent_color(); return mojo_note; }
diff --git a/chrome/browser/policy/test/promotional_tabs_enabled_policy_browsertest.cc b/chrome/browser/policy/test/promotional_tabs_enabled_policy_browsertest.cc index 35146a2..366471c9 100644 --- a/chrome/browser/policy/test/promotional_tabs_enabled_policy_browsertest.cc +++ b/chrome/browser/policy/test/promotional_tabs_enabled_policy_browsertest.cc
@@ -170,7 +170,7 @@ void SetUpCommandLine(base::CommandLine* command_line) override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); command_line->RemoveSwitch(switches::kForceFirstRun); - command_line->AppendSwitch(switches::kNoFirstRun); + command_line->AppendSwitch(switches::kForceWhatsNew); command_line->AppendSwitchPath(switches::kUserDataDir, temp_dir_.GetPath()); // Suppress the welcome page by setting the pref indicating that it has
diff --git a/chrome/browser/query_tiles/query_tile_utils.cc b/chrome/browser/query_tiles/query_tile_utils.cc index dc72e63..89b2f31 100644 --- a/chrome/browser/query_tiles/query_tile_utils.cc +++ b/chrome/browser/query_tiles/query_tile_utils.cc
@@ -43,8 +43,10 @@ } bool IsQueryTilesEnabled() { - return query_tiles::features::IsQueryTilesEnabledForCountry( - GetCountryCode()) || + return (!base::FeatureList::IsEnabled( + query_tiles::features::kQueryTilesDisableCountryOverride) && + query_tiles::features::IsQueryTilesEnabledForCountry( + GetCountryCode())) || (base::FeatureList::IsEnabled(query_tiles::features::kQueryTiles) && base::FeatureList::IsEnabled( query_tiles::features::kQueryTilesInNTP));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn index a0cea01..b4fb442a 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -39,7 +39,7 @@ "background/chromevox_state.js", "background/classic_background.js", "background/color.js", - "background/command_handler.js", + "background/command_handler_interface.js", "background/custom_automation_event.js", "background/desktop_automation_handler.js", "background/editing/editable_line.js", @@ -119,6 +119,7 @@ chromevox_es6_modules = [ "../common/instance_checker.js", "background/background.js", + "background/command_handler.js", "background/download_handler.js", "background/earcon_engine.js", "background/earcons.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js index 53926b1..f7f2051 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js
@@ -186,7 +186,8 @@ nextRange = cursors.Range.fromNode(scrollable).sync(unit, dir); if (unit === cursors.Unit.NODE) { nextRange = - CommandHandler.skipLabelOrDescriptionFor(nextRange, dir); + CommandHandlerInterface.instance.skipLabelOrDescriptionFor( + nextRange, dir); } } else if (pred) { let node;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js index ef0aa8b0..303de378 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -3,6 +3,8 @@ // found in the LICENSE file. import {InstanceChecker} from '../../common/instance_checker.js'; + +import {CommandHandler} from './command_handler.js'; import {DownloadHandler} from './download_handler.js'; import {Earcons} from './earcons.js'; import {FindHandler} from './find_handler.js'; @@ -102,7 +104,6 @@ /** @private {!PageLoadSoundHandler} */ this.pageLoadSoundHandler_ = new PageLoadSoundHandler(); - CommandHandler.init(); FindHandler.init(); DownloadHandler.init(); JaPhoneticData.init(JaPhoneticMap.MAP); @@ -142,9 +143,11 @@ } /** + * @param {cursors.Range} newRange The new range. + * @param {boolean=} opt_fromEditing * @override */ - setCurrentRange(newRange) { + setCurrentRange(newRange, opt_fromEditing) { // Clear anything that was frozen on the braille display whenever // the user navigates. ChromeVox.braille.thaw(); @@ -158,8 +161,9 @@ this.previousRange_ = this.currentRange_; this.currentRange_ = newRange; + ChromeVoxState.observers.forEach(function(observer) { - observer.onCurrentRangeChanged(newRange); + observer.onCurrentRangeChanged(newRange, opt_fromEditing); }); if (!this.currentRange_) { @@ -318,7 +322,7 @@ const isClassicEnabled = false; port.postMessage({target: 'next', isClassicEnabled}); } else if (action === 'onCommand') { - CommandHandler.onCommand(msg['command']); + CommandHandlerInterface.instance.onCommand(msg['command']); } else if (action === 'flushNextUtterance') { Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js index 388a23b..8a294528 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -1046,7 +1046,7 @@ // Fakes a toggleSelection command. root.addEventListener('textSelectionChanged', function() { if (root.focusOffset === 3) { - CommandHandler.onCommand('toggleSelection'); + CommandHandlerInterface.instance.onCommand('toggleSelection'); } }, true);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js index 372f3c0..d48a779 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
@@ -45,22 +45,22 @@ Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH); switch (evt.command) { case BrailleKeyCommand.PAN_LEFT: - CommandHandler.onCommand('previousObject'); + CommandHandlerInterface.instance.onCommand('previousObject'); break; case BrailleKeyCommand.PAN_RIGHT: - CommandHandler.onCommand('nextObject'); + CommandHandlerInterface.instance.onCommand('nextObject'); break; case BrailleKeyCommand.LINE_UP: - CommandHandler.onCommand('previousLine'); + CommandHandlerInterface.instance.onCommand('previousLine'); break; case BrailleKeyCommand.LINE_DOWN: - CommandHandler.onCommand('nextLine'); + CommandHandlerInterface.instance.onCommand('nextLine'); break; case BrailleKeyCommand.TOP: - CommandHandler.onCommand('jumpToTop'); + CommandHandlerInterface.instance.onCommand('jumpToTop'); break; case BrailleKeyCommand.BOTTOM: - CommandHandler.onCommand('jumpToBottom'); + CommandHandlerInterface.instance.onCommand('jumpToBottom'); break; case BrailleKeyCommand.ROUTING: BrailleCommandHandler.onRoutingCommand_( @@ -76,7 +76,7 @@ const command = BrailleCommandData.getCommand(evt.brailleDots); if (command) { if (BrailleCommandHandler.onEditCommand_(command)) { - CommandHandler.onCommand(command); + CommandHandlerInterface.instance.onCommand(command); } } break;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js index bdc5fd0..52f4cce 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -21,13 +21,12 @@ * changes. * @interface */ -ChromeVoxStateObserver = function() {}; - -ChromeVoxStateObserver.prototype = { +ChromeVoxStateObserver = class { /** * @param {cursors.Range} range The new range. + * @param {boolean=} opt_fromEditing */ - onCurrentRangeChanged(range) {} + onCurrentRangeChanged(range, opt_fromEditing) {} }; /** @@ -97,6 +96,7 @@ /** * @param {cursors.Range} newRange The new range. + * @param {boolean=} opt_fromEditing */ setCurrentRange: goog.abstractMethod, /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js index 93c2584..97099e5 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -6,25 +6,7 @@ * @fileoverview ChromeVox commands. */ -goog.provide('CommandHandler'); -goog.require('AutoScrollHandler'); -goog.require('ChromeVoxState'); -goog.require('Color'); -goog.require('CustomAutomationEvent'); -goog.require('EventGenerator'); -goog.require('KeyCode'); -goog.require('LogStore'); -goog.require('Output'); -goog.require('PhoneticData'); -goog.require('SmartStickyMode'); -goog.require('TreeDumper'); -goog.require('ChromeVoxBackground'); -goog.require('ChromeVoxKbHandler'); -goog.require('ChromeVoxPrefs'); -goog.require('CommandStore'); - -goog.scope(function() { const ActionType = chrome.automation.ActionType; const AutomationEvent = chrome.automation.AutomationEvent; const AutomationNode = chrome.automation.AutomationNode; @@ -33,1212 +15,1188 @@ const RoleType = chrome.automation.RoleType; const StateType = chrome.automation.StateType; -/** @private {boolean} */ -CommandHandler.isIncognito_ = !!chrome.runtime.getManifest()['incognito']; +export class CommandHandler extends CommandHandlerInterface { + /** @private */ + constructor() { + super(); -/** @private {boolean} */ -CommandHandler.languageLoggingEnabled_ = false; + /** @private {boolean} */ + this.isIncognito_ = !!chrome.runtime.getManifest()['incognito']; -/** - * Handles toggling sticky mode when encountering editables. - * @private {!SmartStickyMode} - */ -CommandHandler.smartStickyMode_ = new SmartStickyMode(); + /** @private {boolean} */ + this.languageLoggingEnabled_ = false; -/** - * Handles ChromeVox commands. - * @param {string} command - * @return {boolean} True if the command should propagate. - */ -CommandHandler.onCommand = function(command) { - // Check for a command denied in incognito contexts and kiosk. - if ((CommandHandler.isIncognito_ || CommandHandler.isKioskSession_) && - CommandStore.CMD_ALLOWLIST[command] && - CommandStore.CMD_ALLOWLIST[command].denyOOBE) { - return true; + /** + * Handles toggling sticky mode when encountering editables. + * @private {!SmartStickyMode} + */ + this.smartStickyMode_ = new SmartStickyMode(); + + /** + * To support viewGraphicAsBraille_(), the current image node. + * @type {AutomationNode?}; + */ + this.imageNode_; + + /** @private {boolean} */ + this.isKioskSession_ = false; + + this.init(); } - // Check for loss of focus which results in us invalidating our current - // range. Note this call is synchronous. - chrome.automation.getFocus(function(focusedNode) { - const cur = ChromeVoxState.instance.currentRange; - if (cur && !cur.isValid()) { - ChromeVoxState.instance.setCurrentRange( - cursors.Range.fromNode(focusedNode)); - } - - if (!focusedNode || - - // This case detects when TalkBack (in ARC++) is enabled (which also - // covers when the ARC++ window is active). Clear the ChromeVox range so - // keys get passed through for ChromeVox commands. - (ChromeVoxState.instance.talkBackEnabled && - - // This additional check is not strictly necessary, but we use it to - // ensure we are never inadvertently losing focus. ARC++ windows set - // "focus" on a root view. - focusedNode.role === RoleType.CLIENT)) { - ChromeVoxState.instance.setCurrentRange(null); - } - }); - - // These commands don't require a current range. - switch (command) { - case 'speakTimeAndDate': - chrome.automation.getDesktop(function(d) { - // First, try speaking the on-screen time. - const allTime = d.findAll({role: RoleType.TIME}); - allTime.filter(function(t) { - return t.root.role === RoleType.DESKTOP; - }); - - let timeString = ''; - allTime.forEach(function(t) { - if (t.name) { - timeString = t.name; - } - }); - if (timeString) { - ChromeVox.tts.speak(timeString, QueueMode.FLUSH); - ChromeVox.braille.write(NavBraille.fromText(timeString)); - } else { - // Fallback to the old way of speaking time. - const output = new Output(); - const dateTime = new Date(); - output - .withString( - dateTime.toLocaleTimeString() + ', ' + - dateTime.toLocaleDateString()) - .go(); - } - }); - return false; - case 'showOptionsPage': - chrome.runtime.openOptionsPage(); - break; - case 'toggleStickyMode': - ChromeVoxBackground.setPref('sticky', !ChromeVox.isStickyPrefOn, true); - CommandHandler.smartStickyMode_.onStickyModeCommand( - ChromeVoxState.instance.currentRange); - return false; - case 'passThroughMode': - ChromeVox.passThroughMode = true; - ChromeVox.tts.speak(Msgs.getMsg('pass_through_key'), QueueMode.QUEUE); + /** @override */ + onCommand(command) { + // Check for a command denied in incognito contexts and kiosk. + if ((this.isIncognito_ || this.isKioskSession_) && + CommandStore.CMD_ALLOWLIST[command] && + CommandStore.CMD_ALLOWLIST[command].denyOOBE) { return true; - case 'showKbExplorerPage': - const explorerPage = { - url: 'chromevox/learn_mode/kbexplorer.html', - type: 'panel' - }; - chrome.windows.create(explorerPage); - break; - case 'showLogPage': - const logPage = {url: 'chromevox/background/logging/log.html'}; - chrome.tabs.create(logPage); - break; - case 'enableLogging': { - for (const type in ChromeVoxPrefs.loggingPrefs) { - ChromeVoxPrefs.instance.setLoggingPrefs( - ChromeVoxPrefs.loggingPrefs[type], true); - } - } break; - case 'disableLogging': { - for (const type in ChromeVoxPrefs.loggingPrefs) { - ChromeVoxPrefs.instance.setLoggingPrefs( - ChromeVoxPrefs.loggingPrefs[type], false); - } - } break; - case 'dumpTree': - chrome.automation.getDesktop(function(root) { - LogStore.getInstance().writeTreeLog(new TreeDumper(root)); - }); - break; - case 'decreaseTtsRate': - CommandHandler.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, false); - return false; - case 'increaseTtsRate': - CommandHandler.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, true); - return false; - case 'decreaseTtsPitch': - CommandHandler.increaseOrDecreaseSpeechProperty_( - AbstractTts.PITCH, false); - return false; - case 'increaseTtsPitch': - CommandHandler.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, true); - return false; - case 'decreaseTtsVolume': - CommandHandler.increaseOrDecreaseSpeechProperty_( - AbstractTts.VOLUME, false); - return false; - case 'increaseTtsVolume': - CommandHandler.increaseOrDecreaseSpeechProperty_( - AbstractTts.VOLUME, true); - return false; - case 'stopSpeech': - ChromeVox.tts.stop(); - ChromeVoxState.isReadingContinuously = false; - return false; - case 'toggleEarcons': { - ChromeVox.earcons.enabled = !ChromeVox.earcons.enabled; - const announce = ChromeVox.earcons.enabled ? Msgs.getMsg('earcons_on') : - Msgs.getMsg('earcons_off'); - ChromeVox.tts.speak( - announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); } - return false; - case 'cycleTypingEcho': { - ChromeVox.typingEcho = TypingEcho.cycle(ChromeVox.typingEcho); - let announce = ''; - switch (ChromeVox.typingEcho) { - case TypingEcho.CHARACTER: - announce = Msgs.getMsg('character_echo'); - break; - case TypingEcho.WORD: - announce = Msgs.getMsg('word_echo'); - break; - case TypingEcho.CHARACTER_AND_WORD: - announce = Msgs.getMsg('character_and_word_echo'); - break; - case TypingEcho.NONE: - announce = Msgs.getMsg('none_echo'); - break; - } - ChromeVox.tts.speak( - announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); - } - return false; - case 'cyclePunctuationEcho': - ChromeVox.tts.speak( - Msgs.getMsg(ChromeVoxState.backgroundTts.cyclePunctuationEcho()), - QueueMode.FLUSH); - return false; - case 'reportIssue': - let url = 'https://code.google.com/p/chromium/issues/entry?' + - 'labels=Type-Bug,Pri-2,OS-Chrome&' + - 'components=OS>Accessibility>ChromeVox&' + - 'description='; - const description = {}; - description['Version'] = chrome.runtime.getManifest().version; - description['Reproduction Steps'] = '%0a1.%0a2.%0a3.'; - for (const key in description) { - url += key + ':%20' + description[key] + '%0a'; - } - chrome.tabs.create({url}); - return false; - case 'toggleBrailleCaptions': - BrailleCaptionsBackground.setActive( - !BrailleCaptionsBackground.isEnabled()); - return false; - case 'toggleBrailleTable': { - let brailleTableType = localStorage['brailleTableType']; - let output = ''; - if (brailleTableType === 'brailleTable6') { - brailleTableType = 'brailleTable8'; - - // This label reads "switch to 8 dot braille". - output = '@OPTIONS_BRAILLE_TABLE_TYPE_6'; - } else { - brailleTableType = 'brailleTable6'; - - // This label reads "switch to 6 dot braille". - output = '@OPTIONS_BRAILLE_TABLE_TYPE_8'; + // Check for loss of focus which results in us invalidating our current + // range. Note this call is synchronous. + chrome.automation.getFocus(function(focusedNode) { + const cur = ChromeVoxState.instance.currentRange; + if (cur && !cur.isValid()) { + ChromeVoxState.instance.setCurrentRange( + cursors.Range.fromNode(focusedNode)); } - localStorage['brailleTable'] = localStorage[brailleTableType]; - localStorage['brailleTableType'] = brailleTableType; - BrailleBackground.getInstance().getTranslatorManager().refresh( - localStorage[brailleTableType]); - new Output().format(output).go(); - } - return false; - case 'help': - (new PanelCommand(PanelCommandType.TUTORIAL)).send(); - return false; - case 'toggleScreen': - const oldState = sessionStorage.getItem('darkScreen'); - const newState = (oldState === 'true') ? false : true; - if (newState && localStorage['acceptToggleScreen'] !== 'true') { - // If this is the first time, show a confirmation dialog. - chrome.accessibilityPrivate.showConfirmationDialog( - Msgs.getMsg('toggle_screen_title'), - Msgs.getMsg('toggle_screen_description'), (confirmed) => { - if (confirmed) { - sessionStorage.setItem('darkScreen', 'true'); - localStorage['acceptToggleScreen'] = true; - chrome.accessibilityPrivate.darkenScreen(true); - new Output().format('@toggle_screen_off').go(); - } - }); - } else { - sessionStorage.setItem('darkScreen', (newState) ? 'true' : 'false'); - chrome.accessibilityPrivate.darkenScreen(newState); - new Output() - .format((newState) ? '@toggle_screen_off' : '@toggle_screen_on') - .go(); + if (!focusedNode || + + // This case detects when TalkBack (in ARC++) is enabled (which also + // covers when the ARC++ window is active). Clear the ChromeVox range + // so keys get passed through for ChromeVox commands. + (ChromeVoxState.instance.talkBackEnabled && + + // This additional check is not strictly necessary, but we use it to + // ensure we are never inadvertently losing focus. ARC++ windows set + // "focus" on a root view. + focusedNode.role === RoleType.CLIENT)) { + ChromeVoxState.instance.setCurrentRange(null); } - return false; - case 'toggleSpeechOnOrOff': - const state = ChromeVox.tts.toggleSpeechOnOrOff(); - new Output().format(state ? '@speech_on' : '@speech_off').go(); - return false; - case 'enableChromeVoxArcSupportForCurrentApp': - chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp( - true, (response) => {}); - break; - case 'disableChromeVoxArcSupportForCurrentApp': - chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp( - false, (response) => { - if (response === - chrome.accessibilityPrivate.SetNativeChromeVoxResponse - .TALKBACK_NOT_INSTALLED) { - ChromeVox.braille.write(NavBraille.fromText( - Msgs.getMsg('announce_install_talkback'))); - ChromeVox.tts.speak( - Msgs.getMsg('announce_install_talkback'), QueueMode.FLUSH); + }); + + // These commands don't require a current range. + switch (command) { + case 'speakTimeAndDate': + chrome.automation.getDesktop(function(d) { + // First, try speaking the on-screen time. + const allTime = d.findAll({role: RoleType.TIME}); + allTime.filter(function(t) { + return t.root.role === RoleType.DESKTOP; + }); + + let timeString = ''; + allTime.forEach(function(t) { + if (t.name) { + timeString = t.name; } }); - break; - case 'showTtsSettings': - chrome.accessibilityPrivate.openSettingsSubpage( - 'manageAccessibility/tts'); - break; - default: - break; - case 'toggleKeyboardHelp': - (new PanelCommand(PanelCommandType.OPEN_MENUS)).send(); - return false; - case 'showPanelMenuMostRecent': - (new PanelCommand(PanelCommandType.OPEN_MENUS_MOST_RECENT)).send(); - return false; - case 'nextGranularity': - case 'previousGranularity': { - const backwards = command === 'previousGranularity'; - let gran = GestureCommandHandler.granularity; - const next = backwards ? - (--gran >= 0 ? gran : GestureGranularity.COUNT - 1) : - ++gran % GestureGranularity.COUNT; - GestureCommandHandler.granularity = - /** @type {GestureGranularity} */ (next); - - let announce = ''; - switch (GestureCommandHandler.granularity) { - case GestureGranularity.CHARACTER: - announce = Msgs.getMsg('character_granularity'); - break; - case GestureGranularity.WORD: - announce = Msgs.getMsg('word_granularity'); - break; - case GestureGranularity.LINE: - announce = Msgs.getMsg('line_granularity'); - break; - case GestureGranularity.HEADING: - announce = Msgs.getMsg('heading_granularity'); - break; - case GestureGranularity.LINK: - announce = Msgs.getMsg('link_granularity'); - break; - case GestureGranularity.FORM_FIELD_CONTROL: - announce = Msgs.getMsg('form_field_control_granularity'); - break; + if (timeString) { + ChromeVox.tts.speak(timeString, QueueMode.FLUSH); + ChromeVox.braille.write(NavBraille.fromText(timeString)); + } else { + // Fallback to the old way of speaking time. + const output = new Output(); + const dateTime = new Date(); + output + .withString( + dateTime.toLocaleTimeString() + ', ' + + dateTime.toLocaleDateString()) + .go(); + } + }); + return false; + case 'showOptionsPage': + chrome.runtime.openOptionsPage(); + break; + case 'toggleStickyMode': + ChromeVoxBackground.setPref('sticky', !ChromeVox.isStickyPrefOn, true); + this.smartStickyMode_.onStickyModeCommand( + ChromeVoxState.instance.currentRange); + return false; + case 'passThroughMode': + ChromeVox.passThroughMode = true; + ChromeVox.tts.speak(Msgs.getMsg('pass_through_key'), QueueMode.QUEUE); + return true; + case 'showKbExplorerPage': + const explorerPage = { + url: 'chromevox/learn_mode/kbexplorer.html', + type: 'panel' + }; + chrome.windows.create(explorerPage); + break; + case 'showLogPage': + const logPage = {url: 'chromevox/background/logging/log.html'}; + chrome.tabs.create(logPage); + break; + case 'enableLogging': { + for (const type in ChromeVoxPrefs.loggingPrefs) { + ChromeVoxPrefs.instance.setLoggingPrefs( + ChromeVoxPrefs.loggingPrefs[type], true); + } + } break; + case 'disableLogging': { + for (const type in ChromeVoxPrefs.loggingPrefs) { + ChromeVoxPrefs.instance.setLoggingPrefs( + ChromeVoxPrefs.loggingPrefs[type], false); + } + } break; + case 'dumpTree': + chrome.automation.getDesktop(function(root) { + LogStore.getInstance().writeTreeLog(new TreeDumper(root)); + }); + break; + case 'decreaseTtsRate': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, false); + return false; + case 'increaseTtsRate': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, true); + return false; + case 'decreaseTtsPitch': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, false); + return false; + case 'increaseTtsPitch': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, true); + return false; + case 'decreaseTtsVolume': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, false); + return false; + case 'increaseTtsVolume': + this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, true); + return false; + case 'stopSpeech': + ChromeVox.tts.stop(); + ChromeVoxState.isReadingContinuously = false; + return false; + case 'toggleEarcons': { + ChromeVox.earcons.enabled = !ChromeVox.earcons.enabled; + const announce = ChromeVox.earcons.enabled ? Msgs.getMsg('earcons_on') : + Msgs.getMsg('earcons_off'); + ChromeVox.tts.speak( + announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); } - ChromeVox.tts.speak(announce, QueueMode.FLUSH); + return false; + case 'cycleTypingEcho': { + ChromeVox.typingEcho = TypingEcho.cycle(ChromeVox.typingEcho); + let announce = ''; + switch (ChromeVox.typingEcho) { + case TypingEcho.CHARACTER: + announce = Msgs.getMsg('character_echo'); + break; + case TypingEcho.WORD: + announce = Msgs.getMsg('word_echo'); + break; + case TypingEcho.CHARACTER_AND_WORD: + announce = Msgs.getMsg('character_and_word_echo'); + break; + case TypingEcho.NONE: + announce = Msgs.getMsg('none_echo'); + break; + } + ChromeVox.tts.speak( + announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); + } + return false; + case 'cyclePunctuationEcho': + ChromeVox.tts.speak( + Msgs.getMsg(ChromeVoxState.backgroundTts.cyclePunctuationEcho()), + QueueMode.FLUSH); + return false; + case 'reportIssue': + let url = 'https://code.google.com/p/chromium/issues/entry?' + + 'labels=Type-Bug,Pri-2,OS-Chrome&' + + 'components=OS>Accessibility>ChromeVox&' + + 'description='; + + const description = {}; + description['Version'] = chrome.runtime.getManifest().version; + description['Reproduction Steps'] = '%0a1.%0a2.%0a3.'; + for (const key in description) { + url += key + ':%20' + description[key] + '%0a'; + } + chrome.tabs.create({url}); + return false; + case 'toggleBrailleCaptions': + BrailleCaptionsBackground.setActive( + !BrailleCaptionsBackground.isEnabled()); + return false; + case 'toggleBrailleTable': { + let brailleTableType = localStorage['brailleTableType']; + let output = ''; + if (brailleTableType === 'brailleTable6') { + brailleTableType = 'brailleTable8'; + + // This label reads "switch to 8 dot braille". + output = '@OPTIONS_BRAILLE_TABLE_TYPE_6'; + } else { + brailleTableType = 'brailleTable6'; + + // This label reads "switch to 6 dot braille". + output = '@OPTIONS_BRAILLE_TABLE_TYPE_8'; + } + + localStorage['brailleTable'] = localStorage[brailleTableType]; + localStorage['brailleTableType'] = brailleTableType; + BrailleBackground.getInstance().getTranslatorManager().refresh( + localStorage[brailleTableType]); + new Output().format(output).go(); + } + return false; + case 'help': + (new PanelCommand(PanelCommandType.TUTORIAL)).send(); + return false; + case 'toggleScreen': + const oldState = sessionStorage.getItem('darkScreen'); + const newState = (oldState === 'true') ? false : true; + if (newState && localStorage['acceptToggleScreen'] !== 'true') { + // If this is the first time, show a confirmation dialog. + chrome.accessibilityPrivate.showConfirmationDialog( + Msgs.getMsg('toggle_screen_title'), + Msgs.getMsg('toggle_screen_description'), (confirmed) => { + if (confirmed) { + sessionStorage.setItem('darkScreen', 'true'); + localStorage['acceptToggleScreen'] = true; + chrome.accessibilityPrivate.darkenScreen(true); + new Output().format('@toggle_screen_off').go(); + } + }); + } else { + sessionStorage.setItem('darkScreen', (newState) ? 'true' : 'false'); + chrome.accessibilityPrivate.darkenScreen(newState); + new Output() + .format((newState) ? '@toggle_screen_off' : '@toggle_screen_on') + .go(); + } + return false; + case 'toggleSpeechOnOrOff': + const state = ChromeVox.tts.toggleSpeechOnOrOff(); + new Output().format(state ? '@speech_on' : '@speech_off').go(); + return false; + case 'enableChromeVoxArcSupportForCurrentApp': + chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp( + true, (response) => {}); + break; + case 'disableChromeVoxArcSupportForCurrentApp': + chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp( + false, (response) => { + if (response === + chrome.accessibilityPrivate.SetNativeChromeVoxResponse + .TALKBACK_NOT_INSTALLED) { + ChromeVox.braille.write(NavBraille.fromText( + Msgs.getMsg('announce_install_talkback'))); + ChromeVox.tts.speak( + Msgs.getMsg('announce_install_talkback'), QueueMode.FLUSH); + } + }); + break; + case 'showTtsSettings': + chrome.accessibilityPrivate.openSettingsSubpage( + 'manageAccessibility/tts'); + break; + default: + break; + case 'toggleKeyboardHelp': + (new PanelCommand(PanelCommandType.OPEN_MENUS)).send(); + return false; + case 'showPanelMenuMostRecent': + (new PanelCommand(PanelCommandType.OPEN_MENUS_MOST_RECENT)).send(); + return false; + case 'nextGranularity': + case 'previousGranularity': { + const backwards = command === 'previousGranularity'; + let gran = GestureCommandHandler.granularity; + const next = backwards ? + (--gran >= 0 ? gran : GestureGranularity.COUNT - 1) : + ++gran % GestureGranularity.COUNT; + GestureCommandHandler.granularity = + /** @type {GestureGranularity} */ (next); + + let announce = ''; + switch (GestureCommandHandler.granularity) { + case GestureGranularity.CHARACTER: + announce = Msgs.getMsg('character_granularity'); + break; + case GestureGranularity.WORD: + announce = Msgs.getMsg('word_granularity'); + break; + case GestureGranularity.LINE: + announce = Msgs.getMsg('line_granularity'); + break; + case GestureGranularity.HEADING: + announce = Msgs.getMsg('heading_granularity'); + break; + case GestureGranularity.LINK: + announce = Msgs.getMsg('link_granularity'); + break; + case GestureGranularity.FORM_FIELD_CONTROL: + announce = Msgs.getMsg('form_field_control_granularity'); + break; + } + ChromeVox.tts.speak(announce, QueueMode.FLUSH); + } + return false; + case 'announceBatteryDescription': + chrome.accessibilityPrivate.getBatteryDescription(function( + batteryDescription) { + new Output() + .withString(batteryDescription) + .withQueueMode(QueueMode.FLUSH) + .go(); + }); + break; + case 'resetTextToSpeechSettings': + ChromeVox.tts.resetTextToSpeechSettings(); + return false; + case 'copy': + EventGenerator.sendKeyPress(KeyCode.C, {ctrl: true}); + + // The above command doesn't trigger document clipboard events, so we + // need to set this manually. + ChromeVoxState.instance.readNextClipboardDataChange(); + return false; + case 'toggleDictation': + EventGenerator.sendKeyPress(KeyCode.D, {search: true}); + return false; } - return false; - case 'announceBatteryDescription': - chrome.accessibilityPrivate.getBatteryDescription(function( - batteryDescription) { + + // Require a current range. + if (!ChromeVoxState.instance.currentRange_) { + if (!ChromeVoxState.instance.talkBackEnabled) { new Output() - .withString(batteryDescription) + .withString(Msgs.getMsg( + EventSourceState.get() === EventSourceType.TOUCH_GESTURE ? + 'no_focus_touch' : + 'no_focus')) .withQueueMode(QueueMode.FLUSH) .go(); - }); - break; - case 'resetTextToSpeechSettings': - ChromeVox.tts.resetTextToSpeechSettings(); - return false; - case 'copy': - EventGenerator.sendKeyPress(KeyCode.C, {ctrl: true}); - - // The above command doesn't trigger document clipboard events, so we need - // to set this manually. - ChromeVoxState.instance.readNextClipboardDataChange(); - return false; - case 'toggleDictation': - EventGenerator.sendKeyPress(KeyCode.D, {search: true}); - return false; - } - - // Require a current range. - if (!ChromeVoxState.instance.currentRange_) { - if (!ChromeVoxState.instance.talkBackEnabled) { - new Output() - .withString(Msgs.getMsg( - EventSourceState.get() === EventSourceType.TOUCH_GESTURE ? - 'no_focus_touch' : - 'no_focus')) - .withQueueMode(QueueMode.FLUSH) - .go(); + } + return true; } - return true; - } - // Allow edit commands first. - if (!CommandHandler.onEditCommand_(command)) { - return false; - } - - let current = ChromeVoxState.instance.currentRange; - let node = current.start.node; - - // If true, will check if the predicate matches the current node. - let matchCurrent = false; - - let dir = Dir.FORWARD; - let pred = null; - let predErrorMsg = undefined; - let rootPred = AutomationPredicate.rootOrEditableRoot; - let unit = null; - let shouldWrap = true; - const speechProps = {}; - let skipSync = false; - let didNavigate = false; - let tryScrolling = true; - let skipSettingSelection = false; - let skipInitialAncestry = true; - switch (command) { - case 'nextCharacter': - didNavigate = true; - speechProps['phoneticCharacters'] = true; - unit = cursors.Unit.CHARACTER; - current = current.move(cursors.Unit.CHARACTER, Dir.FORWARD); - break; - case 'previousCharacter': - dir = Dir.BACKWARD; - didNavigate = true; - speechProps['phoneticCharacters'] = true; - unit = cursors.Unit.CHARACTER; - current = current.move(cursors.Unit.CHARACTER, dir); - break; - case 'nativeNextCharacter': - case 'nativePreviousCharacter': - if (DesktopAutomationHandler.instance.textEditHandler) { - DesktopAutomationHandler.instance.textEditHandler.injectInferredIntents( - [{ - command: chrome.automation.IntentCommandType.MOVE_SELECTION, - textBoundary: chrome.automation.IntentTextBoundaryType.CHARACTER - }]); - } - return true; - case 'nextWord': - didNavigate = true; - unit = cursors.Unit.WORD; - current = current.move(cursors.Unit.WORD, Dir.FORWARD); - break; - case 'previousWord': - dir = Dir.BACKWARD; - didNavigate = true; - unit = cursors.Unit.WORD; - current = current.move(cursors.Unit.WORD, dir); - break; - case 'nativeNextWord': - case 'nativePreviousWord': - if (DesktopAutomationHandler.instance.textEditHandler) { - DesktopAutomationHandler.instance.textEditHandler.injectInferredIntents( - [{ - command: chrome.automation.IntentCommandType.MOVE_SELECTION, - textBoundary: command === 'nativeNextWord' ? - chrome.automation.IntentTextBoundaryType.WORD_END : - chrome.automation.IntentTextBoundaryType.WORD_START - }]); - } - return true; - case 'forward': - case 'nextLine': - didNavigate = true; - unit = cursors.Unit.LINE; - current = current.move(cursors.Unit.LINE, Dir.FORWARD); - break; - case 'backward': - case 'previousLine': - dir = Dir.BACKWARD; - didNavigate = true; - unit = cursors.Unit.LINE; - current = current.move(cursors.Unit.LINE, dir); - break; - case 'nextButton': - dir = Dir.FORWARD; - pred = AutomationPredicate.button; - predErrorMsg = 'no_next_button'; - break; - case 'previousButton': - dir = Dir.BACKWARD; - pred = AutomationPredicate.button; - predErrorMsg = 'no_previous_button'; - break; - case 'nextCheckbox': - pred = AutomationPredicate.checkBox; - predErrorMsg = 'no_next_checkbox'; - break; - case 'previousCheckbox': - dir = Dir.BACKWARD; - pred = AutomationPredicate.checkBox; - predErrorMsg = 'no_previous_checkbox'; - break; - case 'nextComboBox': - pred = AutomationPredicate.comboBox; - predErrorMsg = 'no_next_combo_box'; - break; - case 'previousComboBox': - dir = Dir.BACKWARD; - pred = AutomationPredicate.comboBox; - predErrorMsg = 'no_previous_combo_box'; - break; - case 'nextEditText': - skipSettingSelection = true; - pred = AutomationPredicate.editText; - predErrorMsg = 'no_next_edit_text'; - CommandHandler.smartStickyMode_.startIgnoringRangeChanges(); - break; - case 'previousEditText': - skipSettingSelection = true; - dir = Dir.BACKWARD; - pred = AutomationPredicate.editText; - predErrorMsg = 'no_previous_edit_text'; - CommandHandler.smartStickyMode_.startIgnoringRangeChanges(); - break; - case 'nextFormField': - skipSettingSelection = true; - pred = AutomationPredicate.formField; - predErrorMsg = 'no_next_form_field'; - CommandHandler.smartStickyMode_.startIgnoringRangeChanges(); - break; - case 'previousFormField': - skipSettingSelection = true; - dir = Dir.BACKWARD; - pred = AutomationPredicate.formField; - predErrorMsg = 'no_previous_form_field'; - CommandHandler.smartStickyMode_.startIgnoringRangeChanges(); - break; - case 'previousGraphic': - skipSettingSelection = true; - dir = Dir.BACKWARD; - pred = AutomationPredicate.image; - predErrorMsg = 'no_previous_graphic'; - break; - case 'nextGraphic': - skipSettingSelection = true; - pred = AutomationPredicate.image; - predErrorMsg = 'no_next_graphic'; - break; - case 'nextHeading': - pred = AutomationPredicate.heading; - predErrorMsg = 'no_next_heading'; - break; - case 'nextHeading1': - pred = AutomationPredicate.makeHeadingPredicate(1); - predErrorMsg = 'no_next_heading_1'; - break; - case 'nextHeading2': - pred = AutomationPredicate.makeHeadingPredicate(2); - predErrorMsg = 'no_next_heading_2'; - break; - case 'nextHeading3': - pred = AutomationPredicate.makeHeadingPredicate(3); - predErrorMsg = 'no_next_heading_3'; - break; - case 'nextHeading4': - pred = AutomationPredicate.makeHeadingPredicate(4); - predErrorMsg = 'no_next_heading_4'; - break; - case 'nextHeading5': - pred = AutomationPredicate.makeHeadingPredicate(5); - predErrorMsg = 'no_next_heading_5'; - break; - case 'nextHeading6': - pred = AutomationPredicate.makeHeadingPredicate(6); - predErrorMsg = 'no_next_heading_6'; - break; - case 'previousHeading': - dir = Dir.BACKWARD; - pred = AutomationPredicate.heading; - predErrorMsg = 'no_previous_heading'; - break; - case 'previousHeading1': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(1); - predErrorMsg = 'no_previous_heading_1'; - break; - case 'previousHeading2': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(2); - predErrorMsg = 'no_previous_heading_2'; - break; - case 'previousHeading3': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(3); - predErrorMsg = 'no_previous_heading_3'; - break; - case 'previousHeading4': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(4); - predErrorMsg = 'no_previous_heading_4'; - break; - case 'previousHeading5': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(5); - predErrorMsg = 'no_previous_heading_5'; - break; - case 'previousHeading6': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeHeadingPredicate(6); - predErrorMsg = 'no_previous_heading_6'; - break; - case 'nextLink': - pred = AutomationPredicate.link; - predErrorMsg = 'no_next_link'; - break; - case 'previousLink': - dir = Dir.BACKWARD; - pred = AutomationPredicate.link; - predErrorMsg = 'no_previous_link'; - break; - case 'nextTable': - pred = AutomationPredicate.table; - predErrorMsg = 'no_next_table'; - break; - case 'previousTable': - dir = Dir.BACKWARD; - pred = AutomationPredicate.table; - predErrorMsg = 'no_previous_table'; - break; - case 'nextVisitedLink': - pred = AutomationPredicate.visitedLink; - predErrorMsg = 'no_next_visited_link'; - break; - case 'previousVisitedLink': - dir = Dir.BACKWARD; - pred = AutomationPredicate.visitedLink; - predErrorMsg = 'no_previous_visited_link'; - break; - case 'nextLandmark': - pred = AutomationPredicate.landmark; - predErrorMsg = 'no_next_landmark'; - break; - case 'previousLandmark': - dir = Dir.BACKWARD; - pred = AutomationPredicate.landmark; - predErrorMsg = 'no_previous_landmark'; - break; - case 'left': - case 'previousObject': - skipSettingSelection = true; - dir = Dir.BACKWARD; - // Falls through. - case 'right': - case 'nextObject': - skipSettingSelection = true; - didNavigate = true; - unit = cursors.Unit.NODE; - current = current.move(cursors.Unit.NODE, dir); - current = CommandHandler.skipLabelOrDescriptionFor(current, dir); - break; - case 'previousGroup': - skipSync = true; - dir = Dir.BACKWARD; - pred = AutomationPredicate.group; - break; - case 'nextGroup': - skipSync = true; - pred = AutomationPredicate.group; - break; - case 'previousPage': - case 'nextPage': - const root = AutomationUtil.getTopLevelRoot(current.start.node); - if (root && root.scrollY !== undefined) { - let page = Math.ceil(root.scrollY / root.location.height) || 1; - page = command === 'nextPage' ? page + 1 : page - 1; - ChromeVox.tts.stop(); - root.setScrollOffset(0, page * root.location.height); - } + // Allow edit commands first. + if (!this.onEditCommand_(command)) { return false; - case 'previousSimilarItem': - dir = Dir.BACKWARD; - // Falls through. - case 'nextSimilarItem': { - skipSync = true; - const originalNode = node; + } - // Scan upwards until we get a role we don't want to ignore. - while (node && AutomationPredicate.ignoreDuringJump(node)) { - node = node.parent; - } + let current = ChromeVoxState.instance.currentRange; + let node = current.start.node; - const useNode = node || originalNode; - pred = AutomationPredicate.roles([node.role]); - } break; - case 'previousInvalidItem': { - dir = Dir.BACKWARD; - rootPred = AutomationPredicate.root; - pred = AutomationPredicate.isInvalid; - predErrorMsg = 'no_invalid_item'; - } break; - case 'nextInvalidItem': { - pred = AutomationPredicate.isInvalid; - rootPred = AutomationPredicate.root; - predErrorMsg = 'no_invalid_item'; - } break; - case 'nextList': - pred = AutomationPredicate.makeListPredicate(current.start.node); - predErrorMsg = 'no_next_list'; - break; - case 'previousList': - dir = Dir.BACKWARD; - pred = AutomationPredicate.makeListPredicate(current.start.node); - predErrorMsg = 'no_previous_list'; - skipInitialAncestry = false; - break; - case 'jumpToTop': { - const node = AutomationUtil.findNodePost( - current.start.node.root, Dir.FORWARD, AutomationPredicate.object); - if (node) { - current = cursors.Range.fromNode(node); - } - tryScrolling = false; - } break; - case 'jumpToBottom': { - const node = AutomationUtil.findLastNode( - current.start.node.root, AutomationPredicate.object); - if (node) { - current = cursors.Range.fromNode(node); - } - tryScrolling = false; - } break; - case 'forceClickOnCurrentItem': - if (ChromeVoxState.instance.currentRange) { - let actionNode = ChromeVoxState.instance.currentRange.start.node; - // Scan for a clickable, which overrides the |actionNode|. - let clickable = actionNode; - while (clickable && !clickable.clickable && - actionNode.root === clickable.root) { - clickable = clickable.parent; + // If true, will check if the predicate matches the current node. + let matchCurrent = false; + + let dir = Dir.FORWARD; + let pred = null; + let predErrorMsg = undefined; + let rootPred = AutomationPredicate.rootOrEditableRoot; + let unit = null; + let shouldWrap = true; + const speechProps = {}; + let skipSync = false; + let didNavigate = false; + let tryScrolling = true; + let skipSettingSelection = false; + let skipInitialAncestry = true; + switch (command) { + case 'nextCharacter': + didNavigate = true; + speechProps['phoneticCharacters'] = true; + unit = cursors.Unit.CHARACTER; + current = current.move(cursors.Unit.CHARACTER, Dir.FORWARD); + break; + case 'previousCharacter': + dir = Dir.BACKWARD; + didNavigate = true; + speechProps['phoneticCharacters'] = true; + unit = cursors.Unit.CHARACTER; + current = current.move(cursors.Unit.CHARACTER, dir); + break; + case 'nativeNextCharacter': + case 'nativePreviousCharacter': + if (DesktopAutomationHandler.instance.textEditHandler) { + DesktopAutomationHandler.instance.textEditHandler + .injectInferredIntents([{ + command: chrome.automation.IntentCommandType.MOVE_SELECTION, + textBoundary: chrome.automation.IntentTextBoundaryType.CHARACTER + }]); } - if (clickable && actionNode.root === clickable.root) { - clickable.doDefault(); - return false; + return true; + case 'nextWord': + didNavigate = true; + unit = cursors.Unit.WORD; + current = current.move(cursors.Unit.WORD, Dir.FORWARD); + break; + case 'previousWord': + dir = Dir.BACKWARD; + didNavigate = true; + unit = cursors.Unit.WORD; + current = current.move(cursors.Unit.WORD, dir); + break; + case 'nativeNextWord': + case 'nativePreviousWord': + if (DesktopAutomationHandler.instance.textEditHandler) { + DesktopAutomationHandler.instance.textEditHandler + .injectInferredIntents([{ + command: chrome.automation.IntentCommandType.MOVE_SELECTION, + textBoundary: command === 'nativeNextWord' ? + chrome.automation.IntentTextBoundaryType.WORD_END : + chrome.automation.IntentTextBoundaryType.WORD_START + }]); } - - if (EventSourceState.get() === EventSourceType.TOUCH_GESTURE && - actionNode.state.editable) { - // Dispatch a click to ensure the VK gets shown. - const location = actionNode.location; - EventGenerator.sendMouseClick( - location.left + Math.round(location.width / 2), - location.top + Math.round(location.height / 2)); - return false; + return true; + case 'forward': + case 'nextLine': + didNavigate = true; + unit = cursors.Unit.LINE; + current = current.move(cursors.Unit.LINE, Dir.FORWARD); + break; + case 'backward': + case 'previousLine': + dir = Dir.BACKWARD; + didNavigate = true; + unit = cursors.Unit.LINE; + current = current.move(cursors.Unit.LINE, dir); + break; + case 'nextButton': + dir = Dir.FORWARD; + pred = AutomationPredicate.button; + predErrorMsg = 'no_next_button'; + break; + case 'previousButton': + dir = Dir.BACKWARD; + pred = AutomationPredicate.button; + predErrorMsg = 'no_previous_button'; + break; + case 'nextCheckbox': + pred = AutomationPredicate.checkBox; + predErrorMsg = 'no_next_checkbox'; + break; + case 'previousCheckbox': + dir = Dir.BACKWARD; + pred = AutomationPredicate.checkBox; + predErrorMsg = 'no_previous_checkbox'; + break; + case 'nextComboBox': + pred = AutomationPredicate.comboBox; + predErrorMsg = 'no_next_combo_box'; + break; + case 'previousComboBox': + dir = Dir.BACKWARD; + pred = AutomationPredicate.comboBox; + predErrorMsg = 'no_previous_combo_box'; + break; + case 'nextEditText': + skipSettingSelection = true; + pred = AutomationPredicate.editText; + predErrorMsg = 'no_next_edit_text'; + this.smartStickyMode_.startIgnoringRangeChanges(); + break; + case 'previousEditText': + skipSettingSelection = true; + dir = Dir.BACKWARD; + pred = AutomationPredicate.editText; + predErrorMsg = 'no_previous_edit_text'; + this.smartStickyMode_.startIgnoringRangeChanges(); + break; + case 'nextFormField': + skipSettingSelection = true; + pred = AutomationPredicate.formField; + predErrorMsg = 'no_next_form_field'; + this.smartStickyMode_.startIgnoringRangeChanges(); + break; + case 'previousFormField': + skipSettingSelection = true; + dir = Dir.BACKWARD; + pred = AutomationPredicate.formField; + predErrorMsg = 'no_previous_form_field'; + this.smartStickyMode_.startIgnoringRangeChanges(); + break; + case 'previousGraphic': + skipSettingSelection = true; + dir = Dir.BACKWARD; + pred = AutomationPredicate.image; + predErrorMsg = 'no_previous_graphic'; + break; + case 'nextGraphic': + skipSettingSelection = true; + pred = AutomationPredicate.image; + predErrorMsg = 'no_next_graphic'; + break; + case 'nextHeading': + pred = AutomationPredicate.heading; + predErrorMsg = 'no_next_heading'; + break; + case 'nextHeading1': + pred = AutomationPredicate.makeHeadingPredicate(1); + predErrorMsg = 'no_next_heading_1'; + break; + case 'nextHeading2': + pred = AutomationPredicate.makeHeadingPredicate(2); + predErrorMsg = 'no_next_heading_2'; + break; + case 'nextHeading3': + pred = AutomationPredicate.makeHeadingPredicate(3); + predErrorMsg = 'no_next_heading_3'; + break; + case 'nextHeading4': + pred = AutomationPredicate.makeHeadingPredicate(4); + predErrorMsg = 'no_next_heading_4'; + break; + case 'nextHeading5': + pred = AutomationPredicate.makeHeadingPredicate(5); + predErrorMsg = 'no_next_heading_5'; + break; + case 'nextHeading6': + pred = AutomationPredicate.makeHeadingPredicate(6); + predErrorMsg = 'no_next_heading_6'; + break; + case 'previousHeading': + dir = Dir.BACKWARD; + pred = AutomationPredicate.heading; + predErrorMsg = 'no_previous_heading'; + break; + case 'previousHeading1': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(1); + predErrorMsg = 'no_previous_heading_1'; + break; + case 'previousHeading2': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(2); + predErrorMsg = 'no_previous_heading_2'; + break; + case 'previousHeading3': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(3); + predErrorMsg = 'no_previous_heading_3'; + break; + case 'previousHeading4': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(4); + predErrorMsg = 'no_previous_heading_4'; + break; + case 'previousHeading5': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(5); + predErrorMsg = 'no_previous_heading_5'; + break; + case 'previousHeading6': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeHeadingPredicate(6); + predErrorMsg = 'no_previous_heading_6'; + break; + case 'nextLink': + pred = AutomationPredicate.link; + predErrorMsg = 'no_next_link'; + break; + case 'previousLink': + dir = Dir.BACKWARD; + pred = AutomationPredicate.link; + predErrorMsg = 'no_previous_link'; + break; + case 'nextTable': + pred = AutomationPredicate.table; + predErrorMsg = 'no_next_table'; + break; + case 'previousTable': + dir = Dir.BACKWARD; + pred = AutomationPredicate.table; + predErrorMsg = 'no_previous_table'; + break; + case 'nextVisitedLink': + pred = AutomationPredicate.visitedLink; + predErrorMsg = 'no_next_visited_link'; + break; + case 'previousVisitedLink': + dir = Dir.BACKWARD; + pred = AutomationPredicate.visitedLink; + predErrorMsg = 'no_previous_visited_link'; + break; + case 'nextLandmark': + pred = AutomationPredicate.landmark; + predErrorMsg = 'no_next_landmark'; + break; + case 'previousLandmark': + dir = Dir.BACKWARD; + pred = AutomationPredicate.landmark; + predErrorMsg = 'no_previous_landmark'; + break; + case 'left': + case 'previousObject': + skipSettingSelection = true; + dir = Dir.BACKWARD; + // Falls through. + case 'right': + case 'nextObject': + skipSettingSelection = true; + didNavigate = true; + unit = cursors.Unit.NODE; + current = current.move(cursors.Unit.NODE, dir); + current = this.skipLabelOrDescriptionFor(current, dir); + break; + case 'previousGroup': + skipSync = true; + dir = Dir.BACKWARD; + pred = AutomationPredicate.group; + break; + case 'nextGroup': + skipSync = true; + pred = AutomationPredicate.group; + break; + case 'previousPage': + case 'nextPage': + const root = AutomationUtil.getTopLevelRoot(current.start.node); + if (root && root.scrollY !== undefined) { + let page = Math.ceil(root.scrollY / root.location.height) || 1; + page = command === 'nextPage' ? page + 1 : page - 1; + ChromeVox.tts.stop(); + root.setScrollOffset(0, page * root.location.height); } - - while (actionNode.role === RoleType.INLINE_TEXT_BOX || - actionNode.role === RoleType.STATIC_TEXT) { - actionNode = actionNode.parent; - } - if (actionNode.inPageLinkTarget) { - ChromeVoxState.instance.navigateToRange( - cursors.Range.fromNode(actionNode.inPageLinkTarget)); - } else { - actionNode.doDefault(); - } - } - // Skip all other processing; if focus changes, we should get an event - // for that. - return false; - case 'jumpToDetails': { - while (node && !node.details) { - node = node.parent; - } - if (node && node.details.length) { - // TODO currently can only jump to first detail. - current = cursors.Range.fromNode(node.details[0]); - } - } break; - case 'readFromHere': - ChromeVoxState.isReadingContinuously = true; - const continueReading = function() { - if (!ChromeVoxState.isReadingContinuously || - !ChromeVoxState.instance.currentRange) { - return; - } - - const prevRange = ChromeVoxState.instance.currentRange; - const newRange = ChromeVoxState.instance.currentRange.move( - cursors.Unit.NODE, Dir.FORWARD); - - // Stop if we've wrapped back to the document. - const maybeDoc = newRange.start.node; - if (AutomationPredicate.root(maybeDoc)) { - ChromeVoxState.isReadingContinuously = false; - return; - } - - ChromeVoxState.instance.setCurrentRange(newRange); - newRange.select(); - - const o = new Output() - .withoutHints() - .withRichSpeechAndBraille( - ChromeVoxState.instance.currentRange, prevRange, - OutputEventType.NAVIGATE) - .onSpeechEnd(continueReading); - - if (!o.hasSpeech) { - continueReading(); - return; - } - - o.go(); - }.bind(this); - - { - const startNode = ChromeVoxState.instance.currentRange.start.node; - const collapsedRange = cursors.Range.fromNode(startNode); - const o = - new Output() - .withoutHints() - .withRichSpeechAndBraille( - collapsedRange, collapsedRange, OutputEventType.NAVIGATE) - .onSpeechEnd(continueReading); - - if (o.hasSpeech) { - o.go(); - } else { - continueReading(); - } - } - return false; - case 'contextMenu': - EventGenerator.sendKeyPress(KeyCode.APPS); - break; - case 'showHeadingsList': - (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_heading')).send(); - return false; - case 'showFormsList': - (new PanelCommand( - PanelCommandType.OPEN_MENUS, 'panel_menu_form_controls')) - .send(); - return false; - case 'showLandmarksList': - (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_landmark')).send(); - return false; - case 'showLinksList': - (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link')).send(); - return false; - case 'showActionsMenu': - (new PanelCommand(PanelCommandType.OPEN_MENUS, 'panel_menu_actions')) - .send(); - return false; - case 'showTablesList': - (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy')).send(); - return false; - case 'toggleSearchWidget': - (new PanelCommand(PanelCommandType.SEARCH)).send(); - return false; - case 'readCurrentTitle': { - let target = ChromeVoxState.instance.currentRange.start.node; - const output = new Output(); - - if (!target) { return false; - } + case 'previousSimilarItem': + dir = Dir.BACKWARD; + // Falls through. + case 'nextSimilarItem': { + skipSync = true; + const originalNode = node; - let firstWindow; - let rootViewWindow; - if (target.root && target.root.role === RoleType.DESKTOP) { - // Search for the first container with a name. - while (target && (!target.name || !AutomationPredicate.root(target))) { - target = target.parent; + // Scan upwards until we get a role we don't want to ignore. + while (node && AutomationPredicate.ignoreDuringJump(node)) { + node = node.parent; } - } else { - // Search for a root window with a title. - while (target) { - const isNamedWindow = - !!target.name && target.role === RoleType.WINDOW; - const isRootView = target.className === 'RootView'; - if (isNamedWindow && !firstWindow) { - firstWindow = target; + + const useNode = node || originalNode; + pred = AutomationPredicate.roles([node.role]); + } break; + case 'previousInvalidItem': { + dir = Dir.BACKWARD; + rootPred = AutomationPredicate.root; + pred = AutomationPredicate.isInvalid; + predErrorMsg = 'no_invalid_item'; + } break; + case 'nextInvalidItem': { + pred = AutomationPredicate.isInvalid; + rootPred = AutomationPredicate.root; + predErrorMsg = 'no_invalid_item'; + } break; + case 'nextList': + pred = AutomationPredicate.makeListPredicate(current.start.node); + predErrorMsg = 'no_next_list'; + break; + case 'previousList': + dir = Dir.BACKWARD; + pred = AutomationPredicate.makeListPredicate(current.start.node); + predErrorMsg = 'no_previous_list'; + skipInitialAncestry = false; + break; + case 'jumpToTop': { + const node = AutomationUtil.findNodePost( + current.start.node.root, Dir.FORWARD, AutomationPredicate.object); + if (node) { + current = cursors.Range.fromNode(node); + } + tryScrolling = false; + } break; + case 'jumpToBottom': { + const node = AutomationUtil.findLastNode( + current.start.node.root, AutomationPredicate.object); + if (node) { + current = cursors.Range.fromNode(node); + } + tryScrolling = false; + } break; + case 'forceClickOnCurrentItem': + if (ChromeVoxState.instance.currentRange) { + let actionNode = ChromeVoxState.instance.currentRange.start.node; + // Scan for a clickable, which overrides the |actionNode|. + let clickable = actionNode; + while (clickable && !clickable.clickable && + actionNode.root === clickable.root) { + clickable = clickable.parent; + } + if (clickable && actionNode.root === clickable.root) { + clickable.doDefault(); + return false; } - if (isNamedWindow && isRootView) { - rootViewWindow = target; - break; + if (EventSourceState.get() === EventSourceType.TOUCH_GESTURE && + actionNode.state.editable) { + // Dispatch a click to ensure the VK gets shown. + const location = actionNode.location; + EventGenerator.sendMouseClick( + location.left + Math.round(location.width / 2), + location.top + Math.round(location.height / 2)); + return false; } - target = target.parent; + + while (actionNode.role === RoleType.INLINE_TEXT_BOX || + actionNode.role === RoleType.STATIC_TEXT) { + actionNode = actionNode.parent; + } + if (actionNode.inPageLinkTarget) { + ChromeVoxState.instance.navigateToRange( + cursors.Range.fromNode(actionNode.inPageLinkTarget)); + } else { + actionNode.doDefault(); + } } - } + // Skip all other processing; if focus changes, we should get an event + // for that. + return false; + case 'jumpToDetails': { + while (node && !node.details) { + node = node.parent; + } + if (node && node.details.length) { + // TODO currently can only jump to first detail. + current = cursors.Range.fromNode(node.details[0]); + } + } break; + case 'readFromHere': + ChromeVoxState.isReadingContinuously = true; + const continueReading = function() { + if (!ChromeVoxState.isReadingContinuously || + !ChromeVoxState.instance.currentRange) { + return; + } - // Re-target with preference for the root. - target = rootViewWindow || firstWindow || target; + const prevRange = ChromeVoxState.instance.currentRange; + const newRange = ChromeVoxState.instance.currentRange.move( + cursors.Unit.NODE, Dir.FORWARD); - if (!target) { - output.format('@no_title'); - } else { - output.withString(target.name); - } + // Stop if we've wrapped back to the document. + const maybeDoc = newRange.start.node; + if (AutomationPredicate.root(maybeDoc)) { + ChromeVoxState.isReadingContinuously = false; + return; + } - output.go(); - } - return false; - case 'readCurrentURL': - const output = new Output(); - const target = ChromeVoxState.instance.currentRange.start.node.root; - output.withString(target.docUrl || '').go(); - return false; - case 'toggleSelection': - if (!ChromeVoxState.instance.pageSel_) { - ChromeVoxState.instance.pageSel_ = ChromeVoxState.instance.currentRange; - DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction( - true); - } else { - const root = ChromeVoxState.instance.currentRange.start.node.root; - if (root && root.selectionStartObject && root.selectionEndObject) { - const sel = new cursors.Range( - new cursors.Cursor( - root.selectionStartObject, root.selectionStartOffset), - new cursors.Cursor( - root.selectionEndObject, root.selectionEndOffset)); + ChromeVoxState.instance.setCurrentRange(newRange); + newRange.select(); + + const o = new Output() + .withoutHints() + .withRichSpeechAndBraille( + ChromeVoxState.instance.currentRange, prevRange, + OutputEventType.NAVIGATE) + .onSpeechEnd(continueReading); + + if (!o.hasSpeech) { + continueReading(); + return; + } + + o.go(); + }.bind(this); + + { + const startNode = ChromeVoxState.instance.currentRange.start.node; + const collapsedRange = cursors.Range.fromNode(startNode); const o = new Output() - .format('@end_selection') - .withSpeechAndBraille(sel, sel, OutputEventType.NAVIGATE) - .go(); - DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction( - false); - } - ChromeVoxState.instance.pageSel_ = null; - return false; - } - break; - case 'fullyDescribe': - const o = new Output(); - o.withContextFirst() - .withRichSpeechAndBraille(current, null, OutputEventType.NAVIGATE) - .go(); - return false; - case 'viewGraphicAsBraille': - CommandHandler.viewGraphicAsBraille_(current); - return false; - // Table commands. - case 'previousRow': { - skipSync = true; - dir = Dir.BACKWARD; - const tableOpts = {row: true, dir}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); - predErrorMsg = 'no_cell_above'; - rootPred = AutomationPredicate.table; - shouldWrap = false; - } break; - case 'previousCol': { - skipSync = true; - dir = Dir.BACKWARD; - const tableOpts = {col: true, dir}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); - predErrorMsg = 'no_cell_left'; - rootPred = AutomationPredicate.row; - shouldWrap = false; - } break; - case 'nextRow': { - skipSync = true; - const tableOpts = {row: true, dir}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); - predErrorMsg = 'no_cell_below'; - rootPred = AutomationPredicate.table; - shouldWrap = false; - } break; - case 'nextCol': { - skipSync = true; - const tableOpts = {col: true, dir}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); - predErrorMsg = 'no_cell_right'; - rootPred = AutomationPredicate.row; - shouldWrap = false; - } break; - case 'goToRowFirstCell': - case 'goToRowLastCell': { - skipSync = true; - while (node && node.role !== RoleType.ROW) { - node = node.parent; - } - if (!node) { - break; - } - const end = AutomationUtil.findNodePost( - node, command === 'goToRowLastCell' ? Dir.BACKWARD : Dir.FORWARD, - AutomationPredicate.leaf); - if (end) { - current = cursors.Range.fromNode(end); - } - } break; - case 'goToColFirstCell': { - skipSync = true; - while (node && node.role !== RoleType.TABLE) { - node = node.parent; - } - if (!node || !node.firstChild) { - return false; - } - const tableOpts = {col: true, dir, end: true}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); - current = cursors.Range.fromNode(node.firstChild); - // Should not be outputted. - predErrorMsg = 'no_cell_above'; - rootPred = AutomationPredicate.table; - shouldWrap = false; - } break; - case 'goToColLastCell': { - skipSync = true; - dir = Dir.BACKWARD; - while (node && node.role !== RoleType.TABLE) { - node = node.parent; - } - if (!node || !node.lastChild) { - return false; - } - const tableOpts = {col: true, dir, end: true}; - pred = AutomationPredicate.makeTableCellPredicate( - current.start.node, tableOpts); + .withoutHints() + .withRichSpeechAndBraille( + collapsedRange, collapsedRange, OutputEventType.NAVIGATE) + .onSpeechEnd(continueReading); - // Try to start on the last cell of the table and allow - // matching that node. - let startNode = node.lastChild; - while (startNode.lastChild && - !AutomationPredicate.cellLike(startNode.role)) { - startNode = startNode.lastChild; - } - current = cursors.Range.fromNode(startNode); - matchCurrent = true; - - // Should not be outputted. - predErrorMsg = 'no_cell_below'; - rootPred = AutomationPredicate.table; - shouldWrap = false; - } break; - case 'goToFirstCell': - case 'goToLastCell': { - skipSync = true; - while (node && node.role !== RoleType.TABLE) { - node = node.parent; - } - if (!node) { - break; - } - const end = AutomationUtil.findNodePost( - node, command === 'goToLastCell' ? Dir.BACKWARD : Dir.FORWARD, - AutomationPredicate.leaf); - if (end) { - current = cursors.Range.fromNode(end); - } - } break; - - // These commands are only available when invoked from touch. - case 'nextAtGranularity': - case 'previousAtGranularity': - const backwards = command === 'previousAtGranularity'; - switch (GestureCommandHandler.granularity) { - case GestureGranularity.CHARACTER: - command = backwards ? 'previousCharacter' : 'nextCharacter'; - break; - case GestureGranularity.WORD: - command = backwards ? 'previousWord' : 'nextWord'; - break; - case GestureGranularity.LINE: - command = backwards ? 'previousLine' : 'nextLine'; - break; - case GestureGranularity.HEADING: - command = backwards ? 'previousHeading' : 'nextHeading'; - break; - case GestureGranularity.LINK: - command = backwards ? 'previousLink' : 'nextLink'; - break; - case GestureGranularity.FORM_FIELD_CONTROL: - command = backwards ? 'previousFormField' : 'nextFormField'; - break; - } - CommandHandler.onCommand(command); - return false; - case 'announceRichTextDescription': { - const optSubs = []; - node.fontSize ? optSubs.push('font size: ' + node.fontSize) : - optSubs.push(''); - node.color ? optSubs.push(Color.getColorDescription(node.color)) : - optSubs.push(''); - node.bold ? optSubs.push(Msgs.getMsg('bold')) : optSubs.push(''); - node.italic ? optSubs.push(Msgs.getMsg('italic')) : optSubs.push(''); - node.underline ? optSubs.push(Msgs.getMsg('underline')) : - optSubs.push(''); - node.lineThrough ? optSubs.push(Msgs.getMsg('linethrough')) : - optSubs.push(''); - node.fontFamily ? optSubs.push('font family: ' + node.fontFamily) : - optSubs.push(''); - - const richTextDescription = Msgs.getMsg('rich_text_attributes', optSubs); - new Output() - .withString(richTextDescription) - .withQueueMode(QueueMode.CATEGORY_FLUSH) - .go(); - } - return false; - case 'readPhoneticPronunciation': { - // Get node info. - const index = ChromeVoxState.instance.currentRange.start.index; - const name = node.name; - // If there is no text to speak, inform the user and return early. - if (!name) { - new Output() - .withString(Msgs.getMsg('empty_name')) - .withQueueMode(QueueMode.CATEGORY_FLUSH) - .go(); - return false; - } - - // Get word start and end indices. - let wordStarts, wordEnds; - if (node.role === RoleType.INLINE_TEXT_BOX) { - wordStarts = node.wordStarts; - wordEnds = node.wordEnds; - } else { - wordStarts = node.nonInlineTextWordStarts; - wordEnds = node.nonInlineTextWordEnds; - } - // Find the word we want to speak phonetically. If index === -1, then the - // index represents an entire node. - let text = ''; - if (index === -1) { - text = name; - } else { - for (let z = 0; z < wordStarts.length; ++z) { - if (wordStarts[z] <= index && wordEnds[z] > index) { - text = name.substring(wordStarts[z], wordEnds[z]); - break; + if (o.hasSpeech) { + o.go(); + } else { + continueReading(); } } - } + return false; + case 'contextMenu': + EventGenerator.sendKeyPress(KeyCode.APPS); + break; + case 'showHeadingsList': + (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_heading')).send(); + return false; + case 'showFormsList': + (new PanelCommand( + PanelCommandType.OPEN_MENUS, 'panel_menu_form_controls')) + .send(); + return false; + case 'showLandmarksList': + (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_landmark')).send(); + return false; + case 'showLinksList': + (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link')).send(); + return false; + case 'showActionsMenu': + (new PanelCommand(PanelCommandType.OPEN_MENUS, 'panel_menu_actions')) + .send(); + return false; + case 'showTablesList': + (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy')) + .send(); + return false; + case 'toggleSearchWidget': + (new PanelCommand(PanelCommandType.SEARCH)).send(); + return false; + case 'readCurrentTitle': { + let target = ChromeVoxState.instance.currentRange.start.node; + const output = new Output(); - const language = chrome.i18n.getUILanguage(); - const phoneticText = PhoneticData.forText(text, language); - if (phoneticText) { + if (!target) { + return false; + } + + let firstWindow; + let rootViewWindow; + if (target.root && target.root.role === RoleType.DESKTOP) { + // Search for the first container with a name. + while (target && + (!target.name || !AutomationPredicate.root(target))) { + target = target.parent; + } + } else { + // Search for a root window with a title. + while (target) { + const isNamedWindow = + !!target.name && target.role === RoleType.WINDOW; + const isRootView = target.className === 'RootView'; + if (isNamedWindow && !firstWindow) { + firstWindow = target; + } + + if (isNamedWindow && isRootView) { + rootViewWindow = target; + break; + } + target = target.parent; + } + } + + // Re-target with preference for the root. + target = rootViewWindow || firstWindow || target; + + if (!target) { + output.format('@no_title'); + } else { + output.withString(target.name); + } + + output.go(); + } + return false; + case 'readCurrentURL': + const output = new Output(); + const target = ChromeVoxState.instance.currentRange.start.node.root; + output.withString(target.docUrl || '').go(); + return false; + case 'toggleSelection': + if (!ChromeVoxState.instance.pageSel_) { + ChromeVoxState.instance.pageSel_ = + ChromeVoxState.instance.currentRange; + DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction( + true); + } else { + const root = ChromeVoxState.instance.currentRange.start.node.root; + if (root && root.selectionStartObject && root.selectionEndObject) { + const sel = new cursors.Range( + new cursors.Cursor( + root.selectionStartObject, root.selectionStartOffset), + new cursors.Cursor( + root.selectionEndObject, root.selectionEndOffset)); + const o = + new Output() + .format('@end_selection') + .withSpeechAndBraille(sel, sel, OutputEventType.NAVIGATE) + .go(); + DesktopAutomationHandler.instance.ignoreDocumentSelectionFromAction( + false); + } + ChromeVoxState.instance.pageSel_ = null; + return false; + } + break; + case 'fullyDescribe': + const o = new Output(); + o.withContextFirst() + .withRichSpeechAndBraille(current, null, OutputEventType.NAVIGATE) + .go(); + return false; + case 'viewGraphicAsBraille': + this.viewGraphicAsBraille_(current); + return false; + // Table commands. + case 'previousRow': { + skipSync = true; + dir = Dir.BACKWARD; + const tableOpts = {row: true, dir}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + predErrorMsg = 'no_cell_above'; + rootPred = AutomationPredicate.table; + shouldWrap = false; + } break; + case 'previousCol': { + skipSync = true; + dir = Dir.BACKWARD; + const tableOpts = {col: true, dir}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + predErrorMsg = 'no_cell_left'; + rootPred = AutomationPredicate.row; + shouldWrap = false; + } break; + case 'nextRow': { + skipSync = true; + const tableOpts = {row: true, dir}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + predErrorMsg = 'no_cell_below'; + rootPred = AutomationPredicate.table; + shouldWrap = false; + } break; + case 'nextCol': { + skipSync = true; + const tableOpts = {col: true, dir}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + predErrorMsg = 'no_cell_right'; + rootPred = AutomationPredicate.row; + shouldWrap = false; + } break; + case 'goToRowFirstCell': + case 'goToRowLastCell': { + skipSync = true; + while (node && node.role !== RoleType.ROW) { + node = node.parent; + } + if (!node) { + break; + } + const end = AutomationUtil.findNodePost( + node, command === 'goToRowLastCell' ? Dir.BACKWARD : Dir.FORWARD, + AutomationPredicate.leaf); + if (end) { + current = cursors.Range.fromNode(end); + } + } break; + case 'goToColFirstCell': { + skipSync = true; + while (node && node.role !== RoleType.TABLE) { + node = node.parent; + } + if (!node || !node.firstChild) { + return false; + } + const tableOpts = {col: true, dir, end: true}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + current = cursors.Range.fromNode(node.firstChild); + // Should not be outputted. + predErrorMsg = 'no_cell_above'; + rootPred = AutomationPredicate.table; + shouldWrap = false; + } break; + case 'goToColLastCell': { + skipSync = true; + dir = Dir.BACKWARD; + while (node && node.role !== RoleType.TABLE) { + node = node.parent; + } + if (!node || !node.lastChild) { + return false; + } + const tableOpts = {col: true, dir, end: true}; + pred = AutomationPredicate.makeTableCellPredicate( + current.start.node, tableOpts); + + // Try to start on the last cell of the table and allow + // matching that node. + let startNode = node.lastChild; + while (startNode.lastChild && + !AutomationPredicate.cellLike(startNode.role)) { + startNode = startNode.lastChild; + } + current = cursors.Range.fromNode(startNode); + matchCurrent = true; + + // Should not be outputted. + predErrorMsg = 'no_cell_below'; + rootPred = AutomationPredicate.table; + shouldWrap = false; + } break; + case 'goToFirstCell': + case 'goToLastCell': { + skipSync = true; + while (node && node.role !== RoleType.TABLE) { + node = node.parent; + } + if (!node) { + break; + } + const end = AutomationUtil.findNodePost( + node, command === 'goToLastCell' ? Dir.BACKWARD : Dir.FORWARD, + AutomationPredicate.leaf); + if (end) { + current = cursors.Range.fromNode(end); + } + } break; + + // These commands are only available when invoked from touch. + case 'nextAtGranularity': + case 'previousAtGranularity': + const backwards = command === 'previousAtGranularity'; + switch (GestureCommandHandler.granularity) { + case GestureGranularity.CHARACTER: + command = backwards ? 'previousCharacter' : 'nextCharacter'; + break; + case GestureGranularity.WORD: + command = backwards ? 'previousWord' : 'nextWord'; + break; + case GestureGranularity.LINE: + command = backwards ? 'previousLine' : 'nextLine'; + break; + case GestureGranularity.HEADING: + command = backwards ? 'previousHeading' : 'nextHeading'; + break; + case GestureGranularity.LINK: + command = backwards ? 'previousLink' : 'nextLink'; + break; + case GestureGranularity.FORM_FIELD_CONTROL: + command = backwards ? 'previousFormField' : 'nextFormField'; + break; + } + this.onCommand(command); + return false; + case 'announceRichTextDescription': { + const optSubs = []; + node.fontSize ? optSubs.push('font size: ' + node.fontSize) : + optSubs.push(''); + node.color ? optSubs.push(Color.getColorDescription(node.color)) : + optSubs.push(''); + node.bold ? optSubs.push(Msgs.getMsg('bold')) : optSubs.push(''); + node.italic ? optSubs.push(Msgs.getMsg('italic')) : optSubs.push(''); + node.underline ? optSubs.push(Msgs.getMsg('underline')) : + optSubs.push(''); + node.lineThrough ? optSubs.push(Msgs.getMsg('linethrough')) : + optSubs.push(''); + node.fontFamily ? optSubs.push('font family: ' + node.fontFamily) : + optSubs.push(''); + + const richTextDescription = + Msgs.getMsg('rich_text_attributes', optSubs); new Output() - .withString(phoneticText) + .withString(richTextDescription) .withQueueMode(QueueMode.CATEGORY_FLUSH) .go(); } - } - return false; - case 'readLinkURL': { - const rootNode = node.root; - while (node && !node.url) { - // URL could be an ancestor of current range. - node = node.parent; - } - // Announce node's URL if it's not the root node; we don't want to - // announce the URL of the current page. - const url = (node && node !== rootNode) ? node.url : ''; - new Output() - .withString( - url ? Msgs.getMsg('url_behind_link', [url]) : - Msgs.getMsg('no_url_found')) - .withQueueMode(QueueMode.CATEGORY_FLUSH) - .go(); - } - return false; - case 'logLanguageInformationForCurrentNode': { - if (!CommandHandler.languageLoggingEnabled_) { return false; - } + case 'readPhoneticPronunciation': { + // Get node info. + const index = ChromeVoxState.instance.currentRange.start.index; + const name = node.name; + // If there is no text to speak, inform the user and return early. + if (!name) { + new Output() + .withString(Msgs.getMsg('empty_name')) + .withQueueMode(QueueMode.CATEGORY_FLUSH) + .go(); + return false; + } - const outString = ` + // Get word start and end indices. + let wordStarts, wordEnds; + if (node.role === RoleType.INLINE_TEXT_BOX) { + wordStarts = node.wordStarts; + wordEnds = node.wordEnds; + } else { + wordStarts = node.nonInlineTextWordStarts; + wordEnds = node.nonInlineTextWordEnds; + } + // Find the word we want to speak phonetically. If index === -1, then + // the index represents an entire node. + let text = ''; + if (index === -1) { + text = name; + } else { + for (let z = 0; z < wordStarts.length; ++z) { + if (wordStarts[z] <= index && wordEnds[z] > index) { + text = name.substring(wordStarts[z], wordEnds[z]); + break; + } + } + } + + const language = chrome.i18n.getUILanguage(); + const phoneticText = PhoneticData.forText(text, language); + if (phoneticText) { + new Output() + .withString(phoneticText) + .withQueueMode(QueueMode.CATEGORY_FLUSH) + .go(); + } + } + return false; + case 'readLinkURL': { + const rootNode = node.root; + while (node && !node.url) { + // URL could be an ancestor of current range. + node = node.parent; + } + // Announce node's URL if it's not the root node; we don't want to + // announce the URL of the current page. + const url = (node && node !== rootNode) ? node.url : ''; + new Output() + .withString( + url ? Msgs.getMsg('url_behind_link', [url]) : + Msgs.getMsg('no_url_found')) + .withQueueMode(QueueMode.CATEGORY_FLUSH) + .go(); + } + return false; + case 'logLanguageInformationForCurrentNode': { + if (!this.languageLoggingEnabled_) { + return false; + } + + const outString = ` Language information for node Name: ${node.name} Detected language: ${node.detectedLanguage || 'None'} Author language: ${node.language || 'None'} `; - new Output() - .withString(outString) - .withQueueMode(QueueMode.CATEGORY_FLUSH) - .go(); - const annotation = node.languageAnnotationForStringAttribute('name'); - const logString = outString.concat(`Language spans: + new Output() + .withString(outString) + .withQueueMode(QueueMode.CATEGORY_FLUSH) + .go(); + const annotation = node.languageAnnotationForStringAttribute('name'); + const logString = outString.concat(`Language spans: ${JSON.stringify(annotation)}`); - console.error(logString); - LogStore.getInstance().writeTextLog(logString, LogStore.LogType.TEXT); + console.error(logString); + LogStore.getInstance().writeTextLog(logString, LogStore.LogType.TEXT); + } + return false; + default: + return true; } - return false; - default: - return true; - } - if (didNavigate) { - chrome.metricsPrivate.recordUserAction('Accessibility.ChromeVox.Navigate'); - } + if (didNavigate) { + chrome.metricsPrivate.recordUserAction( + 'Accessibility.ChromeVox.Navigate'); + } - if (pred) { - chrome.metricsPrivate.recordUserAction('Accessibility.ChromeVox.Jump'); + if (pred) { + chrome.metricsPrivate.recordUserAction('Accessibility.ChromeVox.Jump'); - let bound = current.getBound(dir).node; - if (bound) { - let node = null; + let bound = current.getBound(dir).node; + if (bound) { + let node = null; - if (matchCurrent && pred(bound)) { - node = bound; - } - - if (!node) { - node = AutomationUtil.findNextNode( - bound, dir, pred, {skipInitialAncestry, root: rootPred}); - } - - if (node && !skipSync) { - node = AutomationUtil.findNodePre( - node, Dir.FORWARD, AutomationPredicate.object) || - node; - } - - if (node) { - current = cursors.Range.fromNode(node); - } else { - ChromeVox.earcons.playEarcon(Earcon.WRAP); - if (!shouldWrap) { - if (predErrorMsg) { - new Output() - .withString(Msgs.getMsg(predErrorMsg)) - .withQueueMode(QueueMode.FLUSH) - .go(); - } - CommandHandler.onFinishCommand(); - return false; + if (matchCurrent && pred(bound)) { + node = bound; } - let root = bound; - while (root && !AutomationPredicate.rootOrEditableRoot(root)) { - root = root.parent; + if (!node) { + node = AutomationUtil.findNextNode( + bound, dir, pred, {skipInitialAncestry, root: rootPred}); } - if (!root) { - root = bound.root; - } - - if (dir === Dir.FORWARD) { - bound = root; - } else { - bound = AutomationUtil.findNodePost( - root, dir, AutomationPredicate.leaf) || - bound; - } - node = AutomationUtil.findNextNode(bound, dir, pred, {root: rootPred}); - if (node && !skipSync) { node = AutomationUtil.findNodePre( node, Dir.FORWARD, AutomationPredicate.object) || @@ -1247,289 +1205,315 @@ if (node) { current = cursors.Range.fromNode(node); - } else if (predErrorMsg) { - new Output() - .withString(Msgs.getMsg(predErrorMsg)) - .withQueueMode(QueueMode.FLUSH) - .go(); - CommandHandler.onFinishCommand(); - return false; + } else { + ChromeVox.earcons.playEarcon(Earcon.WRAP); + if (!shouldWrap) { + if (predErrorMsg) { + new Output() + .withString(Msgs.getMsg(predErrorMsg)) + .withQueueMode(QueueMode.FLUSH) + .go(); + } + this.onFinishCommand(); + return false; + } + + let root = bound; + while (root && !AutomationPredicate.rootOrEditableRoot(root)) { + root = root.parent; + } + + if (!root) { + root = bound.root; + } + + if (dir === Dir.FORWARD) { + bound = root; + } else { + bound = AutomationUtil.findNodePost( + root, dir, AutomationPredicate.leaf) || + bound; + } + node = + AutomationUtil.findNextNode(bound, dir, pred, {root: rootPred}); + + if (node && !skipSync) { + node = AutomationUtil.findNodePre( + node, Dir.FORWARD, AutomationPredicate.object) || + node; + } + + if (node) { + current = cursors.Range.fromNode(node); + } else if (predErrorMsg) { + new Output() + .withString(Msgs.getMsg(predErrorMsg)) + .withQueueMode(QueueMode.FLUSH) + .go(); + this.onFinishCommand(); + return false; + } } } } - } - if (tryScrolling && - !AutoScrollHandler.getInstance().onCommandNavigation( - current, dir, pred, unit, speechProps, rootPred, () => { - CommandHandler.onCommand(command); - CommandHandler.onFinishCommand(); - })) { - CommandHandler.onFinishCommand(); + if (tryScrolling && + !AutoScrollHandler.getInstance().onCommandNavigation( + current, dir, pred, unit, speechProps, rootPred, () => { + this.onCommand(command); + this.onFinishCommand(); + })) { + this.onFinishCommand(); + return false; + } + + if (current) { + if (current.wrapped) { + ChromeVox.earcons.playEarcon(Earcon.WRAP); + } + + ChromeVoxState.instance.navigateToRange( + current, undefined, speechProps, skipSettingSelection); + } + + this.onFinishCommand(); return false; } - if (current) { - if (current.wrapped) { - ChromeVox.earcons.playEarcon(Earcon.WRAP); + /** + * Finishes processing of a command. + */ + onFinishCommand() { + this.smartStickyMode_.stopIgnoringRangeChanges(); + } + + /** + * Increase or decrease a speech property and make an announcement. + * @param {string} propertyName The name of the property to change. + * @param {boolean} increase If true, increases the property value by one + * step size, otherwise decreases. + * @private + */ + increaseOrDecreaseSpeechProperty_(propertyName, increase) { + ChromeVox.tts.increaseOrDecreaseProperty(propertyName, increase); + } + + /** + * Called when an image frame is received on a node. + * @param {!(AutomationEvent|CustomAutomationEvent)} event The event. + * @private + */ + onImageFrameUpdated_(event) { + const target = event.target; + if (target !== this.imageNode_) { + return; } - ChromeVoxState.instance.navigateToRange( - current, undefined, speechProps, skipSettingSelection); + if (!AutomationUtil.isDescendantOf( + ChromeVoxState.instance.currentRange.start.node, this.imageNode_)) { + this.imageNode_.removeEventListener( + EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false); + this.imageNode_ = null; + return; + } + + if (target.imageDataUrl) { + ChromeVox.braille.writeRawImage(target.imageDataUrl); + ChromeVox.braille.freeze(); + } } - CommandHandler.onFinishCommand(); - return false; -}; + /** + * Handle the command to view the first graphic within the current range + * as braille. + * @param {!cursors.Range} current The current range. + * @private + */ + viewGraphicAsBraille_(current) { + if (this.imageNode_) { + this.imageNode_.removeEventListener( + EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false); + this.imageNode_ = null; + } -/** - * Finishes processing of a command. - */ -CommandHandler.onFinishCommand = function() { - CommandHandler.smartStickyMode_.stopIgnoringRangeChanges(); -}; + // Find the first node within the current range that supports image data. + const imageNode = AutomationUtil.findNodePost( + current.start.node, Dir.FORWARD, AutomationPredicate.supportsImageData); + if (!imageNode) { + return; + } -/** - * Increase or decrease a speech property and make an announcement. - * @param {string} propertyName The name of the property to change. - * @param {boolean} increase If true, increases the property value by one - * step size, otherwise decreases. - * @private - */ -CommandHandler.increaseOrDecreaseSpeechProperty_ = function( - propertyName, increase) { - ChromeVox.tts.increaseOrDecreaseProperty(propertyName, increase); -}; - -/** - * To support viewGraphicAsBraille_(), the current image node. - * @type {AutomationNode?}; - */ -CommandHandler.imageNode_; - -/** - * Called when an image frame is received on a node. - * @param {!(AutomationEvent|CustomAutomationEvent)} event The event. - * @private - */ -CommandHandler.onImageFrameUpdated_ = function(event) { - const target = event.target; - if (target !== CommandHandler.imageNode_) { - return; + imageNode.addEventListener( + EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false); + this.imageNode_ = imageNode; + if (imageNode.imageDataUrl) { + const event = new CustomAutomationEvent( + EventType.IMAGE_FRAME_UPDATED, imageNode, {eventFrom: 'page'}); + this.onImageFrameUpdated_(event); + } else { + imageNode.getImageData(0, 0); + } } - if (!AutomationUtil.isDescendantOf( - ChromeVoxState.instance.currentRange.start.node, - CommandHandler.imageNode_)) { - CommandHandler.imageNode_.removeEventListener( - EventType.IMAGE_FRAME_UPDATED, CommandHandler.onImageFrameUpdated_, - false); - CommandHandler.imageNode_ = null; - return; - } - - if (target.imageDataUrl) { - ChromeVox.braille.writeRawImage(target.imageDataUrl); - ChromeVox.braille.freeze(); - } -}; - -/** - * Handle the command to view the first graphic within the current range - * as braille. - * @param {!cursors.Range} current The current range. - * @private - */ -CommandHandler.viewGraphicAsBraille_ = function(current) { - if (CommandHandler.imageNode_) { - CommandHandler.imageNode_.removeEventListener( - EventType.IMAGE_FRAME_UPDATED, CommandHandler.onImageFrameUpdated_, - false); - CommandHandler.imageNode_ = null; - } - - // Find the first node within the current range that supports image data. - const imageNode = AutomationUtil.findNodePost( - current.start.node, Dir.FORWARD, AutomationPredicate.supportsImageData); - if (!imageNode) { - return; - } - - imageNode.addEventListener( - EventType.IMAGE_FRAME_UPDATED, CommandHandler.onImageFrameUpdated_, - false); - CommandHandler.imageNode_ = imageNode; - if (imageNode.imageDataUrl) { - const event = new CustomAutomationEvent( - EventType.IMAGE_FRAME_UPDATED, imageNode, {eventFrom: 'page'}); - CommandHandler.onImageFrameUpdated_(event); - } else { - imageNode.getImageData(0, 0); - } -}; - -/** - * Provides a partial mapping from ChromeVox key combinations to - * Search-as-a-function key as seen in Chrome OS documentation. - * @param {string} command - * @return {boolean} True if the command should propagate. - * @private - */ -CommandHandler.onEditCommand_ = function(command) { - if (ChromeVox.isStickyModeOn()) { - return true; - } - - const textEditHandler = DesktopAutomationHandler.instance.textEditHandler; - if (!textEditHandler || - !AutomationUtil.isDescendantOf( - ChromeVoxState.instance.currentRange.start.node, - textEditHandler.node)) { - return true; - } - - // Skip customized keys for read only text fields. - if (textEditHandler.node.restriction === - chrome.automation.Restriction.READ_ONLY) { - return true; - } - - // Skips customized keys if they get suppressed in speech. - if (AutomationPredicate.shouldOnlyOutputSelectionChangeInBraille( - textEditHandler.node)) { - return true; - } - - const isMultiline = AutomationPredicate.multiline(textEditHandler.node); - switch (command) { - case 'previousCharacter': - EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true}); - break; - case 'nextCharacter': - EventGenerator.sendKeyPress(KeyCode.END, {shift: true}); - break; - case 'previousWord': - EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true, ctrl: true}); - break; - case 'nextWord': - EventGenerator.sendKeyPress(KeyCode.END, {shift: true, ctrl: true}); - break; - case 'previousObject': - if (!isMultiline) { - return true; - } - - if (textEditHandler.isSelectionOnFirstLine()) { - ChromeVoxState.instance.setCurrentRange( - cursors.Range.fromNode(textEditHandler.node)); - return true; - } - EventGenerator.sendKeyPress(KeyCode.HOME); - break; - case 'nextObject': - if (!isMultiline) { - return true; - } - - if (textEditHandler.isSelectionOnLastLine()) { - textEditHandler.moveToAfterEditText(); - return false; - } - - EventGenerator.sendKeyPress(KeyCode.END); - break; - case 'previousLine': - if (!isMultiline) { - return true; - } - if (textEditHandler.isSelectionOnFirstLine()) { - ChromeVoxState.instance.setCurrentRange( - cursors.Range.fromNode(textEditHandler.node)); - return true; - } - EventGenerator.sendKeyPress(KeyCode.PRIOR); - break; - case 'nextLine': - if (!isMultiline) { - return true; - } - - if (textEditHandler.isSelectionOnLastLine()) { - textEditHandler.moveToAfterEditText(); - return false; - } - EventGenerator.sendKeyPress(KeyCode.NEXT); - break; - case 'jumpToTop': - EventGenerator.sendKeyPress(KeyCode.HOME, {ctrl: true}); - break; - case 'jumpToBottom': - EventGenerator.sendKeyPress(KeyCode.END, {ctrl: true}); - break; - default: + /** + * Provides a partial mapping from ChromeVox key combinations to + * Search-as-a-function key as seen in Chrome OS documentation. + * @param {string} command + * @return {boolean} True if the command should propagate. + * @private + */ + onEditCommand_(command) { + if (ChromeVox.isStickyModeOn()) { return true; - } - return false; -}; + } -/** - * A helper to object navigation to skip all static text nodes who have - * label/description for on ancestor nodes. - * @param {cursors.Range} current - * @param {Dir} dir - * @return {cursors.Range} The resulting range. - */ -CommandHandler.skipLabelOrDescriptionFor = function(current, dir) { - if (!current) { - return null; + const textEditHandler = DesktopAutomationHandler.instance.textEditHandler; + if (!textEditHandler || + !AutomationUtil.isDescendantOf( + ChromeVoxState.instance.currentRange.start.node, + textEditHandler.node)) { + return true; + } + + // Skip customized keys for read only text fields. + if (textEditHandler.node.restriction === + chrome.automation.Restriction.READ_ONLY) { + return true; + } + + // Skips customized keys if they get suppressed in speech. + if (AutomationPredicate.shouldOnlyOutputSelectionChangeInBraille( + textEditHandler.node)) { + return true; + } + + const isMultiline = AutomationPredicate.multiline(textEditHandler.node); + switch (command) { + case 'previousCharacter': + EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true}); + break; + case 'nextCharacter': + EventGenerator.sendKeyPress(KeyCode.END, {shift: true}); + break; + case 'previousWord': + EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true, ctrl: true}); + break; + case 'nextWord': + EventGenerator.sendKeyPress(KeyCode.END, {shift: true, ctrl: true}); + break; + case 'previousObject': + if (!isMultiline) { + return true; + } + + if (textEditHandler.isSelectionOnFirstLine()) { + ChromeVoxState.instance.setCurrentRange( + cursors.Range.fromNode(textEditHandler.node)); + return true; + } + EventGenerator.sendKeyPress(KeyCode.HOME); + break; + case 'nextObject': + if (!isMultiline) { + return true; + } + + if (textEditHandler.isSelectionOnLastLine()) { + textEditHandler.moveToAfterEditText(); + return false; + } + + EventGenerator.sendKeyPress(KeyCode.END); + break; + case 'previousLine': + if (!isMultiline) { + return true; + } + if (textEditHandler.isSelectionOnFirstLine()) { + ChromeVoxState.instance.setCurrentRange( + cursors.Range.fromNode(textEditHandler.node)); + return true; + } + EventGenerator.sendKeyPress(KeyCode.PRIOR); + break; + case 'nextLine': + if (!isMultiline) { + return true; + } + + if (textEditHandler.isSelectionOnLastLine()) { + textEditHandler.moveToAfterEditText(); + return false; + } + EventGenerator.sendKeyPress(KeyCode.NEXT); + break; + case 'jumpToTop': + EventGenerator.sendKeyPress(KeyCode.HOME, {ctrl: true}); + break; + case 'jumpToBottom': + EventGenerator.sendKeyPress(KeyCode.END, {ctrl: true}); + break; + default: + return true; + } + return false; } - // Keep moving past all nodes acting as labels or descriptions. - while (current && current.start && current.start.node && - current.start.node.role === RoleType.STATIC_TEXT) { - // We must scan upwards as any ancestor might have a label or description. - let ancestor = current.start.node; - while (ancestor) { - if ((ancestor.labelFor && ancestor.labelFor.length > 0) || - (ancestor.descriptionFor && ancestor.descriptionFor.length > 0)) { + /** @override */ + + skipLabelOrDescriptionFor(current, dir) { + if (!current) { + return null; + } + + // Keep moving past all nodes acting as labels or descriptions. + while (current && current.start && current.start.node && + current.start.node.role === RoleType.STATIC_TEXT) { + // We must scan upwards as any ancestor might have a label or description. + let ancestor = current.start.node; + while (ancestor) { + if ((ancestor.labelFor && ancestor.labelFor.length > 0) || + (ancestor.descriptionFor && ancestor.descriptionFor.length > 0)) { + break; + } + ancestor = ancestor.parent; + } + if (ancestor) { + current = current.move(cursors.Unit.NODE, dir); + } else { break; } - ancestor = ancestor.parent; } - if (ancestor) { - current = current.move(cursors.Unit.NODE, dir); - } else { - break; - } + + return current; } - return current; -}; + /** + * Performs global initialization. + */ + init() { + ChromeVoxKbHandler.commandHandler = this.onCommand.bind(this); -/** - * Performs global initialization. - */ -CommandHandler.init = function() { - ChromeVoxKbHandler.commandHandler = CommandHandler.onCommand; + chrome.commandLinePrivate.hasSwitch( + 'enable-experimental-accessibility-language-detection', (enabled) => { + if (enabled) { + this.languageLoggingEnabled_ = true; + } + }); + chrome.commandLinePrivate.hasSwitch( + 'enable-experimental-accessibility-language-detection-dynamic', + (enabled) => { + if (enabled) { + this.languageLoggingEnabled_ = true; + } + }); - chrome.commandLinePrivate.hasSwitch( - 'enable-experimental-accessibility-language-detection', (enabled) => { - if (enabled) { - CommandHandler.languageLoggingEnabled_ = true; - } - }); - chrome.commandLinePrivate.hasSwitch( - 'enable-experimental-accessibility-language-detection-dynamic', - (enabled) => { - if (enabled) { - CommandHandler.languageLoggingEnabled_ = true; - } - }); + chrome.chromeosInfoPrivate.get(['sessionType'], (result) => { + /** @type {boolean} */ + this.isKioskSession_ = result['sessionType'] === + chrome.chromeosInfoPrivate.SessionType.KIOSK; + }); + } +} - chrome.chromeosInfoPrivate.get(['sessionType'], (result) => { - /** @type {boolean} */ - CommandHandler.isKioskSession_ = - result['sessionType'] === chrome.chromeosInfoPrivate.SessionType.KIOSK; - }); -}; -}); // goog.scope +CommandHandlerInterface.instance = new CommandHandler();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js new file mode 100644 index 0000000..d8e8f1cac --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js
@@ -0,0 +1,28 @@ +// Copyright 2022 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. + +goog.provide('CommandHandlerInterface'); + +CommandHandlerInterface = class { + /** + * Handles ChromeVox commands. + * @param {string} command + * @return {boolean} True if the command should propagate. + */ + onCommand(command) {} + + /** + * A helper to object navigation to skip all static text nodes who have + * label/description for on ancestor nodes. + * @param {cursors.Range} current + * @param {constants.Dir} dir + * @return {cursors.Range} The resulting range. + */ + skipLabelOrDescriptionFor(current, dir) {} +}; + +/** + * @type {CommandHandlerInterface} + */ +CommandHandlerInterface.instance;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js index 669d1d8..8e2e17f 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -12,7 +12,7 @@ goog.require('AutomationObjectConstructorInstaller'); goog.require('BaseAutomationHandler'); goog.require('ChromeVoxState'); -goog.require('CommandHandler'); +goog.require('CommandHandlerInterface'); goog.require('CustomAutomationEvent'); goog.require('editing.TextEditHandler'); @@ -418,7 +418,7 @@ ChromeVoxState.instance.setCurrentRange( cursors.Range.fromNode(evt.target)); ChromeVox.tts.stop(); - CommandHandler.onCommand('readFromHere'); + CommandHandlerInterface.instance.onCommand('readFromHere'); return; } @@ -513,7 +513,8 @@ // Sync ChromeVox range with selection. if (!ChromeVoxState.isReadingContinuously) { - ChromeVoxState.instance.setCurrentRange(selectedRange); + ChromeVoxState.instance.setCurrentRange( + selectedRange, true /* from editing */); } } this.textEditHandler_.onEvent(evt);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js index cb5620c..0d2ccb5 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -12,6 +12,8 @@ ChromeVoxDesktopAutomationHandlerTest = class extends ChromeVoxNextE2ETest { /** @override */ async setUpDeferred() { + await super.setUpDeferred(); + window.press = this.press; await new Promise(r => {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js index 47fcf54..d62d186 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -11,6 +11,7 @@ goog.require('AutomationTreeWalker'); goog.require('AutomationUtil'); +goog.require('Color'); goog.require('IntentHandler'); goog.require('Output'); goog.require('OutputEventType'); @@ -976,7 +977,7 @@ } /** @override */ - onCurrentRangeChanged(range) { + onCurrentRangeChanged(range, opt_fromEditing) { const inputType = range && range.start.node.inputType; if (inputType === 'email' || inputType === 'url') { BrailleBackground.getInstance().getTranslatorManager().refresh(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js index 87f89a0..0d5211c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
@@ -9,7 +9,7 @@ goog.provide('GestureCommandHandler'); goog.require('ChromeVoxState'); -goog.require('CommandHandler'); +goog.require('CommandHandlerInterface'); goog.require('EventGenerator'); goog.require('EventSourceState'); goog.require('GestureCommandData'); @@ -113,7 +113,7 @@ const command = commandData.command; if (command) { - CommandHandler.onCommand(command); + CommandHandlerInterface.instance.onCommand(command); } };
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions_test.js index 5f86e60..703f9fd 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions_test.js
@@ -12,6 +12,8 @@ */ ChromeVoxLiveRegionsTest = class extends ChromeVoxNextE2ETest { async setUpDeferred() { + await super.setUpDeferred(); + window.TreeChangeType = chrome.automation.TreeChangeType; await importModule('LiveRegions', '/chromevox/background/live_regions.js'); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js index 0cba40b8..b64c921 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -15,7 +15,8 @@ goog.require('ChromeVoxBackground'); goog.require('ChromeVoxEditableTextBase'); goog.require('ChromeVoxState'); -goog.require('CommandHandler'); +goog.require('CommandHandlerInterface'); +goog.require('CommandStore'); goog.require('DesktopAutomationHandler'); goog.require('ExtensionBridge'); goog.require('GestureCommandHandler'); @@ -27,5 +28,6 @@ goog.require('OutputEventType'); goog.require('PanelCommand'); goog.require('PhoneticData'); +goog.require('SmartStickyMode'); goog.require('constants'); goog.require('cursors.Cursor');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js index 1c3b573d..ae657c6 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -34,8 +34,9 @@ /** * @param {cursors.Range} newRange + * @param {boolean=} opt_fromEditing */ - onCurrentRangeChanged(newRange) { + onCurrentRangeChanged(newRange, opt_fromEditing) { if (this.node_) { this.removeAllListeners(); this.node_ = undefined;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js index b44472b..995049a 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
@@ -33,9 +33,9 @@ } /** @override */ - onCurrentRangeChanged(newRange) { + onCurrentRangeChanged(newRange, opt_fromEditing) { if (!newRange || this.ignoreRangeChanges_ || - ChromeVoxState.isReadingContinuously || + ChromeVoxState.isReadingContinuously || opt_fromEditing || localStorage['smartStickyMode'] !== 'true') { return; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js index d230406..a98d1a2f 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js
@@ -326,6 +326,6 @@ * @private */ static onCommand_(command) { - CommandHandler.onCommand(command); + CommandHandlerInterface.instance.onCommand(command); } -}; \ No newline at end of file +};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background_test.js index 1725efb..a7316e3 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background_test.js
@@ -538,7 +538,7 @@ TEST_F('ChromeVoxTtsBackgroundTest', 'ResetTtsSettingsClearsVoice', function() { this.newCallback(async () => { ChromeVox.tts.ttsEngines_[0].currentVoice = ''; - CommandHandler.onCommand('resetTextToSpeechSettings'); + CommandHandlerInterface.instance.onCommand('resetTextToSpeechSettings'); await new Promise(r => { ChromeVox.tts.speak = textString => { if (textString === 'Reset text to speech settings to default values') {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js index ab8da2f..5530aa1 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js
@@ -37,7 +37,7 @@ desktop.addEventListener( chrome.automation.EventType.LOAD_COMPLETE, listener); - CommandHandler.onCommand('showKbExplorerPage'); + CommandHandlerInterface.instance.onCommand('showKbExplorerPage'); }); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js index 17583dc..ff97fe4 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js
@@ -29,7 +29,7 @@ mockFeedback.expectSpeech('ChromeVox Options'); callback(mockFeedback, evt); }); - CommandHandler.onCommand('showOptionsPage'); + CommandHandlerInterface.instance.onCommand('showOptionsPage'); }); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js index 2372c73ba..e111a96c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -429,8 +429,9 @@ menu.addMenuItem( binding.title, keyText, brailleText, gestureText, function() { const CommandHandler = - chrome.extension.getBackgroundPage()['CommandHandler']; - CommandHandler['onCommand'](binding.command); + chrome.extension + .getBackgroundPage()['CommandHandlerInterface']; + CommandHandler.instance.onCommand(binding.command); }, binding.command); } }); @@ -463,8 +464,9 @@ touchMenu.addMenuItem( item.titleText, '', '', item.gestureText, function() { const CommandHandler = - chrome.extension.getBackgroundPage()['CommandHandler']; - CommandHandler['onCommand'](item.command); + chrome.extension + .getBackgroundPage()['CommandHandlerInterface']; + CommandHandler.instance.onCommand(item.command); }, item.command); } } @@ -1214,8 +1216,8 @@ chromeVoxStateInstance.destroyUserActionMonitor(); }); $('chromevox-tutorial').addEventListener('requestfullydescribe', (evt) => { - const commandHandler = backgroundPage['CommandHandler']; - commandHandler.onCommand('fullyDescribe'); + const commandHandler = backgroundPage['CommandHandlerInterface']; + commandHandler.instance.onCommand('fullyDescribe'); }); $('chromevox-tutorial').addEventListener('requestearcon', (evt) => { const earconId = evt.detail.earconId; @@ -1302,7 +1304,7 @@ Panel.PanelStateObserver = class { constructor() {} - onCurrentRangeChanged(range) { + onCurrentRangeChanged(range, opt_fromEditing) { if (Panel.mode_ === Panel.Mode.FULLSCREEN_TUTORIAL) { if (Panel.tutorial && Panel.tutorial.restartNudges) { Panel.tutorial.restartNudges(); @@ -1374,6 +1376,7 @@ // it in in every case. (fullscreen/focus turns the state off, collapse // turns it back on). if (Panel.originalStickyState_) { - bkgnd['CommandHandler']['onCommand']('toggleStickyMode'); + bkgnd['CommandHandlerInterface']['instance']['onCommand']( + 'toggleStickyMode'); } }, false);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js index 8e57d61..528fd10 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
@@ -103,7 +103,7 @@ // TODO(https://crbug.com/1299765): Re-enable once flaky timeouts are fixed. TEST_F('ChromeVoxPanelTest', 'DISABLED_LinkMenu', function() { this.runWithLoadedTree(this.linksDoc, async function(root) { - CommandHandler.onCommand('showLinksList'); + CommandHandlerInterface.instance.onCommand('showLinksList'); await this.waitForMenu('role_link'); this.fireMockEvent('ArrowLeft')(); this.assertActiveMenuItem('role_landmark', 'No items'); @@ -117,7 +117,7 @@ TEST_F('ChromeVoxPanelTest', 'FormControlsMenu', function() { this.runWithLoadedTree( `<button>Cancel</button><button>OK</button>`, async function(root) { - CommandHandler.onCommand('showFormsList'); + CommandHandlerInterface.instance.onCommand('showFormsList'); await this.waitForMenu('panel_menu_form_controls'); this.fireMockEvent('ArrowDown')(); this.assertActiveMenuItem('panel_menu_form_controls', 'OK Button'); @@ -191,7 +191,7 @@ localStorage['languageSwitching'] = 'true'; this.getPanelWindow().LocaleOutputHelper.instance.availableVoices_ = [{'lang': 'en-US'}, {'lang': 'es-ES'}]; - CommandHandler.onCommand('showFormsList'); + CommandHandlerInterface.instance.onCommand('showFormsList'); await this.waitForMenu('panel_menu_form_controls'); this.fireMockEvent('ArrowDown')(); this.assertActiveMenuItem( @@ -203,7 +203,7 @@ TEST_F('ChromeVoxPanelTest', 'ActionsMenu', function() { this.runWithLoadedTree(this.linksDoc, async function(root) { - CommandHandler.onCommand('showActionsMenu'); + CommandHandlerInterface.instance.onCommand('showActionsMenu'); await this.waitForMenu('panel_menu_actions'); this.fireMockEvent('ArrowDown')(); this.assertActiveMenuItem('panel_menu_actions', 'Start Or End Selection');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js index 42b75b92..c84dcd0 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
@@ -572,16 +572,16 @@ }); }; restart = false; - CommandHandler.onCommand('nextObject'); + CommandHandlerInterface.instance.onCommand('nextObject'); await waitForRestartNudges(); // Show a lesson. tutorial.curriculum = 'essential_keys'; tutorial.showLesson_(0); restart = false; - CommandHandler.onCommand('nextObject'); + CommandHandlerInterface.instance.onCommand('nextObject'); await waitForRestartNudges(); restart = false; - CommandHandler.onCommand('nextObject'); + CommandHandlerInterface.instance.onCommand('nextObject'); await waitForRestartNudges(); }); });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js index d28c621..9776fa2 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
@@ -87,7 +87,7 @@ */ doCmd(cmd) { return () => { - CommandHandler.onCommand(cmd); + CommandHandlerInterface.instance.onCommand(cmd); }; } @@ -113,10 +113,17 @@ } /** @override */ + async setUpDeferred() { + await super.setUpDeferred(); + await importModule( + 'CommandHandler', '/chromevox/background/command_handler.js'); + } + + /** @override */ runWithLoadedTree(doc, callback, opt_params = {}) { callback = this.newCallback(callback); const wrappedCallback = (node) => { - CommandHandler.onCommand('nextObject'); + CommandHandlerInterface.instance.onCommand('nextObject'); callback(node); };
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html index f72133cb9..41e5b7f 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.html
@@ -251,60 +251,60 @@ </div> <div id="dialogBody" slot="body"> <template is="dom-if" if="[[!hasStartedSetupAttempt_]]" restamp> + <div id="start-setup-description"> + <div id="half-container"> + <div id="illustration"></div> + </div> + <div id="half-container"> + <div id="feature-description"> + <template is="dom-if" if="[[shouldShowNotificationItem_]]" + restamp> + <div id="feature-details-container"> + <iron-icon id="feature-icon" + icon="os-settings:multidevice-better-together-suite"> + </iron-icon> + <div id="feature-details-description"> + <div id="feature-title"> + $i18n{multidevicePermissionsSetupNotificationsTitle} + </div> + <div id="feature-subtitle"> + $i18n{multidevicePermissionsSetupNotificationsSummary} + </div> + </div> + </div> + </template> + <template is="dom-if" if="[[shouldShowAppsItem_]]" restamp> + <div id="feature-details-container"> + <iron-icon id="feature-icon" + icon="os-settings:multidevice-better-together-suite"> + </iron-icon> + <div id="feature-details-description"> + <div id="feature-title"> + $i18n{multidevicePermissionsSetupAppsTitle} + </div> + <div id="feature-subtitle"> + $i18n{multidevicePermissionsSetupAppsSummary} + </div> + </div> + </div> + </template> + </div> + </div> + </div> + </template> + <template is="dom-if" if="[[hasStartedSetupAttempt_]]" restamp> <template is="dom-if" if="[[shouldShowScreenLockInstructions_(flowState_)]]" restamp> <settings-multidevice-screen-lock-subpage is-screen-lock-enabled="{{isScreenLockEnabled_}}" is-password-dialog-showing="{{isPasswordDialogShowing}}"> </settings-multidevice-screen-lock-subpage> - </template> + </template> <template is="dom-if" if="[[!shouldShowScreenLockInstructions_(flowState_)]]" restamp> - <div id="start-setup-description"> - <div id="half-container"> - <div id="illustration"></div> - </div> - <div id="half-container"> - <div id="feature-description"> - <template is="dom-if" if="[[shouldShowNotificationItem_]]" - restamp> - <div id="feature-details-container"> - <iron-icon id="feature-icon" - icon="os-settings:multidevice-better-together-suite"> - </iron-icon> - <div id="feature-details-description"> - <div id="feature-title"> - $i18n{multidevicePermissionsSetupNotificationsTitle} - </div> - <div id="feature-subtitle"> - $i18n{multidevicePermissionsSetupNotificationsSummary} - </div> - </div> - </div> - </template> - <template is="dom-if" if="[[shouldShowAppsItem_]]" restamp> - <div id="feature-details-container"> - <iron-icon id="feature-icon" - icon="os-settings:multidevice-better-together-suite"> - </iron-icon> - <div id="feature-details-description"> - <div id="feature-title"> - $i18n{multidevicePermissionsSetupAppsTitle} - </div> - <div id="feature-subtitle"> - $i18n{multidevicePermissionsSetupAppsSummary} - </div> - </div> - </div> - </template> - </div> - </div> - </div> - </template> - </template> - <template is="dom-if" if="[[hasStartedSetupAttempt_]]" restamp> - <div id="illustration"></div> - <template is="dom-if" if="[[description_]]" restamp> - <localized-link localized-string="[[description_]]"> - </localized-link> + <div id="illustration"></div> + <template is="dom-if" if="[[description_]]" restamp> + <localized-link localized-string="[[description_]]"> + </localized-link> + </template> </template> </template> <template is="dom-if" if="[[shouldShowSetupInstructionsSeparately_]]" @@ -351,6 +351,14 @@ $i18n{next} </cr-button> </template> + <template is="dom-if" if="[[hasStartedSetupAttempt_]]" restamp> + <template is="dom-if" if="[[shouldShowScreenLockInstructions_(flowState_)]]" restamp> + <cr-button id="getStartedButton" class="action-button" + on-click="nextPage_"> + $i18n{next} + </cr-button> + </template> + </template> <template is="dom-if" if="[[shouldShowTryAgainButton_(setupState_)]]" restamp> <cr-button id="tryAgainButton" class="action-button"
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js index 93045dad..0be342f3 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
@@ -276,9 +276,8 @@ /** @private */ nextPage_() { - const isScreenLockRequired = loadTimeData.getBoolean('isEcheAppEnabled') && - loadTimeData.getBoolean('isPhoneScreenLockEnabled') && - !loadTimeData.getBoolean('isChromeosScreenLockEnabled'); + const isScreenLockRequired = + this.isScreenLockRequired_(); switch (this.flowState_) { case SetupFlowStatus.INTRO: if (isScreenLockRequired) { @@ -459,4 +458,18 @@ this.phonePermissionSetupMode === PhoneHubPermissionsSetupMode.APPS_SETUP_MODE); }, + + /** + * @return {boolean} + * @private + */ + isScreenLockRequired_() { + return loadTimeData.getBoolean('isEcheAppEnabled') && + loadTimeData.getBoolean('isPhoneScreenLockEnabled') && + !loadTimeData.getBoolean('isChromeosScreenLockEnabled') && + (this.phonePermissionSetupMode === + PhoneHubPermissionsSetupMode.ALL_PERMISSIONS_SETUP_MODE || + this.phonePermissionSetupMode === + PhoneHubPermissionsSetupMode.APPS_SETUP_MODE); + }, });
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js index 81a2d110..a0cd86f 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.js +++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -124,7 +124,7 @@ export {MultiDeviceBrowserProxy, MultiDeviceBrowserProxyImpl} from './multidevice_page/multidevice_browser_proxy.m.js'; export {MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceSettingsMode, PhoneHubNotificationAccessProhibitedReason, PhoneHubNotificationAccessStatus, PhoneHubPermissionsSetupMode, SmartLockSignInEnabledState} from './multidevice_page/multidevice_constants.m.js'; export {NotificationAccessSetupOperationStatus} from './multidevice_page/multidevice_notification_access_setup_dialog.m.js'; -export {PermissionsSetupStatus} from './multidevice_page/multidevice_permissions_setup_dialog.m.js'; +export {PermissionsSetupStatus, SetupFlowStatus} from './multidevice_page/multidevice_permissions_setup_dialog.m.js'; export {Account, NearbyAccountManagerBrowserProxy, NearbyAccountManagerBrowserProxyImpl} from './nearby_share_page/nearby_account_manager_browser_proxy.js'; export {getReceiveManager, observeReceiveManager, setReceiveManagerForTesting} from './nearby_share_page/nearby_share_receive_manager.js'; export {dataUsageStringToEnum, NearbyShareDataUsage} from './nearby_share_page/types.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html index 94d7099..3e4bd479 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html +++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -3,7 +3,7 @@ /* The tap target extends slightly above each visible menu item. */ --tap-target-padding: 3px; /* Width of the keyboard focus border. */ - --focus-border-width: 1px; + --focus-border-width: 2px; box-sizing: border-box; display: block; padding-bottom: 2px; @@ -100,7 +100,8 @@ } :host-context(.focus-outline-visible) #advancedButton:focus { - outline: auto 5px -webkit-focus-ring-color; + border-radius: 0 20px 20px 0; + outline: var(--focus-border-width) solid var(--cros-focus-ring-color); } #advancedButton > span {
diff --git a/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc b/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc index 70f6188..3c57417 100644 --- a/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc +++ b/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc
@@ -116,6 +116,8 @@ mock_speech_delegate_ = std::make_unique<testing::StrictMock<MockSpeechRecognizerDelegate>>(); // Fake that SODA is installed. + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + speech::LanguageCode::kEnUs); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); }
diff --git a/chrome/browser/speech/speech_recognition_client_browser_interface.cc b/chrome/browser/speech/speech_recognition_client_browser_interface.cc index ad3c368..510ade71 100644 --- a/chrome/browser/speech/speech_recognition_client_browser_interface.cc +++ b/chrome/browser/speech/speech_recognition_client_browser_interface.cc
@@ -58,7 +58,10 @@ OnSpeechRecognitionAvailabilityChanged(); } -void SpeechRecognitionClientBrowserInterface::OnSodaInstalled() { +void SpeechRecognitionClientBrowserInterface::OnSodaInstalled( + speech::LanguageCode language_code) { + if (!prefs::IsLanguageCodeForLiveCaption(language_code, profile_prefs_)) + return; NotifyObservers(profile_prefs_->GetBoolean(prefs::kLiveCaptionEnabled)); }
diff --git a/chrome/browser/speech/speech_recognition_client_browser_interface.h b/chrome/browser/speech/speech_recognition_client_browser_interface.h index 6bb65ee..9a302e7 100644 --- a/chrome/browser/speech/speech_recognition_client_browser_interface.h +++ b/chrome/browser/speech/speech_recognition_client_browser_interface.h
@@ -45,9 +45,10 @@ pending_remote) override; // SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaProgress(int combined_progress) override {} - void OnSodaError() override {} + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override {} + void OnSodaError(speech::LanguageCode language_code) override {} private: void OnSpeechRecognitionAvailabilityChanged();
diff --git a/chrome/browser/speech/speech_recognition_test_helper.cc b/chrome/browser/speech/speech_recognition_test_helper.cc index ec3e67aa..5e56dd2a 100644 --- a/chrome/browser/speech/speech_recognition_test_helper.cc +++ b/chrome/browser/speech/speech_recognition_test_helper.cc
@@ -39,6 +39,8 @@ void SpeechRecognitionTestHelper::SetUpOnDeviceRecognition(Profile* profile) { // Fake that SODA is installed so SpeechRecognitionPrivate uses // OnDeviceSpeechRecognizer. + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + speech::LanguageCode::kEnUs); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); CrosSpeechRecognitionServiceFactory::GetInstanceForTest() ->SetTestingFactoryAndUse(
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc index 1842607..97be4722 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
@@ -95,6 +95,7 @@ SetLocale(kEnglishLocale); soda_installer_ = std::make_unique<MockSodaInstaller>(); soda_installer_->NotifySodaInstalledForTesting(); + soda_installer_->NotifySodaInstalledForTesting(speech::LanguageCode::kEnUs); projector_client_ = std::make_unique<ProjectorClientImpl>(&projector_controller_); }
diff --git a/chrome/browser/ui/ash/projector/projector_soda_installation_controller.cc b/chrome/browser/ui/ash/projector/projector_soda_installation_controller.cc index 081ea989..4847fd3 100644 --- a/chrome/browser/ui/ash/projector/projector_soda_installation_controller.cc +++ b/chrome/browser/ui/ash/projector/projector_soda_installation_controller.cc
@@ -89,25 +89,38 @@ return speech::SodaInstaller::GetInstance()->IsSodaInstalled(language_code); } -void ProjectorSodaInstallationController::OnSodaInstalled() { - auto* soda_installer = speech::SodaInstaller::GetInstance(); - // Make sure that both SODA binary and the locale language are available - // before notifying that on device speech recognition is available. - if (!soda_installer->IsSodaInstalled(speech::GetLanguageCode(GetLocale()))) +void ProjectorSodaInstallationController::OnSodaInstalled( + speech::LanguageCode language_code) { + // Check that language code matches the selected language for projector. + if (language_code != speech::GetLanguageCode(GetLocale())) return; - projector_controller_->OnSpeechRecognitionAvailabilityChanged( ash::SpeechRecognitionAvailability::kAvailable); app_client_->OnSodaInstalled(); } -void ProjectorSodaInstallationController::OnSodaError() { +void ProjectorSodaInstallationController::OnSodaError( + speech::LanguageCode language_code) { + // Check that language code matches the selected language for projector or is + // LanguageCode::kNone (signifying the SODA binary failed). + if (language_code != speech::GetLanguageCode(GetLocale()) && + language_code != speech::LanguageCode::kNone) { + return; + } + projector_controller_->OnSpeechRecognitionAvailabilityChanged( ash::SpeechRecognitionAvailability::kSodaInstallationError); app_client_->OnSodaInstallError(); } void ProjectorSodaInstallationController::OnSodaProgress( - int combined_progress) { - app_client_->OnSodaInstallProgress(combined_progress); + speech::LanguageCode language_code, + int progress) { + // Check that language code matches the selected language for projector or is + // LanguageCode::kNone (signifying the SODA binary has progress). + if (language_code != speech::GetLanguageCode(GetLocale()) && + language_code != speech::LanguageCode::kNone) { + return; + } + app_client_->OnSodaInstallProgress(progress); }
diff --git a/chrome/browser/ui/ash/projector/projector_soda_installation_controller.h b/chrome/browser/ui/ash/projector/projector_soda_installation_controller.h index 4fe6a8b9..6adfa1a1 100644 --- a/chrome/browser/ui/ash/projector/projector_soda_installation_controller.h +++ b/chrome/browser/ui/ash/projector/projector_soda_installation_controller.h
@@ -46,15 +46,10 @@ protected: // speech::SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaLanguagePackInstalled( - speech::LanguageCode language_code) override {} - void OnSodaError() override; - void OnSodaLanguagePackError(speech::LanguageCode language_code) override {} - void OnSodaProgress(int combined_progress) override; - void OnSodaLanguagePackProgress(int language_progress, - speech::LanguageCode language_code) override { - } + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaError(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override; ash::ProjectorAppClient* const app_client_; ash::ProjectorController* const projector_controller_;
diff --git a/chrome/browser/ui/ash/projector/projector_soda_installation_controller_unittest.cc b/chrome/browser/ui/ash/projector/projector_soda_installation_controller_unittest.cc index e984ab5..824113b 100644 --- a/chrome/browser/ui/ash/projector/projector_soda_installation_controller_unittest.cc +++ b/chrome/browser/ui/ash/projector/projector_soda_installation_controller_unittest.cc
@@ -100,6 +100,8 @@ } MockSodaInstaller* soda_installer() { return soda_installer_.get(); } + speech::LanguageCode en_us() { return speech::LanguageCode::kEnUs; } + speech::LanguageCode fr_fr() { return speech::LanguageCode::kFrFr; } private: content::BrowserTaskEnvironment task_environment_; @@ -122,25 +124,21 @@ .WillByDefault( testing::Return(std::vector<std::string>({kEnglishLocale}))); - EXPECT_TRUE(soda_installation_controller()->ShouldDownloadSoda( - speech::LanguageCode::kEnUs)); + EXPECT_TRUE(soda_installation_controller()->ShouldDownloadSoda(en_us())); // Other languages other than English are not currently supported. - EXPECT_FALSE(soda_installation_controller()->ShouldDownloadSoda( - speech::LanguageCode::kFrFr)); + EXPECT_FALSE(soda_installation_controller()->ShouldDownloadSoda(fr_fr())); } TEST_F(ProjectorSodaInstallationControllerTest, IsSpeechRecognitionAvailable) { SetLocale(kEnglishLocale); - EXPECT_FALSE(soda_installation_controller()->IsSodaAvailable( - speech::LanguageCode::kEnUs)); + EXPECT_FALSE(soda_installation_controller()->IsSodaAvailable(en_us())); EXPECT_CALL(app_client(), OnSodaInstalled()).Times(1); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); - EXPECT_TRUE(soda_installation_controller()->IsSodaAvailable( - speech::LanguageCode::kEnUs)); - EXPECT_FALSE(soda_installation_controller()->IsSodaAvailable( - speech::LanguageCode::kFrFr)); + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us()); + EXPECT_TRUE(soda_installation_controller()->IsSodaAvailable(en_us())); + EXPECT_FALSE(soda_installation_controller()->IsSodaAvailable(fr_fr())); } TEST_F(ProjectorSodaInstallationControllerTest, InstallSoda) { @@ -153,13 +151,13 @@ EXPECT_CALL(app_client(), OnSodaInstalled()).Times(1); speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us()); } TEST_F(ProjectorSodaInstallationControllerTest, OnSodaInstallProgress) { SetLocale(kEnglishLocale); EXPECT_CALL(app_client(), OnSodaInstallProgress(50)).Times(1); - speech::SodaInstaller::GetInstance()->NotifySodaDownloadProgressForTesting( - 50); + speech::SodaInstaller::GetInstance()->NotifySodaProgressForTesting(50); } TEST_F(ProjectorSodaInstallationControllerTest, OnSodaInstallError) {
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h index f428c1d..0f4f2192 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h
@@ -52,7 +52,6 @@ @property(readonly, class) NSImage* starActiveIcon; @property(readonly, class) NSImage* navigateStopIcon; @property(readonly, class) NSImage* reloadIcon; -@property(readonly, class) NSString* homeItemIdentifier; // Returns the bridge object that BrowserWindowDefaultTouchBar uses to receive // notifications.
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm index 6685d2f..57b35c6 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm
@@ -283,13 +283,6 @@ // The starred button in the touch bar. base::scoped_nsobject<NSButton> _starredButton; - - // The last created BrowserWindowDefaultTouchBar (cached until it needs a - // rebuild). - base::scoped_nsobject<NSTouchBar> _touchBar; - - // The existence of the Home button in the Touch Bar. - bool _touchBarHasHomeButton; } // Creates and returns a touch bar for tab non-fullscreen mode. @@ -441,45 +434,37 @@ } - (NSTouchBar*)createTabTouchBar { - bool showHomeButton = _notificationBridge->show_home_button(); + base::scoped_nsobject<NSTouchBar> touchBar([[NSTouchBar alloc] init]); + [touchBar + setCustomizationIdentifier:ui::GetTouchBarId(kBrowserWindowTouchBarId)]; + [touchBar setDelegate:self]; - if (!_touchBar || _touchBarHasHomeButton != showHomeButton) { - _touchBar.reset([[NSTouchBar alloc] init]); - [_touchBar - setCustomizationIdentifier:ui::GetTouchBarId(kBrowserWindowTouchBarId)]; - [_touchBar setDelegate:self]; + NSMutableArray<NSString*>* customIdentifiers = [NSMutableArray array]; + NSMutableArray<NSString*>* defaultIdentifiers = [NSMutableArray array]; - NSMutableArray<NSString*>* customizationIdentifiers = - [NSMutableArray array]; - NSMutableArray<NSString*>* defaultIdentifiers = [NSMutableArray array]; + NSArray<NSString*>* touchBarItems = @[ + kBackTouchId, kForwardTouchId, kReloadOrStopTouchId, kHomeTouchId, + kSearchTouchId, kStarTouchId, kNewTabTouchId + ]; - NSArray<NSString*>* touchBarItemIdentifiers = @[ - kBackTouchId, kForwardTouchId, kReloadOrStopTouchId, kHomeTouchId, - kSearchTouchId, kStarTouchId, kNewTabTouchId - ]; + for (NSString* item in touchBarItems) { + NSString* itemIdentifier = + ui::GetTouchBarItemId(kBrowserWindowTouchBarId, item); + [customIdentifiers addObject:itemIdentifier]; - for (NSString* itemIdentifier in touchBarItemIdentifiers) { - NSString* fullIdentifier = - ui::GetTouchBarItemId(kBrowserWindowTouchBarId, itemIdentifier); - [customizationIdentifiers addObject:fullIdentifier]; + // Don't add the home button if it's not shown in the toolbar. + if (item == kHomeTouchId && !_notificationBridge->show_home_button()) + continue; - // Don't add the home button if it's not shown in the toolbar. - if (itemIdentifier == kHomeTouchId && !showHomeButton) { - continue; - } - - [defaultIdentifiers addObject:fullIdentifier]; - } - - [customizationIdentifiers addObject:NSTouchBarItemIdentifierFlexibleSpace]; - - [_touchBar setDefaultItemIdentifiers:defaultIdentifiers]; - [_touchBar setCustomizationAllowedItemIdentifiers:customizationIdentifiers]; - - _touchBarHasHomeButton = showHomeButton; + [defaultIdentifiers addObject:itemIdentifier]; } - return _touchBar.get(); + [customIdentifiers addObject:NSTouchBarItemIdentifierFlexibleSpace]; + + [touchBar setDefaultItemIdentifiers:defaultIdentifiers]; + [touchBar setCustomizationAllowedItemIdentifiers:customIdentifiers]; + + return touchBar.autorelease(); } - (NSTouchBar*)createTabFullscreenTouchBar { @@ -646,10 +631,6 @@ return _starDefaultIcon->get(); } -+ (NSString*)homeItemIdentifier { - return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kHomeTouchId); -} - + (NSImage*)starActiveIcon { static const base::NoDestructor<base::scoped_nsobject<NSImage>> _starActiveIcon([]() {
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm index 2d522b3ce..02144e4 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm
@@ -55,16 +55,6 @@ command_updater_->UpdateCommandEnabled(id, enabled); } - bool ShowsHomeButton() { - return browser()->profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton); - } - - void SetShowHomeButton(bool flag) { - browser()->profile()->GetPrefs()->SetBoolean(prefs::kShowHomeButton, flag); - browser()->profile()->GetPrefs()->ChangePrefValueStore(nullptr, nullptr, - nullptr, nullptr); - } - void TearDown() override { if (@available(macOS 10.12.2, *)) { touch_bar_.get().browser = nullptr; @@ -263,31 +253,3 @@ l10n_util::GetNSString(IDS_ACCNAME_FORWARD)); } } - -// Tests that the home button in the Touch Bar is in sync with the setting. -TEST_F(BrowserWindowDefaultTouchBarUnitTest, HomeUpdate) { - if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - - // Save the current state before we start mucking with preferences. - bool home_button_showing = ShowsHomeButton(); - - SetShowHomeButton(false); - touch_bar = [touch_bar_ makeTouchBar]; - - NSString* home_identifier = - [BrowserWindowDefaultTouchBar homeItemIdentifier]; - - EXPECT_FALSE( - [[touch_bar defaultItemIdentifiers] containsObject:home_identifier]); - - SetShowHomeButton(true); - touch_bar = [touch_bar_ makeTouchBar]; - - EXPECT_TRUE( - [[touch_bar defaultItemIdentifiers] containsObject:home_identifier]); - - // Restore the original state. - SetShowHomeButton(home_button_showing); - } -}
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial.cc b/chrome/browser/ui/user_education/tutorial/tutorial.cc index 4c2fc093..4f6b3a18 100644 --- a/chrome/browser/ui/user_education/tutorial/tutorial.cc +++ b/chrome/browser/ui/user_education/tutorial/tutorial.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/ui/user_education/help_bubble_params.h" #include "chrome/browser/ui/user_education/tutorial/tutorial_description.h" #include "chrome/browser/ui/user_education/tutorial/tutorial_service.h" +#include "components/vector_icons/vector_icons.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/interaction/element_identifier.h" #include "ui/base/interaction/element_tracker.h" @@ -196,6 +197,8 @@ if (!is_last_step_) { params.timeout = base::TimeDelta(); params.dismiss_callback = abort_callback; + } else { + params.body_icon = &vector_icons::kCelebrationIcon; } std::unique_ptr<HelpBubble> bubble =
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc index 45d4926..01ab3b18 100644 --- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc +++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -373,12 +373,21 @@ live_caption_button_->SetIsOn(enabled); } -void MediaDialogView::OnSodaInstalled() { +void MediaDialogView::OnSodaInstalled(speech::LanguageCode language_code) { + if (!prefs::IsLanguageCodeForLiveCaption(language_code, profile_->GetPrefs())) + return; speech::SodaInstaller::GetInstance()->RemoveObserver(this); live_caption_title_->SetText(GetLiveCaptionTitle(profile_->GetPrefs())); } -void MediaDialogView::OnSodaError() { +void MediaDialogView::OnSodaError(speech::LanguageCode language_code) { + // Check that language code matches the selected language for Live Caption or + // is LanguageCode::kNone (signifying the SODA binary failed). + if (!prefs::IsLanguageCodeForLiveCaption(language_code, + profile_->GetPrefs()) && + language_code != speech::LanguageCode::kNone) { + return; + } if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) { ToggleLiveCaption(false); } @@ -387,10 +396,17 @@ IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION_DOWNLOAD_ERROR)); } -void MediaDialogView::OnSodaProgress(int combined_progress) { +void MediaDialogView::OnSodaProgress(speech::LanguageCode language_code, + int progress) { + // Check that language code matches the selected language for Live Caption or + // is LanguageCode::kNone (signifying the SODA binary has progress). + if (!prefs::IsLanguageCodeForLiveCaption(language_code, + profile_->GetPrefs()) && + language_code != speech::LanguageCode::kNone) { + return; + } live_caption_title_->SetText(l10n_util::GetStringFUTF16Int( - IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION_DOWNLOAD_PROGRESS, - combined_progress)); + IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION_DOWNLOAD_PROGRESS, progress)); } std::unique_ptr<global_media_controls::MediaItemUIView>
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h index a46b09a..a50139a 100644 --- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h +++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
@@ -127,9 +127,10 @@ void UpdateBubbleSize(); // SodaInstaller::Observer overrides: - void OnSodaInstalled() override; - void OnSodaError() override; - void OnSodaProgress(int combined_progress) override; + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaError(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override; std::unique_ptr<global_media_controls::MediaItemUIView> BuildMediaItemUIView( const std::string& id,
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc index c995529..62d2701 100644 --- a/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc +++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view_interactive_browsertest.cc
@@ -538,7 +538,7 @@ } void OnSodaProgress(int progress) { - speech::SodaInstaller::GetInstance()->NotifySodaDownloadProgressForTesting( + speech::SodaInstaller::GetInstance()->NotifySodaProgressForTesting( progress); } @@ -547,9 +547,8 @@ } void OnSodaLanguagePackInstalled() { - speech::SodaInstaller::GetInstance() - ->NotifyOnSodaLanguagePackInstalledForTesting( - speech::LanguageCode::kEnUs); + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting( + speech::LanguageCode::kEnUs); } protected:
diff --git a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc index 49aa53ec..ba34eba 100644 --- a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc +++ b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
@@ -4,6 +4,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -170,7 +171,13 @@ }; // Test payment request methods are not supported by the payment app. -IN_PROC_BROWSER_TEST_F(PaymentRequestPaymentAppTest, NotSupportedError) { +// Flaky on Linux: http://crbug.com/1296289 +#if BUILDFLAG(IS_LINUX) +#define MAYBE_NotSupportedError DISABLED_NotSupportedError +#else +#define MAYBE_NotSupportedError NotSupportedError +#endif +IN_PROC_BROWSER_TEST_F(PaymentRequestPaymentAppTest, MAYBE_NotSupportedError) { InstallAlicePayForMethod("https://frankpay.com"); {
diff --git a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc index c80cb7bc..f8ef118f7 100644 --- a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc +++ b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
@@ -32,8 +32,9 @@ // "REPLACE_WITH_HOST_AND_PORT" string replaced with |host_port_pair|. // The page at |original_path| should contain the string // "REPLACE_WITH_HOST_AND_PORT". -std::string GetPathWithHostAndPortReplaced(const std::string& original_path, - net::HostPortPair host_port_pair) { +std::string GetPathWithHostAndPortReplaced( + const std::string& original_path, + const net::HostPortPair& host_port_pair) { base::StringPairs replacement_text = { {"REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString()}}; LOG(ERROR) << "host_port_pair.ToString() " << host_port_pair.ToString();
diff --git a/chrome/browser/ui/web_applications/sub_apps_service_impl.cc b/chrome/browser/ui/web_applications/sub_apps_service_impl.cc index cdfcb03..794accf 100644 --- a/chrome/browser/ui/web_applications/sub_apps_service_impl.cc +++ b/chrome/browser/ui/web_applications/sub_apps_service_impl.cc
@@ -45,7 +45,7 @@ // *always* has the same *origin* as the calling app (normally the renderer // should only send the path, but a compromised renderer might send a full URL // instead and we guard against that here). -GURL ResolvePathWithOrigin(const std::string& path, GURL origin) { +GURL ResolvePathWithOrigin(const std::string& path, const GURL& origin) { return origin.Resolve(origin.Resolve(path).PathForRequest()); }
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc index 74b2897..cca15a3 100644 --- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc +++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -75,7 +75,7 @@ namespace web_app { absl::optional<SystemAppType> GetSystemWebAppTypeForAppId(Profile* profile, - AppId app_id) { + const AppId& app_id) { auto* provider = WebAppProvider::GetForSystemWebApps(profile); return provider ? provider->system_web_app_manager().GetSystemAppTypeForAppId( app_id)
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.h b/chrome/browser/ui/web_applications/system_web_app_ui_utils.h index a0ca5100..faeb754 100644 --- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.h +++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.h
@@ -25,7 +25,7 @@ // Returns the system app type for the given App ID. absl::optional<SystemAppType> GetSystemWebAppTypeForAppId(Profile* profile, - AppId app_id); + const AppId& app_id); // Returns the PWA system App ID for the given system app type. absl::optional<AppId> GetAppIdForSystemWebApp(Profile* profile,
diff --git a/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc b/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc index a3f2537..f1e557d2 100644 --- a/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_badging_browsertest.cc
@@ -164,7 +164,7 @@ protected: // Expects a single badge change only. - void ExecuteScriptAndWaitForBadgeChange(std::string script, + void ExecuteScriptAndWaitForBadgeChange(const std::string& script, RenderFrameHost* on) { ExecuteScriptAndWaitForMultipleBadgeChanges( script, on, /*expected_badge_change_count=*/1); @@ -173,7 +173,7 @@ // Handles badge changes that may affect multiple apps. Useful for testing // service workers, which can control many apps. void ExecuteScriptAndWaitForMultipleBadgeChanges( - std::string script, + const std::string& script, RenderFrameHost* on, size_t expected_badge_change_count) { expected_badge_change_count_ = expected_badge_change_count;
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc index 64642a4..b18cb05 100644 --- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -102,7 +102,7 @@ } void InstallTestApp(GURL start_url, bool await_metric) { - start_url_ = start_url; + start_url_ = std::move(start_url); in_scope_1_ = start_url_.Resolve("page1.html"); in_scope_2_ = start_url_.Resolve("page2.html"); scope_ = start_url_.GetWithoutFilename(); @@ -136,7 +136,7 @@ return *provider; } - void AddTab(Browser* browser, GURL url) { + void AddTab(Browser* browser, const GURL& url) { content::TestNavigationObserver observer(url); observer.StartWatchingNewWebContents(); chrome::AddTabAt(browser, url, /*index=*/-1, /*foreground=*/true);
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 89ed6c60..e23b58f 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -531,6 +531,15 @@ } } +void BindEcheDisplayStreamHandler( + ash::eche_app::EcheAppManager* manager, + mojo::PendingReceiver<ash::eche_app::mojom::DisplayStreamHandler> + receiver) { + if (manager) { + manager->BindDisplayStreamHandlerInterface(std::move(receiver)); + } +} + template <> WebUIController* NewWebUI<ash::eche_app::EcheAppUI>(WebUI* web_ui, const GURL& url) { @@ -541,7 +550,8 @@ web_ui, base::BindRepeating(&BindEcheSignalingMessageExchanger, manager), base::BindRepeating(&BindSystemInfoProvider, manager), base::BindRepeating(&BindEcheUidGenerator, manager), - base::BindRepeating(&BindEcheNotificationGenerator, manager)); + base::BindRepeating(&BindEcheNotificationGenerator, manager), + base::BindRepeating(&BindEcheDisplayStreamHandler, manager)); } void BindScanService(
diff --git a/chrome/browser/ui/webui/settings/captions_handler.cc b/chrome/browser/ui/webui/settings/captions_handler.cc index d4d40db..ccdbe8e 100644 --- a/chrome/browser/ui/webui/settings/captions_handler.cc +++ b/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -73,70 +73,78 @@ #endif } -void CaptionsHandler::OnSodaInstalled() { +void CaptionsHandler::OnSodaInstalled(speech::LanguageCode language_code) { if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage) && soda_available_) { + // If multi-language is disabled and the language code received is not for + // Live Caption (perhaps it is downloading because another feature, such as + // dictation on ChromeOS, has a different language selected), then return + // early. We do not check for a matching language if multi-language is + // enabled because we show all of the languages' download status in the UI, + // even ones that are not currently selected. + if (!prefs::IsLanguageCodeForLiveCaption(language_code, prefs_)) + return; speech::SodaInstaller::GetInstance()->RemoveObserver(this); } FireWebUIListener("soda-download-progress-changed", base::Value(l10n_util::GetStringUTF16( - IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_COMPLETE))); -} - -void CaptionsHandler::OnSodaLanguagePackInstalled( - speech::LanguageCode language_code) { - if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) - return; - - FireWebUIListener("soda-download-progress-changed", - base::Value(l10n_util::GetStringUTF16( IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_COMPLETE)), base::Value(speech::GetLanguageName(language_code))); } -void CaptionsHandler::OnSodaError() { +void CaptionsHandler::OnSodaError(speech::LanguageCode language_code) { if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) { + // If multi-language is disabled and the language code received is not for + // Live Caption (perhaps it is downloading because another feature, such as + // dictation on ChromeOS, has a different language selected), then return + // early. We do not check for a matching language if multi-language is + // enabled because we show all of the languages' download status in the UI, + // even ones that are not currently selected. + // Check that language code matches the selected language for Live Caption + // or is LanguageCode::kNone (signifying the SODA binary failed). + if (!prefs::IsLanguageCodeForLiveCaption(language_code, prefs_) && + language_code != speech::LanguageCode::kNone) { + return; + } prefs_->SetBoolean(prefs::kLiveCaptionEnabled, false); } FireWebUIListener("soda-download-progress-changed", base::Value(l10n_util::GetStringUTF16( IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_ERROR)), - base::Value()); -} - -void CaptionsHandler::OnSodaLanguagePackError( - speech::LanguageCode language_code) { - if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) - return; - - prefs_->SetBoolean(prefs::kLiveCaptionEnabled, false); - FireWebUIListener("soda-download-progress-changed", - base::Value(l10n_util::GetStringUTF16( - IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_ERROR)), base::Value(speech::GetLanguageName(language_code))); } -void CaptionsHandler::OnSodaProgress(int combined_progress) { - FireWebUIListener("soda-download-progress-changed", - base::Value(l10n_util::GetStringFUTF16Int( - IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS, - combined_progress)), - base::Value()); -} - -void CaptionsHandler::OnSodaLanguagePackProgress( - int language_progress, - speech::LanguageCode language_code) { - if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) - return; - - FireWebUIListener("soda-download-progress-changed", - base::Value(l10n_util::GetStringFUTF16Int( - IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS, - language_progress)), - base::Value(speech::GetLanguageName(language_code))); +void CaptionsHandler::OnSodaProgress(speech::LanguageCode language_code, + int progress) { + if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage) && + soda_available_) { + // If multi-language is disabled and the language code received is not for + // Live Caption (perhaps it is downloading because another feature, such as + // dictation on ChromeOS, has a different language selected), then return + // early. We do not check for a matching language if multi-language is + // enabled because we show all of the languages' download status in the UI, + // even ones that are not currently selected. + // Check that language code matches the selected language for Live Caption + // or is LanguageCode::kNone (signifying the SODA binary progress). + if (!prefs::IsLanguageCodeForLiveCaption(language_code, prefs_) && + language_code != speech::LanguageCode::kNone) { + return; + } + } + // If the language code is kNone, this means that only the SODA binary has + // begun downloading. Therefore we pass the Live Caption language along to the + // WebUI, since that is the language which will begin downloading. + if (language_code == speech::LanguageCode::kNone) { + language_code = + speech::GetLanguageCode(prefs::GetLiveCaptionLanguageCode(prefs_)); + } + FireWebUIListener( + "soda-download-progress-changed", + base::Value(l10n_util::GetStringFUTF16Int( + IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS, progress)), + base::Value(speech::GetLanguageName(language_code))); } } // namespace settings
diff --git a/chrome/browser/ui/webui/settings/captions_handler.h b/chrome/browser/ui/webui/settings/captions_handler.h index e353336d..421951d 100644 --- a/chrome/browser/ui/webui/settings/captions_handler.h +++ b/chrome/browser/ui/webui/settings/captions_handler.h
@@ -33,13 +33,10 @@ void HandleOpenSystemCaptionsDialog(const base::Value::List& args); // SodaInstaller::Observer overrides: - void OnSodaInstalled() override; - void OnSodaLanguagePackInstalled(speech::LanguageCode language_code) override; - void OnSodaError() override; - void OnSodaLanguagePackError(speech::LanguageCode language_code) override; - void OnSodaProgress(int combined_progress) override; - void OnSodaLanguagePackProgress(int language_progress, - speech::LanguageCode language_code) override; + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaError(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override; raw_ptr<PrefService> prefs_; bool soda_available_ = true;
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc index 8753b90..c2baa56c 100644 --- a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
@@ -162,11 +162,10 @@ } } -void AccessibilityHandler::OnSodaInstallSucceeded() { - if (!speech::SodaInstaller::GetInstance()->IsSodaInstalled( - GetDictationLocale())) { +// SodaInstaller::Observer: +void AccessibilityHandler::OnSodaInstalled(speech::LanguageCode language_code) { + if (language_code != GetDictationLocale()) return; - } // Only show the success message if both the SODA binary and the language pack // matching the Dictation locale have been downloaded. @@ -177,16 +176,15 @@ GetDictationLocaleDisplayName()))); } -void AccessibilityHandler::OnSodaInstallProgress( - int progress, - speech::LanguageCode language_code) { - // TODO(https://crbug.com/1266491): Ensure we use combined progress instead - // of just the language pack progress. - if (language_code != GetDictationLocale()) +void AccessibilityHandler::OnSodaProgress(speech::LanguageCode language_code, + int progress) { + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLocale()) { return; + } - // Only show the progress message if this applies to the language pack - // matching the Dictation locale. + // Only show the progress message if either the Dictation locale or the SODA + // binary has progress (encoded by LanguageCode::kNone). FireWebUIListener( "dictation-locale-menu-subtitle-changed", base::Value(l10n_util::GetStringFUTF16Int( @@ -194,43 +192,19 @@ progress))); } -void AccessibilityHandler::OnSodaInstallFailed( - speech::LanguageCode language_code) { - if (language_code == speech::LanguageCode::kNone || - language_code == GetDictationLocale()) { - // Show the failed message if either the Dictation locale failed or the SODA - // binary failed (encoded by LanguageCode::kNone). - FireWebUIListener( - "dictation-locale-menu-subtitle-changed", - base::Value(l10n_util::GetStringFUTF16( - IDS_SETTINGS_ACCESSIBILITY_DICTATION_SUBTITLE_SODA_DOWNLOAD_ERROR, - GetDictationLocaleDisplayName()))); +void AccessibilityHandler::OnSodaError(speech::LanguageCode language_code) { + if (language_code != speech::LanguageCode::kNone && + language_code != GetDictationLocale()) { + return; } -} -// SodaInstaller::Observer: -void AccessibilityHandler::OnSodaInstalled() { - OnSodaInstallSucceeded(); -} - -void AccessibilityHandler::OnSodaLanguagePackInstalled( - speech::LanguageCode language_code) { - OnSodaInstallSucceeded(); -} - -void AccessibilityHandler::OnSodaLanguagePackProgress( - int language_progress, - speech::LanguageCode language_code) { - OnSodaInstallProgress(language_progress, language_code); -} - -void AccessibilityHandler::OnSodaError() { - OnSodaInstallFailed(speech::LanguageCode::kNone); -} - -void AccessibilityHandler::OnSodaLanguagePackError( - speech::LanguageCode language_code) { - OnSodaInstallFailed(language_code); + // Show the failed message if either the Dictation locale failed or the SODA + // binary failed (encoded by LanguageCode::kNone). + FireWebUIListener( + "dictation-locale-menu-subtitle-changed", + base::Value(l10n_util::GetStringFUTF16( + IDS_SETTINGS_ACCESSIBILITY_DICTATION_SUBTITLE_SODA_DOWNLOAD_ERROR, + GetDictationLocaleDisplayName()))); } void AccessibilityHandler::MaybeAddDictationLocales() {
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h index abaeb2b..b64f767b 100644 --- a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h +++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h
@@ -48,18 +48,12 @@ void OpenExtensionOptionsPage(const char extension_id[]); void MaybeAddSodaInstallerObserver(); - void OnSodaInstallSucceeded(); - void OnSodaInstallProgress(int progress, speech::LanguageCode language_code); - void OnSodaInstallFailed(speech::LanguageCode language_code); // SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaLanguagePackInstalled(speech::LanguageCode language_code) override; - void OnSodaProgress(int progress) override {} - void OnSodaLanguagePackProgress(int language_progress, - speech::LanguageCode language_code) override; - void OnSodaError() override; - void OnSodaLanguagePackError(speech::LanguageCode language_code) override; + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override; + void OnSodaError(speech::LanguageCode language_code) override; void MaybeAddDictationLocales(); speech::LanguageCode GetDictationLocale();
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc index 9377193..7996c4b 100644 --- a/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc +++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_browsertest.cc
@@ -151,9 +151,9 @@ // correct language pack before doing anything. soda_installer()->NotifySodaInstalledForTesting(); AssertWebUICalls(num_calls); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(en_us()); + soda_installer()->NotifySodaInstalledForTesting(en_us()); AssertWebUICalls(num_calls); - soda_installer()->NotifyOnSodaLanguagePackInstalledForTesting(fr_fr()); + soda_installer()->NotifySodaInstalledForTesting(fr_fr()); AssertWebUICalls(num_calls + 1); ASSERT_TRUE(WasWebUIListenerCalledWithStringArgument( "dictation-locale-menu-subtitle-changed", @@ -165,13 +165,12 @@ // the Dictation locale. IN_PROC_BROWSER_TEST_F(AccessibilityHandlerTest, OnSodaProgressNotification) { size_t num_calls = GetNumWebUICalls(); - // Do not give updates for the SODA binary. - soda_installer()->NotifySodaDownloadProgressForTesting(50); + soda_installer()->NotifySodaProgressForTesting(50, fr_fr()); AssertWebUICalls(num_calls); - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(50, fr_fr()); - AssertWebUICalls(num_calls); - soda_installer()->NotifyOnSodaLanguagePackProgressForTesting(50, en_us()); + soda_installer()->NotifySodaProgressForTesting(50, en_us()); AssertWebUICalls(num_calls + 1); + soda_installer()->NotifySodaProgressForTesting(50); + AssertWebUICalls(num_calls + 2); ASSERT_TRUE(WasWebUIListenerCalledWithStringArgument( "dictation-locale-menu-subtitle-changed", "Downloading speech recognition files… 50%")); @@ -197,11 +196,11 @@ size_t num_calls = GetNumWebUICalls(); // Do nothing if the failed language pack is different than the Dictation // locale. - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(fr_fr()); + soda_installer()->NotifySodaErrorForTesting(fr_fr()); AssertWebUICalls(num_calls); // Fire the correct listener when the language pack matching the Dictation // locale fails. - soda_installer()->NotifyOnSodaLanguagePackErrorForTesting(en_us()); + soda_installer()->NotifySodaErrorForTesting(en_us()); AssertWebUICalls(num_calls + 1); ASSERT_TRUE(WasWebUIListenerCalledWithStringArgument( "dictation-locale-menu-subtitle-changed", @@ -285,6 +284,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityHandlerTest, DictationLocalesOfflineAndInstalled) { speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(); + speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(en_us()); MaybeAddDictationLocales(); base::Value::ConstListView argument; ASSERT_TRUE(
diff --git a/chrome/browser/ui/webui/whats_new/whats_new_util.cc b/chrome/browser/ui/webui/whats_new/whats_new_util.cc index 99ca051..4b01269 100644 --- a/chrome/browser/ui/webui/whats_new/whats_new_util.cc +++ b/chrome/browser/ui/webui/whats_new/whats_new_util.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/check.h" +#include "base/command_line.h" #include "base/feature_list.h" #include "base/location.h" #include "base/memory/raw_ptr.h" @@ -23,6 +24,7 @@ #include "chrome/browser/ui/browser_list_observer.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/ui_features.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version.h" #include "chrome/common/pref_names.h" #include "chrome/common/webui_url_constants.h" @@ -53,9 +55,19 @@ } bool ShouldShowForState(PrefService* local_state) { - if (!local_state) + if (!local_state || !local_state->FindPreference(prefs::kLastWhatsNewVersion)) return false; + // Allow disabling the What's New experience in tests using the standard + // kNoFirstRun switch. This behavior can be overridden using the + // kForceWhatsNew switch for the What's New experience integration tests. + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kNoFirstRun) && + !command_line->HasSwitch(switches::kForceWhatsNew)) { + return false; + } + if (!base::FeatureList::IsEnabled(features::kChromeWhatsNewUI)) return false;
diff --git a/chrome/browser/web_applications/alternative_error_page_override_info_browsertest.cc b/chrome/browser/web_applications/alternative_error_page_override_info_browsertest.cc index 7247909..bce8375 100644 --- a/chrome/browser/web_applications/alternative_error_page_override_info_browsertest.cc +++ b/chrome/browser/web_applications/alternative_error_page_override_info_browsertest.cc
@@ -43,7 +43,7 @@ // Helper function to prepare PWA and retrieve information from the // alternative error page function. content::mojom::AlternativeErrorPageOverrideInfoPtr GetErrorPageInfo( - std::string html) { + base::StringPiece html) { ChromeContentBrowserClient browser_client; content::ScopedContentBrowserClientSetting setting(&browser_client);
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc index 4180d1b56..6823c84 100644 --- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc +++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -406,7 +406,7 @@ void WebAppPublisherHelper::PopulateWebAppPermissions( const WebApp* web_app, std::vector<apps::mojom::PermissionPtr>* target) { - const GURL url = web_app->start_url(); + const GURL& url = web_app->start_url(); auto* host_content_settings_map = HostContentSettingsMapFactory::GetForProfile(profile()); @@ -450,7 +450,7 @@ const WebApp* web_app) { apps::Permissions permissions; - const GURL url = web_app->start_url(); + const GURL& url = web_app->start_url(); auto* host_content_settings_map = HostContentSettingsMapFactory::GetForProfile(profile()); DCHECK(host_content_settings_map);
diff --git a/chrome/browser/web_applications/app_service/web_apps.cc b/chrome/browser/web_applications/app_service/web_apps.cc index f201719..c0dbec63 100644 --- a/chrome/browser/web_applications/app_service/web_apps.cc +++ b/chrome/browser/web_applications/app_service/web_apps.cc
@@ -245,7 +245,8 @@ } std::vector<apps::mojom::AppPtr> mojom_apps; - for (apps::AppPtr& app : apps) { + mojom_apps.reserve(apps.size()); + for (const apps::AppPtr& app : apps) { mojom_apps.push_back(apps::ConvertAppToMojomApp(app)); } @@ -262,6 +263,7 @@ } for (auto& subscriber : subscribers_) { std::vector<apps::mojom::AppPtr> cloned_apps; + cloned_apps.reserve(mojom_apps.size()); for (const auto& app : mojom_apps) cloned_apps.push_back(app.Clone()); subscriber->OnApps(std::move(cloned_apps),
diff --git a/chrome/browser/web_applications/daily_metrics_helper.cc b/chrome/browser/web_applications/daily_metrics_helper.cc index 45addac2..a5fb112 100644 --- a/chrome/browser/web_applications/daily_metrics_helper.cc +++ b/chrome/browser/web_applications/daily_metrics_helper.cc
@@ -35,7 +35,7 @@ // This class exists just to be friended by |UkmRecorder|. class DesktopWebAppUkmRecorder { public: - static void Emit(DailyInteraction record) { + static void Emit(const DailyInteraction& record) { DCHECK(record.start_url.is_valid()); ukm::SourceId source_id = ukm::UkmRecorder::GetSourceIdForDesktopWebAppStartUrl(record.start_url); @@ -153,7 +153,7 @@ url::Origin origin = url::Origin::Create(record.start_url); // Ensure origin is still in the history before emitting. ukm_background_service->GetBackgroundSourceIdIfAllowed( - origin, base::BindOnce(&EmitIfSourceIdExists, record)); + origin, base::BindOnce(&EmitIfSourceIdExists, std::move(record))); } void EmitRecords(Profile* profile) { @@ -206,7 +206,8 @@ } // namespace DailyInteraction::DailyInteraction() = default; -DailyInteraction::DailyInteraction(GURL start_url) : start_url(start_url) {} +DailyInteraction::DailyInteraction(GURL start_url) + : start_url(std::move(start_url)) {} DailyInteraction::DailyInteraction(const DailyInteraction&) = default; DailyInteraction::~DailyInteraction() = default;
diff --git a/chrome/browser/web_applications/external_install_options.cc b/chrome/browser/web_applications/external_install_options.cc index ad4b2ac..7b49eb7 100644 --- a/chrome/browser/web_applications/external_install_options.cc +++ b/chrome/browser/web_applications/external_install_options.cc
@@ -89,7 +89,7 @@ base::Value ExternalInstallOptions::AsDebugValue() const { base::Value root(base::Value::Type::DICTIONARY); - auto ConvertStringList = [](const std::vector<std::string> list) { + auto ConvertStringList = [](const std::vector<std::string>& list) { base::Value list_json(base::Value::Type::LIST); for (const std::string& item : list) list_json.Append(item);
diff --git a/chrome/browser/web_applications/externally_managed_app_manager.cc b/chrome/browser/web_applications/externally_managed_app_manager.cc index 229716f..6a26848 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager.cc
@@ -92,7 +92,8 @@ DCHECK(!base::Contains(synchronize_requests_, install_source)); std::vector<GURL> installed_urls; - for (auto apps_it : registrar_->GetExternallyInstalledApps(install_source)) { + for (const auto& apps_it : + registrar_->GetExternallyInstalledApps(install_source)) { // TODO: Remove this check once we cleanup ExternallyInstalledWebAppPrefs on // external app uninstall. // https://crbug.com/1300382 @@ -108,6 +109,7 @@ std::sort(installed_urls.begin(), installed_urls.end()); std::vector<GURL> desired_urls; + desired_urls.reserve(desired_apps_install_options.size()); for (const auto& info : desired_apps_install_options) desired_urls.push_back(info.install_url); @@ -146,7 +148,7 @@ void ExternallyManagedAppManager::SetRegistrationCallbackForTesting( RegistrationCallback callback) { - registration_callback_ = callback; + registration_callback_ = std::move(callback); } void ExternallyManagedAppManager::ClearRegistrationCallbackForTesting() { @@ -178,7 +180,7 @@ auto source_and_request = synchronize_requests_.find(source); DCHECK(source_and_request != synchronize_requests_.end()); SynchronizeRequest& request = source_and_request->second; - request.install_results[app_url] = result; + request.install_results[app_url] = std::move(result); --request.remaining_install_requests; DCHECK_GE(request.remaining_install_requests, 0);
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc index 1cf617d..0fcce091 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc
@@ -50,7 +50,7 @@ using UninstallAppsResults = std::vector<std::pair<GURL, bool>>; ExternalInstallOptions GetInstallOptions( - GURL url, + const GURL& url, absl::optional<bool> override_previous_user_uninstall = absl::optional<bool>()) { ExternalInstallOptions options(std::move(url), DisplayMode::kBrowser, @@ -281,7 +281,7 @@ externally_managed_app_manager_impl->ui_manager(), externally_managed_app_manager_impl->finalizer(), externally_managed_app_manager_impl->install_manager(), - install_options), + std::move(install_options)), externally_managed_app_manager_impl_( externally_managed_app_manager_impl), externally_installed_app_prefs_(profile->GetPrefs()), @@ -366,7 +366,7 @@ ~TestExternallyManagedAppRegistrationTask() override = default; private: - void OnProgress(GURL install_url) { + void OnProgress(const GURL& install_url) { if (externally_managed_app_manager_impl_->MaybePreemptRegistration()) return; externally_managed_app_manager_impl_->OnRegistrationFinished(
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc b/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc index 47e1023..5d48555 100644 --- a/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc +++ b/chrome/browser/web_applications/externally_managed_app_manager_unittest.cc
@@ -79,10 +79,11 @@ externally_managed_app_manager_.reset(); } - void Sync(std::vector<GURL> urls) { + void Sync(const std::vector<GURL>& urls) { ResetCounts(); std::vector<ExternalInstallOptions> install_options_list; + install_options_list.reserve(urls.size()); for (const auto& url : urls) { install_options_list.emplace_back( url, DisplayMode::kStandalone, @@ -104,13 +105,14 @@ void Expect(int deduped_install_count, int deduped_uninstall_count, - std::vector<GURL> installed_app_urls) { + const std::vector<GURL>& installed_app_urls) { EXPECT_EQ(deduped_install_count, deduped_install_count_); EXPECT_EQ(deduped_uninstall_count, deduped_uninstall_count_); std::map<AppId, GURL> apps = app_registrar().GetExternallyInstalledApps( ExternalInstallSource::kInternalDefault); std::vector<GURL> urls; - for (auto it : apps) + urls.reserve(apps.size()); + for (const auto& it : apps) urls.push_back(it.second); std::sort(urls.begin(), urls.end());
diff --git a/chrome/browser/web_applications/isolated_app_browsertest.cc b/chrome/browser/web_applications/isolated_app_browsertest.cc index 470fd4a..485e8c25 100644 --- a/chrome/browser/web_applications/isolated_app_browsertest.cc +++ b/chrome/browser/web_applications/isolated_app_browsertest.cc
@@ -311,7 +311,7 @@ } std::string GetHeader(const net::test_server::HttpRequest& request, - std::string header_name) { + const std::string& header_name) { auto header = request.headers.find(header_name); return header != request.headers.end() ? header->second : ""; }
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc index 77229e10..5abc156a 100644 --- a/chrome/browser/web_applications/web_app.cc +++ b/chrome/browser/web_applications/web_app.cc
@@ -408,6 +408,9 @@ WebApp::SyncFallbackData::SyncFallbackData( const SyncFallbackData& sync_fallback_data) = default; +WebApp::SyncFallbackData::SyncFallbackData( + SyncFallbackData&& sync_fallback_data) noexcept = default; + WebApp::SyncFallbackData& WebApp::SyncFallbackData::operator=( SyncFallbackData&& sync_fallback_data) = default;
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h index 3601625..320f9dd 100644 --- a/chrome/browser/web_applications/web_app.h +++ b/chrome/browser/web_applications/web_app.h
@@ -202,6 +202,7 @@ ~SyncFallbackData(); // Copyable and move-assignable to support Copy-on-Write with Commit. SyncFallbackData(const SyncFallbackData& sync_fallback_data); + SyncFallbackData(SyncFallbackData&& sync_fallback_data) noexcept; SyncFallbackData& operator=(SyncFallbackData&& sync_fallback_data); base::Value AsDebugValue() const;
diff --git a/chrome/browser/web_applications/web_app_icon_generator.cc b/chrome/browser/web_applications/web_app_icon_generator.cc index e61cdbf..03d34cd 100644 --- a/chrome/browser/web_applications/web_app_icon_generator.cc +++ b/chrome/browser/web_applications/web_app_icon_generator.cc
@@ -105,7 +105,7 @@ (*bitmaps)[output_size] = GenerateBitmap(output_size, color, icon_letter); } -void GenerateIcons(std::set<SquareSizePx> generate_sizes, +void GenerateIcons(const std::set<SquareSizePx>& generate_sizes, char16_t icon_letter, SkColor generated_icon_color, SizeToBitmap* bitmap_map) {
diff --git a/chrome/browser/web_applications/web_app_icon_generator_unittest.cc b/chrome/browser/web_applications/web_app_icon_generator_unittest.cc index bca4745..77ee106 100644 --- a/chrome/browser/web_applications/web_app_icon_generator_unittest.cc +++ b/chrome/browser/web_applications/web_app_icon_generator_unittest.cc
@@ -87,11 +87,12 @@ return bitmap_vector.end(); } -void ValidateIconsGeneratedAndResizedCorrectly(std::vector<SkBitmap> downloaded, - SizeToBitmap size_map, - std::set<int> sizes_to_generate, - int expected_generated, - int expected_resized) { +void ValidateIconsGeneratedAndResizedCorrectly( + const std::vector<SkBitmap>& downloaded, + const SizeToBitmap& size_map, + const std::set<int>& sizes_to_generate, + int expected_generated, + int expected_resized) { GURL empty_url(""); int number_generated = 0; int number_resized = 0; @@ -134,7 +135,9 @@ EXPECT_EQ(expected_resized, number_resized); } -void ValidateBitmapSizeAndColor(SkBitmap bitmap, int size, SkColor color) { +void ValidateBitmapSizeAndColor(const SkBitmap& bitmap, + int size, + SkColor color) { // Obtain pixel lock to access pixels. EXPECT_EQ(color, bitmap.getColor(0, 0)); EXPECT_EQ(size, bitmap.width());
diff --git a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc index 823efe96..5fb827f 100644 --- a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc +++ b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
@@ -115,8 +115,8 @@ // Synchronous version of FinalizeInstall. FinalizeInstallResult AwaitFinalizeInstall( - WebAppInstallInfo info, - WebAppInstallFinalizer::FinalizeOptions options) { + const WebAppInstallInfo& info, + const WebAppInstallFinalizer::FinalizeOptions& options) { FinalizeInstallResult result{}; base::RunLoop run_loop; finalizer().FinalizeInstall(
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc index ce6c7a5..4fbb77f 100644 --- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc +++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -359,7 +359,7 @@ std::map<SquareSizePx, SkBitmap> ReadIcons(const AppId& app_id, IconPurpose purpose, - SortedSizesPx sizes_px) { + const SortedSizesPx& sizes_px) { std::map<SquareSizePx, SkBitmap> result; base::RunLoop run_loop; icon_manager().ReadIcons(
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc index e46b38b2..5e2b2b3 100644 --- a/chrome/browser/web_applications/web_app_install_task_unittest.cc +++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -166,8 +166,8 @@ } void CreateRendererAppInfo(const GURL& url, - const std::string name, - const std::string description, + const std::string& name, + const std::string& description, const GURL& scope, absl::optional<SkColor> theme_color, DisplayMode user_display_mode) { @@ -184,8 +184,8 @@ } void CreateRendererAppInfo(const GURL& url, - const std::string name, - const std::string description) { + const std::string& name, + const std::string& description) { CreateRendererAppInfo(url, name, description, GURL(), absl::nullopt, /*user_display_mode=*/DisplayMode::kStandalone); } @@ -403,8 +403,8 @@ ~WebAppInstallTaskWithRunOnOsLoginTest() override = default; void CreateRendererAppInfo(const GURL& url, - const std::string name, - const std::string description, + const std::string& name, + const std::string& description, const GURL& scope, absl::optional<SkColor> theme_color, DisplayMode user_display_mode) { @@ -1496,12 +1496,12 @@ // Installs the app and validates |final_web_app_info| matches the args passed // in. InstallResult InstallWebAppWithShortcutsMenuValidateAndGetResults( - GURL start_url, + const GURL& start_url, SkColor theme_color, - std::string shortcut_name, - GURL shortcut_url, + const std::string& shortcut_name, + const GURL& shortcut_url, SquareSizePx icon_size, - GURL icon_src) { + const GURL& icon_src) { InstallResult result; auto manifest = blink::mojom::Manifest::New(); manifest->start_url = start_url; @@ -1573,12 +1573,12 @@ // Updates the app and validates |final_web_app_info| matches the args passed // in. InstallResult UpdateWebAppWithShortcutsMenuValidateAndGetResults( - GURL url, + const GURL& url, SkColor theme_color, - std::string shortcut_name, - GURL shortcut_url, + const std::string& shortcut_name, + const GURL& shortcut_url, SquareSizePx icon_size, - GURL icon_src) { + const GURL& icon_src) { InstallResult result; const AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, url);
diff --git a/chrome/browser/web_applications/web_app_offline_browsertest.cc b/chrome/browser/web_applications/web_app_offline_browsertest.cc index 580f3f1..d4576897 100644 --- a/chrome/browser/web_applications/web_app_offline_browsertest.cc +++ b/chrome/browser/web_applications/web_app_offline_browsertest.cc
@@ -37,7 +37,7 @@ public: // Start a web app without a service worker and disconnect. void StartWebAppAndDisconnect(content::WebContents* web_contents, - std::string relative_url) { + base::StringPiece relative_url) { GURL target_url(embedded_test_server()->GetURL(relative_url)); web_app::NavigateToURLAndWait(browser(), target_url); web_app::AppId app_id = web_app::test::InstallPwaForCurrentUrl(browser()); @@ -53,7 +53,7 @@ // Start a PWA with a service worker and disconnect. void StartPwaAndDisconnect(content::WebContents* web_contents, - std::string relative_url) { + base::StringPiece relative_url) { GURL target_url(embedded_test_server()->GetURL(relative_url)); web_app::ServiceWorkerRegistrationWaiter registration_waiter( browser()->profile(), target_url);
diff --git a/chrome/browser/web_applications/web_app_origin_association_task.cc b/chrome/browser/web_applications/web_app_origin_association_task.cc index e63dcdd..4d900120 100644 --- a/chrome/browser/web_applications/web_app_origin_association_task.cc +++ b/chrome/browser/web_applications/web_app_origin_association_task.cc
@@ -26,7 +26,7 @@ // count towards kMaxPathsSize. std::vector<std::string> GetValidPaths(std::vector<std::string> paths) { base::flat_set<std::string> result; - for (const std::string& path : paths) { + for (std::string& path : paths) { if (result.size() == kMaxPathsSize) break; @@ -93,7 +93,7 @@ } owner_.GetParser()->ParseWebAppOriginAssociation( - std::move(*file_content), + *file_content, base::BindOnce(&WebAppOriginAssociationManager::Task::OnAssociationParsed, weak_ptr_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/web_applications/web_app_proto_utils.cc b/chrome/browser/web_applications/web_app_proto_utils.cc index fbed3c9..742da70 100644 --- a/chrome/browser/web_applications/web_app_proto_utils.cc +++ b/chrome/browser/web_applications/web_app_proto_utils.cc
@@ -44,7 +44,7 @@ absl::optional<std::vector<apps::IconInfo>> ParseAppIconInfos( const char* container_name_for_logging, - RepeatedIconInfosProto manifest_icons_proto) { + const RepeatedIconInfosProto& manifest_icons_proto) { std::vector<apps::IconInfo> manifest_icons; for (const sync_pb::WebAppIconInfo& icon_info_proto : manifest_icons_proto) { apps::IconInfo icon_info;
diff --git a/chrome/browser/web_applications/web_app_proto_utils.h b/chrome/browser/web_applications/web_app_proto_utils.h index d93f7cd..113cd04 100644 --- a/chrome/browser/web_applications/web_app_proto_utils.h +++ b/chrome/browser/web_applications/web_app_proto_utils.h
@@ -25,7 +25,7 @@ absl::optional<std::vector<apps::IconInfo>> ParseAppIconInfos( const char* container_name_for_logging, - RepeatedIconInfosProto manifest_icons_proto); + const RepeatedIconInfosProto& manifest_icons_proto); // Use the given |app| to populate a |WebAppSpecifics| sync proto. sync_pb::WebAppSpecifics WebAppToSyncProto(const WebApp& app);
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc index edb0059..f77ea9c 100644 --- a/chrome/browser/web_applications/web_app_registrar.cc +++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -479,7 +479,7 @@ bool WebAppRegistrar::IsAllowedLaunchProtocol( const AppId& app_id, - std::string protocol_scheme) const { + const std::string& protocol_scheme) const { const WebApp* web_app = GetAppById(app_id); return web_app && base::Contains(web_app->allowed_launch_protocols(), protocol_scheme); @@ -487,7 +487,7 @@ bool WebAppRegistrar::IsDisallowedLaunchProtocol( const AppId& app_id, - std::string protocol_scheme) const { + const std::string& protocol_scheme) const { const WebApp* web_app = GetAppById(app_id); return web_app && base::Contains(web_app->disallowed_launch_protocols(), protocol_scheme);
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h index 9701ee2c..02e7c498 100644 --- a/chrome/browser/web_applications/web_app_registrar.h +++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -132,12 +132,12 @@ // Returns true if the web app with the |app_id| contains |protocol_scheme| // as one of its allowed launch protocols. bool IsAllowedLaunchProtocol(const AppId& app_id, - std::string protocol_scheme) const; + const std::string& protocol_scheme) const; // Returns true if the web app with the |app_id| contains |protocol_scheme| // as one of its disallowed launch protocols. bool IsDisallowedLaunchProtocol(const AppId& app_id, - std::string protocol_scheme) const; + const std::string& protocol_scheme) const; // Gets all allowed launch protocols from all installed apps. base::flat_set<std::string> GetAllAllowedLaunchProtocols() const;
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc index 87b9f256..64e71dfd 100644 --- a/chrome/browser/web_applications/web_app_sync_bridge.cc +++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -67,7 +67,7 @@ app->AddSource(Source::kSync); // app_id is a hash of start_url. Parse start_url first: - GURL start_url(sync_data.start_url()); + const GURL start_url(sync_data.start_url()); if (start_url.is_empty() || !start_url.is_valid()) { DLOG(ERROR) << "ApplySyncDataToApp: start_url parse error."; return; @@ -91,7 +91,7 @@ } if (app->start_url().is_empty()) { - app->SetStartUrl(std::move(start_url)); + app->SetStartUrl(start_url); } else if (app->start_url() != start_url) { DLOG(ERROR) << "ApplySyncDataToApp: existing start_url doesn't match start_url."; @@ -322,7 +322,7 @@ if (!registrar_->IsInstalled(app_id)) return; if (web_app) - web_app->SetUserPageOrdinal(page_ordinal); + web_app->SetUserPageOrdinal(std::move(page_ordinal)); } void WebAppSyncBridge::SetUserLaunchOrdinal( @@ -337,7 +337,7 @@ return; WebApp* web_app = update->UpdateApp(app_id); if (web_app) - web_app->SetUserLaunchOrdinal(launch_ordinal); + web_app->SetUserLaunchOrdinal(std::move(launch_ordinal)); } void WebAppSyncBridge::AddAllowedLaunchProtocol(
diff --git a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc index 4d5034f..a489694 100644 --- a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc +++ b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
@@ -177,7 +177,7 @@ void RunCallbacksOnInstall( const std::vector<WebApp*>& apps, - FakeWebAppRegistryController::RepeatingInstallCallback callback, + const FakeWebAppRegistryController::RepeatingInstallCallback& callback, webapps::InstallResultCode code) { for (WebApp* app : apps) callback.Run(app->app_id(), code);
diff --git a/chrome/browser/web_applications/web_app_translation_manager.cc b/chrome/browser/web_applications/web_app_translation_manager.cc index 13ac2bc..5ef6dcf 100644 --- a/chrome/browser/web_applications/web_app_translation_manager.cc +++ b/chrome/browser/web_applications/web_app_translation_manager.cc
@@ -40,7 +40,7 @@ } blink::Manifest::TranslationItem ConvertLocaleOverridesToTranslationItem( - LocaleOverrides locale_overrides) { + const LocaleOverrides& locale_overrides) { blink::Manifest::TranslationItem translation_item; if (locale_overrides.has_name()) {
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 4941a3e..a7b1e0c 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1646157575-2058350e1f5fc2f12cca4564dd13fd2ea417d5a4.profdata +chrome-mac-arm-main-1646200174-2e50d0cc712ecc3dbd5c3cae5ce3fa4734f35673.profdata
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index ea6b394..67f93d2 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -311,6 +311,11 @@ // whether or not it's actually the First Run (this overrides kNoFirstRun). const char kForceFirstRun[] = "force-first-run"; +// Displays the What's New experience when the browser is started if it has not +// yet been shown for the current milestone (this overrides kNoFirstRun, without +// showing the First Run experience). +const char kForceWhatsNew[] = "force-whats-new"; + // Does not show the crash restore bubble when the browser is started during the // system startup phase in ChromeOS, if the ChromeOS full restore feature is // enabled, because the ChromeOS full restore notification is shown for the user @@ -377,10 +382,13 @@ // then restart chrome without this switch again. const char kNoExperiments[] = "no-experiments"; -// Skip First Run tasks, whether or not it's actually the First Run. Overridden -// by kForceFirstRun. This does not drop the First Run sentinel and thus doesn't -// prevent first run from occuring the next time chrome is launched without this -// flag. +// Skip First Run tasks, whether or not it's actually the First Run, and the +// What's New page. Overridden by kForceFirstRun (for FRE) and kForceWhatsNew +// (for What's New). This does not drop the First Run sentinel and thus doesn't +// prevent first run from occurring the next time chrome is launched without +// this flag. It also does not update the last What's New milestone, so does not +// prevent What's New from occurring the next time chrome is launched without +// this flag. const char kNoFirstRun[] = "no-first-run"; // Don't send hyperlink auditing pings
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 717c7e09..f79266e8 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -105,6 +105,7 @@ extern const char kExtensionsNotWebstore[]; extern const char kForceAppMode[]; extern const char kForceFirstRun[]; +extern const char kForceWhatsNew[]; extern const char kHideCrashRestoreBubble[]; extern const char kHomePage[]; extern const char kIncognito[];
diff --git a/chrome/common/extensions/api/web_navigation.json b/chrome/common/extensions/api/web_navigation.json index 77f907c9..1ba0e91 100644 --- a/chrome/common/extensions/api/web_navigation.json +++ b/chrome/common/extensions/api/web_navigation.json
@@ -30,15 +30,14 @@ "name": "details", "description": "Information about the frame to retrieve information about.", "properties": { - "tabId": { "type": "integer", "optional": true, "minimum": 0, "description": "The ID of the tab in which the frame is." }, + "tabId": { "type": "integer", "minimum": 0, "description": "The ID of the tab in which the frame is." }, "processId": { "type": "integer", "optional": true, "deprecated": "Frames are now uniquely identified by their tab ID and frame ID; the process ID is no longer needed and therefore ignored.", "description": "The ID of the process that runs the renderer for this tab." }, - "frameId": { "type": "integer", "optional": true, "minimum": 0, "description": "The ID of the frame in the given tab." }, - "documentId": { "type": "string", "optional": true, "nodoc": true, "description": "The UUID of the document. If the frameId and/or tabId are provided they will be validated to match the document found by provided document ID." } + "frameId": { "type": "integer", "minimum": 0, "description": "The ID of the frame in the given tab." } } } ],
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js index 61cd3d3..9a3ead6 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js +++ b/chrome/test/data/extensions/api_test/webnavigation/getFrame/test_getFrame.js
@@ -7,9 +7,6 @@ let ready; let onScriptLoad = chrome.test.loadScript(scriptUrl); -const kNotSpecifiedErrorMessage = - 'Either documentId or both tabId and frameId must be specified.'; - if (inServiceWorker) { ready = onScriptLoad; } else { @@ -61,77 +58,6 @@ }); }, - function testGetFrameNoValues() { - chrome.webNavigation.getFrame({}, - function (details) { - chrome.test.assertEq(null, details); - chrome.test.assertLastError(kNotSpecifiedErrorMessage); - chrome.test.succeed(); - }); - }, - - function testGetFrameNoFrameId() { - chrome.webNavigation.getFrame({tabId: tab.id, processId: processId}, - function (details) { - chrome.test.assertEq(null, details); - chrome.test.assertLastError(kNotSpecifiedErrorMessage); - chrome.test.succeed(); - }); - }, - - function testGetFrameDocumentId() { - chrome.webNavigation.getFrame({tabId: tab.id, documentId: documentId}, - function (details) { - chrome.test.assertEq({ - errorOccurred: false, - url: URL, - parentFrameId: -1, - documentId: documentId, - documentLifecycle: "active", - frameType: "outermost_frame", - }, details); - chrome.test.succeed(); - }); - }, - - function testGetFrameDocumentIdAndFrameId() { - chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, - processId: processId, - documentId: documentId}, - function (details) { - chrome.test.assertEq({ - errorOccurred: false, - url: URL, - parentFrameId: -1, - documentId: documentId, - documentLifecycle: "active", - frameType: "outermost_frame", - }, details); - chrome.test.succeed(); - }); - }, - - function testGetFrameDocumentIdAndFrameIdDoNotMatch() { - chrome.webNavigation.getFrame({tabId: tab.id, frameId: 1, - processId: processId, - documentId: documentId}, - function (details) { - chrome.test.assertEq(null, details); - chrome.test.succeed(); - }); - }, - - function testGetFrameInvalidDocumentId() { - chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, - processId: processId, - documentId: "42AB"}, - function (details) { - chrome.test.assertLastError("Invalid documentId."); - chrome.test.assertEq(null, details); - chrome.test.succeed(); - }); - }, - function testGetAllFrames() { chrome.webNavigation.getAllFrames({tabId: tab.id}, function (details) { chrome.test.assertEq(
diff --git a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.js b/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.js index a404a1b1..9533888 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.js +++ b/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.js
@@ -15,18 +15,10 @@ var URL_TARGET = "http://127.0.0.1:" + port + "/extensions/api_test/webnavigation/targetBlank/b.html"; - var topDocumentId; - chrome.test.runTests([ // Opens a tab and waits for the user to click on a link with // target=_blank in it. function targetBlank() { - // store the real documentId for the testGetFrame later. - chrome.webNavigation.onCommitted.addListener( - function(details) { - if (!topDocumentId) - topDocumentId = details.documentId; - }); expect([ { label: "a-onBeforeNavigate", event: "onBeforeNavigate", @@ -135,21 +127,5 @@ // Notify the api test that we're waiting for the user. chrome.test.notifyPass(); }, - - // Verify GetFrame via documentId works correctly in incognito mode. - function testGetFrame() { - chrome.webNavigation.getFrame({documentId: topDocumentId}, - function (details) { - chrome.test.assertEq({ - errorOccurred: false, - url: URL_LOAD, - parentFrameId: -1, - documentId: topDocumentId, - documentLifecycle: 'active', - frameType: 'outermost_frame', - }, details); - chrome.test.succeed(); - }); - } ]); });
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_permissions_setup_dialog_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_permissions_setup_dialog_tests.js index b618e6aa..4fd5c19 100644 --- a/chrome/test/data/webui/settings/chromeos/multidevice_permissions_setup_dialog_tests.js +++ b/chrome/test/data/webui/settings/chromeos/multidevice_permissions_setup_dialog_tests.js
@@ -9,7 +9,7 @@ // #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js'; // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; // #import {TestMultideviceBrowserProxy} from './test_multidevice_browser_proxy.m.js'; -// #import {MultiDeviceBrowserProxyImpl, PermissionsSetupStatus, PhoneHubPermissionsSetupMode} from 'chrome://os-settings/chromeos/os_settings.js'; +// #import {MultiDeviceBrowserProxyImpl, PermissionsSetupStatus, PhoneHubPermissionsSetupMode, SetupFlowStatus} from 'chrome://os-settings/chromeos/os_settings.js'; // clang-format on /** @@ -59,6 +59,13 @@ return permissionsSetupDialog.shouldShowAppsItem_; } + /** + * @param {SetupFlowStatus} status + */ + function isExpectedFlowState(setupState) { + return permissionsSetupDialog.flowState_ === setupState; + } + setup(() => { PolymerTest.clearBody(); browserProxy = new multidevice.TestMultideviceBrowserProxy(); @@ -84,6 +91,8 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); simulateStatusChanged(PermissionsSetupStatus.CONNECTION_REQUESTED); assertFalse(isNotificationItemShowen()); @@ -145,6 +154,8 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); simulateStatusChanged(PermissionsSetupStatus.CONNECTING); @@ -168,6 +179,8 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); simulateStatusChanged(PermissionsSetupStatus.TIMED_OUT_CONNECTING); @@ -209,6 +222,8 @@ assertFalse(!!buttonContainer.querySelector('#closeButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); simulateStatusChanged( PermissionsSetupStatus.NOTIFICATION_ACCESS_PROHIBITED); @@ -237,6 +252,7 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptAppsSetup'), 1); + assertTrue(isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_APPS)); simulateAppsStatusChanged(PermissionsSetupStatus.CONNECTION_REQUESTED); assertFalse(isNotificationItemShowen()); @@ -298,6 +314,7 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptAppsSetup'), 1); + assertTrue(isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_APPS)); simulateAppsStatusChanged(PermissionsSetupStatus.CONNECTING); @@ -321,6 +338,7 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptAppsSetup'), 1); + assertTrue(isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_APPS)); simulateAppsStatusChanged(PermissionsSetupStatus.TIMED_OUT_CONNECTING); @@ -365,6 +383,8 @@ assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); simulateStatusChanged( PermissionsSetupStatus.SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE); @@ -392,6 +412,7 @@ // becomes PermissionsSetupStatus.COMPLETED_SUCCESSFULLY. assertEquals(browserProxy.getCallCount('setFeatureEnabledState'), 1); assertEquals(browserProxy.getCallCount('attemptAppsSetup'), 1); + assertTrue(isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_APPS)); simulateAppsStatusChanged(PermissionsSetupStatus.COMPLETED_SUCCESSFULLY); assertFalse(isNotificationItemShowen()); @@ -412,7 +433,6 @@ assertFalse(permissionsSetupDialog.$$('#dialog').open); }); - test('Test phone enabled but ChromeOS disabled screen lock', async () => { permissionsSetupDialog.phonePermissionSetupMode = PhoneHubPermissionsSetupMode.ALL_PERMISSIONS_SETUP_MODE; @@ -420,7 +440,15 @@ loadTimeData.overrideValues({isPhoneScreenLockEnabled: true}); loadTimeData.overrideValues({isChromeosScreenLockEnabled: false}); buttonContainer.querySelector('#getStartedButton').click(); + assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 0); + assertTrue(isExpectedFlowState(SetupFlowStatus.SET_LOCKSCREEN)); + assertFalse(isSetupInstructionsShownSeparately()); + assertTrue(!!buttonContainer.querySelector('#learnMore')); + assertTrue(!!buttonContainer.querySelector('#cancelButton')); + assertTrue(!!buttonContainer.querySelector('#getStartedButton')); + assertFalse(!!buttonContainer.querySelector('#doneButton')); + assertFalse(!!buttonContainer.querySelector('#tryAgainButton')); }); test('Test phone and ChromeOS enabled screen lock', async () => { @@ -452,4 +480,31 @@ buttonContainer.querySelector('#getStartedButton').click(); assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); }); + + test('Test screen lock UI when Eche is disabled', async () => { + permissionsSetupDialog.phonePermissionSetupMode = + PhoneHubPermissionsSetupMode.NOTIFICATION_SETUP_MODE; + loadTimeData.overrideValues({isEcheAppEnabled: false}); + loadTimeData.overrideValues({isPhoneScreenLockEnabled: true}); + loadTimeData.overrideValues({isChromeosScreenLockEnabled: false}); + buttonContainer.querySelector('#getStartedButton').click(); + + assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); + }); + + test( + 'Test screen lock UI when handling NOTIFICATION_SETUP_MODE', async () => { + permissionsSetupDialog.phonePermissionSetupMode = + PhoneHubPermissionsSetupMode.NOTIFICATION_SETUP_MODE; + loadTimeData.overrideValues({isEcheAppEnabled: true}); + loadTimeData.overrideValues({isPhoneScreenLockEnabled: true}); + loadTimeData.overrideValues({isChromeosScreenLockEnabled: false}); + buttonContainer.querySelector('#getStartedButton').click(); + + assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1); + assertTrue( + isExpectedFlowState(SetupFlowStatus.WAIT_FOR_PHONE_NOTIFICATION)); + }); });
diff --git a/chromeos/crosapi/mojom/notification.mojom b/chromeos/crosapi/mojom/notification.mojom index a072f42..80ff18e 100644 --- a/chromeos/crosapi/mojom/notification.mojom +++ b/chromeos/crosapi/mojom/notification.mojom
@@ -7,6 +7,7 @@ import "chromeos/crosapi/mojom/bitmap.mojom"; import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/time.mojom"; +import "skia/public/mojom/skcolor.mojom"; import "ui/gfx/image/mojom/image.mojom"; import "url/mojom/url.mojom"; @@ -63,6 +64,9 @@ // API. See documentation at: // https://developer.mozilla.org/en-US/docs/Web/API/notification // https://developer.chrome.com/extensions/notifications#type-NotificationOptions +// +// Next MinVersion: 4 +// Next ID: 27 [Stable] struct Notification { // Type of notification to show. @@ -157,4 +161,8 @@ [MinVersion=2] // Whether the badge needs additional masking. bool badge_needs_additional_masking@25; + + [MinVersion=3] + // Unified theme color used in new style notification. + skia.mojom.SkColor? accent_color@26; };
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc index f22c9016..0ba28db 100644 --- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc +++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -12,6 +12,7 @@ #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/strike_database.h" #include "components/autofill/core/browser/strike_database_base.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "ui/gfx/image/image.h" namespace autofill { @@ -288,7 +289,9 @@ } #if !BUILDFLAG(IS_ANDROID) - if (state_.virtual_card_enrollment_fields.virtual_card_enrollment_source == + if (base::FeatureList::IsEnabled( + features::kAutofillEnableToolbarStatusChip) && + state_.virtual_card_enrollment_fields.virtual_card_enrollment_source == VirtualCardEnrollmentSource::kUpstream && !avatar_animation_complete_) { return;
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc index 7922fb19..eb583edc 100644 --- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc +++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/credit_card.h" @@ -20,6 +21,7 @@ #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/image/image_unittest_util.h" @@ -465,6 +467,8 @@ } TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_ResponseFirst) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kAutofillEnableToolbarStatusChip); personal_data_manager_->ClearCreditCardArtImages(); SetUpCard(); SetValidCardArtImageForCard(*card_);
diff --git a/components/live_caption/live_caption_controller.cc b/components/live_caption/live_caption_controller.cc index 100ff82e..7f1ffc01 100644 --- a/components/live_caption/live_caption_controller.cc +++ b/components/live_caption/live_caption_controller.cc
@@ -142,7 +142,10 @@ DestroyUI(); } -void LiveCaptionController::OnSodaInstalled() { +void LiveCaptionController::OnSodaInstalled( + speech::LanguageCode language_code) { + if (!prefs::IsLanguageCodeForLiveCaption(language_code, profile_prefs_)) + return; // Live Caption should always be enabled when this is called. If Live Caption // has been disabled, then this should not be observing the SodaInstaller // anymore.
diff --git a/components/live_caption/live_caption_controller.h b/components/live_caption/live_caption_controller.h index 3d54187f..f568c8a 100644 --- a/components/live_caption/live_caption_controller.h +++ b/components/live_caption/live_caption_controller.h
@@ -83,9 +83,10 @@ friend class LiveCaptionSpeechRecognitionHostTest; // SodaInstaller::Observer: - void OnSodaInstalled() override; - void OnSodaProgress(int combined_progress) override {} - void OnSodaError() override {} + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int progress) override {} + void OnSodaError(speech::LanguageCode language_code) override {} // ui::NativeThemeObserver: void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override {}
diff --git a/components/live_caption/pref_names.cc b/components/live_caption/pref_names.cc index 79adaac..cf4bb31 100644 --- a/components/live_caption/pref_names.cc +++ b/components/live_caption/pref_names.cc
@@ -34,6 +34,12 @@ return speech::kUsEnglishLocale; } +bool IsLanguageCodeForLiveCaption(speech::LanguageCode language_code, + PrefService* profile_prefs) { + return language_code == + speech::GetLanguageCode(GetLiveCaptionLanguageCode(profile_prefs)); +} + #endif // !defined(ANDROID) // String indicating the size of the captions text as a percentage.
diff --git a/components/live_caption/pref_names.h b/components/live_caption/pref_names.h index 7c137cd..d7b4270f 100644 --- a/components/live_caption/pref_names.h +++ b/components/live_caption/pref_names.h
@@ -7,6 +7,12 @@ #include <string> +#include "build/build_config.h" + +#if !BUILDFLAG(IS_ANDROID) +#include "components/soda/constants.h" +#endif + class PrefService; namespace prefs { @@ -18,6 +24,9 @@ extern const char kLiveCaptionLanguageCode[]; const std::string GetLiveCaptionLanguageCode(PrefService* profile_prefs); +bool IsLanguageCodeForLiveCaption(speech::LanguageCode language_code, + PrefService* profile_prefs); + #endif // !defined(ANDROID) // These kAccessibilityCaptions* caption style prefs are used on Android
diff --git a/components/query_tiles/internal/tile_config.cc b/components/query_tiles/internal/tile_config.cc index 1c2d519..cce11da 100644 --- a/components/query_tiles/internal/tile_config.cc +++ b/components/query_tiles/internal/tile_config.cc
@@ -182,7 +182,10 @@ std::string tag = base::GetFieldTrialParamValueByFeature( features::kQueryTiles, kExperimentTagKey); - if (tag.empty() && features::IsQueryTilesEnabledForCountry(country_code)) { + if (tag.empty() && + !base::FeatureList::IsEnabled( + query_tiles::features::kQueryTilesDisableCountryOverride) && + features::IsQueryTilesEnabledForCountry(country_code)) { return kDefaultExperimentTagForTrendingEnabledCountries; } return tag;
diff --git a/components/query_tiles/switches.cc b/components/query_tiles/switches.cc index fabb95c..ba0f30d 100644 --- a/components/query_tiles/switches.cc +++ b/components/query_tiles/switches.cc
@@ -25,6 +25,9 @@ const base::Feature kQueryTilesSegmentation{"QueryTilesSegmentation", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kQueryTilesDisableCountryOverride{ + "QueryTilesDisableCountryOverride", base::FEATURE_DISABLED_BY_DEFAULT}; + bool IsQueryTilesEnabledForCountry(const std::string& country_code) { std::string enabled_countries[] = {"IN", "NG"}; for (const auto& country : enabled_countries) {
diff --git a/components/query_tiles/switches.h b/components/query_tiles/switches.h index b6b0f8f..4ee5a9e 100644 --- a/components/query_tiles/switches.h +++ b/components/query_tiles/switches.h
@@ -37,6 +37,9 @@ // Whether segmentation rules are applied to query tiles. extern const base::Feature kQueryTilesSegmentation; +// Whether to disable the override rules introduced for countries. +extern const base::Feature kQueryTilesDisableCountryOverride; + // Returns whether query tiles are enabled for the country. bool IsQueryTilesEnabledForCountry(const std::string& country_code); } // namespace features
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn index 7f6e41630..02ca6180 100644 --- a/components/segmentation_platform/internal/BUILD.gn +++ b/components/segmentation_platform/internal/BUILD.gn
@@ -57,6 +57,9 @@ "execution/model_execution_manager_factory.cc", "execution/model_execution_manager_factory.h", "execution/model_execution_status.h", + "execution/query_processor.h", + "execution/sql_feature_processor.cc", + "execution/sql_feature_processor.h", "execution/uma_feature_processor.cc", "execution/uma_feature_processor.h", "platform_options.cc", @@ -150,6 +153,7 @@ # IMPORTANT NOTE: When adding new tests, also remember to update the list of # tests in //components/segmentation_platform/components_unittests.filter sources = [ + "data_collection/training_data_collector_unittest.cc", "database/database_maintenance_impl_unittest.cc", "database/metadata_utils_unittest.cc", "database/mock_signal_database.cc", @@ -172,7 +176,6 @@ "execution/mock_feature_list_query_processor.cc", "execution/mock_feature_list_query_processor.h", "execution/model_execution_manager_factory_unittest.cc", - "execution/query_processor.h", "mock_ukm_data_manager.cc", "mock_ukm_data_manager.h", "scheduler/model_execution_scheduler_unittest.cc",
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector.cc b/components/segmentation_platform/internal/data_collection/training_data_collector.cc index e34fc6a..550a75a 100644 --- a/components/segmentation_platform/internal/data_collection/training_data_collector.cc +++ b/components/segmentation_platform/internal/data_collection/training_data_collector.cc
@@ -4,39 +4,190 @@ #include "components/segmentation_platform/internal/data_collection/training_data_collector.h" +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/metrics_hashes.h" #include "base/notreached.h" +#include "base/time/clock.h" +#include "components/optimization_guide/proto/models.pb.h" +#include "components/segmentation_platform/internal/database/metadata_utils.h" +#include "components/segmentation_platform/internal/database/segment_info_database.h" #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" +#include "components/segmentation_platform/internal/proto/model_prediction.pb.h" +#include "components/segmentation_platform/internal/segmentation_ukm_helper.h" + +using optimization_guide::proto::OptimizationTarget; + +const int kInvalidModelVersion = 0; namespace segmentation_platform { +namespace { -TrainingDataCollector::TrainingDataCollector( +// Parse outputs into a map of metric hash of the uma output and its index in +// the output list. +std::map<uint64_t, int> ParseUmaOutputs( + const proto::SegmentationModelMetadata& metadata) { + std::map<uint64_t, int> hash_index_map; + if (!metadata.has_training_outputs()) + return hash_index_map; + + const auto& training_outputs = metadata.training_outputs(); + for (int i = 0; i < training_outputs.outputs_size(); ++i) { + const auto output = training_outputs.outputs(i); + if (!output.has_uma_output() || !output.uma_output().has_uma_feature()) + continue; + + hash_index_map[output.uma_output().uma_feature().name_hash()] = i; + } + return hash_index_map; +} + +} // namespace + +class TrainingDataCollectorImpl : public TrainingDataCollector { + public: + TrainingDataCollectorImpl(SegmentInfoDatabase* segment_info_database, + FeatureListQueryProcessor* processor, + HistogramSignalHandler* histogram_signal_handler, + base::Clock* clock) + : segment_info_database_(segment_info_database), + feature_list_query_processor_(processor), + histogram_signal_handler_(histogram_signal_handler), + clock_(clock) {} + + ~TrainingDataCollectorImpl() override { + histogram_signal_handler_->RemoveObserver(this); + } + + private: + // TrainingDataCollector implementation. + void OnModelMetadataUpdated() override { NOTIMPLEMENTED(); } + + void OnServiceInitialized() override { + // TODO(xingliu): Filter out segments that doesn't contain enough data. + // Maybe reuse ModelExecutionSchedulerImpl::FilterEligibleSegments. + segment_info_database_->GetAllSegmentInfo( + base::BindOnce(&TrainingDataCollectorImpl::OnGetSegmentsInfoList, + weak_ptr_factory_.GetWeakPtr())); + } + + void OnGetSegmentsInfoList( + std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments) { + histogram_signal_handler_->AddObserver(this); + + DCHECK(segments); + for (const auto& segment : *segments) { + const proto::SegmentInfo& segment_info = segment.second; + // Validate segment info. + auto validation_result = + metadata_utils::ValidateSegmentInfo(segment_info); + // TODO(xingliu): Record histogram for errors. + if (validation_result != + metadata_utils::ValidationResult::kValidationSuccess) { + VLOG(1) << "Segment info validation failed for optimization target: " + << segment.first << ", validation result:" + << static_cast<int>(validation_result); + continue; + } + + // Cache the histograms as outputs of training data, which needs to be + // immediately reported when the histogram is recorded. + auto hash_index_map = ParseUmaOutputs(segment_info.model_metadata()); + for (const auto& hash_index : hash_index_map) { + immediate_collection_histograms_[hash_index.first].emplace( + segment.first); + } + } + } + + // HistogramSignalHandler::Observer implementation. + void OnHistogramSignalUpdated(const std::string& histogram_name, + base::HistogramBase::Sample sample) override { + auto hash = base::HashMetricName(histogram_name); + auto it = immediate_collection_histograms_.find(hash); + // Report training data for all models that are interested in + // |histogram_name| as output. + if (it != immediate_collection_histograms_.end()) { + std::vector<OptimizationTarget> optimization_targets(it->second.begin(), + it->second.end()); + segment_info_database_->GetSegmentInfoForSegments( + optimization_targets, + base::BindOnce(&TrainingDataCollectorImpl::ReportForSegmentsInfoList, + weak_ptr_factory_.GetWeakPtr(), hash, sample)); + } + } + + void ReportForSegmentsInfoList( + uint64_t output_metric_hash, + base::HistogramBase::Sample output_metric_sample, + std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments) { + DCHECK(segments); + for (const auto& segment : *segments) { + const proto::SegmentInfo& segment_info = segment.second; + // Figure out the output index. + auto hash_index_map = ParseUmaOutputs(segment_info.model_metadata()); + if (hash_index_map.find(output_metric_hash) == hash_index_map.end()) + continue; + + // Generate training data input. + // TODO(xingliu): Validate immediate output is not included in the input + // features and update the comment in model_metadata.proto. + feature_list_query_processor_->ProcessFeatureList( + segment_info.model_metadata(), segment_info.segment_id(), + clock_->Now(), + base::BindOnce(&TrainingDataCollectorImpl::OnGetInputTensor, + weak_ptr_factory_.GetWeakPtr(), + static_cast<float>(output_metric_sample), + hash_index_map[output_metric_hash], + segment_info.segment_id())); + } + } + + void OnGetInputTensor(float output_value, + int output_index, + OptimizationTarget segment_id, + bool success, + const std::vector<float>& inputs) { + if (!success) + return; + + // TODO(xingliu): Plumb model version to here. + auto ukm_source_id = + SegmentationUkmHelper::GetInstance()->RecordTrainingData( + segment_id, /*model_version=*/kInvalidModelVersion, inputs, + {output_value}, {output_index}); + if (ukm_source_id == ukm::kInvalidSourceId) { + VLOG(1) << "Failed to collect training data for segment:" << segment_id; + } + } + + raw_ptr<SegmentInfoDatabase> segment_info_database_; + raw_ptr<FeatureListQueryProcessor> feature_list_query_processor_; + raw_ptr<HistogramSignalHandler> histogram_signal_handler_; + raw_ptr<base::Clock> clock_; + + // Hash of histograms for immediate training data collection. When any + // histogram hash contained in the map is recorded, a UKM message is reported + // right away. + base::flat_map<uint64_t, + base::flat_set<optimization_guide::proto::OptimizationTarget>> + immediate_collection_histograms_; + + base::WeakPtrFactory<TrainingDataCollectorImpl> weak_ptr_factory_{this}; +}; + +// static +std::unique_ptr<TrainingDataCollector> TrainingDataCollector::Create( + SegmentInfoDatabase* segment_info_database, FeatureListQueryProcessor* processor, - HistogramSignalHandler* histogram_signal_handler) - : feature_list_query_processor_(processor), - histogram_signal_handler_(histogram_signal_handler) { - DCHECK(histogram_signal_handler_); - histogram_signal_handler_->AddObserver(this); -} - -TrainingDataCollector::~TrainingDataCollector() { - DCHECK(histogram_signal_handler_); - histogram_signal_handler_->RemoveObserver(this); -} - -void TrainingDataCollector::OnModelMetadataUpdated() { - NOTIMPLEMENTED(); -} - -void TrainingDataCollector::OnServiceInitialized() { - NOTIMPLEMENTED(); -} - -void TrainingDataCollector::OnHistogramSignalUpdated( - const std::string& histogram_name, - base::HistogramBase::Sample) { - // TODO(xingliu): Check whether the histogram needs to trigger a data - // collection, and report to UKM. - NOTIMPLEMENTED(); + HistogramSignalHandler* histogram_signal_handler, + base::Clock* clock) { + return std::make_unique<TrainingDataCollectorImpl>( + segment_info_database, processor, histogram_signal_handler, clock); } } // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector.h b/components/segmentation_platform/internal/data_collection/training_data_collector.h index 0b3cb14b..bfc2df6a 100644 --- a/components/segmentation_platform/internal/data_collection/training_data_collector.h +++ b/components/segmentation_platform/internal/data_collection/training_data_collector.h
@@ -5,43 +5,47 @@ #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATA_COLLECTION_TRAINING_DATA_COLLECTOR_H_ #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATA_COLLECTION_TRAINING_DATA_COLLECTOR_H_ -#include "base/memory/raw_ptr.h" -#include "base/metrics/histogram_base.h" +#include <memory> + #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h" +namespace base { +class Clock; +} // namespace base + namespace segmentation_platform { class FeatureListQueryProcessor; class HistogramSignalHandler; +class SegmentInfoDatabase; // Collect training data and report as Ukm message. Live on main thread. // TODO(xingliu): Make a new class that owns the training data collector and // model execution collector. class TrainingDataCollector : public HistogramSignalHandler::Observer { public: - TrainingDataCollector(FeatureListQueryProcessor* processor, - HistogramSignalHandler* histogram_signal_handler); - ~TrainingDataCollector() override; + static std::unique_ptr<TrainingDataCollector> Create( + SegmentInfoDatabase* segment_info_database, + FeatureListQueryProcessor* processor, + HistogramSignalHandler* histogram_signal_handler, + base::Clock* clock); + + // Called when model metadata is updated. May result in training data + // collection behavior change. + virtual void OnModelMetadataUpdated() = 0; + + // Called after segmentation platform is initialized. May report training data + // to Ukm for |UMAOutput| in |SegmentationModelMetadata|. + virtual void OnServiceInitialized() = 0; + + ~TrainingDataCollector() override = default; // Disallow copy/assign. TrainingDataCollector(const TrainingDataCollector&) = delete; TrainingDataCollector& operator=(const TrainingDataCollector&) = delete; - // Called when model metadata is updated. May result in training data - // collection behavior change. - void OnModelMetadataUpdated(); - - // Called after segmentation platform is initialized. May report training data - // to Ukm that has a non-zero |duration| field in |UMAOutput|. - void OnServiceInitialized(); - - // HistogramSignalHandler::Observer overrides. - void OnHistogramSignalUpdated(const std::string& histogram_name, - base::HistogramBase::Sample) override; - - private: - raw_ptr<FeatureListQueryProcessor> feature_list_query_processor_; - raw_ptr<HistogramSignalHandler> histogram_signal_handler_; + protected: + TrainingDataCollector() = default; }; } // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_unittest.cc index 0c792a92..e9ddca3 100644 --- a/components/segmentation_platform/internal/data_collection/training_data_collector_unittest.cc +++ b/components/segmentation_platform/internal/data_collection/training_data_collector_unittest.cc
@@ -4,10 +4,40 @@ #include "components/segmentation_platform/internal/data_collection/training_data_collector.h" +#include <map> + +#include "base/metrics/metrics_hashes.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/simple_test_clock.h" +#include "base/test/task_environment.h" +#include "components/segmentation_platform/internal/database/test_segment_info_database.h" #include "components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" +#include "components/segmentation_platform/internal/proto/model_prediction.pb.h" +#include "components/segmentation_platform/internal/segmentation_ukm_helper.h" #include "components/segmentation_platform/internal/signals/mock_histogram_signal_handler.h" +#include "components/segmentation_platform/public/config.h" +#include "components/segmentation_platform/public/features.h" +#include "components/ukm/test_ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gtest/include/gtest/gtest.h" +using ::base::test::RunOnceCallback; +using ::testing::_; +using ::testing::NiceMock; +using Segmentation_ModelExecution = + ::ukm::builders::Segmentation_ModelExecution; + +constexpr auto kTestOptimizationTarget0 = + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB; +constexpr auto kTestOptimizationTarget1 = + OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE; +constexpr char kHistogramName0[] = "histogram0"; +constexpr char kHistogramName1[] = "histogram1"; + +constexpr int kSample = 1; + namespace segmentation_platform { namespace { @@ -17,24 +47,142 @@ ~TrainingDataCollectorTest() override = default; void SetUp() override { - collector_ = std::make_unique<TrainingDataCollector>( - &feature_list_processor_, &histogram_signal_handler_); + test_recorder_.Purge(); + + // Allow two models to collect training data. + std::map<std::string, std::string> params = { + {kSegmentIdsAllowedForReportingKey, "4,5"}}; + feature_list_.InitAndEnableFeatureWithParameters( + features::kSegmentationStructuredMetricsFeature, params); + + // Setup behavior for |feature_list_processor_|. + std::vector<float> inputs({1.f}); + ON_CALL(feature_list_processor_, ProcessFeatureList(_, _, _, _)) + .WillByDefault(RunOnceCallback<3>(true, inputs)); + + test_segment_info_db_ = std::make_unique<test::TestSegmentInfoDatabase>(); + collector_ = TrainingDataCollector::Create( + test_segment_info_db_.get(), &feature_list_processor_, + &histogram_signal_handler_, &clock_); } protected: TrainingDataCollector* collector() { return collector_.get(); } + test::TestSegmentInfoDatabase* test_segment_db() { + return test_segment_info_db_.get(); + } + base::test::TaskEnvironment* task_environment() { return &task_environment_; } + + void CreateSegmentInfo() { + test_segment_db()->AddUserActionFeature(kTestOptimizationTarget0, "action", + 1, 1, proto::Aggregation::COUNT); + // Segment 0 contains 1 immediate collection uma output for for + // |kHistogramName0|, 1 continuous collection output for for + // |kHistogramName1|. + auto* segment_info = CreateSegment(kTestOptimizationTarget0); + AddOutput(segment_info, kHistogramName0); + proto::TrainingOutput* output1 = AddOutput(segment_info, kHistogramName1); + output1->mutable_uma_output()->set_duration(1u); + } + + proto::SegmentInfo* CreateSegment(OptimizationTarget optimization_target) { + auto* segment_info = + test_segment_db()->FindOrCreateSegment(optimization_target); + segment_info->mutable_model_metadata()->set_time_unit(proto::TimeUnit::DAY); + return segment_info; + } + + proto::TrainingOutput* AddOutput(proto::SegmentInfo* segment_info, + const std::string& histgram_name) { + auto* output = segment_info->mutable_model_metadata() + ->mutable_training_outputs() + ->add_outputs(); + auto* uma_feature = output->mutable_uma_output()->mutable_uma_feature(); + uma_feature->set_name(histgram_name); + uma_feature->set_name_hash(base::HashMetricName(histgram_name)); + return output; + } + + // TODO(xingliu): Share this test code with SegmentationUkmHelperTest, or test + // with mock SegmentationUkmHelperTest. + void ExpectUkm(std::vector<base::StringPiece> metric_names, + std::vector<int64_t> expected_values) { + const auto& entries = test_recorder_.GetEntriesByName( + Segmentation_ModelExecution::kEntryName); + ASSERT_EQ(1u, entries.size()); + for (size_t i = 0; i < metric_names.size(); ++i) { + test_recorder_.ExpectEntryMetric(entries[0], metric_names[i], + expected_values[i]); + } + } + + void ExpectUkmCount(size_t count) { + const auto& entries = test_recorder_.GetEntriesByName( + Segmentation_ModelExecution::kEntryName); + ASSERT_EQ(count, entries.size()); + } + + void Init() { + collector()->OnServiceInitialized(); + task_environment()->RunUntilIdle(); + } + + void WaitForHistogramSignalUpdated(const std::string& histogram_name, + base::HistogramBase::Sample sample) { + base::RunLoop run_loop; + test_recorder_.SetOnAddEntryCallback( + Segmentation_ModelExecution::kEntryName, run_loop.QuitClosure()); + collector_->OnHistogramSignalUpdated(histogram_name, sample); + run_loop.Run(); + } private: - MockFeatureListQueryProcessor feature_list_processor_; - MockHistogramSignalHandler histogram_signal_handler_; + base::SimpleTestClock clock_; + base::test::TaskEnvironment task_environment_; + base::test::ScopedFeatureList feature_list_; + ukm::TestAutoSetUkmRecorder test_recorder_; + NiceMock<MockFeatureListQueryProcessor> feature_list_processor_; + NiceMock<MockHistogramSignalHandler> histogram_signal_handler_; + std::unique_ptr<test::TestSegmentInfoDatabase> test_segment_info_db_; std::unique_ptr<TrainingDataCollector> collector_; }; -// Place holder test case that will be replaced to test real implementation -// logic. -TEST_F(TrainingDataCollectorTest, Construction) { - // TODO(xingliu): Remove this once read test cases are added. - EXPECT_NE(nullptr, collector()); +// No segment info in database. Do nothing. +TEST_F(TrainingDataCollectorTest, NoSegment) { + Init(); + collector()->OnHistogramSignalUpdated(kHistogramName0, kSample); + task_environment()->RunUntilIdle(); + ExpectUkmCount(0u); +} + +// No segment info in database. Do nothing. +TEST_F(TrainingDataCollectorTest, IrrelevantHistogramNotReported) { + CreateSegmentInfo(); + Init(); + collector()->OnHistogramSignalUpdated("irrelevant_histogram", kSample); + task_environment()->RunUntilIdle(); + ExpectUkmCount(0u); +} + +TEST_F(TrainingDataCollectorTest, HistogramImmediatelyReported) { + CreateSegmentInfo(); + Init(); + WaitForHistogramSignalUpdated(kHistogramName0, kSample); + ExpectUkm( + {Segmentation_ModelExecution::kOptimizationTargetName, + Segmentation_ModelExecution::kActualResultName}, + {kTestOptimizationTarget0, SegmentationUkmHelper::FloatToInt64(kSample)}); +} + +TEST_F(TrainingDataCollectorTest, HistogramImmediatelyReported_MultipleModel) { + CreateSegmentInfo(); + // Segment 1 contains 1 immediate collection uma output for for + // |kHistogramName0| + auto* segment_info = CreateSegment(kTestOptimizationTarget1); + AddOutput(segment_info, kHistogramName0); + Init(); + WaitForHistogramSignalUpdated(kHistogramName0, kSample); + ExpectUkmCount(2u); } } // namespace
diff --git a/components/segmentation_platform/internal/execution/feature_list_query_processor.cc b/components/segmentation_platform/internal/execution/feature_list_query_processor.cc index 515e4082..008b607 100644 --- a/components/segmentation_platform/internal/execution/feature_list_query_processor.cc +++ b/components/segmentation_platform/internal/execution/feature_list_query_processor.cc
@@ -12,12 +12,18 @@ #include "components/segmentation_platform/internal/database/metadata_utils.h" #include "components/segmentation_platform/internal/execution/custom_input_processor.h" #include "components/segmentation_platform/internal/execution/feature_processor_state.h" +#include "components/segmentation_platform/internal/execution/sql_feature_processor.h" #include "components/segmentation_platform/internal/execution/uma_feature_processor.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" #include "components/segmentation_platform/internal/stats.h" namespace segmentation_platform { +namespace { +// Index not actually used for legacy code in FeatureQueryProcessor. +const int kIndexNotUsed = 0; +} // namespace + FeatureListQueryProcessor::FeatureListQueryProcessor( SignalDatabase* signal_database, std::unique_ptr<FeatureAggregator> feature_aggregator) @@ -78,7 +84,25 @@ input_feature.custom_input(), std::move(feature_processor_state), base::BindOnce(&FeatureListQueryProcessor::ProcessNextInputFeature, weak_ptr_factory_.GetWeakPtr())); + } else if (input_feature.has_sql_feature()) { + std::map<SqlFeatureProcessor::FeatureIndex, proto::SqlFeature> queries = { + {kIndexNotUsed, input_feature.sql_feature()}}; + auto sql_feature_processor = std::make_unique<SqlFeatureProcessor>(queries); + auto* sql_feature_processor_ptr = sql_feature_processor.get(); + sql_feature_processor_ptr->Process( + std::move(feature_processor_state), + base::BindOnce(&FeatureListQueryProcessor::OnSqlQueryProcessed, + weak_ptr_factory_.GetWeakPtr(), + std::move(sql_feature_processor))); } } +void FeatureListQueryProcessor::OnSqlQueryProcessed( + std::unique_ptr<SqlFeatureProcessor> sql_feature_processor, + std::unique_ptr<FeatureProcessorState> feature_processor_state, + QueryProcessor::IndexedTensors result) { + feature_processor_state->AppendInputTensor(result[kIndexNotUsed]); + ProcessNextInputFeature(std::move(feature_processor_state)); +} + } // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/feature_list_query_processor.h b/components/segmentation_platform/internal/execution/feature_list_query_processor.h index d1cfc2d..f63542e 100644 --- a/components/segmentation_platform/internal/execution/feature_list_query_processor.h +++ b/components/segmentation_platform/internal/execution/feature_list_query_processor.h
@@ -6,12 +6,14 @@ #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_LIST_QUERY_PROCESSOR_H_ #include <deque> +#include <memory> #include <vector> #include "base/memory/weak_ptr.h" #include "components/optimization_guide/proto/models.pb.h" #include "components/segmentation_platform/internal/execution/custom_input_processor.h" #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h" +#include "components/segmentation_platform/internal/execution/query_processor.h" #include "components/segmentation_platform/internal/execution/uma_feature_processor.h" #include "components/segmentation_platform/internal/proto/model_metadata.pb.h" @@ -19,6 +21,7 @@ class FeatureAggregator; class FeatureProcessorState; class SignalDatabase; +class SqlFeatureProcessor; // FeatureListQueryProcessor takes a segmentation model's metadata, processes // each feature in the metadata's feature list in order and computes an input @@ -57,6 +60,15 @@ void ProcessNextInputFeature( std::unique_ptr<FeatureProcessorState> feature_processor_state); + // Callback called after a sql feature has been processed, indicating that we + // can safely discard the sql feature processor that handled the processing. + // Continue with the rest of the input features by calling + // ProcessNextInputFeature. + void OnSqlQueryProcessed( + std::unique_ptr<SqlFeatureProcessor> sql_feature_processor, + std::unique_ptr<FeatureProcessorState> feature_processor_state, + QueryProcessor::IndexedTensors result); + // Feature processor for uma type of input features. UmaFeatureProcessor uma_feature_processor_;
diff --git a/components/segmentation_platform/internal/execution/query_processor.h b/components/segmentation_platform/internal/execution/query_processor.h index 316f942..b6b41c8 100644 --- a/components/segmentation_platform/internal/execution/query_processor.h +++ b/components/segmentation_platform/internal/execution/query_processor.h
@@ -22,11 +22,14 @@ using FeatureIndex = int; using Tensor = std::vector<float>; using IndexedTensors = std::map<FeatureIndex, Tensor>; + using QueryProcessorCallback = + base::OnceCallback<void(std::unique_ptr<FeatureProcessorState>, + IndexedTensors)>; // Processes the data and return the tensor values in |callback|. virtual void Process( std::unique_ptr<FeatureProcessorState> feature_processor_state, - base::OnceCallback<IndexedTensors> callback) = 0; + QueryProcessorCallback callback) = 0; // Disallow copy/assign. QueryProcessor(const QueryProcessor&) = delete;
diff --git a/components/segmentation_platform/internal/execution/sql_feature_processor.cc b/components/segmentation_platform/internal/execution/sql_feature_processor.cc new file mode 100644 index 0000000..1f9881f --- /dev/null +++ b/components/segmentation_platform/internal/execution/sql_feature_processor.cc
@@ -0,0 +1,28 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/segmentation_platform/internal/execution/sql_feature_processor.h" + +#include "components/segmentation_platform/internal/execution/feature_processor_state.h" + +namespace segmentation_platform { + +SqlFeatureProcessor::SqlFeatureProcessor( + std::map<FeatureIndex, proto::SqlFeature> queries) + : queries_(std::move(queries)) {} +SqlFeatureProcessor::~SqlFeatureProcessor() = default; + +void SqlFeatureProcessor::Process( + std::unique_ptr<FeatureProcessorState> feature_processor_state, + QueryProcessorCallback callback) { + // TODO(haileywang): Implement usage of custom input processor for bind + // values. + queries_.clear(); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), std::move(feature_processor_state), + std::move(result_))); +} + +} // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/sql_feature_processor.h b/components/segmentation_platform/internal/execution/sql_feature_processor.h new file mode 100644 index 0000000..e0b8a8c --- /dev/null +++ b/components/segmentation_platform/internal/execution/sql_feature_processor.h
@@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_ +#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "base/callback_forward.h" +#include "components/segmentation_platform/internal/execution/query_processor.h" +#include "components/segmentation_platform/internal/proto/model_metadata.pb.h" + +namespace segmentation_platform { +class FeatureProcessorState; + +// SqlFeatureProcessor takes a list of SqlFeature type of input, fetches samples +// from the UKMDatabase, and computes an input tensor to use when executing the +// ML model. +class SqlFeatureProcessor : public QueryProcessor { + public: + explicit SqlFeatureProcessor( + std::map<FeatureIndex, proto::SqlFeature> queries); + ~SqlFeatureProcessor() override; + + // QueryProcessor implementation. + void Process(std::unique_ptr<FeatureProcessorState> feature_processor_state, + QueryProcessorCallback callback) override; + + private: + // List of sql features to process into input tensors. + std::map<FeatureIndex, proto::SqlFeature> queries_; + + // List of resulting input tensors. + IndexedTensors result_; +}; + +} // namespace segmentation_platform + +#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc index b177f83..75d8b318 100644 --- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc +++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -229,8 +229,10 @@ feature_list_query_processor_ = std::make_unique<FeatureListQueryProcessor>( signal_database_.get(), std::make_unique<FeatureAggregatorImpl>()); - training_data_collector_ = std::make_unique<TrainingDataCollector>( - feature_list_query_processor_.get(), histogram_signal_handler_.get()); + training_data_collector_ = TrainingDataCollector::Create( + segment_info_database_.get(), feature_list_query_processor_.get(), + histogram_signal_handler_.get(), clock_); + training_data_collector_->OnServiceInitialized(); model_execution_manager_ = CreateModelExecutionManager( model_provider_, task_runner_, all_segment_ids_, clock_,
diff --git a/components/segmentation_platform/internal/signals/histogram_signal_handler.cc b/components/segmentation_platform/internal/signals/histogram_signal_handler.cc index 6ce7a721..0b3ae3b 100644 --- a/components/segmentation_platform/internal/signals/histogram_signal_handler.cc +++ b/components/segmentation_platform/internal/signals/histogram_signal_handler.cc
@@ -15,7 +15,9 @@ HistogramSignalHandler::HistogramSignalHandler(SignalDatabase* signal_database) : db_(signal_database), metrics_enabled_(false) {} -HistogramSignalHandler::~HistogramSignalHandler() = default; +HistogramSignalHandler::~HistogramSignalHandler() { + DCHECK(observers_.empty()); +} void HistogramSignalHandler::SetRelevantHistograms( const RelevantHistograms& histograms) {
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc index de20326..99c3d3f 100644 --- a/components/services/app_service/public/cpp/app_update.cc +++ b/components/services/app_service/public/cpp/app_update.cc
@@ -836,19 +836,12 @@ (mojom_delta_->allow_uninstall != mojom_state_->allow_uninstall)); } -apps::mojom::OptionalBool AppUpdate::HasBadge() const { - if (mojom_delta_ && - (mojom_delta_->has_badge != apps::mojom::OptionalBool::kUnknown)) { - return mojom_delta_->has_badge; +absl::optional<bool> AppUpdate::HasBadge() const { + if (ShouldUseNonMojom()) { + GET_VALUE_WITH_FALLBACK(has_badge, absl::nullopt) } - if (mojom_state_) { - return mojom_state_->has_badge; - } - return apps::mojom::OptionalBool::kUnknown; -} -absl::optional<bool> AppUpdate::GetHasBadge() const { - GET_VALUE_WITH_FALLBACK(has_badge, absl::nullopt); + CONVERT_MOJOM_OPTIONALBOOL_TO_OPTIONAL_VALUE(has_badge) } bool AppUpdate::HasBadgeChanged() const { @@ -1046,7 +1039,7 @@ << std::endl; out << "AllowUninstall: " << PRINT_OPTIONAL_VALUE(AllowUninstall) << std::endl; - out << "HasBadge: " << app.HasBadge() << std::endl; + out << "HasBadge: " << PRINT_OPTIONAL_VALUE(HasBadge) << std::endl; out << "Paused: " << PRINT_OPTIONAL_VALUE(Paused) << std::endl; out << "IntentFilters: " << std::endl; for (const auto& filter : app.IntentFilters()) {
diff --git a/components/services/app_service/public/cpp/app_update.h b/components/services/app_service/public/cpp/app_update.h index 37902fb..9e560df 100644 --- a/components/services/app_service/public/cpp/app_update.h +++ b/components/services/app_service/public/cpp/app_update.h
@@ -164,8 +164,7 @@ absl::optional<bool> AllowUninstall() const; bool AllowUninstallChanged() const; - apps::mojom::OptionalBool HasBadge() const; - absl::optional<bool> GetHasBadge() const; + absl::optional<bool> HasBadge() const; bool HasBadgeChanged() const; absl::optional<bool> Paused() const;
diff --git a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc index 6081888..9f76814 100644 --- a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc +++ b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
@@ -96,7 +96,7 @@ absl::optional<bool> expect_allow_uninstall_; bool expect_allow_uninstall_changed_; - apps::mojom::OptionalBool expect_has_badge_; + absl::optional<bool> expect_has_badge_; bool expect_has_badge_changed_; absl::optional<bool> expect_paused_; @@ -282,7 +282,7 @@ expect_show_in_management_ = absl::nullopt; expect_handles_intents_ = absl::nullopt; expect_allow_uninstall_ = absl::nullopt; - expect_has_badge_ = apps::mojom::OptionalBool::kUnknown; + expect_has_badge_ = absl::nullopt; expect_paused_ = absl::nullopt; expect_intent_filters_.clear(); expect_resize_locked_ = apps::mojom::OptionalBool::kUnknown; @@ -779,14 +779,14 @@ if (state) { state->has_badge = apps::mojom::OptionalBool::kFalse; - expect_has_badge_ = apps::mojom::OptionalBool::kFalse; + expect_has_badge_ = false; expect_has_badge_changed_ = false; CheckExpects(u); } if (delta) { delta->has_badge = apps::mojom::OptionalBool::kTrue; - expect_has_badge_ = apps::mojom::OptionalBool::kTrue; + expect_has_badge_ = true; expect_has_badge_changed_ = true; CheckExpects(u); }
diff --git a/components/services/app_service/public/cpp/app_update_unittest.cc b/components/services/app_service/public/cpp/app_update_unittest.cc index 5cd2c8e..487e3090 100644 --- a/components/services/app_service/public/cpp/app_update_unittest.cc +++ b/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -239,7 +239,7 @@ EXPECT_EQ(expect_allow_uninstall_, u.AllowUninstall()); EXPECT_EQ(expect_allow_uninstall_changed_, u.AllowUninstallChanged()); - EXPECT_EQ(expect_has_badge_, u.GetHasBadge()); + EXPECT_EQ(expect_has_badge_, u.HasBadge()); EXPECT_EQ(expect_has_badge_changed_, u.HasBadgeChanged()); EXPECT_EQ(expect_paused_, u.Paused());
diff --git a/components/soda/soda_installer.cc b/components/soda/soda_installer.cc index 113582e3..179ad05 100644 --- a/components/soda/soda_installer.cc +++ b/components/soda/soda_installer.cc
@@ -143,12 +143,8 @@ return (soda_binary_installed_ && IsLanguageInstalled(language_code)); } -bool SodaInstaller::IsAnyLanguagePackInstalled() const { - return !installed_languages_.empty(); -} - bool SodaInstaller::IsLanguageInstalled(LanguageCode language_code) const { - return installed_languages_.find(language_code) != installed_languages_.end(); + return base::Contains(installed_languages_, language_code); } void SodaInstaller::AddObserver(Observer* observer) { @@ -159,20 +155,43 @@ observers_.RemoveObserver(observer); } -void SodaInstaller::NotifySodaInstalledForTesting() { - soda_binary_installed_ = true; - is_soda_downloading_ = false; - installed_languages_.insert(LanguageCode::kEnUs); - language_pack_progress_.clear(); - NotifyOnSodaInstalled(); +void SodaInstaller::NotifySodaInstalledForTesting(LanguageCode language_code) { + // TODO: Call the actual functions in SodaInstallerImpl and + // SodaInstallerImpleChromeOS that do this logic + // (e.g. SodaInstallerImpl::OnSodaBinaryInstalled) rather than faking it. + + // If language code is none, this signifies that the SODA binary installed. + if (language_code == LanguageCode::kNone) { + soda_binary_installed_ = true; + is_soda_downloading_ = false; + for (LanguageCode installed_language : installed_languages_) { + NotifyOnSodaInstalled(installed_language); + } + return; + } + + // Otherwise, this means a language pack installed. + installed_languages_.insert(language_code); + if (base::Contains(language_pack_progress_, language_code)) + language_pack_progress_.erase(language_code); + if (soda_binary_installed_) + NotifyOnSodaInstalled(language_code); } -void SodaInstaller::NotifySodaErrorForTesting() { - soda_binary_installed_ = false; - is_soda_downloading_ = false; - installed_languages_.clear(); - language_pack_progress_.clear(); - NotifyOnSodaError(); +void SodaInstaller::NotifySodaErrorForTesting(LanguageCode language_code) { + // TODO: Call the actual functions in SodaInstallerImpl and + // SodaInstallerImpleChromeOS that do this logic rather than faking it. + if (language_code == LanguageCode::kNone) { + // Error with the SODA binary download. + soda_binary_installed_ = false; + is_soda_downloading_ = false; + language_pack_progress_.clear(); + } else { + // Error with the language pack download. + if (base::Contains(language_pack_progress_, language_code)) + language_pack_progress_.erase(language_code); + } + NotifyOnSodaError(language_code); } void SodaInstaller::UninstallSodaForTesting() { @@ -183,39 +202,26 @@ language_pack_progress_.clear(); } -void SodaInstaller::NotifySodaDownloadProgressForTesting(int progress) { - soda_binary_installed_ = false; - is_soda_downloading_ = true; - installed_languages_.clear(); - NotifyOnSodaProgress(progress); +void SodaInstaller::NotifySodaProgressForTesting(int progress, + LanguageCode language_code) { + // TODO: Call the actual functions in SodaInstallerImpl and + // SodaInstallerImpleChromeOS that do this logic rather than faking it. + if (language_code == LanguageCode::kNone) { + // SODA binary download progress. + soda_binary_installed_ = false; + is_soda_downloading_ = true; + } else { + // Language pack download progress. + if (base::Contains(language_pack_progress_, language_code)) + language_pack_progress_.insert({language_code, progress}); + else + language_pack_progress_[language_code] = progress; + } + NotifyOnSodaProgress(language_code, progress); } -void SodaInstaller::NotifyOnSodaLanguagePackInstalledForTesting( - LanguageCode language_code) { - installed_languages_.insert(language_code); - auto it = language_pack_progress_.find(language_code); - if (it != language_pack_progress_.end()) - language_pack_progress_.erase(language_code); - NotifyOnSodaLanguagePackInstalled(language_code); -} - -void SodaInstaller::NotifyOnSodaLanguagePackProgressForTesting( - int progress, - LanguageCode language_code) { - auto it = language_pack_progress_.find(language_code); - if (it == language_pack_progress_.end()) - language_pack_progress_.insert({language_code, progress}); - else - language_pack_progress_[language_code] = progress; - NotifyOnSodaLanguagePackProgress(progress, language_code); -} - -void SodaInstaller::NotifyOnSodaLanguagePackErrorForTesting( - LanguageCode language_code) { - auto it = language_pack_progress_.find(language_code); - if (it != language_pack_progress_.end()) - language_pack_progress_.erase(language_code); - NotifyOnSodaLanguagePackError(language_code); +bool SodaInstaller::IsAnyLanguagePackInstalledForTesting() const { + return !installed_languages_.empty(); } void SodaInstaller::RegisterRegisteredLanguagePackPref( @@ -227,37 +233,20 @@ base::Value(std::move(default_languages))); } -void SodaInstaller::NotifyOnSodaInstalled() { +void SodaInstaller::NotifyOnSodaInstalled(LanguageCode language_code) { for (Observer& observer : observers_) - observer.OnSodaInstalled(); + observer.OnSodaInstalled(language_code); } -void SodaInstaller::NotifyOnSodaLanguagePackInstalled( - LanguageCode language_code) { +void SodaInstaller::NotifyOnSodaError(LanguageCode language_code) { for (Observer& observer : observers_) - observer.OnSodaLanguagePackInstalled(language_code); + observer.OnSodaError(language_code); } -void SodaInstaller::NotifyOnSodaError() { +void SodaInstaller::NotifyOnSodaProgress(LanguageCode language_code, + int progress) { for (Observer& observer : observers_) - observer.OnSodaError(); -} - -void SodaInstaller::NotifyOnSodaLanguagePackError(LanguageCode language_code) { - for (Observer& observer : observers_) - observer.OnSodaLanguagePackError(language_code); -} - -void SodaInstaller::NotifyOnSodaProgress(int combined_progress) { - for (Observer& observer : observers_) - observer.OnSodaProgress(combined_progress); -} - -void SodaInstaller::NotifyOnSodaLanguagePackProgress( - int language_progress, - LanguageCode language_code) { - for (Observer& observer : observers_) - observer.OnSodaLanguagePackProgress(language_progress, language_code); + observer.OnSodaProgress(language_code, progress); } void SodaInstaller::RegisterLanguage(const std::string& language, @@ -274,8 +263,8 @@ } bool SodaInstaller::IsSodaDownloading(LanguageCode language_code) const { - return is_soda_downloading_ || language_pack_progress_.find(language_code) != - language_pack_progress_.end(); + return is_soda_downloading_ || + base::Contains(language_pack_progress_, language_code); } bool SodaInstaller::IsAnyFeatureUsingSodaEnabled(PrefService* prefs) {
diff --git a/components/soda/soda_installer.h b/components/soda/soda_installer.h index 89b118cf..d406e25 100644 --- a/components/soda/soda_installer.h +++ b/components/soda/soda_installer.h
@@ -28,42 +28,19 @@ // Observer of the SODA (Speech On-Device API) installation. class Observer : public base::CheckedObserver { public: - //////////////////////////////////////////////////////////////////////////// - // Main SODA update functions. Use these when informing the user about - // the availability of speech on device. This means that the general binary - // is ready and at least one language is available. For example, these might - // be used to display download progress next to the feature name in - // settings. + // Called when the SODA binary component and the language pack for this + // language code are installed. + virtual void OnSodaInstalled(LanguageCode language_code) = 0; - // Called when the SODA binary component and at least one language pack is - // installed. - virtual void OnSodaInstalled() = 0; - - // Called if there is an error in the SODA binary or language pack - // installation. - virtual void OnSodaError() = 0; + // Called if there is an error in the SODA installation. If the language + // code is LanguageCode::kNone, the error is for the SODA binary; otherwise + // it is for the language pack. + virtual void OnSodaError(LanguageCode language_code) = 0; // Called during the SODA installation. Progress is the weighted average of - // the download percentage of the SODA binary and at least one language - // pack. - virtual void OnSodaProgress(int combined_progress) = 0; - - //////////////////////////////////////////////////////////////////////////// - // Language-specific SODA update functions. Use these when informing the - // user about the availability of a specific language. For example, these - // might be used to display download progress of a particular language next - // to the language list item. - - // Called when a SODA language pack component is installed. - virtual void OnSodaLanguagePackInstalled(LanguageCode language_code) {} - - // Called if there is an error in a SODA language pack installation. - virtual void OnSodaLanguagePackError(LanguageCode language_code) {} - - // Called during the SODA installation. Progress is the download percentage - // out of 100. - virtual void OnSodaLanguagePackProgress(int language_progress, - LanguageCode language_code) {} + // the combined download percentage of the SODA binary and the language pack + // for this language code. + virtual void OnSodaProgress(LanguageCode language_code, int progress) = 0; }; SodaInstaller(); @@ -131,14 +108,17 @@ void NeverDownloadSodaForTesting() { never_download_soda_for_testing_ = true; } - void NotifySodaInstalledForTesting(); - void NotifySodaErrorForTesting(); + + // The soda binary is encoded as LanguageCode::kNone. + void NotifySodaInstalledForTesting( + LanguageCode language_code = LanguageCode::kNone); + void NotifySodaErrorForTesting( + LanguageCode language_code = LanguageCode::kNone); void UninstallSodaForTesting(); - void NotifySodaDownloadProgressForTesting(int percentage); - void NotifyOnSodaLanguagePackInstalledForTesting(LanguageCode language_code); - void NotifyOnSodaLanguagePackProgressForTesting(int progress, - LanguageCode language_code); - void NotifyOnSodaLanguagePackErrorForTesting(LanguageCode language_code); + void NotifySodaProgressForTesting( + int progress, + LanguageCode language_code = LanguageCode::kNone); + bool IsAnyLanguagePackInstalledForTesting() const; protected: // Registers the preference tracking the installed SODA language packs. @@ -152,31 +132,19 @@ // space may not be freed immediately. virtual void UninstallSoda(PrefService* global_prefs) = 0; - // Notifies the observers that the installation of the SODA binary and at - // least one language pack has completed. - void NotifyOnSodaInstalled(); + // Notifies the observers that the installation of the SODA binary and the + // language pack for this language code has completed. + void NotifyOnSodaInstalled(LanguageCode language_code); - // Notifies the observers that a SODA language pack installation has - // completed. - void NotifyOnSodaLanguagePackInstalled(LanguageCode language_code); - - // Notifies the observers that there is an error in the SODA binary - // installation. - void NotifyOnSodaError(); - - // Notifies the observers that there is an error in a SODA language pack - // installation. - void NotifyOnSodaLanguagePackError(LanguageCode language_code); + // Notifies the observers that there is an error in the SODA installation. + // If the language code is LanguageCode::kNone, the error is for the SODA + // binary; otherwise it is for the language pack. + void NotifyOnSodaError(LanguageCode language_code); // Notifies the observers of the combined progress as the SODA binary and // language pack are installed. Progress is the download percentage out of // 100. - void NotifyOnSodaProgress(int combined_progress); - - // Notifies the observers of the progress percentage the SODA language pack is - // installed. Progress is the download percentage out of 100. - void NotifyOnSodaLanguagePackProgress(int language_progress, - LanguageCode language_code); + void NotifyOnSodaProgress(LanguageCode language_code, int progress); // Registers a language pack by adding it to the preference tracking the // installed SODA language packs. @@ -190,8 +158,6 @@ // installed. The language should be localized in BCP-47, e.g. "en-US". bool IsLanguageInstalled(LanguageCode language_code) const; - bool IsAnyLanguagePackInstalled() const; - base::ObserverList<Observer> observers_; bool soda_binary_installed_ = false; bool soda_installer_initialized_ = false;
diff --git a/components/soda/soda_installer_impl_chromeos.cc b/components/soda/soda_installer_impl_chromeos.cc index 7582987c..8927ca9 100644 --- a/components/soda/soda_installer_impl_chromeos.cc +++ b/components/soda/soda_installer_impl_chromeos.cc
@@ -5,6 +5,7 @@ #include "components/soda/soda_installer_impl_chromeos.h" #include "base/bind.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/numerics/safe_conversions.h" @@ -126,16 +127,17 @@ if (install_result.error == dlcservice::kErrorNone) { soda_binary_installed_ = true; SetSodaBinaryPath(base::FilePath(install_result.root_path)); - if (IsLanguageInstalled(LanguageCode::kEnUs)) { - NotifyOnSodaInstalled(); - } + // TODO(crbug.com/1161569): SODA is only available for English right now. + // Update this to notify on all installed languages. + if (IsLanguageInstalled(LanguageCode::kEnUs)) + NotifyOnSodaInstalled(LanguageCode::kEnUs); base::UmaHistogramTimes(kSodaBinaryInstallationSuccessTimeTaken, base::Time::Now() - start_time); } else { soda_binary_installed_ = false; soda_progress_ = 0.0; - NotifyOnSodaError(); + NotifyOnSodaError(LanguageCode::kNone); base::UmaHistogramTimes(kSodaBinaryInstallationFailureTimeTaken, base::Time::Now() - start_time); } @@ -153,7 +155,7 @@ installed_languages_.insert(language_code); SetLanguagePath(base::FilePath(install_result.root_path)); if (soda_binary_installed_) { - NotifyOnSodaInstalled(); + NotifyOnSodaInstalled(language_code); } base::UmaHistogramTimes( GetInstallationSuccessTimeMetricForLanguagePack(language_code), @@ -162,7 +164,7 @@ } else { // TODO: Notify the observer of the specific language pack that failed // to install. ChromeOS currently only supports the en-US language pack. - NotifyOnSodaLanguagePackError(language_code); + NotifyOnSodaError(language_code); base::UmaHistogramTimes( GetInstallationFailureTimeMetricForLanguagePack(language_code), @@ -181,23 +183,21 @@ void SodaInstallerImplChromeOS::OnLanguageProgress(double progress) { language_pack_progress_[LanguageCode::kEnUs] = progress; - - // TODO: Notify the observer of the specific language pack that is currently - // being installed. ChromeOS currently only supports the en-US language pack. - NotifyOnSodaLanguagePackProgress(progress, LanguageCode::kEnUs); + OnSodaCombinedProgress(); } void SodaInstallerImplChromeOS::OnSodaCombinedProgress() { // TODO(crbug.com/1055150): Consider updating this implementation. // e.g.: (1) starting progress from 0% if we are downloading language // only (2) weighting download progress proportionally to DLC binary size. - double language_progress = 0; - auto it = language_pack_progress_.find(LanguageCode::kEnUs); - if (it != language_pack_progress_.end()) - language_progress = it->second; + double language_progress = 0.0; + if (base::Contains(language_pack_progress_, LanguageCode::kEnUs)) + language_progress = language_pack_progress_[LanguageCode::kEnUs]; const double progress = (soda_progress_ + language_progress) / 2; - NotifyOnSodaProgress(base::ClampFloor(100 * progress)); + // TODO: Notify the observer of the specific language pack that is currently + // being installed. ChromeOS currently only supports the en-US language pack. + NotifyOnSodaProgress(LanguageCode::kEnUs, base::ClampFloor(100 * progress)); } void SodaInstallerImplChromeOS::OnDlcUninstalled(const std::string& dlc_id,
diff --git a/components/soda/soda_installer_impl_chromeos_unittest.cc b/components/soda/soda_installer_impl_chromeos_unittest.cc index 97cfd78..d63efb72 100644 --- a/components/soda/soda_installer_impl_chromeos_unittest.cc +++ b/components/soda/soda_installer_impl_chromeos_unittest.cc
@@ -74,7 +74,7 @@ } bool IsAnyLanguagePackInstalled() { - return soda_installer_impl_->IsAnyLanguagePackInstalled(); + return soda_installer_impl_->IsAnyLanguagePackInstalledForTesting(); } bool IsSodaDownloading() { @@ -197,7 +197,13 @@ ASSERT_FALSE(IsSodaDownloading()); ASSERT_FALSE(IsLanguageInstalled(kEnglishLocale)); ASSERT_FALSE(IsSodaDownloading()); + + // Install just the binary. GetInstance()->NotifySodaInstalledForTesting(); + ASSERT_FALSE(IsSodaDownloading()); + + // Now install the language pack. + GetInstance()->NotifySodaInstalledForTesting(kEnglishLocale); ASSERT_TRUE(IsSodaInstalled()); ASSERT_FALSE(IsSodaDownloading()); ASSERT_TRUE(IsLanguageInstalled(kEnglishLocale)); @@ -219,7 +225,7 @@ ASSERT_FALSE(IsSodaDownloading()); ASSERT_FALSE(IsLanguageInstalled(kEnglishLocale)); Init(); - GetInstance()->NotifySodaDownloadProgressForTesting(50); + GetInstance()->NotifySodaProgressForTesting(50); ASSERT_FALSE(IsSodaInstalled()); ASSERT_FALSE(IsAnyLanguagePackInstalled()); ASSERT_TRUE(IsSodaDownloading()); @@ -232,10 +238,10 @@ Init(); RunUntilIdle(); ASSERT_FALSE(IsLanguageInstalled(fr_fr)); - GetInstance()->NotifyOnSodaLanguagePackProgressForTesting(50, fr_fr); + GetInstance()->NotifySodaProgressForTesting(50, fr_fr); ASSERT_TRUE(GetInstance()->IsSodaDownloading(fr_fr)); ASSERT_FALSE(IsLanguageInstalled(fr_fr)); - GetInstance()->NotifyOnSodaLanguagePackInstalledForTesting(fr_fr); + GetInstance()->NotifySodaInstalledForTesting(fr_fr); ASSERT_TRUE(IsLanguageInstalled(fr_fr)); } @@ -245,10 +251,10 @@ Init(); RunUntilIdle(); ASSERT_FALSE(IsLanguageInstalled(fr_fr)); - GetInstance()->NotifyOnSodaLanguagePackProgressForTesting(50, fr_fr); + GetInstance()->NotifySodaProgressForTesting(50, fr_fr); ASSERT_TRUE(GetInstance()->IsSodaDownloading(fr_fr)); ASSERT_FALSE(IsLanguageInstalled(fr_fr)); - GetInstance()->NotifyOnSodaLanguagePackErrorForTesting(fr_fr); + GetInstance()->NotifySodaErrorForTesting(fr_fr); ASSERT_FALSE(IsLanguageInstalled(fr_fr)); ASSERT_FALSE(GetInstance()->IsSodaDownloading(fr_fr)); }
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn index a7dcf9c..c506406 100644 --- a/components/vector_icons/BUILD.gn +++ b/components/vector_icons/BUILD.gn
@@ -22,6 +22,7 @@ "call_end.icon", "caret_down.icon", "caret_up.icon", + "celebration.icon", "certificate.icon", "check_circle.icon", "close.icon",
diff --git a/components/vector_icons/celebration.icon b/components/vector_icons/celebration.icon new file mode 100644 index 0000000..0f9f77f --- /dev/null +++ b/components/vector_icons/celebration.icon
@@ -0,0 +1,60 @@ +// Copyright 2022 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. + +CANVAS_DIMENSIONS, 24, +NEW_PATH, +MOVE_TO, 2, 22, +R_LINE_TO, 14, -5, +LINE_TO, 7, 8, +LINE_TO, 2, 22, +CLOSE, +MOVE_TO, 12.35f, 16.18f, +LINE_TO, 5.3f, 18.7f, +R_LINE_TO, 2.52f, -7.05f, +LINE_TO, 12.35f, 16.18f, +CLOSE, +NEW_PATH, +MOVE_TO, 14.53f, 12.53f, +R_LINE_TO, 5.59f, -5.59f, +R_CUBIC_TO, 0.49f, -0.49f, 1.28f, -0.49f, 1.77f, 0, +R_LINE_TO, 0.59f, 0.59f, +R_LINE_TO, 1.06f, -1.06f, +R_LINE_TO, -0.59f, -0.59f, +R_CUBIC_TO, -1.07f, -1.07f, -2.82f, -1.07f, -3.89f, 0, +R_LINE_TO, -5.59f, 5.59f, +LINE_TO, 14.53f, 12.53f, +CLOSE, +NEW_PATH, +MOVE_TO, 10.06f, 6.88f, +LINE_TO, 9.47f, 7.47f, +R_LINE_TO, 1.06f, 1.06f, +R_LINE_TO, 0.59f, -0.59f, +R_CUBIC_TO, 1.07f, -1.07f, 1.07f, -2.82f, 0, -3.89f, +R_LINE_TO, -0.59f, -0.59f, +LINE_TO, 9.47f, 4.53f, +R_LINE_TO, 0.59f, 0.59f, +CUBIC_TO, 10.54f, 5.6f, 10.54f, 6.4f, 10.06f, 6.88f, +CLOSE, +NEW_PATH, +MOVE_TO, 17.06f, 11.88f, +R_LINE_TO, -1.59f, 1.59f, +R_LINE_TO, 1.06f, 1.06f, +R_LINE_TO, 1.59f, -1.59f, +R_CUBIC_TO, 0.49f, -0.49f, 1.28f, -0.49f, 1.77f, 0, +R_LINE_TO, 1.61f, 1.61f, +R_LINE_TO, 1.06f, -1.06f, +R_LINE_TO, -1.61f, -1.61f, +CUBIC_TO, 19.87f, 10.81f, 18.13f, 10.81f, 17.06f, 11.88f, +CLOSE, +NEW_PATH, +MOVE_TO, 15.06f, 5.88f, +R_LINE_TO, -3.59f, 3.59f, +R_LINE_TO, 1.06f, 1.06f, +R_LINE_TO, 3.59f, -3.59f, +R_CUBIC_TO, 1.07f, -1.07f, 1.07f, -2.82f, 0, -3.89f, +R_LINE_TO, -1.59f, -1.59f, +R_LINE_TO, -1.06f, 1.06f, +R_LINE_TO, 1.59f, 1.59f, +CUBIC_TO, 15.54f, 4.6f, 15.54f, 5.4f, 15.06f, 5.88f, +CLOSE \ No newline at end of file
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc index c8e6cec..a0f358a 100644 --- a/content/browser/accessibility/browser_accessibility_android.cc +++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -627,10 +627,10 @@ return true; } -// Note: this is used to compute an object's name on Android, and is exposed as -// the name field in Android dump tree tests. -// TODO(accessibility) Should it be called GetName() so that engineers not -// familiar with Android can find it more easily? +// Note: In the Android accessibility API, the word "text" is used where other +// platforms would use "name". The value returned here will appear in dump tree +// tests as "name" in the ...-android.txt files, but as "text" in the +// ...-android-external.txt files. On other platforms this may be ::GetName(). std::u16string BrowserAccessibilityAndroid::GetTextContentUTF16() const { if (ui::IsIframe(GetRole())) return std::u16string(); @@ -753,6 +753,11 @@ return value; } +// This method maps to the Android API's "hint" attribute. For nodes that have +// chosen to expose their value in the name ("text") attribute, the hint must +// contain the text that would otherwise have been present. The hint includes +// the placeholder and describedby values for all nodes regardless of where the +// value is placed. These pieces of content are concatenated for Android. std::u16string BrowserAccessibilityAndroid::GetHint() const { std::vector<std::u16string> strings; @@ -1917,6 +1922,11 @@ return false; } +// This method determines if a node should expose its value as a name, which is +// placed in the Android API's "text" attribute. For controls that can take on +// a value (e.g. a date time, or combobox), we wish to expose the value that +// the user has chosen. When the value is exposed as the name, then the +// accessible name is added to the Android API's "hint" attribute instead. bool BrowserAccessibilityAndroid::ShouldExposeValueAsName() const { switch (GetRole()) { case ax::mojom::Role::kDate: @@ -1935,6 +1945,9 @@ if (IsTextField()) return true; + if (IsCombobox()) + return true; + if (GetRole() == ax::mojom::Role::kPopUpButton && !GetValueForControl().empty()) { return true;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 2f9821b..d0b8331 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -2391,6 +2391,11 @@ RunHtmlTest(FILE_PATH_LITERAL("ins.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + AccessibilityInteractiveControlsWithLabels) { + RunHtmlTest(FILE_PATH_LITERAL("interactive-controls-with-labels.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityLabel) { RunHtmlTest(FILE_PATH_LITERAL("label.html")); }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc index c389c6e..47a224b 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -145,6 +145,11 @@ attribution_manager_->HandleSource(std::move(storable_source)); } +void AttributionDataHostManagerImpl::TriggerDataAvailable( + blink::mojom::AttributionTriggerDataPtr data) { + // TODO(johnidel): Add browser process handling for attributionsrc triggers. +} + void AttributionDataHostManagerImpl::OnDataHostDisconnected() { receiver_source_destinations_.erase(receivers_.current_receiver()); }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h index aace73b5..3156798 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -63,6 +63,8 @@ // blink::mojom::AttributionDataHost: void SourceDataAvailable( blink::mojom::AttributionSourceDataPtr data) override; + void TriggerDataAvailable( + blink::mojom::AttributionTriggerDataPtr data) override; void OnDataHostDisconnected();
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc new file mode 100644 index 0000000..fb1e613 --- /dev/null +++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -0,0 +1,655 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <utility> + +#include "base/run_loop.h" +#include "base/strings/strcat.h" +#include "content/browser/attribution_reporting/attribution_manager_impl.h" +#include "content/browser/attribution_reporting/attribution_test_utils.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/controllable_http_response.h" +#include "net/test/embedded_test_server/default_handlers.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h" +#include "url/gurl.h" + +namespace content { + +namespace { + +using ::testing::ElementsAre; +using ::testing::Field; +using ::testing::IsEmpty; +using ::testing::Pair; + +std::unique_ptr<MockDataHost> GetRegisteredDataHost( + mojo::PendingReceiver<blink::mojom::AttributionDataHost> data_host) { + return std::make_unique<MockDataHost>(std::move(data_host)); +} + +} // namespace + +class AttributionSrcBrowserTest : public ContentBrowserTest { + public: + AttributionSrcBrowserTest() { + AttributionManagerImpl::RunInMemoryForTesting(); + } + + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "content/test/data/attribution_reporting"); + embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); + content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + + https_server_ = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + net::test_server::RegisterDefaultHandlers(https_server_.get()); + https_server_->ServeFilesFromSourceDirectory( + "content/test/data/attribution_reporting"); + https_server_->ServeFilesFromSourceDirectory("content/test/data"); + SetupCrossSiteRedirector(https_server_.get()); + ASSERT_TRUE(https_server_->Start()); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + // Sets up the blink runtime feature for ConversionMeasurement. + command_line->AppendSwitch( + switches::kEnableExperimentalWebPlatformFeatures); + + // Sets up support for event sources. + command_line->AppendSwitch(switches::kEnableBlinkTestFeatures); + } + + WebContents* web_contents() { return shell()->web_contents(); } + + net::EmbeddedTestServer* https_server() { return https_server_.get(); } + + private: + std::unique_ptr<net::EmbeddedTestServer> https_server_; +}; + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImg_SourceRegistered) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = + https_server()->GetURL("c.test", "/register_source_headers.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.front()->source_event_id, 5UL); + EXPECT_EQ(source_data.front()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); + EXPECT_EQ(source_data.front()->priority, 0); + EXPECT_EQ(source_data.front()->expiry, absl::nullopt); + EXPECT_FALSE(source_data.front()->debug_key); + EXPECT_THAT(source_data.front()->filter_data->filter_values, IsEmpty()); + EXPECT_THAT(source_data.front()->aggregatable_sources->sources, IsEmpty()); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImg_SourceRegisteredWithOptionalParams) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_source_headers_all_params.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.front()->source_event_id, 5UL); + EXPECT_EQ(source_data.front()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); + EXPECT_EQ(source_data.front()->priority, 10); + EXPECT_EQ(source_data.front()->expiry, base::Seconds(1000)); + EXPECT_EQ(source_data.front()->debug_key, + blink::mojom::AttributionDebugKey::New(789)); + EXPECT_THAT(source_data.front()->filter_data->filter_values, + UnorderedElementsAre(Pair("a", IsEmpty()), + Pair("b", ElementsAre("1", "2")))); +} + +IN_PROC_BROWSER_TEST_F( + AttributionSrcBrowserTest, + AttributionSrcImg_SourceRegisteredWithAttributionAggregatableSources) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_aggregatable_source_headers.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.front()->source_event_id, 5UL); + EXPECT_EQ(source_data.front()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); + EXPECT_EQ(source_data.front()->priority, 0); + EXPECT_EQ(source_data.front()->expiry, absl::nullopt); + EXPECT_FALSE(source_data.front()->debug_key); + EXPECT_THAT( + source_data.front()->aggregatable_sources->sources, + UnorderedElementsAre( + Pair("key1", + Pointee(AllOf( + Field(&blink::mojom::AttributionAggregatableKey::high_bits, + 0), + Field(&blink::mojom::AttributionAggregatableKey::low_bits, + 5)))), + Pair("key2", + Pointee(AllOf( + Field(&blink::mojom::AttributionAggregatableKey::high_bits, + 0), + Field(&blink::mojom::AttributionAggregatableKey::low_bits, + 345)))))); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImgRedirect_MultipleSourcesRegistered) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_source_headers_and_redirect.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/2); + const auto& source_data = data_host->source_data(); + + EXPECT_EQ(source_data.size(), 2u); + EXPECT_EQ(source_data.front()->source_event_id, 1UL); + EXPECT_EQ(source_data.front()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); + EXPECT_EQ(source_data.back()->source_event_id, 5UL); + EXPECT_EQ(source_data.back()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImgRedirect_InvalidJsonIgnored) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_source_headers_and_redirect_invalid.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + // Only the second source is registered. + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.back()->source_event_id, 5UL); + EXPECT_EQ(source_data.back()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImgSlowResponse_SourceRegistered) { + // Create a separate server as we cannot register a `ControllableHttpResponse` + // after the server starts. + auto https_server = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + net::test_server::RegisterDefaultHandlers(https_server.get()); + https_server->ServeFilesFromSourceDirectory( + "content/test/data/attribution_reporting"); + https_server->ServeFilesFromSourceDirectory("content/test/data"); + SetupCrossSiteRedirector(https_server.get()); + + auto register_response = + std::make_unique<net::test_server::ControllableHttpResponse>( + https_server.get(), "/register_source"); + ASSERT_TRUE(https_server->Start()); + + GURL page_url = + https_server->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server->GetURL("d.test", "/register_source"); + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + + // Navigate cross-site before sending a response. + GURL page2_url = + https_server->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page2_url)); + + register_response->WaitForRequest(); + auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); + http_response->set_code(net::HTTP_OK); + http_response->AddCustomHeader("Access-Control-Allow-Origin", "*"); + http_response->AddCustomHeader( + "Attribution-Reporting-Register-Source", + R"({"source_event_id":"5", "destination":"https://advertiser.example"})"); + register_response->Send(http_response->ToResponseString()); + register_response->Done(); + + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + // Only the second source is registered. + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.back()->source_event_id, 5UL); + EXPECT_EQ(source_data.back()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImg_TriggerRegistered) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = + https_server()->GetURL("c.test", "/register_trigger_headers.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForTriggerData(/*num_trigger_data=*/1); + const auto& trigger_data = data_host->trigger_data(); + + EXPECT_EQ(trigger_data.size(), 1u); + EXPECT_EQ(trigger_data.front()->reporting_origin, + url::Origin::Create(register_url)); + EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u); + EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImg_TriggerRegisteredAllParams) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_trigger_headers_all_params.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForTriggerData(/*num_trigger_data=*/1); + const auto& trigger_data = data_host->trigger_data(); + + EXPECT_EQ(trigger_data.size(), 1u); + EXPECT_EQ(trigger_data.front()->reporting_origin, + url::Origin::Create(register_url)); + EXPECT_EQ(trigger_data.front()->event_triggers.size(), 2u); + + // Verify first trigger. + const auto& event_trigger_datas = trigger_data.front()->event_triggers; + EXPECT_EQ(event_trigger_datas.front()->data, 1u); + EXPECT_EQ(event_trigger_datas.front()->priority, 5); + EXPECT_EQ(event_trigger_datas.front()->dedup_key->value, 1024u); + + EXPECT_EQ(event_trigger_datas.back()->data, 2u); + EXPECT_EQ(event_trigger_datas.back()->priority, 10); + EXPECT_FALSE(event_trigger_datas.back()->dedup_key); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImg_InvalidTriggerJsonIgnored) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server()->GetURL( + "c.test", "/register_trigger_headers_then_redirect_invalid.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForTriggerData(/*num_trigger_data=*/1); + const auto& trigger_data = data_host->trigger_data(); + + EXPECT_EQ(trigger_data.size(), 1u); + EXPECT_EQ(trigger_data.front()->reporting_origin, + url::Origin::Create(register_url)); + EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u); + EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u); +} + +IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest, + AttributionSrcImgTriggerThenSource_SourceIgnored) { + GURL page_url = + https_server()->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = + https_server()->GetURL("c.test", "/register_trigger_source_trigger.html"); + + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + if (!data_host) + loop.Run(); + data_host->WaitForTriggerData(/*num_trigger_data=*/2); + const auto& trigger_data = data_host->trigger_data(); + + EXPECT_EQ(trigger_data.size(), 2u); + EXPECT_EQ(trigger_data.front()->reporting_origin, + url::Origin::Create(register_url)); + + // Both triggers should be processed. + EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 5u); + EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 10u); + + // Middle redirect source should be ignored. + EXPECT_EQ(data_host->source_data().size(), 0u); +} + +class AttributionSrcInvalidFiltersBrowserTest + : public AttributionSrcBrowserTest, + public ::testing::WithParamInterface<const char*> {}; + +IN_PROC_BROWSER_TEST_P(AttributionSrcInvalidFiltersBrowserTest, + AttributionSrcImgFiltersInvalid_SourceDropped) { + // Create a separate server as we cannot register a `ControllableHttpResponse` + // after the server starts. + auto https_server = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + net::test_server::RegisterDefaultHandlers(https_server.get()); + https_server->ServeFilesFromSourceDirectory( + "content/test/data/attribution_reporting"); + https_server->ServeFilesFromSourceDirectory("content/test/data"); + SetupCrossSiteRedirector(https_server.get()); + + auto register_response = + std::make_unique<net::test_server::ControllableHttpResponse>( + https_server.get(), "/register_source"); + ASSERT_TRUE(https_server->Start()); + + GURL page_url = + https_server->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server->GetURL("d.test", "/register_source"); + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + + register_response->WaitForRequest(); + auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); + http_response->set_code(net::HTTP_MOVED_PERMANENTLY); + http_response->AddCustomHeader( + "Attribution-Reporting-Register-Source", + base::StrCat( + {R"({"source_event_id":"9", "destination":"https://advertiser.example", "filter_data":)", + GetParam(), "}"})); + http_response->AddCustomHeader("Location", "/register_source_headers.html"); + register_response->Send(http_response->ToResponseString()); + register_response->Done(); + + if (!data_host) + loop.Run(); + data_host->WaitForSourceData(/*num_source_data=*/1); + const auto& source_data = data_host->source_data(); + + // Only the second source is registered. + EXPECT_EQ(source_data.size(), 1u); + EXPECT_EQ(source_data.back()->source_event_id, 5UL); + EXPECT_EQ(source_data.back()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); +} + +INSTANTIATE_TEST_SUITE_P( + AttributionSrcInvalidFilters, + AttributionSrcInvalidFiltersBrowserTest, + ::testing::Values(R"("x")", // not a dictionary + R"({"a":"y"})", // dictionary value isn't an array + R"({"b":[8]})" // array value isn't a string + )); + +class AttributionSrcFilterSizeBrowserTest + : public AttributionSrcBrowserTest, + public ::testing::WithParamInterface<AttributionFilterSizeTestCase> {}; + +IN_PROC_BROWSER_TEST_P(AttributionSrcFilterSizeBrowserTest, + AttributionSrcImgExcessiveFilterSize_SourceDropped) { + const AttributionFilterSizeTestCase& test_case = GetParam(); + + // Create a separate server as we cannot register a `ControllableHttpResponse` + // after the server starts. + auto https_server = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); + net::test_server::RegisterDefaultHandlers(https_server.get()); + https_server->ServeFilesFromSourceDirectory( + "content/test/data/attribution_reporting"); + https_server->ServeFilesFromSourceDirectory("content/test/data"); + SetupCrossSiteRedirector(https_server.get()); + + auto register_response = + std::make_unique<net::test_server::ControllableHttpResponse>( + https_server.get(), "/register_source"); + ASSERT_TRUE(https_server->Start()); + + GURL page_url = + https_server->GetURL("b.test", "/page_with_impression_creator.html"); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + + MockAttributionHost host(web_contents()); + std::unique_ptr<MockDataHost> data_host; + base::RunLoop loop; + EXPECT_CALL(host, RegisterDataHost) + .WillOnce( + [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { + data_host = GetRegisteredDataHost(std::move(host)); + loop.Quit(); + }); + + GURL register_url = https_server->GetURL("d.test", "/register_source"); + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); + + register_response->WaitForRequest(); + auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); + http_response->set_code(net::HTTP_MOVED_PERMANENTLY); + + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetStringKey("source_event_id", "9"); + dict.SetStringKey("destination", "https://advertiser.example"); + + base::Value filter_data(base::Value::Type::DICTIONARY); + for (auto [filter, values] : test_case.AsMap()) { + base::Value list(base::Value::Type::LIST); + for (auto value : values) { + list.Append(std::move(value)); + } + filter_data.SetKey(std::move(filter), std::move(list)); + } + dict.SetKey("filter_data", std::move(filter_data)); + + std::string json; + EXPECT_TRUE(base::JSONWriter::Write(dict, &json)); + + http_response->AddCustomHeader("Attribution-Reporting-Register-Source", + std::move(json)); + http_response->AddCustomHeader("Location", "/register_source_headers.html"); + register_response->Send(http_response->ToResponseString()); + register_response->Done(); + + if (!data_host) + loop.Run(); + + const size_t expected_sources = test_case.valid ? 2 : 1; + data_host->WaitForSourceData(/*num_source_data=*/expected_sources); + const auto& source_data = data_host->source_data(); + + EXPECT_EQ(source_data.size(), expected_sources); + EXPECT_EQ(source_data.back()->source_event_id, 5UL); + EXPECT_EQ(source_data.back()->destination, + url::Origin::Create(GURL("https://advertiser.example"))); +} + +INSTANTIATE_TEST_SUITE_P( + AttributionSrcFilterSizes, + AttributionSrcFilterSizeBrowserTest, + ::testing::ValuesIn(kAttributionFilterSizeTestCases), + /*name_generator=*/ + [](const ::testing::TestParamInfo<AttributionFilterSizeTestCase>& info) { + return info.param.description; + }); + +// TODO(apaseltiner): Add tests for overlong filters. + +} // namespace content
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc index b58fc7b..c8a96fb4 100644 --- a/content/browser/attribution_reporting/attribution_test_utils.cc +++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -67,6 +67,14 @@ wait_loop_.Run(); } +void MockDataHost::WaitForTriggerData(size_t num_trigger_data) { + min_trigger_data_count_ = num_trigger_data; + if (trigger_data_.size() >= min_trigger_data_count_) { + return; + } + wait_loop_.Run(); +} + void MockDataHost::SourceDataAvailable( blink::mojom::AttributionSourceDataPtr data) { source_data_.push_back(std::move(data)); @@ -76,6 +84,15 @@ wait_loop_.Quit(); } +void MockDataHost::TriggerDataAvailable( + blink::mojom::AttributionTriggerDataPtr data) { + trigger_data_.push_back(std::move(data)); + if (trigger_data_.size() < min_trigger_data_count_) { + return; + } + wait_loop_.Quit(); +} + MockDataHostManager::MockDataHostManager() = default; MockDataHostManager::~MockDataHostManager() = default;
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h index d952cbb..f484793 100644 --- a/content/browser/attribution_reporting/attribution_test_utils.h +++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -106,21 +106,33 @@ ~MockDataHost() override; void WaitForSourceData(size_t num_source_data); + void WaitForTriggerData(size_t num_trigger_data); const std::vector<blink::mojom::AttributionSourceDataPtr>& source_data() const { return source_data_; } + const std::vector<blink::mojom::AttributionTriggerDataPtr>& trigger_data() + const { + return trigger_data_; + } + private: // blink::mojom::AttributionDataHost: void SourceDataAvailable( blink::mojom::AttributionSourceDataPtr data) override; + void TriggerDataAvailable( + blink::mojom::AttributionTriggerDataPtr data) override; size_t min_source_data_count_ = 0; - mojo::Receiver<blink::mojom::AttributionDataHost> receiver_{this}; - base::RunLoop wait_loop_; std::vector<blink::mojom::AttributionSourceDataPtr> source_data_; + + size_t min_trigger_data_count_ = 0; + std::vector<blink::mojom::AttributionTriggerDataPtr> trigger_data_; + + base::RunLoop wait_loop_; + mojo::Receiver<blink::mojom::AttributionDataHost> receiver_{this}; }; class MockDataHostManager : public AttributionDataHostManager {
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc index c18886f..13e4fda 100644 --- a/content/browser/attribution_reporting/attributions_browsertest.cc +++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -714,9 +714,8 @@ "a.test", "/attribution_reporting/register_source_headers_debug_key.html"); - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); + EXPECT_TRUE(ExecJs(web_contents(), + JsReplace("createAttributionSrcImg($1);", register_url))); GURL conversion_url = https_server()->GetURL( "b.test", "/attribution_reporting/page_with_conversion_redirect.html");
diff --git a/content/browser/attribution_reporting/source_declaration_browsertest.cc b/content/browser/attribution_reporting/source_declaration_browsertest.cc index 7932b9f..88cf306a 100644 --- a/content/browser/attribution_reporting/source_declaration_browsertest.cc +++ b/content/browser/attribution_reporting/source_declaration_browsertest.cc
@@ -6,7 +6,6 @@ #include "base/json/json_writer.h" #include "base/run_loop.h" -#include "base/strings/strcat.h" #include "base/test/metrics/histogram_tester.h" #include "base/values.h" #include "build/build_config.h" @@ -42,11 +41,6 @@ using ::testing::Pointee; using ::testing::UnorderedElementsAre; -std::unique_ptr<MockDataHost> GetRegisteredDataHost( - mojo::PendingReceiver<blink::mojom::AttributionDataHost> data_host) { - return std::make_unique<MockDataHost>(std::move(data_host)); -} - // WebContentsObserver that waits until a source is available on a // navigation handle for a finished navigation. class SourceObserver : public TestNavigationObserver { @@ -167,275 +161,6 @@ }; IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, - AttributionSrcImg_SourceRegistered) { - SourceObserver source_observer(web_contents()); - GURL page_url = - https_server()->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = - https_server()->GetURL("c.test", "/register_source_headers.html"); - - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.front()->source_event_id, 5UL); - EXPECT_EQ(source_data.front()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); - EXPECT_EQ(source_data.front()->priority, 0); - EXPECT_EQ(source_data.front()->expiry, absl::nullopt); - EXPECT_FALSE(source_data.front()->debug_key); - EXPECT_THAT(source_data.front()->filter_data->filter_values, IsEmpty()); - EXPECT_THAT(source_data.front()->aggregatable_sources->sources, IsEmpty()); -} - -IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, - AttributionSrcImg_SourceRegisteredWithOptionalParams) { - SourceObserver source_observer(web_contents()); - GURL page_url = - https_server()->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server()->GetURL( - "c.test", "/register_source_headers_all_params.html"); - - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.front()->source_event_id, 5UL); - EXPECT_EQ(source_data.front()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); - EXPECT_EQ(source_data.front()->priority, 10); - EXPECT_EQ(source_data.front()->expiry, base::Seconds(1000)); - EXPECT_EQ(source_data.front()->debug_key, - blink::mojom::AttributionDebugKey::New(789)); - EXPECT_THAT(source_data.front()->filter_data->filter_values, - UnorderedElementsAre(Pair("a", IsEmpty()), - Pair("b", ElementsAre("1", "2")))); -} - -IN_PROC_BROWSER_TEST_F( - AttributionSourceDeclarationBrowserTest, - AttributionSrcImg_SourceRegisteredWithAttributionAggregatableSources) { - SourceObserver source_observer(web_contents()); - GURL page_url = - https_server()->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server()->GetURL( - "c.test", "/register_aggregatable_source_headers.html"); - - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.front()->source_event_id, 5UL); - EXPECT_EQ(source_data.front()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); - EXPECT_EQ(source_data.front()->priority, 0); - EXPECT_EQ(source_data.front()->expiry, absl::nullopt); - EXPECT_FALSE(source_data.front()->debug_key); - EXPECT_THAT( - source_data.front()->aggregatable_sources->sources, - UnorderedElementsAre( - Pair("key1", - Pointee(AllOf( - Field(&blink::mojom::AttributionAggregatableKey::high_bits, - 0), - Field(&blink::mojom::AttributionAggregatableKey::low_bits, - 5)))), - Pair("key2", - Pointee(AllOf( - Field(&blink::mojom::AttributionAggregatableKey::high_bits, - 0), - Field(&blink::mojom::AttributionAggregatableKey::low_bits, - 345)))))); -} - -IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, - AttributionSrcImgRedirect_MultipleSourcesRegistered) { - SourceObserver source_observer(web_contents()); - GURL page_url = - https_server()->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server()->GetURL( - "c.test", "/register_source_headers_and_redirect.html"); - - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/2); - const auto& source_data = data_host->source_data(); - - EXPECT_EQ(source_data.size(), 2u); - EXPECT_EQ(source_data.front()->source_event_id, 1UL); - EXPECT_EQ(source_data.front()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); - EXPECT_EQ(source_data.back()->source_event_id, 5UL); - EXPECT_EQ(source_data.back()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); -} - -IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, - AttributionSrcImgRedirect_InvalidJsonIgnored) { - SourceObserver source_observer(web_contents()); - GURL page_url = - https_server()->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server()->GetURL( - "c.test", "/register_source_headers_and_redirect_invalid.html"); - - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - // Only the second source is registered. - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.back()->source_event_id, 5UL); - EXPECT_EQ(source_data.back()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); -} - -IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, - AttributionSrcImgSlowResponse_SourceRegistered) { - // Create a separate server as we cannot register a `ControllableHttpResponse` - // after the server starts. - auto https_server = std::make_unique<net::EmbeddedTestServer>( - net::EmbeddedTestServer::TYPE_HTTPS); - https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); - net::test_server::RegisterDefaultHandlers(https_server.get()); - https_server->ServeFilesFromSourceDirectory( - "content/test/data/attribution_reporting"); - https_server->ServeFilesFromSourceDirectory("content/test/data"); - SetupCrossSiteRedirector(https_server.get()); - - auto register_response = - std::make_unique<net::test_server::ControllableHttpResponse>( - https_server.get(), "/register_source"); - ASSERT_TRUE(https_server->Start()); - - GURL page_url = - https_server->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server->GetURL("d.test", "/register_source"); - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - - // Navigate cross-site before sending a response. - GURL page2_url = - https_server->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page2_url)); - - register_response->WaitForRequest(); - auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); - http_response->set_code(net::HTTP_OK); - http_response->AddCustomHeader("Access-Control-Allow-Origin", "*"); - http_response->AddCustomHeader( - "Attribution-Reporting-Register-Source", - R"({"source_event_id":"5", "destination":"https://advertiser.example"})"); - register_response->Send(http_response->ToResponseString()); - register_response->Done(); - - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - // Only the second source is registered. - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.back()->source_event_id, 5UL); - EXPECT_EQ(source_data.back()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); -} - -IN_PROC_BROWSER_TEST_F(AttributionSourceDeclarationBrowserTest, ImpressionTagClicked_ImpressionReceived) { SourceObserver source_observer(web_contents()); GURL page_url = @@ -1262,171 +987,4 @@ EXPECT_TRUE(source_observer.WaitForNavigationWithNoImpression()); } -class AttributionSourceDeclarationInvalidFiltersBrowserTest - : public AttributionSourceDeclarationBrowserTest, - public ::testing::WithParamInterface<const char*> {}; - -IN_PROC_BROWSER_TEST_P(AttributionSourceDeclarationInvalidFiltersBrowserTest, - AttributionSrcImgFiltersInvalid_SourceDropped) { - // Create a separate server as we cannot register a `ControllableHttpResponse` - // after the server starts. - auto https_server = std::make_unique<net::EmbeddedTestServer>( - net::EmbeddedTestServer::TYPE_HTTPS); - https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); - net::test_server::RegisterDefaultHandlers(https_server.get()); - https_server->ServeFilesFromSourceDirectory( - "content/test/data/attribution_reporting"); - https_server->ServeFilesFromSourceDirectory("content/test/data"); - SetupCrossSiteRedirector(https_server.get()); - - auto register_response = - std::make_unique<net::test_server::ControllableHttpResponse>( - https_server.get(), "/register_source"); - ASSERT_TRUE(https_server->Start()); - - GURL page_url = - https_server->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server->GetURL("d.test", "/register_source"); - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - - register_response->WaitForRequest(); - auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); - http_response->set_code(net::HTTP_MOVED_PERMANENTLY); - http_response->AddCustomHeader( - "Attribution-Reporting-Register-Source", - base::StrCat( - {R"({"source_event_id":"9", "destination":"https://advertiser.example", "filter_data":)", - GetParam(), "}"})); - http_response->AddCustomHeader("Location", "/register_source_headers.html"); - register_response->Send(http_response->ToResponseString()); - register_response->Done(); - - if (!data_host) - loop.Run(); - data_host->WaitForSourceData(/*num_source_data=*/1); - const auto& source_data = data_host->source_data(); - - // Only the second source is registered. - EXPECT_EQ(source_data.size(), 1u); - EXPECT_EQ(source_data.back()->source_event_id, 5UL); - EXPECT_EQ(source_data.back()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); -} - -INSTANTIATE_TEST_SUITE_P( - AttributionSourceDeclarationInvalidFilters, - AttributionSourceDeclarationInvalidFiltersBrowserTest, - ::testing::Values(R"("x")", // not a dictionary - R"({"a":"y"})", // dictionary value isn't an array - R"({"b":[8]})" // array value isn't a string - )); - -class AttributionSourceDeclarationFilterSizeBrowserTest - : public AttributionSourceDeclarationBrowserTest, - public ::testing::WithParamInterface<AttributionFilterSizeTestCase> {}; - -IN_PROC_BROWSER_TEST_P(AttributionSourceDeclarationFilterSizeBrowserTest, - AttributionSrcImgExcessiveFilterSize_SourceDropped) { - const AttributionFilterSizeTestCase& test_case = GetParam(); - - // Create a separate server as we cannot register a `ControllableHttpResponse` - // after the server starts. - auto https_server = std::make_unique<net::EmbeddedTestServer>( - net::EmbeddedTestServer::TYPE_HTTPS); - https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); - net::test_server::RegisterDefaultHandlers(https_server.get()); - https_server->ServeFilesFromSourceDirectory( - "content/test/data/attribution_reporting"); - https_server->ServeFilesFromSourceDirectory("content/test/data"); - SetupCrossSiteRedirector(https_server.get()); - - auto register_response = - std::make_unique<net::test_server::ControllableHttpResponse>( - https_server.get(), "/register_source"); - ASSERT_TRUE(https_server->Start()); - - GURL page_url = - https_server->GetURL("b.test", "/page_with_impression_creator.html"); - EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); - - MockAttributionHost host(web_contents()); - std::unique_ptr<MockDataHost> data_host; - base::RunLoop loop; - EXPECT_CALL(host, RegisterDataHost) - .WillOnce( - [&](mojo::PendingReceiver<blink::mojom::AttributionDataHost> host) { - data_host = GetRegisteredDataHost(std::move(host)); - loop.Quit(); - }); - - GURL register_url = https_server->GetURL("d.test", "/register_source"); - EXPECT_TRUE( - ExecJs(web_contents(), - JsReplace("createAttributionSourceImg($1);", register_url))); - - register_response->WaitForRequest(); - auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); - http_response->set_code(net::HTTP_MOVED_PERMANENTLY); - - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetStringKey("source_event_id", "9"); - dict.SetStringKey("destination", "https://advertiser.example"); - - base::Value filter_data(base::Value::Type::DICTIONARY); - for (auto [filter, values] : test_case.AsMap()) { - base::Value list(base::Value::Type::LIST); - for (auto value : values) { - list.Append(std::move(value)); - } - filter_data.SetKey(std::move(filter), std::move(list)); - } - dict.SetKey("filter_data", std::move(filter_data)); - - std::string json; - EXPECT_TRUE(base::JSONWriter::Write(dict, &json)); - - http_response->AddCustomHeader("Attribution-Reporting-Register-Source", - std::move(json)); - http_response->AddCustomHeader("Location", "/register_source_headers.html"); - register_response->Send(http_response->ToResponseString()); - register_response->Done(); - - if (!data_host) - loop.Run(); - - const size_t expected_sources = test_case.valid ? 2 : 1; - data_host->WaitForSourceData(/*num_source_data=*/expected_sources); - const auto& source_data = data_host->source_data(); - - EXPECT_EQ(source_data.size(), expected_sources); - EXPECT_EQ(source_data.back()->source_event_id, 5UL); - EXPECT_EQ(source_data.back()->destination, - url::Origin::Create(GURL("https://advertiser.example"))); -} - -INSTANTIATE_TEST_SUITE_P( - AttributionSourceDeclarationFilterSizes, - AttributionSourceDeclarationFilterSizeBrowserTest, - ::testing::ValuesIn(kAttributionFilterSizeTestCases), - /*name_generator=*/ - [](const ::testing::TestParamInfo<AttributionFilterSizeTestCase>& info) { - return info.param.description; - }); - -// TODO(apaseltiner): Add tests for overlong filters. - } // namespace content
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc index a482999..73e7904 100644 --- a/content/browser/renderer_host/back_forward_cache_impl.cc +++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -692,9 +692,9 @@ // flattened list, and return the tree if needed. std::unique_ptr<BackForwardCacheCanStoreTreeResult> result_tree; if (rfh->IsInPrimaryMainFrame() || main_frame_in_bfcache) { - result_tree = PopulateReasonsForDocumentAndDescendants( - rfh, rfh->GetLastCommittedOrigin(), flattened_result, - include_non_sticky, create_tree); + NotRestoredReasonBuilder builder(rfh, include_non_sticky, create_tree); + result_tree = builder.GetTreeResult(); + flattened_result.AddReasonsFrom(builder.GetFlattenedResult()); } else { result_tree = BackForwardCacheCanStoreTreeResult::CreateEmptyTree(rfh); } @@ -961,37 +961,55 @@ } } -std::unique_ptr<BackForwardCacheCanStoreTreeResult> -BackForwardCacheImpl::PopulateReasonsForDocumentAndDescendants( - RenderFrameHostImpl* rfh, - const url::Origin& main_origin, - BackForwardCacheCanStoreDocumentResult& flattened_result, +BackForwardCacheImpl::NotRestoredReasonBuilder::NotRestoredReasonBuilder( + RenderFrameHostImpl* root_rfh, bool include_non_sticky, - bool create_tree) { - BackForwardCacheCanStoreDocumentResult result_for_this_document; - PopulateReasonsForDocument(result_for_this_document, rfh, include_non_sticky); - flattened_result.AddReasonsFrom(result_for_this_document); + bool create_tree) + : root_rfh_(root_rfh), + bfcache_(root_rfh_->frame_tree_node() + ->navigator() + .controller() + .GetBackForwardCache()), + include_non_sticky_(include_non_sticky), + create_tree_(create_tree) { + // |root_rfh_| should be either primary main frame or back/forward cached + // page's main frame. + DCHECK(root_rfh_->IsInPrimaryMainFrame() || + (root_rfh_->IsInBackForwardCache() && root_rfh_->is_main_frame())); + // Populate the reasons and build the tree if needed. + tree_result_ = PopulateReasonsAndReturnSubtreeIfNeededFor(root_rfh_); +} + +BackForwardCacheImpl::NotRestoredReasonBuilder::~NotRestoredReasonBuilder() = + default; + +std::unique_ptr<BackForwardCacheCanStoreTreeResult> BackForwardCacheImpl:: + NotRestoredReasonBuilder::PopulateReasonsAndReturnSubtreeIfNeededFor( + RenderFrameHostImpl* rfh) { + BackForwardCacheCanStoreDocumentResult result_for_rfh; + // Populate |result_for_rfh| by checking the bfcache eligibility of |rfh|. + bfcache_.PopulateReasonsForDocument(result_for_rfh, rfh, include_non_sticky_); + flattened_result_.AddReasonsFrom(result_for_rfh); // Finds the reasons recursively and create the reason subtree for the // children if needed. BackForwardCacheCanStoreTreeResult::ChildrenVector children_result; for (size_t i = 0; i < rfh->child_count(); i++) { std::unique_ptr<BackForwardCacheCanStoreTreeResult> child = - PopulateReasonsForDocumentAndDescendants( - rfh->child_at(i)->current_frame_host(), main_origin, - flattened_result, include_non_sticky, create_tree); - if (create_tree) { + PopulateReasonsAndReturnSubtreeIfNeededFor( + rfh->child_at(i)->current_frame_host()); + if (create_tree_) { children_result.emplace_back(std::move(child)); } } - if (!create_tree) + if (!create_tree_) return nullptr; std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree( - new BackForwardCacheCanStoreTreeResult(rfh, main_origin, - result_for_this_document, - std::move(children_result))); + new BackForwardCacheCanStoreTreeResult( + rfh, root_rfh_->GetLastCommittedOrigin(), result_for_rfh, + std::move(children_result))); return tree; }
diff --git a/content/browser/renderer_host/back_forward_cache_impl.h b/content/browser/renderer_host/back_forward_cache_impl.h index 75541b14..d4d2209 100644 --- a/content/browser/renderer_host/back_forward_cache_impl.h +++ b/content/browser/renderer_host/back_forward_cache_impl.h
@@ -360,18 +360,6 @@ bool include_non_sticky, bool create_tree); - // Populates the reasons why this |rfh| and its subframes cannot enter the - // back/forward cache. - // |main_origin| is the origin of the outermost document. Refer to - // |PopulateReasonsForPage| for other params. - std::unique_ptr<BackForwardCacheCanStoreTreeResult> - PopulateReasonsForDocumentAndDescendants( - RenderFrameHostImpl* rfh, - const url::Origin& main_origin, - BackForwardCacheCanStoreDocumentResult& flattened_result, - bool include_non_sticky, - bool create_tree); - // Populates the sticky reasons for `rfh` without recursing into subframes. // Sticky features can't be unregistered and remain active for the rest of the // lifetime of the page. @@ -463,6 +451,56 @@ const UnloadSupportStrategy unload_strategy_; + // Helper class to iterate through the frame tree in the page and populate the + // NotRestoredReasons. + class NotRestoredReasonBuilder { + public: + // |rfh_root| represents the root document of the page. |include_non_sticky| + // controls whether or not we should record non-sticky reasons in the tree, + // and |create_tree| controls whether or not we should build + // |BackForwardCacheCanStoreTreeResult|. If |create_tree| is false, we only + // record them in a flattened list. + NotRestoredReasonBuilder(RenderFrameHostImpl* rfh_root, + bool include_non_sticky, + bool create_tree); + + ~NotRestoredReasonBuilder(); + + // Access the populated result. + BackForwardCacheCanStoreDocumentResult GetFlattenedResult() { + // TODO(yuzus): Check that |flattened_result_| and the tree result match. + return flattened_result_; + } + + std::unique_ptr<BackForwardCacheCanStoreTreeResult> GetTreeResult() { + return std::move(tree_result_); + } + + private: + // Populate NotRestoredReasons for the subtree whose root is |rfh| by + // iterating the frame tree and populating NotRestoredReasons in + // |flattened_result_|. This will return nullptr if |create_tree| is false, + // and returns a NotRestoredReason tree otherwise. + std::unique_ptr<BackForwardCacheCanStoreTreeResult> + PopulateReasonsAndReturnSubtreeIfNeededFor(RenderFrameHostImpl* rfh); + + // Root document of the tree. + RenderFrameHostImpl* const root_rfh_; + // BackForwardCacheImpl instance to access eligibility check functions. + BackForwardCacheImpl& bfcache_; + // Flattened list of NotRestoredReasons for the tree. This is empty at the + // start and has to be merged using |GetFlattenedResult()|. + BackForwardCacheCanStoreDocumentResult flattened_result_; + // Tree result of NotRestoredReasons. This is populated in the constructor. + std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_result_; + // If true, check both non-sticky reasons and sticky reasons. If false, + // check only sticky reasons. + const bool include_non_sticky_; + // If true, construct a tree of NotRestoredReasons representing the frame + // tree structure. If false, only populate |flattened_result_|. + const bool create_tree_; + }; + base::WeakPtrFactory<BackForwardCacheImpl> weak_factory_; };
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java index 2d6943b..0ce9841a 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -1855,6 +1855,12 @@ @Test @SmallTest + public void test_interactiveControlsWithLabels() { + performHtmlTest("interactive-controls-with-labels.html"); + } + + @Test + @SmallTest public void test_isInteresting() { performHtmlTest("isInteresting.html"); }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index ae062bd..cb8f39c 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1086,6 +1086,7 @@ "../browser/accessibility/snapshot_ax_tree_browsertest.cc", "../browser/accessibility/touch_accessibility_aura_browsertest.cc", "../browser/attribution_reporting/attribution_internals_browsertest.cc", + "../browser/attribution_reporting/attribution_src_browsertest.cc", "../browser/attribution_reporting/attributions_browsertest.cc", "../browser/attribution_reporting/attributions_origin_trial_browsertest.cc", "../browser/attribution_reporting/source_declaration_browsertest.cc",
diff --git a/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android-external.txt b/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android-external.txt index 5636d0f..04a2c360 100644 --- a/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android-external.txt +++ b/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android-external.txt
@@ -1,2 +1,2 @@ WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] -++Spinner text:"Choose your language." viewIdResName:"test" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton"] \ No newline at end of file +++Spinner text:"English" hint:"Choose your language." viewIdResName:"test" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton", hint="Choose your language."] \ No newline at end of file
diff --git a/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android.txt b/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android.txt index 9ab123e..cd370de 100644 --- a/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android.txt +++ b/content/test/data/accessibility/accname/desc-combobox-focusable-expected-android.txt
@@ -1 +1 @@ -android.widget.Spinner +android.widget.Spinner hint='Choose your language.' \ No newline at end of file
diff --git a/content/test/data/accessibility/accname/name-combobox-focusable-expected-android-external.txt b/content/test/data/accessibility/accname/name-combobox-focusable-expected-android-external.txt index 5636d0f..04a2c360 100644 --- a/content/test/data/accessibility/accname/name-combobox-focusable-expected-android-external.txt +++ b/content/test/data/accessibility/accname/name-combobox-focusable-expected-android-external.txt
@@ -1,2 +1,2 @@ WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] -++Spinner text:"Choose your language." viewIdResName:"test" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton"] \ No newline at end of file +++Spinner text:"English" hint:"Choose your language." viewIdResName:"test" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton", hint="Choose your language."] \ No newline at end of file
diff --git a/content/test/data/accessibility/accname/name-combobox-focusable-expected-android.txt b/content/test/data/accessibility/accname/name-combobox-focusable-expected-android.txt index c4d799a..954164e 100644 --- a/content/test/data/accessibility/accname/name-combobox-focusable-expected-android.txt +++ b/content/test/data/accessibility/accname/name-combobox-focusable-expected-android.txt
@@ -1 +1 @@ -android.widget.Spinner name='Choose your language.' +android.widget.Spinner name='English' hint='Choose your language.' \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt index db3df51..c3ab803 100644 --- a/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt +++ b/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt
@@ -1,5 +1,5 @@ WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] -++View text:"ComboBoxGrouping" canOpenPopUp actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxGrouping"] +++View hint:"ComboBoxGrouping" canOpenPopUp actions:[AX_FOCUS] bundle:[chromeRole="comboBoxGrouping", hint="ComboBoxGrouping"] ++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"] ++EditText hint:"TextFieldWithComboBox" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="TextFieldWithComboBox"] -++Spinner text:"ComboBoxMenuButton"" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton"] \ No newline at end of file +++Spinner text:"Select" hint:"ComboBoxMenuButton"" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton", hint="ComboBoxMenuButton""] \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt index 778c3c96..88265f8 100644 --- a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt +++ b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
@@ -1,6 +1,6 @@ WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] ++TextView text:"Choose a fruit, with text content" viewIdResName:"combo1-label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"] -++Spinner text:"Choose a fruit, with text content" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxMenuButton"] +++Spinner text:"Apple" hint:"Choose a fruit, with text content" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxMenuButton", hint="Choose a fruit, with text content"] ++ListView viewIdResName:"listbox1" stateDescription:"3 items" clickable CollectionInfo:[rows=3, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"] ++++View text:"Apple" viewIdResName:"combo1-0" stateDescription:"in list, item 1 of 3" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"] ++++View text:"Banana" viewIdResName:"combo1-1" stateDescription:"in list, item 2 of 3" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
diff --git a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android.txt b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android.txt index 77d084d..2fd4151 100644 --- a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android.txt +++ b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android.txt
@@ -1,6 +1,6 @@ android.webkit.WebView focusable focused scrollable ++android.widget.TextView name='Choose a fruit, with text content' -++android.widget.Spinner clickable collapsed focusable name='Choose a fruit, with text content' +++android.widget.Spinner clickable collapsed focusable name='Apple' hint='Choose a fruit, with text content' ++android.widget.ListView role_description='list box' clickable collection state_description='3 items' item_count=3 row_count=3 ++++android.view.View clickable collection_item focusable selected name='Apple' state_description='in list, item 1 of 3' ++++android.view.View clickable collection_item focusable name='Banana' state_description='in list, item 2 of 3' item_index=1 row_index=1
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android-external.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android-external.txt new file mode 100644 index 0000000..e3b7dc7 --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android-external.txt
@@ -0,0 +1,28 @@ +WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"] +++View text:"Test label" viewIdResName:"label1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="labelText"] +++TextView text:"aria label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"] +++TextView text:"Test label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"] +++EditText text:"Textbox" hint:"aria label" clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="aria label"] +++EditText text:"Textbox with aria-labelledby" hint:"Test label" clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Test label"] +++Button text:"aria label" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"] +++Button text:"Test label" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"] +++EditText hint:"aria label" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="aria label"] +++ListView stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"] +++++View text:"Option 1" viewIdResName:"option1" stateDescription:"in list, item 1 of 2" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++++View text:"Option 2" viewIdResName:"option2" stateDescription:"in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++EditText hint:"Test label" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="Test label"] +++ListView stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"] +++++View text:"Option 3" viewIdResName:"option3" stateDescription:"in list, item 1 of 2" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++++View text:"Option 4" viewIdResName:"option4" stateDescription:"in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++Spinner text:"Combobox" hint:"aria label" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton", hint="aria label"] +++ListView viewIdResName:"listbox1" stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"] +++++View text:"Option 5" viewIdResName:"option5" stateDescription:"in list, item 1 of 2" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++++View text:"Option 6" viewIdResName:"option6" stateDescription:"in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++Spinner text:"Combobox with aria-labelledby" hint:"Test label" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton", hint="Test label"] +++ListView viewIdResName:"listbox2" stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"] +++++View text:"Option 7" viewIdResName:"option7" stateDescription:"in list, item 1 of 2" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++++View text:"Option 8" viewIdResName:"option8" stateDescription:"in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"] +++Spinner text:"12:05" hint:"aria label" clickable focusable inputType:36 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="inputTime", clickableScore="300", hint="aria label", roleDescription="time picker"] +++Spinner text:"12:05" hint:"Test label" clickable focusable inputType:36 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="inputTime", clickableScore="300", hint="Test label", roleDescription="time picker"] +++Spinner text:"#E4E4E4" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="colorWell", clickableScore="300", roleDescription="color picker"] +++Spinner text:"#E4E4E4" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="colorWell", clickableScore="300", roleDescription="color picker"] \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android.txt new file mode 100644 index 0000000..0817c6c --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-android.txt
@@ -0,0 +1,28 @@ +android.webkit.WebView focusable focused scrollable +++android.view.View name='Test label' +++android.widget.TextView name='aria label' +++android.widget.TextView name='Test label' +++android.widget.EditText clickable editable_text has_non_empty_value name='Textbox' hint='aria label' text_change_added_count=7 +++android.widget.EditText clickable editable_text has_non_empty_value name='Textbox with aria-labelledby' hint='Test label' text_change_added_count=28 +++android.widget.Button role_description='button' clickable name='aria label' +++android.widget.Button role_description='button' clickable name='Test label' +++android.widget.EditText clickable editable_text focusable hint='aria label' input_type=1 +++android.widget.ListView role_description='list box' clickable collection state_description='2 items' item_count=2 row_count=2 +++++android.view.View collection_item focusable name='Option 1' state_description='in list, item 1 of 2' +++++android.view.View collection_item focusable name='Option 2' state_description='in list, item 2 of 2' item_index=1 row_index=1 +++android.widget.EditText clickable editable_text focusable hint='Test label' input_type=1 +++android.widget.ListView role_description='list box' clickable collection state_description='2 items' item_count=2 row_count=2 +++++android.view.View collection_item focusable name='Option 3' state_description='in list, item 1 of 2' +++++android.view.View collection_item focusable name='Option 4' state_description='in list, item 2 of 2' item_index=1 row_index=1 +++android.widget.Spinner clickable focusable name='Combobox' hint='aria label' +++android.widget.ListView role_description='list box' clickable collection state_description='2 items' item_count=2 row_count=2 +++++android.view.View collection_item focusable name='Option 5' state_description='in list, item 1 of 2' +++++android.view.View collection_item focusable name='Option 6' state_description='in list, item 2 of 2' item_index=1 row_index=1 +++android.widget.Spinner clickable focusable name='Combobox with aria-labelledby' hint='Test label' +++android.widget.ListView role_description='list box' clickable collection state_description='2 items' item_count=2 row_count=2 +++++android.view.View collection_item focusable name='Option 7' state_description='in list, item 1 of 2' +++++android.view.View collection_item focusable name='Option 8' state_description='in list, item 2 of 2' item_index=1 row_index=1 +++android.widget.Spinner role_description='time picker' clickable focusable name='12:05' hint='aria label' input_type=36 +++android.widget.Spinner role_description='time picker' clickable focusable name='12:05' hint='Test label' input_type=36 +++android.widget.Spinner role_description='color picker' clickable focusable name='#E4E4E4' +++android.widget.Spinner role_description='color picker' clickable focusable name='#E4E4E4' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-auralinux.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-auralinux.txt new file mode 100644 index 0000000..2601726d --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-auralinux.txt
@@ -0,0 +1,51 @@ +[document web] +++[label] label-for +++++[static] name='Test label' +++[section] name='aria label' +++++[static] name='Generic container' +++[section] name='Test label' labelled-by +++++[static] name='Generic container with aria-labelledby' +++[entry] name='aria label' selectable-text +++++[static] name='Textbox' +++[entry] name='Test label' selectable-text labelled-by +++++[static] name='Textbox with aria-labelledby' +++[push button] name='aria label' +++[push button] name='Test label' labelled-by +++[combo box] name='aria label' selectable-text +++[list box] +++++[list item] name='Option 1' selectable +++++[list item] name='Option 2' selectable +++[combo box] name='Test label' selectable-text labelled-by +++[list box] +++++[list item] name='Option 3' selectable +++++[list item] name='Option 4' selectable +++[combo box] name='aria label' controller-for +++++[static] name='Combobox' +++[list box] +++++[list item] name='Option 5' selectable +++++[list item] name='Option 6' selectable +++[combo box] name='Test label' controller-for labelled-by +++++[static] name='Combobox with aria-labelledby' +++[list box] controlled-by +++++[list item] name='Option 7' selectable +++++[list item] name='Option 8' selectable +++[dateeditor] name='aria label' +++++[section] +++++++[section] +++++++++[spin button] name='Hours aria label' current=12.000000 minimum=1.000000 maximum=12.000000 +++++++++[static] name=':' +++++++++[spin button] name='Minutes aria label' current=5.000000 minimum=0.000000 maximum=59.000000 +++++++++[static] name=' ' +++++++++[spin button] name='AM/PM aria label' current=2.000000 minimum=1.000000 maximum=2.000000 +++++[push button] name='Show time picker' +++[dateeditor] name='Test label' labelled-by +++++[section] +++++++[section] +++++++++[spin button] name='Hours Test label' labelled-by current=12.000000 minimum=1.000000 maximum=12.000000 +++++++++[static] name=':' +++++++++[spin button] name='Minutes Test label' labelled-by current=5.000000 minimum=0.000000 maximum=59.000000 +++++++++[static] name=' ' +++++++++[spin button] name='AM/PM Test label' labelled-by current=2.000000 minimum=1.000000 maximum=2.000000 +++++[push button] name='Show time picker' +++[push button] name='aria label' +++[push button] name='Test label' labelled-by \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-blink.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-blink.txt new file mode 100644 index 0000000..7ffdcd2b --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-blink.txt
@@ -0,0 +1,126 @@ +rootWebArea +++genericContainer ignored +++++genericContainer ignored +++++++labelText +++++++++staticText name='Test label' +++++++++++inlineTextBox name='Test label' +++++++genericContainer name='aria label' +++++++++staticText name='Generic container' +++++++++++inlineTextBox name='Generic container' +++++++genericContainer name='Test label' +++++++++staticText name='Generic container with aria-labelledby' +++++++++++inlineTextBox name='Generic container with aria-labelledby' +++++++textField name='aria label' value='Textbox' +++++++++staticText name='Textbox' +++++++++++inlineTextBox name='Textbox' +++++++textField name='Test label' value='Textbox with aria-labelledby' +++++++++staticText name='Textbox with aria-labelledby' +++++++++++inlineTextBox name='Textbox with aria-labelledby' +++++++button name='aria label' +++++++++staticText name='Button' +++++++++++inlineTextBox name='Button' +++++++button name='Test label' +++++++++staticText name='Button with aria-labelledby' +++++++++++inlineTextBox name='Button with aria-labelledby' +++++++textFieldWithComboBox name='aria label' activedescendantId=listBoxOption +++++++++genericContainer +++++++listBox +++++++++listBoxOption name='Option 1' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 1' +++++++++++++inlineTextBox name='Option 1' +++++++++listBoxOption name='Option 2' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 2' +++++++++++++inlineTextBox name='Option 2' +++++++textFieldWithComboBox name='Test label' activedescendantId=listBoxOption +++++++++genericContainer +++++++listBox +++++++++listBoxOption name='Option 3' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 3' +++++++++++++inlineTextBox name='Option 3' +++++++++listBoxOption name='Option 4' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 4' +++++++++++++inlineTextBox name='Option 4' +++++++comboBoxMenuButton name='aria label' value='Combobox' controlsIds=listBox +++++++++staticText name='Combobox' +++++++++++inlineTextBox name='Combobox' +++++++listBox +++++++++listBoxOption name='Option 5' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 5' +++++++++++++inlineTextBox name='Option 5' +++++++++listBoxOption name='Option 6' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 6' +++++++++++++inlineTextBox name='Option 6' +++++++comboBoxMenuButton name='Test label' value='Combobox with aria-labelledby' controlsIds=listBox +++++++++staticText name='Combobox with aria-labelledby' +++++++++++inlineTextBox name='Combobox with aria-labelledby' +++++++listBox +++++++++listBoxOption name='Option 7' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 7' +++++++++++++inlineTextBox name='Option 7' +++++++++listBoxOption name='Option 8' selected=false +++++++++++none ignored +++++++++++++staticText name='%E2%80%A2 ' +++++++++++++++inlineTextBox name='%E2%80%A2 ' +++++++++++staticText name='Option 8' +++++++++++++inlineTextBox name='Option 8' +++++++inputTime name='aria label' value='12:05' +++++++++genericContainer +++++++++++genericContainer +++++++++++++spinButton name='Hours aria label' placeholder='--' value='12' valueForRange=12.00 minValueForRange=1.00 maxValueForRange=12.00 +++++++++++++++staticText name='12' +++++++++++++++++inlineTextBox name='12' +++++++++++++staticText name=':' +++++++++++++++inlineTextBox name=':' +++++++++++++spinButton name='Minutes aria label' placeholder='--' value='05' valueForRange=5.00 minValueForRange=0.00 maxValueForRange=59.00 +++++++++++++++staticText name='05' +++++++++++++++++inlineTextBox name='05' +++++++++++++staticText name=' ' +++++++++++++++inlineTextBox name=' ' +++++++++++++spinButton name='AM/PM aria label' placeholder='--' value='PM' valueForRange=2.00 minValueForRange=1.00 maxValueForRange=2.00 +++++++++++++++staticText name='PM' +++++++++++++++++inlineTextBox name='PM' +++++++++popUpButton name='Show time picker' +++++++inputTime name='Test label' value='12:05' +++++++++genericContainer +++++++++++genericContainer +++++++++++++spinButton name='Hours Test label' placeholder='--' value='12' valueForRange=12.00 minValueForRange=1.00 maxValueForRange=12.00 +++++++++++++++staticText name='12' +++++++++++++++++inlineTextBox name='12' +++++++++++++staticText name=':' +++++++++++++++inlineTextBox name=':' +++++++++++++spinButton name='Minutes Test label' placeholder='--' value='05' valueForRange=5.00 minValueForRange=0.00 maxValueForRange=59.00 +++++++++++++++staticText name='05' +++++++++++++++++inlineTextBox name='05' +++++++++++++staticText name=' ' +++++++++++++++inlineTextBox name=' ' +++++++++++++spinButton name='AM/PM Test label' placeholder='--' value='PM' valueForRange=2.00 minValueForRange=1.00 maxValueForRange=2.00 +++++++++++++++staticText name='PM' +++++++++++++++++inlineTextBox name='PM' +++++++++popUpButton name='Show time picker' +++++++colorWell name='aria label' value='#e4e4e4' +++++++++genericContainer ignored +++++++++++genericContainer ignored +++++++colorWell name='Test label' value='#e4e4e4' +++++++++genericContainer ignored +++++++++++genericContainer ignored \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-mac.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-mac.txt new file mode 100644 index 0000000..36d070e --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-mac.txt
@@ -0,0 +1,51 @@ +AXWebArea +++AXGroup +++++AXStaticText AXValue='Test label' +++AXGroup AXDescription='aria label' +++++AXStaticText AXValue='Generic container' +++AXGroup AXDescription='Test label' +++++AXStaticText AXValue='Generic container with aria-labelledby' +++AXTextField AXDescription='aria label' AXValue='Textbox' +++++AXStaticText AXValue='Textbox' +++AXTextField AXTitle='Test label' AXValue='Textbox with aria-labelledby' +++++AXStaticText AXValue='Textbox with aria-labelledby' +++AXButton AXDescription='aria label' +++AXButton AXTitle='Test label' +++AXComboBox AXDescription='aria label' +++AXList +++++AXStaticText AXValue='Option 1' +++++AXStaticText AXValue='Option 2' +++AXComboBox AXTitle='Test label' +++AXList +++++AXStaticText AXValue='Option 3' +++++AXStaticText AXValue='Option 4' +++AXComboBox AXDescription='aria label' AXValue='Combobox' +++++AXStaticText AXValue='Combobox' +++AXList +++++AXStaticText AXValue='Option 5' +++++AXStaticText AXValue='Option 6' +++AXComboBox AXTitle='Test label' AXValue='Combobox with aria-labelledby' +++++AXStaticText AXValue='Combobox with aria-labelledby' +++AXList +++++AXStaticText AXValue='Option 7' +++++AXStaticText AXValue='Option 8' +++AXTimeField AXDescription='aria label' AXValue='12:05' +++++AXGroup +++++++AXGroup +++++++++AXIncrementor AXDescription='Hours aria label' AXValue=12 +++++++++AXStaticText AXValue=':' +++++++++AXIncrementor AXDescription='Minutes aria label' AXValue=5 +++++++++AXStaticText AXValue=' ' +++++++++AXIncrementor AXDescription='AM/PM aria label' AXValue=2 +++++AXPopUpButton AXDescription='Show time picker' +++AXTimeField AXTitle='Test label' AXValue='12:05' +++++AXGroup +++++++AXGroup +++++++++AXIncrementor AXTitle='Hours Test label' AXValue=12 +++++++++AXStaticText AXValue=':' +++++++++AXIncrementor AXTitle='Minutes Test label' AXValue=5 +++++++++AXStaticText AXValue=' ' +++++++++AXIncrementor AXTitle='AM/PM Test label' AXValue=2 +++++AXPopUpButton AXDescription='Show time picker' +++AXColorWell AXDescription='aria label' AXValue='rgb 0.89412 0.89412 0.89412 1' +++AXColorWell AXTitle='Test label' AXValue='rgb 0.89412 0.89412 0.89412 1' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-uia-win.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-uia-win.txt new file mode 100644 index 0000000..e32d805 --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-uia-win.txt
@@ -0,0 +1,51 @@ +Document +++Text +++++Text Name='Test label' +++Group Name='aria label' +++++Text Name='Generic container' +++Group Name='Test label' +++++Text Name='Generic container with aria-labelledby' +++Edit Name='aria label' Value.Value='Textbox' +++++Text Name='Textbox' +++Edit Name='Test label' Value.Value='Textbox with aria-labelledby' +++++Text Name='Textbox with aria-labelledby' +++Button Name='aria label' +++Button Name='Test label' +++ComboBox Name='aria label' ExpandCollapse.ExpandCollapseState='LeafNode' +++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++ListItem Name='Option 1' SelectionItem.IsSelected=false +++++ListItem Name='Option 2' SelectionItem.IsSelected=false +++ComboBox Name='Test label' ExpandCollapse.ExpandCollapseState='LeafNode' +++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++ListItem Name='Option 3' SelectionItem.IsSelected=false +++++ListItem Name='Option 4' SelectionItem.IsSelected=false +++ComboBox Name='aria label' ExpandCollapse.ExpandCollapseState='LeafNode' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Combobox' +++++Text Name='Combobox' +++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++ListItem Name='Option 5' SelectionItem.IsSelected=false +++++ListItem Name='Option 6' SelectionItem.IsSelected=false +++ComboBox Name='Test label' ExpandCollapse.ExpandCollapseState='LeafNode' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Combobox with aria-labelledby' +++++Text Name='Combobox with aria-labelledby' +++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false +++++ListItem Name='Option 7' SelectionItem.IsSelected=false +++++ListItem Name='Option 8' SelectionItem.IsSelected=false +++Group Name='aria label' Value.Value='12:05' +++++Group IsControlElement=false +++++++Group IsControlElement=false +++++++++Spinner Name='Hours aria label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=12.00 Value.Value='12' +++++++++Text Name=':' +++++++++Spinner Name='Minutes aria label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=5.00 Value.Value='05' +++++++++Text Name=' ' +++++++++Spinner Name='AM/PM aria label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=2.00 Value.Value='PM' +++++Button Name='Show time picker' ExpandCollapse.ExpandCollapseState='Collapsed' +++Group Name='Test label' Value.Value='12:05' +++++Group IsControlElement=false +++++++Group IsControlElement=false +++++++++Spinner Name='Hours Test label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=12.00 Value.Value='12' +++++++++Text Name=':' +++++++++Spinner Name='Minutes Test label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=59.00 RangeValue.Minimum=0.00 RangeValue.Value=5.00 Value.Value='05' +++++++++Text Name=' ' +++++++++Spinner Name='AM/PM Test label' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=2.00 RangeValue.Minimum=1.00 RangeValue.Value=2.00 Value.Value='PM' +++++Button Name='Show time picker' ExpandCollapse.ExpandCollapseState='Collapsed' +++Button Name='aria label' Value.Value='89% red 89% green 89% blue' +++Button Name='Test label' Value.Value='89% red 89% green 89% blue' \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels-expected-win.txt b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-win.txt new file mode 100644 index 0000000..30e6708 --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels-expected-win.txt
@@ -0,0 +1,51 @@ +ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE +++IA2_ROLE_LABEL +++++ROLE_SYSTEM_STATICTEXT name='Test label' +++IA2_ROLE_SECTION name='aria label' +++++ROLE_SYSTEM_STATICTEXT name='Generic container' +++IA2_ROLE_SECTION name='Test label' +++++ROLE_SYSTEM_STATICTEXT name='Generic container with aria-labelledby' +++ROLE_SYSTEM_TEXT name='aria label' value='Textbox' +++++ROLE_SYSTEM_STATICTEXT name='Textbox' +++ROLE_SYSTEM_TEXT name='Test label' value='Textbox with aria-labelledby' +++++ROLE_SYSTEM_STATICTEXT name='Textbox with aria-labelledby' +++ROLE_SYSTEM_PUSHBUTTON name='aria label' +++ROLE_SYSTEM_PUSHBUTTON name='Test label' +++ROLE_SYSTEM_COMBOBOX name='aria label' FOCUSABLE HASPOPUP +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Option 1' FOCUSABLE +++++ROLE_SYSTEM_LISTITEM name='Option 2' FOCUSABLE +++ROLE_SYSTEM_COMBOBOX name='Test label' FOCUSABLE HASPOPUP +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Option 3' FOCUSABLE +++++ROLE_SYSTEM_LISTITEM name='Option 4' FOCUSABLE +++ROLE_SYSTEM_COMBOBOX name='aria label' value='Combobox' FOCUSABLE HASPOPUP +++++ROLE_SYSTEM_STATICTEXT name='Combobox' +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Option 5' FOCUSABLE +++++ROLE_SYSTEM_LISTITEM name='Option 6' FOCUSABLE +++ROLE_SYSTEM_COMBOBOX name='Test label' value='Combobox with aria-labelledby' FOCUSABLE HASPOPUP +++++ROLE_SYSTEM_STATICTEXT name='Combobox with aria-labelledby' +++ROLE_SYSTEM_LIST +++++ROLE_SYSTEM_LISTITEM name='Option 7' FOCUSABLE +++++ROLE_SYSTEM_LISTITEM name='Option 8' FOCUSABLE +++ROLE_SYSTEM_GROUPING name='aria label' value='12:05' FOCUSABLE +++++IA2_ROLE_SECTION +++++++IA2_ROLE_SECTION +++++++++ROLE_SYSTEM_SPINBUTTON name='Hours aria label' value='12' FOCUSABLE +++++++++ROLE_SYSTEM_STATICTEXT name=':' +++++++++ROLE_SYSTEM_SPINBUTTON name='Minutes aria label' value='05' FOCUSABLE +++++++++ROLE_SYSTEM_STATICTEXT name=' ' +++++++++ROLE_SYSTEM_SPINBUTTON name='AM/PM aria label' value='PM' FOCUSABLE +++++ROLE_SYSTEM_BUTTONMENU name='Show time picker' FOCUSABLE HASPOPUP +++ROLE_SYSTEM_GROUPING name='Test label' value='12:05' FOCUSABLE +++++IA2_ROLE_SECTION +++++++IA2_ROLE_SECTION +++++++++ROLE_SYSTEM_SPINBUTTON name='Hours Test label' value='12' FOCUSABLE +++++++++ROLE_SYSTEM_STATICTEXT name=':' +++++++++ROLE_SYSTEM_SPINBUTTON name='Minutes Test label' value='05' FOCUSABLE +++++++++ROLE_SYSTEM_STATICTEXT name=' ' +++++++++ROLE_SYSTEM_SPINBUTTON name='AM/PM Test label' value='PM' FOCUSABLE +++++ROLE_SYSTEM_BUTTONMENU name='Show time picker' FOCUSABLE HASPOPUP +++IA2_ROLE_COLOR_CHOOSER name='aria label' value='89% red 89% green 89% blue' FOCUSABLE +++IA2_ROLE_COLOR_CHOOSER name='Test label' value='89% red 89% green 89% blue' FOCUSABLE \ No newline at end of file
diff --git a/content/test/data/accessibility/html/interactive-controls-with-labels.html b/content/test/data/accessibility/html/interactive-controls-with-labels.html new file mode 100644 index 0000000..b758c3b --- /dev/null +++ b/content/test/data/accessibility/html/interactive-controls-with-labels.html
@@ -0,0 +1,42 @@ +<html> +<body> + <label id="label1">Test label</label> + <div aria-label="aria label">Generic container</div> + <div aria-labelledby="label1">Generic container with aria-labelledby</div> + + <div role="textbox" aria-label="aria label">Textbox</div> + <div role="textbox" aria-labelledby="label1">Textbox with aria-labelledby</div> + + <div role="button" aria-label="aria label">Button</div> + <div role="button" aria-labelledby="label1">Button with aria-labelledby</div> + + <input type="text" role="combobox" aria-activedescendant="option1" aria-label="aria label"> + <ul role="listbox"> + <li id="option1" role="option">Option 1</li> + <li id="option2" role="option">Option 2</li> + </ul> + <input type="text" role="combobox" aria-activedescendant="option3" aria-labelledby="label1"> + <ul role="listbox"> + <li id="option3" role="option">Option 3</li> + <li id="option4" role="option">Option 4</li> + </ul> + + <div role="combobox" tabindex="0" aria-controls="listbox2" aria-label="aria label">Combobox</div> + <ul id="listbox1" role="listbox"> + <li id="option5" role="option">Option 5</li> + <li id="option6" role="option">Option 6</li> + </ul> + + <div role="combobox" tabindex="0" aria-controls="listbox2" aria-labelledby="label1">Combobox with aria-labelledby</div> + <ul id="listbox2" role="listbox"> + <li id="option7" role="option">Option 7</li> + <li id="option8" role="option">Option 8</li> + </ul> + + <input type="time" value="12:05" aria-label="aria label"> + <input type="time" value="12:05" aria-labelledby="label1"> + + <input type="color" value="#e4e4e4" aria-label="aria label"> + <input type="color" value="#e4e4e4" aria-labelledby="label1"> +</body> +</html>
diff --git a/content/test/data/attribution_reporting/register_impression.js b/content/test/data/attribution_reporting/register_impression.js index d9526084..8f7bcbb0 100644 --- a/content/test/data/attribution_reporting/register_impression.js +++ b/content/test/data/attribution_reporting/register_impression.js
@@ -71,7 +71,7 @@ return anchor; } -function createAttributionSourceImg(src) { +function createAttributionSrcImg(src) { const img = document.createElement('img'); img.setAttribute('target', "top"); img.width = 100;
diff --git a/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html b/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html new file mode 100644 index 0000000..4225c14 --- /dev/null +++ b/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html
@@ -0,0 +1 @@ +Redirect chain which registers source -> trigger
diff --git a/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html.mock-http-headers b/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html.mock-http-headers new file mode 100644 index 0000000..318247b3 --- /dev/null +++ b/content/test/data/attribution_reporting/register_source_trigger_redirect_chain.html.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 301 Yo +Attribution-Reporting-Register-Source:{"source_event_id":"5","destination":"https://advertiser.example"} +Location: /register_trigger_headers.html \ No newline at end of file
diff --git a/content/test/data/attribution_reporting/register_trigger_headers.html b/content/test/data/attribution_reporting/register_trigger_headers.html new file mode 100644 index 0000000..d40c54c6 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers.html
@@ -0,0 +1 @@ +Registers a trigger with headers
diff --git a/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers new file mode 100644 index 0000000..a1ddf2c4 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "10"}] \ No newline at end of file
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_all_params.html b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html new file mode 100644 index 0000000..9555c15 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html
@@ -0,0 +1 @@ +Registers a trigger with headers using all parameters and multiple event trigger datas
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers new file mode 100644 index 0000000..d4aea3e1 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers_all_params.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "1","priority":"5","deduplication_key":"1024"},{"trigger_data":"2","priority":"10"}] \ No newline at end of file
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html b/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html new file mode 100644 index 0000000..60e8a1d1 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html
@@ -0,0 +1 @@ +Registers a trigger with a bad header value
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html.mock-http-headers new file mode 100644 index 0000000..09ca1ff --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_headers_then_redirect_invalid.html.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 301 Yo +Attribution-Reporting-Register-Event-Trigger:{[]} +Location: /register_trigger_headers.html \ No newline at end of file
diff --git a/content/test/data/attribution_reporting/register_trigger_source_trigger.html b/content/test/data/attribution_reporting/register_trigger_source_trigger.html new file mode 100644 index 0000000..419e3319 --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_source_trigger.html
@@ -0,0 +1 @@ +Redirect chain which registers trigger -> source -> trigger
diff --git a/content/test/data/attribution_reporting/register_trigger_source_trigger.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_source_trigger.html.mock-http-headers new file mode 100644 index 0000000..65f4c0ea --- /dev/null +++ b/content/test/data/attribution_reporting/register_trigger_source_trigger.html.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 301 Yo +Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "5"}] +Location: /register_source_trigger_redirect_chain.html \ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt index b5c5a5d..683d4c3 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -408,14 +408,6 @@ crbug.com/angleproject/6430 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/texturefiltering/cube_sizes_04.html [ Failure ] crbug.com/angleproject/6430 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/texturespecification/basic_copyteximage2d.html [ Failure ] crbug.com/angleproject/6430 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/texturespecification/basic_copytexsubimage2d.html [ Failure ] -# Post Python 3 conversion: crbug.com/1266604 -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] conformance/textures/canvas_sub_rectangle/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/clipping.html [ Failure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/fborender/shared_colorbuffer_00.html [ Failure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/fborender/shared_colorbuffer_01.html [ Failure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/fborender/shared_colorbuffer_02.html [ Failure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/fborender/shared_colorbuffer_clear.html [ Failure ] -crbug.com/1271941 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/fragdepth.html [ Failure ] crbug.com/1298619 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/occlusionquery_strict.html [ Failure ] ###################################################################### @@ -790,6 +782,10 @@ crbug.com/1175229 [ android android-pixel-4 angle-opengles passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb565-rgb-unsigned_short_5_6_5.html [ Failure ] crbug.com/1175232 [ android android-pixel-4 angle-opengles passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ] +crbug.com/1302143 [ android android-pixel-4 no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_lines.html [ RetryOnFailure ] +crbug.com/1302143 [ android android-pixel-4 no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_points.html [ RetryOnFailure ] +crbug.com/1302143 [ android android-pixel-4 no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ RetryOnFailure ] + crbug.com/1191030 [ android android-pixel-4 ] conformance/textures/misc/video-rotation.html [ Failure ] crbug.com/1239079 [ android no-passthrough ] conformance2/transform_feedback/too-small-buffers.html [ Failure ]
diff --git a/extensions/browser/extension_api_frame_id_map.cc b/extensions/browser/extension_api_frame_id_map.cc index 49f8b0b0..b28fa94 100644 --- a/extensions/browser/extension_api_frame_id_map.cc +++ b/extensions/browser/extension_api_frame_id_map.cc
@@ -138,31 +138,6 @@ return rfh; } -content::RenderFrameHost* -ExtensionApiFrameIdMap::GetRenderFrameHostByDocumentId( - const DocumentId& document_id) { - auto iter = document_id_map_.find(document_id); - if (iter == document_id_map_.end()) - return nullptr; - return &iter->second->render_frame_host(); -} - -ExtensionApiFrameIdMap::DocumentId ExtensionApiFrameIdMap::DocumentIdFromString( - const std::string& document_id) { - if (document_id.length() != 32) - return DocumentId(); - - base::StringPiece string_piece(document_id); - uint64_t high = 0; - uint64_t low = 0; - if (!base::HexStringToUInt64(string_piece.substr(0, 16), &high) || - !base::HexStringToUInt64(string_piece.substr(16, 16), &low)) { - return DocumentId(); - } - - return base::UnguessableToken::Deserialize(high, low); -} - ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::KeyToValue( content::GlobalRenderFrameHostId key, bool require_live_frame) const { @@ -312,14 +287,10 @@ ExtensionApiFrameIdMap::ExtensionDocumentUserData::ExtensionDocumentUserData( content::RenderFrameHost* render_frame_host) : content::DocumentUserData<ExtensionDocumentUserData>(render_frame_host), - document_id_(DocumentId::Create()) { - Get()->document_id_map_[document_id_] = this; -} + document_id_(DocumentId::Create()) {} ExtensionApiFrameIdMap::ExtensionDocumentUserData:: - ~ExtensionDocumentUserData() { - Get()->document_id_map_.erase(document_id_); -} + ~ExtensionDocumentUserData() = default; DOCUMENT_USER_DATA_KEY_IMPL(ExtensionApiFrameIdMap::ExtensionDocumentUserData);
diff --git a/extensions/browser/extension_api_frame_id_map.h b/extensions/browser/extension_api_frame_id_map.h index 5c976bee..842eab5a 100644 --- a/extensions/browser/extension_api_frame_id_map.h +++ b/extensions/browser/extension_api_frame_id_map.h
@@ -145,14 +145,6 @@ content::WebContents* web_contents, int frame_id); - // Find the current RenderFrameHost for a given extension documentID. - // Returns nullptr if not found. - content::RenderFrameHost* GetRenderFrameHostByDocumentId( - const DocumentId& document_id); - - // Parses a serialized document id string to a DocumentId. - static DocumentId DocumentIdFromString(const std::string& document_id); - // Retrieves the FrameData for a given RenderFrameHost id. [[nodiscard]] FrameData GetFrameData(content::GlobalRenderFrameHostId rfh_id); @@ -196,10 +188,6 @@ // continue after a frame is unloaded can access the FrameData. using FrameDataMap = std::map<content::GlobalRenderFrameHostId, FrameData>; FrameDataMap deleted_frame_data_map_; - - // Holds mapping of DocumentIds to ExtensionDocumentUserData objects. - using DocumentIdMap = std::map<DocumentId, ExtensionDocumentUserData*>; - DocumentIdMap document_id_map_; }; } // namespace extensions
diff --git a/infra/config/generated/builders/ci/android-backuprefptr-arm-fyi-rel/properties.json b/infra/config/generated/builders/ci/android-backuprefptr-arm-fyi-rel/properties.json index fc3bce3..b232002 100644 --- a/infra/config/generated/builders/ci/android-backuprefptr-arm-fyi-rel/properties.json +++ b/infra/config/generated/builders/ci/android-backuprefptr-arm-fyi-rel/properties.json
@@ -1,9 +1,8 @@ { - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org", - "use_luci_auth": true + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "jobs": 250, + "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": { "column_keys": [],
diff --git a/infra/config/generated/builders/ci/android-backuprefptr-arm64-fyi-rel/properties.json b/infra/config/generated/builders/ci/android-backuprefptr-arm64-fyi-rel/properties.json index fc3bce3..b232002 100644 --- a/infra/config/generated/builders/ci/android-backuprefptr-arm64-fyi-rel/properties.json +++ b/infra/config/generated/builders/ci/android-backuprefptr-arm64-fyi-rel/properties.json
@@ -1,9 +1,8 @@ { - "$build/goma": { - "enable_ats": true, - "rpc_extra_params": "?prod", - "server_host": "goma.chromium.org", - "use_luci_auth": true + "$build/reclient": { + "instance": "rbe-chromium-trusted", + "jobs": 250, + "metrics_project": "chromium-reclient-metrics" }, "$recipe_engine/resultdb/test_presentation": { "column_keys": [],
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index b6d28eb0..c82f3ee 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -3653,31 +3653,6 @@ short_name: "x86" } builders { - name: "buildbucket/luci.chromium.ci/android-pie-arm64-wpt-rel-non-cq" - category: "builder_tester|arm64" - short_name: "P-WPT" - } - builders { - name: "buildbucket/luci.chromium.ci/android-chrome-pie-x86-wpt-fyi-rel" - category: "builder_tester|web-platform" - short_name: "P" - } - builders { - name: "buildbucket/luci.chromium.ci/android-weblayer-pie-x86-wpt-fyi-rel" - category: "builder_tester|weblayer" - short_name: "P" - } - builders { - name: "buildbucket/luci.chromium.ci/android-weblayer-pie-x86-wpt-smoketest" - category: "builder_tester|weblayer" - short_name: "P" - } - builders { - name: "buildbucket/luci.chromium.ci/android-webview-pie-x86-wpt-fyi-rel" - category: "builder_tester|webview" - short_name: "P" - } - builders { name: "buildbucket/luci.chromium.ci/android-cronet-x86-dbg-kitkat-tests" category: "cronet|test" short_name: "k" @@ -3732,6 +3707,31 @@ category: "tester|webview" short_name: "12" } + builders { + name: "buildbucket/luci.chromium.ci/android-chrome-pie-x86-wpt-fyi-rel" + category: "wpt|chrome" + short_name: "p-x86" + } + builders { + name: "buildbucket/luci.chromium.ci/android-weblayer-pie-x86-wpt-fyi-rel" + category: "wpt|weblayer" + short_name: "p-x86" + } + builders { + name: "buildbucket/luci.chromium.ci/android-weblayer-pie-x86-wpt-smoketest" + category: "wpt|weblayer" + short_name: "p-x86" + } + builders { + name: "buildbucket/luci.chromium.ci/android-pie-arm64-wpt-rel-non-cq" + category: "wpt|webview" + short_name: "p-arm64" + } + builders { + name: "buildbucket/luci.chromium.ci/android-webview-pie-x86-wpt-fyi-rel" + category: "wpt|webview" + short_name: "p-x86" + } header { oncalls { name: "Chromium"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star index 1a6ad001..b37152b0 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -42,8 +42,8 @@ ci.builder( name = "android-pie-arm64-wpt-rel-non-cq", console_view_entry = consoles.console_view_entry( - category = "builder_tester|arm64", - short_name = "P-WPT", + category = "wpt|webview", + short_name = "p-arm64", ), goma_backend = None, reclient_jobs = rbe_jobs.DEFAULT, @@ -53,8 +53,8 @@ ci.builder( name = "android-chrome-pie-x86-wpt-fyi-rel", console_view_entry = consoles.console_view_entry( - category = "builder_tester|web-platform", - short_name = "P", + category = "wpt|chrome", + short_name = "p-x86", ), goma_backend = None, reclient_jobs = rbe_jobs.DEFAULT, @@ -74,8 +74,8 @@ ci.builder( name = "android-weblayer-pie-x86-wpt-fyi-rel", console_view_entry = consoles.console_view_entry( - category = "builder_tester|weblayer", - short_name = "P", + category = "wpt|weblayer", + short_name = "p-x86", ), goma_backend = None, reclient_jobs = rbe_jobs.DEFAULT, @@ -85,8 +85,8 @@ ci.builder( name = "android-weblayer-pie-x86-wpt-smoketest", console_view_entry = consoles.console_view_entry( - category = "builder_tester|weblayer", - short_name = "P", + category = "wpt|weblayer", + short_name = "p-x86", ), goma_backend = None, reclient_jobs = rbe_jobs.DEFAULT, @@ -96,8 +96,8 @@ ci.builder( name = "android-webview-pie-x86-wpt-fyi-rel", console_view_entry = consoles.console_view_entry( - category = "builder_tester|webview", - short_name = "P", + category = "wpt|webview", + short_name = "p-x86", ), goma_backend = None, reclient_jobs = rbe_jobs.DEFAULT,
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index 9a03670..51ce50c 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -127,6 +127,9 @@ ), notifies = ["chrome-memory-safety"], os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT, + goma_backend = None, + reclient_jobs = rbe_jobs.DEFAULT, + reclient_instance = rbe_instance.DEFAULT, ) ci.builder( @@ -138,6 +141,9 @@ ), notifies = ["chrome-memory-safety"], os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT, + goma_backend = None, + reclient_jobs = rbe_jobs.DEFAULT, + reclient_instance = rbe_instance.DEFAULT, ) ci.builder(
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm index 2137647..e5b25e25 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -38,8 +38,11 @@ using chrome_test_util::AddToBookmarksButton; using chrome_test_util::AddToReadingListButton; using chrome_test_util::CloseTabMenuButton; +using chrome_test_util::TabGridCellAtIndex; +using chrome_test_util::TabGridNormalModePageControl; using chrome_test_util::TabGridSearchBar; using chrome_test_util::TabGridSearchCancelButton; +using chrome_test_util::TabGridSearchModeToolbar; using chrome_test_util::TabGridSearchTabsButton; using chrome_test_util::TabGridSelectTabsMenuButton; @@ -59,9 +62,19 @@ const CFTimeInterval kSnackbarAppearanceTimeout = 5; const CFTimeInterval kSnackbarDisappearanceTimeout = 11; +id<GREYMatcher> TabGridCell() { + return grey_allOf(grey_kindOfClassName(@"GridCell"), + grey_sufficientlyVisible(), nil); +} + id<GREYMatcher> TabWithTitle(NSString* title) { - return grey_allOf(grey_accessibilityLabel(title), grey_sufficientlyVisible(), - nil); + return grey_allOf(TabGridCell(), grey_accessibilityLabel(title), + grey_sufficientlyVisible(), nil); +} + +id<GREYMatcher> TabWithTitleAndIndex(char* title, unsigned int index) { + return grey_allOf(TabWithTitle([NSString stringWithUTF8String:title]), + TabGridCellAtIndex(index), nil); } // Identifer for cell at given |index| in the tab grid. @@ -104,6 +117,12 @@ base::test::ios::kWaitForUIElementTimeout, condition); GREYAssertTrue(fullscreenAchieved, @"BrowserViewHiderView still shown"); } + +// Returns a matcher for the scrim view on the tab search. +id<GREYMatcher> SearchScrim() { + return grey_accessibilityID(kTabGridScrimIdentifier); +} + } // namespace @interface TabGridTestCase : WebHttpServerChromeTestCase { @@ -118,11 +137,18 @@ - (AppLaunchConfiguration)appConfigurationForTestCase { AppLaunchConfiguration config; - - if ([self isRunningTest:@selector(testEnterExitSearch)]) { - config.features_enabled.push_back(kTabsSearch); + std::vector<SEL> searchTests = { + @selector(testEnterExitSearch), + @selector(testTabGridResetAfterExitingSearch), + @selector(testScrimVisibleInSearchModeWhenSearchBarIsEmpty), + @selector(testTapOnSearchScrimExitsSearchMode), + @selector(testSearchRegularOpenTabs)}; + for (SEL test : searchTests) { + if ([self isRunningTest:test]) { + config.features_enabled.push_back(kTabsSearch); + break; + } } - return config; } @@ -1208,15 +1234,130 @@ [ChromeEarlGrey openNewTab]; [ChromeEarlGrey showTabSwitcher]; + // Enter search mode. [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()] performAction:grey_tap()]; - [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridSearchBar()] - performAction:grey_typeText(@"text")]; + + // Verify that search mode is active. + [[EarlGrey + selectElementWithMatcher:chrome_test_util::TabGridSearchModeToolbar()] + assertWithMatcher:grey_notNil()]; + + // Exit search mode. [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()] performAction:grey_tap()]; - GREYAssertEqual([ChromeEarlGrey mainTabCount], 2, - @"All tabs did not return after exiting search."); + // Verify that normal mode is active. + [[EarlGrey + selectElementWithMatcher:chrome_test_util::TabGridNormalModePageControl()] + assertWithMatcher:grey_notNil()]; +} + +// Tests that exiting search mode reset the tabs count to the original number. +- (void)testTabGridResetAfterExitingSearch { + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey showTabSwitcher]; + + // Enter search mode & search with a query that produce no results. + [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()] + performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridSearchBar()] + performAction:grey_typeText(@"hello")]; + + // Verify that search reduced the number of visible tabs. + [self verifyVisibleTabsCount:0]; + + // Exit search mode & verify that tabs grid was reset. + [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()] + performAction:grey_tap()]; + [self verifyVisibleTabsCount:2]; +} + +// Tests that the scrim view is always shown when the search bar is empty in the +// search mode. +- (void)testScrimVisibleInSearchModeWhenSearchBarIsEmpty { + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey showTabSwitcher]; + + // Enter search mode. + [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()] + performAction:grey_tap()]; + + // Upon entry, the search bar is empty. Verify that scrim is visible. + [[EarlGrey selectElementWithMatcher:SearchScrim()] + assertWithMatcher:grey_notNil()]; + + // Searching with any query should render scrim invisible. + [[EarlGrey selectElementWithMatcher:TabGridSearchBar()] + performAction:grey_typeText(@"text")]; + [[EarlGrey selectElementWithMatcher:SearchScrim()] + assertWithMatcher:grey_nil()]; + + // Clearing search bar text should render scrim visible again. + [[EarlGrey selectElementWithMatcher:TabGridSearchBar()] + performAction:grey_clearText()]; + [[EarlGrey selectElementWithMatcher:SearchScrim()] + assertWithMatcher:grey_notNil()]; + + // Cancel search mode. + [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()] + performAction:grey_tap()]; + + // Verify that scrim is not visible anymore. + [[EarlGrey selectElementWithMatcher:SearchScrim()] + assertWithMatcher:grey_nil()]; +} + +// Tests that tapping on the scrim view while in search mode dismisses the scrim +// and exits search mode. +- (void)testTapOnSearchScrimExitsSearchMode { + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey showTabSwitcher]; + + // Enter search mode. + [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()] + performAction:grey_tap()]; + + // Tap on scrim. + [[EarlGrey selectElementWithMatcher:SearchScrim()] performAction:grey_tap()]; + + // Verify that search mode is exit, scrim not visible, and transition to + // normal mode was successful. + [[EarlGrey selectElementWithMatcher:SearchScrim()] + assertWithMatcher:grey_nil()]; + [[EarlGrey selectElementWithMatcher:TabGridNormalModePageControl()] + assertWithMatcher:grey_notNil()]; + [self verifyVisibleTabsCount:2]; +} + +// Tests that searching in open tabs in the regular mode will filter the tabs +// correctly. +- (void)testSearchRegularOpenTabs { + [self loadTestURLsInNewTabs]; + [ChromeEarlGrey showTabSwitcher]; + + [self verifyVisibleTabsCount:4]; + + // Enter search mode. + [[EarlGrey selectElementWithMatcher:TabGridSearchTabsButton()] + performAction:grey_tap()]; + + // Searching with the word "Page" should match only 3 results. + [[EarlGrey selectElementWithMatcher:TabGridSearchBar()] + performAction:grey_typeText(@"Page")]; + [self verifyVisibleTabsCount:3]; + + // Verify that search results are correct and in the expected order. + [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle1, 0)] + assertWithMatcher:grey_notNil()]; + [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle2, 1)] + assertWithMatcher:grey_notNil()]; + [[EarlGrey selectElementWithMatcher:TabWithTitleAndIndex(kTitle4, 2)] + assertWithMatcher:grey_notNil()]; + + // Cancel search mode. + [[EarlGrey selectElementWithMatcher:TabGridSearchCancelButton()] + performAction:grey_tap()]; } #pragma mark - Helper Methods @@ -1232,6 +1373,23 @@ [ChromeEarlGrey waitForWebStateContainingText:kResponse3]; } +- (void)loadTestURLsInNewTabs { + [ChromeEarlGrey loadURL:_URL1]; + [ChromeEarlGrey waitForWebStateContainingText:kResponse1]; + + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey loadURL:_URL2]; + [ChromeEarlGrey waitForWebStateContainingText:kResponse2]; + + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey loadURL:_URL3]; + [ChromeEarlGrey waitForWebStateContainingText:kResponse3]; + + [ChromeEarlGrey openNewTab]; + [ChromeEarlGrey loadURL:_URL4]; + [ChromeEarlGrey waitForWebStateContainingText:kResponse4]; +} + // Loads a URL in a new tab and deletes it to populate Recent Tabs. Then, // navigates to the Recent tabs via tab grid. - (void)prepareRecentTabWithURL:(const GURL&)URL @@ -1337,4 +1495,21 @@ @"Snackbar did not disappear."); } +// Verifies that the tab grid has exactly |expectedCount| tabs. +- (void)verifyVisibleTabsCount:(NSUInteger)expectedCount { + // Verify that the cell # |expectedCount| exist. + if (expectedCount == 0) { + [[EarlGrey selectElementWithMatcher:TabGridCell()] + assertWithMatcher:grey_nil()]; + } else { + [[[EarlGrey selectElementWithMatcher:TabGridCell()] + atIndex:expectedCount - 1] assertWithMatcher:grey_notNil()]; + } + // Then verify that there is no more cells after that. + [[EarlGrey + selectElementWithMatcher:grey_allOf(TabGridCell(), + TabGridCellAtIndex(expectedCount), + nil)] assertWithMatcher:grey_nil()]; +} + @end
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h index 738ab80..bc2e0810 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.h +++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -487,6 +487,11 @@ // the tab grid. id<GREYMatcher> TabGridOtherDevicesPanelButton(); +// Returns a matcher that matches tab grid normal mode page control - The +// PageControl panel always exist only on the tab grid normal mode, So this can +// be used to validate that the tab grid normal mode is active. +id<GREYMatcher> TabGridNormalModePageControl(); + // Returns a matcher for the tab grid background. id<GREYMatcher> TabGridBackground(); @@ -642,6 +647,9 @@ // Returns a matcher for the tab grid search cancel button. id<GREYMatcher> TabGridSearchCancelButton(); +// Returns a matcher for the tab grid search mode toolbar. +id<GREYMatcher> TabGridSearchModeToolbar(); + } // namespace chrome_test_util #endif // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm index 89f1ce4..aa7d80ca 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -602,6 +602,10 @@ return [ChromeMatchersAppInterface tabGridOtherDevicesPanelButton]; } +id<GREYMatcher> TabGridNormalModePageControl() { + return [ChromeMatchersAppInterface tabGridNormalModePageControl]; +} + id<GREYMatcher> TabGridBackground() { return [ChromeMatchersAppInterface tabGridBackground]; } @@ -801,4 +805,8 @@ return [ChromeMatchersAppInterface tabGridSearchCancelButton]; } +id<GREYMatcher> TabGridSearchModeToolbar() { + return [ChromeMatchersAppInterface tabGridSearchModeToolbar]; +} + } // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h index 3a5fa9ae..008b19d 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h +++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -474,6 +474,11 @@ // the tab grid. + (id<GREYMatcher>)tabGridOtherDevicesPanelButton; +// Returns a matcher that matches tab grid normal mode page control - The +// PageControl panel always exist only on the tab grid normal mode, So this can +// be used to validate that the tab grid normal mode is active. ++ (id<GREYMatcher>)tabGridNormalModePageControl; + // Returns the GREYMatcher for the background of the tab grid. + (id<GREYMatcher>)tabGridBackground; @@ -627,6 +632,9 @@ // Returns a matcher for the tab grid search cancel button. + (id<GREYMatcher>)tabGridSearchCancelButton; +// Returns a matcher for the tab grid search mode toolbar. ++ (id<GREYMatcher>)tabGridSearchModeToolbar; + @end #endif // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm index 78417e2..cb9aa6f 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -930,6 +930,18 @@ return grey_accessibilityID(kTabGridRemoteTabsPageButtonIdentifier); } ++ (id<GREYMatcher>)tabGridNormalModePageControl { + return grey_allOf( + grey_kindOfClassName(@"UIControl"), + grey_descendant( + [ChromeMatchersAppInterface tabGridIncognitoTabsPanelButton]), + grey_descendant([ChromeMatchersAppInterface tabGridOpenTabsPanelButton]), + grey_descendant( + [ChromeMatchersAppInterface tabGridOtherDevicesPanelButton]), + grey_ancestor(grey_kindOfClassName(@"UIToolbar")), + grey_sufficientlyVisible(), nil); +} + + (id<GREYMatcher>)tabGridBackground { return grey_accessibilityID(kGridBackgroundIdentifier); } @@ -1214,4 +1226,12 @@ grey_sufficientlyVisible(), nil); } ++ (id<GREYMatcher>)tabGridSearchModeToolbar { + return grey_allOf( + grey_kindOfClassName(@"UIToolbar"), + grey_descendant([ChromeMatchersAppInterface tabGridSearchBar]), + grey_descendant([ChromeMatchersAppInterface tabGridSearchCancelButton]), + grey_sufficientlyVisible(), nil); +} + @end
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc index 7cd58a2..61febee3 100644 --- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc +++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -40,7 +40,7 @@ namespace media { namespace { - +const uint32_t kDefaultGOPLength = 3000; const uint32_t kDefaultTargetBitrate = 5000000u; const size_t kMaxFrameRateNumerator = 30; const size_t kMaxFrameRateDenominator = 1; @@ -402,7 +402,7 @@ frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator; bitrate_ = config.bitrate; bitstream_buffer_size_ = config.input_visible_size.GetArea(); - gop_length_ = config.gop_length; + gop_length_ = config.gop_length.value_or(kDefaultGOPLength); low_latency_mode_ = config.require_low_delay; if (config.HasTemporalLayer()) @@ -838,11 +838,9 @@ } } - if (gop_length_.has_value()) { - var.ulVal = gop_length_.value(); - hr = codec_api_->SetValue(&CODECAPI_AVEncMPVGOPSize, &var); - RETURN_ON_HR_FAILURE(hr, "Couldn't set low keyframe interval", false); - } + var.ulVal = gop_length_; + hr = codec_api_->SetValue(&CODECAPI_AVEncMPVGOPSize, &var); + RETURN_ON_HR_FAILURE(hr, "Couldn't set keyframe interval", false); if (S_OK == codec_api_->IsModifiable(&CODECAPI_AVLowLatencyMode)) { var.vt = VT_BOOL; @@ -1288,6 +1286,11 @@ { MediaBufferScopedPointer scoped_buffer(output_buffer.Get()); + if (!buffer_ref->mapping.IsValid() || !scoped_buffer.get()) { + DLOG(ERROR) << "Failed to copy bitstream media buffer."; + return; + } + memcpy(buffer_ref->mapping.memory(), scoped_buffer.get(), size); }
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h index 6d43cab7..2136435 100644 --- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h +++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -186,7 +186,7 @@ // Group of picture length for encoded output stream, indicates the // distance between two key frames. - absl::optional<uint32_t> gop_length_; + uint32_t gop_length_; Microsoft::WRL::ComPtr<IMFActivate> activate_; Microsoft::WRL::ComPtr<IMFTransform> encoder_;
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn index ecc27957..92e64d4 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -98,6 +98,7 @@ "map_traits_stl.h", "message.h", "message_header_validator.h", + "message_metadata_helpers.h", "scoped_interface_endpoint_handle.h", "scoped_message_error_crash_key.cc", "scoped_message_error_crash_key.h",
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h index 84b7019..b1c097e 100644 --- a/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -33,9 +33,9 @@ #include "mojo/public/cpp/bindings/lib/control_message_proxy.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/message_dispatcher.h" +#include "mojo/public/cpp/bindings/message_metadata_helpers.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/bindings/thread_safe_proxy.h" -#include "mojo/public/cpp/bindings/tracing_helpers.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace mojo {
diff --git a/mojo/public/cpp/bindings/message_metadata_helpers.h b/mojo/public/cpp/bindings/message_metadata_helpers.h new file mode 100644 index 0000000..4a403e3 --- /dev/null +++ b/mojo/public/cpp/bindings/message_metadata_helpers.h
@@ -0,0 +1,23 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_METADATA_HELPERS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_METADATA_HELPERS_H_ + +#include <cstdint> + +namespace mojo { + +class Message; + +// Alias for a function taking mojo::Message and returning an IPC hash (stable +// across Chrome versions). +using MessageToStableIPCHashCallback = uint32_t (*)(Message&); + +// Alias for a function taking mojo::Message and returning method name. +using MessageToMethodNameCallback = const char* (*)(Message&); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_METADATA_HELPERS_H_
diff --git a/mojo/public/cpp/bindings/tracing_helpers.h b/mojo/public/cpp/bindings/tracing_helpers.h index ec8fedc..23644d5 100644 --- a/mojo/public/cpp/bindings/tracing_helpers.h +++ b/mojo/public/cpp/bindings/tracing_helpers.h
@@ -24,11 +24,4 @@ #define TRACE_CATEGORY_OR_DISABLED_BY_DEFAULT_MOJOM(category) category #endif -namespace mojo { - -using MessageToStableIPCHashCallback = uint32_t (*)(Message&); -using MessageToMethodNameCallback = const char* (*)(Message&); - -} // namespace mojo - #endif // MOJO_PUBLIC_CPP_BINDINGS_TRACING_HELPERS_H_
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl index ee5fa34..87077f1 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -26,7 +26,6 @@ #include "base/hash/md5_constexpr.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" -#include "base/task/common/task_annotator.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/typed_macros.h" #include "mojo/public/cpp/bindings/lib/generated_code_util.h"
diff --git a/net/cert/x509_util_ios_and_mac_unittest.cc b/net/cert/x509_util_ios_and_mac_unittest.cc index 0a4dc6b..34dcfed 100644 --- a/net/cert/x509_util_ios_and_mac_unittest.cc +++ b/net/cert/x509_util_ios_and_mac_unittest.cc
@@ -69,7 +69,7 @@ BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 3))); } -TEST(X509UtilTest, DISABLED_CreateSecCertificateArrayForX509CertificateErrors) { +TEST(X509UtilTest, CreateSecCertificateArrayForX509CertificateErrors) { scoped_refptr<X509Certificate> ok_cert( ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); ASSERT_TRUE(ok_cert); @@ -83,7 +83,7 @@ ASSERT_TRUE(ok_cert); std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates; - intermediates.push_back(std::move(bad_cert)); + intermediates.push_back(bssl::UpRef(bad_cert)); intermediates.push_back(bssl::UpRef(ok_cert2->cert_buffer())); scoped_refptr<X509Certificate> cert_with_intermediates( X509Certificate::CreateFromBuffer(bssl::UpRef(ok_cert->cert_buffer()), @@ -91,25 +91,46 @@ ASSERT_TRUE(cert_with_intermediates); EXPECT_EQ(2U, cert_with_intermediates->intermediate_buffers().size()); - // Normal CreateSecCertificateArrayForX509Certificate fails with invalid - // certs in chain. - EXPECT_FALSE(CreateSecCertificateArrayForX509Certificate( - cert_with_intermediates.get())); - // With InvalidIntermediateBehavior::kIgnore, invalid intermediate certs // should be silently dropped. base::ScopedCFTypeRef<CFMutableArrayRef> sec_certs( CreateSecCertificateArrayForX509Certificate( cert_with_intermediates.get(), InvalidIntermediateBehavior::kIgnore)); ASSERT_TRUE(sec_certs); - ASSERT_EQ(2, CFArrayGetCount(sec_certs.get())); - for (int i = 0; i < 2; ++i) + for (int i = 0; i < CFArrayGetCount(sec_certs.get()); ++i) ASSERT_TRUE(CFArrayGetValueAtIndex(sec_certs.get(), i)); - EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), - BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); - EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), - BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); + if (CFArrayGetCount(sec_certs.get()) == 2) { + EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), + BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); + EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), + BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); + + // Normal CreateSecCertificateArrayForX509Certificate should fail with + // invalid certs in chain. + EXPECT_FALSE(CreateSecCertificateArrayForX509Certificate( + cert_with_intermediates.get())); + } else if (CFArrayGetCount(sec_certs.get()) == 3) { + // On older macOS versions that do lazy parsing of SecCertificates, the + // invalid certificate may be accepted, which is okay. The test is just + // verifying that *if* creating a SecCertificate from one of the + // intermediates fails, that cert is ignored and the other certs are still + // returned. + EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), + BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); + EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(bad_cert.get()), + BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); + EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), + BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 2))); + + // Normal CreateSecCertificateArrayForX509Certificate should also + // succeed in this case. + EXPECT_TRUE(CreateSecCertificateArrayForX509Certificate( + cert_with_intermediates.get())); + } else { + ADD_FAILURE() << "CFArrayGetCount(sec_certs.get()) = " + << CFArrayGetCount(sec_certs.get()); + } } TEST(X509UtilTest,
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h index 09b718f..0e73ffd86 100644 --- a/net/log/net_log_event_type_list.h +++ b/net/log/net_log_event_type_list.h
@@ -687,20 +687,6 @@ // } EVENT_TYPE(CERT_CT_COMPLIANCE_CHECKED) -// The EV certificate was checked for compliance with Certificate Transparency -// requirements. -// -// The following parameters are attached to the event: -// { -// "certificate": <An X.509 certificate, same format as in -// CERT_VERIFIER_JOB.> -// "policy_enforcement_required": <boolean> -// "build_timely": <boolean> -// "ct_compliance_status": <string describing compliance status> -// "ev_whitelist_version": <optional; string representing whitelist version> -// } -EVENT_TYPE(EV_CERT_CT_COMPLIANCE_CHECKED) - // A Certificate Transparency log entry was audited for inclusion in the // log. //
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json index 6e3623f..a7a9576 100644 --- a/testing/buildbot/chrome.json +++ b/testing/buildbot/chrome.json
@@ -1778,7 +1778,7 @@ { "args": [], "cros_board": "atlas", - "cros_img": "atlas-release/R101-14538.0.0", + "cros_img": "atlas-release/R101-14541.0.0", "name": "lacros_all_tast_tests_ATLAS_LKGM", "resultdb": { "enable": true, @@ -1838,7 +1838,7 @@ { "args": [], "cros_board": "eve", - "cros_img": "eve-release/R101-14538.0.0", + "cros_img": "eve-release/R101-14541.0.0", "name": "lacros_all_tast_tests_EVE_LKGM", "resultdb": { "enable": true, @@ -1943,7 +1943,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "lacros_all_tast_tests_KEVIN_LKGM", "resultdb": { "enable": true, @@ -1958,7 +1958,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "lacros_all_tast_tests_HANA_LKGM", "resultdb": { "enable": true, @@ -1973,7 +1973,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "ozone_unittests_KEVIN_LKGM", "resultdb": { "enable": true, @@ -1987,7 +1987,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "ozone_unittests_HANA_LKGM", "resultdb": { "enable": true, @@ -2001,7 +2001,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "viz_unittests_KEVIN_LKGM", "resultdb": { "enable": true, @@ -2015,7 +2015,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "viz_unittests_HANA_LKGM", "resultdb": { "enable": true,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index a0b27aed..012d9f2f 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -6769,6 +6769,174 @@ "--test-runner-outdir", ".", "--client-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android30.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android30", + "path": ".android_emulator/generic_android30" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android30" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android30.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android30", + "path": ".android_emulator/generic_android30" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android30" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--implementation-outdir", ".", @@ -7023,6 +7191,174 @@ "--client-outdir", ".", "--implementation-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android30.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android30", + "path": ".android_emulator/generic_android30" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android30" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android30.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android30", + "path": ".android_emulator/generic_android30" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android30" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--test-expectations", "../../weblayer/browser/android/javatests/skew/expectations.txt",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 4e64afc7..80539ba 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -41788,6 +41788,174 @@ "--test-runner-outdir", ".", "--client-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android29.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android29", + "path": ".android_emulator/generic_android29" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android29" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android29.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android29", + "path": ".android_emulator/generic_android29" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android29" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--implementation-outdir", ".", @@ -42042,6 +42210,174 @@ "--client-outdir", ".", "--implementation-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android29.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android29", + "path": ".android_emulator/generic_android29" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android29" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android29.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android29", + "path": ".android_emulator/generic_android29" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android29" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--test-expectations", "../../weblayer/browser/android/javatests/skew/expectations.txt", @@ -42296,6 +42632,174 @@ "--test-runner-outdir", ".", "--client-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android23.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_chrome_with_client_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_chrome_with_client_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android23", + "path": ".android_emulator/generic_android23" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android23" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests_with_chrome", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests_with_chrome/" + }, + { + "args": [ + "--additional-apk=apks/ChromePublic.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android23.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_chrome_with_client_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_chrome_with_client_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android23", + "path": ".android_emulator/generic_android23" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android23" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests_with_chrome", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests_with_chrome/" + }, + { + "args": [ + "--additional-apk=apks/ChromePublic.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--implementation-outdir", ".", @@ -42550,6 +43054,174 @@ "--client-outdir", ".", "--implementation-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android23.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_chrome_with_impl_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_chrome_with_impl_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android23", + "path": ".android_emulator/generic_android23" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android23" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests_with_chrome", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests_with_chrome/" + }, + { + "args": [ + "--additional-apk=apks/ChromePublic.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android23.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_chrome_with_impl_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_chrome_with_impl_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android23", + "path": ".android_emulator/generic_android23" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android23" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests_with_chrome", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests_with_chrome/" + }, + { + "args": [ + "--additional-apk=apks/ChromePublic.apk", + "--webview-apk-path=apks/AOSP_SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--test-expectations", "../../weblayer/browser/android/javatests/skew/expectations.txt", @@ -42871,6 +43543,174 @@ "--test-runner-outdir", ".", "--client-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android27.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android27", + "path": ".android_emulator/generic_android27" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android27" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android27.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android27", + "path": ".android_emulator/generic_android27" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android27" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--implementation-outdir", ".", @@ -43125,6 +43965,174 @@ "--client-outdir", ".", "--implementation-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android27.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android27", + "path": ".android_emulator/generic_android27" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android27" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android27.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android27", + "path": ".android_emulator/generic_android27" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android27" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--test-expectations", "../../weblayer/browser/android/javatests/skew/expectations.txt", @@ -43446,6 +44454,174 @@ "--test-runner-outdir", ".", "--client-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android28.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android28", + "path": ".android_emulator/generic_android28" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android28" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--implementation-outdir", + ".", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--client-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android28.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_client_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_client_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android28", + "path": ".android_emulator/generic_android28" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android28" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--implementation-outdir", ".", @@ -43700,6 +44876,174 @@ "--client-outdir", ".", "--implementation-outdir", + "../../weblayer_instrumentation_test_M96/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=96", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android28.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_96" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_96", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M96", + "revision": "version:96.0.4664.141" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android28", + "path": ".android_emulator/generic_android28" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android28" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", + "../../weblayer_instrumentation_test_M97/out/Release", + "--test-expectations", + "../../weblayer/browser/android/javatests/skew/expectations.txt", + "--impl-version=97", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android28.textpb" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "weblayer_skew_tests_with_impl_from_97" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "weblayer_skew_tests_with_impl_from_97", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "chromium/testing/weblayer-x86", + "location": "weblayer_instrumentation_test_M97", + "revision": "version:97.0.4692.102" + }, + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-16.04|Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android28", + "path": ".android_emulator/generic_android28" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android28" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 2 + }, + "test": "weblayer_skew_tests", + "test_id_prefix": "ninja://weblayer/browser/android/javatests:weblayer_skew_tests/" + }, + { + "args": [ + "--additional-apk=apks/WebLayerShellSystemWebView.apk", + "--webview-apk-path=apks/SystemWebView.apk", + "--test-runner-outdir", + ".", + "--client-outdir", + ".", + "--implementation-outdir", "../../weblayer_instrumentation_test_M99/out/Release", "--test-expectations", "../../weblayer/browser/android/javatests/skew/expectations.txt",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index bb7da7a..53f2064 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5796,7 +5796,7 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "isolate_profile_data": true, @@ -5804,14 +5804,14 @@ "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [ @@ -5938,7 +5938,7 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "isolate_profile_data": true, @@ -5946,14 +5946,14 @@ "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index d4523a1..cc3a390 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -85831,7 +85831,7 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "isolate_profile_data": true, @@ -85839,14 +85839,14 @@ "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -85948,7 +85948,7 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "isolate_profile_data": true, @@ -85956,14 +85956,14 @@ "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -87330,21 +87330,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [ @@ -87472,21 +87472,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [ @@ -89027,21 +89027,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [ @@ -89169,21 +89169,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "dimension_sets": [ @@ -89920,21 +89920,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -90016,21 +90016,21 @@ }, { "args": [ - "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome", "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4918.0", + "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 101.0.4919.0", "swarming": { "can_use_on_swarming_builders": true, "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v101.0.4918.0", - "revision": "version:101.0.4918.0" + "location": "lacros_version_skew_tests_v101.0.4919.0", + "revision": "version:101.0.4919.0" } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json index 701b88b9..e8d237b7f 100644 --- a/testing/buildbot/internal.chromeos.fyi.json +++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1125,7 +1125,7 @@ { "args": [], "cros_board": "octopus", - "cros_img": "octopus-release/R101-14538.0.0", + "cros_img": "octopus-release/R101-14541.0.0", "name": "lacros_fyi_tast_tests_OCTOPUS_LKGM", "swarming": {}, "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", @@ -1169,7 +1169,7 @@ { "args": [], "cros_board": "octopus", - "cros_img": "octopus-release/R101-14538.0.0", + "cros_img": "octopus-release/R101-14541.0.0", "name": "ozone_unittests_OCTOPUS_LKGM", "swarming": {}, "test": "ozone_unittests", @@ -1217,7 +1217,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "lacros_all_tast_tests_KEVIN_LKGM", "swarming": {}, "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", @@ -1228,7 +1228,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "lacros_all_tast_tests_HANA_LKGM", "swarming": {}, "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)", @@ -1239,7 +1239,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "ozone_unittests_KEVIN_LKGM", "swarming": {}, "test": "ozone_unittests", @@ -1249,7 +1249,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "ozone_unittests_HANA_LKGM", "swarming": {}, "test": "ozone_unittests", @@ -1259,7 +1259,7 @@ { "args": [], "cros_board": "kevin", - "cros_img": "kevin-release/R101-14538.0.0", + "cros_img": "kevin-release/R101-14541.0.0", "name": "viz_unittests_KEVIN_LKGM", "swarming": {}, "test": "viz_unittests", @@ -1269,7 +1269,7 @@ { "args": [], "cros_board": "hana", - "cros_img": "hana-release/R101-14538.0.0", + "cros_img": "hana-release/R101-14541.0.0", "name": "viz_unittests_HANA_LKGM", "swarming": {}, "test": "viz_unittests",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index fe8b7394..a274b43 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -6608,10 +6608,14 @@ 'variants': [ 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', ] } }, @@ -6621,10 +6625,14 @@ 'variants': [ 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', ] } }, @@ -6639,10 +6647,14 @@ 'variants': [ 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE', 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE', - 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE', + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE', ] } },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 7d375680..fa21a4db 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -28,16 +28,16 @@ }, 'LACROS_VERSION_SKEW_CANARY': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4918.0/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v101.0.4919.0/test_ash_chrome', '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter', ], - 'identifier': 'Lacros version skew testing ash 101.0.4918.0', + 'identifier': 'Lacros version skew testing ash 101.0.4919.0', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v101.0.4918.0', - 'revision': 'version:101.0.4918.0', + 'location': 'lacros_version_skew_tests_v101.0.4919.0', + 'revision': 'version:101.0.4919.0', }, ], }, @@ -416,7 +416,55 @@ ], }, }, - 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': { + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/AOSP_SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '.', + '--implementation-outdir', + '../../weblayer_instrumentation_test_M97/out/Release', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--impl-version=97', + ], + 'identifier': 'with_impl_from_97', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M97', + 'revision': 'version:97.0.4692.102', + } + ], + }, + }, + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/AOSP_SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '.', + '--implementation-outdir', + '../../weblayer_instrumentation_test_M96/out/Release', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--impl-version=96', + ], + 'identifier': 'with_impl_from_96', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M96', + 'revision': 'version:96.0.4664.141', + } + ], + }, + }, + 'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE': { 'args': [ '--webview-apk-path=apks/AOSP_SystemWebView.apk', '--test-runner-outdir', @@ -488,7 +536,55 @@ ], }, }, - 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': { + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '.', + '--implementation-outdir', + '../../weblayer_instrumentation_test_M97/out/Release', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--impl-version=97', + ], + 'identifier': 'with_impl_from_97', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M97', + 'revision': 'version:97.0.4692.102', + } + ], + }, + }, + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '.', + '--implementation-outdir', + '../../weblayer_instrumentation_test_M96/out/Release', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--impl-version=96', + ], + 'identifier': 'with_impl_from_96', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M96', + 'revision': 'version:96.0.4664.141', + } + ], + }, + }, + 'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE': { 'args': [ '--webview-apk-path=apks/SystemWebView.apk', '--test-runner-outdir', @@ -560,7 +656,55 @@ ], }, }, - 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': { + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_THREE_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '../../weblayer_instrumentation_test_M97/out/Release', + '--implementation-outdir', + '.', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--client-version=97', + ], + 'identifier': 'with_client_from_97', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M97', + 'revision': 'version:97.0.4692.102', + } + ], + }, + }, + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FOUR_MILESTONE': { + 'args': [ + '--webview-apk-path=apks/SystemWebView.apk', + '--test-runner-outdir', + '.', + '--client-outdir', + '../../weblayer_instrumentation_test_M96/out/Release', + '--implementation-outdir', + '.', + '--test-expectations', + '../../weblayer/browser/android/javatests/skew/expectations.txt', + '--client-version=96', + ], + 'identifier': 'with_client_from_96', + 'swarming': { + 'cipd_packages': [ + { + 'cipd_package': 'chromium/testing/weblayer-x86', + 'location': 'weblayer_instrumentation_test_M96', + 'revision': 'version:96.0.4664.141', + } + ], + }, + }, + 'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_FIVE_MILESTONE': { 'args': [ '--webview-apk-path=apks/SystemWebView.apk', '--test-runner-outdir', @@ -588,7 +732,7 @@ 'skylab': { 'cros_board': 'atlas', 'cros_chrome_version': '101.0.4907.0', - 'cros_img': 'atlas-release/R101-14538.0.0', + 'cros_img': 'atlas-release/R101-14541.0.0', }, 'enabled': True, 'identifier': 'ATLAS_LKGM', @@ -624,7 +768,7 @@ 'skylab': { 'cros_board': 'eve', 'cros_chrome_version': '101.0.4907.0', - 'cros_img': 'eve-release/R101-14538.0.0', + 'cros_img': 'eve-release/R101-14541.0.0', }, 'enabled': True, 'identifier': 'EVE_LKGM', @@ -660,7 +804,7 @@ 'skylab': { 'cros_board': 'kevin', 'cros_chrome_version': '101.0.4907.0', - 'cros_img': 'kevin-release/R101-14538.0.0', + 'cros_img': 'kevin-release/R101-14541.0.0', }, 'enabled': True, 'identifier': 'KEVIN_LKGM', @@ -669,7 +813,7 @@ 'skylab': { 'cros_board': 'hana', 'cros_chrome_version': '101.0.4907.0', - 'cros_img': 'hana-release/R101-14538.0.0', + 'cros_img': 'hana-release/R101-14541.0.0', }, 'enabled': True, 'identifier': 'HANA_LKGM', @@ -678,7 +822,7 @@ 'skylab': { 'cros_board': 'octopus', 'cros_chrome_version': '101.0.4907.0', - 'cros_img': 'octopus-release/R101-14538.0.0', + 'cros_img': 'octopus-release/R101-14541.0.0', }, 'enabled': True, 'identifier': 'OCTOPUS_LKGM',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 1481b05..c89c083 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1695,6 +1695,21 @@ ] } ], + "ClankAppLanguagePrompt": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AppLanguagePrompt" + ] + } + ] + } + ], "CleanUndecryptablePasswordsLinuxDuringInitialSync": [ { "platforms": [
diff --git a/third_party/blink/public/common/attribution_reporting/constants.h b/third_party/blink/public/common/attribution_reporting/constants.h index a059dea..a352117f 100644 --- a/third_party/blink/public/common/attribution_reporting/constants.h +++ b/third_party/blink/public/common/attribution_reporting/constants.h
@@ -14,6 +14,8 @@ constexpr size_t kMaxBytesPerAttributionAggregatableKeyId = 25; constexpr size_t kMaxAttributionAggregatableKeysPerSource = 50; +constexpr size_t kMaxAttributionEventTriggerData = 10; + } // namespace blink #endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_ATTRIBUTION_REPORTING_CONSTANTS_H_
diff --git a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom index c3d22019..9b4e164 100644 --- a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom +++ b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
@@ -61,6 +61,44 @@ AttributionAggregatableSources aggregatable_sources; }; +// Deduplication key set by a reporting origin which prevents duplicate triggers +// from generating multiple attribution reports for a given source. +struct AttributionTriggerDedupKey { + // Arbitrary value for deduplication set by the reporting origin. + uint64 value; +}; + +// Mojo representation of the trigger configuration provided by a reporting +// origin. This data is provided arbitrarily by certain subresources on a +// page which invoke Attribution Reporting. +struct EventTriggerData { + // Value which identifies this trigger in attribution reports, determined by + // reporting origin. + uint64 data = 0; + + // Priority of this trigger relative to other attributed triggers for a + // source. Reports created with high priority triggers will be reported over + // lower priority ones. + int64 priority = 0; + + // Key which allows deduplication against existing attributions for the same + // source. + AttributionTriggerDedupKey? dedup_key; +}; + +// Represents a request from a reporting origin to trigger attribution on a +// given site. See: +// https://github.com/WICG/conversion-measurement-api/blob/main/EVENT.md#triggering-attribution +struct AttributionTriggerData { + // Origin that registered this trigger, used to determine which source this + // trigger is associated with. + url.mojom.Origin reporting_origin; + + // List of all event trigger data objects declared by the event trigger + // header. This data is arbitrarily set by the reporting_origin. + array<EventTriggerData> event_triggers; +}; + // Browser-process interface responsible for processing attribution // configurations registered by the renderer. These configurations may be sent // out of the normal frame lifecycle, e.g. after the frame is destroyed. @@ -68,4 +106,8 @@ // Called when data from the renderer is available for a given attributionsrc // request. SourceDataAvailable(AttributionSourceData data); -}; + + // Called when trigger data from the renderer is available for a given + // attributionsrc request. + TriggerDataAvailable(AttributionTriggerData data); +}; \ No newline at end of file
diff --git a/third_party/blink/public/mojom/conversions/conversions.mojom b/third_party/blink/public/mojom/conversions/conversions.mojom index 33f78e0..3b7d6d7 100644 --- a/third_party/blink/public/mojom/conversions/conversions.mojom +++ b/third_party/blink/public/mojom/conversions/conversions.mojom
@@ -8,10 +8,6 @@ import "third_party/blink/public/mojom/conversions/attribution_data_host.mojom"; import "url/mojom/origin.mojom"; -struct DedupKey { - uint64 value; -}; - struct Conversion { // Origin of the conversion registration redirect. url.mojom.Origin reporting_origin; @@ -30,7 +26,7 @@ // Key specified in conversion redirect for deduplication against existing // conversions with the same source. - DedupKey? dedup_key; + AttributionTriggerDedupKey? dedup_key; // The request id of the conversion redirect. In case the conversion is // invalid and an error is reported to DevTools, the error can be tied to the
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index 17bf49ae..b42975ba 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -25,8 +25,6 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_disabled_callback.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_state_restore_callback.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_state_restore_callback.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_callback.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_callback.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_event_handler_non_null.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_event_handler_non_null.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_for_each_iterator_callback.cc", @@ -137,8 +135,10 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_layout_constraints_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_timeline_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_timeline_options.h", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_set_element_options.cc", - "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_set_element_options.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_prepare_options.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_prepare_options.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_start_options.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_document_transition_start_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_dom_matrix_2d_init.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_dom_matrix_2d_init.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_dom_matrix_init.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni index 43598ff..c40506e0 100644 --- a/third_party/blink/renderer/bindings/idl_in_core.gni +++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -126,8 +126,9 @@ "//third_party/blink/renderer/core/css/style_sheet.idl", "//third_party/blink/renderer/core/css/style_sheet_list.idl", "//third_party/blink/renderer/core/document_transition/document_transition.idl", - "//third_party/blink/renderer/core/document_transition/document_transition_callback.idl", - "//third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl", + "//third_party/blink/renderer/core/document_transition/document_transition_config.idl", + "//third_party/blink/renderer/core/document_transition/document_transition_prepare_options.idl", + "//third_party/blink/renderer/core/document_transition/document_transition_start_options.idl", "//third_party/blink/renderer/core/document_transition/document_transition_supplement.idl", "//third_party/blink/renderer/core/dom/abort_controller.idl", "//third_party/blink/renderer/core/dom/abort_signal.idl",
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc index 51b19989..a87ae724 100644 --- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc +++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
@@ -103,8 +103,8 @@ // Paint containment requires using the overflow clip edge. To do otherwise // results in overflow-clip-margin not being painted in certain scenarios. intersection_observer_ = IntersectionObserver::Create( - {Length::Percent(150.f)}, {std::numeric_limits<float>::min()}, - document_, + {Length::Percent(kViewportMarginPercentage)}, + {std::numeric_limits<float>::min()}, document_, WTF::BindRepeating( &DisplayLockDocumentState::ProcessDisplayLockActivationObservation, WrapWeakPersistent(this)),
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h index 4b28969e..412f9966 100644 --- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h +++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
@@ -171,6 +171,8 @@ base::TimeTicks GetLockUpdateTimestamp(); + static constexpr float kViewportMarginPercentage = 150.f; + private: IntersectionObserver& EnsureIntersectionObserver();
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.cc b/third_party/blink/renderer/core/document_transition/document_transition.cc index 0d327e5..7c69ea8 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition.cc +++ b/third_party/blink/renderer/core/document_transition/document_transition.cc
@@ -11,13 +11,16 @@ #include "cc/trees/paint_holding_reason.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_config.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_set_element_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_prepare_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_start_options.h" #include "third_party/blink/renderer/core/css/style_change_reason.h" +#include "third_party/blink/renderer/core/dom/abort_signal.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/pseudo_element.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/page/chrome_client.h" @@ -33,15 +36,58 @@ namespace blink { namespace { -const char kAbortedFromCaptureAndHold[] = - "Aborted due to captureAndHold() call"; -const char kAbortedFromScript[] = "Aborted due to abort() call"; +const char kAbortedFromPrepare[] = "Aborted due to prepare() call"; +const char kAbortedFromSignal[] = "Aborted due to abortSignal"; + +DocumentTransitionRequest::Effect ParseEffect(const String& input) { + using MapType = HashMap<String, DocumentTransitionRequest::Effect>; + DEFINE_STATIC_LOCAL( + MapType*, lookup_map, + (new MapType{ + {"cover-down", DocumentTransitionRequest::Effect::kCoverDown}, + {"cover-left", DocumentTransitionRequest::Effect::kCoverLeft}, + {"cover-right", DocumentTransitionRequest::Effect::kCoverRight}, + {"cover-up", DocumentTransitionRequest::Effect::kCoverUp}, + {"explode", DocumentTransitionRequest::Effect::kExplode}, + {"fade", DocumentTransitionRequest::Effect::kFade}, + {"implode", DocumentTransitionRequest::Effect::kImplode}, + {"reveal-down", DocumentTransitionRequest::Effect::kRevealDown}, + {"reveal-left", DocumentTransitionRequest::Effect::kRevealLeft}, + {"reveal-right", DocumentTransitionRequest::Effect::kRevealRight}, + {"reveal-up", DocumentTransitionRequest::Effect::kRevealUp}})); + + auto it = lookup_map->find(input); + return it != lookup_map->end() ? it->value + : DocumentTransitionRequest::Effect::kNone; +} + +DocumentTransitionRequest::Effect ParseRootTransition( + const DocumentTransitionPrepareOptions* options) { + return options->hasRootTransition() + ? ParseEffect(options->rootTransition()) + : DocumentTransitionRequest::Effect::kNone; +} uint32_t NextDocumentTag() { static uint32_t next_document_tag = 1u; return next_document_tag++; } +DocumentTransitionRequest::TransitionConfig ParseTransitionConfig( + const DocumentTransitionConfig& config) { + DocumentTransitionRequest::TransitionConfig transition_config; + + if (config.hasDuration()) { + transition_config.duration = base::Milliseconds(config.duration()); + } + + if (config.hasDelay()) { + transition_config.delay = base::Milliseconds(config.delay()); + } + + return transition_config; +} + } // namespace DocumentTransition::DocumentTransition(Document* document) @@ -51,8 +97,10 @@ void DocumentTransition::Trace(Visitor* visitor) const { visitor->Trace(document_); - visitor->Trace(capture_promise_resolver_); + visitor->Trace(prepare_promise_resolver_); visitor->Trace(start_promise_resolver_); + visitor->Trace(active_shared_elements_); + visitor->Trace(signal_); visitor->Trace(style_tracker_); ScriptWrappable::Trace(visitor); @@ -61,9 +109,9 @@ } void DocumentTransition::ContextDestroyed() { - if (capture_promise_resolver_) { - capture_promise_resolver_->Detach(); - capture_promise_resolver_ = nullptr; + if (prepare_promise_resolver_) { + prepare_promise_resolver_->Detach(); + prepare_promise_resolver_ = nullptr; } if (start_promise_resolver_) { start_promise_resolver_->Detach(); @@ -73,44 +121,18 @@ } bool DocumentTransition::HasPendingActivity() const { - if (capture_promise_resolver_ || start_promise_resolver_) + if (prepare_promise_resolver_ || start_promise_resolver_) return true; return false; } -void DocumentTransition::AssertNoTransition() { - DCHECK_EQ(state_, State::kIdle); - DCHECK(!style_tracker_); - DCHECK(!capture_promise_resolver_); - DCHECK(!start_promise_resolver_); -} - -void DocumentTransition::StartNewTransition() { - style_tracker_ = - MakeGarbageCollected<DocumentTransitionStyleTracker>(*document_); -} - -void DocumentTransition::FinalizeNewTransition() {} - -void DocumentTransition::setElement( +ScriptPromise DocumentTransition::prepare( ScriptState* script_state, - Element* element, - const AtomicString& tag, - const DocumentTransitionSetElementOptions* opts, - ExceptionState& exception_state) { - DCHECK(style_tracker_); - if (tag.IsNull()) - style_tracker_->RemoveSharedElement(element); - else - style_tracker_->AddSharedElement(element, tag); -} - -ScriptPromise DocumentTransition::captureAndHold( - ScriptState* script_state, + const DocumentTransitionPrepareOptions* options, ExceptionState& exception_state) { // Reject any previous prepare promises. - if (state_ == State::kCapturing || state_ == State::kCaptured) - CancelPendingTransition(kAbortedFromCaptureAndHold); + if (state_ == State::kPreparing || state_ == State::kPrepared) + CancelPendingTransition(kAbortedFromPrepare); // Get the sequence id before any early outs so we will correctly process // callbacks from previous requests. @@ -133,52 +155,148 @@ return ScriptPromise(); } - capture_promise_resolver_ = + std::string error; + DocumentTransitionRequest::TransitionConfig root_config; + if (options->hasRootConfig()) + root_config = ParseTransitionConfig(*options->rootConfig()); + if (!root_config.IsValid(&error)) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + String(error.data(), error.size())); + return ScriptPromise(); + } + + // This stores a per-shared-element configuration, if specified. Note that + // this is likely to change when the API is redesigned at + // https://github.com/WICG/shared-element-transitions. + // + // Note that we add one extra config for the "root" element, after parsing the + // shared elements. + std::vector<DocumentTransitionRequest::TransitionConfig> + shared_elements_config; + if (options->hasSharedElements()) { + shared_elements_config.resize(options->sharedElements().size()); + + // TODO(vmpstr): This is likely to be superceded by CSS customization. + if (options->hasSharedElementsConfig()) { + const auto& shared_elements_config_options = + options->sharedElementsConfig(); + + if (shared_elements_config_options.size() != + shared_elements_config.size()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "The sharedElementsConfig size must " + "match the list of shared elements"); + return ScriptPromise(); + } + + for (wtf_size_t i = 0; i < shared_elements_config_options.size(); i++) { + shared_elements_config[i] = + ParseTransitionConfig(*shared_elements_config_options[i]); + if (!shared_elements_config[i].IsValid(&error)) { + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + String(error.data(), error.size())); + return ScriptPromise(); + } + } + } + } + + // The root snapshot is handled as a shared element by the compositing stack. + shared_elements_config.emplace_back(); + + if (options->hasAbortSignal()) { + if (options->abortSignal()->aborted()) { + exception_state.ThrowDOMException(DOMExceptionCode::kAbortError, + kAbortedFromSignal); + return ScriptPromise(); + } + + signal_ = options->abortSignal(); + signal_->AddAlgorithm(WTF::Bind(&DocumentTransition::Abort, + WrapWeakPersistent(this), + WrapWeakPersistent(signal_.Get()))); + } + + // We're going to be creating a new transition, parse the options. + auto effect = ParseRootTransition(options); + if (options->hasSharedElements()) + SetActiveSharedElements(options->sharedElements()); + prepare_shared_element_count_ = active_shared_elements_.size(); + + prepare_promise_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state); - state_ = State::kCapturing; - pending_request_ = DocumentTransitionRequest::CreateCapture( - document_tag_, style_tracker_->PendingSharedElementCount() + 1, + state_ = State::kPreparing; + pending_request_ = DocumentTransitionRequest::CreatePrepare( + effect, document_tag_, root_config, std::move(shared_elements_config), ConvertToBaseOnceCallback(CrossThreadBindOnce( - &DocumentTransition::NotifyCaptureFinished, - WrapCrossThreadWeakPersistent(this), last_prepare_sequence_id_))); + &DocumentTransition::NotifyPrepareFinished, + WrapCrossThreadWeakPersistent(this), last_prepare_sequence_id_)), + /*is_renderer_transition=*/true); - style_tracker_->Capture(); + style_tracker_ = + MakeGarbageCollected<DocumentTransitionStyleTracker>(*document_); + style_tracker_->Prepare(active_shared_elements_); + NotifyHasChangesToCommit(); - - return capture_promise_resolver_->Promise(); + return prepare_promise_resolver_->Promise(); } -ScriptPromise DocumentTransition::start(ScriptState* script_state, - ExceptionState& exception_state) { - if (state_ != State::kCaptured) { +void DocumentTransition::Abort(AbortSignal* signal) { + // There is no RemoveAlgorithm() method on AbortSignal so compare the signal + // bound to this callback to the one last passed to start(). + if (signal_ != signal) + return; + + CancelPendingTransition(kAbortedFromSignal); +} + +ScriptPromise DocumentTransition::start( + ScriptState* script_state, + const DocumentTransitionStartOptions* options, + ExceptionState& exception_state) { + if (state_ != State::kPrepared) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "Transition must be prepared before it can be started."); return ScriptPromise(); } + signal_ = nullptr; StopDeferringCommits(); + if (options->hasSharedElements()) + SetActiveSharedElements(options->sharedElements()); + + // We need to have the same amount of shared elements (even if null) as the + // prepared ones. + if (prepare_shared_element_count_ != active_shared_elements_.size()) { + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + String::Format("Start request sharedElement count (%u) must match the " + "prepare sharedElement count (%u).", + active_shared_elements_.size(), + prepare_shared_element_count_)); + + // TODO(khushalsagar) : Viz keeps copy results cached for 5 seconds at this + // point. We should send an early release. See crbug.com/1266500. + ResetState(); + return ScriptPromise(); + } + last_start_sequence_id_ = next_sequence_id_++; state_ = State::kStarted; start_promise_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state); pending_request_ = DocumentTransitionRequest::CreateAnimateRenderer(document_tag_); - style_tracker_->Start(); + style_tracker_->Start(active_shared_elements_); NotifyHasChangesToCommit(); return start_promise_resolver_->Promise(); } -void DocumentTransition::ignoreCSSTaggedElements(ScriptState*, - ExceptionState&) {} - -void DocumentTransition::abandon(ScriptState*, ExceptionState&) { - CancelPendingTransition(kAbortedFromScript); -} - void DocumentTransition::NotifyHasChangesToCommit() { if (!document_ || !document_->GetPage() || !document_->View()) return; @@ -191,26 +309,27 @@ document_->View()->SetPaintArtifactCompositorNeedsUpdate(); } -void DocumentTransition::NotifyCaptureFinished(uint32_t sequence_id) { +void DocumentTransition::NotifyPrepareFinished(uint32_t sequence_id) { // This notification is for a different sequence id. if (sequence_id != last_prepare_sequence_id_) return; // We could have detached the resolver if the execution context was destroyed. - if (!capture_promise_resolver_) + if (!prepare_promise_resolver_) return; - DCHECK(state_ == State::kCapturing); - DCHECK(capture_promise_resolver_); + DCHECK(state_ == State::kPreparing); + DCHECK(prepare_promise_resolver_); if (style_tracker_) - style_tracker_->CaptureResolved(); + style_tracker_->PrepareResolved(); // Defer commits before resolving the promise to ensure any updates made in // the callback are deferred. StartDeferringCommits(); - capture_promise_resolver_->Resolve(); - capture_promise_resolver_ = nullptr; - state_ = State::kCaptured; + prepare_promise_resolver_->Resolve(); + prepare_promise_resolver_ = nullptr; + state_ = State::kPrepared; + SetActiveSharedElements({}); } void DocumentTransition::NotifyStartFinished(uint32_t sequence_id) { @@ -245,10 +364,8 @@ bool DocumentTransition::IsTransitionParticipant( const LayoutObject& object) const { - // If our state is idle and we're outside of script mutation scope, it implies - // that we have no style tracker. - DCHECK(state_ != State::kIdle || script_mutations_allowed_ || - !style_tracker_); + // If our state is idle it implies that we have no style tracker. + DCHECK(state_ != State::kIdle || !style_tracker_); // The layout view is always a participant if there is a transition. if (auto* layout_view = DynamicTo<LayoutView>(object)) @@ -256,7 +373,7 @@ // Otherwise check if the layout object has an active shared element. auto* element = DynamicTo<Element>(object.GetNode()); - return element && style_tracker_ && style_tracker_->IsSharedElement(element); + return element && active_shared_elements_.Contains(element); } PaintPropertyChangeType DocumentTransition::UpdateEffect( @@ -279,17 +396,29 @@ if (!element) { // The only non-element participant is the layout view. DCHECK(object.IsLayoutView()); - - style_tracker_->UpdateRootIndexAndSnapshotId( - state.document_transition_shared_element_id, - state.shared_element_resource_id); + // This matches one past the size of the shared element configs generated in + // ::prepare(). + state.document_transition_shared_element_id.AddIndex( + active_shared_elements_.size()); + state.shared_element_resource_id = style_tracker_->GetLiveRootSnapshotId(); DCHECK(state.document_transition_shared_element_id.valid()); return style_tracker_->UpdateRootEffect(std::move(state), current_effect); } - style_tracker_->UpdateElementIndicesAndSnapshotId( - element, state.document_transition_shared_element_id, - state.shared_element_resource_id); + for (wtf_size_t i = 0; i < active_shared_elements_.size(); ++i) { + if (active_shared_elements_[i] != element) + continue; + state.document_transition_shared_element_id.AddIndex(i); + + // This tags the shared element's content with the resource id used by the + // first pseudo element. This is okay since in the eventual API we should + // have a 1:1 mapping between shared elements and pseudo elements. + if (!state.shared_element_resource_id.IsValid()) { + state.shared_element_resource_id = + style_tracker_->GetLiveSnapshotId(element); + } + } + return style_tracker_->UpdateEffect(element, std::move(state), current_effect); } @@ -305,8 +434,39 @@ } void DocumentTransition::VerifySharedElements() { - if (state_ != State::kIdle) - style_tracker_->VerifySharedElements(); + for (auto& active_element : active_shared_elements_) { + if (!active_element) + continue; + + auto* object = active_element->GetLayoutObject(); + + // TODO(vmpstr): Should this work for replaced elements as well? + if (object) { + if (object->ShouldApplyPaintContainment()) + continue; + + auto* console_message = MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kRendering, + mojom::ConsoleMessageLevel::kError, + "Dropping element from transition. Shared element must have " + "containt:paint"); + console_message->SetNodes(document_->GetFrame(), + {DOMNodeIds::IdForNode(active_element)}); + document_->AddConsoleMessage(console_message); + } + + // Clear the shared element. Note that we don't remove the element from the + // vector, since we need to preserve the order of the elements and we + // support nulls as a valid active element. + + // Invalidate the element since we should no longer be compositing it. + auto* box = active_element->GetLayoutBox(); + if (box && box->HasSelfPaintingLayer()) { + box->SetNeedsPaintPropertyUpdate(); + box->Layer()->SetNeedsCompositingInputsUpdate(); + } + active_element = nullptr; + } } void DocumentTransition::RunPostLayoutSteps() { @@ -361,9 +521,41 @@ const String& DocumentTransition::UAStyleSheet() const { DCHECK(style_tracker_); + return style_tracker_->UAStyleSheet(); } +void DocumentTransition::SetActiveSharedElements( + HeapVector<Member<Element>> elements) { + // The way this is used, we should never be overriding a non-empty set with + // another non-empty set of elements. + DCHECK(elements.IsEmpty() || active_shared_elements_.IsEmpty()); + + InvalidateActiveElements(); + active_shared_elements_ = std::move(elements); + InvalidateActiveElements(); +} + +void DocumentTransition::InvalidateActiveElements() { + for (auto& element : active_shared_elements_) { + // We allow nulls. + if (!element) + continue; + + auto* box = element->GetLayoutBox(); + if (!box || !box->HasSelfPaintingLayer()) + continue; + + // We propagate the shared element id on an effect node for the object. This + // means that we should update the paint properties to update the shared + // element id. + box->SetNeedsPaintPropertyUpdate(); + + // We might need to composite or decomposite this layer. + box->Layer()->SetNeedsCompositingInputsUpdate(); + } +} + void DocumentTransition::StartDeferringCommits() { DCHECK(!deferring_commits_); @@ -401,29 +593,26 @@ } void DocumentTransition::CancelPendingTransition(const char* abort_message) { - if (capture_promise_resolver_) { - capture_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( + DCHECK(state_ == State::kPreparing || state_ == State::kPrepared) + << "Can not cancel transition at state : " << static_cast<int>(state_); + + if (prepare_promise_resolver_) { + prepare_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kAbortError, abort_message)); - capture_promise_resolver_ = nullptr; - } - if (start_promise_resolver_) { - start_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( - DOMExceptionCode::kAbortError, abort_message)); - start_promise_resolver_ = nullptr; + prepare_promise_resolver_ = nullptr; } ResetState(); } void DocumentTransition::ResetState(bool abort_style_tracker) { + SetActiveSharedElements({}); if (style_tracker_ && abort_style_tracker) style_tracker_->Abort(); style_tracker_ = nullptr; StopDeferringCommits(); state_ = State::kIdle; - // If script mutations are still allowed, we recreate the style tracker. - if (script_mutations_allowed_) - StartNewTransition(); + signal_ = nullptr; } } // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.h b/third_party/blink/renderer/core/document_transition/document_transition.h index edcca73e..17bbbd54 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition.h +++ b/third_party/blink/renderer/core/document_transition/document_transition.h
@@ -22,8 +22,10 @@ namespace blink { +class AbortSignal; class Document; -class DocumentTransitionSetElementOptions; +class DocumentTransitionPrepareOptions; +class DocumentTransitionStartOptions; class Element; class ExceptionState; class LayoutObject; @@ -51,46 +53,13 @@ // ActiveScriptWrappable functionality. bool HasPendingActivity() const override; - bool CanCreateNewTransition() const { - return state_ == State::kIdle && !script_mutations_allowed_; - } - - class ScriptMutationsAllowedScope { - STACK_ALLOCATED(); - - public: - ~ScriptMutationsAllowedScope() { - transition_->script_mutations_allowed_ = false; - transition_->FinalizeNewTransition(); - } - - private: - friend class DocumentTransition; - - explicit ScriptMutationsAllowedScope(DocumentTransition* transition) - : transition_(transition) { - transition_->script_mutations_allowed_ = true; - transition_->AssertNoTransition(); - transition_->StartNewTransition(); - } - - DocumentTransition* transition_; - }; - - ScriptMutationsAllowedScope CreateScriptMutationsAllowedScope() { - return ScriptMutationsAllowedScope{this}; - } - // JavaScript API implementation. - void setElement(ScriptState*, - Element*, - const AtomicString&, - const DocumentTransitionSetElementOptions*, - ExceptionState&); - ScriptPromise captureAndHold(ScriptState*, ExceptionState&); - ScriptPromise start(ScriptState*, ExceptionState&); - void ignoreCSSTaggedElements(ScriptState*, ExceptionState&); - void abandon(ScriptState*, ExceptionState&); + ScriptPromise prepare(ScriptState*, + const DocumentTransitionPrepareOptions*, + ExceptionState&); + ScriptPromise start(ScriptState*, + const DocumentTransitionStartOptions*, + ExceptionState&); // This uses std::move semantics to take the request from this object. std::unique_ptr<DocumentTransitionRequest> TakePendingRequest(); @@ -136,22 +105,21 @@ // LifecycleNotificationObserver overrides. void WillStartLifecycleUpdate(const LocalFrameView&) override; - bool HasActiveTransition() const { return state_ != State::kIdle; } - private: friend class DocumentTransitionTest; - enum class State { kIdle, kCapturing, kCaptured, kStarted }; - - void AssertNoTransition(); - void StartNewTransition(); - void FinalizeNewTransition(); + enum class State { kIdle, kPreparing, kPrepared, kStarted }; void NotifyHasChangesToCommit(); - void NotifyCaptureFinished(uint32_t sequence_id); + void NotifyPrepareFinished(uint32_t sequence_id); void NotifyStartFinished(uint32_t sequence_id); + // Sets new active shared elements. Note that this is responsible for making + // sure we invalidate the right bits both on the old and new elements. + void SetActiveSharedElements(HeapVector<Member<Element>> elements); + void InvalidateActiveElements(); + // Used to defer visual updates between transition prepare finishing and // transition start to allow the page to set up the final scene // asynchronously. @@ -161,6 +129,8 @@ // Allow canceling a transition until it reaches start(). void CancelPendingTransition(const char* abort_message); + void Abort(AbortSignal* signal); + // Resets internal state, called in both abort situations and transition // finished situations. void ResetState(bool abort_style_tracker = true); @@ -169,8 +139,20 @@ State state_ = State::kIdle; - Member<ScriptPromiseResolver> capture_promise_resolver_; + Member<ScriptPromiseResolver> prepare_promise_resolver_; Member<ScriptPromiseResolver> start_promise_resolver_; + Member<AbortSignal> signal_; + + // `active_shared_elements_` represents elements that are identified as shared + // during the current step of the transition. Specifically, it represents + // `prepare()` call sharedElements if the state is kPreparing and `start()` + // call sharedElements if the state is kStarted. + // `prepare_shared_element_count_` represents the number of shared elements + // that were specified in the `prepare()` call. This is used to verify that + // the number of shared elements specified in the `prepare()` and `start()` + // calls is the same. + HeapVector<Member<Element>> active_shared_elements_; + wtf_size_t prepare_shared_element_count_ = 0u; // Created conditionally if renderer based SharedElementTransitions is // enabled. @@ -192,9 +174,6 @@ bool deferring_commits_ = false; - // This is set to true when we allow script calls to modify state. - bool script_mutations_allowed_ = false; - // Set only for tests. bool disable_end_transition_ = false; };
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.idl b/third_party/blink/renderer/core/document_transition/document_transition.idl index 7c304109..e1b8042 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition.idl +++ b/third_party/blink/renderer/core/document_transition/document_transition.idl
@@ -9,21 +9,13 @@ Exposed=Window, RuntimeEnabled=DocumentTransition ] interface DocumentTransition { - // Set or unset (if tag is null) an element that will participate in the next - // transition, whether as a part of captureAndHold or start phases. - [CallWith=ScriptState, RaisesException] void setElement(Element element, DOMString? tag, optional DocumentTransitionSetElementOptions options = {}); + // - This should only be called after any previous start() calls have resolved. + // - Rejects any previous unresolved prepare() promises. + // - Returns a promise that resolves after the transition has been + // prepared. + [CallWith=ScriptState, RaisesException] Promise<void> prepare(optional DocumentTransitionPrepareOptions options = {}); - // Request to capture the currently set elements, including the root, and - // hold visual contents until start is called - [CallWith=ScriptState, RaisesException] Promise<void> captureAndHold(); - - // Starts the transition with the captured elements and elements set for - // start. - [CallWith=ScriptState, RaisesException] Promise<void> start(); - - // Ignores CSS tagged elements - [CallWith=ScriptState, RaisesException] void ignoreCSSTaggedElements(); - - // Abandons the transition. - [CallWith=ScriptState, RaisesException] void abandon(); + // Can only be called after prepare(), during the task during + // which prepare() most recently resolved. + [CallWith=ScriptState, RaisesException] Promise<void> start(optional DocumentTransitionStartOptions options = {}); };
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_callback.idl b/third_party/blink/renderer/core/document_transition/document_transition_callback.idl deleted file mode 100644 index bf1e354..0000000 --- a/third_party/blink/renderer/core/document_transition/document_transition_callback.idl +++ /dev/null
@@ -1,8 +0,0 @@ -// Copyright 2022 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. - -[ - RuntimeEnabled=DocumentTransition -] callback DocumentTransitionCallback = void(DocumentTransition documentTransition); -
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl b/third_party/blink/renderer/core/document_transition/document_transition_config.idl similarity index 66% copy from third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl copy to third_party/blink/renderer/core/document_transition/document_transition_config.idl index 8c7837a..3e404d2 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl +++ b/third_party/blink/renderer/core/document_transition/document_transition_config.idl
@@ -2,5 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -dictionary DocumentTransitionSetElementOptions { +dictionary DocumentTransitionConfig { + DOMTimeStamp duration; + DOMTimeStamp delay; };
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_prepare_options.idl b/third_party/blink/renderer/core/document_transition/document_transition_prepare_options.idl new file mode 100644 index 0000000..c373f3bd --- /dev/null +++ b/third_party/blink/renderer/core/document_transition/document_transition_prepare_options.idl
@@ -0,0 +1,30 @@ +// Copyright 2020 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. + +enum RootTransitionType { + "none", + "cover-down", + "cover-left", + "cover-right", + "cover-up", + "explode", + "fade", + "implode", + "reveal-down", + "reveal-left", + "reveal-right", + "reveal-up" +}; + +dictionary DocumentTransitionPrepareOptions { + RootTransitionType rootTransition; + DocumentTransitionConfig rootConfig; + sequence<Element?> sharedElements; + AbortSignal abortSignal; + + // This config should be folded with the list of |sharedElements| into a + // single dictionary. Fix once we have a resolution on API shape : + // https://github.com/WICG/shared-element-transitions/issues/2. + sequence<DocumentTransitionConfig> sharedElementsConfig; +};
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl b/third_party/blink/renderer/core/document_transition/document_transition_start_options.idl similarity index 67% rename from third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl rename to third_party/blink/renderer/core/document_transition/document_transition_start_options.idl index 8c7837a..8bc91eb 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_set_element_options.idl +++ b/third_party/blink/renderer/core/document_transition/document_transition_start_options.idl
@@ -2,5 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -dictionary DocumentTransitionSetElementOptions { +dictionary DocumentTransitionStartOptions { + sequence<Element?> sharedElements; };
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc index efee5fc..28e99cb 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc +++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
@@ -15,7 +15,6 @@ #include "third_party/blink/renderer/core/document_transition/document_transition_utils.h" #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/pseudo_element.h" -#include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h" @@ -32,8 +31,6 @@ return kRootTag; } -constexpr int root_index = 0; - const String& StaticUAStyles() { DEFINE_STATIC_LOCAL( String, kStaticUAStyles, @@ -48,6 +45,12 @@ return kAnimationUAStyles; } +AtomicString IdFromIndex(wtf_size_t index) { + StringBuilder builder; + builder.AppendFormat("shared-%d", index); + return builder.ToAtomicString(); +} + } // namespace class DocumentTransitionStyleTracker::ImageWrapperPseudoElement @@ -101,108 +104,71 @@ DocumentTransitionStyleTracker::~DocumentTransitionStyleTracker() = default; -void DocumentTransitionStyleTracker::AddSharedElement(Element* element, - const AtomicString& tag) { - DCHECK(element); - // TODO(vmpstr): Log a console warning if we're modifying elements in a state - // that does not permit to do so. - if (state_ == State::kCapturing || state_ == State::kStarted) - return; - - // TODO(vmpstr): One element can have multiple tags associated with it, but - // it isn't currently allowed to have one tag be associated with more than one - // element. The explainer dictates to abandon the transition. We need to - // detect that case and abandon the transition. - pending_shared_elements_.push_back(element); - pseudo_document_transition_tags_.push_back(tag); -} - -void DocumentTransitionStyleTracker::RemoveSharedElement(Element* element) { - // TODO(vmpstr): Log a console warning if we're modifying elements in a state - // that does not permit to do so. - if (state_ == State::kCapturing || state_ == State::kStarted) - return; - for (wtf_size_t i = 0; i < pending_shared_elements_.size(); ++i) { - if (pending_shared_elements_[i] == element) { - pending_shared_elements_.EraseAt(i); - pseudo_document_transition_tags_.EraseAt(i); - } - } -} - -void DocumentTransitionStyleTracker::Capture() { +void DocumentTransitionStyleTracker::Prepare( + const HeapVector<Member<Element>>& old_elements) { DCHECK_EQ(state_, State::kIdle); - state_ = State::kCapturing; + state_ = State::kPreparing; + + // An id for each shared element + root. + pseudo_document_transition_tags_.resize(old_elements.size() + 1); // The order of IDs in this list defines the DOM order and as a result the // paint order of these elements. This is why root needs to be first in the // list. + pseudo_document_transition_tags_[0] = RootTag(); old_root_snapshot_id_ = viz::SharedElementResourceId::Generate(); - element_data_map_.ReserveCapacityForSize(pending_shared_elements_.size()); - for (wtf_size_t i = 0; i < pending_shared_elements_.size(); ++i) { - const auto& document_transition_tag = pseudo_document_transition_tags_[i]; + element_data_map_.ReserveCapacityForSize(old_elements.size()); + for (wtf_size_t i = 0; i < old_elements.size(); ++i) { + auto document_transition_tag = IdFromIndex(i); auto* element_data = MakeGarbageCollected<ElementData>(); - element_data->target_element = pending_shared_elements_[i]; - DCHECK_NE(root_index, static_cast<int>(i + 1)); - element_data->element_index = i + 1; - if (pending_shared_elements_[i]) + element_data->target_element = old_elements[i]; + if (old_elements[i]) element_data->old_snapshot_id = viz::SharedElementResourceId::Generate(); element_data_map_.insert(document_transition_tag, std::move(element_data)); + + pseudo_document_transition_tags_[i + 1] = + std::move(document_transition_tag); } - // TODO(vmpstr): This is a bit awkward. push/set/pop - pseudo_document_transition_tags_.push_front(RootTag()); document_->GetStyleEngine().SetDocumentTransitionTags( pseudo_document_transition_tags_); - pseudo_document_transition_tags_.EraseAt(0); // We need a style invalidation to generate the pseudo element tree. InvalidateStyle(); } -void DocumentTransitionStyleTracker::CaptureResolved() { - DCHECK_EQ(state_, State::kCapturing); +void DocumentTransitionStyleTracker::PrepareResolved() { + DCHECK_EQ(state_, State::kPreparing); - state_ = State::kCaptured; - - // Since the elements will be unset, we need to invalidate their style first. - // TODO(vmpstr): We don't have to invalidate the pseudo styles at this point, - // just the shared elements. We can split InvalidateStyle() into two functions - // as an optimization. - InvalidateStyle(); + state_ = State::kPrepared; for (auto& entry : element_data_map_) { auto& element_data = entry.value; element_data->target_element = nullptr; element_data->cached_border_box_size = element_data->border_box_size; element_data->cached_viewport_matrix = element_data->viewport_matrix; - element_data->effect_node = nullptr; } - root_effect_node_ = nullptr; } -void DocumentTransitionStyleTracker::Start() { - DCHECK_EQ(state_, State::kCaptured); +void DocumentTransitionStyleTracker::Start( + const HeapVector<Member<Element>>& new_elements) { + DCHECK_EQ(state_, State::kPrepared); + DCHECK_EQ(element_data_map_.size(), new_elements.size()); state_ = State::kStarted; new_root_snapshot_id_ = viz::SharedElementResourceId::Generate(); - for (wtf_size_t i = 0; i < pending_shared_elements_.size(); ++i) { - const auto& document_transition_tag = pseudo_document_transition_tags_[i]; - - // TODO(vmpstr): Support new elements during start. It requires us to figure - // out what the new document tag set is as well as creating new element - // data. - if (element_data_map_.find(document_transition_tag) == - element_data_map_.end()) - continue; + for (wtf_size_t i = 0; i < new_elements.size(); ++i) { + auto document_transition_tag = IdFromIndex(i); auto& element_data = element_data_map_.find(document_transition_tag)->value; - element_data->target_element = pending_shared_elements_[i]; - if (pending_shared_elements_[i]) + element_data->target_element = new_elements[i]; + if (new_elements[i]) element_data->new_snapshot_id = viz::SharedElementResourceId::Generate(); + element_data->effect_node = nullptr; } + root_effect_node_ = nullptr; // We need a style invalidation to generate new content pseudo elements for // new elements in the DOM. @@ -223,39 +189,32 @@ element_data_map_.clear(); pseudo_document_transition_tags_.clear(); - pending_shared_elements_.clear(); document_->GetStyleEngine().SetDocumentTransitionTags({}); // We need a style invalidation to remove the pseudo element tree. InvalidateStyle(); } -void DocumentTransitionStyleTracker::UpdateElementIndicesAndSnapshotId( - Element* element, - DocumentTransitionSharedElementId& index, - viz::SharedElementResourceId& resource_id) const { +viz::SharedElementResourceId DocumentTransitionStyleTracker::GetLiveSnapshotId( + const Element* element) const { DCHECK(element); for (const auto& entry : element_data_map_) { if (entry.value->target_element == element) { - index.AddIndex(entry.value->element_index); - resource_id = HasLiveNewContent() ? entry.value->new_snapshot_id - : entry.value->old_snapshot_id; - DCHECK(resource_id.IsValid()); - return; + auto snapshot_id = HasLiveNewContent() ? entry.value->new_snapshot_id + : entry.value->old_snapshot_id; + DCHECK(snapshot_id.IsValid()); + return snapshot_id; } } NOTREACHED(); + return viz::SharedElementResourceId(); } -void DocumentTransitionStyleTracker::UpdateRootIndexAndSnapshotId( - DocumentTransitionSharedElementId& index, - viz::SharedElementResourceId& resource_id) const { - index.AddIndex(root_index); - resource_id = - HasLiveNewContent() ? new_root_snapshot_id_ : old_root_snapshot_id_; - DCHECK(resource_id.IsValid()); +viz::SharedElementResourceId +DocumentTransitionStyleTracker::GetLiveRootSnapshotId() const { + return HasLiveNewContent() ? new_root_snapshot_id_ : old_root_snapshot_id_; } PseudoElement* DocumentTransitionStyleTracker::CreatePseudoElement( @@ -467,56 +426,6 @@ return root_effect_node_.get(); } -void DocumentTransitionStyleTracker::VerifySharedElements() { - for (auto& entry : element_data_map_) { - auto& element_data = entry.value; - if (!element_data->target_element) - continue; - auto& active_element = element_data->target_element; - - auto* object = active_element->GetLayoutObject(); - - // TODO(vmpstr): Should this work for replaced elements as well? - if (object) { - if (object->ShouldApplyPaintContainment()) - continue; - - auto* console_message = MakeGarbageCollected<ConsoleMessage>( - mojom::blink::ConsoleMessageSource::kRendering, - mojom::blink::ConsoleMessageLevel::kError, - "Dropping element from transition. Shared element must have " - "containt:paint"); - console_message->SetNodes(document_->GetFrame(), - {DOMNodeIds::IdForNode(active_element)}); - document_->AddConsoleMessage(console_message); - } - - // Clear the shared element. Note that we don't remove the element from the - // vector, since we need to preserve the order of the elements and we - // support nulls as a valid active element. - - // Invalidate the element since we should no longer be compositing it. - auto* box = active_element->GetLayoutBox(); - if (box && box->HasSelfPaintingLayer()) { - box->SetNeedsPaintPropertyUpdate(); - box->Layer()->SetNeedsCompositingInputsUpdate(); - } - active_element = nullptr; - } -} - -bool DocumentTransitionStyleTracker::IsSharedElement(Element* element) const { - // In stable states, we don't have shared elements. - if (state_ == State::kIdle || state_ == State::kCaptured) - return false; - - for (auto& entry : element_data_map_) { - if (entry.value->target_element == element) - return true; - } - return false; -} - void DocumentTransitionStyleTracker::InvalidateStyle() { ua_style_sheet_.reset(); document_->GetStyleEngine().InvalidateUADocumentTransitionStyle(); @@ -540,25 +449,13 @@ if (layout_view->HasSelfPaintingLayer()) layout_view->Layer()->SetNeedsCompositingInputsUpdate(); } - for (auto& entry : element_data_map_) { if (!entry.value->target_element) continue; auto* object = entry.value->target_element->GetLayoutObject(); if (!object) continue; - - // We propagate the shared element id on an effect node for the object. This - // means that we should update the paint properties to update the shared - // element id. object->SetNeedsPaintPropertyUpdate(); - - auto* box = entry.value->target_element->GetLayoutBox(); - if (!box || !box->HasSelfPaintingLayer()) - continue; - - // We might need to composite or decomposite this layer. - box->Layer()->SetNeedsCompositingInputsUpdate(); } } @@ -646,7 +543,6 @@ void DocumentTransitionStyleTracker::Trace(Visitor* visitor) const { visitor->Trace(document_); visitor->Trace(element_data_map_); - visitor->Trace(pending_shared_elements_); } void DocumentTransitionStyleTracker::ElementData::Trace(
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h index 3b22be9..3e0a759 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h +++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
@@ -9,7 +9,6 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/platform/geometry/layout_size.h" -#include "third_party/blink/renderer/platform/graphics/document_transition_shared_element_id.h" #include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" @@ -37,20 +36,19 @@ explicit DocumentTransitionStyleTracker(Document& document); ~DocumentTransitionStyleTracker(); - void AddSharedElement(Element*, const AtomicString&); - void RemoveSharedElement(Element*); - - // Notifies when the transition is initiated. - void Capture(); + // Notifies when the transition is initiated. |elements| is the set of shared + // elements in the old DOM. + void Prepare(const HeapVector<Member<Element>>& old_elements); // Notifies when caching snapshots for elements in the old DOM finishes. This // is dispatched before script is notified to ensure this class releases any // references to elements in the old DOM before it is mutated by script. - void CaptureResolved(); + void PrepareResolved(); // Notifies when the new DOM has finished loading and a transition can be - // started. - void Start(); + // started. |elements| is the set of shared elements in the new DOM paired + // sequentially with the list of |elements| in the Prepare call. + void Start(const HeapVector<Member<Element>>& new_elements); // Notifies when the animation setup for the transition during Start have // finished executing. @@ -60,12 +58,13 @@ // is initiated. void Abort(); - void UpdateRootIndexAndSnapshotId(DocumentTransitionSharedElementId&, - viz::SharedElementResourceId&) const; + // Returns the resource id that |element| should be tagged with. This + // |element| must be a shared element in the current DOM (specified in Prepare + // or Start). + viz::SharedElementResourceId GetLiveSnapshotId(const Element* element) const; - void UpdateElementIndicesAndSnapshotId(Element*, - DocumentTransitionSharedElementId&, - viz::SharedElementResourceId&) const; + // Returns the resource id for the root stacking context. + viz::SharedElementResourceId GetLiveRootSnapshotId() const; // Creates a PseudoElement for the corresponding |pseudo_id| and // |document_transition_tag|. The |pseudo_id| must be a ::transition* element. @@ -100,20 +99,12 @@ EffectPaintPropertyNode* GetEffect(Element* element) const; EffectPaintPropertyNode* GetRootEffect() const; - void VerifySharedElements(); - - int PendingSharedElementCount() const { - return pending_shared_elements_.size(); - } - - bool IsSharedElement(Element* element) const; - private: class ImageWrapperPseudoElement; // These state transitions are executed in a serial order unless the // transition is aborted. - enum class State { kIdle, kCapturing, kCaptured, kStarted, kFinished }; + enum class State { kIdle, kPreparing, kPrepared, kStarted, kFinished }; struct ElementData : public GarbageCollected<ElementData> { void Trace(Visitor* visitor) const; @@ -140,9 +131,6 @@ // An effect used to represent the `target_element`'s contents, including // any of element's own effects, in a pseudo element layer. scoped_refptr<EffectPaintPropertyNode> effect_node; - - // Index to add to the document transition shared element id. - int element_index; }; void InvalidateStyle(); @@ -151,8 +139,7 @@ Member<Document> document_; State state_ = State::kIdle; - VectorOf<AtomicString> pseudo_document_transition_tags_; - VectorOf<Element> pending_shared_elements_; + Vector<AtomicString> pseudo_document_transition_tags_; HeapHashMap<AtomicString, Member<ElementData>> element_data_map_; viz::SharedElementResourceId old_root_snapshot_id_; viz::SharedElementResourceId new_root_snapshot_id_;
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_supplement.cc b/third_party/blink/renderer/core/document_transition/document_transition_supplement.cc index 456b39f..d7349c6b 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_supplement.cc +++ b/third_party/blink/renderer/core/document_transition/document_transition_supplement.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h" #include "cc/document_transition/document_transition_request.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_callback.h" #include "third_party/blink/renderer/core/document_transition/document_transition.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -34,25 +33,13 @@ } // static -DocumentTransition* DocumentTransitionSupplement::EnsureDocumentTransition( +DocumentTransition* DocumentTransitionSupplement::documentTransition( Document& document) { auto* supplement = From(document); DCHECK(supplement->GetTransition()); return supplement->GetTransition(); } -// static -void DocumentTransitionSupplement::createDocumentTransition( - Document& document, - V8DocumentTransitionCallback* callback) { - auto* transition = EnsureDocumentTransition(document); - // TODO(vmpstr): We need to figure what to do if we already have a transition. - if (transition->HasActiveTransition()) - return; - auto script_scope = transition->CreateScriptMutationsAllowedScope(); - callback->InvokeAndReportException(&document, transition); -} - DocumentTransition* DocumentTransitionSupplement::GetTransition() { return transition_; }
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_supplement.h b/third_party/blink/renderer/core/document_transition/document_transition_supplement.h index a18d0a3..c998dbcb 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_supplement.h +++ b/third_party/blink/renderer/core/document_transition/document_transition_supplement.h
@@ -12,7 +12,6 @@ namespace blink { class DocumentTransition; -class V8DocumentTransitionCallback; class CORE_EXPORT DocumentTransitionSupplement : public GarbageCollected<DocumentTransitionSupplement>, @@ -24,10 +23,7 @@ static DocumentTransitionSupplement* From(Document&); static DocumentTransitionSupplement* FromIfExists(Document&); - static DocumentTransition* EnsureDocumentTransition(Document&); - - static void createDocumentTransition(Document&, - V8DocumentTransitionCallback* callback); + static DocumentTransition* documentTransition(Document&); DocumentTransition* GetTransition();
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_supplement.idl b/third_party/blink/renderer/core/document_transition/document_transition_supplement.idl index 7d9578dd..ff7541ff 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_supplement.idl +++ b/third_party/blink/renderer/core/document_transition/document_transition_supplement.idl
@@ -6,5 +6,5 @@ ImplementedAs=DocumentTransitionSupplement, RuntimeEnabled=DocumentTransition ] partial interface Document { - [MeasureAs=DocumentTransition] void createDocumentTransition(DocumentTransitionCallback callback); + [SameObject, MeasureAs=DocumentTransition] readonly attribute DocumentTransition documentTransition; };
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_test.cc b/third_party/blink/renderer/core/document_transition/document_transition_test.cc index 3eb3827..8e1d0dd 100644 --- a/third_party/blink/renderer/core/document_transition/document_transition_test.cc +++ b/third_party/blink/renderer/core/document_transition/document_transition_test.cc
@@ -12,7 +12,8 @@ #include "third_party/blink/public/web/web_settings.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_set_element_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_prepare_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_start_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_root_transition_type.h" #include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/css/style_engine.h" @@ -108,14 +109,14 @@ void FinishTransition() { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); + DocumentTransitionSupplement::documentTransition(GetDocument()); transition->NotifyStartFinished(transition->last_start_sequence_id_); } bool ShouldCompositeForDocumentTransition(Element* e) { auto* layout_object = e->GetLayoutObject(); auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); + DocumentTransitionSupplement::documentTransition(GetDocument()); return layout_object && transition && transition->IsTransitionParticipant(*layout_object); } @@ -174,9 +175,9 @@ TEST_P(DocumentTransitionTest, TransitionObjectPersists) { auto* first_transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); + DocumentTransitionSupplement::documentTransition(GetDocument()); auto* second_transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); + DocumentTransitionSupplement::documentTransition(GetDocument()); EXPECT_TRUE(first_transition); EXPECT_EQ(GetState(first_transition), State::kIdle); @@ -185,9 +186,9 @@ } TEST_P(DocumentTransitionTest, TransitionPreparePromiseResolves) { + DocumentTransitionPrepareOptions options; auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); ASSERT_TRUE(transition); EXPECT_EQ(GetState(transition), State::kIdle); @@ -196,32 +197,35 @@ ExceptionState& exception_state = v8_scope.GetExceptionState(); ScriptPromiseTester promise_tester( - script_state, transition->captureAndHold(script_state, exception_state)); + script_state, + transition->prepare(script_state, &options, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + EXPECT_EQ(GetState(transition), State::kPreparing); UpdateAllLifecyclePhasesAndFinishDirectives(); promise_tester.WaitUntilSettled(); EXPECT_TRUE(promise_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + EXPECT_EQ(GetState(transition), State::kPrepared); } TEST_P(DocumentTransitionTest, AdditionalPrepareRejectsPreviousPromise) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); + DocumentTransitionPrepareOptions options; ScriptPromiseTester first_promise_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + script_state, + transition->prepare(script_state, &options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); ScriptPromiseTester second_promise_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + script_state, + transition->prepare(script_state, &options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); UpdateAllLifecyclePhasesAndFinishDirectives(); first_promise_tester.WaitUntilSettled(); @@ -229,7 +233,37 @@ EXPECT_TRUE(first_promise_tester.IsRejected()); EXPECT_TRUE(second_promise_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + EXPECT_EQ(GetState(transition), State::kPrepared); +} + +TEST_P(DocumentTransitionTest, EffectParsing) { + // Test default init. + auto* transition = + DocumentTransitionSupplement::documentTransition(GetDocument()); + + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + ExceptionState& exception_state = v8_scope.GetExceptionState(); + DocumentTransitionPrepareOptions default_options; + transition->prepare(script_state, &default_options, exception_state); + + auto request = transition->TakePendingRequest(); + ASSERT_TRUE(request); + + auto directive = request->ConstructDirective({}); + EXPECT_EQ(directive.effect(), DocumentTransitionRequest::Effect::kNone); + + // Test "explode" effect parsing. + DocumentTransitionPrepareOptions explode_options; + explode_options.setRootTransition( + V8RootTransitionType(V8RootTransitionType::Enum::kExplode)); + transition->prepare(script_state, &explode_options, exception_state); + + request = transition->TakePendingRequest(); + ASSERT_TRUE(request); + + directive = request->ConstructDirective({}); + EXPECT_EQ(directive.effect(), DocumentTransitionRequest::Effect::kExplode); } TEST_P(DocumentTransitionTest, PrepareSharedElementsWantToBeComposited) { @@ -248,8 +282,7 @@ auto* e3 = GetDocument().getElementById("e3"); auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); @@ -259,9 +292,10 @@ EXPECT_FALSE(ShouldCompositeForDocumentTransition(e2)); EXPECT_FALSE(ShouldCompositeForDocumentTransition(e3)); - transition->setElement(script_state, e1, "e1", nullptr, exception_state); - transition->setElement(script_state, e3, "e3", nullptr, exception_state); - transition->captureAndHold(script_state, exception_state); + DocumentTransitionPrepareOptions options; + // Set two of the elements to be shared. + options.setSharedElements({e1, e3}); + transition->prepare(script_state, &options, exception_state); // Update the lifecycle while keeping the transition active. UpdateAllLifecyclePhasesForTest(); @@ -302,8 +336,7 @@ auto* e3 = GetDocument().getElementById("e3"); auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); @@ -313,10 +346,9 @@ EXPECT_FALSE(ShouldCompositeForDocumentTransition(e2)); EXPECT_FALSE(ShouldCompositeForDocumentTransition(e3)); - transition->setElement(script_state, e1, "e1", nullptr, exception_state); - transition->setElement(script_state, e2, "e2", nullptr, exception_state); - transition->setElement(script_state, e3, "e3", nullptr, exception_state); - transition->captureAndHold(script_state, exception_state); + DocumentTransitionPrepareOptions options; + options.setSharedElements({e1, e2, e3}); + transition->prepare(script_state, &options, exception_state); EXPECT_TRUE(ShouldCompositeForDocumentTransition(e1)); EXPECT_TRUE(ShouldCompositeForDocumentTransition(e2)); @@ -335,6 +367,44 @@ EXPECT_FALSE(ElementIsComposited("e3")); } +TEST_P(DocumentTransitionTest, StartSharedElementCountMismatch) { + SetHtmlInnerHTML(R"HTML( + <div id=e1></div> + <div id=e2></div> + <div id=e3></div> + )HTML"); + + auto* e1 = GetDocument().getElementById("e1"); + auto* e2 = GetDocument().getElementById("e2"); + auto* e3 = GetDocument().getElementById("e3"); + + auto* transition = + DocumentTransitionSupplement::documentTransition(GetDocument()); + + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + ExceptionState& exception_state = v8_scope.GetExceptionState(); + + DocumentTransitionPrepareOptions prepare_options; + // Set two of the elements to be shared. + prepare_options.setSharedElements({e1, e3}); + transition->prepare(script_state, &prepare_options, exception_state); + + UpdateAllLifecyclePhasesAndFinishDirectives(); + + DocumentTransitionStartOptions start_options; + // Set all of the elements as shared. This should cause an exception. + start_options.setSharedElements({e1, e2, e3}); + + EXPECT_FALSE(exception_state.HadException()); + transition->start(script_state, &start_options, exception_state); + EXPECT_TRUE(exception_state.HadException()); + + EXPECT_FALSE(ShouldCompositeForDocumentTransition(e1)); + EXPECT_FALSE(ShouldCompositeForDocumentTransition(e2)); + EXPECT_FALSE(ShouldCompositeForDocumentTransition(e3)); +} + TEST_P(DocumentTransitionTest, StartSharedElementsWantToBeComposited) { SetHtmlInnerHTML(R"HTML( <div id=e1></div> @@ -347,17 +417,16 @@ auto* e3 = GetDocument().getElementById("e3"); auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); + DocumentTransitionPrepareOptions prepare_options; // Set two of the elements to be shared. - transition->setElement(script_state, e1, "e1", nullptr, exception_state); - transition->setElement(script_state, e3, "e3", nullptr, exception_state); - transition->captureAndHold(script_state, exception_state); + prepare_options.setSharedElements({e1, e3}); + transition->prepare(script_state, &prepare_options, exception_state); EXPECT_TRUE(ShouldCompositeForDocumentTransition(e1)); EXPECT_FALSE(ShouldCompositeForDocumentTransition(e2)); @@ -365,14 +434,10 @@ UpdateAllLifecyclePhasesAndFinishDirectives(); + DocumentTransitionStartOptions start_options; // Set two different elements as shared. - // Unset e3. - transition->setElement(script_state, e3, AtomicString(), nullptr, - exception_state); - // Set e2 to be the same tag as "e3". - // TODO(vmpstr): We should be able to support new tags for entry transitions. - transition->setElement(script_state, e2, "e3", nullptr, exception_state); - transition->start(script_state, exception_state); + start_options.setSharedElements({e1, e2}); + transition->start(script_state, &start_options, exception_state); EXPECT_TRUE(ShouldCompositeForDocumentTransition(e1)); EXPECT_TRUE(ShouldCompositeForDocumentTransition(e2)); @@ -387,30 +452,32 @@ TEST_P(DocumentTransitionTest, AdditionalPrepareAfterPreparedSucceeds) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); + DocumentTransitionPrepareOptions options; ScriptPromiseTester first_promise_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + script_state, + transition->prepare(script_state, &options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); UpdateAllLifecyclePhasesAndFinishDirectives(); first_promise_tester.WaitUntilSettled(); EXPECT_TRUE(first_promise_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + EXPECT_EQ(GetState(transition), State::kPrepared); ScriptPromiseTester second_promise_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + script_state, + transition->prepare(script_state, &options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); UpdateAllLifecyclePhasesAndFinishDirectives(); second_promise_tester.WaitUntilSettled(); EXPECT_TRUE(second_promise_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + EXPECT_EQ(GetState(transition), State::kPrepared); } TEST_P(DocumentTransitionTest, TransitionCleanedUpBeforePromiseResolution) { @@ -418,11 +485,11 @@ ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionPrepareOptions options; ScriptPromiseTester tester( - script_state, transition->captureAndHold(script_state, exception_state)); + script_state, + DocumentTransitionSupplement::documentTransition(GetDocument()) + ->prepare(script_state, &options, exception_state)); // ActiveScriptWrappable should keep the transition alive. ThreadState::Current()->CollectAllGarbageForTesting(); @@ -434,8 +501,7 @@ TEST_P(DocumentTransitionTest, StartHasNoEffectUnlessPrepared) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); EXPECT_EQ(GetState(transition), State::kIdle); EXPECT_FALSE(transition->TakePendingRequest()); @@ -443,7 +509,8 @@ ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - transition->start(script_state, exception_state); + DocumentTransitionStartOptions options; + transition->start(script_state, &options, exception_state); EXPECT_EQ(GetState(transition), State::kIdle); EXPECT_FALSE(transition->TakePendingRequest()); EXPECT_TRUE(exception_state.HadException()); @@ -451,24 +518,27 @@ TEST_P(DocumentTransitionTest, StartAfterPrepare) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - ScriptPromiseTester capture_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + DocumentTransitionPrepareOptions prepare_options; + ScriptPromiseTester prepare_tester( + script_state, + transition->prepare(script_state, &prepare_options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); UpdateAllLifecyclePhasesAndFinishDirectives(); - capture_tester.WaitUntilSettled(); - EXPECT_TRUE(capture_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + prepare_tester.WaitUntilSettled(); + EXPECT_TRUE(prepare_tester.IsFulfilled()); + EXPECT_EQ(GetState(transition), State::kPrepared); + DocumentTransitionStartOptions start_options; ScriptPromiseTester start_tester( - script_state, transition->start(script_state, exception_state)); + script_state, + transition->start(script_state, &start_options, exception_state)); // Take the request. auto start_request = transition->TakePendingRequest(); EXPECT_TRUE(start_request); @@ -476,7 +546,7 @@ // Subsequent starts should get an exception. EXPECT_FALSE(exception_state.HadException()); - transition->start(script_state, exception_state); + transition->start(script_state, &start_options, exception_state); EXPECT_TRUE(exception_state.HadException()); EXPECT_FALSE(transition->TakePendingRequest()); @@ -488,30 +558,33 @@ TEST_P(DocumentTransitionTest, StartPromiseIsResolved) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - ScriptPromiseTester capture_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + DocumentTransitionPrepareOptions prepare_options; + ScriptPromiseTester prepare_tester( + script_state, + transition->prepare(script_state, &prepare_options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); - // Visual updates are allows during capture phase. + // Visual updates are allows during prepare phase. EXPECT_FALSE(LayerTreeHost()->IsDeferringCommits()); UpdateAllLifecyclePhasesAndFinishDirectives(); - capture_tester.WaitUntilSettled(); - EXPECT_TRUE(capture_tester.IsFulfilled()); - EXPECT_EQ(GetState(transition), State::kCaptured); + prepare_tester.WaitUntilSettled(); + EXPECT_TRUE(prepare_tester.IsFulfilled()); + EXPECT_EQ(GetState(transition), State::kPrepared); - // Visual updates are stalled between captured and start. + // Visual updates are stalled between prepared and start. EXPECT_TRUE(LayerTreeHost()->IsDeferringCommits()); + DocumentTransitionStartOptions start_options; ScriptPromiseTester start_tester( - script_state, transition->start(script_state, exception_state)); + script_state, + transition->start(script_state, &start_options, exception_state)); EXPECT_EQ(GetState(transition), State::kStarted); UpdateAllLifecyclePhasesAndFinishDirectives(); @@ -525,23 +598,26 @@ EXPECT_EQ(GetState(transition), State::kIdle); } -TEST_P(DocumentTransitionTest, Abandon) { +TEST_P(DocumentTransitionTest, AbortSignal) { auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - ScriptPromiseTester capture_tester( - script_state, transition->captureAndHold(script_state, exception_state)); - EXPECT_EQ(GetState(transition), State::kCapturing); + auto* abort_signal = + MakeGarbageCollected<AbortSignal>(v8_scope.GetExecutionContext()); + DocumentTransitionPrepareOptions prepare_options; + prepare_options.setAbortSignal(abort_signal); + ScriptPromiseTester prepare_tester( + script_state, + transition->prepare(script_state, &prepare_options, exception_state)); + EXPECT_EQ(GetState(transition), State::kPreparing); - transition->abandon(script_state, exception_state); - - capture_tester.WaitUntilSettled(); - EXPECT_TRUE(capture_tester.IsRejected()); + abort_signal->SignalAbort(script_state); + prepare_tester.WaitUntilSettled(); + EXPECT_TRUE(prepare_tester.IsRejected()); EXPECT_EQ(GetState(transition), State::kIdle); } @@ -563,23 +639,21 @@ auto* e3 = GetDocument().getElementById("e3"); auto* transition = - DocumentTransitionSupplement::EnsureDocumentTransition(GetDocument()); - auto scope = transition->CreateScriptMutationsAllowedScope(); + DocumentTransitionSupplement::documentTransition(GetDocument()); V8TestingScope v8_scope; ScriptState* script_state = v8_scope.GetScriptState(); ExceptionState& exception_state = v8_scope.GetExceptionState(); - transition->setElement(script_state, e1, "e1", nullptr, exception_state); - transition->setElement(script_state, e2, "e2", nullptr, exception_state); - transition->setElement(script_state, e3, "e3", nullptr, exception_state); - transition->captureAndHold(script_state, exception_state); + DocumentTransitionPrepareOptions options; + options.setSharedElements({e1, e2, e3}); + transition->prepare(script_state, &options, exception_state); ASSERT_FALSE(exception_state.HadException()); UpdateAllLifecyclePhasesForTest(); // The prepare phase should generate the pseudo tree. - const Vector<AtomicString> document_transition_tags = {"root", "e1", "e2", - "e3"}; + const Vector<AtomicString> document_transition_tags = {"shared-0", "shared-1", + "shared-2"}; ValidatePseudoElementTree(document_transition_tags, false); // Finish the prepare phase, mutate the DOM and start the animation. @@ -593,7 +667,9 @@ <div id=e2></div> <div id=e3></div> )HTML"); - transition->start(script_state, exception_state); + DocumentTransitionStartOptions start_options; + start_options.setSharedElements({e1, e2, e3}); + transition->start(script_state, &start_options, exception_state); ASSERT_FALSE(exception_state.HadException()); // The start phase should generate pseudo elements for rendering new live
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc index d328614..8af7a484 100644 --- a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc +++ b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
@@ -82,12 +82,12 @@ } RasterInvalidationTracking* CaretRasterInvalidationTracking() const { - for (const auto& client : GetDocument() - .View() - ->GetPaintArtifactCompositor() - ->ContentLayerClientsForTesting()) { + wtf_size_t i = 0; + auto* pac = GetDocument().View()->GetPaintArtifactCompositor(); + while (auto* client = pac->ContentLayerClientForTesting(i)) { if (client->Layer().DebugName() == "Caret") return client->GetRasterInvalidator().GetTracking(); + ++i; } return nullptr; }
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc index b47394f..9c0fd2d1 100644 --- a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc +++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
@@ -120,4 +120,72 @@ ResponseParseStatus::kSuccess, std::move(sources)); } +bool ParseEventTriggerData( + const AtomicString& json_string, + WTF::Vector<mojom::blink::EventTriggerDataPtr>& event_trigger_data) { + // Populate attribution data from provided JSON. + std::unique_ptr<JSONValue> json = ParseJSON(json_string); + + // TODO(johnidel): Log a devtools issues if JSON parsing fails and on + // individual early exits below. + if (!json) + return false; + + JSONArray* array_value = JSONArray::Cast(json.get()); + if (!array_value) + return false; + + // Do not proceed if too many event trigger data are specified. + if (array_value->size() > kMaxAttributionEventTriggerData) + return false; + + // Process each event trigger. + for (wtf_size_t i = 0; i < array_value->size(); ++i) { + JSONValue* value = array_value->at(i); + DCHECK(value); + + const auto* object_val = JSONObject::Cast(value); + if (!object_val) + return false; + + mojom::blink::EventTriggerDataPtr event_trigger = + mojom::blink::EventTriggerData::New(); + + String trigger_data_string; + // A valid header must declare data for each sub-item. + if (!object_val->GetString("trigger_data", &trigger_data_string)) + return false; + bool trigger_data_is_valid = false; + uint64_t trigger_data_value = + trigger_data_string.ToUInt64Strict(&trigger_data_is_valid); + + // Default invalid data values to 0 so a report will get sent. + event_trigger->data = trigger_data_is_valid ? trigger_data_value : 0; + + // Treat invalid priority and deduplication key as if they were not set. + String priority_string; + if (object_val->GetString("priority", &priority_string)) { + bool priority_is_valid = false; + int64_t priority = priority_string.ToInt64Strict(&priority_is_valid); + if (priority_is_valid) + event_trigger->priority = priority; + } + + // Treat invalid priority and deduplication_key as if they were not set. + String dedup_key_string; + if (object_val->GetString("deduplication_key", &dedup_key_string)) { + bool dedup_key_is_valid = false; + uint64_t dedup_key = dedup_key_string.ToUInt64Strict(&dedup_key_is_valid); + if (dedup_key_is_valid) { + event_trigger->dedup_key = + mojom::blink::AttributionTriggerDedupKey::New(dedup_key); + } + } + + event_trigger_data.push_back(std::move(event_trigger)); + } + + return true; +} + } // namespace blink::attribution_response_parsing
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.h b/third_party/blink/renderer/core/frame/attribution_response_parsing.h index d0ce380..ceb65b6dc 100644 --- a/third_party/blink/renderer/core/frame/attribution_response_parsing.h +++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_ATTRIBUTION_RESPONSE_PARSING_H_ #include <utility> +#include <vector> #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" @@ -47,6 +48,19 @@ CORE_EXPORT ResponseParseResult<mojom::blink::AttributionAggregatableSources> ParseAttributionAggregatableSources(const AtomicString& json_string); +// Parses event trigger data header of the form: +// +// [{ +// "trigger_data": "5" +// "priority": "10", +// "deduplication_key": "456" +// }] +// +// Returns whether parsing was successful. +CORE_EXPORT bool ParseEventTriggerData( + const AtomicString& json_string, + WTF::Vector<mojom::blink::EventTriggerDataPtr>& event_trigger_data); + } // namespace blink::attribution_response_parsing #endif // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_ATTRIBUTION_RESPONSE_PARSING_H_
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc index 88e348f6..82dd4341 100644 --- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc +++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -166,7 +166,9 @@ local_frame_->GetRemoteNavigationAssociatedInterfaces()->GetInterface( &conversion_host); conversion_host->RegisterDataHost(data_host.BindNewPipeAndPassReceiver()); - resource_data_host_map_.insert(resource, std::move(data_host)); + resource_context_map_.insert( + resource, AttributionSrcContext{.type = AttributionSrcType::kUndetermined, + .data_host = std::move(data_host)}); } void AttributionSrcLoader::Shutdown() { @@ -186,28 +188,50 @@ } void AttributionSrcLoader::NotifyFinished(Resource* resource) { - DCHECK(resource_data_host_map_.Contains(resource)); - resource_data_host_map_.erase(resource); + DCHECK(resource_context_map_.Contains(resource)); + resource_context_map_.erase(resource); } void AttributionSrcLoader::HandleResponseHeaders( Resource* resource, const ResourceResponse& response) { - if (!resource_data_host_map_.Contains(resource)) + auto it = resource_context_map_.find(resource); + if (it == resource_context_map_.end()) return; + AttributionSrcContext& context = it->value; + const auto& headers = response.HttpHeaderFields(); - if (headers.Contains(http_names::kAttributionReportingRegisterSource)) - HandleSourceRegistration(resource, response); + + bool can_process_source = context.type == AttributionSrcType::kUndetermined || + context.type == AttributionSrcType::kSource; + if (can_process_source && + headers.Contains(http_names::kAttributionReportingRegisterSource)) { + context.type = AttributionSrcType::kSource; + HandleSourceRegistration(resource, response, context); + return; + } + + // TODO(johnidel): Consider surfacing an error when source and trigger headers + // are present together. + bool can_process_trigger = + context.type == AttributionSrcType::kUndetermined || + context.type == AttributionSrcType::kTrigger; + if (can_process_trigger && + headers.Contains(http_names::kAttributionReportingRegisterEventTrigger)) { + context.type = AttributionSrcType::kTrigger; + HandleTriggerRegistration(resource, response, context); + } // TODO(johnidel): Add parsing for trigger and filter headers. } void AttributionSrcLoader::HandleSourceRegistration( Resource* resource, - const ResourceResponse& response) { - auto it = resource_data_host_map_.find(resource); - DCHECK_NE(it, resource_data_host_map_.end()); + const ResourceResponse& response, + AttributionSrcContext& context) { + auto it = resource_context_map_.find(resource); + DCHECK_NE(it, resource_context_map_.end()); mojom::blink::AttributionSourceDataPtr source_data = mojom::blink::AttributionSourceData::New(); @@ -294,7 +318,33 @@ aggregatable_sources_json); source_data->aggregatable_sources = std::move(aggregatable_sources.value); - it->value->SourceDataAvailable(std::move(source_data)); + context.data_host->SourceDataAvailable(std::move(source_data)); +} + +void AttributionSrcLoader::HandleTriggerRegistration( + Resource* resource, + const ResourceResponse& response, + AttributionSrcContext& context) { + mojom::blink::AttributionTriggerDataPtr trigger_data = + mojom::blink::AttributionTriggerData::New(); + + // Verify the current url is trustworthy and capable of registering triggers. + scoped_refptr<const SecurityOrigin> reporting_origin = + SecurityOrigin::CreateFromString(response.CurrentRequestUrl()); + if (!reporting_origin->IsPotentiallyTrustworthy()) + return; + trigger_data->reporting_origin = + SecurityOrigin::Create(response.CurrentRequestUrl()); + + // Populate event triggers. + bool success = attribution_response_parsing::ParseEventTriggerData( + response.HttpHeaderField( + http_names::kAttributionReportingRegisterEventTrigger), + trigger_data->event_triggers); + if (!success) + return; + + context.data_host->TriggerDataAvailable(std::move(trigger_data)); } void AttributionSrcLoader::LogAuditIssue(
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.h b/third_party/blink/renderer/core/frame/attribution_src_loader.h index 07e36ca..41703df 100644 --- a/third_party/blink/renderer/core/frame/attribution_src_loader.h +++ b/third_party/blink/renderer/core/frame/attribution_src_loader.h
@@ -41,13 +41,27 @@ void Trace(Visitor* visitor) const override { visitor->Trace(local_frame_); - visitor->Trace(resource_data_host_map_); + visitor->Trace(resource_context_map_); RawResourceClient::Trace(visitor); } String DebugName() const override { return "AttributionSrcLoader"; } private: + // Represents what events are able to be registered from an attributionsrc. + enum class AttributionSrcType { kUndetermined, kSource, kTrigger }; + + // State associated with each ongoing attribution src request. + struct AttributionSrcContext { + // Type of events this request can register. In some cases, this will not be + // assigned until the first event is received. A single attributionsrc + // request can only register one type of event across redirects. + AttributionSrcType type; + + // Remote used for registering responses with the browser-process. + mojo::Remote<mojom::blink::AttributionDataHost> data_host; + }; + // RawResourceClient: void ResponseReceived(Resource* resource, const ResourceResponse& response) override; @@ -59,16 +73,19 @@ void HandleResponseHeaders(Resource* resource, const ResourceResponse& response); void HandleSourceRegistration(Resource* resource, - const ResourceResponse& response); + const ResourceResponse& response, + AttributionSrcContext& context); + void HandleTriggerRegistration(Resource* resource, + const ResourceResponse& response, + AttributionSrcContext& context); void LogAuditIssue(AttributionReportingIssueType issue_type, const String& string, HTMLElement* element = nullptr); Member<LocalFrame> local_frame_; - HeapHashMap<WeakMember<Resource>, - mojo::Remote<mojom::blink::AttributionDataHost>> - resource_data_host_map_; + HeapHashMap<WeakMember<Resource>, AttributionSrcContext> + resource_context_map_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index 3bc77d4d..170c74e21 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -858,8 +858,7 @@ !frame_->PagePopupOwner() && !FirstMeaningfulPaintDetector::From(*frame_->GetDocument()) .SeenFirstMeaningfulPaint()); - DeferredShapingViewportScope viewport_scope( - *this, GetLayoutView()->InitialContainingBlockSize().block_size); + DeferredShapingViewportScope viewport_scope(*this, *GetLayoutView()); GetLayoutView()->UpdateLayout(); } }
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni index 2a072722..658f98e7 100644 --- a/third_party/blink/renderer/core/layout/build.gni +++ b/third_party/blink/renderer/core/layout/build.gni
@@ -40,6 +40,7 @@ "custom_scrollbar.h", "depth_ordered_layout_object_list.cc", "depth_ordered_layout_object_list.h", + "deferred_shaping.cc", "deferred_shaping.h", "flexible_box_algorithm.cc", "flexible_box_algorithm.h",
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping.cc b/third_party/blink/renderer/core/layout/deferred_shaping.cc new file mode 100644 index 0000000..7b79c83 --- /dev/null +++ b/third_party/blink/renderer/core/layout/deferred_shaping.cc
@@ -0,0 +1,30 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/deferred_shaping.h" + +#include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" + +namespace blink { + +DeferredShapingViewportScope::DeferredShapingViewportScope( + LocalFrameView& view, + const LayoutView& layout_view) + : view_(view), previous_value_(view.CurrentViewportBottom()) { + LayoutUnit viewport_top = + LayoutUnit(layout_view.GetScrollableArea() + ? view.GetScrollableArea()->GetScrollOffset().y() + : 0); + LayoutUnit viewport_height = + layout_view.InitialContainingBlockSize().block_size; + view_.SetCurrentViewportBottom( + PassKey(), + viewport_top + viewport_height + + LayoutUnit(viewport_height * + DisplayLockDocumentState::kViewportMarginPercentage / + 100)); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping.h b/third_party/blink/renderer/core/layout/deferred_shaping.h index 0213bd01..280a808 100644 --- a/third_party/blink/renderer/core/layout/deferred_shaping.h +++ b/third_party/blink/renderer/core/layout/deferred_shaping.h
@@ -16,11 +16,8 @@ using PassKey = base::PassKey<DeferredShapingViewportScope>; public: - DeferredShapingViewportScope(LocalFrameView& view, LayoutUnit viewport_bottom) - : view_(view), previous_value_(view.CurrentViewportBottom()) { - view_.SetCurrentViewportBottom(PassKey(), viewport_bottom); - } - + DeferredShapingViewportScope(LocalFrameView& view, + const LayoutView& layout_view); ~DeferredShapingViewportScope() { view_.SetCurrentViewportBottom(PassKey(), previous_value_); }
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping_test.cc b/third_party/blink/renderer/core/layout/deferred_shaping_test.cc index 3004004..13a7496 100644 --- a/third_party/blink/renderer/core/layout/deferred_shaping_test.cc +++ b/third_party/blink/renderer/core/layout/deferred_shaping_test.cc
@@ -46,6 +46,18 @@ EXPECT_FALSE(IsLocked("target")); } +TEST_F(DeferredShapingTest, ViewportMargin) { + // The box starting around y=1200 (viewport height * 2) is not deferred due to + // a viewport margin setting for IntersectionObserver. + SetBodyInnerHTML(R"HTML( +<div style="height:1200px"></div> +<div id="target">IFC</div> +)HTML"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_FALSE(IsDefer("target")); + EXPECT_FALSE(IsLocked("target")); +} + TEST_F(DeferredShapingTest, AlreadyAuto) { // If the element has content-visibility:auto, it never be deferred. SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h index 4ccfacd1..9021038 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h +++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
@@ -298,12 +298,6 @@ return {descendants.data(), descendants.size()}; } - void Clear() override { - oof_positioned_fragmentainer_descendants.clear(); - multicols_with_pending_oofs.clear(); - NGPhysicalFragment::OutOfFlowData::Clear(); - } - void Trace(Visitor* visitor) const override { visitor->Trace(oof_positioned_fragmentainer_descendants); visitor->Trace(multicols_with_pending_oofs);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index 9358837d..d6a42e3 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -444,10 +444,13 @@ } } -NGPhysicalBoxFragment::~NGPhysicalBoxFragment() = default; +NGPhysicalBoxFragment::~NGPhysicalBoxFragment() { + // Note: This function may not always be called because the dtor of + // NGPhysicalFragment is made non-virtual for memory efficiency. + ink_overflow_type_ = ink_overflow_.Reset(InkOverflowType()); +} void NGPhysicalBoxFragment::Dispose() { - ink_overflow_.Reset(InkOverflowType()); if (const_has_fragment_items_) ComputeItemsAddress()->~NGFragmentItems(); if (const_has_rare_data_)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc index 94ea260..c6e961e7 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -29,23 +29,10 @@ struct SameSizeAsNGPhysicalFragment : public GarbageCollected<SameSizeAsNGPhysicalFragment> { - // It is necessary to have not only the member variables but also - // USING_PRE_FINALIZER baceuse Win bots fail without it. - USING_PRE_FINALIZER(SameSizeAsNGPhysicalFragment, Dispose); - - public: - void Dispose() {} - void Trace(Visitor* visitor) const { - visitor->Trace(layout_object); - visitor->Trace(break_token); - visitor->Trace(oof_data); - } - - Member<LayoutObject> layout_object; + Member<void*> layout_object; PhysicalSize size; - [[maybe_unused]] unsigned flags; - Member<const NGBreakToken> break_token; - const Member<NGPhysicalFragment::OutOfFlowData> oof_data; + unsigned flags; + Member<void*> members[2]; }; ASSERT_SIZE(NGPhysicalFragment, SameSizeAsNGPhysicalFragment); @@ -450,11 +437,11 @@ DCHECK(children_valid_); } -NGPhysicalFragment::~NGPhysicalFragment() = default; +NGPhysicalFragment::~NGPhysicalFragment() { + Dispose(); +} void NGPhysicalFragment::Dispose() { - if (UNLIKELY(oof_data_ && has_fragmented_out_of_flow_data_)) - const_cast<NGPhysicalFragment*>(this)->ClearOutOfFlowData(); switch (Type()) { case kFragmentBox: static_cast<NGPhysicalBoxFragment*>(this)->Dispose(); @@ -504,13 +491,6 @@ return !!oof_data_; } -void NGPhysicalFragment::ClearOutOfFlowData() { - CHECK(oof_data_ && has_fragmented_out_of_flow_data_); - auto* oof_data = const_cast<Member<OutOfFlowData>*>(&oof_data_); - oof_data->Get()->Clear(); - oof_data->Clear(); -} - NGPhysicalFragment::OutOfFlowData* NGPhysicalFragment::CloneOutOfFlowData() const { DCHECK(oof_data_); @@ -1058,10 +1038,6 @@ return false; } -void NGPhysicalFragment::OutOfFlowData::Clear() { - oof_positioned_descendants.clear(); -} - void NGPhysicalFragment::OutOfFlowData::Trace(Visitor* visitor) const { visitor->Trace(oof_positioned_descendants); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h index dfd5b00..59f67c0 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -52,8 +52,6 @@ // coordinate system. class CORE_EXPORT NGPhysicalFragment : public GarbageCollected<NGPhysicalFragment> { - USING_PRE_FINALIZER(NGPhysicalFragment, Dispose); - public: enum NGFragmentType { kFragmentBox = 0, @@ -602,10 +600,7 @@ struct OutOfFlowData : public GarbageCollected<OutOfFlowData> { public: - virtual void Clear(); - virtual void Trace(Visitor* visitor) const; - HeapVector<NGPhysicalOutOfFlowPositionedNode> oof_positioned_descendants; };
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc index c0b8195..f54e5f1 100644 --- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc +++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -932,7 +932,8 @@ if (String string = search_params->get("dedup-key")) { if (absl::optional<uint64_t> value = parse_uint64(string)) { - conversion->dedup_key = mojom::blink::DedupKey::New(*value); + conversion->dedup_key = + mojom::blink::AttributionTriggerDedupKey::New(*value); } else { AuditsIssue::ReportAttributionIssue( document_->domWindow(),
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc index 66eeb8a..bc2e94e 100644 --- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc +++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -18,9 +18,8 @@ static ContentLayerClientImpl* GetContentLayerClient( const LocalFrameView& root_frame_view, wtf_size_t index) { - const auto& clients = root_frame_view.GetPaintArtifactCompositor() - ->ContentLayerClientsForTesting(); - return index < clients.size() ? clients[index].get() : nullptr; + return root_frame_view.GetPaintArtifactCompositor() + ->ContentLayerClientForTesting(index); } const RasterInvalidationTracking* GetRasterInvalidationTracking(
diff --git a/third_party/blink/renderer/core/web_test/web_test_web_frame_widget_impl.cc b/third_party/blink/renderer/core/web_test/web_test_web_frame_widget_impl.cc index c8f83c7e..b3caefd 100644 --- a/third_party/blink/renderer/core/web_test/web_test_web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/web_test/web_test_web_frame_widget_impl.cc
@@ -120,7 +120,7 @@ } void WebTestWebFrameWidgetImpl::DisableEndDocumentTransition() { - DocumentTransitionSupplement::EnsureDocumentTransition( + DocumentTransitionSupplement::documentTransition( *LocalRootImpl()->GetFrame()->GetDocument()) ->DisableEndTransition(); }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc index 613d059..ec4d357 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -61,14 +61,9 @@ #endif } -scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer( +void ContentLayerClientImpl::UpdateCcPictureLayer( const PendingLayer& pending_layer) { const auto& paint_chunks = pending_layer.Chunks(); - if (paint_chunks.begin()->is_cacheable) - id_.emplace(paint_chunks.begin()->id); - else - id_ = absl::nullopt; - #if EXPENSIVE_DCHECKS_ARE_ON() paint_chunk_debug_data_ = std::make_unique<JSONArray>(); for (auto it = paint_chunks.begin(); it != paint_chunks.end(); ++it) { @@ -119,7 +114,7 @@ cc_picture_layer_->draws_content() == pending_layer.DrawsContent() && !raster_under_invalidation_params) { DCHECK_EQ(cc_picture_layer_->bounds(), layer_bounds); - return cc_picture_layer_; + return; } cc_display_item_list_ = PaintChunksToCcLayer::Convert( @@ -131,7 +126,14 @@ cc_picture_layer_->SetHitTestable(true); cc_picture_layer_->SetIsDrawable(pending_layer.DrawsContent()); - return cc_picture_layer_; + bool contents_opaque = pending_layer.RectKnownToBeOpaque().Contains( + gfx::RectF(gfx::PointAtOffsetFromOrigin(pending_layer.LayerOffset()), + gfx::SizeF(pending_layer.LayerBounds()))); + cc_picture_layer_->SetContentsOpaque(contents_opaque); + if (!contents_opaque) { + cc_picture_layer_->SetContentsOpaqueForText( + pending_layer.TextKnownToBeOnOpaqueBackground()); + } } void ContentLayerClientImpl::InvalidateRect(const gfx::Rect& rect) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h index 71a1279..05b19d6 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h +++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -47,11 +47,7 @@ cc::Layer& Layer() const { return *cc_picture_layer_.get(); } const PropertyTreeState& State() const { return layer_state_; } - bool Matches(const PaintChunk& paint_chunk) const { - return id_ && paint_chunk.Matches(*id_); - } - - scoped_refptr<cc::PictureLayer> UpdateCcPictureLayer(const PendingLayer&); + void UpdateCcPictureLayer(const PendingLayer&); RasterInvalidator& GetRasterInvalidator() { return raster_invalidator_; } @@ -61,7 +57,6 @@ // Callback from raster_invalidator_. void InvalidateRect(const gfx::Rect&); - absl::optional<PaintChunk::Id> id_; scoped_refptr<cc::PictureLayer> cc_picture_layer_; scoped_refptr<cc::DisplayItemList> cc_display_item_list_; RasterInvalidator raster_invalidator_;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index 9993bbd..1847908 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -9,7 +9,6 @@ #include "base/logging.h" #include "cc/document_transition/document_transition_request.h" -#include "cc/layers/scrollbar_layer_base.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_flags.h" #include "cc/trees/effect_node.h" @@ -18,7 +17,6 @@ #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/geometry/geometry_as_json.h" #include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h" -#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item.h" @@ -45,6 +43,35 @@ // http://crbug.com/692842#c4. static int g_s_property_tree_sequence_number = 1; +class PaintArtifactCompositor::OldPendingLayerMatcher { + public: + explicit OldPendingLayerMatcher(PendingLayers pending_layers) + : pending_layers_(std::move(pending_layers)) {} + + // Finds the next PendingLayer that can be matched by |new_layer|. + // It's efficient if most of the pending layers can be matched sequentially. + PendingLayer* Find(const PendingLayer& new_layer) { + if (pending_layers_.IsEmpty()) + return nullptr; + if (!new_layer.FirstPaintChunk().CanMatchOldChunk()) + return nullptr; + wtf_size_t i = next_index_; + do { + wtf_size_t next = (i + 1) % pending_layers_.size(); + if (new_layer.Matches(pending_layers_[i])) { + next_index_ = next; + return &pending_layers_[i]; + } + i = next; + } while (i != next_index_); + return nullptr; + } + + private: + wtf_size_t next_index_ = 0; + PendingLayers pending_layers_; +}; + PaintArtifactCompositor::PaintArtifactCompositor( base::WeakPtr<CompositorScrollCallbacks> scroll_callbacks) : scroll_callbacks_(std::move(scroll_callbacks)), @@ -56,8 +83,10 @@ void PaintArtifactCompositor::SetTracksRasterInvalidations(bool should_track) { tracks_raster_invalidations_ = should_track || VLOG_IS_ON(3); - for (auto& client : content_layer_clients_) - client->GetRasterInvalidator().SetTracksRasterInvalidations(should_track); + for (auto& pending_layer : pending_layers_) { + if (auto* client = pending_layer.GetContentLayerClient()) + client->GetRasterInvalidator().SetTracksRasterInvalidations(should_track); + } } void PaintArtifactCompositor::WillBeRemovedFromFrame() { @@ -91,10 +120,10 @@ for (const auto& layer : root_layer_->children()) { const LayerAsJSONClient* json_client = nullptr; const TransformPaintPropertyNode* transform = nullptr; - for (const auto& client : content_layer_clients_) { - if (&client->Layer() == layer.get()) { - json_client = client.get(); - transform = &client->State().Transform(); + for (const auto& pending_layer : pending_layers_) { + if (layer.get() == &pending_layer.CcLayer()) { + json_client = pending_layer.GetContentLayerClient(); + transform = &pending_layer.GetPropertyTreeState().Transform(); break; } } @@ -114,27 +143,6 @@ return layers_as_json.Finalize(); } -scoped_refptr<cc::Layer> PaintArtifactCompositor::WrappedCcLayerForPendingLayer( - const PendingLayer& pending_layer) { - if (pending_layer.GetCompositingType() != PendingLayer::kForeignLayer) - return nullptr; - - // UpdateTouchActionRects() depends on the layer's offset, but when the - // layer's offset changes, we do not call SetNeedsUpdate() (this is an - // optimization because the update would only cause an extra commit) This is - // only OK if the ForeignLayer doesn't have hit test data. - DCHECK(!pending_layer.FirstPaintChunk().hit_test_data); - const auto& foreign_layer_display_item = - To<ForeignLayerDisplayItem>(pending_layer.FirstDisplayItem()); - - gfx::Vector2dF layer_offset = gfx::Vector2dF( - foreign_layer_display_item.VisualRect().OffsetFromOrigin()); - cc::Layer* layer = foreign_layer_display_item.GetLayer(); - layer->SetOffsetToTransformParent( - layer_offset + pending_layer.OffsetOfDecompositedTransforms()); - return layer; -} - const TransformPaintPropertyNode& PaintArtifactCompositor::NearestScrollTranslationForLayer( const PendingLayer& pending_layer) { @@ -148,140 +156,6 @@ return transform.NearestScrollTranslationNode(); } -scoped_refptr<cc::Layer> -PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer( - const PendingLayer& pending_layer) { - if (pending_layer.GetCompositingType() != PendingLayer::kScrollHitTestLayer) - return nullptr; - - // We shouldn't decomposite scroll transform nodes. - DCHECK_EQ(gfx::Vector2dF(), pending_layer.OffsetOfDecompositedTransforms()); - - const auto& scroll_node = - *pending_layer.ScrollTranslationForScrollHitTestLayer().ScrollNode(); - - scoped_refptr<cc::Layer> scroll_layer; - auto scroll_element_id = scroll_node.GetCompositorElementId(); - for (auto& existing_layer : scroll_hit_test_layers_) { - if (existing_layer && existing_layer->element_id() == scroll_element_id) { - scroll_layer = std::move(existing_layer); - break; - } - } - - if (scroll_layer) { - DCHECK_EQ(scroll_layer->element_id(), scroll_node.GetCompositorElementId()); - } else { - scroll_layer = cc::Layer::Create(); - scroll_layer->SetElementId(scroll_node.GetCompositorElementId()); - scroll_layer->SetHitTestable(true); - } - - scroll_layer->SetOffsetToTransformParent( - gfx::Vector2dF(scroll_node.ContainerRect().OffsetFromOrigin())); - // TODO(pdr): The scroll layer's bounds are currently set to the clipped - // container bounds but this does not include the border. We may want to - // change this behavior to make non-composited and composited hit testing - // match (see: crbug.com/753124). To do this, use - // |scroll_hit_test->scroll_container_bounds|. Set the layer's bounds equal - // to the container because the scroll layer does not scroll. - scroll_layer->SetBounds(scroll_node.ContainerRect().size()); - - if (scroll_node.NodeChanged() != PaintPropertyChangeType::kUnchanged) { - scroll_layer->SetNeedsPushProperties(); - scroll_layer->SetNeedsCommit(); - } - - return scroll_layer; -} - -scoped_refptr<cc::ScrollbarLayerBase> -PaintArtifactCompositor::ScrollbarLayerForPendingLayer( - const PendingLayer& pending_layer) { - if (pending_layer.GetCompositingType() != PendingLayer::kScrollbarLayer) - return nullptr; - - const auto& item = pending_layer.FirstDisplayItem(); - DCHECK(item.IsScrollbar()); - - const auto& scrollbar_item = To<ScrollbarDisplayItem>(item); - scoped_refptr<cc::ScrollbarLayerBase> scrollbar_layer; - for (auto& layer : scrollbar_layers_) { - if (layer && layer->element_id() == scrollbar_item.ElementId()) { - scrollbar_layer = std::move(layer); - break; - } - } - - scrollbar_layer = scrollbar_item.CreateOrReuseLayer(scrollbar_layer.get()); - scrollbar_layer->SetOffsetToTransformParent( - scrollbar_layer->offset_to_transform_parent() + - gfx::Vector2dF(pending_layer.OffsetOfDecompositedTransforms())); - return scrollbar_layer; -} - -std::unique_ptr<ContentLayerClientImpl> -PaintArtifactCompositor::ClientForPaintChunk(const PaintChunk& paint_chunk) { - // TODO(chrishtr): for now, just using a linear walk. In the future we can - // optimize this by using the same techniques used in PaintController for - // display lists. - for (auto& client : content_layer_clients_) { - if (client && client->Matches(paint_chunk)) - return std::move(client); - } - - auto client = std::make_unique<ContentLayerClientImpl>(); - client->GetRasterInvalidator().SetTracksRasterInvalidations( - tracks_raster_invalidations_); - return client; -} - -scoped_refptr<cc::Layer> -PaintArtifactCompositor::CompositedLayerForPendingLayer( - const PendingLayer& pending_layer, - Vector<std::unique_ptr<ContentLayerClientImpl>>& new_content_layer_clients, - Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers, - Vector<scoped_refptr<cc::ScrollbarLayerBase>>& new_scrollbar_layers) { - // If the paint chunk is a foreign layer or pre-composited layer, just return - // its cc::Layer. - if (auto cc_layer = WrappedCcLayerForPendingLayer(pending_layer)) - return cc_layer; - - // If the paint chunk is a scroll hit test layer, lookup/create the layer. - if (auto scroll_layer = ScrollHitTestLayerForPendingLayer(pending_layer)) { - new_scroll_hit_test_layers.push_back(scroll_layer); - return scroll_layer; - } - - if (auto scrollbar_layer = ScrollbarLayerForPendingLayer(pending_layer)) { - new_scrollbar_layers.push_back(scrollbar_layer); - return scrollbar_layer; - } - - // The common case: create or reuse a PictureLayer for painted content. - std::unique_ptr<ContentLayerClientImpl> content_layer_client = - ClientForPaintChunk(pending_layer.FirstPaintChunk()); - - gfx::Vector2dF layer_offset = pending_layer.LayerOffset(); - gfx::Size layer_bounds = pending_layer.LayerBounds(); - auto cc_layer = content_layer_client->UpdateCcPictureLayer(pending_layer); - new_content_layer_clients.push_back(std::move(content_layer_client)); - - // Set properties that foreign layers would normally control for themselves - // here to avoid changing foreign layers. This includes things set by video - // clients etc. - bool contents_opaque = pending_layer.RectKnownToBeOpaque().Contains( - gfx::RectF(gfx::PointAtOffsetFromOrigin(layer_offset), - gfx::SizeF(layer_bounds))); - cc_layer->SetContentsOpaque(contents_opaque); - if (!contents_opaque) { - cc_layer->SetContentsOpaqueForText( - pending_layer.TextKnownToBeOnOpaqueBackground()); - } - - return cc_layer; -} - namespace { cc::Layer* ForeignLayer(const PaintChunk& chunk, @@ -447,7 +321,7 @@ PendingLayer& layer = pending_layers_[layer_index]; if (&layer.GetPropertyTreeState().Effect() != &effect) return false; - if (layer.RequiresOwnLayer()) + if (layer.ChunkRequiresOwnLayer()) return false; if (effect.HasDirectCompositingReasons()) return false; @@ -525,7 +399,7 @@ // force_draws_content doesn't apply to pending layers that require own // layer, specifically scrollbar layers, foreign layers, scroll hit // testing layers. - if (pending_layers_.back().RequiresOwnLayer()) + if (pending_layers_.back().ChunkRequiresOwnLayer()) continue; } else { const EffectPaintPropertyNode* subgroup = @@ -555,7 +429,7 @@ // processed. Now determine whether it could be merged into a previous // layer. PendingLayer& new_layer = pending_layers_.back(); - DCHECK(!new_layer.RequiresOwnLayer()); + DCHECK(!new_layer.ChunkRequiresOwnLayer()); DCHECK_EQ(¤t_group, &new_layer.GetPropertyTreeState().Effect()); if (force_draws_content) new_layer.ForceDrawsContent(); @@ -578,8 +452,6 @@ void PaintArtifactCompositor::CollectPendingLayers( scoped_refptr<const PaintArtifact> artifact) { - // Shrink, but do not release the backing. Re-use it from the last frame. - pending_layers_.Shrink(0); PaintChunkSubset subset(artifact); auto cursor = subset.begin(); LayerizeGroup(subset, EffectPaintPropertyNode::Root(), cursor, @@ -769,6 +641,10 @@ root_layer_->set_property_tree_sequence_number( g_s_property_tree_sequence_number); + wtf_size_t old_size = pending_layers_.size(); + OldPendingLayerMatcher old_pending_layer_matcher(std::move(pending_layers_)); + pending_layers_.ReserveCapacity(old_size); + // Make compositing decisions, storing the result in |pending_layers_|. CollectPendingLayers(artifact); PendingLayer::DecompositeTransforms(pending_layers_); @@ -786,11 +662,6 @@ if (RuntimeEnabledFeatures::ScrollUnificationEnabled()) property_tree_manager.EnsureCompositorScrollNodes(scroll_translation_nodes); - Vector<std::unique_ptr<ContentLayerClientImpl>> new_content_layer_clients; - new_content_layer_clients.ReserveCapacity(pending_layers_.size()); - Vector<scoped_refptr<cc::Layer>> new_scroll_hit_test_layers; - Vector<scoped_refptr<cc::ScrollbarLayerBase>> new_scrollbar_layers; - for (auto& entry : synthesized_clip_cache_) entry.in_use = false; @@ -798,33 +669,28 @@ ->effect_tree_mutable() .ClearTransitionPseudoElementEffectNodes(); cc::LayerSelection layer_selection; - for (const auto& pending_layer : pending_layers_) { + for (auto& pending_layer : pending_layers_) { + pending_layer.UpdateCompositedLayer( + old_pending_layer_matcher.Find(pending_layer), layer_selection, + tracks_raster_invalidations_); + cc::Layer& layer = pending_layer.CcLayer(); + layer.SetLayerTreeHost(root_layer_->layer_tree_host()); + const auto& property_state = pending_layer.GetPropertyTreeState(); const auto& transform = property_state.Transform(); const auto& clip = property_state.Clip(); const auto& effect = property_state.Effect(); - - scoped_refptr<cc::Layer> layer = CompositedLayerForPendingLayer( - pending_layer, new_content_layer_clients, new_scroll_hit_test_layers, - new_scrollbar_layers); - - UpdateLayerProperties(*layer, pending_layer); - UpdateLayerSelection(*layer, pending_layer, layer_selection); - - layer->SetLayerTreeHost(root_layer_->layer_tree_host()); - int transform_id = property_tree_manager.EnsureCompositorTransformNode(transform); int clip_id = property_tree_manager.EnsureCompositorClipNode(clip); int effect_id = property_tree_manager.SwitchToEffectNodeWithSynthesizedClip( - effect, clip, layer->draws_content()); + effect, clip, layer.draws_content()); // We need additional bookkeeping for backdrop-filter mask. if (effect.RequiresCompositingForBackdropFilterMask() && effect.CcNodeId(g_s_property_tree_sequence_number) == effect_id) { - static_cast<cc::PictureLayer*>(layer.get()) - ->SetIsBackdropFilterMask(true); - layer->SetElementId(effect.GetCompositorElementId()); + static_cast<cc::PictureLayer&>(layer).SetIsBackdropFilterMask(true); + layer.SetElementId(effect.GetCompositorElementId()); auto& effect_tree = host->property_trees()->effect_tree_mutable(); auto* cc_node = effect_tree.Node(effect_id); effect_tree.Node(cc_node->parent_id)->backdrop_mask_element_id = @@ -842,29 +708,29 @@ if (RuntimeEnabledFeatures::ScrollUnificationEnabled()) property_tree_manager.SetCcScrollNodeIsComposited(scroll_id); - layer_list_builder.Add(layer); + layer_list_builder.Add(&layer); - layer->set_property_tree_sequence_number( + layer.set_property_tree_sequence_number( root_layer_->property_tree_sequence_number()); - layer->SetTransformTreeIndex(transform_id); - layer->SetScrollTreeIndex(scroll_id); - layer->SetClipTreeIndex(clip_id); - layer->SetEffectTreeIndex(effect_id); + layer.SetTransformTreeIndex(transform_id); + layer.SetScrollTreeIndex(scroll_id); + layer.SetClipTreeIndex(clip_id); + layer.SetEffectTreeIndex(effect_id); bool backface_hidden = transform.IsBackfaceHidden(); - layer->SetShouldCheckBackfaceVisibility(backface_hidden); + layer.SetShouldCheckBackfaceVisibility(backface_hidden); // If the property tree state has changed between the layer and the root, // we need to inform the compositor so damage can be calculated. Calling // |PropertyTreeStateChanged| for every pending layer is O(|property // nodes|^2) and could be optimized by caching the lookup of nodes known // to be changed/unchanged. - if (layer->subtree_property_changed() || + if (layer.subtree_property_changed() || pending_layer.PropertyTreeStateChanged()) { - layer->SetSubtreePropertyChanged(); + layer.SetSubtreePropertyChanged(); root_layer_->SetNeedsCommit(); } - auto shared_element_id = layer->DocumentTransitionResourceId(); + auto shared_element_id = layer.DocumentTransitionResourceId(); if (shared_element_id.IsValid()) { host->property_trees() ->effect_tree_mutable() @@ -875,9 +741,6 @@ root_layer_->layer_tree_host()->RegisterSelection(layer_selection); property_tree_manager.Finalize(); - content_layer_clients_.swap(new_content_layer_clients); - scroll_hit_test_layers_.swap(new_scroll_hit_test_layers); - scrollbar_layers_.swap(new_scrollbar_layers); auto* new_end = std::remove_if( synthesized_clip_cache_.begin(), synthesized_clip_cache_.end(), @@ -915,44 +778,6 @@ .Utf8(); } -void PaintArtifactCompositor::UpdateLayerProperties( - cc::Layer& layer, - const PendingLayer& pending_layer) { - // Properties of foreign layers are managed by their owners. - if (pending_layer.GetCompositingType() == PendingLayer::kForeignLayer) - return; - PaintChunksToCcLayer::UpdateLayerProperties( - layer, pending_layer.GetPropertyTreeState(), pending_layer.Chunks()); -} - -void PaintArtifactCompositor::UpdateLayerSelection( - cc::Layer& layer, - const PendingLayer& pending_layer, - cc::LayerSelection& layer_selection) { - // Foreign layers cannot contain selection. - if (pending_layer.GetCompositingType() == PendingLayer::kForeignLayer) - return; - PaintChunksToCcLayer::UpdateLayerSelection( - layer, pending_layer.GetPropertyTreeState(), pending_layer.Chunks(), - layer_selection); -} - -void PaintArtifactCompositor::UpdateRepaintedContentLayerClient( - const PendingLayer& pending_layer, - bool pending_layer_chunks_unchanged, - ContentLayerClientImpl& content_layer_client) { - // Checking |pending_layer_chunks_unchanged| is an optimization to avoid the - // expensive call to |UpdateCcPictureLayer| when no repainting occurs for this - // PendingLayer. - if (pending_layer_chunks_unchanged) { - // See RasterInvalidator::SetOldPaintArtifact() for the reason for this. - content_layer_client.GetRasterInvalidator().SetOldPaintArtifact( - &pending_layer.Chunks().GetPaintArtifact()); - } else { - content_layer_client.UpdateCcPictureLayer(pending_layer); - } -} - void PaintArtifactCompositor::UpdateRepaintedLayers( scoped_refptr<const PaintArtifact> repainted_artifact) { // |Update| should be used for full updates. @@ -973,18 +798,14 @@ // The loop below iterates over the existing PendingLayers and issues updates. auto* repainted_chunk_iterator = repainted_chunks.begin(); - auto* content_layer_client_it = content_layer_clients_.begin(); - auto* scroll_hit_test_layer_it = scroll_hit_test_layers_.begin(); - auto* scrollbar_layer_it = scrollbar_layers_.begin(); - for (auto* pending_layer_it = pending_layers_.begin(); - pending_layer_it != pending_layers_.end(); pending_layer_it++) { + for (auto& pending_layer : pending_layers_) { // We need to both copy the repainted paint chunks and update the cc::Layer. // To do this, we need the previous PaintChunks (from the PendingLayer) and // the matching repainted PaintChunks (from |repainted_chunks|). Because // repaint-only updates cannot add, remove, or re-order PaintChunks, // |repainted_chunk_iterator| searches forward in |repainted_chunks| for // the matching paint chunk, ensuring this function is O(chunks). - const PaintChunk& first = *pending_layer_it->Chunks().begin(); + const PaintChunk& first = *pending_layer.Chunks().begin(); while (repainted_chunk_iterator != repainted_chunks.end()) { if (repainted_chunk_iterator->Matches(first)) break; @@ -995,51 +816,8 @@ // instead of a repaint update. CHECK(repainted_chunk_iterator != repainted_chunks.end()); - // Essentially replace the paint chunks of the pending layer with the - // repainted chunks in |repainted_artifact|. The pending layer's paint - // chunks (a |PaintChunkSubset|) actually store indices to |PaintChunk|s - // in a |PaintArtifact|. In repaint updates, chunks are not added, - // removed, or re-ordered, so we can simply swap in a repainted - // |PaintArtifact| instead of copying |PaintChunk|s individually. - const PaintArtifact& previous_artifact = - pending_layer_it->Chunks().GetPaintArtifact(); - DCHECK_EQ(previous_artifact.PaintChunks().size(), - repainted_artifact->PaintChunks().size()); - pending_layer_it->SetPaintArtifact(repainted_artifact); - - bool pending_layer_chunks_unchanged = true; - for (const auto& chunk : pending_layer_it->Chunks()) { - if (!chunk.is_moved_from_cached_subsequence) { - pending_layer_chunks_unchanged = false; - break; - } - } - - cc::Layer* cc_layer = nullptr; - switch (pending_layer_it->GetCompositingType()) { - case PendingLayer::kForeignLayer: - continue; - case PendingLayer::kScrollbarLayer: - cc_layer = scrollbar_layer_it->get(); - ++scrollbar_layer_it; - break; - case PendingLayer::kScrollHitTestLayer: - cc_layer = scroll_hit_test_layer_it->get(); - ++scroll_hit_test_layer_it; - break; - default: - UpdateRepaintedContentLayerClient(*pending_layer_it, - pending_layer_chunks_unchanged, - **content_layer_client_it); - cc_layer = &(*content_layer_client_it)->Layer(); - ++content_layer_client_it; - break; - } - DCHECK(cc_layer); - - if (!pending_layer_chunks_unchanged) - UpdateLayerProperties(*cc_layer, *pending_layer_it); - UpdateLayerSelection(*cc_layer, *pending_layer_it, layer_selection); + pending_layer.UpdateCompositedLayerForRepaint(repainted_artifact, + layer_selection); } root_layer_->layer_tree_host()->RegisterSelection(layer_selection); @@ -1180,35 +958,14 @@ if (!layer_debug_info_enabled_) return; - auto* content_layer_client_it = content_layer_clients_.begin(); - auto* scroll_hit_test_layer_it = scroll_hit_test_layers_.begin(); - auto* scrollbar_layer_it = scrollbar_layers_.begin(); const PendingLayer* previous_pending_layer = nullptr; for (const auto& pending_layer : pending_layers_) { - cc::Layer* layer; + cc::Layer& layer = pending_layer.CcLayer(); RasterInvalidationTracking* tracking = nullptr; - switch (pending_layer.GetCompositingType()) { - case PendingLayer::kForeignLayer: - layer = To<ForeignLayerDisplayItem>(pending_layer.FirstDisplayItem()) - .GetLayer(); - break; - case PendingLayer::kScrollbarLayer: - layer = scrollbar_layer_it->get(); - ++scrollbar_layer_it; - break; - case PendingLayer::kScrollHitTestLayer: - layer = scroll_hit_test_layer_it->get(); - ++scroll_hit_test_layer_it; - break; - default: - tracking = - (*content_layer_client_it)->GetRasterInvalidator().GetTracking(); - layer = &(*content_layer_client_it)->Layer(); - ++content_layer_client_it; - break; - } + if (auto* client = pending_layer.GetContentLayerClient()) + tracking = client->GetRasterInvalidator().GetTracking(); UpdateLayerDebugInfo( - *layer, pending_layer.FirstPaintChunk().id, + layer, pending_layer.FirstPaintChunk().id, pending_layer.Chunks().GetPaintArtifact(), GetCompositingReasons(pending_layer, previous_pending_layer), tracking); previous_pending_layer = &pending_layer; @@ -1220,7 +977,7 @@ const PendingLayer* previous_layer) const { DCHECK(layer_debug_info_enabled_); - if (layer.RequiresOwnLayer()) { + if (layer.ChunkRequiresOwnLayer()) { if (layer.GetCompositingType() == PendingLayer::kScrollHitTestLayer) return CompositingReason::kOverflowScrolling; switch (layer.FirstDisplayItem().GetType()) { @@ -1313,28 +1070,26 @@ } size_t PaintArtifactCompositor::ApproximateUnsharedMemoryUsage() const { - size_t result = sizeof(*this) + content_layer_clients_.CapacityInBytes() + - synthesized_clip_cache_.CapacityInBytes() + - scroll_hit_test_layers_.CapacityInBytes() + - scrollbar_layers_.CapacityInBytes() + + size_t result = sizeof(*this) + synthesized_clip_cache_.CapacityInBytes() + pending_layers_.CapacityInBytes(); - for (auto& client : content_layer_clients_) - result += client->ApproximateUnsharedMemoryUsage(); - for (auto& layer : pending_layers_) { + if (auto* client = layer.GetContentLayerClient()) + result += client->ApproximateUnsharedMemoryUsage(); size_t chunks_size = layer.Chunks().ApproximateUnsharedMemoryUsage(); DCHECK_GE(chunks_size, sizeof(layer.Chunks())); result += chunks_size - sizeof(layer.Chunks()); } + return result; } void PaintArtifactCompositor::SetScrollbarNeedsDisplay( CompositorElementId element_id) { - for (auto& layer : scrollbar_layers_) { - if (layer->element_id() == element_id) { - layer->SetNeedsDisplay(); + for (auto& pending_layer : pending_layers_) { + if (pending_layer.GetCompositingType() == PendingLayer::kScrollbarLayer && + pending_layer.CcLayer().element_id() == element_id) { + pending_layer.CcLayer().SetNeedsDisplay(); return; } } @@ -1364,4 +1119,16 @@ } #endif +ContentLayerClientImpl* PaintArtifactCompositor::ContentLayerClientForTesting( + wtf_size_t i) const { + for (auto& pending_layer : pending_layers_) { + if (auto* client = pending_layer.GetContentLayerClient()) { + if (i == 0) + return client; + --i; + } + } + return nullptr; +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h index c041cd7..1eed2ef 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -10,7 +10,6 @@ #include "base/dcheck_is_on.h" #include "base/memory/ptr_util.h" #include "base/memory/scoped_refptr.h" -#include "cc/input/layer_selection_bound.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/layer_collections.h" #include "cc/layers/picture_layer.h" @@ -30,7 +29,6 @@ #endif namespace cc { -class ScrollbarLayerBase; class DocumentTransitionRequest; } @@ -189,10 +187,8 @@ void ShowDebugData(); #endif - const Vector<std::unique_ptr<ContentLayerClientImpl>>& - ContentLayerClientsForTesting() const { - return content_layer_clients_; - } + // Returns the ith ContentLayerClientImpl for testing. + ContentLayerClientImpl* ContentLayerClientForTesting(wtf_size_t i) const; // Mark this as needing a full compositing update. Repaint-only updates that // do not affect compositing can use a fast-path in |UpdateRepaintedLayers| @@ -233,18 +229,6 @@ void SetScrollbarNeedsDisplay(CompositorElementId element_id); private: - static void UpdateLayerProperties(cc::Layer&, const PendingLayer&); - static void UpdateLayerSelection(cc::Layer&, - const PendingLayer&, - cc::LayerSelection& layer_selection); - - // Updates |content_layer_client| associated with a |pending_layer| following - // a paint. This includes updating the drawings and raster invalidation. - void UpdateRepaintedContentLayerClient( - const PendingLayer& pending_layer, - bool pending_layer_chunks_unchanged, - ContentLayerClientImpl& content_layer_client); - // Collects the PaintChunks into groups which will end up in the same // cc layer. This is the entry point of the layerization algorithm. void CollectPendingLayers(scoped_refptr<const PaintArtifact>); @@ -275,37 +259,9 @@ const EffectPaintPropertyNode& effect, wtf_size_t layer_index); - // Builds a leaf layer that represents a single paint chunk. - scoped_refptr<cc::Layer> CompositedLayerForPendingLayer( - const PendingLayer&, - Vector<std::unique_ptr<ContentLayerClientImpl>>& - new_content_layer_clients, - Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers, - Vector<scoped_refptr<cc::ScrollbarLayerBase>>& new_scrollbar_layers); - const TransformPaintPropertyNode& NearestScrollTranslationForLayer( const PendingLayer&); - // Returns the cc::Layer if the pending layer contains a foreign layer or a - // wrapper of a GraphicsLayer. If it's the latter and the graphics layer has - // been repainted, also updates the layer properties. - scoped_refptr<cc::Layer> WrappedCcLayerForPendingLayer(const PendingLayer&); - - // Finds an existing or creates a new scroll hit test layer for the pending - // layer, returning nullptr if the layer is not a scroll hit test layer. - scoped_refptr<cc::Layer> ScrollHitTestLayerForPendingLayer( - const PendingLayer&); - - // Finds an existing or creates a new scrollbar layer for the pending layer, - // returning nullptr if the layer is not a scrollbar layer. - scoped_refptr<cc::ScrollbarLayerBase> ScrollbarLayerForPendingLayer( - const PendingLayer&); - - // Finds a client among the current vector of clients that matches the paint - // chunk's id, or otherwise allocates a new one. - std::unique_ptr<ContentLayerClientImpl> ClientForPaintChunk( - const PaintChunk&); - // if |needs_layer| is false, no cc::Layer is created, |mask_effect_id| is // not set, and the Layer() method on the returned SynthesizedClip returns // nullptr. @@ -340,7 +296,6 @@ bool prefers_lcd_text_ = false; scoped_refptr<cc::Layer> root_layer_; - Vector<std::unique_ptr<ContentLayerClientImpl>> content_layer_clients_; struct SynthesizedClipEntry { const ClipPaintPropertyNode* key; std::unique_ptr<SynthesizedClip> synthesized_clip; @@ -348,10 +303,9 @@ }; Vector<SynthesizedClipEntry> synthesized_clip_cache_; - Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers_; - Vector<scoped_refptr<cc::ScrollbarLayerBase>> scrollbar_layers_; - - Vector<PendingLayer, 0> pending_layers_; + using PendingLayers = Vector<PendingLayer, 0>; + class OldPendingLayerMatcher; + PendingLayers pending_layers_; friend class StubChromeClientForCAP; friend class PaintArtifactCompositorTest;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc index 44e6bf7..703aa552 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -4,8 +4,11 @@ #include "third_party/blink/renderer/platform/graphics/compositing/pending_layer.h" +#include "cc/layers/scrollbar_layer_base.h" #include "third_party/blink/renderer/platform/geometry/geometry_as_json.h" +#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" @@ -88,7 +91,7 @@ property_tree_state_( first_chunk.properties.GetPropertyTreeState().Unalias()), compositing_type_(kOther) { - DCHECK(!RequiresOwnLayer() || first_chunk.size() <= 1u); + DCHECK(!ChunkRequiresOwnLayer() || first_chunk.size() <= 1u); // Though text_known_to_be_on_opaque_background is only meaningful when // has_text is true, we expect text_known_to_be_on_opaque_background to be // true when !has_text to simplify code. @@ -173,7 +176,7 @@ } void PendingLayer::Upcast(const PropertyTreeState& new_state) { - DCHECK(!RequiresOwnLayer()); + DCHECK(!ChunkRequiresOwnLayer()); if (property_tree_state_ == new_state) return; @@ -187,21 +190,22 @@ } const PaintChunk& PendingLayer::FirstPaintChunk() const { - DCHECK(!RequiresOwnLayer() || chunks_.size() == 1); return *chunks_.begin(); } const DisplayItem& PendingLayer::FirstDisplayItem() const { -#if DCHECK_IS_ON() - // This method should never be called if the first paint chunk is empty. - if (RequiresOwnLayer()) - DCHECK_EQ(FirstPaintChunk().size(), 1u); - else - DCHECK_GE(FirstPaintChunk().size(), 1u); -#endif return *chunks_.begin().DisplayItems().begin(); } +bool PendingLayer::Matches(const PendingLayer& old_pending_layer) const { + if (ChunkRequiresOwnLayer() != old_pending_layer.ChunkRequiresOwnLayer()) + return false; + if (ChunkRequiresOwnLayer() && + compositing_type_ != old_pending_layer.compositing_type_) + return false; + return FirstPaintChunk().Matches(old_pending_layer.FirstPaintChunk()); +} + // We will only allow merging if // merged_area - (home_area + guest_area) <= kMergeSparsityAreaTolerance static constexpr float kMergeSparsityAreaTolerance = 10000; @@ -212,7 +216,7 @@ bool dry_run) { if (&Chunks().GetPaintArtifact() != &guest.Chunks().GetPaintArtifact()) return false; - if (RequiresOwnLayer() || guest.RequiresOwnLayer()) + if (ChunkRequiresOwnLayer() || guest.ChunkRequiresOwnLayer()) return false; if (&GetPropertyTreeState().Effect() != &guest_state.Effect()) return false; @@ -455,6 +459,176 @@ } } +void PendingLayer::UpdateForeignLayer() { + DCHECK_EQ(compositing_type_, PendingLayer::kForeignLayer); + + // UpdateTouchActionRects() depends on the layer's offset, but when the + // layer's offset changes, we do not call SetNeedsUpdate() (this is an + // optimization because the update would only cause an extra commit) This is + // only OK if the ForeignLayer doesn't have hit test data. + DCHECK(!FirstPaintChunk().hit_test_data); + const auto& foreign_layer_display_item = + To<ForeignLayerDisplayItem>(FirstDisplayItem()); + + gfx::Vector2dF layer_offset( + foreign_layer_display_item.VisualRect().OffsetFromOrigin()); + cc_layer_ = foreign_layer_display_item.GetLayer(); + cc_layer_->SetOffsetToTransformParent(layer_offset + + offset_of_decomposited_transforms_); +} + +void PendingLayer::UpdateScrollHitTestLayer(PendingLayer* old_pending_layer) { + DCHECK_EQ(compositing_type_, kScrollHitTestLayer); + + // We shouldn't decomposite scroll transform nodes. + DCHECK_EQ(gfx::Vector2dF(), offset_of_decomposited_transforms_); + + const auto& scroll_node = + *ScrollTranslationForScrollHitTestLayer().ScrollNode(); + + DCHECK(!cc_layer_); + if (old_pending_layer) + cc_layer_ = std::move(old_pending_layer->cc_layer_); + + if (cc_layer_) { + DCHECK_EQ(cc_layer_->element_id(), scroll_node.GetCompositorElementId()); + } else { + cc_layer_ = cc::Layer::Create(); + cc_layer_->SetElementId(scroll_node.GetCompositorElementId()); + cc_layer_->SetHitTestable(true); + } + + cc_layer_->SetOffsetToTransformParent( + gfx::Vector2dF(scroll_node.ContainerRect().OffsetFromOrigin())); + // TODO(pdr): The scroll layer's bounds are currently set to the clipped + // container bounds but this does not include the border. We may want to + // change this behavior to make non-composited and composited hit testing + // match (see: crbug.com/753124). To do this, use + // |scroll_hit_test->scroll_container_bounds|. Set the layer's bounds equal + // to the container because the scroll layer does not scroll. + cc_layer_->SetBounds(scroll_node.ContainerRect().size()); + + if (scroll_node.NodeChanged() != PaintPropertyChangeType::kUnchanged) { + cc_layer_->SetNeedsPushProperties(); + cc_layer_->SetNeedsCommit(); + } +} + +void PendingLayer::UpdateScrollbarLayer(PendingLayer* old_pending_layer) { + DCHECK_EQ(compositing_type_, kScrollbarLayer); + + const auto& item = FirstDisplayItem(); + DCHECK(item.IsScrollbar()); + + const auto& scrollbar_item = To<ScrollbarDisplayItem>(item); + scoped_refptr<cc::ScrollbarLayerBase> scrollbar_layer; + if (old_pending_layer) { + scrollbar_layer = static_cast<cc::ScrollbarLayerBase*>( + std::move(old_pending_layer->cc_layer_).get()); + } + + scrollbar_layer = scrollbar_item.CreateOrReuseLayer(scrollbar_layer.get()); + scrollbar_layer->SetOffsetToTransformParent( + scrollbar_layer->offset_to_transform_parent() + + gfx::Vector2dF(offset_of_decomposited_transforms_)); + DCHECK(!cc_layer_); + cc_layer_ = std::move(scrollbar_layer); +} + +void PendingLayer::UpdateContentLayer(PendingLayer* old_pending_layer, + bool tracks_raster_invalidations) { + DCHECK(!ChunkRequiresOwnLayer()); + DCHECK(!content_layer_client_); + if (old_pending_layer) + content_layer_client_ = std::move(old_pending_layer->content_layer_client_); + if (!content_layer_client_) { + content_layer_client_ = std::make_unique<ContentLayerClientImpl>(); + content_layer_client_->GetRasterInvalidator().SetTracksRasterInvalidations( + tracks_raster_invalidations); + } + content_layer_client_->UpdateCcPictureLayer(*this); +} + +void PendingLayer::UpdateCompositedLayer(PendingLayer* old_pending_layer, + cc::LayerSelection& layer_selection, + bool tracks_raster_invalidations) { + switch (compositing_type_) { + case PendingLayer::kForeignLayer: + UpdateForeignLayer(); + break; + case PendingLayer::kScrollHitTestLayer: + UpdateScrollHitTestLayer(old_pending_layer); + break; + case PendingLayer::kScrollbarLayer: + UpdateScrollbarLayer(old_pending_layer); + break; + default: + DCHECK(!ChunkRequiresOwnLayer()); + UpdateContentLayer(old_pending_layer, tracks_raster_invalidations); + break; + } + + UpdateLayerProperties(); + UpdateLayerSelection(layer_selection); +} + +void PendingLayer::UpdateCompositedLayerForRepaint( + scoped_refptr<const PaintArtifact> repainted_artifact, + cc::LayerSelection& layer_selection) { + // Essentially replace the paint chunks of the pending layer with the + // repainted chunks in |repainted_artifact|. The pending layer's paint + // chunks (a |PaintChunkSubset|) actually store indices to |PaintChunk|s + // in a |PaintArtifact|. In repaint updates, chunks are not added, + // removed, or re-ordered, so we can simply swap in a repainted + // |PaintArtifact| instead of copying |PaintChunk|s individually. + const PaintArtifact& old_artifact = Chunks().GetPaintArtifact(); + DCHECK_EQ(old_artifact.PaintChunks().size(), + repainted_artifact->PaintChunks().size()); + SetPaintArtifact(std::move(repainted_artifact)); + + bool chunks_unchanged = true; + for (const auto& chunk : Chunks()) { + if (!chunk.is_moved_from_cached_subsequence) { + chunks_unchanged = false; + break; + } + } + + if (!ChunkRequiresOwnLayer()) { + DCHECK(content_layer_client_); + // Checking |pending_layer_chunks_unchanged| is an optimization to avoid + // the expensive call to |UpdateCcPictureLayer| when no repainting occurs + // for this PendingLayer. + if (chunks_unchanged) { + // See RasterInvalidator::SetOldPaintArtifact() for the reason for this. + content_layer_client_->GetRasterInvalidator().SetOldPaintArtifact( + &Chunks().GetPaintArtifact()); + } else { + content_layer_client_->UpdateCcPictureLayer(*this); + } + } + + if (!chunks_unchanged) + UpdateLayerProperties(); + UpdateLayerSelection(layer_selection); +} + +void PendingLayer::UpdateLayerProperties() { + // Properties of foreign layers are managed by their owners. + if (compositing_type_ == PendingLayer::kForeignLayer) + return; + PaintChunksToCcLayer::UpdateLayerProperties(CcLayer(), GetPropertyTreeState(), + Chunks()); +} + +void PendingLayer::UpdateLayerSelection(cc::LayerSelection& layer_selection) { + // Foreign layers cannot contain selection. + if (compositing_type_ == PendingLayer::kForeignLayer) + return; + PaintChunksToCcLayer::UpdateLayerSelection(CcLayer(), GetPropertyTreeState(), + Chunks(), layer_selection); +} + bool PendingLayer::IsSolidColor() const { if (Chunks().size() != 1) return false;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h index 969dcea..df4af518 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h +++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h
@@ -5,6 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PENDING_LAYER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PENDING_LAYER_H_ +#include "cc/input/layer_selection_bound.h" +#include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" #include "ui/gfx/geometry/rect_f.h" @@ -88,6 +90,8 @@ const PaintChunk& FirstPaintChunk() const; const DisplayItem& FirstDisplayItem() const; + bool Matches(const PendingLayer& old_pending_layer) const; + const TransformPaintPropertyNode& ScrollTranslationForScrollHitTestLayer() const; @@ -96,8 +100,18 @@ void ForceDrawsContent() { draws_content_ = true; } bool DrawsContent() const { return draws_content_; } - bool RequiresOwnLayer() const { - return compositing_type_ != kOverlap && compositing_type_ != kOther; + bool ChunkRequiresOwnLayer() const { + bool result = compositing_type_ != kOverlap && compositing_type_ != kOther; +#if DCHECK_IS_ON() + if (result) { + DCHECK(!content_layer_client_); + DCHECK_EQ(chunks_.size(), 1u); + } else { + DCHECK(!cc_layer_); + DCHECK_GE(chunks_.size(), 1u); + } +#endif + return result; } bool PropertyTreeStateChanged() const; @@ -106,6 +120,32 @@ static void DecompositeTransforms(Vector<PendingLayer>& pending_layers); + // This is valid only when SetCclayer() or SetContentLayerClient() has been + // called. + cc::Layer& CcLayer() const { + if (content_layer_client_) + return content_layer_client_->Layer(); + DCHECK(cc_layer_); + return *cc_layer_; + } + + ContentLayerClientImpl* GetContentLayerClient() const { + return content_layer_client_.get(); + } + + // For this PendingLayer, creates a composited layer or uses the existing + // one in |old_pending_layer|, and updates the layer according to the current + // contents and properties of this PendingLayer. + void UpdateCompositedLayer(PendingLayer* old_pending_layer, + cc::LayerSelection&, + bool tracks_raster_invalidations); + + // A lighter version of UpdateCompositedLayer(). Called when the existing + // composited layer has only repainted since the last update. + void UpdateCompositedLayerForRepaint( + scoped_refptr<const PaintArtifact> repainted_artifact, + cc::LayerSelection&); + private: PendingLayer(const PaintChunkSubset&, const PaintChunk& first_chunk, @@ -121,6 +161,17 @@ // True if this contains only a single solid color DrawingDisplayItem. bool IsSolidColor() const; + // The following methods are called by UpdateCompositedLayer(), each for a + // particular type of composited layer. + void UpdateForeignLayer(); + void UpdateScrollHitTestLayer(PendingLayer* old_pending_layer); + void UpdateScrollbarLayer(PendingLayer* old_pending_layer); + void UpdateContentLayer(PendingLayer* old_pending_layer, + bool tracks_raster_invalidations); + + void UpdateLayerProperties(); + void UpdateLayerSelection(cc::LayerSelection&); + // The rects are in the space of property_tree_state. gfx::RectF bounds_; gfx::RectF rect_known_to_be_opaque_; @@ -133,6 +184,11 @@ PaintPropertyChangeType change_of_decomposited_transforms_ = PaintPropertyChangeType::kUnchanged; CompositingType compositing_type_; + + // This is set to non-null after layerization if ChunkRequiresOwnLayer(). + scoped_refptr<cc::Layer> cc_layer_; + // This is set to non-null after layerization if !ChunkRequiresOwnLayer(). + std::unique_ptr<ContentLayerClientImpl> content_layer_client_; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h b/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h index 4de8a024..70b7bae 100644 --- a/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h +++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
@@ -88,8 +88,8 @@ return old.is_cacheable && Matches(old.id); } - bool Matches(const Id& other_id) const { - if (!is_cacheable || id != other_id) + bool CanMatchOldChunk() const { + if (!is_cacheable) return false; // A chunk whose client is just created should not match any cached chunk, // even if it's id equals the old chunk's id (which may happen if this @@ -98,6 +98,10 @@ return !client_is_just_created; } + bool Matches(const Id& other_id) const { + return CanMatchOldChunk() && id == other_id; + } + bool EqualsForUnderInvalidationChecking(const PaintChunk& other) const; HitTestData& EnsureHitTestData() {
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc index f1eee9d..a5d6501 100644 --- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc +++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc
@@ -43,7 +43,7 @@ wtf_size_t RasterInvalidator::MatchNewChunkToOldChunk( const PaintChunk& new_chunk, wtf_size_t old_index) const { - if (!new_chunk.is_cacheable) + if (!new_chunk.CanMatchOldChunk()) return kNotFound; for (wtf_size_t i = old_index; i < old_paint_chunks_info_.size(); i++) {
diff --git a/third_party/blink/renderer/platform/network/http_names.json5 b/third_party/blink/renderer/platform/network/http_names.json5 index 9d7ec51..63880453 100644 --- a/third_party/blink/renderer/platform/network/http_names.json5 +++ b/third_party/blink/renderer/platform/network/http_names.json5
@@ -24,6 +24,7 @@ "Access-Control-Request-Method", "Allow-CSP-From", "Attribution-Reporting-Register-Aggregatable-Source", + "Attribution-Reporting-Register-Event-Trigger", "Attribution-Reporting-Register-Source", "Cache-Control", "Content-DPR",
diff --git a/third_party/blink/web_tests/document-transition/multiple-shared-elements-animate-crash.html b/third_party/blink/web_tests/document-transition/multiple-shared-elements-animate-crash.html index afd028e..b9ff763 100644 --- a/third_party/blink/web_tests/document-transition/multiple-shared-elements-animate-crash.html +++ b/third_party/blink/web_tests/document-transition/multiple-shared-elements-animate-crash.html
@@ -46,16 +46,16 @@ async_test((t) => { t.step(() => { requestAnimationFrame(() => requestAnimationFrame(async () => { - document.createDocumentTransition(async (transition) => { - transition.setElement(e1, "e1"); - transition.setElement(e2, "e2"); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2] + }); - await transition.captureAndHold(); + container.classList.remove("left"); + container.classList.add("right"); - container.classList.remove("left"); - container.classList.add("right"); - - await transition.start(); + await document.documentTransition.start({ + sharedElements: [e1, e2] }); requestAnimationFrame(() => {
diff --git a/third_party/blink/web_tests/document-transition/paint-order.html b/third_party/blink/web_tests/document-transition/paint-order.html index 1daff159..5590d8c 100644 --- a/third_party/blink/web_tests/document-transition/paint-order.html +++ b/third_party/blink/web_tests/document-transition/paint-order.html
@@ -9,12 +9,13 @@ background-color: blue; contain: paint; } -html::page-transition-outgoing-image(shared) { +html::page-transition-container(shared-0) { isolation: isolate; } +html::page-transition-outgoing-image(shared-0) { opacity: 1; mix-blend-mode: normal; animation: unset; } -html::page-transition-incoming-image(shared) { +html::page-transition-incoming-image(shared-0) { opacity: 1; mix-blend-mode: multiply; animation: unset; @@ -32,19 +33,20 @@ async function doTransition() { let elem = document.getElementsByTagName("div")[0]; - document.createDocumentTransition(async (t) => { - t.setElement(elem, "shared"); - await t.captureAndHold(); - - elem.style.backgroundColor = "red"; - - await t.start(); - - if (window.testRunner) { - requestAnimationFrame(() => requestAnimationFrame(() => testRunner.notifyDone())); - } + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [elem] }); + + elem.style.backgroundColor = "red"; + await document.documentTransition.start({ + sharedElements: [elem] + }); + + if (window.testRunner) { + requestAnimationFrame(() => requestAnimationFrame(() => testRunner.notifyDone())); + } } -onload = requestAnimationFrame(() => requestAnimationFrame(doTransition)); +onload = doTransition; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-clip-path.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-clip-path.html index 893b1856..9a578cb 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-clip-path.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-clip-path.html
@@ -34,14 +34,15 @@ <div id=e1 class=box></div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - await t.captureAndHold(); - e1.classList.add("dst"); - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1] }); + e1.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1] + }); + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-different-size.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-different-size.html index e854acb..6cd4cb8 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-different-size.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-different-size.html
@@ -45,17 +45,15 @@ <div id=e3 class=box>three</div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - t.setElement(e2, "e2"); - t.setElement(e3, "e3"); - await t.captureAndHold(); - - e1.classList.add("dst"); - e2.classList.add("dst"); - e3.classList.add("dst"); - - t.start(); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2, e3] + }); + e1.classList.add("dst"); + e2.classList.add("dst"); + e3.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1, e2, e3] }); requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); }
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-opacity.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-opacity.html index f1a79e9..72f348a0 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-opacity.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-opacity.html
@@ -33,20 +33,17 @@ <div id=e3 class=box></div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - t.setElement(e2, "e2"); - t.setElement(e3, "e3"); - await t.captureAndHold(); - - e1.classList.add("dst"); - e2.classList.add("dst"); - e3.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2, e3] }); + e1.classList.add("dst"); + e2.classList.add("dst"); + e3.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1, e2, e3] + }); + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root-ref.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root-ref.html deleted file mode 100644 index d649fc5f..0000000 --- a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root-ref.html +++ /dev/null
@@ -1,20 +0,0 @@ -<!DOCTYPE html> -<title>Shared transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/shared-element-transitions"> -<link rel="author" href="mailto:vmpstr@chromium.org"> -<style> -.box { - background: lightgreen; - width: 100px; - height: 100px; - contain: paint; - position: absolute; - will-change: transform; -} -#e1 { - top: 10px; - left: 30px; -} -</style> -<div id=e1 class=box></div> -
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root.html b/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root.html deleted file mode 100644 index 8f1ae23..0000000 --- a/third_party/blink/web_tests/wpt_internal/document-transition/new-content-captures-root.html +++ /dev/null
@@ -1,54 +0,0 @@ -<!DOCTYPE html> -<html class=reftest-wait> -<title>Shared transitions: capture root elements</title> -<link rel="help" href="https://github.com/WICG/shared-element-transitions"> -<link rel="author" href="mailto:vmpstr@chromium.org"> -<link rel="match" href="new-content-captures-root-ref.html"> -<script src="/common/reftest-wait.js"></script> -<style> -.box { - background: lightblue; - width: 100px; - height: 100px; - contain: paint; - position: absolute; - will-change: transform; -} -#e1 { - top: 10px; - left: 30px; -} -#shared { - contain: paint; - width: 100px; - height: 100px; - background: red; -} - -div.dst { background: lightgreen; } -/* We're verifying what we capture, so just display the old contents for 5 minutes. */ -html::page-transition { background: pink; } -html::page-transition-container(shared) { animation-duration: 300s; } -html::page-transition-image-wrapper(shared) { visibility: hidden } -html::page-transition-outgoing-image(root) { animation-duration: 0s; opacity: 0 } -html::page-transition-incoming-image(root) { animation-duration: 0s; opacity: 1 } -</style> -<div id=e1 class=box></div> -<div id=shared></div> -<script> -async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(shared, "shared"); - - await t.captureAndHold(); - - e1.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); - }); -} -onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); -</script> -
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-clip-path.html b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-clip-path.html index 9958196..30e7af5 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-clip-path.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-clip-path.html
@@ -33,16 +33,15 @@ <div id=e1 class=box></div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - await t.captureAndHold(); - - e1.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1] }); + e1.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1] + }); + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-different-size.html b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-different-size.html index 2e5c66b4d..2c8677a 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-different-size.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-different-size.html
@@ -45,20 +45,17 @@ <div id=e3 class=box>three</div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - t.setElement(e2, "e2"); - t.setElement(e3, "e3"); - await t.captureAndHold(); - - e1.classList.add("dst"); - e2.classList.add("dst"); - e3.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2, e3] }); + e1.classList.add("dst"); + e2.classList.add("dst"); + e3.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1, e2, e3] + }); + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-opacity.html b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-opacity.html index db57c1b..5f02095 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-opacity.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-opacity.html
@@ -34,20 +34,17 @@ <div id=e3 class=box>three</div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - t.setElement(e2, "e2"); - t.setElement(e3, "e3"); - await t.captureAndHold(); - - e1.classList.add("dst"); - e2.classList.add("dst"); - e3.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2, e3] }); + e1.classList.add("dst"); + e2.classList.add("dst"); + e3.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [e1, e2, e3] + }); + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-root.html b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-root.html index ce187204..dac3abab 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-root.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/old-content-captures-root.html
@@ -24,12 +24,11 @@ height: 100px; background: red; } - div.dst { background: lightgreen; } /* We're verifying what we capture, so just display the old contents for 5 minutes. */ html::page-transition { background: pink; } -html::page-transition-container(shared) { animation-duration: 300s; } -html::page-transition-image-wrapper(shared) { visibility: hidden } +html::page-transition-container(shared-0) { animation-duration: 300s; } +html::page-transition-image-wrapper(shared-0) { visibility: hidden } html::page-transition-outgoing-image(root) { animation: unset; opacity: 1 } html::page-transition-incoming-image(root) { animation: unset; opacity: 0 } </style> @@ -37,17 +36,17 @@ <div id=shared></div> <script> async function runTest() { - document.createDocumentTransition(async (t) => { - t.setElement(shared, "shared"); - - await t.captureAndHold(); - - e1.classList.add("dst"); - - t.start(); - - requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [shared] }); + e1.classList.add("dst"); + document.documentTransition.start({ + sharedElements: [shared] + }); + + requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)); } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); </script> +
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html index 38e8c7c..00b8128c 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html
@@ -57,16 +57,16 @@ background-color: grey; } - html::page-transition-container(target) { + html::page-transition-container(shared-0) { left: 50px; } - html::page-transition-outgoing-image(target) { + html::page-transition-outgoing-image(shared-0) { opacity: 0.5; animation-name: none; } - html::page-transition-incoming-image(target) { + html::page-transition-incoming-image(shared-0) { opacity: 0.5; } ` @@ -75,20 +75,20 @@ pseudoStyle.appendChild(document.createTextNode(transitionStyle)); async function runAnimation() { - document.createDocumentTransition(async (t) => { - t.setElement(target, "target"); - await t.captureAndHold(); - - target.classList.remove(classes[i]); - i = (i + 1) % classes.length; - target.classList.add(classes[i]); - - document.head.appendChild(pseudoStyle); - - await t.start(); - - document.head.removeChild(pseudoStyle); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [target] }); + + target.classList.remove(classes[i]); + i = (i + 1) % classes.length; + target.classList.add(classes[i]); + + document.head.appendChild(pseudoStyle); + await document.documentTransition.start({ + sharedElements: [target] + }); + document.head.removeChild(pseudoStyle); } function init() {
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half-with-config.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half-with-config.manual.html new file mode 100644 index 0000000..0514f2b --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half-with-config.manual.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html> +<title>Shared transitions of different elements and shapes</title> +<link rel="help" href="https://github.com/vmpstr/shared-element-transitions"> +<link rel="author" href="mailto:vmpstr@chromium.org"> + +<style> +body { + background: lightpink; + overflow: hidden; +} + +input { + position: absolute; + left: 8px; + top: 8px; + z-index: 10; +} + +.top { + top: 0px; +} +.bottom { + bottom: 0px; +} + +div { + position: absolute; + left: 0px; + right: 0px; + height: 40vh; + background: green; + contain: paint; +} +</style> + +<input id=toggle type=button value="Toggle!"></input> +<div id=target class=top> +The green div should alternate being at the bottom and at the top. +Other than green and pink background no other colors should appear. +</div> + +<script> +let classes = ["top", "bottom"] +let backgroundColors = ["lightpink", "lightblue"] +let i = 0; +async function runAnimation() { + await document.documentTransition.prepare({ + rootTransition: "explode", + rootConfig: {duration:"500", delay: "500"}, + sharedElements: [target], + sharedElementsConfig: [{duration:"1000", delay:"1000"}] + }); + + document.body.style.background = backgroundColors[i]; + target.classList.remove(classes[i]); + i = (i + 1) % classes.length; + target.classList.add(classes[i]); + + await document.documentTransition.start({ + sharedElements: [target] + }); +} + +function init() { + toggle.addEventListener("click", runAnimation); +} +onload = init; +</script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half.manual.html index 90ee5b8..4c041b6c 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half.manual.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-half.manual.html
@@ -44,15 +44,17 @@ let classes = ["top", "bottom"] let i = 0; async function runAnimation() { - document.createDocumentTransition(async (t) => { - t.setElement(target, "target"); - await t.captureAndHold(); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [target] + }); - target.classList.remove(classes[i]); - i = (i + 1) % classes.length; - target.classList.add(classes[i]); + target.classList.remove(classes[i]); + i = (i + 1) % classes.length; + target.classList.add(classes[i]); - await t.start(); + await document.documentTransition.start({ + sharedElements: [target] }); }
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-repeated-elements.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-repeated-elements.manual.html new file mode 100644 index 0000000..d6251e2a --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-repeated-elements.manual.html
@@ -0,0 +1,79 @@ +<!DOCTYPE html> +<html> +<title>Shared transitions: repeated elements (one -> two elements and back)</title> +<link rel="help" href="https://github.com/vmpstr/shared-element-transitions"> +<link rel="author" href="mailto:vmpstr@chromium.org"> + +<style> +body { + background: lightpink; +} + +#container { + width: max-content; + position: relative; +} + +.hidden { display: none; } + +.shape { + width: 100px; + height: 100px; + border-radius: 50%; + border: 1px solid black; + position: absolute; + contain: paint; +} + +#yellow { + background: yellow; + left: 300px; + top: 50px; +} +#green { + background: green; + left: 50px; + top: 150px; +} +#blue { + background: blue; + left: 300px; + top: 250px; +} +</style> + +<input id=toggle type=button value="Toggle!"></input> +<span>One shape becomes two and vice versa</span> +<div id=green class=shape></div> +<div id=blue class="shape hidden"></div> +<div id=yellow class="shape hidden"></div> + +<script> +function visibleSharedElements() { + if (green.classList.contains("hidden")) { + return [blue, yellow]; + } else { + return [green, green]; + } +} + +async function runAnimation() { + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: visibleSharedElements() + }); + + green.classList.toggle("hidden"); + blue.classList.toggle("hidden"); + yellow.classList.toggle("hidden"); + + await document.documentTransition.start({ + sharedElements: visibleSharedElements() + }); +} + +function init() { + toggle.addEventListener("click", runAnimation); +} +onload = init; +</script>
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-shapes.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-shapes.manual.html index 8a06dbad..7de5adbf 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-shapes.manual.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-shapes.manual.html
@@ -60,19 +60,17 @@ let classes = ["left", "right"] let i = 0; async function runAnimation() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - t.setElement(e2, "e2"); - t.setElement(e3, "e3"); - t.setElement(e4, "e4"); - t.setElement(e5, "e5"); - await t.captureAndHold(); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1, e2, e3, e4, e5] + }); - container.classList.remove(classes[i]); - i = (i + 1) % classes.length; - container.classList.add(classes[i]); + container.classList.remove(classes[i]); + i = (i + 1) % classes.length; + container.classList.add(classes[i]); - await t.start(); + await document.documentTransition.start({ + sharedElements: [e1, e2, e3, e4, e5] }); }
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/transition-waits-for-animations.html b/third_party/blink/web_tests/wpt_internal/document-transition/transition-waits-for-animations.html index 204e891..a7c75ab3 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/transition-waits-for-animations.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/transition-waits-for-animations.html
@@ -34,14 +34,16 @@ <script> async function startTransition() { - document.createDocumentTransition(async (t) => { - t.setElement(e1, "e1"); - await t.captureAndHold(); + await document.documentTransition.prepare({ + rootTransition: "none", + sharedElements: [e1] + }); - e1.classList.remove("left"); - e1.classList.add("right"); + e1.classList.remove("left"); + e1.classList.add("right"); - t.start(); + document.documentTransition.start({ + sharedElements: [e1] }); }
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/uncontained-transition-crash.html b/third_party/blink/web_tests/wpt_internal/document-transition/uncontained-transition-crash.html index 1f6c8836..038c5c9 100644 --- a/third_party/blink/web_tests/wpt_internal/document-transition/uncontained-transition-crash.html +++ b/third_party/blink/web_tests/wpt_internal/document-transition/uncontained-transition-crash.html
@@ -31,22 +31,13 @@ async function runTest() { await waitForAtLeastOneFrame(); // Prepare with a shared element - document.createDocumentTransition(async (t) => { - t.setElement(first, "shared"); - let promise = t.captureAndHold(); - - // Force a hit test, which will determine compositing reasons. - document.elementFromPoint(0, 0); - - // Now wait for the capture to happen. This will note that we don't have - // paint containment and should de-composite the element. - await promise; - - t.setElement(first, null); - t.setElement(second, "shared"); - - t.start(); - }); + let promise = document.documentTransition.prepare({ sharedElements: [first] }); + // Force a hit test, which will determine compositing reasons. + document.elementFromPoint(0, 0); + // Now wait for the prepare to happen. This will note that we don't have + // paint containment and should de-composite the element. + await promise; + document.documentTransition.start({ sharedElements: [second] }); takeScreenshot(); }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index c395644fa..0be53f3 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -367,7 +367,7 @@ 'linux-lacros-code-coverage': 'lacros_on_linux_clang_code_coverage', 'linux-lacros-version-skew-fyi': 'lacros_on_linux_release_not_build_ash_bot', 'linux-perfetto-rel': 'perfetto_release_bot', - 'linux-upload-perfetto': 'release_bot', + 'linux-upload-perfetto': 'release_bot_perfetto_zlib', 'linux-wpt-fyi-rel': 'release_bot_minimal_symbols_dcheck_always_on', 'linux-wpt-identity-fyi-rel': 'release_bot_minimal_symbols', 'linux-wpt-input-fyi-rel': 'release_bot_minimal_symbols', @@ -377,13 +377,13 @@ 'mac-hermetic-upgrade-rel': 'release_bot', 'mac-paeverywhere-x64-fyi-dbg': 'debug_bot_paeverywhere_x64', 'mac-paeverywhere-x64-fyi-rel': 'release_trybot_paeverywhere_x64', - 'mac-upload-perfetto': 'release_bot', + 'mac-upload-perfetto': 'release_bot_perfetto_zlib', 'win-annotator-rel': 'release_bot', 'win-celab-builder-rel': 'release_bot_minimal_symbols', 'win-backuprefptr-x86-fyi-rel': 'release_trybot_backuprefptr_x86', 'win-backuprefptr-x64-fyi-rel': 'release_trybot_backuprefptr_x64', 'win-pixel-builder-rel': 'release_bot', - 'win-upload-perfetto': 'release_bot', + 'win-upload-perfetto': 'release_bot_perfetto_zlib', 'win10-code-coverage': 'clang_code_coverage', 'win32-archive-rel-goma-rbe-canary': 'release_bot_x86_minimal_symbols_enable_archive_compression', 'win32-archive-rel-goma-rbe-latest': 'release_bot_x86_minimal_symbols_enable_archive_compression', @@ -3087,6 +3087,10 @@ 'release', 'official_optimize_goma', 'fuchsia', 'arm64' ], + 'release_bot_perfetto_zlib' : [ + 'release_bot', 'perfetto_zlib', + ], + 'release_trybot': [ 'release_trybot', ], @@ -3977,6 +3981,10 @@ 'gn_args': 'use_perfetto_client_library=true', }, + 'perfetto_zlib': { + 'gn_args': 'enable_perfetto_zlib=true', + }, + 'pgo_phase_0': { 'mixins': ['strip_absolute_paths_from_debug_symbols'], 'gn_args': 'chrome_pgo_phase=0'
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index da0d7811..c0a74d46 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -1072,6 +1072,7 @@ "linux-upload-perfetto": { "gn_args": { "dcheck_always_on": false, + "enable_perfetto_zlib": true, "is_component_build": false, "is_debug": false, "use_goma": true @@ -1158,6 +1159,7 @@ "mac-upload-perfetto": { "gn_args": { "dcheck_always_on": false, + "enable_perfetto_zlib": true, "is_component_build": false, "is_debug": false, "use_goma": true @@ -1219,6 +1221,7 @@ "win-upload-perfetto": { "gn_args": { "dcheck_always_on": false, + "enable_perfetto_zlib": true, "is_component_build": false, "is_debug": false, "use_goma": true
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index cca404d..4c5a66cb 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -93653,11 +93653,6 @@ <int value="4" label="STORAGE"/> </enum> -<enum name="WebApkUninstallSourceChromeOS"> - <int value="0" label="Uninstalled from Ash"/> - <int value="1" label="Uninstalled from ARC"/> -</enum> - <enum name="WebApkUpdateRequestQueued"> <int value="0" label="Queued for the first time"/> <int value="1" label="Queued for the second time"/>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index 7345d43..0e94fe03 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -83,7 +83,7 @@ </histogram> <histogram name="Arc.AccessibilityWithTalkBack" enum="BooleanEnabled" - expires_after="2022-04-10"> + expires_after="2023-04-10"> <owner>hirokisato@chromium.org</owner> <owner>arc-framework@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 67e7de0..347f029 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1303,7 +1303,7 @@ </histogram> <histogram name="Ash.Desks.NumberOfWindowsOnDesk_2" units="units" - expires_after="2022-04-10"> + expires_after="2022-10-03"> <owner>afakhry@chromium.org</owner> <owner>tclaiborne@chromium.org</owner> <summary> @@ -1313,7 +1313,7 @@ </histogram> <histogram name="Ash.Desks.NumberOfWindowsOnDesk_3" units="units" - expires_after="2022-04-10"> + expires_after="2022-10-03"> <owner>afakhry@chromium.org</owner> <owner>tclaiborne@chromium.org</owner> <summary> @@ -1323,7 +1323,7 @@ </histogram> <histogram name="Ash.Desks.NumberOfWindowsOnDesk_4" units="units" - expires_after="2022-04-10"> + expires_after="2022-10-03"> <owner>afakhry@chromium.org</owner> <owner>tclaiborne@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml index 30012693..9ebb9684 100644 --- a/tools/metrics/histograms/metadata/chromeos/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -848,12 +848,12 @@ </histogram> <histogram base="true" name="ChromeOS.HardwareVerifier.Report" - enum="HardwareVerifierQualificationStatus" expires_after="2022-04-10"> + enum="HardwareVerifierQualificationStatus" expires_after="2023-03-01"> <!-- Name completed by histogram_suffixes name="HardwareVerifierSupportCategory" --> <owner>itspeter@chromium.org</owner> <owner>stimim@chromium.org</owner> - <owner>chromeos-hw-checker@google.com</owner> + <owner>chromeos-runtime-probe@google.com</owner> <summary> Qualification status of each component types. This entry is generated by hardware_verifier.conf at boot time. @@ -861,28 +861,28 @@ </histogram> <histogram name="ChromeOS.HardwareVerifier.Report.IsCompliant" enum="Boolean" - expires_after="2022-07-03"> + expires_after="2023-03-01"> <owner>itspeter@chromium.org</owner> <owner>stimim@chromium.org</owner> - <owner>chromeos-hw-checker@google.com</owner> + <owner>chromeos-runtime-probe@google.com</owner> <summary>Aggregated result of hardware verifier check.</summary> </histogram> <histogram name="ChromeOS.HardwareVerifier.TimeToFinish" units="ms" - expires_after="2022-04-17"> + expires_after="2023-03-01"> <owner>itspeter@chromium.org</owner> <owner>stimim@chromium.org</owner> - <owner>chromeos-hw-checker@google.com</owner> + <owner>chromeos-runtime-probe@google.com</owner> <summary> The amount of time it takes to finish one hardware verification run. </summary> </histogram> <histogram name="ChromeOS.HardwareVerifier.TimeToProbe" units="ms" - expires_after="2022-04-10"> + expires_after="2023-03-01"> <owner>itspeter@chromium.org</owner> <owner>stimim@chromium.org</owner> - <owner>chromeos-hw-checker@google.com</owner> + <owner>chromeos-runtime-probe@google.com</owner> <summary>The amount of time it takes to probe hardware components.</summary> </histogram> @@ -1772,44 +1772,8 @@ </summary> </histogram> -<histogram name="ChromeOS.WebAPK.MinterResponseOrErrorCode" - enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-04-14"> - <owner>tsergeant@chromium.org</owner> - <owner>chromeos-apps-foundation-team@google.com</owner> - <summary> - HTTP response code or net error code for requests made to the WebAPK minter - service. Logged after a request to generate a WebAPK finishes, which happens - when a PWA which supports Web Share Target is installed or updated. - </summary> -</histogram> - -<histogram name="ChromeOS.WebApk.UninstallSource" - enum="WebApkUninstallSourceChromeOS" expires_after="2022-04-01"> - <owner>tsergeant@chromium.org</owner> - <owner>chromeos-apps-foundation-team@google.com</owner> - <summary> - Records the source of a WebAPK uninstall event. A WebAPK is installed when a - PWA which supports Web Share Target is installed in the browser, and is - uninstalled when the user uninstalls the PWA or uninstalls the WebAPK - directly through ARC Android settings. - </summary> -</histogram> - -<histogram name="ChromeOS.WebAPK.UnlinkedWebAPKCount" units="count" - expires_after="2022-03-14"> - <owner>tsergeant@chromium.org</owner> - <owner>chromeos-apps-foundation-team@google.com</owner> - <summary> - Records the number of installed WebAPKs that were not linked to a Web App - which were found and removed. The presence of these apps indicates that - something went wrong in the WebAPK installation process. Unlinked apps are - detected every startup, this histogram is only recorded when at least 1 app - is found. - </summary> -</histogram> - <histogram name="ChromeOS.WebAPK.{InstallType}.ArcInstallResult" - enum="WebApkArcInstallResultChromeOS" expires_after="2022-04-14"> + enum="WebApkArcInstallResultChromeOS" expires_after="2022-10-14"> <owner>tsergeant@chromium.org</owner> <owner>chromeos-apps-foundation-team@google.com</owner> <summary> @@ -1826,7 +1790,7 @@ </histogram> <histogram name="ChromeOS.WebAPK.{InstallType}.Result" - enum="WebApkInstallResultChromeOS" expires_after="2022-04-14"> + enum="WebApkInstallResultChromeOS" expires_after="2022-10-14"> <owner>tsergeant@chromium.org</owner> <owner>chromeos-apps-foundation-team@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml index 9285f580..4fbb522 100644 --- a/tools/metrics/histograms/metadata/file/histograms.xml +++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -47,7 +47,7 @@ </histogram> <histogram name="FileBrowser.ChangeDirectory.RootType" - enum="FileManagerRootType" expires_after="2022-04-10"> + enum="FileManagerRootType" expires_after="2023-02-28"> <owner>simmonsjosh@google.com</owner> <owner>src/ui/file_manager/OWNERS</owner> <summary> @@ -455,7 +455,7 @@ </histogram> <histogram name="FileBrowser.Notification.Show" - enum="FileManagerNotificationType" expires_after="2022-04-10"> + enum="FileManagerNotificationType" expires_after="2023-02-28"> <owner>simmonsjosh@google.com</owner> <owner>src/ui/file_manager/OWNERS</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml index 51deb50..611bdbf 100644 --- a/tools/metrics/histograms/metadata/network/histograms.xml +++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -707,7 +707,7 @@ </histogram> <histogram name="Network.DnsProxy.DnsOverHttpsMode" - enum="DnsProxy.DnsOverHttpsMode" expires_after="2022-04-03"> + enum="DnsProxy.DnsOverHttpsMode" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -717,7 +717,7 @@ </histogram> <histogram name="Network.DnsProxy.DnsOverHttpsQuery.HttpErrors" - enum="DnsProxy.HttpError" expires_after="2022-04-03"> + enum="DnsProxy.HttpError" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -728,7 +728,7 @@ </histogram> <histogram name="Network.DnsProxy.NameserverTypes" - enum="DnsProxy.NameserverType" expires_after="2022-04-03"> + enum="DnsProxy.NameserverType" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -739,7 +739,7 @@ </histogram> <histogram name="Network.DnsProxy.Query.Failed{Stage}Duration" units="ms" - expires_after="2022-04-03"> + expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -775,7 +775,7 @@ </histogram> <histogram name="Network.DnsProxy.Query.{Stage}Duration" units="ms" - expires_after="2022-04-03"> + expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -811,7 +811,7 @@ </histogram> <histogram name="Network.DnsProxy.{Family}Nameservers" units="units" - expires_after="2022-04-03"> + expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -826,7 +826,7 @@ </histogram> <histogram name="Network.DnsProxy.{ProcessType}.Event" - enum="DnsProxy.ProcessEvent" expires_after="2022-04-03"> + enum="DnsProxy.ProcessEvent" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -844,7 +844,7 @@ </histogram> <histogram name="Network.DnsProxy.{Type}Query.Errors" - enum="DnsProxy.QueryError" expires_after="2022-04-03"> + enum="DnsProxy.QueryError" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -859,7 +859,7 @@ </histogram> <histogram name="Network.DnsProxy.{Type}Query.FailedResolveDuration" units="ms" - expires_after="2022-04-03"> + expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -880,7 +880,7 @@ </histogram> <histogram name="Network.DnsProxy.{Type}Query.ResolveDuration" units="ms" - expires_after="2022-04-03"> + expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary> @@ -901,7 +901,7 @@ </histogram> <histogram name="Network.DnsProxy.{Type}Query.Results" - enum="DnsProxy.QueryResult" expires_after="2022-04-03"> + enum="DnsProxy.QueryResult" expires_after="2022-12-31"> <owner>garrick@chromium.org</owner> <owner>cros-network-metrics@google.com</owner> <summary>
diff --git a/ui/display/manager/touch_transform_controller.cc b/ui/display/manager/touch_transform_controller.cc index 267ebee..a4ac305 100644 --- a/ui/display/manager/touch_transform_controller.cc +++ b/ui/display/manager/touch_transform_controller.cc
@@ -8,7 +8,6 @@ #include <vector> #include "base/logging.h" -#include "skia/ext/skia_matrix_44.h" #include "ui/display/display_layout.h" #include "ui/display/manager/display_manager.h" #include "ui/display/manager/managed_display_info.h" @@ -19,6 +18,7 @@ #include "ui/display/types/display_snapshot.h" #include "ui/events/devices/device_data_manager.h" #include "ui/events/devices/touch_device_transform.h" +#include "ui/gfx/geometry/transform.h" namespace display { @@ -48,14 +48,16 @@ // Vector of the X-coordinate of display points corresponding to each of the // touch points. - skia::Vector4 display_points_x( - touch_point_pairs[0].first.x(), touch_point_pairs[1].first.x(), - touch_point_pairs[2].first.x(), touch_point_pairs[3].first.x()); + SkV4 display_points_x = {static_cast<float>(touch_point_pairs[0].first.x()), + static_cast<float>(touch_point_pairs[1].first.x()), + static_cast<float>(touch_point_pairs[2].first.x()), + static_cast<float>(touch_point_pairs[3].first.x())}; // Vector of the Y-coordinate of display points corresponding to each of the // touch points. - skia::Vector4 display_points_y( - touch_point_pairs[0].first.y(), touch_point_pairs[1].first.y(), - touch_point_pairs[2].first.y(), touch_point_pairs[3].first.y()); + SkV4 display_points_y = {static_cast<float>(touch_point_pairs[0].first.y()), + static_cast<float>(touch_point_pairs[1].first.y()), + static_cast<float>(touch_point_pairs[2].first.y()), + static_cast<float>(touch_point_pairs[3].first.y())}; // Initialize |touch_point_matrix| // If {(xt_1, yt_1), (xt_2, yt_2), (xt_3, yt_3)....} are a set of touch points @@ -65,48 +67,55 @@ // |xt_2 yt_2 1 0| // |xt_3 yt_3 1 0| // |xt_4 yt_4 1 0| - skia::Matrix44 touch_point_matrix; + gfx::Transform touch_point_matrix; for (int row = 0; row < 4; row++) { - touch_point_matrix.setRC(row, 0, touch_point_pairs[row].second.x()); - touch_point_matrix.setRC(row, 1, touch_point_pairs[row].second.y()); - touch_point_matrix.setRC(row, 2, 1); - touch_point_matrix.setRC(row, 3, 0); + touch_point_matrix.matrix().setRC(row, 0, + touch_point_pairs[row].second.x()); + touch_point_matrix.matrix().setRC(row, 1, + touch_point_pairs[row].second.y()); + touch_point_matrix.matrix().setRC(row, 2, 1); + touch_point_matrix.matrix().setRC(row, 3, 0); } - skia::Matrix44 touch_point_matrix_transpose(touch_point_matrix); - touch_point_matrix_transpose.transpose(); + gfx::Transform touch_point_matrix_transpose = touch_point_matrix; + touch_point_matrix_transpose.Transpose(); - skia::Matrix44 product_matrix = + gfx::Transform product_matrix = touch_point_matrix_transpose * touch_point_matrix; // Set (3, 3) = 1 so that |determinent| of the matrix is != 0 and the inverse // can be calculated. - product_matrix.setRC(3, 3, 1); + product_matrix.matrix().setRC(3, 3, 1); - skia::Matrix44 product_matrix_inverse; + gfx::Transform product_matrix_inverse; // NOTE: If the determinent is zero then the inverse cannot be computed. The // only solution is to restart touch calibration and get new points from user. - if (!product_matrix.invert(&product_matrix_inverse)) { + if (!product_matrix.GetInverse(&product_matrix_inverse)) { NOTREACHED() << "Touch Calibration failed. Determinent is zero."; return false; } - product_matrix_inverse.setRC(3, 3, 0); + product_matrix_inverse.matrix().setRC(3, 3, 0); product_matrix = product_matrix_inverse * touch_point_matrix_transpose; - // Constants [A, B, C, 0] used to calibrate the x-coordinate of touch input. - // x_new = x_old * A + y_old * B + C; - skia::Vector4 x_constants = product_matrix * display_points_x; - // Constants [D, E, F, 0] used to calibrate the y-coordinate of touch input. - // y_new = x_old * D + y_old * E + F; - skia::Vector4 y_constants = product_matrix * display_points_y; + // The result [A, B, C, 0] will be used to calibrate the x-coordinate of + // touch input: + // x_new = x_old * A + y_old * B + C; + product_matrix.TransformVector4(&display_points_x); + // The result [D, E, F, 0] will be used to calibrate the y-coordinate of + // touch input: + // y_new = x_old * D + y_old * E + F; + product_matrix.TransformVector4(&display_points_y); // Create a transform matrix using the touch calibration data. + // clang-format off ctm->ConcatTransform(gfx::Transform( - x_constants.fData[0], x_constants.fData[1], 0, x_constants.fData[2], - y_constants.fData[0], y_constants.fData[1], 0, y_constants.fData[2], 0, 0, - 1, 0, 0, 0, 0, 1)); + display_points_x.x, display_points_x.y, 0, display_points_x.z, + display_points_y.x, display_points_y.y, 0, display_points_y.z, + 0, 0, 1, 0, + 0, 0, 0, 1)); + // clang-format on return true; }
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 0e2ada0..72c5273 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -2108,13 +2108,20 @@ const dirEntry = fileManager.getCurrentDirectoryEntry(); const selection = fileManager.getSelection(); - // Enable this only for a single selected file which is an archive. - // TODO(crbug.com/953256) allow more selections and check for ZIP only. - if (selection.entries.length === 1 && selection.iconType === 'archive') { - event.command.setHidden(false); - event.canExecute = dirEntry && !fileManager.directoryModel.isReadOnly() && - selection && selection.totalCount > 0; + if (!dirEntry || fileManager.directoryModel.isReadOnly() || !selection || + selection.totalCount === 0) { + event.command.setHidden(true); + event.canExecute = false; } else { + // Check the selected entries for a ZIP archive in the selected set. + for (const entry of selection.entries) { + if (FileType.getExtension(entry) === '.zip') { + event.command.setHidden(false); + event.canExecute = true; + return; + } + } + // Didn't find any ZIP files, disable extract-all. event.command.setHidden(true); event.canExecute = false; }
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.m.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.m.js index b60179c..e1773c22 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.m.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.m.js
@@ -289,6 +289,7 @@ const folderEntry = MockDirectoryEntry.create(downloadsFileSystem, '/folder'); const textFileEntry = new MockEntry(downloadsFileSystem, '/file.txt'); const zipFileEntry = new MockEntry(downloadsFileSystem, '/archive.zip'); + const imageFileEntry = new MockEntry(downloadsFileSystem, '/image.jpg'); // Mock `Event`. const event = { @@ -341,13 +342,20 @@ assertFalse(event.canExecute); assertTrue(event.command.hidden); - // Check: canExecute is false and command hidden for multiple selection. - currentSelection.entries = [zipFileEntry, textFileEntry]; + // Check: canExecute is false and command hidden for no ZIP multi-selection. + currentSelection.entries = [imageFileEntry, textFileEntry]; currentSelection.totalCount = 2; command.canExecute(event, fileManager); assertFalse(event.canExecute); assertTrue(event.command.hidden); + // Check: canExecute is true and command visible for ZIP multiple selection. + currentSelection.entries = [zipFileEntry, textFileEntry]; + currentSelection.totalCount = 2; + command.canExecute(event, fileManager); + assertTrue(event.canExecute); + assertFalse(event.command.hidden); + // Check: ZIP canExecute is true and command visible for multiple selection. zipCommand.canExecute(event, fileManager); assertTrue(event.canExecute);
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc index 9e60204d..15fe7f32 100644 --- a/ui/gfx/geometry/transform.cc +++ b/ui/gfx/geometry/transform.cc
@@ -443,6 +443,11 @@ TransformVectorInternal(matrix_, vector); } +void Transform::TransformVector4(SkV4* vector) const { + DCHECK(vector); + matrix_.mapScalars(vector->ptr()); +} + bool Transform::TransformPointReverse(Point* point) const { DCHECK(point);
diff --git a/ui/gfx/geometry/transform.h b/ui/gfx/geometry/transform.h index b7fbfd2..b8a4c8c5 100644 --- a/ui/gfx/geometry/transform.h +++ b/ui/gfx/geometry/transform.h
@@ -237,6 +237,9 @@ // Applies the transformation to the vector. void TransformVector(Vector3dF* vector) const; + // Applies the transformation to the vector. + void TransformVector4(SkV4* vector) const; + // Applies the reverse transformation on the point. Returns true if the // transformation can be inverted. bool TransformPointReverse(Point3F* point) const;
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc index 6de703cd..ec1eab9 100644 --- a/ui/gfx/geometry/transform_unittest.cc +++ b/ui/gfx/geometry/transform_unittest.cc
@@ -2760,6 +2760,20 @@ EXPECT_TRUE(backface_invisible.IsBackFaceVisible()); } +TEST(XFormTest, TransformVector4) { + Transform transform; + transform.matrix().setRC(0, 0, 2.5f); + transform.matrix().setRC(1, 1, 3.5f); + transform.matrix().setRC(2, 2, 4.5f); + transform.matrix().setRC(3, 3, 5.5f); + SkV4 v = {11.5f, 22.5f, 33.5f, 44.5f}; + transform.TransformVector4(&v); + EXPECT_EQ(28.75f, v.x); + EXPECT_EQ(78.75f, v.y); + EXPECT_EQ(150.75f, v.z); + EXPECT_EQ(244.75f, v.w); +} + } // namespace } // namespace gfx
diff --git a/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc b/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc index e30535e..01f472dc 100644 --- a/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc +++ b/ui/ozone/platform/wayland/fuzzer/wayland_buffer_fuzzer.cc
@@ -222,7 +222,7 @@ env.SetTerminateGpuCallback(manager_host); } - manager_host->DestroyBuffer(widget, kBufferId); + manager_host->DestroyBuffer(kBufferId); // Wait until the buffers are destroyed. SyncServer(&server, &env.task_environment);
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc index 4a63438..2ec4e4e 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -34,7 +34,7 @@ GbmPixmapWayland::~GbmPixmapWayland() { if (gbm_bo_ && widget_ != gfx::kNullAcceleratedWidget) - buffer_manager_->DestroyBuffer(widget_, buffer_id_); + buffer_manager_->DestroyBuffer(buffer_id_); } bool GbmPixmapWayland::InitializeBuffer(
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index d3cd402..43110b6 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -77,8 +77,7 @@ void GbmSurfacelessWayland::SolidColorBufferHolder::OnSubmission( BufferId buffer_id, - WaylandBufferManagerGpu* buffer_manager, - gfx::AcceleratedWidget widget) { + WaylandBufferManagerGpu* buffer_manager) { // Solid color buffers do not require on submission as skia doesn't track // them. Instead, they are tracked by GbmSurfacelessWayland. In the future, // when SharedImageFactory allows to create non-backed shared images, this @@ -96,7 +95,7 @@ // ones until the maximum number of available solid color buffer. while (available_solid_color_buffers_.size() > kMaxSolidColorBuffers) { buffer_manager->DestroyBuffer( - widget, available_solid_color_buffers_.begin()->buffer_id); + available_solid_color_buffers_.begin()->buffer_id); available_solid_color_buffers_.erase( available_solid_color_buffers_.begin()); } @@ -104,10 +103,9 @@ } void GbmSurfacelessWayland::SolidColorBufferHolder::EraseBuffers( - WaylandBufferManagerGpu* buffer_manager, - gfx::AcceleratedWidget widget) { + WaylandBufferManagerGpu* buffer_manager) { for (const auto& buffer : available_solid_color_buffers_) - buffer_manager->DestroyBuffer(widget, buffer.buffer_id); + buffer_manager->DestroyBuffer(buffer.buffer_id); available_solid_color_buffers_.clear(); } @@ -289,7 +287,7 @@ surface_scale_factor_ = scale_factor; // Remove all the buffers. - solid_color_buffers_holder_->EraseBuffers(buffer_manager_, widget_); + solid_color_buffers_holder_->EraseBuffers(buffer_manager_); return gl::SurfacelessEGL::Resize(size, scale_factor, color_space, has_alpha); } @@ -408,8 +406,7 @@ submitted_frames_.erase(submitted_frames_.begin()); for (auto& plane : submitted_frame->planes) { // Let the holder mark this buffer as free to reuse. - solid_color_buffers_holder_->OnSubmission(plane.first, buffer_manager_, - widget_); + solid_color_buffers_holder_->OnSubmission(plane.first, buffer_manager_); } submitted_frame->planes.clear(); submitted_frame->overlays.clear();
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index fd3911e..9bf10dc 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -91,10 +91,8 @@ WaylandBufferManagerGpu* buffer_manager); void OnSubmission(BufferId buffer_id, - WaylandBufferManagerGpu* buffer_manager, - gfx::AcceleratedWidget widget); - void EraseBuffers(WaylandBufferManagerGpu* buffer_manager, - gfx::AcceleratedWidget widget); + WaylandBufferManagerGpu* buffer_manager); + void EraseBuffers(WaylandBufferManagerGpu* buffer_manager); private: // Gpu-size holder for the solid color buffers. These are not backed by
diff --git a/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc b/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc index 8df7730..273df35 100644 --- a/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc
@@ -163,12 +163,12 @@ void GLSurfaceEglReadbackWayland::DestroyBuffers() { for (const auto& pixel_buffer : available_buffers_) - buffer_manager_->DestroyBuffer(widget_, pixel_buffer->buffer_id_); + buffer_manager_->DestroyBuffer(pixel_buffer->buffer_id_); for (const auto& pixel_buffer : in_flight_pixel_buffers_) - buffer_manager_->DestroyBuffer(widget_, pixel_buffer->buffer_id_); + buffer_manager_->DestroyBuffer(pixel_buffer->buffer_id_); if (displayed_buffer_) - buffer_manager_->DestroyBuffer(widget_, displayed_buffer_->buffer_id_); + buffer_manager_->DestroyBuffer(displayed_buffer_->buffer_id_); available_buffers_.clear(); in_flight_pixel_buffers_.clear();
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc index 84c7091..34912fa8 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -295,20 +295,19 @@ RunOrQueueTask(std::move(task)); } -void WaylandBufferManagerGpu::DestroyBuffer(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandBufferManagerGpu::DestroyBuffer(uint32_t buffer_id) { DCHECK(gpu_thread_runner_); if (!gpu_thread_runner_->BelongsToCurrentThread()) { // Do the mojo call on the GpuMainThread. gpu_thread_runner_->PostTask( FROM_HERE, base::BindOnce(&WaylandBufferManagerGpu::DestroyBuffer, - base::Unretained(this), widget, buffer_id)); + base::Unretained(this), buffer_id)); return; } base::OnceClosure task = base::BindOnce(&WaylandBufferManagerGpu::DestroyBufferTask, - base::Unretained(this), widget, buffer_id); + base::Unretained(this), buffer_id); RunOrQueueTask(std::move(task)); } @@ -515,12 +514,11 @@ remote_host_->CommitOverlays(widget, std::move(overlays)); } -void WaylandBufferManagerGpu::DestroyBufferTask(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandBufferManagerGpu::DestroyBufferTask(uint32_t buffer_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_); DCHECK(remote_host_); - remote_host_->DestroyBuffer(widget, buffer_id); + remote_host_->DestroyBuffer(buffer_id); } } // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h index 69dd6538..c9115f5 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h +++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -133,7 +133,7 @@ std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays); // Asks Wayland to destroy a wl_buffer. - void DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id); + void DestroyBuffer(uint32_t buffer_id); #if defined(WAYLAND_GBM) // Returns a gbm_device based on a DRM render node. @@ -218,7 +218,7 @@ void CommitOverlaysTask( gfx::AcceleratedWidget widget, std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays); - void DestroyBufferTask(gfx::AcceleratedWidget widget, uint32_t buffer_id); + void DestroyBufferTask(uint32_t buffer_id); #if defined(WAYLAND_GBM) // Finds drm render node, opens it and stores the handle into
diff --git a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc index a88a6161..e5610e60 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
@@ -45,7 +45,7 @@ SharedMemoryBuffer(const SharedMemoryBuffer&) = delete; SharedMemoryBuffer& operator=(const SharedMemoryBuffer&) = delete; - ~SharedMemoryBuffer() { buffer_manager_->DestroyBuffer(widget_, buffer_id_); } + ~SharedMemoryBuffer() { buffer_manager_->DestroyBuffer(buffer_id_); } // Returns SkSurface, which the client can use to write to this buffer. sk_sp<SkSurface> sk_surface() const { return sk_surface_; }
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc index bdd4376..fbb23a2 100644 --- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc +++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -285,11 +285,7 @@ window->CommitOverlays(overlays); } -void WaylandBufferManagerHost::DestroyBuffer( - [[maybe_unused]] gfx::AcceleratedWidget widget, - uint32_t buffer_id) { - // TODO(fangzhoug): Remove |widget| from the argument list of the mojo - // interface. +void WaylandBufferManagerHost::DestroyBuffer(uint32_t buffer_id) { DCHECK(base::CurrentUIThread::IsSet()); TRACE_EVENT1("wayland", "WaylandBufferManagerHost::DestroyBuffer",
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h index e086267..e15eea2 100644 --- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h +++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
@@ -103,8 +103,7 @@ uint32_t buffer_id) override; // Called by the GPU to destroy the imported wl_buffer with a |buffer_id|. - void DestroyBuffer(gfx::AcceleratedWidget widget, - uint32_t buffer_id) override; + void DestroyBuffer(uint32_t buffer_id) override; // Called by the GPU and asks to configure the surface/subsurfaces and attach // wl_buffers to WaylandWindow with the specified |widget|. Calls OnSubmission // and OnPresentation on successful swap and pixels presented.
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc index e993f39..294802d 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
@@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/check.h" +#include "base/containers/contains.h" #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/notreached.h" @@ -232,12 +233,10 @@ // TODO(crbug.com/1102946): Exo does not support custom mime types. In this // case, |data_offer_| will hold an empty mime_types list and, at this point, // it's safe just to skip the offer checks and requests here. - if (data_offer_->mime_types().empty()) + if (!base::Contains(data_offer_->mime_types(), kMimeTypeChromiumWindow)) { + DVLOG(1) << "OnEnter. No valid mime type found."; return; - - // Ensure this is a valid "window drag" offer. - DCHECK_EQ(data_offer_->mime_types().size(), 1u); - DCHECK_EQ(data_offer_->mime_types().front(), kMimeTypeChromiumWindow); + } // Accept the offer and set the dnd action. data_offer_->SetDndActions(kDndActionWindowDrag);
diff --git a/ui/ozone/platform/wayland/mojom/wayland_buffer_manager.mojom b/ui/ozone/platform/wayland/mojom/wayland_buffer_manager.mojom index 3646107..559efeef 100644 --- a/ui/ozone/platform/wayland/mojom/wayland_buffer_manager.mojom +++ b/ui/ozone/platform/wayland/mojom/wayland_buffer_manager.mojom
@@ -73,14 +73,11 @@ // These two methods are independent from the type of rendering. // - // Destroys a wl_buffer created by WaylandConnection based on the |buffer_id| - // for the WaylandWindow, which has the following |widget|. The |buffer_id| - // is the unique id of the buffer objects being destroyed on the browser - // process side. If the buffer with |buffer_id| has never been assigned to an - // AcceleratedWidget, it can be destroyed by passing a null widget - // with a correct buffer id. Providing wrong pair of the |widget| and the - // |buffer_id| will result in the termination of the GPU process. - DestroyBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id); + // Destroys a wl_buffer created by WaylandConnection based on the |buffer_id|. + // The |buffer_id| is the unique id of the buffer objects being destroyed on + // the browser process side. Providing wrong |buffer_id| will result in the + // termination of the GPU process. + DestroyBuffer(uint32 buffer_id); // Send overlay configurations for a frame to a WaylandWindow with the // following |widget|.
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc index cd9a52c..0a0aa914 100644 --- a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc +++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -199,12 +199,10 @@ Sync(); } - void DestroyBufferAndSetTerminateExpectation(gfx::AcceleratedWidget widget, - uint32_t buffer_id, - bool fail) { + void DestroyBufferAndSetTerminateExpectation(uint32_t buffer_id, bool fail) { SetTerminateCallbackExpectationAndDestroyChannel(&callback_, fail); - buffer_manager_gpu_->DestroyBuffer(widget, buffer_id); + buffer_manager_gpu_->DestroyBuffer(buffer_id); Sync(); } @@ -256,8 +254,7 @@ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kDmabufBufferId); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kDmabufBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/); } TEST_P(WaylandBufferManagerTest, VerifyModifiers) { @@ -307,8 +304,7 @@ EXPECT_EQ(params_vector[0]->modifier_lo_, kFormatModiferLinear & UINT32_MAX); // Clean up. - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kDmabufBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/); } TEST_P(WaylandBufferManagerTest, CreateShmBasedBuffers) { @@ -316,8 +312,7 @@ CreateShmBasedBufferAndSetTerminateExpecation(false /*fail*/, kShmBufferId); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kShmBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kShmBufferId, false /*fail*/); } TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) { @@ -388,11 +383,10 @@ // ... impossible to destroy non-existing buffer. { // Either it is attached... - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, true /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/); // Or not attached. - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId1, true /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/); } // Can destroy the buffer without specifying the widget. @@ -404,8 +398,7 @@ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds(), kDefaultScale, window_->GetBounds()); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); } // Still can destroy the buffer even if it has not been attached to any @@ -414,7 +407,7 @@ EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); } // ... impossible to destroy buffers twice. @@ -430,23 +423,20 @@ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); // Can't destroy the buffer with non-existing id (the manager cleared the // state after the previous failure). - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, true /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, true /*fail*/); // Non-attached buffer must have been also destroyed (we can't destroy it // twice) if there was a failure. - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId2, true /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, true /*fail*/); // Create and destroy non-attached buffer twice. CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId2, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId2, true /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, true /*fail*/); } } @@ -517,8 +507,7 @@ false /* fail */); // Destroying the buffer causes all wl_buffer objects to be destroyed. - DestroyBufferAndSetTerminateExpectation(window_->GetWidget(), 1u, - false /*fail*/); + DestroyBufferAndSetTerminateExpectation(1u, false /*fail*/); SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); buffer_manager_gpu_->CommitBuffer(window_->GetWidget(), 1u, window_->GetBounds(), kDefaultScale, @@ -681,8 +670,8 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); } TEST_P(WaylandBufferManagerTest, @@ -753,7 +742,7 @@ EXPECT_CALL(mock_surface_gpu, OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _)) .Times(1); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, /*fail=*/false); + DestroyBufferAndSetTerminateExpectation(kBufferId1, /*fail=*/false); mock_surface->DestroyPrevAttachedBuffer(); mock_surface->SendFrameCallback(); Sync(); @@ -793,13 +782,13 @@ ::testing::Eq(gfx::PresentationFeedback::Flags::kFailure)))) .Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(1); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, /*fail=*/false); + DestroyBufferAndSetTerminateExpectation(kBufferId2, /*fail=*/false); mock_surface->DestroyPrevAttachedBuffer(); mock_surface->SendFrameCallback(); mock_wp_presentation->SendPresentationCallback(); Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/); } // This test ensures that a discarded presentation feedback sent prior receiving @@ -945,9 +934,9 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/); } TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { @@ -1022,10 +1011,8 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId, - false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId2, - false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId2, false /*fail*/); } // Tests the surface does not have buffers attached until it's configured at @@ -1086,8 +1073,7 @@ window_->SetPointerFocus(false); temp_window.reset(); - DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId, - false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/); Sync(); } @@ -1204,9 +1190,9 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/); } TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) { @@ -1226,7 +1212,7 @@ Sync(); temp_window.reset(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId, false /*fail*/); } TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) { @@ -1258,7 +1244,7 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId, false /*fail*/); } TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { @@ -1334,8 +1320,8 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); } // Tests that OnSubmission and OnPresentation are properly triggered if a buffer @@ -1389,7 +1375,7 @@ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); // Destroying buffer2 should do nothing yet. - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); Sync(); testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); @@ -1399,7 +1385,7 @@ OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK, _)) .Times(2); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(2); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); Sync(); testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); @@ -1466,8 +1452,8 @@ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); } // Tests that OnSubmission and OnPresentation callbacks are properly called @@ -1548,9 +1534,9 @@ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/); } // This test verifies that submitting the buffer more than once results in @@ -1711,8 +1697,8 @@ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); } // Tests that submitting a single buffer only receives an OnSubmission. This is @@ -1744,7 +1730,7 @@ bounds); Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); } // Tests that when CommitOverlays(), root_surface can only be committed once all @@ -1900,9 +1886,9 @@ Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId3, false /*fail*/); } // Tests that destroying a channel doesn't result in resetting surface state @@ -1994,7 +1980,7 @@ bounds); Sync(); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); } // Tests that destroying a channel results in attaching null buffers to the root @@ -2385,8 +2371,8 @@ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); } - DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); - DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false /*fail*/); } TEST_P(WaylandBufferManagerTest, ExecutesTasksAfterInitialization) { @@ -2404,8 +2390,7 @@ buffer_manager_gpu_->CommitBuffer(window_->GetWidget(), kDmabufBufferId, window_->GetBounds(), kDefaultScale, window_->GetBounds()); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kDmabufBufferId, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(kDmabufBufferId, false /*fail*/); base::RunLoop().RunUntilIdle(); @@ -2514,10 +2499,8 @@ mock_surface_of_subsurface->SendFrameCallback(); mock_surface->SendFrameCallback(); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId1, false); - DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, - kBufferId2, false); + DestroyBufferAndSetTerminateExpectation(kBufferId1, false); + DestroyBufferAndSetTerminateExpectation(kBufferId2, false); } };