| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/app_restore/full_restore_app_launch_handler.h" |
| |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/public/cpp/autotest_desks_api.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/cpp/split_view_test_api.h" |
| #include "ash/public/cpp/tablet_mode.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_util.h" |
| #include "ash/wm/desks/desk_action_context_menu.h" |
| #include "ash/wm/desks/desks_test_api.h" |
| #include "ash/wm/desks/overview_desk_bar_view.h" |
| #include "ash/wm/desks/templates/saved_desk_test_util.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_grid.h" |
| #include "ash/wm/overview/overview_test_util.h" |
| #include "ash/wm/window_restore/window_restore_util.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/wm_event.h" |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/apps/app_service/launch_utils.h" |
| #include "chrome/browser/apps/platform_apps/app_browsertest_util.h" |
| #include "chrome/browser/ash/app_restore/app_restore_arc_task_handler.h" |
| #include "chrome/browser/ash/app_restore/app_restore_arc_test_helper.h" |
| #include "chrome/browser/ash/app_restore/app_restore_test_util.h" |
| #include "chrome/browser/ash/app_restore/arc_app_queue_restore_handler.h" |
| #include "chrome/browser/ash/app_restore/full_restore_prefs.h" |
| #include "chrome/browser/ash/app_restore/full_restore_service.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/ash/system_web_apps/apps/os_url_handler_system_web_app_info.h" |
| #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h" |
| #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/notifications/notification_display_service_tester.h" |
| #include "chrome/browser/prefs/session_startup_pref.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/ash/desks/desks_client.h" |
| #include "chrome/browser/ui/ash/device_scheduled_reboot/reboot_notification_controller.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" |
| #include "chrome/browser/web_applications/web_app_install_info.h" |
| #include "chrome/browser/web_applications/web_app_utils.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "components/app_constants/constants.h" |
| #include "components/app_restore/app_launch_info.h" |
| #include "components/app_restore/app_restore_info.h" |
| #include "components/app_restore/full_restore_read_handler.h" |
| #include "components/app_restore/full_restore_save_handler.h" |
| #include "components/app_restore/window_info.h" |
| #include "components/app_restore/window_properties.h" |
| #include "components/exo/buffer.h" |
| #include "components/exo/shell_surface.h" |
| #include "components/exo/surface.h" |
| #include "components/exo/test/shell_surface_builder.h" |
| #include "components/exo/wm_helper.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/services/app_service/public/cpp/app_launch_util.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/webapps/common/web_app_id.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/app_window/native_app_window.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/window_open_disposition.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/message_center/public/cpp/notification.h" |
| #include "ui/views/controls/menu/menu_item_view.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash::full_restore { |
| |
| namespace { |
| |
| constexpr char kAppId[] = "mldnpnnoiloahfhddhobgjeophloidmo"; |
| constexpr int32_t kWindowId1 = 100; |
| constexpr int32_t kWindowId2 = 200; |
| |
| constexpr char kTestAppPackage[] = "test.arc.app.package"; |
| |
| // Name of the preference that the SessionID of the Browser is saved to. This |
| // is used to transfer the SessionID from PRE_ test to actual test. |
| constexpr char kRestoreIdPrefName[] = "browser_restore_id"; |
| |
| // Test values for a test WindowInfo object. |
| constexpr int kActivationIndex = 2; |
| constexpr int kDeskId = 2; |
| const base::Uuid kDeskUuid = base::Uuid::GenerateRandomV4(); |
| constexpr gfx::Rect kCurrentBounds(500, 200); |
| constexpr chromeos::WindowStateType kWindowStateType = |
| chromeos::WindowStateType::kPrimarySnapped; |
| |
| void RemoveInactiveDesks() { |
| // Removes all the inactive desks and waits for their async operations to |
| // complete. |
| while (true) { |
| base::RunLoop run_loop; |
| if (!AutotestDesksApi().RemoveActiveDesk(run_loop.QuitClosure())) |
| break; |
| run_loop.Run(); |
| } |
| } |
| |
| void ActivateDesk(int index) { |
| base::RunLoop run_loop; |
| AutotestDesksApi().ActivateDeskAtIndex(index, run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| // Gets the ARC app launch information from the full restore file for `app_id` |
| // and `session_id`. |
| std::unique_ptr<::app_restore::AppLaunchInfo> GetArcAppLaunchInfo( |
| const std::string& app_id, |
| int32_t session_id) { |
| return ::full_restore::FullRestoreReadHandler::GetInstance() |
| ->GetArcAppLaunchInfo(app_id, session_id); |
| } |
| |
| class TestAppRestoreInfoObserver |
| : public ::app_restore::AppRestoreInfo::Observer { |
| public: |
| // ::app_restore::AppRestoreInfo::Observer: |
| void OnAppLaunched(aura::Window* window) override { |
| ++launched_windows_[window]; |
| } |
| |
| void OnWindowInitialized(aura::Window* window) override { |
| ++initialized_windows_[window]; |
| } |
| |
| void Reset() { |
| launched_windows_.clear(); |
| initialized_windows_.clear(); |
| } |
| |
| std::map<aura::Window*, int>& launched_windows() { return launched_windows_; } |
| std::map<aura::Window*, int>& initialized_windows() { |
| return initialized_windows_; |
| } |
| |
| private: |
| std::map<aura::Window*, int> launched_windows_; |
| std::map<aura::Window*, int> initialized_windows_; |
| }; |
| |
| // Creates a `WindowInfo` object and then saves it. |
| void CreateAndSaveWindowInfo( |
| aura::Window* window, |
| std::optional<uint32_t> activation_index, |
| int desk_id, |
| const base::Uuid& desk_uuid, |
| const gfx::Rect& current_bounds, |
| chromeos::WindowStateType window_state_type, |
| std::optional<ui::WindowShowState> pre_minimized_show_state, |
| std::optional<uint32_t> snap_percentage) { |
| ::app_restore::WindowInfo window_info; |
| window_info.window = window; |
| window_info.activation_index = activation_index; |
| window_info.desk_id = desk_id; |
| window_info.desk_guid = desk_uuid; |
| window_info.current_bounds = current_bounds; |
| window_info.window_state_type = window_state_type; |
| |
| if (pre_minimized_show_state) { |
| CHECK(chromeos::IsMinimizedWindowStateType(window_state_type)); |
| window_info.pre_minimized_show_state_type = pre_minimized_show_state; |
| } |
| |
| if (snap_percentage) { |
| CHECK(chromeos::IsSnappedWindowStateType(window_state_type)); |
| window_info.snap_percentage = snap_percentage; |
| } |
| |
| ::full_restore::SaveWindowInfo(window_info); |
| } |
| |
| void CreateAndSaveWindowInfo( |
| int32_t window_id, |
| int desk_id, |
| const base::Uuid& desk_uuid, |
| const gfx::Rect& current_bounds, |
| chromeos::WindowStateType window_state_type, |
| std::optional<ui::WindowShowState> pre_minimized_show_state, |
| std::optional<uint32_t> snap_percentage) { |
| // A window is needed for `SaveWindowInfo()`, but all it needs is a layer and |
| // `kWindowIdKey` to be set. `window` needs to be alive when save is called |
| // for `SaveWindowInfo()` to work. |
| auto window = std::make_unique<aura::Window>(nullptr); |
| window->Init(ui::LAYER_NOT_DRAWN); |
| window->SetProperty(::app_restore::kWindowIdKey, window_id); |
| |
| CreateAndSaveWindowInfo(window.get(), /*activation_index=*/std::nullopt, |
| desk_id, desk_uuid, current_bounds, window_state_type, |
| pre_minimized_show_state, snap_percentage); |
| } |
| |
| void CreateAndSaveWindowInfo(aura::Window* window, |
| uint32_t activation_index, |
| chromeos::WindowStateType window_state_type) { |
| CreateAndSaveWindowInfo(window, activation_index, kDeskId, kDeskUuid, |
| kCurrentBounds, window_state_type, |
| /*pre_minimized_show_state=*/std::nullopt, |
| /*snap_percentage=*/std::nullopt); |
| } |
| |
| void CreateAndSaveWindowInfo(aura::Window* window) { |
| CreateAndSaveWindowInfo(window, kActivationIndex, |
| WindowState::Get(window)->GetStateType()); |
| } |
| |
| // Gets the browser whose restore window id is same as `window_id`. |
| Browser* GetBrowserForWindowId(int32_t window_id) { |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| if (browser->window()->GetNativeWindow()->GetProperty( |
| ::app_restore::kRestoreWindowIdKey) == window_id) { |
| return browser; |
| } |
| } |
| return nullptr; |
| } |
| |
| void ClickView(const views::View* view) { |
| ASSERT_TRUE(view); |
| ASSERT_TRUE(view->GetVisible()); |
| aura::Window* root_window = |
| view->GetWidget()->GetNativeWindow()->GetRootWindow(); |
| ui::test::EventGenerator event_generator(root_window); |
| event_generator.MoveMouseToInHost(view->GetBoundsInScreen().CenterPoint()); |
| event_generator.ClickLeftButton(); |
| } |
| |
| void ClickSaveDeskAsTemplateButton() { |
| ClickView(GetSaveDeskAsTemplateButton()); |
| // Wait for the template to be stored in the model. |
| WaitForSavedDeskUI(); |
| // Clicking the save template button selects the newly created template's name |
| // field. We can press enter or escape or click to select out of it. |
| ui::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow()); |
| SendKey(ui::VKEY_RETURN, &event_generator); |
| } |
| |
| void SelectSaveDeskAsTemplateMenuItem(int index) { |
| views::MenuItemView* menu_item = |
| ash::DesksTestApi::OpenDeskContextMenuAndGetMenuItem( |
| ash::Shell::GetPrimaryRootWindow(), index, |
| ash::DeskActionContextMenu::CommandId::kSaveAsTemplate); |
| ASSERT_TRUE(menu_item); |
| ClickView(menu_item); |
| |
| // Wait for the template to be stored in the model. |
| WaitForSavedDeskUI(); |
| |
| // Clicking the save template button selects the newly created template's name |
| // field. We can press enter or escape or click to select out of it. |
| ui::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow()); |
| SendKey(ui::VKEY_RETURN, &event_generator); |
| } |
| |
| void ClickTemplateItem(int index) { |
| ClickView(GetSavedDeskItemButton(/*index=*/0)); |
| } |
| |
| } // namespace |
| |
| class FullRestoreAppLaunchHandlerTestBase |
| : public extensions::PlatformAppBrowserTest { |
| public: |
| FullRestoreAppLaunchHandlerTestBase() |
| : faster_animations_( |
| ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) { |
| scoped_restore_for_testing_ = std::make_unique<ScopedRestoreForTesting>(); |
| set_launch_browser_for_testing(nullptr); |
| } |
| ~FullRestoreAppLaunchHandlerTestBase() override = default; |
| |
| void SetUpOnMainThread() override { |
| extensions::PlatformAppBrowserTest::SetUpOnMainThread(); |
| display_service_ = |
| std::make_unique<NotificationDisplayServiceTester>(profile()); |
| Shell::Get() |
| ->login_unlock_throughput_recorder() |
| ->SetLoginFinishedReportedForTesting(); |
| } |
| |
| void SetShouldRestore(FullRestoreAppLaunchHandler* app_launch_handler) { |
| content::RunAllTasksUntilIdle(); |
| app_launch_handler->SetShouldRestore(); |
| content::RunAllTasksUntilIdle(); |
| } |
| |
| void CreateWebApp() { |
| auto web_app_install_info = |
| web_app::WebAppInstallInfo::CreateWithStartUrlForTesting( |
| GURL("https://example.org")); |
| web_app::test::InstallWebApp(profile(), std::move(web_app_install_info)); |
| } |
| |
| aura::Window* FindWebAppWindow() { |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| aura::Window* window = browser->window()->GetNativeWindow(); |
| if (window->GetProperty(::app_restore::kRestoreWindowIdKey) == |
| kWindowId2) { |
| return window; |
| } |
| } |
| return nullptr; |
| } |
| |
| // Creates and saves an app using `kAppId` and `kWindowId2` as the app ID and |
| // window ID respectively. |
| void SaveDefaultAppLaunchInfo() { |
| ::full_restore::SaveAppLaunchInfo( |
| profile()->GetPath(), |
| std::make_unique<::app_restore::AppLaunchInfo>( |
| kAppId, kWindowId2, apps::LaunchContainer::kLaunchContainerWindow, |
| WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId, |
| std::vector<base::FilePath>{}, nullptr)); |
| } |
| |
| void SaveBrowserAppLaunchInfo(int32_t window_id, |
| bool app_type_browser = false) { |
| auto app_launch_info = std::make_unique<::app_restore::AppLaunchInfo>( |
| app_constants::kChromeAppId, window_id); |
| if (app_type_browser) { |
| app_launch_info->browser_extra_info.app_type_browser = app_type_browser; |
| } |
| ::full_restore::SaveAppLaunchInfo(profile()->GetPath(), |
| std::move(app_launch_info)); |
| } |
| |
| void SaveChromeAppLaunchInfo(const std::string& app_id) { |
| ::full_restore::SaveAppLaunchInfo( |
| profile()->GetPath(), |
| std::make_unique<::app_restore::AppLaunchInfo>( |
| app_id, apps::LaunchContainer::kLaunchContainerWindow, |
| WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId, |
| std::vector<base::FilePath>{}, nullptr)); |
| } |
| |
| std::unique_ptr<::app_restore::WindowInfo> GetWindowInfo( |
| absl::variant<int32_t, aura::Window*> restore_window_id_or_window) { |
| auto* read_handler = ::full_restore::FullRestoreReadHandler::GetInstance(); |
| if (absl::holds_alternative<int32_t>(restore_window_id_or_window)) { |
| return read_handler->GetWindowInfo( |
| absl::get<int32_t>(restore_window_id_or_window)); |
| } |
| aura::Window* window = |
| absl::get<aura::Window*>(restore_window_id_or_window); |
| CHECK(window); |
| return read_handler->GetWindowInfo(window); |
| } |
| |
| bool HasNotificationFor(const std::string& notification_id) const { |
| std::optional<message_center::Notification> message_center_notification = |
| display_service_->GetNotification(notification_id); |
| return message_center_notification.has_value(); |
| } |
| |
| void VerifyPostRebootNotificationTitle(const std::string& notification_id) { |
| std::optional<message_center::Notification> message_center_notification = |
| display_service_->GetNotification(notification_id); |
| ASSERT_TRUE(message_center_notification.has_value()); |
| EXPECT_EQ(message_center_notification.value().title(), |
| l10n_util::GetStringUTF16(IDS_POLICY_DEVICE_POST_REBOOT_TITLE)); |
| } |
| |
| void SimulateClick(RestoreNotificationButtonIndex action_index) { |
| FullRestoreService::GetForProfile(profile())->Click( |
| static_cast<int>(action_index), std::nullopt); |
| } |
| |
| void ResetRestoreForTesting() { scoped_restore_for_testing_.reset(); } |
| |
| protected: |
| ui::ScopedAnimationDurationScaleMode faster_animations_; |
| std::unique_ptr<ScopedRestoreForTesting> scoped_restore_for_testing_; |
| std::unique_ptr<NotificationDisplayServiceTester> display_service_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| class FullRestoreAppLaunchHandlerBrowserTest |
| : public FullRestoreAppLaunchHandlerTestBase { |
| public: |
| FullRestoreAppLaunchHandlerBrowserTest() { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/{features::kDesksTemplates}, |
| /*disabled_features=*/{features::kDeskTemplateSync}); |
| } |
| ~FullRestoreAppLaunchHandlerBrowserTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| NoBrowserOnLaunch) { |
| EXPECT_TRUE(BrowserList::GetInstance()->empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| NotLaunchBrowser) { |
| // Add app launch info. |
| ::full_restore::SaveAppLaunchInfo( |
| profile()->GetPath(), std::make_unique<::app_restore::AppLaunchInfo>( |
| app_constants::kChromeAppId, kWindowId1)); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Create FullRestoreAppLaunchHandler, and set should restore. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndAddApp) { |
| // Add app launch info. |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create `FullRestoreAppLaunchHandler`, and set should restore. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| CreateWebApp(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| aura::Window* web_app_window = FindWebAppWindow(); |
| ASSERT_TRUE(web_app_window); |
| EXPECT_TRUE(web_app_window->GetProperty(::app_restore::kWindowInfoKey)); |
| } |
| |
| // Tests that restoring windows that are minimized will restore their |
| // pre-minimized window state when unminimizing. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| PreMinimizedState) { |
| // Add app launch info. |
| SaveDefaultAppLaunchInfo(); |
| CreateAndSaveWindowInfo(kWindowId2, kDeskId, kDeskUuid, kCurrentBounds, |
| chromeos::WindowStateType::kMinimized, |
| ui::SHOW_STATE_MAXIMIZED, |
| /*snap_percentage=*/std::nullopt); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler, and set should restore. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // The web app window should be attainable. |
| CreateWebApp(); |
| content::RunAllTasksUntilIdle(); |
| aura::Window* app_window = FindWebAppWindow(); |
| ASSERT_TRUE(app_window); |
| |
| // The current window state should be minimized, and when we unminimize it |
| // should be maximized. |
| WindowState* window_state = WindowState::Get(app_window); |
| ASSERT_TRUE(window_state->IsMinimized()); |
| window_state->Unminimize(); |
| EXPECT_TRUE(window_state->IsMaximized()); |
| |
| window_state->Restore(); |
| EXPECT_EQ(kCurrentBounds, app_window->GetBoundsInScreen()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| AddAppAndRestore) { |
| // Add app launch info. |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| CreateWebApp(); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| EXPECT_TRUE(FindWebAppWindow()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| FirstRunFullRestore) { |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/true); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is a new browser launched. |
| EXPECT_EQ(count + 1, BrowserList::GetInstance()->size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, NotRestore) { |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| |
| CreateWebApp(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| EXPECT_FALSE(FindWebAppWindow()); |
| } |
| |
| // Verify simple post reboot notification is shown when |
| // |kShowPostRebootNotification| pref is set and restore notification is not |
| // shown. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| NotRestoreAndShowSimplePostRebootNotification) { |
| // Add app launch infos. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Set the pref for showing post reboot notification. |
| profile()->GetPrefs()->SetBoolean(prefs::kShowPostRebootNotification, true); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| |
| CreateWebApp(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| EXPECT_FALSE(FindWebAppWindow()); |
| EXPECT_TRUE(HasNotificationFor(kPostRebootNotificationId)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndLaunchBrowser) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 1, BrowserList::GetInstance()->size()); |
| } |
| |
| // Verify the restore data is saved when the restore setting is always and the |
| // restore finishes. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndLaunchBrowserWithAlwaysSetting) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Set the restore pref setting as 'Always restore'. |
| profile()->GetPrefs()->SetInteger(prefs::kRestoreAppsAndPagesPrefName, |
| static_cast<int>(RestoreOption::kAlways)); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system startup. |
| auto* full_restore_service = FullRestoreService::GetForProfile(profile()); |
| full_restore_service->SetAppLaunchHandlerForTesting( |
| std::make_unique<FullRestoreAppLaunchHandler>( |
| profile(), /*should_init_service=*/true)); |
| auto* app_launch_handler1 = full_restore_service->app_launch_handler(); |
| |
| app_launch_handler1->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 1, BrowserList::GetInstance()->size()); |
| |
| AppLaunchInfoSaveWaiter::Wait(/*allow_save*/ false); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system startup again. |
| full_restore_service->SetAppLaunchHandlerForTesting( |
| std::make_unique<FullRestoreAppLaunchHandler>( |
| profile(), /*should_init_service=*/true)); |
| auto* app_launch_handler2 = full_restore_service->app_launch_handler(); |
| |
| app_launch_handler2->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is a new browser launched again. |
| EXPECT_EQ(count + 2, BrowserList::GetInstance()->size()); |
| } |
| |
| // Verify the restore data is saved when the restore button is clicked and the |
| // restore finishes. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndLaunchBrowserWithClickRestore) { |
| // TODO(http://b/328779923): This test tests clicking a notification that will |
| // not be shown if forest feature is enabled. Remove this test once forest |
| // feature can no longer be disabled. |
| if (ash::features::IsForestFeatureEnabled()) { |
| GTEST_SKIP() << "Skipping test body for Forest Feature."; |
| } |
| |
| base::HistogramTester histogram_tester; |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Set the restore pref setting as 'Ask every time'. |
| profile()->GetPrefs()->SetInteger( |
| prefs::kRestoreAppsAndPagesPrefName, |
| static_cast<int>(RestoreOption::kAskEveryTime)); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system startup. |
| auto* full_restore_service = FullRestoreService::GetForProfile(profile()); |
| full_restore_service->SetAppLaunchHandlerForTesting( |
| std::make_unique<FullRestoreAppLaunchHandler>( |
| profile(), /*should_init_service=*/true)); |
| auto* app_launch_handler1 = full_restore_service->app_launch_handler(); |
| |
| app_launch_handler1->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| EXPECT_TRUE(HasNotificationFor(kRestoreNotificationId)); |
| SimulateClick(RestoreNotificationButtonIndex::kRestore); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 1, BrowserList::GetInstance()->size()); |
| |
| AppLaunchInfoSaveWaiter::Wait(/*allow_save*/ false); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system startup again. |
| auto app_launch_handler2 = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| app_launch_handler2->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| SetShouldRestore(app_launch_handler2.get()); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is a new browser launched again. |
| EXPECT_EQ(count + 2, BrowserList::GetInstance()->size()); |
| histogram_tester.ExpectBucketCount( |
| "Ash.PostLoginGlanceables.HypotheticalFetchEvent.NoDelay", 0, |
| /*expected_bucket_count=*/1); |
| } |
| |
| // Verify the restore notification is shown with post reboot notification title |
| // when |kShowPostRebootNotification| pref is set. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreWithPostRebootTitle) { |
| // TODO(http://b/328779923): This test tests checking a notification that will |
| // not be shown if forest feature is enabled. Remove this test once forest |
| // feature can no longer be disabled. |
| if (ash::features::IsForestFeatureEnabled()) { |
| GTEST_SKIP() << "Skipping test body for Forest Feature."; |
| } |
| |
| base::HistogramTester histogram_tester; |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Set the restore pref setting as 'Ask every time'. |
| profile()->GetPrefs()->SetInteger( |
| prefs::kRestoreAppsAndPagesPrefName, |
| static_cast<int>(RestoreOption::kAskEveryTime)); |
| // Set the pref for showing post reboot notification. |
| profile()->GetPrefs()->SetBoolean(prefs::kShowPostRebootNotification, true); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system startup. |
| auto* full_restore_service = FullRestoreService::GetForProfile(profile()); |
| full_restore_service->SetAppLaunchHandlerForTesting( |
| std::make_unique<FullRestoreAppLaunchHandler>( |
| profile(), /*should_init_service=*/true)); |
| auto* app_launch_handler1 = full_restore_service->app_launch_handler(); |
| |
| app_launch_handler1->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| EXPECT_TRUE(HasNotificationFor(kRestoreNotificationId)); |
| VerifyPostRebootNotificationTitle(kRestoreNotificationId); |
| EXPECT_FALSE(HasNotificationFor(kPostRebootNotificationId)); |
| |
| histogram_tester.ExpectBucketCount( |
| "Ash.PostLoginGlanceables.HypotheticalFetchEvent.NoDelay", 0, |
| /*expected_bucket_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndNoBrowserLaunchInfo) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add app launch info, but no browser launch info. |
| SaveDefaultAppLaunchInfo(); |
| |
| // Remove the browser app to mock no browser launch info. |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->RemoveApp( |
| profile()->GetPath(), app_constants::kChromeAppId); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| LaunchBrowserAndRestore) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| |
| // Set should restore. |
| app_launch_handler->SetShouldRestore(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 1, BrowserList::GetInstance()->size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| RestoreAndLaunchBrowserAndAddApp) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add app launch infos. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler, and set should restore. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| |
| CreateWebApp(); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 2, BrowserList::GetInstance()->size()); |
| EXPECT_TRUE(FindWebAppWindow()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| LaunchBrowserAndAddAppAndRestore) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Add app launch infos. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| |
| CreateWebApp(); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Verify there is new browser launched. |
| EXPECT_EQ(count + 2, BrowserList::GetInstance()->size()); |
| EXPECT_TRUE(FindWebAppWindow()); |
| } |
| |
| // Tests that the window properties on the browser window match the ones we set |
| // in the window info. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| WindowProperties) { |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| constexpr uint32_t kSnapPercentage = 75; |
| CreateAndSaveWindowInfo( |
| kWindowId1, kDeskId, kDeskUuid, kCurrentBounds, kWindowStateType, |
| /*pre_minimized_show_state=*/std::nullopt, kSnapPercentage); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Launch the browser. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| ASSERT_EQ(count + 1u, BrowserList::GetInstance()->size()); |
| |
| // TODO(sammiequon): Check the values from the actual browser window. |
| auto window = std::make_unique<aura::Window>(nullptr); |
| window->Init(ui::LAYER_NOT_DRAWN); |
| window->SetProperty(::app_restore::kRestoreWindowIdKey, kWindowId1); |
| auto stored_window_info = GetWindowInfo(window.get()); |
| EXPECT_EQ(kDeskId, *stored_window_info->desk_id); |
| EXPECT_EQ(kDeskUuid, stored_window_info->desk_guid); |
| EXPECT_EQ(kCurrentBounds, *stored_window_info->current_bounds); |
| EXPECT_EQ(kWindowStateType, *stored_window_info->window_state_type); |
| EXPECT_EQ(kSnapPercentage, *stored_window_info->snap_percentage); |
| } |
| |
| // The PRE phase of the FullRestoreOverridesSessionRestoreTest. Creates a |
| // browser and turns on session restore. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| PRE_FullRestoreOverridesSessionRestoreTest) { |
| // Create a browser and create a tab for it. Its bounds should not equal |
| // |kCurrentBounds|. |
| Browser* browser = Browser::Create(Browser::CreateParams(profile(), true)); |
| PrefService* local_state = g_browser_process->local_state(); |
| static_cast<PrefRegistrySimple*>(local_state->DeprecatedGetPrefRegistry()) |
| ->RegisterIntegerPref(kRestoreIdPrefName, 0); |
| local_state->SetInteger(kRestoreIdPrefName, browser->session_id().id()); |
| AddBlankTabAndShow(browser); |
| aura::Window* window = browser->window()->GetNativeWindow(); |
| ASSERT_NE(kCurrentBounds, window->bounds()); |
| |
| // Ensure that |browser| is in a normal show state. |
| auto* window_state = WindowState::Get(window); |
| window_state->Restore(); |
| ASSERT_TRUE(window_state->IsNormalStateType()); |
| |
| // Turn on session restore. |
| SessionStartupPref::SetStartupPref( |
| profile(), SessionStartupPref(SessionStartupPref::LAST)); |
| } |
| |
| // Tests that Full Restore data overrides the browser's session restore data. |
| // Session restore is turned on in the PRE phase of the test, simulating a user |
| // logging out and back in. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| FullRestoreOverridesSessionRestoreTest) { |
| PrefService* local_state = g_browser_process->local_state(); |
| static_cast<PrefRegistrySimple*>(local_state->DeprecatedGetPrefRegistry()) |
| ->RegisterIntegerPref(kRestoreIdPrefName, 0); |
| const SessionID::id_type previous_browser_id = |
| static_cast<SessionID::id_type>( |
| local_state->GetInteger(kRestoreIdPrefName)); |
| ASSERT_NE(0, previous_browser_id); |
| |
| auto* browser_list = BrowserList::GetInstance(); |
| // Initially there should not be any browsers. |
| ASSERT_TRUE(browser_list->empty()); |
| |
| // Create Full Restore launch data before launching any browser, simulating |
| // Full Restore data being saved prior to restart. |
| SaveBrowserAppLaunchInfo(previous_browser_id); |
| CreateAndSaveWindowInfo(previous_browser_id, kDeskId, kDeskUuid, |
| kCurrentBounds, chromeos::WindowStateType::kNormal, |
| /*pre_minimized_show_state=*/std::nullopt, |
| /*snap_percentage=*/std::nullopt); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Launch the browser. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| ASSERT_EQ(1u, browser_list->size()); |
| |
| // The restored browser's bounds should be the bounds saved by Full Restore, |
| // i.e. |kCurrentBounds|. |
| const gfx::Rect& browser_bounds = |
| browser_list->get(0u)->window()->GetNativeWindow()->bounds(); |
| EXPECT_EQ(kCurrentBounds, browser_bounds); |
| } |
| |
| // TODO(crbug.com/41485298): Re-enable this test when the flakiness issue is |
| // fixed. Test Lacros window properties and bounds are restored correctly. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| DISABLED_RestoreLacrosWindowProperties) { |
| gfx::Size size(32, 32); |
| gfx::Point origin(100, 100); |
| gfx::Rect prerestore_bounds(origin, size); |
| |
| // Create Full Restore launch data before launching any browser, simulating |
| // Full Restore data being saved prior to restart. `kWindowId1` is the restore |
| // window id. |
| ::full_restore::SaveAppLaunchInfo( |
| profile()->GetPath(), std::make_unique<::app_restore::AppLaunchInfo>( |
| app_constants::kLacrosAppId, kWindowId1)); |
| CreateAndSaveWindowInfo(kWindowId1, kDeskId, kDeskUuid, prerestore_bounds, |
| chromeos::WindowStateType::kNormal, |
| /*pre_minimized_show_state=*/std::nullopt, |
| /*snap_percentage=*/std::nullopt); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler, and set should restore to save the Full |
| // Restore data. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Create a WMHelper instance for Surface to set in the constructor. |
| std::unique_ptr<exo::WMHelper> wm_helper = std::make_unique<exo::WMHelper>(); |
| |
| // Create the Lacros window surface and restore it. |
| auto shell_surface = |
| exo::test::ShellSurfaceBuilder(size).SetNoCommit().BuildShellSurface(); |
| shell_surface->SetRestoreInfo(/*restore_session_id=*/kWindowId2, |
| /*restore_window_id=*/kWindowId1); |
| shell_surface->root_surface()->Commit(); |
| |
| auto* shell_surface_window = shell_surface->GetWidget()->GetNativeWindow(); |
| EXPECT_EQ(kWindowId2, |
| shell_surface_window->GetProperty(::app_restore::kWindowIdKey)); |
| EXPECT_EQ(kWindowId1, shell_surface_window->GetProperty( |
| ::app_restore::kRestoreWindowIdKey)); |
| EXPECT_EQ(prerestore_bounds, shell_surface_window->GetBoundsInScreen()); |
| } |
| |
| // Launch a desk template with a browser after full restore. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerBrowserTest, |
| LaunchDeskTemplateAfterFullRestore) { |
| // Add the chrome browser launch info. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveBrowserAppLaunchInfo(kWindowId2, /*app_type_browser=*/true); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is new browser launched. |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| Browser* browser_from_full_restore = BrowserList::GetInstance()->get(0); |
| |
| // We're now going to create a new desk and a browser in that desk. |
| AutotestDesksApi().CreateNewDesk(); |
| ActivateDesk(/*index=*/1); |
| |
| const gfx::Rect expected_bounds(10, 10, 500, 300); |
| const GURL expected_url("https://example.org"); |
| |
| Browser* new_browser = Browser::Create( |
| Browser::CreateParams(Browser::TYPE_NORMAL, profile(), false)); |
| |
| content::TestNavigationObserver navigation_observer(expected_url); |
| navigation_observer.StartWatchingNewWebContents(); |
| chrome::AddTabAt(new_browser, expected_url, /*index=*/-1, |
| /*foreground=*/true); |
| navigation_observer.Wait(); |
| |
| new_browser->window()->Show(); |
| new_browser->window()->SetBounds(expected_bounds); |
| |
| ASSERT_EQ(BrowserList::GetInstance()->size(), 2u); |
| |
| // The browser has now been created. We're now going to enter overview mode |
| // and save the desk as a template. Once saved, we'll exit overview mode. |
| ToggleOverview(); |
| WaitForOverviewEnterAnimation(); |
| |
| if (features::IsForestFeatureEnabled()) { |
| SelectSaveDeskAsTemplateMenuItem(/*index=*/1); |
| } else { |
| ClickSaveDeskAsTemplateButton(); |
| } |
| |
| ToggleOverview(); |
| WaitForOverviewExitAnimation(); |
| |
| ASSERT_FALSE(OverviewController::Get()->overview_session()); |
| |
| // Move the browser a bit and then close it. This is to make sure that when we |
| // create a new browser, its bounds are actually coming from the template. |
| new_browser->window()->SetBounds(expected_bounds + gfx::Vector2d(10, 10)); |
| web_app::CloseAndWait(new_browser); |
| |
| ASSERT_EQ(BrowserList::GetInstance()->size(), 1u); |
| |
| // We're now going to launch the template and verify that we have a new |
| // browser, and that it has the correct bounds and URL. |
| ToggleOverview(); |
| WaitForOverviewEnterAnimation(); |
| |
| // Enter the saved desk library. |
| ClickView(GetLibraryButton()); |
| // Launch the first entry. |
| ClickTemplateItem(/*index=*/0); |
| |
| ToggleOverview(); |
| WaitForOverviewExitAnimation(); |
| |
| ASSERT_EQ(BrowserList::GetInstance()->size(), 2u); |
| |
| Browser* browser_from_template = nullptr; |
| for (Browser* b : *BrowserList::GetInstance()) { |
| if (b != browser_from_full_restore) { |
| browser_from_template = b; |
| break; |
| } |
| } |
| |
| ASSERT_TRUE(browser_from_template); |
| // Verify that the browser has the same bounds as was captured. |
| EXPECT_EQ(expected_bounds, browser_from_template->window()->GetBounds()); |
| // Verify that the browser has a tab with the expected URL. |
| EXPECT_EQ(1, browser_from_template->tab_strip_model()->count()); |
| EXPECT_EQ(expected_url, browser_from_template->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetVisibleURL()); |
| // Verify that the browser window has a negative restore window ID (and lower |
| // than the special value -1). |
| EXPECT_LT(browser_from_template->window()->GetNativeWindow()->GetProperty( |
| ::app_restore::kRestoreWindowIdKey), |
| -1); |
| } |
| |
| class FullRestoreAppLaunchHandlerWithFloatingWorkspaceBrowserTest |
| : public FullRestoreAppLaunchHandlerTestBase { |
| public: |
| FullRestoreAppLaunchHandlerWithFloatingWorkspaceBrowserTest() { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/{features::kFloatingWorkspaceV2, |
| features::kDeskTemplateSync}, |
| /*disabled_features=*/{}); |
| } |
| ~FullRestoreAppLaunchHandlerWithFloatingWorkspaceBrowserTest() override = |
| default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F( |
| FullRestoreAppLaunchHandlerWithFloatingWorkspaceBrowserTest, |
| AddAppAndNotRestoreWithFloatingWorkspaceEnabled) { |
| // Add app launch infos. |
| SaveBrowserAppLaunchInfo(kWindowId1); |
| SaveDefaultAppLaunchInfo(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| size_t count = BrowserList::GetInstance()->size(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| app_launch_handler->LaunchBrowserWhenReady(/*first_run_full_restore=*/false); |
| |
| CreateWebApp(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify there is no new browser launched. |
| EXPECT_EQ(count, BrowserList::GetInstance()->size()); |
| EXPECT_FALSE(FindWebAppWindow()); |
| } |
| |
| class FullRestoreAppLaunchHandlerChromeAppBrowserTest |
| : public FullRestoreAppLaunchHandlerBrowserTest { |
| public: |
| FullRestoreAppLaunchHandlerChromeAppBrowserTest() { |
| ResetRestoreForTesting(); |
| set_launch_browser_for_testing( |
| std::make_unique<ScopedLaunchBrowserForTesting>()); |
| } |
| ~FullRestoreAppLaunchHandlerChromeAppBrowserTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerChromeAppBrowserTest, |
| RestoreChromeApp) { |
| // Have 4 desks total. |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| ActivateDesk(2); |
| |
| ::full_restore::SetActiveProfilePath(profile()->GetPath()); |
| |
| // Create the restore data. |
| const extensions::Extension* extension = |
| LoadAndLaunchPlatformApp("launch", "Launched"); |
| ASSERT_TRUE(extension); |
| SaveChromeAppLaunchInfo(extension->id()); |
| |
| extensions::AppWindow* app_window = CreateAppWindow(profile(), extension); |
| ASSERT_TRUE(app_window); |
| |
| auto* window = app_window->GetNativeWindow(); |
| CreateAndSaveWindowInfo(window); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Simulate the system shutdown process, and the window is closed. |
| FullRestoreService::GetForProfile(profile())->OnAppTerminating(); |
| CloseAppWindow(app_window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create a non-restored window in the restored window's desk container. |
| Browser::CreateParams non_restored_params(profile(), true); |
| non_restored_params.initial_workspace = "2"; |
| Browser* non_restored_browser = Browser::Create(non_restored_params); |
| AddBlankTabAndShow(non_restored_browser); |
| aura::Window* non_restored_window = |
| non_restored_browser->window()->GetNativeWindow(); |
| |
| // Read from the restore data. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Verify the restore window id. |
| app_window = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window); |
| |
| window = app_window->GetNativeWindow(); |
| ASSERT_TRUE(window); |
| int restore_window_id = |
| window->GetProperty(::app_restore::kRestoreWindowIdKey); |
| EXPECT_NE(0, restore_window_id); |
| |
| auto* window_info = window->GetProperty(::app_restore::kWindowInfoKey); |
| ASSERT_TRUE(window_info); |
| EXPECT_TRUE(window_info->activation_index.has_value()); |
| int32_t* index = window->GetProperty(::app_restore::kActivationIndexKey); |
| ASSERT_TRUE(index); |
| EXPECT_EQ(kActivationIndex, *index); |
| EXPECT_EQ(kDeskId, window->GetProperty(aura::client::kWindowWorkspaceKey)); |
| |
| // Non-topmost windows created from full restore are not activated. They will |
| // become activatable after a couple seconds. Verify that the |
| // `non_restored_window` is topmost and check that `window` is not |
| // activatable. |
| EXPECT_THAT(non_restored_window->parent()->children(), |
| testing::ElementsAre(window, non_restored_window)); |
| EXPECT_FALSE(views::Widget::GetWidgetForNativeView(window)->IsActive()); |
| EXPECT_FALSE(wm::CanActivateWindow(window)); |
| |
| // Wait a couple seconds and verify the window is now activatable. |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::Seconds(3)); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(wm::CanActivateWindow(window)); |
| |
| EXPECT_EQ(0, ::app_restore::FetchRestoreWindowId(extension->id())); |
| |
| // Close the window. |
| CloseAppWindow(app_window); |
| ASSERT_FALSE(GetWindowInfo(restore_window_id)); |
| |
| // Remove the added desks. |
| RemoveInactiveDesks(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerChromeAppBrowserTest, |
| RestoreMinimizedChromeApp) { |
| ::full_restore::SetActiveProfilePath(profile()->GetPath()); |
| |
| // Create the restore data. |
| const extensions::Extension* extension = |
| LoadAndLaunchPlatformApp("launch", "Launched"); |
| ASSERT_TRUE(extension); |
| SaveChromeAppLaunchInfo(extension->id()); |
| |
| extensions::AppWindow* app_window = CreateAppWindow(profile(), extension); |
| ASSERT_TRUE(app_window); |
| |
| // Save app window as minimized. |
| CreateAndSaveWindowInfo(app_window->GetNativeWindow(), 1u, |
| chromeos::WindowStateType::kMinimized); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Read from the restore data. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Tests that the created window is minimized. |
| app_window = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window); |
| EXPECT_TRUE(app_window->GetBaseWindow()->IsMinimized()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerChromeAppBrowserTest, |
| RestoreMultipleChromeAppWindows) { |
| ::full_restore::SetActiveProfilePath(profile()->GetPath()); |
| |
| // Create the restore data, 2 windows for 1 chrome app. |
| const extensions::Extension* extension = |
| LoadAndLaunchPlatformApp("launch", "Launched"); |
| ASSERT_TRUE(extension); |
| const std::string& app_id = extension->id(); |
| SaveChromeAppLaunchInfo(app_id); |
| |
| extensions::AppWindow* app_window1 = CreateAppWindow(profile(), extension); |
| ASSERT_TRUE(app_window1); |
| auto* window1 = app_window1->GetNativeWindow(); |
| CreateAndSaveWindowInfo(window1); |
| |
| SaveChromeAppLaunchInfo(app_id); |
| |
| extensions::AppWindow* app_window2 = CreateAppWindow(profile(), extension); |
| ASSERT_TRUE(app_window2); |
| auto* window2 = app_window2->GetNativeWindow(); |
| CreateAndSaveWindowInfo(window2); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Read from the restore data. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Verify the restore window id; |
| app_window1 = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window1); |
| window1 = app_window1->GetNativeWindow(); |
| ASSERT_TRUE(window1); |
| EXPECT_NE(0, window1->GetProperty(::app_restore::kRestoreWindowIdKey)); |
| |
| auto window_info = GetWindowInfo(window1); |
| ASSERT_TRUE(window_info); |
| EXPECT_TRUE(window_info->activation_index.has_value()); |
| EXPECT_EQ(INT32_MAX, window_info->activation_index.value()); |
| |
| app_window2 = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window2); |
| window2 = app_window2->GetNativeWindow(); |
| ASSERT_TRUE(window2); |
| EXPECT_NE(0, window2->GetProperty(::app_restore::kRestoreWindowIdKey)); |
| |
| window_info = GetWindowInfo(window2); |
| ASSERT_TRUE(window_info); |
| EXPECT_TRUE(window_info->activation_index.has_value()); |
| EXPECT_EQ(INT32_MAX, window_info->activation_index.value()); |
| |
| // Create a new window, verity the restore window id is 0. |
| auto* app_window = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window); |
| auto* window = app_window->GetNativeWindow(); |
| ASSERT_TRUE(window); |
| EXPECT_EQ(0, window->GetProperty(::app_restore::kRestoreWindowIdKey)); |
| |
| // Close the window. |
| CloseAppWindow(app_window1); |
| CloseAppWindow(app_window2); |
| } |
| |
| // Tests that fullscreened windows will not be restored as fullscreen, which is |
| // not supported for full restore. Regression test for |
| // https://crbug.com/1203010. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerChromeAppBrowserTest, |
| ImmersiveFullscreenApp) { |
| ::full_restore::SetActiveProfilePath(profile()->GetPath()); |
| |
| // Create the restore data. |
| const extensions::Extension* extension = |
| LoadAndLaunchPlatformApp("launch", "Launched"); |
| ASSERT_TRUE(extension); |
| SaveChromeAppLaunchInfo(extension->id()); |
| |
| extensions::AppWindow* app_window = CreateAppWindow(profile(), extension); |
| ASSERT_TRUE(app_window); |
| |
| // Toggle immersive fullscreen by simulating what happens when F4 is pressed. |
| // WindowRestoreController will save to file when the state changes. |
| const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN); |
| WindowState::Get(app_window->GetNativeWindow())->OnWMEvent(&event); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Read from the restore data. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Tests that the created window is not fullscreen. |
| app_window = CreateAppWindow(browser()->profile(), extension); |
| ASSERT_TRUE(app_window); |
| EXPECT_FALSE(app_window->GetBaseWindow()->IsFullscreenOrPending()); |
| } |
| |
| class FullRestoreAppLaunchHandlerArcAppBrowserTest |
| : public FullRestoreAppLaunchHandlerBrowserTest { |
| public: |
| FullRestoreAppLaunchHandlerArcAppBrowserTest() = default; |
| FullRestoreAppLaunchHandlerArcAppBrowserTest( |
| const FullRestoreAppLaunchHandlerArcAppBrowserTest&) = delete; |
| FullRestoreAppLaunchHandlerArcAppBrowserTest& operator=( |
| const FullRestoreAppLaunchHandlerArcAppBrowserTest&) = delete; |
| ~FullRestoreAppLaunchHandlerArcAppBrowserTest() override = default; |
| |
| // FullRestoreAppLaunchHandlerBrowserTest: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| arc_helper_.SetUpCommandLine(command_line); |
| FullRestoreAppLaunchHandlerBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| arc_helper_.SetUpInProcessBrowserTestFixture(); |
| FullRestoreAppLaunchHandlerBrowserTest::SetUpInProcessBrowserTestFixture(); |
| } |
| |
| void SetUpOnMainThread() override { |
| arc_helper_.SetUpOnMainThread(profile()); |
| FullRestoreAppLaunchHandlerBrowserTest::SetUpOnMainThread(); |
| } |
| |
| protected: |
| arc::mojom::AppHost* app_host() { return arc_helper_.GetAppHost(); } |
| |
| void SetProfile() { |
| ::full_restore::FullRestoreSaveHandler::GetInstance() |
| ->SetPrimaryProfilePath(profile()->GetPath()); |
| ::full_restore::SetActiveProfilePath(profile()->GetPath()); |
| test_app_restore_info_observer_.Reset(); |
| } |
| |
| void SaveAppLaunchInfo(const std::string& app_id, int32_t arc_session_id) { |
| ::full_restore::SaveAppLaunchInfo( |
| profile()->GetPath(), |
| std::make_unique<::app_restore::AppLaunchInfo>( |
| app_id, ui::EF_NONE, arc_session_id, display::kDefaultDisplayId)); |
| } |
| |
| void Restore() { |
| test_app_restore_info_observer_.Reset(); |
| |
| auto* arc_task_handler = |
| app_restore::AppRestoreArcTaskHandler::GetForProfile(profile()); |
| ASSERT_TRUE(arc_task_handler); |
| |
| arc_app_queue_restore_handler_ = |
| arc_task_handler->GetFullRestoreArcAppQueueRestoreHandler(); |
| DCHECK(arc_app_queue_restore_handler_); |
| arc_app_queue_restore_handler_->is_app_connection_ready_ = false; |
| |
| app_launch_handler_ = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler_.get()); |
| } |
| |
| void ForceLaunchApp(const std::string& app_id, int32_t window_id) { |
| if (arc_app_queue_restore_handler_) { |
| arc_app_queue_restore_handler_->LaunchAppWindow(app_id, window_id); |
| content::RunAllTasksUntilIdle(); |
| } |
| } |
| |
| void VerifyGetArcAppLaunchInfo(const std::string& app_id, |
| int32_t session_id, |
| int32_t restore_window_id) { |
| auto app_launch_info = GetArcAppLaunchInfo(app_id, session_id); |
| ASSERT_TRUE(app_launch_info); |
| |
| EXPECT_EQ(app_id, app_launch_info->app_id); |
| EXPECT_THAT(app_launch_info->window_id, |
| testing::Optional(restore_window_id)); |
| EXPECT_THAT(app_launch_info->event_flag, testing::Optional(ui::EF_NONE)); |
| } |
| |
| void VerifyWindowProperty(aura::Window* window, |
| int32_t window_id, |
| int32_t restore_window_id, |
| bool hidden) { |
| ASSERT_TRUE(window); |
| EXPECT_EQ(window_id, window->GetProperty(::app_restore::kWindowIdKey)); |
| EXPECT_EQ(restore_window_id, |
| window->GetProperty(::app_restore::kRestoreWindowIdKey)); |
| EXPECT_EQ(hidden, |
| window->GetProperty(::app_restore::kParentToHiddenContainerKey)); |
| } |
| |
| void VerifyWindowInfo(aura::Window* window, |
| int32_t activation_index, |
| chromeos::WindowStateType window_state_type = |
| chromeos::WindowStateType::kDefault) { |
| auto window_info = GetWindowInfo(window); |
| ASSERT_TRUE(window_info); |
| EXPECT_THAT(window_info->activation_index, |
| testing::Optional(activation_index)); |
| EXPECT_FALSE(window_info->current_bounds.has_value()); |
| |
| // For ARC windows, Android can restore window minimized or maximized |
| // status, so the WindowStateType from the window info for the minimized and |
| // maximized state will be removed. |
| if (window_state_type == chromeos::WindowStateType::kMaximized || |
| window_state_type == chromeos::WindowStateType::kMinimized) { |
| EXPECT_FALSE(window_info->window_state_type.has_value()); |
| } else { |
| EXPECT_THAT(window_info->window_state_type, |
| testing::Optional(window_state_type)); |
| } |
| } |
| |
| void VerifyObserver(aura::Window* window, int launch_count, int init_count) { |
| auto& launched_windows = test_app_restore_info_observer_.launched_windows(); |
| if (launch_count == 0) { |
| EXPECT_TRUE(launched_windows.find(window) == launched_windows.end()); |
| } else { |
| EXPECT_EQ(launch_count, launched_windows[window]); |
| } |
| |
| auto& initialized_windows = |
| test_app_restore_info_observer_.initialized_windows(); |
| if (init_count == 0) { |
| EXPECT_TRUE(initialized_windows.find(window) == |
| initialized_windows.end()); |
| } else { |
| EXPECT_EQ(init_count, initialized_windows[window]); |
| } |
| } |
| |
| void VerifyThemeColor(const std::string& app_id, |
| int32_t task_id, |
| uint32_t primary_color, |
| uint32_t status_bar_color) { |
| const auto& app_id_to_launch_list = |
| app_launch_handler()->restore_data()->app_id_to_launch_list(); |
| |
| auto it = app_id_to_launch_list.find(app_id); |
| ASSERT_TRUE(it != app_id_to_launch_list.end()); |
| |
| auto data_it = it->second.find(task_id); |
| ASSERT_TRUE(data_it != it->second.end()); |
| EXPECT_THAT(data_it->second->primary_color, |
| testing::Optional(primary_color)); |
| EXPECT_THAT(data_it->second->status_bar_color, |
| testing::Optional(status_bar_color)); |
| } |
| |
| void VerifyRestoreData(const std::string& app_id, int32_t window_id) { |
| DCHECK(app_launch_handler()); |
| |
| const auto& app_id_to_launch_list = |
| app_launch_handler()->restore_data()->app_id_to_launch_list(); |
| |
| auto it = app_id_to_launch_list.find(app_id); |
| EXPECT_TRUE(it != app_id_to_launch_list.end()); |
| |
| auto data_it = it->second.find(window_id); |
| EXPECT_TRUE(data_it != it->second.end()); |
| } |
| |
| void VerifyNoRestoreData(const std::string& app_id) { |
| DCHECK(app_launch_handler()); |
| |
| const auto& app_id_to_launch_list = |
| app_launch_handler()->restore_data()->app_id_to_launch_list(); |
| EXPECT_FALSE(base::Contains(app_id_to_launch_list, app_id)); |
| } |
| |
| FullRestoreAppLaunchHandler* app_launch_handler() { |
| if (!app_launch_handler_) |
| app_launch_handler_ = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| return app_launch_handler_.get(); |
| } |
| |
| TestAppRestoreInfoObserver* test_app_restore_info_observer() { |
| return &test_app_restore_info_observer_; |
| } |
| |
| const std::map<int32_t, std::string>& task_id_to_app_id() { |
| auto* arc_save_handler = |
| ::full_restore::FullRestoreSaveHandler::GetInstance() |
| ->arc_save_handler_.get(); |
| DCHECK(arc_save_handler); |
| return arc_save_handler->task_id_to_app_id_; |
| } |
| |
| protected: |
| raw_ptr<app_restore::ArcAppQueueRestoreHandler, DanglingUntriaged> |
| arc_app_queue_restore_handler_ = nullptr; |
| AppRestoreArcTestHelper arc_helper_; |
| |
| private: |
| std::unique_ptr<FullRestoreAppLaunchHandler> app_launch_handler_; |
| TestAppRestoreInfoObserver test_app_restore_info_observer_; |
| }; |
| |
| // Test the not restored ARC window is not added to the hidden container. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| NotHideArcWindow) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id1); |
| |
| // Create the window for app1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/0); |
| VerifyWindowProperty(window, kTaskId1, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId1, session_id1); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/0); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| Restore(); |
| widget->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| int32_t session_id2 = 1; |
| |
| // Create the window to simulate launching the ARC app. |
| int32_t kTaskId2 = 200; |
| auto* widget1 = CreateExoWindow("org.chromium.arc.200"); |
| auto* window1 = widget1->GetNativeWindow(); |
| |
| // The task is not ready, so the window is currently in a hidden container. |
| EXPECT_EQ(Shell::GetContainer(window1->GetRootWindow(), |
| kShellWindowId_UnparentedContainer), |
| window1->parent()); |
| |
| VerifyObserver(window1, /*launch_count=*/0, /*init_count=*/1); |
| VerifyWindowProperty(window1, kTaskId2, |
| ::app_restore::kParentToHiddenContainer, |
| /*hidden=*/true); |
| |
| // Simulate creating the task for the ARC app window. |
| arc_helper_.CreateTask(app_id, kTaskId2, session_id2); |
| |
| VerifyObserver(window1, /*launch_count=*/0, /*init_count=*/1); |
| VerifyWindowProperty(window1, kTaskId2, |
| ::app_restore::kParentToHiddenContainer, |
| /*hidden=*/false); |
| |
| int32_t session_id3 = 2; |
| int32_t kTaskId3 = 300; |
| // Simulate creating the task before the window is created. |
| arc_helper_.CreateTask(app_id, kTaskId3, session_id3); |
| |
| // Create the window to simulate launching the ARC app. |
| auto* widget2 = CreateExoWindow("org.chromium.arc.300"); |
| auto* window2 = widget2->GetNativeWindow(); |
| |
| VerifyObserver(window2, /*launch_count=*/0, /*init_count=*/0); |
| // The window should not be hidden. |
| VerifyWindowProperty(window2, kTaskId3, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Destroy the task and close the window. |
| app_host()->OnTaskDestroyed(kTaskId2); |
| widget1->CloseNow(); |
| app_host()->OnTaskDestroyed(kTaskId3); |
| widget2->CloseNow(); |
| |
| ::app_restore::AppRestoreInfo::GetInstance()->RemoveObserver( |
| test_app_restore_info_observer()); |
| arc_helper_.StopInstance(); |
| } |
| |
| // Test restoration when the ARC window is created before OnTaskCreated is |
| // called. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| RestoreArcApp) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id1); |
| |
| // Create the window for app1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/0); |
| VerifyWindowProperty(window, kTaskId1, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId1, session_id1); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/0); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Simulate the system shutdown process, and the window is closed. |
| FullRestoreService::GetForProfile(profile())->OnAppTerminating(); |
| widget->CloseNow(); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| Restore(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| int32_t session_id2 = |
| ::app_restore::kArcSessionIdOffsetForRestoredLaunching + 1; |
| |
| // Create some desks so we can test that the exo window is placed in the |
| // correct desk container after the task is created. |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| |
| ForceLaunchApp(app_id, kTaskId1); |
| |
| // Create the window to simulate the restoration for the app. The task id |
| // needs to match the |window_app_id| arg of CreateExoWindow. |
| int32_t kTaskId2 = 200; |
| widget = CreateExoWindow("org.chromium.arc.200"); |
| window = widget->GetNativeWindow(); |
| |
| // The task is not ready, so the window is currently in a hidden container. |
| EXPECT_EQ(Shell::GetContainer(window->GetRootWindow(), |
| kShellWindowId_UnparentedContainer), |
| window->parent()); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/1); |
| VerifyWindowProperty(window, kTaskId2, |
| ::app_restore::kParentToHiddenContainer, |
| /*hidden=*/true); |
| |
| // Simulate creating the task for the restored window. |
| arc_helper_.CreateTask(app_id, kTaskId2, session_id2); |
| |
| // Tests that after the task is created, the window is placed in the container |
| // associated with `kDeskId` (2), which is desk C. |
| EXPECT_EQ(Shell::GetContainer(window->GetRootWindow(), |
| kShellWindowId_DeskContainerC), |
| window->parent()); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/1); |
| VerifyWindowProperty(window, kTaskId2, kTaskId1, /*hidden=*/false); |
| VerifyWindowInfo(window, kActivationIndex); |
| |
| // Destroy the task and close the window. |
| app_host()->OnTaskDestroyed(kTaskId2); |
| widget->CloseNow(); |
| |
| ASSERT_FALSE(GetWindowInfo(kTaskId1)); |
| |
| ::app_restore::AppRestoreInfo::GetInstance()->RemoveObserver( |
| test_app_restore_info_observer()); |
| arc_helper_.StopInstance(); |
| |
| RemoveInactiveDesks(); |
| } |
| |
| // Test restoration when the ARC ghost window is created before OnTaskCreated is |
| // called. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| RestoreArcGhostWindow) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id1); |
| |
| // Create the window for app1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/0); |
| VerifyWindowProperty(window, kTaskId1, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId1, session_id1); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/0); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| Restore(); |
| widget->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| int32_t session_id2 = ::app_restore::CreateArcSessionId(); |
| ::full_restore::FullRestoreReadHandler::GetInstance() |
| ->SetArcSessionIdForWindowId(session_id2, kTaskId1); |
| |
| // Create the window with the ghost window session to simulate the ghost |
| // window restoration for the app. |
| widget = CreateExoWindow( |
| base::StringPrintf("org.chromium.arc.session.%d", session_id2), app_id); |
| window = widget->GetNativeWindow(); |
| |
| SaveAppLaunchInfo(app_id, session_id2); |
| |
| // The ghost window should not be hidden. |
| VerifyWindowProperty(window, /*window_id*/ 0, |
| /*restore_window_id*/ kTaskId1, |
| /*hidden=*/false); |
| |
| VerifyGetArcAppLaunchInfo(app_id, session_id2, kTaskId1); |
| |
| // Call SaveAppLaunchInfo to simulate the ARC app is ready, and launch the app |
| // again. |
| SaveAppLaunchInfo(app_id, session_id2); |
| |
| // Simulate creating the task for the restored window. |
| int32_t kTaskId2 = 200; |
| window->SetProperty(::app_restore::kWindowIdKey, kTaskId2); |
| arc_helper_.CreateTask(app_id, kTaskId2, session_id2); |
| |
| VerifyWindowProperty(window, kTaskId2, kTaskId1, /*hidden=*/false); |
| VerifyWindowInfo(window, kActivationIndex); |
| |
| // Verify the ghost window session id has been removed from the restore data. |
| EXPECT_FALSE(GetArcAppLaunchInfo(app_id, session_id2)); |
| |
| // Destroy the task and close the window. |
| app_host()->OnTaskDestroyed(kTaskId2); |
| widget->CloseNow(); |
| |
| ::app_restore::AppRestoreInfo::GetInstance()->RemoveObserver( |
| test_app_restore_info_observer()); |
| arc_helper_.StopInstance(); |
| |
| RemoveInactiveDesks(); |
| } |
| |
| // Test the ARC ghost window is saved if the task is not created. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| SaveArcGhostWindow) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id1); |
| |
| // Create the window for app1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/0); |
| VerifyWindowProperty(window, kTaskId1, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId1, session_id1); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/0); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Simulate the system reboot. |
| Restore(); |
| widget->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| int32_t session_id2 = ::app_restore::CreateArcSessionId(); |
| ::full_restore::FullRestoreReadHandler::GetInstance() |
| ->SetArcSessionIdForWindowId(session_id2, kTaskId1); |
| |
| // Create the window with the ghost window session to simulate the ghost |
| // window restoration for the app. |
| widget = CreateExoWindow( |
| base::StringPrintf("org.chromium.arc.session.%d", session_id2), app_id); |
| window = widget->GetNativeWindow(); |
| |
| SaveAppLaunchInfo(app_id, session_id2); |
| CreateAndSaveWindowInfo(window); |
| |
| // The ghost window should not be hidden. |
| VerifyWindowProperty(window, /*window_id*/ 0, |
| /*restore_window_id*/ kTaskId1, |
| /*hidden=*/false); |
| |
| VerifyGetArcAppLaunchInfo(app_id, session_id2, kTaskId1); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Simulate the system reboot before the task id is created. |
| Restore(); |
| widget->CloseNow(); |
| |
| int32_t session_id3 = ::app_restore::CreateArcSessionId(); |
| ::full_restore::FullRestoreReadHandler::GetInstance() |
| ->SetArcSessionIdForWindowId(session_id3, session_id2); |
| |
| // Create the window with the ghost window session to simulate the ghost |
| // window restoration for the app. |
| widget = CreateExoWindow( |
| base::StringPrintf("org.chromium.arc.session.%d", session_id3), app_id); |
| window = widget->GetNativeWindow(); |
| |
| SaveAppLaunchInfo(app_id, session_id3); |
| CreateAndSaveWindowInfo(window); |
| |
| // Call SaveAppLaunchInfo to simulate the ARC app is ready, and launch the app |
| // again. |
| SaveAppLaunchInfo(app_id, session_id3); |
| |
| // Simulate creating the task for the restored window. |
| int32_t kTaskId2 = 200; |
| arc_helper_.CreateTask(app_id, kTaskId2, session_id3); |
| window->SetProperty(::app_restore::kWindowIdKey, kTaskId2); |
| |
| VerifyWindowProperty(window, kTaskId2, session_id2, /*hidden=*/false); |
| VerifyWindowInfo(window, kActivationIndex); |
| |
| // Verify the ghost window session id has been removed from the restore data. |
| EXPECT_FALSE(GetArcAppLaunchInfo(app_id, session_id3)); |
| |
| // Destroy the task and close the window. |
| app_host()->OnTaskDestroyed(kTaskId2); |
| widget->CloseNow(); |
| |
| ::app_restore::AppRestoreInfo::GetInstance()->RemoveObserver( |
| test_app_restore_info_observer()); |
| arc_helper_.StopInstance(); |
| |
| RemoveInactiveDesks(); |
| } |
| |
| // Test restoration with multiple ARC apps, when the ARC windows are created |
| // before and after OnTaskCreated is called. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| RestoreMultipleArcApps) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1 and store its bounds. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| gfx::Rect pre_restore_bounds_1 = window1->GetBoundsInScreen(); |
| |
| // Create the window for the app2 and store its bounds. The task id needs to |
| // match the |window_app_id| arg of CreateExoWindow. |
| int32_t kTaskId2 = 101; |
| views::Widget* widget2 = CreateExoWindow("org.chromium.arc.101"); |
| aura::Window* window2 = widget2->GetNativeWindow(); |
| gfx::Rect pre_restore_bounds_2 = window2->GetBoundsInScreen(); |
| |
| // Simulate creating kTaskId2. |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| VerifyObserver(window1, /*launch_count=*/1, /*init_count=*/0); |
| VerifyObserver(window2, /*launch_count=*/1, /*init_count=*/0); |
| |
| VerifyWindowProperty(window1, kTaskId1, /*restore_window_id*/ 0, |
| /*hidden=*/false); |
| VerifyWindowProperty(window2, kTaskId2, /*restore_window_id*/ 0, |
| /*hidden=*/false); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| int32_t activation_index2 = 12; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kMaximized); |
| CreateAndSaveWindowInfo(window2, activation_index2, |
| chromeos::WindowStateType::kMinimized); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| Restore(); |
| widget1->CloseNow(); |
| widget2->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| app_host()->OnTaskDestroyed(kTaskId2); |
| |
| ForceLaunchApp(app_id1, kTaskId1); |
| ForceLaunchApp(app_id2, kTaskId2); |
| |
| int32_t session_id3 = |
| ::app_restore::kArcSessionIdOffsetForRestoredLaunching + 1; |
| int32_t session_id4 = |
| ::app_restore::kArcSessionIdOffsetForRestoredLaunching + 2; |
| |
| // Create the window to simulate the restoration for the app1. The task id |
| // needs to match the |window_app_id| arg of CreateExoWindow. |
| int32_t kTaskId3 = 201; |
| widget1 = CreateExoWindow("org.chromium.arc.201"); |
| window1 = widget1->GetNativeWindow(); |
| |
| VerifyWindowProperty(window1, kTaskId3, |
| ::app_restore::kParentToHiddenContainer, |
| /*hidden=*/true); |
| EXPECT_EQ(pre_restore_bounds_1, window1->GetBoundsInScreen()); |
| |
| // Simulate creating tasks for apps. |
| arc_helper_.CreateTask(app_id1, kTaskId3, session_id3); |
| |
| int32_t kTaskId4 = 202; |
| arc_helper_.CreateTask(app_id2, kTaskId4, session_id4); |
| |
| // Create the window to simulate the restoration for the app2. |
| widget2 = CreateExoWindow("org.chromium.arc.202"); |
| window2 = widget2->GetNativeWindow(); |
| EXPECT_EQ(pre_restore_bounds_2, window2->GetBoundsInScreen()); |
| |
| VerifyObserver(window1, /*launch_count=*/1, /*init_count=*/1); |
| VerifyObserver(window2, /*launch_count=*/1, /*init_count=*/1); |
| VerifyWindowProperty(window1, kTaskId3, kTaskId1, /*hidden=*/false); |
| VerifyWindowProperty(window2, kTaskId4, kTaskId2, /*hidden=*/false); |
| VerifyWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kMaximized); |
| VerifyWindowInfo(window2, activation_index2, |
| chromeos::WindowStateType::kMinimized); |
| |
| // destroy tasks and close windows. |
| widget1->CloseNow(); |
| app_host()->OnTaskDestroyed(kTaskId3); |
| app_host()->OnTaskDestroyed(kTaskId4); |
| widget2->CloseNow(); |
| |
| ASSERT_FALSE(GetWindowInfo(kTaskId1)); |
| ASSERT_FALSE(GetWindowInfo(kTaskId2)); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Test ARC apps restore data is removed, when Play Store is disabled. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| DisablePlayStore) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1 and store its bounds. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| // Create the window for the app2 and store its bounds. |
| int32_t kTaskId2 = 101; |
| views::Widget* widget2 = CreateExoWindow("org.chromium.arc.101"); |
| aura::Window* window2 = widget2->GetNativeWindow(); |
| |
| // Simulate creating kTaskId2. |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| VerifyObserver(window1, /*launch_count=*/1, /*init_count=*/0); |
| VerifyObserver(window2, /*launch_count=*/1, /*init_count=*/0); |
| |
| VerifyWindowProperty(window1, kTaskId1, /*restore_window_id*/ 0, |
| /*hidden=*/false); |
| VerifyWindowProperty(window2, kTaskId2, /*restore_window_id*/ 0, |
| /*hidden=*/false); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| int32_t activation_index2 = 12; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kMaximized); |
| CreateAndSaveWindowInfo(window2, activation_index2, |
| chromeos::WindowStateType::kMinimized); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Verify ARC app launch info is saved in `restore_data`. |
| const auto* restore_data = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetRestoreData( |
| profile()->GetPath()); |
| ASSERT_TRUE(restore_data); |
| ASSERT_FALSE(restore_data->app_id_to_launch_list().empty()); |
| ASSERT_FALSE(task_id_to_app_id().empty()); |
| |
| // Simulate Play Store is disabled. |
| app_restore::AppRestoreArcTaskHandler::GetForProfile(profile()) |
| ->OnArcPlayStoreEnabledChanged(/*enabled=*/false); |
| widget1->CloseNow(); |
| |
| // Verify ARC app launch info is removed from `restore_data`. |
| ASSERT_TRUE(restore_data->app_id_to_launch_list().empty()); |
| ASSERT_TRUE(task_id_to_app_id().empty()); |
| |
| widget2->CloseNow(); |
| |
| // Simulate Play Store is enabled and `app_id1` is launched. |
| int32_t session_id3 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t kTaskId3 = 201; |
| SaveAppLaunchInfo(app_id1, session_id3); |
| arc_helper_.CreateTask(app_id1, kTaskId3, session_id3); |
| ASSERT_FALSE(restore_data->app_id_to_launch_list().empty()); |
| ASSERT_TRUE(base::Contains(restore_data->app_id_to_launch_list(), app_id1)); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Test restoration when the ARC window is created before OnTaskCreated is |
| // called. |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| ArcAppThemeColorUpdate) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id); |
| |
| // Create the window for app1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId = 100; |
| uint32_t kPrimaryColor = 0xFFFFFFFF; |
| uint32_t kStatusBarColor = 0xFF000000; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/0); |
| VerifyWindowProperty(window, kTaskId, /*restore_window_id=*/0, |
| /*hidden=*/false); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId, session_id); |
| arc_helper_.UpdateThemeColor(kTaskId, kPrimaryColor, kStatusBarColor); |
| |
| VerifyObserver(window, /*launch_count=*/1, /*init_count=*/0); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| ASSERT_TRUE(app_launch_handler()); |
| content::RunAllTasksUntilIdle(); |
| |
| VerifyThemeColor(app_id, kTaskId, kPrimaryColor, kStatusBarColor); |
| |
| widget->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId); |
| |
| ::app_restore::AppRestoreInfo::GetInstance()->RemoveObserver( |
| test_app_restore_info_observer()); |
| arc_helper_.StopInstance(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(FullRestoreAppLaunchHandlerArcAppBrowserTest, |
| DeskTemplateAfterFullRestoreArcApp) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, false); |
| |
| const std::string app_id = GetTestApp1Id(kTestAppPackage); |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| ::app_restore::AppRestoreInfo::GetInstance()->AddObserver( |
| test_app_restore_info_observer()); |
| |
| SaveAppLaunchInfo(app_id, session_id1); |
| |
| // Create the window for app1. The task id needs to match the `window_app_id` |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| views::Widget* widget = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window = widget->GetNativeWindow(); |
| |
| // Simulate creating the task. |
| arc_helper_.CreateTask(app_id, kTaskId1, session_id1); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Simulate the system shutdown process, and the window is closed. |
| widget->CloseNow(); |
| |
| Restore(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| int32_t session_id2 = |
| ::app_restore::kArcSessionIdOffsetForRestoredLaunching + 1; |
| |
| // Create some desks so we can test that the exo window is placed in the |
| // correct desk container after the task is created. |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| AutotestDesksApi().CreateNewDesk(); |
| |
| ForceLaunchApp(app_id, kTaskId1); |
| |
| // Create the window to simulate the restoration for the app. The task id |
| // needs to match the |window_app_id| arg of CreateExoWindow. |
| int32_t kTaskId2 = 200; |
| widget = CreateExoWindow("org.chromium.arc.200"); |
| window = widget->GetNativeWindow(); |
| |
| // The task is not ready, so the window is currently in a hidden container. |
| EXPECT_EQ(Shell::GetContainer(window->GetRootWindow(), |
| kShellWindowId_UnparentedContainer), |
| window->parent()); |
| |
| VerifyObserver(window, /*launch_count=*/0, /*init_count=*/1); |
| VerifyWindowProperty(window, kTaskId2, |
| ::app_restore::kParentToHiddenContainer, |
| /*hidden=*/true); |
| |
| // Simulate creating the task for the restored window. |
| arc_helper_.CreateTask(app_id, kTaskId2, session_id2); |
| |
| // Activate the most recently created desk. |
| ActivateDesk(/*index=*/2); |
| |
| // Capture the active desk as a template. |
| ToggleOverview(); |
| WaitForOverviewEnterAnimation(); |
| if (features::IsForestFeatureEnabled()) { |
| SelectSaveDeskAsTemplateMenuItem(/*index=*/2); |
| } else { |
| ClickSaveDeskAsTemplateButton(); |
| } |
| ToggleOverview(); |
| WaitForOverviewExitAnimation(); |
| |
| // Destroy the task and close the window. |
| app_host()->OnTaskDestroyed(kTaskId2); |
| widget->CloseNow(); |
| |
| // Launch the template. |
| ToggleOverview(); |
| WaitForOverviewEnterAnimation(); |
| ClickView(GetLibraryButton()); |
| ClickTemplateItem(/*index=*/0); |
| ToggleOverview(); |
| WaitForOverviewExitAnimation(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| int32_t session_id3 = |
| ::app_restore::kArcSessionIdOffsetForRestoredLaunching + 2; |
| int32_t kTaskId3 = 300; |
| |
| arc_helper_.CreateTask(app_id, kTaskId3, session_id3); |
| |
| widget = CreateExoWindow("org.chromium.arc.300"); |
| window = widget->GetNativeWindow(); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify that the ARC window has a negative restore window ID (and lower than |
| // the special value -1). |
| EXPECT_LT(window->GetProperty(::app_restore::kRestoreWindowIdKey), -1); |
| } |
| |
| class ArcAppQueueRestoreHandlerArcAppBrowserTest |
| : public FullRestoreAppLaunchHandlerArcAppBrowserTest { |
| protected: |
| void UpdateApp(const std::string& app_id, apps::Readiness readiness) { |
| apps::AppPtr app = std::make_unique<apps::App>(apps::AppType::kArc, app_id); |
| app->readiness = readiness; |
| |
| auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile()); |
| std::vector<apps::AppPtr> deltas; |
| deltas.push_back(std::move(app)); |
| proxy->OnApps(std::move(deltas), apps::AppType::kArc, |
| false /* should_notify_initialized */); |
| } |
| |
| void RemoveApp(const std::string& app_id) { |
| apps::AppPtr app = std::make_unique<apps::App>(apps::AppType::kArc, app_id); |
| app->readiness = apps::Readiness::kUninstalledByUser; |
| |
| auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile()); |
| std::vector<apps::AppPtr> deltas; |
| deltas.push_back(std::move(app)); |
| proxy->OnApps(std::move(deltas), apps::AppType::kArc, |
| false /* should_notify_initialized */); |
| } |
| |
| bool HasRestoreData() { |
| DCHECK(arc_app_queue_restore_handler_); |
| return arc_app_queue_restore_handler_->HasRestoreData(); |
| } |
| |
| bool HasRestoreData(const std::string& app_id) { |
| DCHECK(arc_app_queue_restore_handler_); |
| |
| for (auto it : arc_app_queue_restore_handler_->windows_) { |
| if (it.second.app_id == app_id) |
| return true; |
| } |
| for (auto it : arc_app_queue_restore_handler_->no_stack_windows_) { |
| if (it.app_id == app_id) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| std::set<std::string> GetAppIds() { |
| DCHECK(arc_app_queue_restore_handler_); |
| return arc_app_queue_restore_handler_->app_ids_; |
| } |
| |
| void OnAppConnectionReady() { |
| if (!arc_app_queue_restore_handler_) { |
| arc_app_queue_restore_handler_ = |
| app_restore::AppRestoreArcTaskHandler::GetForProfile(profile()) |
| ->GetFullRestoreArcAppQueueRestoreHandler(); |
| } |
| arc_app_queue_restore_handler_->OnAppConnectionReady(); |
| } |
| |
| void VerifyWindows(int32_t index, |
| const std::string& app_id, |
| int32_t window_id) { |
| DCHECK(arc_app_queue_restore_handler_); |
| auto it = arc_app_queue_restore_handler_->windows_.find(index); |
| ASSERT_TRUE(it != arc_app_queue_restore_handler_->windows_.end()); |
| EXPECT_EQ(app_id, it->second.app_id); |
| EXPECT_EQ(window_id, it->second.window_id); |
| } |
| |
| void VerifyNoStackWindows(const std::string& app_id, int32_t window_id) { |
| DCHECK(arc_app_queue_restore_handler_); |
| bool found = false; |
| for (auto it : arc_app_queue_restore_handler_->no_stack_windows_) { |
| if (it.app_id == app_id && it.window_id == window_id) { |
| found = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found); |
| } |
| }; |
| |
| // Verify the saved windows in ArcAppLaunchHandler when apps are removed. |
| IN_PROC_BROWSER_TEST_F(ArcAppQueueRestoreHandlerArcAppBrowserTest, RemoveApps) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| // Simulate creating kTaskId2 for the app2. |
| int32_t kTaskId2 = 101; |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kNormal); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| base::HistogramTester histogram_tester; |
| Restore(); |
| widget1->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| app_host()->OnTaskDestroyed(kTaskId2); |
| |
| EXPECT_TRUE(HasRestoreData()); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| VerifyNoStackWindows(app_id2, kTaskId2); |
| VerifyRestoreData(app_id1, kTaskId1); |
| VerifyRestoreData(app_id2, kTaskId2); |
| |
| // Remove `app_id2`, and verify windows and restore data for `app_id2` have |
| // been removed. |
| RemoveApp(app_id2); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| EXPECT_FALSE(HasRestoreData(app_id2)); |
| VerifyRestoreData(app_id1, kTaskId1); |
| VerifyNoRestoreData(app_id2); |
| |
| OnAppConnectionReady(); |
| EXPECT_EQ(1, histogram_tester.GetBucketCount( |
| app_restore::kRestoredAppWindowCountHistogram, 1)); |
| |
| // Remove app_id1, and verify windows and restore data for `app_id1` have been |
| // removed. |
| RemoveApp(app_id1); |
| EXPECT_FALSE(HasRestoreData(app_id1)); |
| VerifyNoRestoreData(app_id1); |
| VerifyNoRestoreData(app_id2); |
| |
| // Modify `app_id1` status to be ready to simulate `app_id1` is installed. |
| UpdateApp(app_id1, apps::Readiness::kReady); |
| EXPECT_FALSE(HasRestoreData(app_id1)); |
| EXPECT_TRUE(GetAppIds().empty()); |
| VerifyNoRestoreData(app_id1); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Verify the saved windows in ArcAppLaunchHandler when apps status are |
| // modified. |
| IN_PROC_BROWSER_TEST_F(ArcAppQueueRestoreHandlerArcAppBrowserTest, UpdateApps) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| // Simulate creating kTaskId2 for the app2. |
| int32_t kTaskId2 = 101; |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kNormal); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Modify apps status before restoring, so that apps can't be restored. |
| UpdateApp(app_id1, apps::Readiness::kDisabledByPolicy); |
| UpdateApp(app_id2, apps::Readiness::kDisabledByPolicy); |
| base::HistogramTester histogram_tester; |
| Restore(); |
| widget1->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| app_host()->OnTaskDestroyed(kTaskId2); |
| |
| std::set<std::string> app_ids = GetAppIds(); |
| EXPECT_THAT(app_ids, testing::ElementsAre(app_id1, app_id2)); |
| |
| // Modify `app_id1` status to be ready to prepare launching `app_id1`. |
| UpdateApp(app_id1, apps::Readiness::kReady); |
| app_ids = GetAppIds(); |
| EXPECT_THAT(app_ids, testing::ElementsAre(app_id2)); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| |
| // Modify `app_id2` status to be ready to prepare launching `app_id2`. |
| UpdateApp(app_id2, apps::Readiness::kReady); |
| app_ids = GetAppIds(); |
| EXPECT_TRUE(app_ids.empty()); |
| VerifyNoStackWindows(app_id2, kTaskId2); |
| |
| // Verify the restore data and windows for `app_id1` and `app_id2` are not |
| // removed. |
| VerifyRestoreData(app_id1, kTaskId1); |
| VerifyRestoreData(app_id2, kTaskId2); |
| |
| OnAppConnectionReady(); |
| EXPECT_EQ(1, histogram_tester.GetBucketCount( |
| app_restore::kRestoredAppWindowCountHistogram, 2)); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Verify the saved windows in ArcAppLaunchHandler when only restore one of the |
| // apps. |
| IN_PROC_BROWSER_TEST_F(ArcAppQueueRestoreHandlerArcAppBrowserTest, |
| RestoreOneApp) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kNormal); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| base::HistogramTester histogram_tester; |
| Restore(); |
| widget1->CloseNow(); |
| app_host()->OnTaskDestroyed(kTaskId1); |
| |
| EXPECT_TRUE(GetAppIds().empty()); |
| EXPECT_TRUE(HasRestoreData()); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| |
| // Verify the restore data and windows for `app_id1` are not removed. |
| VerifyRestoreData(app_id1, kTaskId1); |
| VerifyNoRestoreData(app_id2); |
| |
| OnAppConnectionReady(); |
| EXPECT_EQ(1, histogram_tester.GetBucketCount( |
| app_restore::kRestoredAppWindowCountHistogram, 1)); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Verify the saved windows in ArcAppLaunchHandler when one of apps is ready |
| // late. |
| IN_PROC_BROWSER_TEST_F(ArcAppQueueRestoreHandlerArcAppBrowserTest, |
| AppIsReadyLate) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| // Simulate creating kTaskId2 for the app2. |
| int32_t kTaskId2 = 101; |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| int32_t activation_index1 = 11; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kNormal); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Remove `app_id2` before restoring, so that `app_id2` can't be restored. |
| RemoveApp(app_id2); |
| base::HistogramTester histogram_tester; |
| Restore(); |
| widget1->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| app_host()->OnTaskDestroyed(kTaskId2); |
| |
| EXPECT_THAT(GetAppIds(), testing::ElementsAre(app_id2)); |
| EXPECT_TRUE(HasRestoreData()); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| |
| OnAppConnectionReady(); |
| EXPECT_EQ(1, histogram_tester.GetBucketCount( |
| app_restore::kRestoredAppWindowCountHistogram, 1)); |
| |
| // Modify `app_id2` status to be ready to prepare launching `app_id2`. |
| UpdateApp(app_id2, apps::Readiness::kReady); |
| EXPECT_TRUE(GetAppIds().empty()); |
| EXPECT_TRUE(HasRestoreData()); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| VerifyNoStackWindows(app_id2, kTaskId2); |
| |
| // Verify the restore data and windows for `app_id1` and `app_id2` are not |
| // removed. |
| VerifyRestoreData(app_id1, kTaskId1); |
| VerifyRestoreData(app_id2, kTaskId2); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| // Verify the restore process when the user clicks the `restore` button very |
| // late after the OnAppConnectionReady is called. |
| IN_PROC_BROWSER_TEST_F(ArcAppQueueRestoreHandlerArcAppBrowserTest, |
| RestoreLate) { |
| SetProfile(); |
| arc_helper_.InstallTestApps(kTestAppPackage, true); |
| |
| const std::string app_id1 = GetTestApp1Id(kTestAppPackage); |
| const std::string app_id2 = GetTestApp2Id(kTestAppPackage); |
| |
| int32_t session_id1 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| int32_t session_id2 = |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->GetArcSessionId(); |
| |
| SaveAppLaunchInfo(app_id1, session_id1); |
| SaveAppLaunchInfo(app_id2, session_id2); |
| |
| // Simulate creating kTaskId1. The task id needs to match the |window_app_id| |
| // arg of CreateExoWindow. |
| int32_t kTaskId1 = 100; |
| arc_helper_.CreateTask(app_id1, kTaskId1, session_id1); |
| |
| // Create the window for the app1. |
| views::Widget* widget1 = CreateExoWindow("org.chromium.arc.100"); |
| aura::Window* window1 = widget1->GetNativeWindow(); |
| |
| // Simulate creating kTaskId2 for the app2. |
| int32_t kTaskId2 = 101; |
| arc_helper_.CreateTask(app_id2, kTaskId2, session_id2); |
| |
| int32_t activation_index1 = 11; |
| CreateAndSaveWindowInfo(window1, activation_index1, |
| chromeos::WindowStateType::kNormal); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Call OnAppConnectionReady to simulate the app connection is ready. |
| base::HistogramTester histogram_tester; |
| OnAppConnectionReady(); |
| |
| // Simulate the user clicks the `restore` button. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| SetShouldRestore(app_launch_handler.get()); |
| |
| widget1->CloseNow(); |
| |
| app_host()->OnTaskDestroyed(kTaskId1); |
| app_host()->OnTaskDestroyed(kTaskId2); |
| |
| EXPECT_TRUE(HasRestoreData()); |
| VerifyWindows(activation_index1, app_id1, kTaskId1); |
| VerifyNoStackWindows(app_id2, kTaskId2); |
| |
| EXPECT_EQ(1, histogram_tester.GetBucketCount( |
| app_restore::kRestoredAppWindowCountHistogram, 2)); |
| |
| arc_helper_.StopInstance(); |
| } |
| |
| class FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest |
| : public SystemWebAppIntegrationTest { |
| public: |
| FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest() { |
| OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(true); |
| } |
| |
| ~FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest() override { |
| OsUrlHandlerSystemWebAppDelegate::EnableDelegateForTesting(false); |
| } |
| |
| void SetUpOnMainThread() override { |
| SystemWebAppIntegrationTest::SetUpOnMainThread(); |
| Shell::Get() |
| ->login_unlock_throughput_recorder() |
| ->SetLoginFinishedReportedForTesting(); |
| } |
| |
| Browser* LaunchSystemWebApp(const GURL& gurl, |
| SystemWebAppType system_app_type, |
| apps::LaunchSource launch_source, |
| bool is_override_gurl = false) { |
| WaitForTestSystemAppInstall(); |
| |
| auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile()); |
| content::TestNavigationObserver navigation_observer(gurl); |
| navigation_observer.StartWatchingNewWebContents(); |
| |
| const std::string app_id = |
| *GetManager().GetAppIdForSystemApp(system_app_type); |
| auto window_info = |
| std::make_unique<apps::WindowInfo>(display::kDefaultDisplayId); |
| if (is_override_gurl) { |
| proxy->LaunchAppWithUrl(app_id, ui::EF_NONE, gurl, launch_source, |
| std::move(window_info)); |
| } else { |
| proxy->Launch(app_id, ui::EF_NONE, launch_source, std::move(window_info)); |
| } |
| |
| navigation_observer.Wait(); |
| |
| return BrowserList::GetInstance()->GetLastActive(); |
| } |
| |
| Browser* LaunchSystemWebAppWithOverrideURL(SystemWebAppType system_app_type, |
| const GURL& override_url) { |
| return LaunchSystemWebApp(override_url, system_app_type, |
| apps::LaunchSource::kFromChromeInternal, |
| /*is_override_gurl=*/true); |
| } |
| |
| Browser* LaunchHelpSystemWebApp() { |
| return LaunchSystemWebApp(GURL("chrome://help-app/"), |
| SystemWebAppType::HELP, |
| apps::LaunchSource::kFromChromeInternal); |
| } |
| |
| // Launches the media system web app. Used when a test needs to use a |
| // different system web app. |
| Browser* LaunchMediaSystemWebApp( |
| apps::LaunchSource launch_source = |
| apps::LaunchSource::kFromChromeInternal) { |
| return LaunchSystemWebApp(GURL("chrome://media-app/"), |
| SystemWebAppType::MEDIA, launch_source); |
| } |
| |
| void ModifyAppReadiness(apps::Readiness readiness) { |
| apps::AppType app_type = apps::AppType::kWeb; |
| if (crosapi::browser_util::IsLacrosEnabled() && |
| web_app::IsWebAppsCrosapiEnabled()) { |
| app_type = apps::AppType::kSystemWeb; |
| } |
| |
| auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile()); |
| apps::AppPtr app = std::make_unique<apps::App>( |
| app_type, *GetManager().GetAppIdForSystemApp(SystemWebAppType::HELP)); |
| app->readiness = readiness; |
| std::vector<apps::AppPtr> deltas; |
| deltas.push_back(std::move(app)); |
| proxy->OnApps(std::move(deltas), app_type, |
| false /* should_notify_initialized */); |
| } |
| |
| void SetShouldRestore(FullRestoreAppLaunchHandler* app_launch_handler) { |
| content::RunAllTasksUntilIdle(); |
| app_launch_handler->SetShouldRestore(); |
| content::RunAllTasksUntilIdle(); |
| } |
| |
| bool HasWindowInfo(int32_t restore_window_id) { |
| return ::full_restore::FullRestoreReadHandler::GetInstance()->HasWindowInfo( |
| restore_window_id); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| LaunchSWA) { |
| Browser* app_browser = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser); |
| ASSERT_NE(browser(), app_browser); |
| |
| // Get the window id. |
| aura::Window* window = app_browser->window()->GetNativeWindow(); |
| int32_t window_id = window->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser); |
| |
| ASSERT_FALSE(HasWindowInfo(window_id)); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| ASSERT_TRUE(HasWindowInfo(window_id)); |
| |
| // Get the restored browser for the system web app. |
| Browser* restore_app_browser = GetBrowserForWindowId(window_id); |
| ASSERT_TRUE(restore_app_browser); |
| ASSERT_NE(browser(), restore_app_browser); |
| |
| // Get the restore window id. |
| window = restore_app_browser->window()->GetNativeWindow(); |
| int32_t restore_window_id = |
| window->GetProperty(::app_restore::kRestoreWindowIdKey); |
| |
| EXPECT_EQ(window_id, restore_window_id); |
| } |
| |
| // Ensure that Full Restore respects the override URL specified in a SWA's |
| // AppLaunchParams if configured to do so. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| LaunchSWAWithRestoreOverrideURL) { |
| const auto swa_type = SystemWebAppType::OS_URL_HANDLER; |
| const auto override_url = GURL(chrome::kChromeUIVersionURL); |
| |
| Browser* app_browser = |
| LaunchSystemWebAppWithOverrideURL(swa_type, override_url); |
| ASSERT_TRUE(app_browser); |
| ASSERT_NE(browser(), app_browser); |
| |
| // Get the window id. |
| aura::Window* window = app_browser->window()->GetNativeWindow(); |
| int32_t window_id = window->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser); |
| |
| ASSERT_FALSE(HasWindowInfo(window_id)); |
| |
| content::TestNavigationObserver navigation_observer(override_url); |
| navigation_observer.StartWatchingNewWebContents(); |
| SetShouldRestore(app_launch_handler.get()); |
| navigation_observer.Wait(); |
| |
| ASSERT_TRUE(HasWindowInfo(window_id)); |
| |
| // Get the restored browser for the system web app. |
| Browser* restore_app_browser = GetBrowserForWindowId(window_id); |
| ASSERT_TRUE(restore_app_browser); |
| ASSERT_NE(browser(), restore_app_browser); |
| |
| // Get the restore window id. |
| window = restore_app_browser->window()->GetNativeWindow(); |
| int32_t restore_window_id = |
| window->GetProperty(::app_restore::kRestoreWindowIdKey); |
| |
| EXPECT_EQ(window_id, restore_window_id); |
| |
| EXPECT_EQ(override_url, restore_app_browser->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetLastCommittedURL()); |
| } |
| |
| // Verify that when the full restore doesn't start, the browser window of the |
| // SWA doesn't have the restore info. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| LaunchSWAWithoutRestore) { |
| Browser* app_browser = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser); |
| ASSERT_NE(browser(), app_browser); |
| |
| // Get the window id. |
| aura::Window* window = app_browser->window()->GetNativeWindow(); |
| int32_t window_id = window->GetProperty(::app_restore::kWindowIdKey); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| ASSERT_FALSE(HasWindowInfo(window_id)); |
| |
| Browser* new_app_browser = LaunchHelpSystemWebApp(); |
| |
| ASSERT_TRUE(new_app_browser); |
| ASSERT_NE(browser(), new_app_browser); |
| |
| window = new_app_browser->window()->GetNativeWindow(); |
| auto* window_state = WindowState::Get(window); |
| EXPECT_FALSE(window_state->HasRestoreBounds()); |
| } |
| |
| // Verify the restoration if the SWA is not available when set |
| // restore, and the restoration can work if the SWA is added later. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| NoSWAWhenRestore) { |
| Browser* app_browser = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser); |
| ASSERT_NE(browser(), app_browser); |
| |
| // Get the window id. |
| aura::Window* window = app_browser->window()->GetNativeWindow(); |
| int32_t window_id = window->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser); |
| |
| // Modify the app readiness to uninstall to simulate the app is not installed |
| // during the system startup phase. |
| ModifyAppReadiness(apps::Readiness::kUninstalledByUser); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Verify the app is not restored because the app is not installed. |
| Browser* restore_app_browser = GetBrowserForWindowId(window_id); |
| ASSERT_FALSE(restore_app_browser); |
| |
| // Modify the app readiness to kReady to simulate the app is installed. |
| ModifyAppReadiness(apps::Readiness::kReady); |
| |
| // Wait for the restoration. |
| content::RunAllTasksUntilIdle(); |
| |
| // Get the restored browser for the system web app to verify the app is |
| // restored. |
| restore_app_browser = GetBrowserForWindowId(window_id); |
| ASSERT_TRUE(restore_app_browser); |
| ASSERT_NE(browser(), restore_app_browser); |
| |
| // Get the restore window id. |
| window = restore_app_browser->window()->GetNativeWindow(); |
| int32_t restore_window_id = |
| window->GetProperty(::app_restore::kRestoreWindowIdKey); |
| |
| EXPECT_EQ(window_id, restore_window_id); |
| } |
| |
| // Launch the help app. Reboot, no restore. Launch the media app by the system. |
| // Reboot, verify the help app can be restored. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| RestartMutiTimesWithLaunchBySystem) { |
| Browser* app_browser1 = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser1); |
| ASSERT_NE(browser(), app_browser1); |
| |
| // Get the window id. |
| aura::Window* window1 = app_browser1->window()->GetNativeWindow(); |
| int32_t window_id1 = window1->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser1); |
| |
| // Modify the app readiness to uninstall to simulate the app is not installed |
| // during the system startup phase. |
| ModifyAppReadiness(apps::Readiness::kUninstalledByUser); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system reboot. |
| auto app_launch_handler1 = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify the app is not restored because the app is not installed. |
| ASSERT_FALSE(GetBrowserForWindowId(window_id1)); |
| |
| // Launch another app from kFromChromeInternal, so not start the save timer, |
| // to prevent overwriting the full restore file. |
| Browser* app_browser2 = LaunchMediaSystemWebApp(); |
| ASSERT_TRUE(app_browser2); |
| aura::Window* window2 = app_browser2->window()->GetNativeWindow(); |
| int32_t window_id2 = window2->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(/*allow_save=*/false); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| app_launch_handler1.reset(); |
| |
| // Modify the app readiness to kReady to simulate the app is installed. |
| ModifyAppReadiness(apps::Readiness::kReady); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system reboot again. |
| auto app_launch_handler2 = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| SetShouldRestore(app_launch_handler2.get()); |
| |
| // Wait for the restoration. |
| content::RunAllTasksUntilIdle(); |
| |
| ASSERT_FALSE(GetBrowserForWindowId(window_id2)); |
| |
| // Get the restored browser for the system web app to verify the app is |
| // restored. |
| auto* restore_app_browser = GetBrowserForWindowId(window_id1); |
| ASSERT_TRUE(restore_app_browser); |
| |
| // Get the restore window id. |
| window1 = restore_app_browser->window()->GetNativeWindow(); |
| int32_t restore_window_id = |
| window1->GetProperty(::app_restore::kRestoreWindowIdKey); |
| |
| EXPECT_EQ(window_id1, restore_window_id); |
| } |
| |
| // Launch the help app. Reboot, no restore. Launch the media app by the user. |
| // Reboot, verify the media app can be restored. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| RestartMultiTimesWithLaunchByUser) { |
| Browser* app_browser1 = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser1); |
| ASSERT_NE(browser(), app_browser1); |
| |
| // Get the window id. |
| aura::Window* window1 = app_browser1->window()->GetNativeWindow(); |
| int32_t window_id1 = window1->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| // Close app_browser so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser1); |
| |
| // Modify the app readiness to uninstall to simulate the app is not installed |
| // during the system startup phase. |
| ModifyAppReadiness(apps::Readiness::kUninstalledByUser); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system reboot. |
| auto app_launch_handler1 = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| // Verify the app is not restored because the app is not installed. |
| ASSERT_FALSE(GetBrowserForWindowId(window_id1)); |
| |
| // Launch another app from kFromShelf, so start the save timer, |
| Browser* app_browser2 = |
| LaunchMediaSystemWebApp(apps::LaunchSource::kFromShelf); |
| ASSERT_TRUE(app_browser2); |
| aura::Window* window2 = app_browser2->window()->GetNativeWindow(); |
| int32_t window_id2 = window2->GetProperty(::app_restore::kWindowIdKey); |
| |
| AppLaunchInfoSaveWaiter::Wait(/*allow_save=*/false); |
| ::full_restore::FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| |
| web_app::CloseAndWait(app_browser2); |
| |
| app_launch_handler1.reset(); |
| |
| // Modify the app readiness to kReady to simulate the app is installed. |
| ModifyAppReadiness(apps::Readiness::kReady); |
| |
| // Create FullRestoreAppLaunchHandler to simulate the system reboot again. |
| auto app_launch_handler2 = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| content::RunAllTasksUntilIdle(); |
| |
| SetShouldRestore(app_launch_handler2.get()); |
| |
| // Wait for the restoration. |
| content::RunAllTasksUntilIdle(); |
| |
| ASSERT_FALSE(GetBrowserForWindowId(window_id1)); |
| |
| // Get the restored browser for the system web app to verify the app is |
| // restored. |
| auto* restore_app_browser = GetBrowserForWindowId(window_id2); |
| ASSERT_TRUE(restore_app_browser); |
| |
| // Get the restore window id. |
| window2 = restore_app_browser->window()->GetNativeWindow(); |
| int32_t restore_window_id = |
| window2->GetProperty(::app_restore::kRestoreWindowIdKey); |
| |
| EXPECT_EQ(window_id2, restore_window_id); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| WindowProperties) { |
| Browser* app_browser = LaunchHelpSystemWebApp(); |
| ASSERT_TRUE(app_browser); |
| ASSERT_NE(browser(), app_browser); |
| |
| // Get the window id. |
| aura::Window* window = app_browser->window()->GetNativeWindow(); |
| int32_t window_id = window->GetProperty(::app_restore::kWindowIdKey); |
| |
| // Snap |window| to the left and store its window properties. |
| // TODO(sammiequon): Store and check desk id and restore bounds. |
| auto* window_state = WindowState::Get(window); |
| const WindowSnapWMEvent left_snap_event(WM_EVENT_SNAP_PRIMARY); |
| window_state->OnWMEvent(&left_snap_event); |
| const chromeos::WindowStateType pre_save_state_type = |
| window_state->GetStateType(); |
| EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped, pre_save_state_type); |
| const gfx::Rect pre_save_bounds = window->GetBoundsInScreen(); |
| |
| CreateAndSaveWindowInfo(window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| // Close |app_browser| so that the SWA can be relaunched. |
| web_app::CloseAndWait(app_browser); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| // Get the restored browser for the system web app. |
| Browser* restore_app_browser = GetBrowserForWindowId(window_id); |
| ASSERT_TRUE(restore_app_browser); |
| ASSERT_NE(browser(), restore_app_browser); |
| |
| // Get the restored browser's window. |
| window = restore_app_browser->window()->GetNativeWindow(); |
| ASSERT_EQ(window_id, window->GetProperty(::app_restore::kRestoreWindowIdKey)); |
| |
| // Check that |window|'s properties match the one's we stored. |
| EXPECT_EQ(pre_save_bounds, window->GetBoundsInScreen()); |
| window_state = WindowState::Get(window); |
| EXPECT_EQ(pre_save_state_type, window_state->GetStateType()); |
| |
| // Verify that |window_state| has viable restore bounds for when the user |
| // wants to return to normal window show state. Regression test for |
| // https://crbug.com/1188986. |
| EXPECT_TRUE(window_state->HasRestoreBounds()); |
| } |
| |
| // Tests that apps maintain splitview snap status after being relaunched with |
| // full restore. |
| IN_PROC_BROWSER_TEST_P(FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest, |
| TabletSplitView) { |
| TabletMode::Get()->SetEnabledForTest(true); |
| |
| Browser* app1_browser = LaunchHelpSystemWebApp(); |
| Browser* app2_browser = LaunchMediaSystemWebApp(); |
| |
| aura::Window* app1_window = app1_browser->window()->GetNativeWindow(); |
| aura::Window* app2_window = app2_browser->window()->GetNativeWindow(); |
| |
| SplitViewTestApi split_view_test_api; |
| split_view_test_api.SnapWindow(app1_window, ash::SnapPosition::kPrimary); |
| split_view_test_api.SnapWindow(app2_window, ash::SnapPosition::kSecondary); |
| ASSERT_EQ(app1_window, split_view_test_api.GetPrimaryWindow()); |
| ASSERT_EQ(app2_window, split_view_test_api.GetSecondaryWindow()); |
| |
| const int32_t app1_id = app1_window->GetProperty(::app_restore::kWindowIdKey); |
| const int32_t app2_id = app2_window->GetProperty(::app_restore::kWindowIdKey); |
| |
| CreateAndSaveWindowInfo(app1_window); |
| CreateAndSaveWindowInfo(app2_window); |
| AppLaunchInfoSaveWaiter::Wait(); |
| |
| // Create FullRestoreAppLaunchHandler. |
| auto app_launch_handler = |
| std::make_unique<FullRestoreAppLaunchHandler>(profile()); |
| |
| // Close `app1_browser` and `app2_browser` so that the SWA can be relaunched. |
| web_app::CloseAndWait(app1_browser); |
| web_app::CloseAndWait(app2_browser); |
| |
| SetShouldRestore(app_launch_handler.get()); |
| |
| aura::Window* restore_app1_window = nullptr; |
| aura::Window* restore_app2_window = nullptr; |
| |
| // Find the restored app windows in the browser list. |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| aura::Window* native_window = browser->window()->GetNativeWindow(); |
| if (native_window->GetProperty(::app_restore::kRestoreWindowIdKey) == |
| app1_id) { |
| restore_app1_window = native_window; |
| } |
| if (native_window->GetProperty(::app_restore::kRestoreWindowIdKey) == |
| app2_id) { |
| restore_app2_window = native_window; |
| } |
| } |
| |
| ASSERT_TRUE(restore_app1_window); |
| ASSERT_TRUE(restore_app2_window); |
| EXPECT_EQ(restore_app1_window, split_view_test_api.GetPrimaryWindow()); |
| EXPECT_EQ(restore_app2_window, split_view_test_api.GetSecondaryWindow()); |
| } |
| |
| INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( |
| FullRestoreAppLaunchHandlerSystemWebAppsBrowserTest); |
| |
| } // namespace ash::full_restore |