| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include <list> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/ash/crostini/crostini_util.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_browsertest_base.h" |
| #include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/defaults.h" |
| #include "chrome/browser/prefs/session_startup_pref.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sessions/session_restore_test_helper.h" |
| #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/web_applications/web_app_helpers.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/test_launcher_utils.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/test_utils.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/wm/core/wm_core_switches.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "ash/public/cpp/autotest_desks_api.h" |
| #include "chromeos/ui/wm/desks/desks_helper.h" |
| #endif |
| |
| #if defined(USE_AURA) |
| #include "ui/aura/client/aura_constants.h" |
| #endif |
| |
| namespace { |
| const char* test_app_name1 = "TestApp1"; |
| const char* test_app_name2 = "TestApp2"; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // Activates the desk at |index| and waits for its async operations to complete. |
| void SwitchToDesk(int index) { |
| base::RunLoop run_loop; |
| ASSERT_TRUE(ash::AutotestDesksApi().ActivateDeskAtIndex( |
| index, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| |
| // Removes all the inactive desks and waits for their async operations to |
| // complete. |
| void RemoveInactiveDesks() { |
| const int kMaxDeskRemovalTries = 100; |
| for (int i = 0; i < kMaxDeskRemovalTries; ++i) { |
| base::RunLoop run_loop; |
| if (!ash::AutotestDesksApi().RemoveActiveDesk(run_loop.QuitClosure())) |
| return; |
| run_loop.Run(); |
| } |
| // This should not be reached. |
| ADD_FAILURE(); |
| } |
| #endif |
| |
| } // namespace |
| |
| class SessionRestoreTestChromeOS : public InProcessBrowserTest { |
| public: |
| SessionRestoreTestChromeOS() |
| : faster_animations_( |
| ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {} |
| ~SessionRestoreTestChromeOS() override {} |
| |
| protected: |
| void SetUpDefaultCommandLine(base::CommandLine* command_line) override { |
| base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM); |
| InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line); |
| |
| // Animations have caused crashes in session restore in the past but are |
| // usually disabled in tests. Remove --wm-window-animations-disabled to |
| // re-enable animations. |
| test_launcher_utils::RemoveCommandLineSwitch( |
| default_command_line, wm::switches::kWindowAnimationsDisabled, |
| command_line); |
| } |
| |
| Browser* CreateBrowserWithParams(Browser::CreateParams params) { |
| Browser* browser = Browser::Create(params); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| |
| Browser::CreateParams CreateParamsForApp(const std::string& name, |
| bool trusted) { |
| return Browser::CreateParams::CreateForApp(name, trusted, gfx::Rect(), |
| profile(), true); |
| } |
| |
| Browser::CreateParams CreateParamsForAppPopup(const std::string& name, |
| bool trusted) { |
| return Browser::CreateParams::CreateForAppPopup(name, trusted, gfx::Rect(), |
| profile(), true); |
| } |
| |
| // Turn on session restore before we restart. |
| void TurnOnSessionRestore() { |
| SessionStartupPref::SetStartupPref( |
| browser()->profile(), SessionStartupPref(SessionStartupPref::LAST)); |
| } |
| |
| Profile* profile() { return browser()->profile(); } |
| |
| private: |
| ui::ScopedAnimationDurationScaleMode faster_animations_; |
| }; |
| |
| // Thse tests are in pairs. The PRE_ test creates some browser windows and |
| // the following test confirms that the correct windows are restored after a |
| // restart. |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreBrowserWindows) { |
| // One browser window is always created by default. |
| EXPECT_TRUE(browser()); |
| // Create a second normal browser window. |
| CreateBrowserWithParams(Browser::CreateParams(profile(), true)); |
| // Create a third incognito browser window which should not get restored. |
| CreateBrowserWithParams(Browser::CreateParams( |
| profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), true)); |
| TurnOnSessionRestore(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, RestoreBrowserWindows) { |
| size_t total_count = 0; |
| size_t incognito_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->profile()->IsOffTheRecord()) |
| ++incognito_count; |
| } |
| EXPECT_EQ(2u, total_count); |
| EXPECT_EQ(0u, incognito_count); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // Assigns three browser windows to three different desks. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, |
| PRE_RestoreBrowserWindowsToDesks) { |
| // Create two more desks so we have three desks in total. |
| ash::AutotestDesksApi().CreateNewDesk(); |
| ash::AutotestDesksApi().CreateNewDesk(); |
| |
| // A browser window is always created to the current desk, which |
| // is the first desk by default. |
| EXPECT_TRUE(browser()); |
| browser()->SetWindowUserTitle("0"); |
| |
| // Create a second normal browser window in the second desk by |
| // setting window workspace property. |
| SwitchToDesk(1); |
| Browser* browser_desk1 = |
| CreateBrowserWithParams(Browser::CreateParams(profile(), true)); |
| browser_desk1->SetWindowUserTitle("1"); |
| browser_desk1->window()->GetNativeWindow()->SetProperty( |
| aura::client::kWindowWorkspaceKey, 1); |
| |
| // Create a third normal browser window in the third desk |
| // specified with params.initial_workspace. |
| SwitchToDesk(2); |
| Browser::CreateParams browser_desk2_params = |
| Browser::CreateParams(profile(), true); |
| browser_desk2_params.initial_workspace = "2"; |
| Browser* browser_desk2 = CreateBrowserWithParams(browser_desk2_params); |
| browser_desk2->SetWindowUserTitle("2"); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| // Verifies that three windows restored to their right desk after restored. Also |
| // verifies that the fourth window is visible on all desks after being restored. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, |
| RestoreBrowserWindowsToDesks) { |
| auto* browser_list = BrowserList::GetInstance(); |
| ASSERT_EQ(3u, browser_list->size()); |
| |
| // The first, second and third browser should restore to the first, second |
| // and third desk, consecutively. |
| for (int i = 0; i < 3; i++) { |
| auto* browser = browser_list->get(i); |
| int desk_index = 0; |
| ASSERT_TRUE(base::StringToInt(browser->initial_workspace(), &desk_index)); |
| // Verify that browser i_th with title i, has initial_workspace equals to |
| // desk i_th. |
| ASSERT_EQ(i, desk_index); |
| ASSERT_EQ(base::NumberToString(i), browser->user_title()); |
| |
| // Check that a browser window is restored to the right desk i_th. |
| ASSERT_TRUE(ash::AutotestDesksApi().IsWindowInDesk( |
| browser->window()->GetNativeWindow(), desk_index)); |
| int workspace = browser->window()->GetNativeWindow()->GetProperty( |
| aura::client::kWindowWorkspaceKey); |
| ASSERT_EQ(desk_index, |
| workspace == aura::client::kWindowWorkspaceUnassignedWorkspace |
| ? 0 |
| : workspace); |
| } |
| |
| RemoveInactiveDesks(); |
| } |
| |
| // Assigns a browser window to all desks. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, |
| PRE_RestoreAllDesksBrowserWindow) { |
| // Create two desks so we have three in total. |
| ash::AutotestDesksApi().CreateNewDesk(); |
| ash::AutotestDesksApi().CreateNewDesk(); |
| |
| // Create a browser that is visible on all desks. |
| Browser::CreateParams visible_on_all_desks_browser_params = |
| Browser::CreateParams(profile(), true); |
| visible_on_all_desks_browser_params.initial_visible_on_all_workspaces_state = |
| true; |
| Browser* visible_on_all_desks_browser = |
| CreateBrowserWithParams(visible_on_all_desks_browser_params); |
| |
| // Ensure the visible on all desks browser has the right properties. |
| auto* visible_on_all_desks_window = |
| visible_on_all_desks_browser->window()->GetNativeWindow(); |
| ASSERT_TRUE(visible_on_all_desks_window->GetProperty( |
| aura::client::kWindowWorkspaceKey) == |
| aura::client::kWindowWorkspaceVisibleOnAllWorkspaces); |
| ASSERT_TRUE(chromeos::DesksHelper::Get(visible_on_all_desks_window) |
| ->BelongsToActiveDesk(visible_on_all_desks_window)); |
| |
| // Check that there are two browsers, the default one and the visible on all |
| // desks browser. |
| auto* browser_list = BrowserList::GetInstance(); |
| ASSERT_EQ(2u, browser_list->size()); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| // Verifies that the visible on all desks browser window is restore properly. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, |
| RestoreAllDesksBrowserWindow) { |
| // There should be two browsers restored, the default browser and the all |
| // desks browser. |
| auto* browser_list = BrowserList::GetInstance(); |
| ASSERT_EQ(2u, browser_list->size()); |
| |
| // Check that the visible on all desks browser is restored properly. |
| auto* visible_on_all_desks_browser = browser_list->get(1); |
| auto* visible_on_all_desks_window = |
| visible_on_all_desks_browser->window()->GetNativeWindow(); |
| |
| EXPECT_EQ("", visible_on_all_desks_browser->initial_workspace()); |
| |
| EXPECT_TRUE(visible_on_all_desks_window->GetProperty( |
| aura::client::kWindowWorkspaceKey) == |
| aura::client::kWindowWorkspaceVisibleOnAllWorkspaces); |
| // Visible on all desks windows should always reside on the active desk, |
| // even if there is a desk switch. |
| EXPECT_TRUE(chromeos::DesksHelper::Get(visible_on_all_desks_window) |
| ->BelongsToActiveDesk(visible_on_all_desks_window)); |
| |
| RemoveInactiveDesks(); |
| } |
| #endif |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreAppsV1) { |
| // Create a trusted app. |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name1, true)); |
| // Create a second trusted app with two windows. |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name2, true)); |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name2, true)); |
| // Create a third untrusted (child) app3. This should not get restored. |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name2, false)); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, RestoreAppsV1) { |
| size_t total_count = 0; |
| size_t app1_count = 0; |
| size_t app2_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->app_name() == test_app_name1) |
| ++app1_count; |
| if (browser->app_name() == test_app_name2) |
| ++app2_count; |
| } |
| EXPECT_EQ(1u, app1_count); |
| EXPECT_EQ(2u, app2_count); // Only the trusted app windows are restored. |
| EXPECT_EQ(4u, total_count); // Default browser() + 3 app windows |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreAppsPopup) { |
| // Create a trusted app popup. |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name1, true)); |
| // Create a second trusted app popup with two windows. |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name2, true)); |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name2, true)); |
| // Create a third untrusted (child) app popup. This should not get restored. |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name2, false)); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, RestoreAppsPopup) { |
| size_t total_count = 0; |
| size_t app1_count = 0; |
| size_t app2_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->app_name() == test_app_name1) |
| ++app1_count; |
| if (browser->app_name() == test_app_name2) |
| ++app2_count; |
| } |
| EXPECT_EQ(1u, app1_count); |
| EXPECT_EQ(2u, app2_count); // Only the trusted app windows are restored. |
| EXPECT_EQ(4u, total_count); // Default browser() + 3 app windows |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreNoDevtools) { |
| // Create devtools. |
| CreateBrowserWithParams(Browser::CreateParams::CreateForDevTools(profile())); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, RestoreNoDevtools) { |
| size_t total_count = 0; |
| size_t devtools_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->is_type_devtools()) |
| ++devtools_count; |
| } |
| EXPECT_EQ(1u, total_count); |
| EXPECT_EQ(0u, devtools_count); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreMaximized) { |
| // One browser window is always created by default. |
| ASSERT_TRUE(browser()); |
| // Create a second browser window and maximize it. |
| Browser* browser2 = |
| CreateBrowserWithParams(Browser::CreateParams(profile(), true)); |
| browser2->window()->Maximize(); |
| |
| // Create two app windows and maximize the second one. |
| Browser* app_browser1 = |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name1, true)); |
| Browser* app_browser2 = |
| CreateBrowserWithParams(CreateParamsForApp(test_app_name2, true)); |
| app_browser2->window()->Maximize(); |
| |
| // Create two app popup windows and maximize the second one. |
| Browser* app_popup_browser1 = |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name1, true)); |
| Browser* app_popup_browser2 = |
| CreateBrowserWithParams(CreateParamsForAppPopup(test_app_name2, true)); |
| app_popup_browser2->window()->Maximize(); |
| |
| EXPECT_FALSE(browser()->window()->IsMaximized()); |
| EXPECT_TRUE(browser2->window()->IsMaximized()); |
| EXPECT_FALSE(app_browser1->window()->IsMaximized()); |
| EXPECT_TRUE(app_browser2->window()->IsMaximized()); |
| EXPECT_FALSE(app_popup_browser1->window()->IsMaximized()); |
| EXPECT_TRUE(app_popup_browser2->window()->IsMaximized()); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| // https://crbug.com/1216209 |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, DISABLED_RestoreMaximized) { |
| size_t total_count = 0; |
| size_t app1_maximized_count = 0; |
| size_t app2_maximized_count = 0; |
| size_t total_maximized_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->window()->IsMaximized()) { |
| ++total_maximized_count; |
| if (browser->app_name() == test_app_name1) |
| ++app1_maximized_count; |
| if (browser->app_name() == test_app_name2) |
| ++app2_maximized_count; |
| } |
| } |
| EXPECT_EQ(6u, total_count); |
| EXPECT_EQ(0u, app1_maximized_count); |
| EXPECT_EQ(2u, app2_maximized_count); // One TYPE_APP + One TYPE_APP_POPUP |
| EXPECT_EQ(3u, total_maximized_count); |
| } |
| |
| // Test for crash when restoring minimized windows. http://crbug.com/679513. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, PRE_RestoreMinimized) { |
| // One browser window is always created by default. |
| ASSERT_TRUE(browser()); |
| browser()->window()->Minimize(); |
| |
| Browser* browser2 = |
| CreateBrowserWithParams(Browser::CreateParams(profile(), true)); |
| browser2->window()->Minimize(); |
| |
| EXPECT_TRUE(browser()->window()->IsMinimized()); |
| EXPECT_TRUE(browser2->window()->IsMinimized()); |
| |
| TurnOnSessionRestore(); |
| } |
| |
| // https://crbug.com/1216209 |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTestChromeOS, DISABLED_RestoreMinimized) { |
| size_t total_count = 0; |
| size_t minimized_count = 0; |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| ++total_count; |
| if (browser->window()->IsMinimized()) |
| ++minimized_count; |
| } |
| EXPECT_EQ(2u, total_count); |
| // Chrome OS always activates the last browser windows on login to remind |
| // users they have a browser running instead of just showing them an empty |
| // desktop. |
| EXPECT_NE(2u, minimized_count); |
| } |
| |
| class SystemWebAppSessionRestoreTestChromeOS |
| : public TestProfileTypeMixin<ash::SystemWebAppBrowserTestBase> { |
| public: |
| SystemWebAppSessionRestoreTestChromeOS() { |
| auto installation = |
| ash::TestSystemWebAppInstallation::SetUpStandaloneSingleWindowApp(); |
| installation->set_update_policy( |
| ash::SystemWebAppManager::UpdatePolicy::kOnVersionChange); |
| SetSystemWebAppInstallation(std::move(installation)); |
| } |
| |
| ~SystemWebAppSessionRestoreTestChromeOS() override = default; |
| |
| protected: |
| SessionRestoreTestHelper waiter_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(SystemWebAppSessionRestoreTestChromeOS, |
| PRE_OmitSystemWebApps) { |
| // Wait for the app to install, launch, and load, otherwise the app might not |
| // be restored. |
| WaitForTestSystemAppInstall(); |
| LaunchApp(GetAppType()); |
| |
| // Should have one SWA window and one default browser window. |
| EXPECT_TRUE(ash::FindSystemWebAppBrowser(browser()->profile(), GetAppType())); |
| EXPECT_EQ(2u, BrowserList::GetInstance()->size()); |
| |
| SessionStartupPref::SetStartupPref( |
| browser()->profile(), SessionStartupPref(SessionStartupPref::LAST)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SystemWebAppSessionRestoreTestChromeOS, |
| OmitSystemWebApps) { |
| waiter_.Wait(); |
| |
| // Should have only one default browser window. |
| // |
| // Session restore doesn't go through system web app launch path, so system |
| // web app utils like `FindSystemWebAppBrowser` might not recognize such |
| // windows as a SWA browser window. Therefore we count the number of browser |
| // windows here instead of trying to find one. |
| EXPECT_EQ(1u, BrowserList::GetInstance()->size()); |
| } |
| |
| INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P( |
| SystemWebAppSessionRestoreTestChromeOS); |