| // 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. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include <stddef.h> |
| |
| #include <array> |
| #include <set> |
| #include <vector> |
| |
| #include "base/base_switches.h" |
| #include "base/command_line.h" |
| #include "base/containers/adapters.h" |
| #include "base/containers/span.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/path_service.h" |
| #include "base/process/launch.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/buildflags.h" |
| #include "chrome/browser/collaboration/messaging/messaging_backend_service_factory.h" |
| #include "chrome/browser/defaults.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/prefs/session_startup_pref.h" |
| #include "chrome/browser/preloading/scoped_prewarm_feature_list.h" |
| #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" |
| #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/profiles/profile_window.h" |
| #include "chrome/browser/resource_coordinator/session_restore_policy.h" |
| #include "chrome/browser/resource_coordinator/tab_manager_features.h" |
| #include "chrome/browser/sessions/app_session_service.h" |
| #include "chrome/browser/sessions/app_session_service_factory.h" |
| #include "chrome/browser/sessions/app_session_service_test_helper.h" |
| #include "chrome/browser/sessions/exit_type_service.h" |
| #include "chrome/browser/sessions/session_restore_test_helper.h" |
| #include "chrome/browser/sessions/session_restore_test_utils.h" |
| #include "chrome/browser/sessions/session_service.h" |
| #include "chrome/browser/sessions/session_service_factory.h" |
| #include "chrome/browser/sessions/session_service_log.h" |
| #include "chrome/browser/sessions/session_service_test_helper.h" |
| #include "chrome/browser/sessions/tab_loader_delegate.h" |
| #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| #include "chrome/browser/tab_contents/web_contents_collection.h" |
| #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_navigator.h" |
| #include "chrome/browser/ui/browser_navigator_params.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/startup/startup_tab.h" |
| #include "chrome/browser/ui/startup/startup_types.h" |
| #include "chrome/browser/ui/tabs/split_tab_metrics.h" |
| #include "chrome/browser/ui/tabs/tab_enums.h" |
| #include "chrome/browser/ui/tabs/tab_group_model.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/web_applications/app_browser_controller.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" |
| #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h" |
| #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" |
| #include "chrome/browser/web_applications/web_app_provider.h" |
| #include "chrome/browser/web_applications/web_app_registrar.h" |
| #include "chrome/browser/web_applications/web_app_tab_helper.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/collaboration/public/messaging/empty_messaging_backend_service.h" |
| #include "components/keep_alive_registry/keep_alive_types.h" |
| #include "components/keep_alive_registry/scoped_keep_alive.h" |
| #include "components/memory_pressure/fake_memory_pressure_monitor.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/saved_tab_groups/internal/saved_tab_group_model.h" |
| #include "components/saved_tab_groups/public/features.h" |
| #include "components/saved_tab_groups/public/saved_tab_group.h" |
| #include "components/saved_tab_groups/public/tab_group_sync_service.h" |
| #include "components/saved_tab_groups/public/types.h" |
| #include "components/sessions/content/content_live_tab.h" |
| #include "components/sessions/content/content_test_helper.h" |
| #include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
| #include "components/sessions/core/session_constants.h" |
| #include "components/sessions/core/session_types.h" |
| #include "components/sessions/core/tab_restore_service.h" |
| #include "components/tab_groups/tab_group_color.h" |
| #include "components/tab_groups/tab_group_id.h" |
| #include "components/tab_groups/tab_group_visual_data.h" |
| #include "components/tabs/public/split_tab_data.h" |
| #include "components/tabs/public/tab_group.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/bindings_policy.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/download_test_observer.h" |
| #include "content/public/test/slow_http_response.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/cookies/canonical_cookie.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/mojom/cookie_manager.mojom.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" |
| #include "ui/base/page_transition_types.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/gfx/color_palette.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "base/apple/scoped_nsautorelease_pool.h" |
| #endif |
| |
| #if defined(USE_AURA) |
| #include "ui/aura/window.h" |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "ash/constants/web_app_id_constants.h" |
| #include "base/json/json_reader.h" |
| #include "chrome/browser/web_applications/test/web_app_test_observers.h" |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| using sessions::ContentTestHelper; |
| using sessions::SerializedNavigationEntry; |
| using sessions::SerializedNavigationEntryTestHelper; |
| |
| GURL GetUrl1() { |
| return ui_test_utils::GetTestUrl( |
| base::FilePath().AppendASCII("session_history"), |
| base::FilePath().AppendASCII("bot1.html")); |
| } |
| |
| GURL GetUrl2() { |
| return ui_test_utils::GetTestUrl( |
| base::FilePath().AppendASCII("session_history"), |
| base::FilePath().AppendASCII("bot2.html")); |
| } |
| |
| GURL GetUrl3() { |
| return ui_test_utils::GetTestUrl( |
| base::FilePath().AppendASCII("session_history"), |
| base::FilePath().AppendASCII("bot3.html")); |
| } |
| |
| bool WaitForTabToLoad(Browser* browser, int index) { |
| if (index >= browser->tab_strip_model()->count()) |
| return false; |
| content::WebContents* contents = |
| browser->tab_strip_model()->GetWebContentsAt(index); |
| contents->GetController().LoadIfNecessary(); |
| return content::WaitForLoadStop(contents); |
| } |
| |
| void WaitForTabsToLoad(Browser* browser) { |
| for (int i = 0; i < browser->tab_strip_model()->count(); ++i) |
| EXPECT_TRUE(WaitForTabToLoad(browser, i)); |
| } |
| |
| class SessionRestoreTest : public InProcessBrowserTest { |
| public: |
| SessionRestoreTest() { |
| dependency_manager_subscription_ = |
| BrowserContextDependencyManager::GetInstance() |
| ->RegisterCreateServicesCallbackForTesting( |
| base::BindRepeating(&SessionRestoreTest::RegisterFakeServices, |
| base::Unretained(this))); |
| } |
| ~SessionRestoreTest() override = default; |
| |
| protected: |
| #if BUILDFLAG(IS_CHROMEOS) |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // TODO(nkostylev): Investigate if we can remove this switch. |
| command_line->AppendSwitch(switches::kCreateBrowserOnStartupForTests); |
| } |
| #endif |
| |
| void RegisterFakeServices(content::BrowserContext* context) { |
| collaboration::messaging::MessagingBackendServiceFactory::GetInstance() |
| ->SetTestingFactory( |
| context, base::BindRepeating([](content::BrowserContext* context) |
| -> std::unique_ptr<KeyedService> { |
| return std::make_unique< |
| collaboration::messaging::EmptyMessagingBackendService>(); |
| })); |
| } |
| |
| void SetUpOnMainThread() override { |
| active_browser_list_ = BrowserList::GetInstance(); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| const testing::TestInfo* const test_info = |
| testing::UnitTest::GetInstance()->current_test_info(); |
| if (strcmp(test_info->name(), "NoSessionRestoreNewWindowChromeOS") != 0) { |
| // Undo the effect of kBrowserAliveWithNoWindows in defaults.cc so that we |
| // can get these test to work without quitting. |
| SessionServiceTestHelper helper(browser()->profile()); |
| helper.SetForceBrowserNotAliveWithNoWindows(true); |
| } |
| #endif |
| if (browser()) { |
| SessionStartupPref pref(SessionStartupPref::LAST); |
| SessionStartupPref::SetStartupPref(browser()->profile(), pref); |
| } |
| } |
| |
| // Quit the supplied browser, restore it, then wait for any tabs whose URL is |
| // |target_page| to signal that their Javascript is running, via the DOM |
| // automation API. |
| // |
| // The implementation is a bit horrible: not all the WebContents created |
| // during session restore are of interest, so this function has to create |
| // DOMMessageQueues for all created WebContents, then decide after session |
| // restore is finished which of them it should actually look for messages |
| // from. |
| Browser* QuitBrowserRestoreAndWaitForTabJavascript(Browser* browser, |
| const GURL& target_page, |
| size_t expected_count) { |
| std::vector<std::unique_ptr<content::DOMMessageQueue>> queues; |
| auto subscription = content::RegisterWebContentsCreationCallback( |
| base::BindLambdaForTesting([&](content::WebContents* contents) { |
| queues.emplace_back( |
| std::make_unique<content::DOMMessageQueue>(contents)); |
| })); |
| |
| Browser* restored = QuitBrowserAndRestore(browser); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| |
| // Deliberately skip over any WebContents that didn't load |target_page|. |
| size_t ready = 0; |
| for (int i = 0; i < tab_strip_model->count(); i++) { |
| auto* contents = tab_strip_model->GetWebContentsAt(i); |
| if (contents->GetLastCommittedURL() == target_page) { |
| std::string message; |
| if (!queues[i]->WaitForMessage(&message) || message != "\"READY\"") { |
| ADD_FAILURE() << "WaitForMessage() failed or didn't return READY"; |
| return nullptr; |
| } |
| ready++; |
| } |
| } |
| |
| EXPECT_EQ(expected_count, ready); |
| |
| return restored; |
| } |
| |
| // Closes `browser` synchronously and creates a new Browser. |
| // `after_close_calback` is run after closing `browser` but before creating |
| // a new Browser. |
| Browser* QuitBrowserAndRestore( |
| Browser* browser, |
| const GURL& url = GURL(), |
| bool no_memory_pressure = true, |
| base::OnceClosure after_close_calback = base::DoNothing()) { |
| Profile* profile = browser->profile(); |
| |
| // Close the browser. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| CloseBrowserSynchronously(browser); |
| |
| std::move(after_close_calback).Run(); |
| |
| ui_test_utils::AllBrowserTabAddedWaiter tab_waiter; |
| SessionRestoreTestHelper restore_observer; |
| |
| // Ensure the session service factory is started, even if it was explicitly |
| // shut down. |
| SessionServiceTestHelper helper(profile); |
| helper.SetForceBrowserNotAliveWithNoWindows(true); |
| |
| // Ensure stale cookies are cleared immediately on startup for testing. |
| profile->GetDefaultStoragePartition() |
| ->OverrideDeleteStaleSessionOnlyCookiesDelayForTesting( |
| base::Minutes(0)); |
| |
| // Create a new window, which should trigger session restore. |
| if (url.is_empty()) { |
| chrome::NewEmptyWindow(profile); |
| } else { |
| NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK); |
| Navigate(¶ms); |
| } |
| |
| Browser* new_browser = chrome::FindBrowserWithTab(tab_waiter.Wait()); |
| |
| // Stop loading anything more if we are running out of space. |
| if (!no_memory_pressure) { |
| fake_memory_pressure_monitor_.SetAndNotifyMemoryPressure( |
| base::MemoryPressureMonitor::MemoryPressureLevel:: |
| MEMORY_PRESSURE_LEVEL_CRITICAL); |
| } |
| restore_observer.Wait(); |
| |
| if (no_memory_pressure) |
| WaitForTabsToLoad(new_browser); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| |
| return new_browser; |
| } |
| |
| void GoBack(Browser* browser) { |
| content::TestNavigationObserver observer( |
| browser->tab_strip_model()->GetActiveWebContents()); |
| chrome::GoBack(browser, WindowOpenDisposition::CURRENT_TAB); |
| observer.Wait(); |
| } |
| |
| void GoForward(Browser* browser) { |
| content::TestNavigationObserver observer( |
| browser->tab_strip_model()->GetActiveWebContents()); |
| chrome::GoForward(browser, WindowOpenDisposition::CURRENT_TAB); |
| observer.Wait(); |
| } |
| |
| void AssertOneWindowWithOneTab(Browser* browser) { |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(1, browser->tab_strip_model()->count()); |
| } |
| |
| int RenderProcessHostCount() { |
| content::RenderProcessHost::iterator hosts = |
| content::RenderProcessHost::AllHostsIterator(); |
| int count = 0; |
| while (!hosts.IsAtEnd()) { |
| content::RenderProcessHost* current_host = hosts.GetCurrentValue(); |
| |
| // Count live renderers but exclude spares. |
| if (current_host->IsInitializedAndNotDead() && !current_host->IsSpare()) { |
| count++; |
| } |
| |
| hosts.Advance(); |
| } |
| return count; |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| Profile* CreateSecondaryProfile(int profile_num) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| base::FilePath profile_path = profile_manager->user_data_dir(); |
| profile_path = profile_path.AppendASCII( |
| base::StringPrintf("New Profile %d", profile_num)); |
| Profile* profile = profile_manager->GetProfile(profile_path); |
| SessionStartupPref pref(SessionStartupPref::LAST); |
| SessionStartupPref::SetStartupPref(profile, pref); |
| return profile; |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| raw_ptr<const BrowserList> active_browser_list_ = nullptr; |
| |
| private: |
| // TODO(https://crbug.com/423465927): Explore a better approach to make the |
| // existing tests run with the prewarm feature enabled. |
| test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ |
| test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; |
| #if !BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| base::test::ScopedFeatureList scoped_feature_list_; |
| #endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| |
| memory_pressure::test::FakeMemoryPressureMonitor |
| fake_memory_pressure_monitor_; |
| |
| base::CallbackListSubscription dependency_manager_subscription_; |
| }; |
| |
| // Activates the smart restore behaviour. |
| class SmartSessionRestoreTest : public SessionRestoreTest { |
| public: |
| SmartSessionRestoreTest() = default; |
| |
| SmartSessionRestoreTest(const SmartSessionRestoreTest&) = delete; |
| SmartSessionRestoreTest& operator=(const SmartSessionRestoreTest&) = delete; |
| |
| protected: |
| static const size_t kExpectedNumTabs; |
| static const char* const kUrls[]; |
| |
| private: |
| testing::ScopedAlwaysLoadSessionRestoreTestPolicy test_policy_; |
| }; |
| |
| // static |
| const size_t SmartSessionRestoreTest::kExpectedNumTabs = 6; |
| // static |
| const char* const SmartSessionRestoreTest::kUrls[] = { |
| "https://google.com/1", "https://google.com/2", "https://google.com/3", |
| "https://google.com/4", "https://google.com/5", "https://google.com/6"}; |
| |
| // Restore session with url passed in command line. |
| class SessionRestoreWithURLInCommandLineTest : public SessionRestoreTest { |
| public: |
| SessionRestoreWithURLInCommandLineTest() = default; |
| |
| SessionRestoreWithURLInCommandLineTest( |
| const SessionRestoreWithURLInCommandLineTest&) = delete; |
| SessionRestoreWithURLInCommandLineTest& operator=( |
| const SessionRestoreWithURLInCommandLineTest&) = delete; |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| SessionRestoreTest::SetUpCommandLine(command_line); |
| command_line_url_ = ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title1.html"))); |
| command_line->AppendArg(command_line_url_.spec()); |
| } |
| |
| GURL command_line_url_; |
| }; |
| |
| // Verifies that restored tabs have a root window. This is important |
| // otherwise the wrong information is communicated to the renderer. |
| // (http://crbug.com/342672). |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoredTabsShouldHaveWindow) { |
| // Create tabs. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restart and session restore the tabs. |
| Browser* restored = QuitBrowserAndRestore(browser()); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| const int tabs = tab_strip_model->count(); |
| ASSERT_EQ(3, tabs); |
| |
| // Check the restored tabs have a window to get screen info from. |
| // On Aura it should also have a root window. |
| for (int i = 0; i < tabs; ++i) { |
| content::WebContents* contents = tab_strip_model->GetWebContentsAt(i); |
| EXPECT_TRUE(contents->GetTopLevelNativeWindow()); |
| #if defined(USE_AURA) |
| EXPECT_TRUE(contents->GetNativeView()->GetRootWindow()); |
| #endif |
| } |
| } |
| |
| // Verify that restored tabs have correct disposition. Only one tab should |
| // have "visible" visibility state, the rest should not. |
| // (http://crbug.com/155365 http://crbug.com/118269) |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| RestoredTabsHaveCorrectVisibilityState) { |
| // Create tabs. |
| GURL test_page(ui_test_utils::GetTestUrl( |
| base::FilePath(), |
| base::FilePath(FILE_PATH_LITERAL("tab-restore-visibility.html")))); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), test_page, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), test_page, WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restart and session restore the tabs. There should be three total tabs, two |
| // of which are the restored tabs from above and one of which is a new, blank |
| // tab. |
| Browser* restored = |
| QuitBrowserRestoreAndWaitForTabJavascript(browser(), test_page, 2); |
| ASSERT_TRUE(restored); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| ASSERT_EQ(3, tab_strip_model->count()); |
| |
| // The middle tab only should have visible disposition. |
| for (int i = 0; i < tab_strip_model->count(); ++i) { |
| content::WebContents* contents = tab_strip_model->GetWebContentsAt(i); |
| const char kGetStateJS[] = "window.document.visibilityState;"; |
| std::string document_visibility_state = |
| content::EvalJs(contents, kGetStateJS).ExtractString(); |
| if (i == 1) { |
| EXPECT_EQ("visible", document_visibility_state); |
| } else { |
| EXPECT_EQ("hidden", document_visibility_state); |
| } |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoredTabsHaveCorrectInitialSize) { |
| // Create tabs. |
| GURL test_page(ui_test_utils::GetTestUrl( |
| base::FilePath(), |
| base::FilePath(FILE_PATH_LITERAL("tab-restore-visibility.html")))); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), test_page, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), test_page, WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restart and session restore the tabs. |
| Browser* restored = |
| QuitBrowserRestoreAndWaitForTabJavascript(browser(), test_page, 2); |
| ASSERT_TRUE(restored); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| ASSERT_EQ(3, tab_strip_model->count()); |
| |
| const gfx::Size contents_size = restored->window()->GetContentsSize(); |
| for (int i = 0; i < tab_strip_model->count(); ++i) { |
| content::WebContents* contents = tab_strip_model->GetWebContentsAt(i); |
| const char kGetWidthJS[] = "window.innerWidth;"; |
| int width = content::EvalJs(contents, kGetWidthJS).ExtractInt(); |
| const char kGetHeigthJS[] = "window.innerHeight;"; |
| int height = content::EvalJs(contents, kGetHeigthJS).ExtractInt(); |
| const gfx::Size tab_size(width, height); |
| EXPECT_EQ(contents_size, tab_size); |
| } |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Verify that session restore does not occur when a user opens a browser window |
| // when no other browser windows are open on ChromeOS. |
| // TODO(pkotwicz): Add test which doesn't open incognito browser once |
| // disable-zero-browsers-open-for-tests is removed. |
| // (http://crbug.com/119175) |
| // TODO(pkotwicz): Mac should have the behavior outlined by this test. It should |
| // not do session restore if an incognito window is already open. |
| // (http://crbug.com/120927) |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, NoSessionRestoreNewWindowChromeOS) { |
| // When the full restore feature is enabled, session restore does occur when a |
| // user opens a browser window. So set the pref as default, open the New Tab |
| // page for this test, to verify that session restore does not occur. |
| Profile* profile = browser()->profile(); |
| SessionStartupPref current_pref = SessionStartupPref::GetStartupPref(profile); |
| SessionStartupPref pref(SessionStartupPref::DEFAULT); |
| SessionStartupPref::SetStartupPref(profile, pref); |
| |
| GURL url(ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title1.html")))); |
| |
| // Add a single tab. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| Browser* incognito_browser = CreateIncognitoBrowser(); |
| chrome::AddTabAt(incognito_browser, GURL(), -1, true); |
| incognito_browser->window()->Show(); |
| |
| // Close the normal browser. After this we only have the incognito window |
| // open. |
| CloseBrowserSynchronously(browser()); |
| |
| // Create a new window, which should open NTP. |
| ui_test_utils::BrowserChangeObserver new_browser_observer( |
| nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded); |
| chrome::NewWindow(incognito_browser); |
| Browser* new_browser = new_browser_observer.Wait(); |
| ui_test_utils::WaitUntilBrowserBecomeActive(new_browser); |
| EXPECT_NE(new_browser, incognito_browser); |
| |
| ASSERT_TRUE(new_browser); |
| EXPECT_EQ(1, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ(GURL(chrome::kChromeUINewTabURL), |
| new_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| } |
| |
| // Test that maximized applications get restored maximized. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, MaximizedApps) { |
| const char* app_name = "TestApp"; |
| Browser* app_browser = CreateBrowserForApp(app_name, browser()->profile()); |
| app_browser->window()->Maximize(); |
| app_browser->window()->Show(); |
| EXPECT_TRUE(app_browser->window()->IsMaximized()); |
| EXPECT_TRUE(app_browser->is_type_app()); |
| |
| // Close the normal browser. After this we only have the app_browser window. |
| CloseBrowserSynchronously(browser()); |
| |
| // Create a new window, which should open NTP. |
| chrome::NewWindow(app_browser); |
| Browser* new_browser = ui_test_utils::WaitForBrowserToOpen(); |
| |
| ASSERT_TRUE(new_browser); |
| EXPECT_TRUE(app_browser->window()->IsMaximized()); |
| EXPECT_TRUE(app_browser->is_type_app()); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Creates a tabbed browser and popup and makes sure we restore both. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, NormalAndPopup) { |
| // Open a popup. |
| Browser* popup = CreateBrowserForPopup(browser()->profile()); |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| |
| // Simulate an exit by shutting down the session service. If we don't do this |
| // the first window close is treated as though the user closed the window |
| // and won't be restored. |
| SessionServiceFactory::ShutdownForProfile(browser()->profile()); |
| |
| // Restart and make sure we have two windows. |
| CloseBrowserSynchronously(popup); |
| QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| EXPECT_EQ(Browser::TYPE_NORMAL, active_browser_list_->get(0)->type()); |
| EXPECT_EQ(Browser::TYPE_POPUP, active_browser_list_->get(1)->type()); |
| } |
| |
| // Flaky on Mac. https://crbug.com/1334914. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RestoreIndividualTabFromWindow \ |
| DISABLED_RestoreIndividualTabFromWindow |
| #else |
| #define MAYBE_RestoreIndividualTabFromWindow RestoreIndividualTabFromWindow |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| MAYBE_RestoreIndividualTabFromWindow) { |
| GURL url1(ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title1.html")))); |
| // Any page that will yield a 200 status code will work here. |
| GURL url2(chrome::kChromeUIVersionURL); |
| GURL url3(ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title3.html")))); |
| |
| // Add and navigate three tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1)); |
| { |
| content::CreateAndLoadWebContentsObserver observer; |
| chrome::AddSelectedTabWithURL(browser(), url2, ui::PAGE_TRANSITION_LINK); |
| observer.Wait(); |
| } |
| { |
| content::CreateAndLoadWebContentsObserver observer; |
| chrome::AddSelectedTabWithURL(browser(), url3, ui::PAGE_TRANSITION_LINK); |
| observer.Wait(); |
| } |
| |
| sessions::TabRestoreService* service = |
| TabRestoreServiceFactory::GetForProfile(browser()->profile()); |
| service->ClearEntries(); |
| |
| browser()->window()->Close(); |
| |
| // Expect a window with three tabs. |
| ASSERT_EQ(1U, service->entries().size()); |
| ASSERT_EQ(sessions::tab_restore::Type::WINDOW, |
| service->entries().front()->type); |
| auto* window = static_cast<sessions::tab_restore::Window*>( |
| service->entries().front().get()); |
| EXPECT_EQ(3U, window->tabs.size()); |
| |
| // Find the SessionID for entry2. Since the session service was destroyed, |
| // there is no guarantee that the SessionID for the tab has remained the same. |
| base::Time timestamp; |
| int http_status_code = 0; |
| ui_test_utils::BrowserChangeObserver browser_change_observer( |
| nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded); |
| for (const auto& tab_ptr : window->tabs) { |
| const sessions::tab_restore::Tab& tab = *tab_ptr; |
| // If this tab held url2, then restore this single tab. |
| if (tab.navigations[0].virtual_url() == url2) { |
| timestamp = tab.navigations[0].timestamp(); |
| http_status_code = tab.navigations[0].http_status_code(); |
| std::vector<sessions::LiveTab*> content = service->RestoreEntryById( |
| nullptr, tab.id, WindowOpenDisposition::UNKNOWN); |
| ASSERT_EQ(1U, content.size()); |
| sessions::ContentLiveTab* live_tab = |
| static_cast<sessions::ContentLiveTab*>(content[0]); |
| ASSERT_TRUE(live_tab); |
| EXPECT_EQ(url2, live_tab->web_contents()->GetURL()); |
| break; |
| } |
| } |
| EXPECT_FALSE(timestamp.is_null()); |
| EXPECT_EQ(200, http_status_code); |
| |
| // Make sure that the restored tab is removed from the service. |
| ASSERT_EQ(1U, service->entries().size()); |
| ASSERT_EQ(sessions::tab_restore::Type::WINDOW, |
| service->entries().front()->type); |
| window = static_cast<sessions::tab_restore::Window*>( |
| service->entries().front().get()); |
| EXPECT_EQ(2U, window->tabs.size()); |
| |
| Browser* restored_browser = browser_change_observer.Wait(); |
| // Make sure that the restored tab was restored with the correct |
| // timestamp and status code. |
| content::WebContents* contents = |
| restored_browser->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| content::NavigationEntry* entry = |
| contents->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| EXPECT_EQ(timestamp, entry->GetTimestamp()); |
| EXPECT_EQ(http_status_code, entry->GetHttpStatusCode()); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // This test does not apply to ChromeOS as ChromeOS does not do session |
| // restore when a new window is open. |
| |
| // Verifies we remember the last browser window when closing the last |
| // non-incognito window while an incognito window is open. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, IncognitotoNonIncognito) { |
| GURL url(ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title1.html")))); |
| |
| // Add a single tab. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Create a new incognito window. |
| Browser* incognito_browser = CreateIncognitoBrowser(); |
| chrome::AddTabAt(incognito_browser, GURL(), -1, true); |
| incognito_browser->window()->Show(); |
| |
| // Close the normal browser. After this we only have the incognito window |
| // open. |
| CloseBrowserSynchronously(browser()); |
| |
| // Create a new window, which should trigger session restore. |
| chrome::NewWindow(incognito_browser); |
| Browser* new_browser = ui_test_utils::WaitForBrowserToOpen(); |
| |
| // The first tab should have 'url' as its url. |
| ASSERT_TRUE(new_browser); |
| EXPECT_EQ(url, new_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| namespace { |
| |
| // Verifies that the given NavigationController has exactly two |
| // entries that correspond to the given URLs and that all entries have non-null |
| // timestamps. |
| void VerifyNavigationEntries(content::NavigationController& controller, |
| GURL url1, |
| GURL url2) { |
| ASSERT_EQ(2, controller.GetEntryCount()); |
| EXPECT_EQ(1, controller.GetCurrentEntryIndex()); |
| EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL()); |
| EXPECT_EQ(url2, controller.GetEntryAtIndex(1)->GetURL()); |
| EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null()); |
| EXPECT_FALSE(controller.GetEntryAtIndex(1)->GetTimestamp().is_null()); |
| } |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreForeignTab) { |
| GURL url1("https://google.com"); |
| GURL url2("https://google2.com"); |
| |
| // Set up the restore data. |
| sessions::SessionTab tab; |
| tab.tab_visual_index = 0; |
| tab.current_navigation_index = 1; |
| tab.pinned = false; |
| tab.navigations.push_back( |
| ContentTestHelper::CreateNavigation(url1.spec(), "one")); |
| tab.navigations.push_back( |
| ContentTestHelper::CreateNavigation(url2.spec(), "two")); |
| |
| for (size_t i = 0; i < tab.navigations.size(); ++i) { |
| ASSERT_FALSE(tab.navigations[i].timestamp().is_null()); |
| tab.navigations[i].set_index(i); |
| } |
| |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| |
| // Restore in the current tab. |
| content::WebContents* tab_content = nullptr; |
| { |
| content::CreateAndLoadWebContentsObserver observer; |
| tab_content = SessionRestore::RestoreForeignSessionTab( |
| browser()->tab_strip_model()->GetActiveWebContents(), tab, |
| WindowOpenDisposition::CURRENT_TAB); |
| observer.Wait(); |
| } |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetWebContentsAt(0); |
| VerifyNavigationEntries(web_contents->GetController(), url1, url2); |
| ASSERT_TRUE(web_contents->GetUserAgentOverride().ua_string_override.empty()); |
| ASSERT_TRUE(tab_content); |
| ASSERT_EQ(url2, tab_content->GetURL()); |
| |
| // Restore in a new tab. |
| tab_content = nullptr; |
| { |
| content::CreateAndLoadWebContentsObserver observer; |
| tab_content = SessionRestore::RestoreForeignSessionTab( |
| browser()->tab_strip_model()->GetActiveWebContents(), tab, |
| WindowOpenDisposition::NEW_BACKGROUND_TAB); |
| observer.Wait(); |
| } |
| ASSERT_EQ(2, browser()->tab_strip_model()->count()); |
| ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); |
| web_contents = browser()->tab_strip_model()->GetWebContentsAt(1); |
| VerifyNavigationEntries(web_contents->GetController(), url1, url2); |
| ASSERT_TRUE(web_contents->GetUserAgentOverride().ua_string_override.empty()); |
| ASSERT_TRUE(tab_content); |
| ASSERT_EQ(url2, tab_content->GetURL()); |
| |
| // Restore in a new window. |
| Browser* new_browser = nullptr; |
| tab_content = nullptr; |
| { |
| content::CreateAndLoadWebContentsObserver observer; |
| ui_test_utils::BrowserChangeObserver new_browser_observer( |
| nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded); |
| tab_content = SessionRestore::RestoreForeignSessionTab( |
| browser()->tab_strip_model()->GetActiveWebContents(), tab, |
| WindowOpenDisposition::NEW_WINDOW); |
| observer.Wait(); |
| new_browser = new_browser_observer.Wait(); |
| ui_test_utils::WaitForBrowserSetLastActive(new_browser); |
| EXPECT_NE(new_browser, browser()); |
| } |
| |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| web_contents = new_browser->tab_strip_model()->GetWebContentsAt(0); |
| VerifyNavigationEntries(web_contents->GetController(), url1, url2); |
| ASSERT_TRUE(web_contents->GetUserAgentOverride().ua_string_override.empty()); |
| ASSERT_TRUE(tab_content); |
| ASSERT_EQ(url2, tab_content->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreForeignSession) { |
| Profile* profile = browser()->profile(); |
| |
| GURL url1("http://google.com"); |
| GURL url2("http://google2.com"); |
| |
| // Set up the restore data -- one window with two tabs. |
| std::vector<const sessions::SessionWindow*> session; |
| sessions::SessionWindow window; |
| { |
| auto tab1 = std::make_unique<sessions::SessionTab>(); |
| tab1->tab_visual_index = 0; |
| tab1->current_navigation_index = 0; |
| tab1->pinned = true; |
| tab1->navigations.push_back( |
| ContentTestHelper::CreateNavigation(url1.spec(), "one")); |
| window.tabs.push_back(std::move(tab1)); |
| } |
| |
| { |
| auto tab2 = std::make_unique<sessions::SessionTab>(); |
| tab2->tab_visual_index = 1; |
| tab2->current_navigation_index = 0; |
| tab2->pinned = false; |
| tab2->navigations.push_back( |
| ContentTestHelper::CreateNavigation(url2.spec(), "two")); |
| window.tabs.push_back(std::move(tab2)); |
| } |
| |
| // Leave a third tab empty. Should have no effect on restored session, but |
| // simulates partially complete foreign session data. |
| window.tabs.push_back(std::make_unique<sessions::SessionTab>()); |
| |
| session.push_back(static_cast<const sessions::SessionWindow*>(&window)); |
| std::vector<Browser*> browsers = SessionRestore::RestoreForeignSessionWindows( |
| profile, session.begin(), session.end()); |
| ASSERT_EQ(1u, browsers.size()); |
| Browser* new_browser = browsers[0]; |
| ASSERT_TRUE(new_browser); |
| EXPECT_NE(new_browser, browser()); |
| EXPECT_EQ(new_browser->profile(), browser()->profile()); |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| |
| content::WebContents* web_contents_1 = |
| new_browser->tab_strip_model()->GetWebContentsAt(0); |
| content::WebContents* web_contents_2 = |
| new_browser->tab_strip_model()->GetWebContentsAt(1); |
| ASSERT_EQ(url1, web_contents_1->GetURL()); |
| ASSERT_EQ(url2, web_contents_2->GetURL()); |
| |
| // Check user agent override state. |
| ASSERT_TRUE( |
| web_contents_1->GetUserAgentOverride().ua_string_override.empty()); |
| ASSERT_TRUE( |
| web_contents_2->GetUserAgentOverride().ua_string_override.empty()); |
| |
| content::NavigationEntry* entry = |
| web_contents_1->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| ASSERT_FALSE(entry->GetIsOverridingUserAgent()); |
| |
| entry = web_contents_2->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| ASSERT_FALSE(entry->GetIsOverridingUserAgent()); |
| } |
| |
| // Test that a session can be restored even if the PageState were corrupted. |
| // This test covers a GetPageState call that used to fail a DCHECK when the |
| // PageState failed to decode during the command building step of session |
| // restore, per https://crbug.com/1412401. See also https://crbug.com/1196330, |
| // where release builds would crash in the renderer process during restore. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreInvalidPageState) { |
| Profile* profile = browser()->profile(); |
| |
| // Set up restore data with one tab and one navigation. |
| std::vector<const sessions::SessionWindow*> session; |
| sessions::SessionWindow window; |
| { |
| auto tab1 = std::make_unique<sessions::SessionTab>(); |
| tab1->tab_visual_index = 0; |
| tab1->current_navigation_index = 0; |
| tab1->pinned = true; |
| tab1->navigations.push_back( |
| ContentTestHelper::CreateNavigation(GetUrl1().spec(), "one")); |
| |
| // Corrupt the PageState for the SerializedNavigationEntry. |
| tab1->navigations.at(0).set_encoded_page_state("garbage"); |
| |
| window.tabs.push_back(std::move(tab1)); |
| } |
| |
| // Restore the session in a new window. |
| session.push_back(static_cast<const sessions::SessionWindow*>(&window)); |
| std::vector<Browser*> browsers = SessionRestore::RestoreForeignSessionWindows( |
| profile, session.begin(), session.end()); |
| ASSERT_EQ(1u, browsers.size()); |
| Browser* new_browser = browsers[0]; |
| ASSERT_TRUE(new_browser); |
| WaitForTabsToLoad(new_browser); |
| EXPECT_NE(new_browser, browser()); |
| EXPECT_EQ(new_browser->profile(), browser()->profile()); |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| |
| // The URL should still successfully commit, even though the rest of the |
| // previous PageState was lost. |
| content::WebContents* web_contents_1 = |
| new_browser->tab_strip_model()->GetWebContentsAt(0); |
| EXPECT_EQ(GetUrl1(), web_contents_1->GetLastCommittedURL()); |
| EXPECT_EQ(GetUrl1(), |
| web_contents_1->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| |
| content::NavigationEntry* entry = |
| web_contents_1->GetController().GetLastCommittedEntry(); |
| ASSERT_TRUE(entry); |
| EXPECT_EQ(GetUrl1(), entry->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, Basic) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2())); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| GoBack(new_browser); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| namespace { |
| |
| // Groups the tabs in |model| according to |specified_groups|. |
| void CreateTabGroups(TabStripModel* model, |
| base::span<const std::optional<int>> specified_groups) { |
| ASSERT_TRUE(model->SupportsTabGroups()); |
| ASSERT_EQ(static_cast<size_t>(model->count()), specified_groups.size()); |
| |
| // Maps |specified_groups| IDs to actual group IDs in |model|. |
| base::flat_map<int, tab_groups::TabGroupId> group_map; |
| |
| for (int i = 0; i < model->count(); ++i) { |
| if (const auto& entry = specified_groups[static_cast<size_t>(i)]) { |
| auto match = group_map.find(*entry); |
| |
| // If |group_map| doesn't contain a value for |specified_group|, we can |
| // assume we haven't created the group yet. |
| if (match == group_map.end()) { |
| const tab_groups::TabGroupId actual_group = model->AddToNewGroup({i}); |
| group_map.insert(std::make_pair(*entry, actual_group)); |
| } else { |
| const content::WebContents* const contents = model->GetWebContentsAt(i); |
| model->AddToExistingGroup({i}, match->second); |
| // Make sure we didn't move the tab. |
| EXPECT_EQ(contents, model->GetWebContentsAt(i)); |
| } |
| } |
| } |
| } |
| |
| // Checks that the grouping of tabs in |model| is equivalent to that specified |
| // in |specified_groups| up to relabeling of the group IDs. |
| void CheckTabGrouping(TabStripModel* model, |
| base::span<const std::optional<int>> specified_groups) { |
| ASSERT_EQ(model->count(), static_cast<int>(specified_groups.size())); |
| |
| // Maps |specified_groups| IDs to actual group IDs in |model|. |
| base::flat_map<int, tab_groups::TabGroupId> group_map; |
| |
| for (int i = 0; i < model->count(); ++i) { |
| SCOPED_TRACE(i); |
| |
| const std::optional<int> specified_group = |
| specified_groups[static_cast<size_t>(i)]; |
| const std::optional<tab_groups::TabGroupId> actual_group = |
| model->GetTabGroupForTab(i); |
| |
| // The tab should be grouped iff it's grouped in |specified_groups|. |
| EXPECT_EQ(actual_group.has_value(), specified_group.has_value()); |
| |
| if (actual_group.has_value() && specified_group.has_value()) { |
| auto match = group_map.find(specified_group.value()); |
| if (match == group_map.end()) { |
| group_map.insert( |
| std::make_pair(specified_group.value(), actual_group.value())); |
| } else { |
| EXPECT_EQ(actual_group.value(), match->second); |
| } |
| } |
| } |
| } |
| |
| // Returns the optional group ID for each tab in a vector. |
| std::vector<std::optional<tab_groups::TabGroupId>> GetTabGroups( |
| const TabStripModel* model) { |
| std::vector<std::optional<tab_groups::TabGroupId>> result(model->count()); |
| for (int i = 0; i < model->count(); ++i) |
| result[i] = model->GetTabGroupForTab(i); |
| return result; |
| } |
| |
| // Building session state from scratch and from an existing browser use |
| // different code paths. So, create a parametrized test fixture to run each test |
| // with and without a command reset. The bool test parameter determines whether |
| // to do a command reset when quitting and restoring. |
| class SessionRestoreTabGroupsTest : public SessionRestoreTest { |
| public: |
| SessionRestoreTabGroupsTest() = default; |
| |
| protected: |
| void SetUpOnMainThread() override { SessionRestoreTest::SetUpOnMainThread(); } |
| |
| Browser* QuitBrowserAndRestore(Browser* browser) { |
| // The test parameter determines whether to do a command reset. |
| SessionService* const session_service = |
| SessionServiceFactory::GetForProfile(browser->profile()); |
| session_service->ResetFromCurrentBrowsers(); |
| |
| return SessionRestoreTest::QuitBrowserAndRestore(browser); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_override_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTabGroupsTest, TabsWithGroups) { |
| ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); |
| |
| constexpr int kNumTabs = 6; |
| const std::array<std::optional<int>, kNumTabs> group_spec = { |
| 0, 0, std::nullopt, std::nullopt, 1, 1}; |
| |
| // Open |kNumTabs| tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| for (int i = 1; i < kNumTabs; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count()); |
| |
| CreateTabGroups(browser()->tab_strip_model(), group_spec); |
| ASSERT_NO_FATAL_FAILURE( |
| CheckTabGrouping(browser()->tab_strip_model(), group_spec)); |
| const auto groups = GetTabGroups(browser()->tab_strip_model()); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(kNumTabs, new_browser->tab_strip_model()->count()); |
| ASSERT_NO_FATAL_FAILURE( |
| CheckTabGrouping(new_browser->tab_strip_model(), group_spec)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTabGroupsTest, GroupMetadataRestored) { |
| ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); |
| |
| // Open up 4 more tabs, making 5 including the initial tab. |
| for (int i = 0; i < 4; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| |
| TabStripModel* const tsm = browser()->tab_strip_model(); |
| ASSERT_EQ(5, tsm->count()); |
| |
| // Group the first 2 and second 2 tabs, making for 2 groups with 2 tabs and 1 |
| // ungrouped tab in the strip. |
| const tab_groups::TabGroupId group1 = tsm->AddToNewGroup({0, 1}); |
| const tab_groups::TabGroupId group2 = tsm->AddToNewGroup({2, 3}); |
| |
| // Get the default visual data for the first group and set custom visual data |
| // for the second. |
| const tab_groups::TabGroupVisualData group1_data = |
| *tsm->group_model()->GetTabGroup(group1)->visual_data(); |
| const tab_groups::TabGroupVisualData group2_data( |
| u"Foo", tab_groups::TabGroupColorId::kBlue, true); |
| tsm->ChangeTabGroupVisuals(group2, group2_data); |
| |
| Browser* const new_browser = QuitBrowserAndRestore(browser()); |
| TabStripModel* const new_tsm = new_browser->tab_strip_model(); |
| ASSERT_EQ(5, new_tsm->count()); |
| |
| const std::optional<tab_groups::TabGroupId> new_group1 = |
| new_tsm->GetTabGroupForTab(0); |
| const std::optional<tab_groups::TabGroupId> new_group2 = |
| new_tsm->GetTabGroupForTab(2); |
| |
| ASSERT_TRUE(new_group1); |
| ASSERT_TRUE(new_group2); |
| |
| // Check that the restored visual data is the same. |
| const tab_groups::TabGroupVisualData* const group1_restored_data = |
| new_tsm->group_model()->GetTabGroup(*new_group1)->visual_data(); |
| const tab_groups::TabGroupVisualData* const group2_restored_data = |
| new_tsm->group_model()->GetTabGroup(*new_group2)->visual_data(); |
| |
| EXPECT_EQ(group1_data.title(), group1_restored_data->title()); |
| EXPECT_EQ(group1_data.color(), group1_restored_data->color()); |
| EXPECT_EQ(group1_data.is_collapsed(), group1_restored_data->is_collapsed()); |
| EXPECT_EQ(group2_data.title(), group2_restored_data->title()); |
| EXPECT_EQ(group2_data.color(), group2_restored_data->color()); |
| EXPECT_EQ(group2_data.is_collapsed(), group2_restored_data->is_collapsed()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTabGroupsTest, |
| TabGroupIDsRelabeledOnRestore) { |
| ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); |
| |
| constexpr int kNumTabs = 3; |
| const std::array<std::optional<int>, kNumTabs> group_spec = {0, 0, 1}; |
| |
| // Open |kNumTabs| tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| for (int i = 1; i < kNumTabs; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count()); |
| |
| CreateTabGroups(browser()->tab_strip_model(), group_spec); |
| ASSERT_NO_FATAL_FAILURE( |
| CheckTabGrouping(browser()->tab_strip_model(), group_spec)); |
| const auto orig_groups = GetTabGroups(browser()->tab_strip_model()); |
| |
| Browser* const new_browser = QuitBrowserAndRestore(browser()); |
| TabStripModel* const new_tsm = new_browser->tab_strip_model(); |
| ASSERT_EQ(kNumTabs, new_tsm->count()); |
| ASSERT_NO_FATAL_FAILURE(CheckTabGrouping(new_tsm, group_spec)); |
| const auto new_groups = GetTabGroups(new_tsm); |
| |
| for (int i = 0; i < kNumTabs; ++i) { |
| SCOPED_TRACE(i); |
| EXPECT_NE(orig_groups[i], new_groups[i]); |
| } |
| } |
| |
| // Flaky (https://crbug.com/1383903) |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RecentlyClosedGroup DISABLED_RecentlyClosedGroup |
| #else |
| #define MAYBE_RecentlyClosedGroup RecentlyClosedGroup |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTabGroupsTest, MAYBE_RecentlyClosedGroup) { |
| ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); |
| |
| constexpr int kNumTabs = 2; |
| const std::array<std::optional<int>, kNumTabs> group_spec = {0, 0}; |
| |
| // Open |kNumTabs| tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| for (int i = 1; i < kNumTabs; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count()); |
| |
| CreateTabGroups(browser()->tab_strip_model(), group_spec); |
| ASSERT_NO_FATAL_FAILURE( |
| CheckTabGrouping(browser()->tab_strip_model(), group_spec)); |
| |
| // Close the tab group. |
| const auto group = GetTabGroups(browser()->tab_strip_model()).front(); |
| TabStripModel* tab_strip_model = browser()->tab_strip_model(); |
| int first_tab_in_group = tab_strip_model->group_model() |
| ->GetTabGroup(group.value()) |
| ->ListTabs() |
| .start(); |
| content::WebContentsDestroyedWatcher destroyed_watcher( |
| tab_strip_model->GetWebContentsAt(first_tab_in_group)); |
| browser()->tab_strip_model()->CloseAllTabsInGroup(group.value()); |
| destroyed_watcher.Wait(); |
| |
| // We should have a restore entry for the group. |
| sessions::TabRestoreService* service = |
| TabRestoreServiceFactory::GetForProfile(browser()->profile()); |
| const sessions::TabRestoreService::Entries& entries = service->entries(); |
| ASSERT_GE(entries.size(), 1u); |
| ASSERT_EQ(entries.front()->type, sessions::tab_restore::Type::GROUP); |
| const auto* const group_entry = |
| static_cast<sessions::tab_restore::Group*>(entries.front().get()); |
| ASSERT_EQ(group_entry->tabs.size(), 2u); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| // We should still have a restore entry for the group. |
| sessions::TabRestoreService* new_service = |
| TabRestoreServiceFactory::GetForProfile(new_browser->profile()); |
| const sessions::TabRestoreService::Entries& new_entries = |
| new_service->entries(); |
| ASSERT_GE(new_entries.size(), 1u); |
| ASSERT_EQ(new_entries.front()->type, sessions::tab_restore::Type::GROUP); |
| const auto* const new_group_entry = |
| static_cast<sessions::tab_restore::Group*>(new_entries.front().get()); |
| ASSERT_EQ(new_group_entry->tabs.size(), 2u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreAfterDelete) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl3())); |
| |
| content::NavigationController& controller = |
| browser()->tab_strip_model()->GetActiveWebContents()->GetController(); |
| // Three urls and the NTP. |
| EXPECT_EQ(4, controller.GetEntryCount()); |
| controller.DeleteNavigationEntries( |
| base::BindLambdaForTesting([&](content::NavigationEntry* entry) { |
| return entry->GetURL() == GetUrl2(); |
| })); |
| EXPECT_EQ(3, controller.GetEntryCount()); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| content::NavigationController& new_controller = |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetController(); |
| EXPECT_EQ(3, new_controller.GetEntryCount()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(GetUrl3(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| GoBack(new_browser); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| class SplitTabSessionRestoreTest : public SessionRestoreTest { |
| public: |
| SplitTabSessionRestoreTest() { |
| feature_list_.InitWithFeatures( |
| {features::kSideBySide, features::kSideBySideSessionRestore}, {}); |
| } |
| SplitTabSessionRestoreTest(const SplitTabSessionRestoreTest&) = delete; |
| SplitTabSessionRestoreTest& operator=(const SplitTabSessionRestoreTest&) = |
| delete; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SplitTabSessionRestoreTest, TabsWithSplits) { |
| constexpr int kNumTabs = 8; |
| |
| // Open |kNumTabs| tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| for (int i = 1; i < kNumTabs; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| |
| ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count()); |
| |
| // Pin the first two tabs. |
| browser()->tab_strip_model()->SetTabPinned(0, true); |
| browser()->tab_strip_model()->SetTabPinned(1, true); |
| |
| // Group the next two tabs. |
| browser()->tab_strip_model()->AddToNewGroup({2, 3}); |
| |
| // Create three splits for pinned, group, unpinned. |
| browser()->tab_strip_model()->ActivateTabAt( |
| 0, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_pinned = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {1}, split_tabs::SplitTabVisualData(), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| browser()->tab_strip_model()->ActivateTabAt( |
| 2, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_group = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {3}, split_tabs::SplitTabVisualData(), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| browser()->tab_strip_model()->ActivateTabAt( |
| 4, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_unpinned = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {5}, split_tabs::SplitTabVisualData(), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| const auto groups = GetTabGroups(browser()->tab_strip_model()); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| EXPECT_EQ(kNumTabs, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ(new_browser->tab_strip_model()->IndexOfFirstNonPinnedTab(), 2); |
| EXPECT_EQ( |
| new_browser->tab_strip_model() |
| ->group_model() |
| ->GetTabGroup( |
| new_browser->tab_strip_model()->GetTabGroupForTab(2).value()) |
| ->ListTabs() |
| .length(), |
| 2); |
| EXPECT_EQ(new_browser->tab_strip_model()->ListSplits(), |
| std::set<split_tabs::SplitTabId>( |
| {split_pinned, split_group, split_unpinned})); |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_pinned) |
| ->ListTabs() |
| .size(), |
| 2u); |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_group) |
| ->ListTabs() |
| .size(), |
| 2u); |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_unpinned) |
| ->ListTabs() |
| .size(), |
| 2u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SplitTabSessionRestoreTest, SplitVisualDataRestored) { |
| constexpr int kNumTabs = 8; |
| |
| // Open |kNumTabs| tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| for (int i = 1; i < kNumTabs; ++i) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| |
| ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count()); |
| |
| // Pin the first two tabs. |
| browser()->tab_strip_model()->SetTabPinned(0, true); |
| browser()->tab_strip_model()->SetTabPinned(1, true); |
| |
| // Group the next two tabs. |
| browser()->tab_strip_model()->AddToNewGroup({2, 3}); |
| |
| // Create three splits for pinned, group, unpinned. |
| browser()->tab_strip_model()->ActivateTabAt( |
| 0, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_pinned = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {1}, |
| split_tabs::SplitTabVisualData(split_tabs::SplitTabLayout::kVertical, |
| 0.5), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| browser()->tab_strip_model()->ActivateTabAt( |
| 2, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_group = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {3}, |
| split_tabs::SplitTabVisualData( |
| split_tabs::SplitTabLayout::kHorizontal, 0.7), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| browser()->tab_strip_model()->ActivateTabAt( |
| 4, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| split_tabs::SplitTabId split_unpinned = |
| browser()->tab_strip_model()->AddToNewSplit( |
| {5}, |
| split_tabs::SplitTabVisualData(split_tabs::SplitTabLayout::kVertical, |
| 0.3), |
| split_tabs::SplitTabCreatedSource::kTabContextMenu); |
| |
| const auto groups = GetTabGroups(browser()->tab_strip_model()); |
| |
| // Update a some of the split visual data. |
| browser()->tab_strip_model()->UpdateSplitLayout( |
| split_unpinned, split_tabs::SplitTabLayout::kHorizontal); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| EXPECT_EQ(kNumTabs, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ(new_browser->tab_strip_model()->IndexOfFirstNonPinnedTab(), 2); |
| EXPECT_EQ( |
| new_browser->tab_strip_model() |
| ->group_model() |
| ->GetTabGroup( |
| new_browser->tab_strip_model()->GetTabGroupForTab(2).value()) |
| ->ListTabs() |
| .length(), |
| 2); |
| EXPECT_EQ(new_browser->tab_strip_model()->ListSplits(), |
| std::set<split_tabs::SplitTabId>( |
| {split_pinned, split_group, split_unpinned})); |
| |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_pinned) |
| ->ListTabs() |
| .size(), |
| 2u); |
| EXPECT_EQ(*new_browser->tab_strip_model() |
| ->GetSplitData(split_pinned) |
| ->visual_data(), |
| split_tabs::SplitTabVisualData( |
| split_tabs::SplitTabLayout::kVertical, 0.5)); |
| |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_group) |
| ->ListTabs() |
| .size(), |
| 2u); |
| EXPECT_EQ( |
| *new_browser->tab_strip_model()->GetSplitData(split_group)->visual_data(), |
| split_tabs::SplitTabVisualData(split_tabs::SplitTabLayout::kHorizontal, |
| 0.7)); |
| |
| EXPECT_EQ(new_browser->tab_strip_model() |
| ->GetSplitData(split_unpinned) |
| ->ListTabs() |
| .size(), |
| 2u); |
| EXPECT_EQ(*new_browser->tab_strip_model() |
| ->GetSplitData(split_unpinned) |
| ->visual_data(), |
| split_tabs::SplitTabVisualData( |
| split_tabs::SplitTabLayout::kHorizontal, 0.3)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, StartupPagesWithOnlyNtp) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), |
| GURL(chrome::kChromeUINewTabURL))); |
| content::WebContentsDestroyedWatcher original_tab_destroyed_watcher( |
| browser()->tab_strip_model()->GetWebContentsAt(0)); |
| |
| SessionStartupPref pref(SessionStartupPref::URLS); |
| pref.urls.push_back(GetUrl1()); |
| pref.urls.push_back(GetUrl2()); |
| SessionStartupPref::SetStartupPref(browser()->profile(), pref); |
| |
| SessionRestore::OpenStartupPagesAfterCrash(browser()); |
| // Wait until the original tab finished closing. |
| original_tab_destroyed_watcher.Wait(); |
| |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(GetUrl1(), |
| browser()->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| EXPECT_EQ(GetUrl2(), |
| browser()->tab_strip_model()->GetWebContentsAt(1)->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, StartupPagesWithExistingPages) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl3())); |
| |
| SessionStartupPref pref(SessionStartupPref::URLS); |
| pref.urls.push_back(GetUrl1()); |
| pref.urls.push_back(GetUrl2()); |
| SessionStartupPref::SetStartupPref(browser()->profile(), pref); |
| |
| SessionRestore::OpenStartupPagesAfterCrash(browser()); |
| |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(3, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(GetUrl3(), |
| browser()->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| EXPECT_EQ(GetUrl1(), |
| browser()->tab_strip_model()->GetWebContentsAt(1)->GetURL()); |
| EXPECT_EQ(GetUrl2(), |
| browser()->tab_strip_model()->GetWebContentsAt(2)->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, NoMemoryPressureLoadsAllTabs) { |
| // Add several tabs to the browser. Restart the browser and check that all |
| // tabs got loaded properly. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| Browser* restored = QuitBrowserAndRestore(browser(), GURL(), true); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| |
| ASSERT_EQ(3, tab_strip_model->count()); |
| // All render widgets should be initialized by now. |
| ASSERT_TRUE(tab_strip_model->GetWebContentsAt(0)->GetRenderWidgetHostView() && |
| tab_strip_model->GetWebContentsAt(1)->GetRenderWidgetHostView() && |
| tab_strip_model->GetWebContentsAt(2)->GetRenderWidgetHostView()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, MemoryPressureLoadsNotAllTabs) { |
| // Add several tabs to the browser. Restart the browser and check that all |
| // tabs got loaded properly. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| // Restore the brwoser, but instead of directly waiting, we issue a critical |
| // memory pressure event and finish then the loading. |
| Browser* restored = QuitBrowserAndRestore(browser(), GURL(), false); |
| |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| |
| ASSERT_EQ(3, tab_strip_model->count()); |
| // At least one of the render widgets should not be initialized yet. |
| ASSERT_FALSE( |
| tab_strip_model->GetWebContentsAt(0)->GetRenderWidgetHostView() && |
| tab_strip_model->GetWebContentsAt(1)->GetRenderWidgetHostView() && |
| tab_strip_model->GetWebContentsAt(2)->GetRenderWidgetHostView()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreWebUI) { |
| const GURL webui_url(chrome::kChromeUIDownloadsURL); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), webui_url)); |
| content::WebContents* old_tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_TRUE(old_tab->GetPrimaryMainFrame()->GetEnabledBindings().Has( |
| content::BindingsPolicyValue::kMojoWebUi)); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| content::WebContents* new_tab = |
| new_browser->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_EQ(webui_url, new_tab->GetURL()); |
| EXPECT_TRUE(new_tab->GetPrimaryMainFrame()->GetEnabledBindings().Has( |
| content::BindingsPolicyValue::kMojoWebUi)); |
| } |
| |
| // http://crbug.com/803510 : Flaky on dbg and ASan bots. |
| #if defined(ADDRESS_SANITIZER) || !defined(NDEBUG) |
| #define MAYBE_RestoreWebUISettings DISABLED_RestoreWebUISettings |
| #else |
| #define MAYBE_RestoreWebUISettings RestoreWebUISettings |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, MAYBE_RestoreWebUISettings) { |
| const GURL webui_url(chrome::kChromeUISettingsURL); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), webui_url)); |
| content::WebContents* old_tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_TRUE(old_tab->GetPrimaryMainFrame()->GetEnabledBindings().Has( |
| content::BindingsPolicyValue::kWebUi)); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| content::WebContents* new_tab = |
| new_browser->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_EQ(webui_url, new_tab->GetURL()); |
| EXPECT_TRUE(new_tab->GetPrimaryMainFrame()->GetEnabledBindings().Has( |
| content::BindingsPolicyValue::kWebUi)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoresForwardAndBackwardNavs) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl3())); |
| |
| GoBack(browser()); |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| GoForward(new_browser); |
| ASSERT_EQ(GetUrl3(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| GoBack(new_browser); |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| |
| // Test renderer-initiated back/forward as well. |
| GURL go_back_url("javascript:history.back();"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, go_back_url)); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| // Tests that the SiteInstances used for entries in a restored tab's history |
| // are correctly initialized, so that going back to a restored cross-site page |
| // and then forward again works. (See b/1204135.) |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| RestoresCrossSiteForwardAndBackwardNavs) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL cross_site_url(embedded_test_server()->GetURL("/title2.html")); |
| |
| // Visit URLs on different sites. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cross_site_url)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl2())); |
| |
| GoBack(browser()); |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| WaitForTabsToLoad(new_browser); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| |
| // Check that back and forward work as expected. |
| content::WebContents* tab = |
| new_browser->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_EQ(cross_site_url, tab->GetLastCommittedURL()); |
| |
| GoBack(new_browser); |
| ASSERT_EQ(GetUrl1(), tab->GetLastCommittedURL()); |
| |
| GoForward(new_browser); |
| ASSERT_EQ(cross_site_url, tab->GetLastCommittedURL()); |
| |
| // Test renderer-initiated back/forward as well. |
| GURL go_forward_url("javascript:history.forward();"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, go_forward_url)); |
| ASSERT_EQ(GetUrl2(), tab->GetLastCommittedURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TwoTabsSecondSelected) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->active_index()); |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, OnErrorWritingSessionCommands) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| auto* session_service = |
| SessionServiceFactory::GetForProfile(browser()->profile()); |
| session_service->OnErrorWritingSessionCommands(); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->active_index()); |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| } |
| |
| // Creates two tabs, closes one, quits and makes sure only one tab is restored. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, ClosedTabStaysClosed) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| chrome::CloseTab(browser()); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| AssertOneWindowWithOneTab(new_browser); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| // Closes the one and only tab and verifies it is not restored. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, CloseSingleTabRestoresNothing) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| |
| Profile* profile = browser()->profile(); |
| std::unique_ptr<ScopedKeepAlive> keep_alive(new ScopedKeepAlive( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED)); |
| |
| chrome::CloseTab(browser()); |
| ui_test_utils::WaitForBrowserToClose(browser()); |
| |
| ui_test_utils::AllBrowserTabAddedWaiter tab_waiter; |
| SessionRestoreTestHelper restore_observer; |
| |
| // Ensure the session service factory is started, even if it was explicitly |
| // shut down. |
| SessionServiceTestHelper helper(profile); |
| helper.SetForceBrowserNotAliveWithNoWindows(true); |
| |
| chrome::NewEmptyWindow(profile); |
| |
| Browser* new_browser = chrome::FindBrowserWithTab(tab_waiter.Wait()); |
| |
| restore_observer.Wait(); |
| WaitForTabsToLoad(new_browser); |
| |
| keep_alive.reset(); |
| |
| AssertOneWindowWithOneTab(new_browser); |
| EXPECT_EQ(chrome::kChromeUINewTabURL, |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| // Verifies that launching with no previous session to a url which closes itself |
| // results in no session being restored on the next launch. |
| // Regression test for http://crbug.com/1052096 |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| AutoClosedSingleTabDoesNotGetRestored) { |
| Profile* profile = browser()->profile(); |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // First close the original browser to clear the session information (as |
| // verified by CloseSingleTabRestoresNothing). |
| chrome::CloseTab(browser()); |
| ui_test_utils::WaitForBrowserToClose(browser()); |
| |
| SessionRestoreTestHelper restore_observer; |
| |
| // Ensure the session service factory is started, even if it was explicitly |
| // shut down. |
| SessionServiceTestHelper helper(profile); |
| helper.SetForceBrowserNotAliveWithNoWindows(true); |
| |
| // Create a new browser by navigating to the test page. |
| GURL url = ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(FILE_PATH_LITERAL("title1.html"))); |
| NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK); |
| Navigate(¶ms); |
| |
| restore_observer.Wait(); |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| ui_test_utils::BrowserChangeObserver browser_removed_observer( |
| params.browser, |
| ui_test_utils::BrowserChangeObserver::ChangeType::kRemoved); |
| |
| // Have the page trigger closing the browser. |
| ASSERT_TRUE( |
| content::ExecJs(params.browser->tab_strip_model()->GetActiveWebContents(), |
| "window.open('', '_self').close()")); |
| |
| // Wait for the browser to close as a result of the single tab closing |
| // itself. |
| browser_removed_observer.Wait(); |
| |
| ui_test_utils::AllBrowserTabAddedWaiter tab_waiter; |
| SessionRestoreTestHelper restore_observer2; |
| |
| // Create a new browser from scratch and verify the tab is not restored. |
| chrome::NewEmptyWindow(profile); |
| |
| Browser* new_browser = chrome::FindBrowserWithTab(tab_waiter.Wait()); |
| |
| restore_observer2.Wait(); |
| WaitForTabsToLoad(new_browser); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| |
| AssertOneWindowWithOneTab(new_browser); |
| EXPECT_EQ(chrome::kChromeUINewTabURL, |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| // Ensures active tab properly restored when tabs before it closed. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, ActiveIndexUpdatedAtClose) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl3(), WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| browser()->tab_strip_model()->CloseWebContentsAt( |
| 0, TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| ASSERT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| ASSERT_EQ(new_browser->tab_strip_model()->active_index(), 0); |
| } |
| |
| // Ensures active tab properly restored when tabs are inserted before it . |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, ActiveIndexUpdatedAtInsert) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| NavigateParams navigate_params(browser(), GetUrl3(), |
| ui::PAGE_TRANSITION_TYPED); |
| navigate_params.tabstrip_index = 0; |
| navigate_params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB; |
| ui_test_utils::NavigateToURL(&navigate_params); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| ASSERT_EQ(new_browser->tab_strip_model()->active_index(), 1); |
| } |
| |
| #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS) |
| // This test doesn't apply to Mac; see GetCommandLineForRelaunch for details. It |
| // was disabled for ChromeOS a long time so might never have worked there. |
| |
| // Launches an app window, closes tabbed browser, launches and makes sure |
| // we restore the tabbed browser url. |
| // If this test flakes, use http://crbug.com/29110 |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| RestoreAfterClosingTabbedBrowserWithAppAndLaunching) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| |
| // Launch an app. |
| base::CommandLine app_launch_arguments = GetCommandLineForRelaunch(); |
| app_launch_arguments.AppendSwitchASCII(switches::kApp, GetUrl2().spec()); |
| |
| base::LaunchProcess(app_launch_arguments, base::LaunchOptionsForTest()); |
| Browser* app_window = ui_test_utils::WaitForBrowserToOpen(); |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| |
| // Close the first window. The only window left is the App window. |
| CloseBrowserSynchronously(browser()); |
| |
| // Restore the session, which should bring back the first window with |
| // GetUrl1(). |
| Browser* new_browser = QuitBrowserAndRestore(app_window); |
| |
| AssertOneWindowWithOneTab(new_browser); |
| |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| #endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS) |
| |
| // Creates two windows, closes one, restores, make sure only one window open. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TwoWindowsCloseOneRestoreOnlyOne) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| |
| // Open a second window. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER); |
| |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| |
| // Close it. |
| Browser* new_window = active_browser_list_->get(1); |
| CloseBrowserSynchronously(new_window); |
| |
| // Restart and make sure we have only one window with one tab and the url |
| // is GetUrl1(). |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| AssertOneWindowWithOneTab(new_browser); |
| |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreWindowUserTitle) { |
| // Open one browser window and navigate it to url 1. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_EQ( |
| GetUrl1(), |
| browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL()); |
| |
| // Open a second window and navigate it to url 2. |
| Browser* browser2 = CreateBrowser(browser()->profile()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser2, GetUrl2(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser2->tab_strip_model()->count()); |
| EXPECT_EQ( |
| GetUrl2(), |
| browser2->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL()); |
| |
| // Set a custom user title to this second browser window. |
| const std::string custom_user_title = "Window 2"; |
| browser2->SetWindowUserTitle(custom_user_title); |
| ASSERT_EQ(custom_user_title, active_browser_list_->get(1)->user_title()); |
| |
| // Simulate an exit by shutting down the session service. If we don't do this |
| // the window close is treated as though the user closed the window and won't |
| // be restored. |
| SessionServiceFactory::ShutdownForProfile(browser()->profile()); |
| |
| // Then close all the browsers and "restart" Chromium. |
| CloseBrowserSynchronously(browser2); |
| QuitBrowserAndRestore(browser()); |
| |
| // The two browsers should be restored with their respective URLs. |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| EXPECT_EQ(GetUrl1(), active_browser_list_->get(0) |
| ->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetLastCommittedURL()); |
| EXPECT_EQ(GetUrl2(), active_browser_list_->get(1) |
| ->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetLastCommittedURL()); |
| |
| // The user title should be empty for first window as it did not have a |
| // custom title. |
| EXPECT_TRUE(active_browser_list_->get(0)->user_title().empty()); |
| |
| // The user title for second window should be restored. |
| EXPECT_EQ(custom_user_title, active_browser_list_->get(1)->user_title()); |
| } |
| |
| // Make sure after a restore the number of processes matches that of the number |
| // of processes running before the restore. This creates a new tab so that |
| // we should have two new tabs running. (This test will pass in both |
| // process-per-site and process-per-site-instance, because we treat the new tab |
| // as a special case in process-per-site-instance so that it only ever uses one |
| // process.) |
| // |
| // Flaky: http://code.google.com/p/chromium/issues/detail?id=52022 |
| // Unfortunately, the fix at http://codereview.chromium.org/6546078 |
| // breaks NTP background image refreshing, so ThemeSource had to revert to |
| // replacing the existing data source. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, ShareProcessesOnRestore) { |
| // Create two new tabs. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| int expected_process_count = RenderProcessHostCount(); |
| |
| // Restart. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| |
| ASSERT_EQ(3, new_browser->tab_strip_model()->count()); |
| |
| ASSERT_EQ(expected_process_count, RenderProcessHostCount()); |
| } |
| |
| // Test that changing the user agent override will persist it to disk. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, PersistAndRestoreUserAgentOverride) { |
| // Create a tab with an overridden user agent. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); |
| blink::UserAgentOverride ua_override; |
| ua_override.ua_string_override = "override"; |
| ua_override.ua_metadata_override.emplace(); |
| ua_override.ua_metadata_override->brand_version_list.emplace_back("Overrider", |
| "0"); |
| ua_override.ua_metadata_override->brand_full_version_list.emplace_back( |
| "Overrider", "0.0.0.0"); |
| browser()->tab_strip_model()->GetWebContentsAt(0)->SetUserAgentOverride( |
| ua_override, false); |
| |
| // Create a tab without an overridden user agent. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->active_index()); |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->active_index()); |
| |
| // Confirm that the user agent overrides are properly set. |
| blink::UserAgentOverride over0 = new_browser->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetUserAgentOverride(); |
| EXPECT_EQ("override", over0.ua_string_override); |
| ASSERT_TRUE(over0.ua_metadata_override.has_value()); |
| EXPECT_TRUE(over0.ua_metadata_override == ua_override.ua_metadata_override); |
| |
| blink::UserAgentOverride over1 = new_browser->tab_strip_model() |
| ->GetWebContentsAt(1) |
| ->GetUserAgentOverride(); |
| EXPECT_EQ(std::string(), over1.ua_string_override); |
| EXPECT_FALSE(over1.ua_metadata_override.has_value()); |
| } |
| |
| // Regression test for crbug.com/125958. When restoring a pinned selected tab in |
| // a setting where there are existing tabs, the selected index computation was |
| // wrong, leading to the wrong tab getting selected, DCHECKs firing, and the |
| // pinned tab not getting loaded. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestorePinnedSelectedTab) { |
| // Create a pinned tab. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| browser()->tab_strip_model()->SetTabPinned(0, true); |
| ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); |
| // Create a nonpinned tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->active_index()); |
| // Select the pinned tab. |
| browser()->tab_strip_model()->ActivateTabAt( |
| 0, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); |
| Profile* profile = browser()->profile(); |
| |
| // This will also initiate a session restore, but we're not interested in it. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(0, new_browser->tab_strip_model()->active_index()); |
| // Close the pinned tab. |
| new_browser->tab_strip_model()->CloseSelectedTabs(); |
| |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(0, new_browser->tab_strip_model()->active_index()); |
| // Use the existing tab to navigate away, so that we can verify it was really |
| // clobbered. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, GetUrl3())); |
| |
| // Restore the session again, clobbering the existing tab. |
| SessionRestore::RestoreSession(profile, new_browser, |
| SessionRestore::CLOBBER_CURRENT_TAB | |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_BROWSER, |
| StartupTabs()); |
| |
| // The pinned tab is the selected tab. |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ(0, new_browser->tab_strip_model()->active_index()); |
| EXPECT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| EXPECT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetWebContentsAt(1)->GetURL()); |
| } |
| |
| // Regression test for crbug.com/240156. When restoring tabs with a navigation, |
| // the navigation should take active tab focus. |
| // Flaky on Mac. http://crbug.com/656211. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RestoreWithNavigateSelectedTab \ |
| DISABLED_RestoreWithNavigateSelectedTab |
| #else |
| #define MAYBE_RestoreWithNavigateSelectedTab RestoreWithNavigateSelectedTab |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| MAYBE_RestoreWithNavigateSelectedTab) { |
| // Create 2 tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restore the session by calling chrome::Navigate(). |
| Browser* new_browser = QuitBrowserAndRestore(browser(), GetUrl3(), true); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(3, new_browser->tab_strip_model()->count()); |
| // Navigated url should be the active tab. |
| ASSERT_EQ(GetUrl3(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| // Ensure that AUTO_SUBFRAME navigations in subframes are restored. |
| // See https://crbug.com/638088. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreAfterAutoSubframe) { |
| // Load a page with a blank iframe, then navigate the iframe. This will be an |
| // auto-subframe commit, and we expect it to be restored. |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL main_url(embedded_test_server()->GetURL("/iframe_blank.html")); |
| GURL subframe_url(embedded_test_server()->GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| { |
| content::TestNavigationObserver observer( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| std::string nav_frame_script = |
| content::JsReplace("frames[0].location.href = $1;", subframe_url); |
| ASSERT_TRUE( |
| content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(), |
| nav_frame_script)); |
| observer.Wait(); |
| } |
| |
| // Restore the session. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| |
| // The restored page should have the right iframe. |
| content::WebContents* new_web_contents = |
| new_browser->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_EQ(main_url, new_web_contents->GetURL()); |
| EXPECT_EQ(subframe_url.possibly_invalid_spec(), |
| content::EvalJs(new_web_contents, "frames[0].location.href")); |
| } |
| |
| // Do a clobber restore from the new tab page. This test follows the code path |
| // of a crash followed by the user clicking restore from the new tab page. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, ClobberRestoreTest) { |
| // Create 2 tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| ASSERT_EQ(0, browser()->tab_strip_model()->active_index()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->active_index()); |
| Profile* profile = browser()->profile(); |
| |
| // This will also initiate a session restore, but we're not interested in it. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->active_index()); |
| // Close the first tab. |
| chrome::CloseTab(new_browser); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| ASSERT_EQ(0, new_browser->tab_strip_model()->active_index()); |
| // Use the existing tab to navigate to the NTP. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, |
| GURL(chrome::kChromeUINewTabURL))); |
| content::WebContentsDestroyedWatcher existing_tab_destroyed_watcher( |
| new_browser->tab_strip_model()->GetWebContentsAt(0)); |
| |
| // Restore the session again, clobbering the existing tab. |
| SessionRestore::RestoreSession(profile, new_browser, |
| SessionRestore::CLOBBER_CURRENT_TAB | |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_BROWSER, |
| StartupTabs()); |
| |
| // Wait until the existing tab finished closing. |
| existing_tab_destroyed_watcher.Wait(); |
| |
| // 2 tabs should have been restored, with the existing tab clobbered, giving |
| // us a total of 2 tabs. |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ(1, new_browser->tab_strip_model()->active_index()); |
| EXPECT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL()); |
| EXPECT_EQ(GetUrl2(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, SessionStorage) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl1())); |
| content::NavigationController* controller = |
| &browser()->tab_strip_model()->GetActiveWebContents()->GetController(); |
| ASSERT_TRUE(controller->GetDefaultSessionStorageNamespace()); |
| std::string session_storage_id = |
| controller->GetDefaultSessionStorageNamespace()->id(); |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(GetUrl1(), |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| content::NavigationController* new_controller = |
| &new_browser->tab_strip_model()->GetActiveWebContents()->GetController(); |
| ASSERT_TRUE(new_controller->GetDefaultSessionStorageNamespace()); |
| std::string restored_session_storage_id = |
| new_controller->GetDefaultSessionStorageNamespace()->id(); |
| EXPECT_EQ(session_storage_id, restored_session_storage_id); |
| } |
| |
| // Failing on Mac. See https://crbug.com/1484860 |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_TabWithDownloadDoesNotGetRestored \ |
| DISABLED_TabWithDownloadDoesNotGetRestored |
| #else |
| #define MAYBE_TabWithDownloadDoesNotGetRestored \ |
| TabWithDownloadDoesNotGetRestored |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| MAYBE_TabWithDownloadDoesNotGetRestored) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(browser()->is_type_normal()); |
| |
| GURL first_download_url = |
| embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| { |
| content::DownloadTestObserverTerminal observer( |
| browser()->profile()->GetDownloadManager(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_download_url)); |
| observer.WaitForFinished(); |
| |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| } |
| |
| { |
| content::DownloadManager* download_manager = |
| browser()->profile()->GetDownloadManager(); |
| content::DownloadTestObserverInProgress in_progress_counter( |
| download_manager, 2); |
| content::DownloadTestObserverTerminal observer( |
| download_manager, 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT); |
| |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| |
| // In addition to restarting the browser, create a new download in a new |
| // tab. If session restore erroneously created a new download, then its |
| // initiation task chain should strictly precede the task chain for the new |
| // download initiated here. While the download termination is asynchronous, |
| // the erroneous download should enter the IN_PROGRESS state prior to the |
| // second download reaching COMPLETE. |
| // |
| // Hence verifying that there was only one IN_PROGRESS download by the time |
| // the new download completes ensures that there is no second download. |
| GURL second_download_url = |
| embedded_test_server()->GetURL("/downloads/image-octet-stream.png"); |
| ui_test_utils::NavigateToURLWithDisposition( |
| new_browser, second_download_url, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| |
| observer.WaitForFinished(); |
| EXPECT_EQ(1u, in_progress_counter.NumDownloadsSeenInState( |
| download::DownloadItem::IN_PROGRESS)); |
| EXPECT_EQ( |
| 1u, observer.NumDownloadsSeenInState(download::DownloadItem::COMPLETE)); |
| |
| // We still need to verify that the second download that completed above is |
| // the new one that we initiated. This would be true iff the DownloadManager |
| // has exactly two downloads and they correspond to |first_download_url| and |
| // |second_download_url|. |
| std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads; |
| download_manager->GetAllDownloads(&downloads); |
| ASSERT_EQ(2u, downloads.size()); |
| std::set<GURL> download_urls{downloads[0]->GetURL(), |
| downloads[1]->GetURL()}; |
| std::set<GURL> expected_urls{first_download_url, second_download_url}; |
| EXPECT_EQ(expected_urls, download_urls); |
| } |
| } |
| |
| // Test is flaky on Linux and Windows: https://crbug.com/1181867 |
| #if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) |
| namespace { |
| |
| class MultiBrowserObserver : public BrowserListObserver { |
| public: |
| enum class Event { |
| kAdded, |
| kRemoved, |
| }; |
| MultiBrowserObserver(size_t num_expected, Event event) |
| : num_expected_(num_expected), event_(event) { |
| BrowserList::AddObserver(this); |
| } |
| |
| MultiBrowserObserver(const MultiBrowserObserver&) = delete; |
| MultiBrowserObserver& operator=(const MultiBrowserObserver&) = delete; |
| |
| ~MultiBrowserObserver() override { BrowserList::RemoveObserver(this); } |
| |
| // Note that the returned pointers might no longer be valid (because the |
| // Browser objects were closed). |
| std::vector<raw_ptr<Browser, VectorExperimental>> Wait() { |
| run_loop_.Run(); |
| return browsers_; |
| } |
| |
| // BrowserListObserver implementation. |
| void OnBrowserAdded(Browser* browser) override { |
| if (event_ == Event::kAdded) { |
| browsers_.push_back(browser); |
| if (--num_expected_ == 0) |
| run_loop_.Quit(); |
| } |
| } |
| void OnBrowserRemoved(Browser* browser) override { |
| if (event_ == Event::kRemoved) { |
| browsers_.push_back(browser); |
| if (--num_expected_ == 0) |
| run_loop_.Quit(); |
| } |
| } |
| |
| private: |
| size_t num_expected_; |
| Event event_; |
| std::vector<raw_ptr<Browser, VectorExperimental>> browsers_; |
| base::RunLoop run_loop_; |
| }; |
| |
| } // namespace |
| |
| // Test that when closing a profile with multiple browsers, all browsers are |
| // restored when the profile is reopened. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreAllBrowsers) { |
| // Create two profiles with two browsers each. |
| Browser* first_profile_browser_one = browser(); |
| // Open the second browser with the first profile and verify it becomes the |
| // active browser window. |
| ui_test_utils::BrowserChangeObserver new_browser_observer( |
| nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded); |
| chrome::NewWindow(first_profile_browser_one); |
| Browser* first_profile_browser_two = new_browser_observer.Wait(); |
| ui_test_utils::WaitUntilBrowserBecomeActive(first_profile_browser_two); |
| EXPECT_NE(first_profile_browser_one, first_profile_browser_two); |
| |
| // Open the first browser window for the second profile and verify it becomes |
| // the active browser window. |
| Profile* second_profile = CreateSecondaryProfile(1); |
| base::FilePath second_profile_path = second_profile->GetPath(); |
| profiles::FindOrCreateNewWindowForProfile( |
| second_profile, chrome::startup::IsProcessStartup::kNo, |
| chrome::startup::IsFirstRun::kNo, false); |
| Browser* second_profile_browser_one = ui_test_utils::WaitForBrowserToOpen(); |
| ui_test_utils::WaitUntilBrowserBecomeActive(second_profile_browser_one); |
| |
| // Open the second browser window for the second profile and verify it becomes |
| // the active browser window. |
| ui_test_utils::BrowserChangeObserver second_profile_new_browser_observer( |
| nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded); |
| chrome::NewWindow(second_profile_browser_one); |
| Browser* second_profile_browser_two = |
| second_profile_new_browser_observer.Wait(); |
| ui_test_utils::WaitUntilBrowserBecomeActive(second_profile_browser_two); |
| EXPECT_NE(second_profile_browser_one, second_profile_browser_two); |
| |
| // Navigate the tab in each browser to a unique URL we can later reidentify. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(first_profile_browser_one, |
| GURL("data:,profile 1 browser 1"))); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(first_profile_browser_two, |
| GURL("data:,profile 1 browser 2"))); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(second_profile_browser_one, |
| GURL("data:,profile 2 browser 1"))); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(second_profile_browser_two, |
| GURL("data:,profile 2 browser 2"))); |
| |
| // Double-check preconditions. |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| ASSERT_EQ(profile_manager->GetNumberOfProfiles(), 2u); |
| ASSERT_EQ(chrome::GetTotalBrowserCount(), 4u); |
| |
| // Close all profiles associated with the second profile. |
| MultiBrowserObserver removed_observer(2, |
| MultiBrowserObserver::Event::kRemoved); |
| BrowserList::GetInstance()->CloseAllBrowsersWithProfile( |
| second_profile, BrowserList::CloseCallback(), |
| BrowserList::CloseCallback(), false); |
| removed_observer.Wait(); |
| |
| // The second profile should have no browsers anymore at this point. |
| ASSERT_EQ(chrome::FindBrowserWithProfile(second_profile), nullptr); |
| ASSERT_EQ(chrome::GetTotalBrowserCount(), 2u); |
| |
| // Clean up now stale pointers. |
| second_profile_browser_one = nullptr; |
| second_profile_browser_two = nullptr; |
| second_profile = nullptr; // See DestroyProfileOnBrowserClose flag. |
| |
| // Reopen the second profile and trigger session restore. |
| MultiBrowserObserver added_observer(2, MultiBrowserObserver::Event::kAdded); |
| profiles::SwitchToProfile(second_profile_path, false, {}); |
| std::vector<raw_ptr<Browser, VectorExperimental>> browsers = |
| added_observer.Wait(); |
| |
| // Verify that the correct URLs where restored. |
| std::set<GURL> expected_urls; |
| expected_urls.insert(GURL("data:,profile 2 browser 1")); |
| expected_urls.insert(GURL("data:,profile 2 browser 2")); |
| for (Browser* browser : browsers) { |
| WaitForTabsToLoad(browser); |
| TabStripModel* tab_strip_model = browser->tab_strip_model(); |
| EXPECT_EQ(tab_strip_model->count(), 1); |
| EXPECT_EQ(tab_strip_model->active_index(), 0); |
| EXPECT_EQ( |
| expected_urls.erase(tab_strip_model->GetActiveWebContents()->GetURL()), |
| 1u) |
| << "Browser with unexpected URL " |
| << tab_strip_model->GetActiveWebContents() |
| ->GetURL() |
| .possibly_invalid_spec(); |
| } |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| // Tracks the load order of tabs in a new browser. |
| class LoadOrderObserver : public BrowserListObserver, |
| public TabStripModelObserver, |
| public WebContentsCollection::Observer { |
| public: |
| explicit LoadOrderObserver(int expected_tabs) |
| : expected_tabs_(expected_tabs) { |
| BrowserList::AddObserver(this); |
| } |
| |
| ~LoadOrderObserver() override { |
| browser_->tab_strip_model()->RemoveObserver(this); |
| BrowserList::RemoveObserver(this); |
| } |
| |
| void WaitForAllTabsToStartLoading() { run_loop_.Run(); } |
| |
| const std::vector<raw_ptr<content::WebContents, VectorExperimental>>& |
| web_contents() const { |
| return web_contents_; |
| } |
| |
| // BrowserListObserver: |
| void OnBrowserAdded(Browser* browser) override { |
| ASSERT_EQ(browser_, nullptr); |
| browser_ = browser; |
| EXPECT_TRUE(browser_->tab_strip_model()->empty()); |
| browser_->tab_strip_model()->AddObserver(this); |
| } |
| |
| // TabStripModelObserver: |
| void OnTabStripModelChanged( |
| TabStripModel* tab_strip_model, |
| const TabStripModelChange& change, |
| const TabStripSelectionChange& selection) override { |
| if (change.type() != TabStripModelChange::kInserted) { |
| return; |
| } |
| |
| for (auto& contents : change.GetInsert()->contents) { |
| content::WebContents* new_contents = contents.contents; |
| EXPECT_FALSE(new_contents->IsLoading()); |
| web_contents_collection_.StartObserving(contents.contents); |
| } |
| } |
| |
| // WebContentsCollection::Observer: |
| void DidStartLoading(content::WebContents* web_contents) override { |
| web_contents_.push_back(web_contents); |
| if (web_contents_.size() == expected_tabs_) { |
| run_loop_.Quit(); |
| } |
| } |
| |
| private: |
| const size_t expected_tabs_; |
| raw_ptr<Browser> browser_ = nullptr; |
| base::RunLoop run_loop_; |
| WebContentsCollection web_contents_collection_{this}; |
| // Ordered by load start order. |
| std::vector<raw_ptr<content::WebContents, VectorExperimental>> web_contents_; |
| }; |
| |
| // PRE_CorrectLoadingOrder is flaky on ChromeOS MSAN and Mac. |
| // See http://crbug.com/493167. |
| #if (BUILDFLAG(IS_CHROMEOS) && defined(MEMORY_SANITIZER)) || BUILDFLAG(IS_MAC) |
| #define MAYBE_PRE_CorrectLoadingOrder DISABLED_PRE_CorrectLoadingOrder |
| #define MAYBE_CorrectLoadingOrder DISABLED_CorrectLoadingOrder |
| #else |
| #define MAYBE_PRE_CorrectLoadingOrder PRE_CorrectLoadingOrder |
| #define MAYBE_CorrectLoadingOrder CorrectLoadingOrder |
| #endif |
| IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, MAYBE_PRE_CorrectLoadingOrder) { |
| Profile* profile = browser()->profile(); |
| |
| const auto activation_order = std::to_array<int>({4, 2, 1, 5, 0, 3}); |
| |
| // Replace the first tab and add the other tabs. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kUrls[0]))); |
| for (size_t i = 1; i < kExpectedNumTabs; i++) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(kUrls[i]), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| } |
| |
| ASSERT_EQ(static_cast<int>(kExpectedNumTabs), |
| browser()->tab_strip_model()->count()); |
| |
| // Activate the tabs one by one following the specified activation order. |
| for (int i : activation_order) |
| browser()->tab_strip_model()->ActivateTabAt( |
| i, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| |
| // Close the browser. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| CloseBrowserSynchronously(browser()); |
| |
| LoadOrderObserver load_order_observer(kExpectedNumTabs); |
| |
| // Create a new window, which should trigger session restore. |
| chrome::NewEmptyWindow(profile); |
| Browser* new_browser = ui_test_utils::WaitForBrowserToOpen(); |
| ASSERT_TRUE(new_browser); |
| load_order_observer.WaitForAllTabsToStartLoading(); |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| |
| const auto& web_contents = load_order_observer.web_contents(); |
| |
| ASSERT_EQ(kExpectedNumTabs, web_contents.size()); |
| // Test that we have observed the tabs being loaded in the inverse order of |
| // their activation (MRU). Also validate that their last active time is in the |
| // correct order. |
| for (size_t i = 0; i < web_contents.size(); i++) { |
| GURL expected_url = GURL(kUrls[activation_order[kExpectedNumTabs - i - 1]]); |
| ASSERT_EQ(expected_url, web_contents[i]->GetLastCommittedURL()); |
| if (i > 0) { |
| ASSERT_GT(web_contents[i - 1]->GetLastActiveTimeTicks(), |
| web_contents[i]->GetLastActiveTimeTicks()); |
| } |
| } |
| |
| // Activate the 2nd tab before the browser closes. This should be persisted in |
| // the following test. |
| new_browser->tab_strip_model()->ActivateTabAt( |
| 1, TabStripUserGestureDetails( |
| TabStripUserGestureDetails::GestureType::kOther)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, MAYBE_CorrectLoadingOrder) { |
| const auto activation_order = std::to_array<int>({4, 2, 5, 0, 3, 1}); |
| Profile* profile = browser()->profile(); |
| |
| // Close the browser that gets opened automatically so we can track the order |
| // of loading of the tabs. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| CloseBrowserSynchronously(browser()); |
| // We have an extra tab that is added when the test starts, which gets ignored |
| // later when we test for proper order. |
| LoadOrderObserver load_order_observer(kExpectedNumTabs + 1); |
| |
| // Create a new window, which should trigger session restore. |
| chrome::NewEmptyWindow(profile); |
| Browser* new_browser = ui_test_utils::WaitForBrowserToOpen(); |
| ASSERT_TRUE(new_browser); |
| load_order_observer.WaitForAllTabsToStartLoading(); |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| |
| const auto& web_contents = load_order_observer.web_contents(); |
| |
| ASSERT_EQ(kExpectedNumTabs + 1, web_contents.size()); |
| |
| // Test that we have observed the tabs being loaded in the inverse order of |
| // their activation (MRU). Also validate that their last active time is in the |
| // correct order. |
| // |
| // Note that we ignore the first tab as it's an empty one that is added |
| // automatically at the start of the test. |
| for (size_t i = 1; i < web_contents.size(); i++) { |
| GURL expected_url = GURL(kUrls[activation_order[kExpectedNumTabs - i]]); |
| ASSERT_EQ(expected_url, web_contents[i]->GetLastCommittedURL()); |
| if (i > 0) { |
| ASSERT_GT(web_contents[i - 1]->GetLastActiveTimeTicks(), |
| web_contents[i]->GetLastActiveTimeTicks()); |
| } |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithURLInCommandLineTest, |
| PRE_TabWithURLFromCommandLineIsActive) { |
| SessionStartupPref pref(SessionStartupPref::DEFAULT); |
| SessionStartupPref::SetStartupPref(browser()->profile(), pref); |
| TabStripModel* tab_strip_model = browser()->tab_strip_model(); |
| // Add 3 pinned tabs. |
| for (const auto& url : {GetUrl1(), GetUrl2(), GetUrl3()}) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| tab_strip_model->SetTabPinned(tab_strip_model->active_index(), true); |
| } |
| EXPECT_EQ(4, tab_strip_model->count()); |
| EXPECT_EQ(3, tab_strip_model->IndexOfFirstNonPinnedTab()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithURLInCommandLineTest, |
| TabWithURLFromCommandLineIsActive) { |
| TabStripModel* tab_strip_model = browser()->tab_strip_model(); |
| EXPECT_EQ(4, tab_strip_model->count()); |
| EXPECT_EQ(3, tab_strip_model->active_index()); |
| EXPECT_EQ(command_line_url_, |
| tab_strip_model->GetActiveWebContents()->GetURL()); |
| |
| // Check that the all pinned tabs have been restored. |
| EXPECT_EQ(3, tab_strip_model->IndexOfFirstNonPinnedTab()); |
| EXPECT_EQ(GetUrl1(), tab_strip_model->GetWebContentsAt(0)->GetURL()); |
| EXPECT_EQ(GetUrl2(), tab_strip_model->GetWebContentsAt(1)->GetURL()); |
| EXPECT_EQ(GetUrl3(), tab_strip_model->GetWebContentsAt(2)->GetURL()); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // This test does not apply to ChromeOS as ChromeOS does not consider command |
| // line urls while determining startup tabs. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithURLInCommandLineTest, |
| PRE_StartupPrefSetAsLastAndURLs) { |
| SessionStartupPref pref(SessionStartupPref::LAST_AND_URLS); |
| pref.urls = {GetUrl1()}; |
| SessionStartupPref::SetStartupPref(browser()->profile(), pref); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| } |
| |
| // The startup pref urls shouldn't be opened if a command line url is supplied. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithURLInCommandLineTest, |
| StartupPrefSetAsLastAndURLs) { |
| EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); |
| TabStripModel* tab_strip_model = browser()->tab_strip_model(); |
| EXPECT_EQ(2, tab_strip_model->count()); |
| // The first tab is restored from the last session. |
| EXPECT_EQ(GetUrl2(), tab_strip_model->GetWebContentsAt(0)->GetURL()); |
| // The second tab is opened from the command line url. |
| EXPECT_EQ(command_line_url_, tab_strip_model->GetWebContentsAt(1)->GetURL()); |
| EXPECT_EQ(1, tab_strip_model->active_index()); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| class MultiOriginSessionRestoreTest : public SessionRestoreTest { |
| public: |
| MultiOriginSessionRestoreTest() |
| : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} |
| |
| MultiOriginSessionRestoreTest(const MultiOriginSessionRestoreTest&) = delete; |
| MultiOriginSessionRestoreTest& operator=( |
| const MultiOriginSessionRestoreTest&) = delete; |
| |
| void SetUpOnMainThread() override { |
| SessionRestoreTest::SetUpOnMainThread(); |
| |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| |
| https_test_server_.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_test_server_.Start()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| content::WebContents* GetTab(Browser* browser, int tab_index) { |
| DCHECK_LT(tab_index, browser->tab_strip_model()->count()); |
| return browser->tab_strip_model()->GetWebContentsAt(tab_index); |
| } |
| |
| std::string GetContent(Browser* browser, int tab_index) { |
| return EvalJs(GetTab(browser, tab_index), "document.body.innerText") |
| .ExtractString(); |
| } |
| |
| GURL GetSameOriginUrl(const std::string& path_and_query) { |
| return https_test_server_.GetURL(path_and_query); |
| } |
| |
| GURL GetCrossSiteUrl(const std::string& path_and_query) { |
| return embedded_test_server()->GetURL("another.origin.example.com", |
| path_and_query); |
| } |
| |
| private: |
| net::EmbeddedTestServer https_test_server_; |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Test that Sec-Fetch-Site http request header is correctly replayed during |
| // session restore. This is a regression test for https://crbug.com/976055. |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, SecFetchSite) { |
| GURL sec_fetch_url = GetSameOriginUrl("/echoheader?sec-fetch-site"); |
| |
| // Tab #1: Same-origin navigation. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), |
| GetSameOriginUrl("/title1.html"))); |
| { |
| content::WebContents* tab1 = GetTab(browser(), 0); |
| content::TestNavigationObserver nav_observer(tab1); |
| ASSERT_TRUE(content::ExecJs( |
| tab1, content::JsReplace("location = $1", sec_fetch_url))); |
| nav_observer.Wait(); |
| } |
| |
| // Tab #2: Cross-site navigation. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetCrossSiteUrl("/title1.html"), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| { |
| content::WebContents* tab2 = GetTab(browser(), 1); |
| content::TestNavigationObserver nav_observer(tab2); |
| ASSERT_TRUE(content::ExecJs( |
| tab2, content::JsReplace("location = $1", sec_fetch_url))); |
| nav_observer.Wait(); |
| } |
| |
| // Tab #3: Omnibox navigation. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), sec_fetch_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Verify that all the tabs have seen the expected Sec-Fetch-Site header. |
| ASSERT_EQ(3, browser()->tab_strip_model()->count()); |
| EXPECT_EQ("same-origin", GetContent(browser(), 0)); |
| EXPECT_EQ("cross-site", GetContent(browser(), 1)); |
| EXPECT_EQ("none", GetContent(browser(), 2)); |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| |
| // Verify again (after session restore) that all the tabs have seen the |
| // expected Sec-Fetch-Site header. This is the main verification for |
| // https://crbug.com/976055. |
| ASSERT_EQ(3, new_browser->tab_strip_model()->count()); |
| EXPECT_EQ("same-origin", GetContent(new_browser, 0)); |
| EXPECT_EQ("cross-site", GetContent(new_browser, 1)); |
| EXPECT_EQ("none", GetContent(new_browser, 2)); |
| } |
| |
| // Test that it is possible to navigate back to a restored about:blank history |
| // entry with a non-null initiator origin. This test cases covers the original |
| // repro steps reported in https://crbug.com/1026474. |
| // |
| // See also TabRestoreTest.BackToAboutBlank |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, BackToAboutBlank1) { |
| // Open about:blank in a new tab. |
| GURL initial_url = embedded_test_server()->GetURL("foo.com", "/title1.html"); |
| url::Origin initial_origin = url::Origin::Create(initial_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| content::WebContents* old_popup = nullptr; |
| content::WebContents* tab1 = GetTab(browser(), 0); |
| { |
| // Open a same-origin popup. |
| content::WebContentsAddedObserver popup_observer; |
| ASSERT_TRUE(ExecJs(tab1, "var w = window.open('/title2.html');")); |
| old_popup = popup_observer.GetWebContents(); |
| EXPECT_EQ(initial_origin, |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| EXPECT_TRUE(WaitForLoadStop(old_popup)); |
| } |
| |
| { |
| // Navigate the popup to about:blank, inheriting the opener origin. Note |
| // that we didn't immediately open the popup to about:blank to avoid making |
| // it use the initial NavigationEntry, which can't be navigated back to. |
| content::TestNavigationObserver nav_observer(old_popup); |
| ASSERT_TRUE(ExecJs(tab1, "w.location.href = 'about:blank';")); |
| nav_observer.Wait(); |
| EXPECT_EQ(GURL(url::kAboutBlankURL), |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(initial_origin, |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| } |
| |
| // Navigate the popup to another site. |
| GURL other_url = embedded_test_server()->GetURL("bar.com", "/title1.html"); |
| url::Origin other_origin = url::Origin::Create(other_url); |
| { |
| content::TestNavigationObserver nav_observer(old_popup); |
| ASSERT_TRUE(content::ExecJs( |
| old_popup, content::JsReplace("location = $1", other_url))); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(other_url, old_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(other_origin, |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| ASSERT_TRUE(old_popup->GetController().CanGoBack()); |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| content::WebContents* new_popup = GetTab(new_browser, 1); |
| old_popup = nullptr; |
| |
| // Verify that the restored popup hosts |other_url|. |
| EXPECT_EQ(other_url, new_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(other_origin, |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| ASSERT_TRUE(new_popup->GetController().CanGoBack()); |
| |
| // Navigate the popup back to about:blank. |
| { |
| content::TestNavigationObserver nav_observer(new_popup); |
| new_popup->GetController().GoBack(); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(GURL(url::kAboutBlankURL), |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(initial_origin, |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| } |
| |
| // Test that it is possible to navigate back to a restored about:blank history |
| // entry with a missing initiator origin. Note that this scenario did not hit |
| // the CHECK from https://crbug.com/1026474, because the CHECK is/was skipped |
| // for opaque origins (which would be the case for about:blank with a missing |
| // initiator origin). |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, |
| BackToAboutBlank1_Omnibox) { |
| // Browser-initiated navigation to about:blank. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL))); |
| content::WebContents* old_tab = GetTab(browser(), 0); |
| EXPECT_EQ(GURL(url::kAboutBlankURL), |
| old_tab->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_TRUE( |
| old_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque()); |
| |
| // Navigate the tab to another site. |
| GURL other_url = embedded_test_server()->GetURL("bar.com", "/title1.html"); |
| url::Origin other_origin = url::Origin::Create(other_url); |
| { |
| content::TestNavigationObserver nav_observer(old_tab); |
| ASSERT_TRUE(content::ExecJs( |
| old_tab, content::JsReplace("location = $1", other_url))); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(other_url, old_tab->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(other_origin, |
| old_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| ASSERT_TRUE(old_tab->GetController().CanGoBack()); |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| content::WebContents* new_tab = GetTab(new_browser, 0); |
| old_tab = nullptr; |
| |
| // Verify that the restored popup hosts |other_url|. |
| EXPECT_EQ(other_url, new_tab->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(other_origin, |
| new_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| ASSERT_TRUE(new_tab->GetController().CanGoBack()); |
| |
| // Navigate the popup back to about:blank. |
| { |
| content::TestNavigationObserver nav_observer(new_tab); |
| new_tab->GetController().GoBack(); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(GURL(url::kAboutBlankURL), |
| new_tab->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_TRUE( |
| new_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque()); |
| } |
| |
| // Test that it is possible to navigate back to a restored about:blank history |
| // entry with a non-null initiator origin. This test cases covers the variant |
| // of the repro that was reported in https://crbug.com/1016954#c27. |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, BackToAboutBlank2) { |
| // Open about:blank#foo in a new tab. |
| // |
| // Note that about:blank (rather than about:blank#foo) wouldn't work, because |
| // about:blank is treated by the renderer-side as an initial, empty history |
| // entry and replaced during the navigation to |other_url| below. |
| GURL initial_url = embedded_test_server()->GetURL("foo.com", "/title1.html"); |
| url::Origin initial_origin = url::Origin::Create(initial_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| { |
| content::WebContents* tab1 = GetTab(browser(), 0); |
| content::WebContentsAddedObserver popup_observer; |
| ASSERT_TRUE(ExecJs(tab1, "window.open('about:blank#foo')")); |
| content::WebContents* old_popup = popup_observer.GetWebContents(); |
| EXPECT_EQ(GURL("about:blank#foo"), |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(initial_origin, |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| } |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(2, new_browser->tab_strip_model()->count()); |
| content::WebContents* new_popup = GetTab(new_browser, 1); |
| |
| // Verify that the restored popup hosts about:blank#foo. |
| EXPECT_EQ(GURL("about:blank#foo"), |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(initial_origin, |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| |
| // Navigate the popup to another site. |
| GURL other_url = embedded_test_server()->GetURL("bar.com", "/title1.html"); |
| url::Origin other_origin = url::Origin::Create(other_url); |
| { |
| content::TestNavigationObserver nav_observer(new_popup); |
| ASSERT_TRUE(content::ExecJs( |
| new_popup, content::JsReplace("location = $1", other_url))); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(other_url, new_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(other_origin, |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| ASSERT_TRUE(new_popup->GetController().CanGoBack()); |
| |
| // Navigate the popup back to about:blank#foo. |
| { |
| content::TestNavigationObserver nav_observer(new_popup); |
| new_popup->GetController().GoBack(); |
| nav_observer.Wait(); |
| } |
| EXPECT_EQ(GURL("about:blank#foo"), |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(initial_origin, |
| new_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| } |
| |
| // Test that it is possible to navigate back to a subframe with a restored |
| // about:blank history entry with a non-null initiator origin - see |
| // https://crbug.com/1026474. |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, |
| BackToAboutBlankSubframe) { |
| // Navigate to a.com(a.com/title2.html). |
| GURL main_url = embedded_test_server()->GetURL( |
| "a.com", "/frame_tree/page_with_samesite_frame.html"); |
| url::Origin a_origin = url::Origin::Create(main_url); |
| GURL subframe_url = embedded_test_server()->GetURL("a.com", "/title2.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| content::WebContents* old_tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHostWrapper old_tab_main_frame( |
| old_tab->GetPrimaryMainFrame()); |
| EXPECT_EQ(main_url, old_tab_main_frame->GetLastCommittedURL()); |
| EXPECT_EQ(a_origin, old_tab_main_frame->GetLastCommittedOrigin()); |
| content::RenderFrameHost* subframe = |
| ChildFrameAt(old_tab_main_frame.get(), 0); |
| ASSERT_TRUE(subframe); |
| EXPECT_EQ(subframe_url, subframe->GetLastCommittedURL()); |
| EXPECT_EQ(a_origin, subframe->GetLastCommittedOrigin()); |
| |
| // Have main frame initiate navigating the subframe to about:blank. |
| // Expected state after the navigation: a.com(a.com-blank). |
| { |
| content::TestNavigationObserver nav_observer(old_tab); |
| ASSERT_TRUE( |
| content::ExecJs(old_tab, "window.open('about:blank', 'subframe');")); |
| nav_observer.Wait(); |
| } |
| subframe = ChildFrameAt(old_tab_main_frame.get(), 0); |
| EXPECT_EQ(GURL(url::kAboutBlankURL), subframe->GetLastCommittedURL()); |
| EXPECT_EQ(a_origin, subframe->GetLastCommittedOrigin()); |
| |
| // Have subframe (about:blank from a.com origin) navigate itself to c.com. |
| // Expected state after the navigation: a.com(c.com). |
| GURL c_url = embedded_test_server()->GetURL("c.com", "/title3.html"); |
| url::Origin c_origin = url::Origin::Create(c_url); |
| { |
| content::TestNavigationObserver nav_observer(old_tab); |
| ASSERT_TRUE( |
| content::ExecJs(subframe, content::JsReplace("location = $1;", c_url))); |
| nav_observer.Wait(); |
| } |
| subframe = ChildFrameAt(old_tab_main_frame.get(), 0); |
| ASSERT_TRUE(subframe); |
| EXPECT_EQ(c_url, subframe->GetLastCommittedURL()); |
| EXPECT_EQ(c_origin, subframe->GetLastCommittedOrigin()); |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| ASSERT_TRUE(old_tab->GetController().CanGoBack()); |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| content::WebContents* new_tab = |
| new_browser->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(new_tab->GetController().CanGoBack()); |
| old_tab = nullptr; |
| |
| content::RenderFrameHost* new_tab_main_frame = new_tab->GetPrimaryMainFrame(); |
| // Verify that the restored tab hosts: a.com(c.com). |
| EXPECT_EQ(main_url, new_tab_main_frame->GetLastCommittedURL()); |
| EXPECT_EQ(a_origin, new_tab_main_frame->GetLastCommittedOrigin()); |
| subframe = ChildFrameAt(new_tab_main_frame, 0); |
| ASSERT_TRUE(subframe); |
| EXPECT_EQ(c_url, subframe->GetLastCommittedURL()); |
| EXPECT_EQ(c_origin, subframe->GetLastCommittedOrigin()); |
| |
| // Check that main frame and subframe are in the same BrowsingInstance. |
| content::SiteInstance* subframe_instance_c = subframe->GetSiteInstance(); |
| EXPECT_TRUE(new_tab_main_frame->GetSiteInstance()->IsRelatedSiteInstance( |
| subframe_instance_c)); |
| |
| // Go back - this should reach: a.com(a.com-blank). |
| { |
| content::TestNavigationObserver nav_observer(new_tab); |
| new_tab->GetController().GoBack(); |
| nav_observer.Wait(); |
| } |
| subframe = ChildFrameAt(new_tab_main_frame, 0); |
| ASSERT_TRUE(subframe); |
| EXPECT_EQ(GURL(url::kAboutBlankURL), subframe->GetLastCommittedURL()); |
| EXPECT_EQ(a_origin, subframe->GetLastCommittedOrigin()); |
| |
| // Check that we're still in the same BrowsingInstance. |
| EXPECT_TRUE(new_tab_main_frame->GetSiteInstance()->IsRelatedSiteInstance( |
| subframe->GetSiteInstance())); |
| EXPECT_TRUE( |
| subframe_instance_c->IsRelatedSiteInstance(subframe->GetSiteInstance())); |
| } |
| |
| // Tests that an initial NavigationEntry does not get restored. |
| IN_PROC_BROWSER_TEST_F(MultiOriginSessionRestoreTest, RestoreInitialEntry) { |
| GURL main_url = embedded_test_server()->GetURL("foo.com", "/title1.html"); |
| url::Origin main_origin = url::Origin::Create(main_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| content::WebContents* old_popup = nullptr; |
| { |
| // Open a new window that stays on the initial NavigationEntry. |
| content::WebContents* tab1 = GetTab(browser(), 0); |
| content::WebContentsAddedObserver popup_observer; |
| ASSERT_TRUE(ExecJs(tab1, "window.open('/nocontent')")); |
| old_popup = popup_observer.GetWebContents(); |
| EXPECT_EQ(GURL(), old_popup->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(main_origin, |
| old_popup->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| EXPECT_TRUE( |
| old_popup->GetController().GetLastCommittedEntry()->IsInitialEntry()); |
| } |
| |
| // Kill the original browser then open a new one to trigger a restore. |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| // We only restore 1 tab, the main tab. |
| ASSERT_EQ(1, new_browser->tab_strip_model()->count()); |
| content::WebContents* new_browser_tab = GetTab(new_browser, 0); |
| old_popup = nullptr; |
| |
| // Verify that the restored tab is the main tab instead of the initial |
| // NavigationEntry tab. |
| EXPECT_EQ(main_url, |
| new_browser_tab->GetPrimaryMainFrame()->GetLastCommittedURL()); |
| EXPECT_EQ(main_origin, |
| new_browser_tab->GetPrimaryMainFrame()->GetLastCommittedOrigin()); |
| EXPECT_FALSE(new_browser_tab->GetController() |
| .GetLastCommittedEntry() |
| ->IsInitialEntry()); |
| } |
| |
| // Check that TabManager.TimeSinceTabClosedUntilRestored histogram is not |
| // recorded on session restore. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| TimeSinceTabClosedUntilRestoredNotRecorded) { |
| base::HistogramTester histogram_tester; |
| const char kTimeSinceTabClosedUntilRestored[] = |
| "TabManager.TimeSinceTabClosedUntilRestored"; |
| |
| // Add several tabs to the browser. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restart the browser and check that the metric is not recorded. |
| EXPECT_EQ( |
| histogram_tester.GetAllSamples(kTimeSinceTabClosedUntilRestored).size(), |
| 0U); |
| |
| QuitBrowserAndRestore(browser(), GURL(), true); |
| |
| EXPECT_EQ( |
| histogram_tester.GetAllSamples(kTimeSinceTabClosedUntilRestored).size(), |
| 0U); |
| } |
| |
| // Check that TabManager.TimeSinceWindowClosedUntilRestored histogram is not |
| // recorded on session restore. |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, |
| TimeSinceWindowClosedUntilRestoredNotRecorded) { |
| base::HistogramTester histogram_tester; |
| const char kTimeSinceWindowClosedUntilRestored[] = |
| "TabManager.TimeSinceWindowClosedUntilRestored"; |
| |
| // Add several tabs to the browser. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Restart the browser and check that the metric is not recorded. |
| EXPECT_EQ(histogram_tester.GetAllSamples(kTimeSinceWindowClosedUntilRestored) |
| .size(), |
| 0U); |
| |
| QuitBrowserAndRestore(browser(), GURL(), true); |
| |
| EXPECT_EQ(histogram_tester.GetAllSamples(kTimeSinceWindowClosedUntilRestored) |
| .size(), |
| 0U); |
| } |
| |
| // This class and tests are to verify reading a file with an error in it results |
| // in a restore and logging the error. The file was created from the test |
| // TwoTabsSecondSelected with an extra command at the end of the file that does |
| // not have the right amount of data in it. This should still result in a |
| // restore. |
| // |
| // The test is split in to two parts. An empty one whose sole purpose is to |
| // ensure on the second run the profile is not marked as new. This is necessary |
| // as the startup flow won't honor kRestoreLastSession for a new profile. |
| // The real test copies the file and verifies restore. |
| class SessionRestoreWithIncompleteFileTest : public InProcessBrowserTest { |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch(switches::kRestoreLastSession); |
| } |
| bool SetUpUserDataDirectory() override { |
| const bool result = InProcessBrowserTest::SetUpUserDataDirectory(); |
| if (!result) |
| return false; |
| |
| // Copy a file over that has an incomplete command. The file should still |
| // be read, but a read error should be logged. |
| base::FilePath user_data_dir; |
| EXPECT_TRUE(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)); |
| base::FilePath sessions_dir = |
| user_data_dir.AppendASCII(TestingProfile::kTestUserProfileDir); |
| if (!base::DeletePathRecursively( |
| sessions_dir.Append(sessions::kSessionsDirectory))) { |
| ADD_FAILURE() << "Unable to delete sessions directory"; |
| return false; |
| } |
| if (!base::CreateDirectory(sessions_dir)) { |
| ADD_FAILURE() << "Unable to create sessions directory"; |
| return false; |
| } |
| base::FilePath session_file_path = sessions_dir.Append( |
| base::FilePath(sessions::kLegacyCurrentSessionFileName)); |
| base::FilePath data_dir; |
| if (!base::PathService::Get(chrome::DIR_TEST_DATA, &data_dir)) { |
| ADD_FAILURE() << "Unable to get data dir"; |
| return false; |
| } |
| base::FilePath bogus_file_path = |
| data_dir.AppendASCII("sessions") |
| .AppendASCII("file_with_incomplete_command"); |
| if (!base::CopyFile(bogus_file_path, session_file_path)) { |
| ADD_FAILURE() << "Unable to copy file for test"; |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithIncompleteFileTest, |
| PRE_LogsReadError) { |
| // Does nothing as kRestoreLastSession is only used if the profile is not a |
| // new (just created) profile. At this point the profile is new, but after the |
| // restart and LogsReadError() runs it will no longer be new. |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreWithIncompleteFileTest, LogsReadError) { |
| // The file that was copied over should result in two tabs with the second |
| // selected (mirrors that of TwoTabsSecondSelected). It has one extra tab as |
| // the startup path appends a url to the commandline, which triggers another |
| // tab. The first two tabs come from the saved session file. |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| ASSERT_EQ(3, browser()->tab_strip_model()->count()); |
| // Use ExtractFileName() as the path written to the restore file is from a |
| // local build. |
| EXPECT_EQ("bot1.html", browser() |
| ->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetURL() |
| .ExtractFileName()); |
| EXPECT_EQ("bot2.html", browser() |
| ->tab_strip_model() |
| ->GetWebContentsAt(1) |
| ->GetURL() |
| .ExtractFileName()); |
| // The tab at index 2 is the one created by startup. |
| |
| // Ensure there is a restore event |
| auto events = GetSessionServiceEvents(browser()->profile()); |
| for (const SessionServiceEvent& event : base::Reversed(events)) { |
| // For normal shutdown (as this test triggers) kRestore should always occur |
| // after kExit. This iterates in reverse, so that kRestore should occur |
| // first. |
| ASSERT_NE(SessionServiceEventLogType::kExit, event.type); |
| if (event.type == SessionServiceEventLogType::kRestore) { |
| EXPECT_EQ(1, event.data.restore.encountered_error_reading); |
| break; |
| } |
| } |
| } |
| |
| // TODO(crbug.com/41488859): Test fails on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SameDocumentNavigationWithNothingCommittedAfterRestore \ |
| DISABLED_SameDocumentNavigationWithNothingCommittedAfterRestore |
| #else |
| #define MAYBE_SameDocumentNavigationWithNothingCommittedAfterRestore \ |
| SameDocumentNavigationWithNothingCommittedAfterRestore |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| SessionRestoreTest, |
| MAYBE_SameDocumentNavigationWithNothingCommittedAfterRestore) { |
| // The test sets this closure before each navigation to /sometimes-slow in |
| // order to control the response for that navigation. |
| content::SlowHttpResponse::GotRequestCallback got_slow_request; |
| |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [&](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != "/sometimes-slow") |
| return nullptr; |
| DCHECK(got_slow_request) |
| << "Set `got_slow_request` before each navigation request."; |
| return std::make_unique<content::SlowHttpResponse>( |
| std::move(got_slow_request)); |
| })); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL url1 = embedded_test_server()->GetURL("/sometimes-slow"); |
| GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo"); |
| |
| content::WebContents* wc = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Successfully navigate to `url1`. |
| got_slow_request = content::SlowHttpResponse::FinishResponseImmediately(); |
| EXPECT_TRUE(NavigateToURL(wc, url1)); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // We perform session restore under memory pressure so the tab is not restored |
| // in the background. |
| Browser* new_browser = |
| QuitBrowserAndRestore(browser(), GURL(), /*no_memory_pressure=*/false); |
| wc = new_browser->tab_strip_model()->GetWebContentsAt(0); |
| EXPECT_EQ(2, new_browser->tab_strip_model()->count()); |
| EXPECT_NE(0, new_browser->tab_strip_model()->active_index()); |
| |
| // Now we reactivate the tab. This brings the process back to life for the |
| // current RenderFrameHost, but we prevent it from loading before we perform a |
| // navigation. |
| { |
| base::RunLoop loop; |
| got_slow_request = |
| base::BindLambdaForTesting([&](base::OnceClosure start_response, |
| base::OnceClosure finish_response) { |
| // Never starts the response, but informs the test the request has |
| // been received. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce([](base::RunLoop* loop) { loop->Quit(); }, &loop), |
| base::Seconds(1)); |
| // loop.Quit(); |
| }); |
| new_browser->tab_strip_model()->ActivateTabAt(0); |
| loop.Run(); |
| } |
| // The navigation has not completed, but the renderer has come alive. |
| EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive()); |
| EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), ""); |
| |
| // Now try to navigate to `url2`. We're currently trying to load `url1` since |
| // the above navigation will be delayed. Going to `url2` should be a |
| // same-document navigation according to the urls alone. But it can't be since |
| // the current frame host does not actually have a document loaded. |
| content::NavigationHandleCommitObserver nav_observer(wc, url2); |
| { |
| content::NavigationController::LoadURLParams params(url2); |
| params.transition_type = ui::PageTransitionFromInt( |
| ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); |
| |
| got_slow_request = content::SlowHttpResponse::FinishResponseImmediately(); |
| wc->GetController().LoadURLWithParams(params); |
| } |
| EXPECT_TRUE(WaitForLoadStop(wc)); |
| EXPECT_TRUE(nav_observer.has_committed()); |
| EXPECT_FALSE(nav_observer.was_same_document()); |
| } |
| |
| // TODO(crbug.com/41488859): Test fails on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SameDocumentHistoryNavigationWithNothingCommittedAfterRestore \ |
| DISABLED_SameDocumentHistoryNavigationWithNothingCommittedAfterRestore |
| #else |
| #define MAYBE_SameDocumentHistoryNavigationWithNothingCommittedAfterRestore \ |
| SameDocumentHistoryNavigationWithNothingCommittedAfterRestore |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| SessionRestoreTest, |
| MAYBE_SameDocumentHistoryNavigationWithNothingCommittedAfterRestore) { |
| // The test sets this closure before each navigation to /sometimes-slow in |
| // order to control the response for that navigation. |
| content::SlowHttpResponse::GotRequestCallback got_slow_request; |
| |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [&](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != "/sometimes-slow") |
| return nullptr; |
| DCHECK(got_slow_request) |
| << "Set `got_slow_request` before each navigation request."; |
| return std::make_unique<content::SlowHttpResponse>( |
| std::move(got_slow_request)); |
| })); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL url1 = embedded_test_server()->GetURL("/sometimes-slow"); |
| GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo"); |
| |
| content::WebContents* wc = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Successfully navigate to `url1`, then do a same-document navigation to |
| // `url2`. |
| got_slow_request = content::SlowHttpResponse::FinishResponseImmediately(); |
| EXPECT_TRUE(NavigateToURL(wc, url1)); |
| EXPECT_TRUE(NavigateToURL(wc, url2)); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url::kAboutBlankURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // We perform session restore under memory pressure so the tab is not restored |
| // in the background. |
| Browser* new_browser = |
| QuitBrowserAndRestore(browser(), GURL(), /*no_memory_pressure=*/false); |
| wc = new_browser->tab_strip_model()->GetWebContentsAt(0); |
| EXPECT_EQ(2, new_browser->tab_strip_model()->count()); |
| EXPECT_NE(0, new_browser->tab_strip_model()->active_index()); |
| |
| // Now we reactivate the tab. This brings the process back to life for the |
| // current RenderFrameHost, but we prevent it from loading before we perform a |
| // navigation. |
| { |
| base::RunLoop loop; |
| got_slow_request = |
| base::BindLambdaForTesting([&](base::OnceClosure start_response, |
| base::OnceClosure finish_response) { |
| // Never starts the response, but informs the test the request has |
| // been received. |
| loop.Quit(); |
| }); |
| new_browser->tab_strip_model()->ActivateTabAt(0); |
| loop.Run(); |
| } |
| // The navigation has not completed, but the renderer has come alive. |
| EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive()); |
| EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), ""); |
| |
| content::NavigationHandleCommitObserver back_observer(wc, url1); |
| // Now try to go back. We're currently at `url2`, so going back to `url1` |
| // should be a same-document history navigation according to the |
| // NavigationEntry. But it can't be since the current frame host does not |
| // actually have a document loaded. |
| got_slow_request = content::SlowHttpResponse::FinishResponseImmediately(); |
| wc->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(wc)); |
| EXPECT_TRUE(back_observer.has_committed()); |
| EXPECT_FALSE(back_observer.was_same_document()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreTest, OmitFromSessionRestore) { |
| // Tests start with one browser window; navigate it to url 1. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_EQ( |
| GetUrl1(), |
| browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL()); |
| |
| // Make a second window; navigate it to url 2. |
| Browser* browser2 = CreateBrowser(browser()->profile()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser2, GetUrl2(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser2->tab_strip_model()->count()); |
| EXPECT_EQ( |
| GetUrl2(), |
| browser2->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL()); |
| |
| // Make a third window that is omitted from session restore; navigate it to |
| // url 3. |
| Browser::CreateParams params(browser()->profile(), true); |
| params.omit_from_session_restore = true; |
| Browser* browser3 = Browser::Create(params); |
| content::WebContents* tab = chrome::AddSelectedTabWithURL( |
| browser3, GURL(GetUrl3()), ui::PAGE_TRANSITION_AUTO_TOPLEVEL); |
| content::TestNavigationObserver observer(tab); |
| observer.Wait(); |
| ASSERT_EQ(1, browser3->tab_strip_model()->count()); |
| EXPECT_EQ( |
| GetUrl3(), |
| browser3->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL()); |
| |
| // Simulate an exit by shutting down the session service. If we don't do this |
| // the first two window closes are treated as though the user closed the |
| // windows and won't be restored. |
| SessionServiceFactory::ShutdownForProfile(browser()->profile()); |
| |
| // Then close all the browsers and "restart" Chromium. |
| CloseBrowserSynchronously(browser3); |
| CloseBrowserSynchronously(browser2); |
| QuitBrowserAndRestore(browser()); |
| |
| // The first two browsers with url1 and url2 should be back, but the browser |
| // with url3 shouldn't. |
| ASSERT_EQ(2u, active_browser_list_->size()); |
| EXPECT_EQ(GetUrl1(), active_browser_list_->get(0) |
| ->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetLastCommittedURL()); |
| EXPECT_EQ(GetUrl2(), active_browser_list_->get(1) |
| ->tab_strip_model() |
| ->GetWebContentsAt(0) |
| ->GetLastCommittedURL()); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Skip for ChromeOS because the keep alive is not created for ChromeOS. |
| // See https://crbug.com/1174627. |
| class SessionRestoreSilentLaunchTest : public SessionRestoreTest { |
| protected: |
| // SessionRestoreTest: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| SessionRestoreTest::SetUpCommandLine(command_line); |
| if (GetTestPreCount() == 1) |
| command_line->AppendSwitch(switches::kNoStartupWindow); |
| } |
| |
| ExitType GetLastSessionExitType() { |
| return GetExitTypeService()->last_session_exit_type(); |
| } |
| |
| bool IsSessionServiceSavingEnabled() { |
| SessionService* session_service = SessionServiceFactory::GetForProfile( |
| ProfileManager::GetLastUsedProfile()); |
| return session_service && session_service->is_saving_enabled(); |
| } |
| |
| ExitTypeService* GetExitTypeService() { |
| return ExitTypeService::GetInstanceForProfile( |
| ProfileManager::GetLastUsedProfile()); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreSilentLaunchTest, |
| PRE_PRE_SilentLaunchAfterCrash) { |
| // Marks session as crashed. |
| ExitTypeService::GetInstanceForProfile(browser()->profile()) |
| ->SetWaitingForUserToAckCrashForTest(true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreSilentLaunchTest, |
| PRE_SilentLaunchAfterCrash) { |
| // This is a launch with no startup windows. Last session should remain |
| // crashed, and saving should be disabled. |
| EXPECT_EQ(ExitType::kCrashed, GetLastSessionExitType()); |
| EXPECT_FALSE(IsSessionServiceSavingEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreSilentLaunchTest, SilentLaunchAfterCrash) { |
| // Last session still crashed, and saving disabled. |
| EXPECT_EQ(ExitType::kCrashed, GetLastSessionExitType()); |
| EXPECT_FALSE(IsSessionServiceSavingEnabled()); |
| } |
| #endif |
| |
| class AppSessionRestoreTest : public SessionRestoreTest { |
| public: |
| AppSessionRestoreTest() = default; |
| ~AppSessionRestoreTest() override = default; |
| |
| protected: |
| void ShutdownServices(Profile* profile) { |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down |
| // for real. |
| AppSessionServiceFactory::ShutdownForProfile(profile); |
| SessionServiceFactory::ShutdownForProfile(profile); |
| } |
| |
| void StartupServices(Profile* profile) { |
| // We need to start up the services again before restoring. |
| AppSessionServiceFactory::GetForProfileForSessionRestore(profile); |
| SessionServiceFactory::GetForProfileForSessionRestore(profile); |
| } |
| |
| webapps::AppId InstallPWA(Profile* profile, const GURL& start_url) { |
| auto web_app_info = |
| web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(start_url); |
| web_app_info->scope = start_url.GetWithoutFilename(); |
| web_app_info->user_display_mode = |
| web_app::mojom::UserDisplayMode::kStandalone; |
| web_app_info->title = u"A Web App"; |
| return web_app::test::InstallWebApp(profile, std::move(web_app_info)); |
| } |
| |
| webapps::AppId InstallTabbedPWA(Profile* profile, const GURL& start_url) { |
| blink::Manifest::TabStrip tab_strip; |
| tab_strip.home_tab = blink::Manifest::HomeTabParams(); |
| |
| auto web_app_info = |
| web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(start_url); |
| web_app_info->scope = start_url.GetWithoutFilename(); |
| web_app_info->user_display_mode = |
| web_app::mojom::UserDisplayMode::kStandalone; |
| web_app_info->display_override = {blink::mojom::DisplayMode::kTabbed}; |
| web_app_info->title = u"A Web App"; |
| web_app_info->tab_strip = std::move(tab_strip); |
| return web_app::test::InstallWebApp(profile, std::move(web_app_info)); |
| } |
| |
| private: |
| web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_; |
| }; |
| |
| // This is disabled on mac pending http://crbug.com/1194201 |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_BasicAppSessionRestore DISABLED_BasicAppSessionRestore |
| #else |
| #define MAYBE_BasicAppSessionRestore BasicAppSessionRestore |
| #endif |
| // A simple test that apps are being tracked by AppSessionService correctly. |
| // Open 1 app for a total of 1 app, 1 browser. |
| // Do a simulated shutdown and restore, check for 2 apps 2 browsers. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, MAYBE_BasicAppSessionRestore) { |
| Profile* profile = browser()->profile(); |
| |
| auto example_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| EXPECT_TRUE(app_browser->is_type_app()); |
| |
| // At this point, we have a browser and an app. |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| int apps = 0; |
| int browsers = 0; |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_APP) { |
| apps++; |
| EXPECT_EQ(browser, app_browser); |
| } else if (browser->type() == Browser::Type::TYPE_NORMAL) { |
| browsers++; |
| EXPECT_EQ(browser->tab_strip_model()->count(), 2); |
| auto tab1_url = browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(); |
| auto tab2_url = browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(); |
| EXPECT_EQ(tab2_url, example_url2); |
| } |
| } |
| EXPECT_EQ(browsers, 1); |
| EXPECT_EQ(apps, 1); |
| |
| // Pretend to 'close the browser'. |
| ShutdownServices(profile); |
| |
| // Now trigger a restore. |
| // We need to start up the services again before restoring. |
| StartupServices(profile); |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| // We should get +1 browser +1 app. |
| // Ensure the apps are the same, and ensure the browsers are the same. |
| ASSERT_EQ(4u, BrowserList::GetInstance()->size()); |
| apps = 0; |
| browsers = 0; |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_APP) { |
| EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(browser, app_id)); |
| apps++; |
| auto url = browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(); |
| EXPECT_EQ(url, example_url); |
| } else if (browser->type() == Browser::Type::TYPE_NORMAL) { |
| browsers++; |
| // Every browser should look the same, with two tabs and example_url2 |
| // on the second tab. |
| EXPECT_EQ(browser->tab_strip_model()->count(), 2); |
| auto tab1_url = browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(); |
| auto tab2_url = browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(); |
| EXPECT_EQ(tab2_url, example_url2); |
| } |
| } |
| EXPECT_EQ(browsers, 2); |
| EXPECT_EQ(apps, 2); |
| } |
| |
| // This feature is only available on ChromeOS. |
| // This test opens an unclosable app and ensures that it is not restored. |
| #if BUILDFLAG(IS_CHROMEOS) |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, DontTrackUnclosableApp) { |
| Profile* profile = browser()->profile(); |
| |
| // Make sure the app is unclosable when before it is launched to influence the |
| // tracking for session restore. |
| { |
| web_app::WebAppTestInstallObserver observer(profile); |
| observer.BeginListening({ash::kCalculatorAppId}); |
| |
| base::Value::List web_app_settings = base::JSONReader::Read(R"([ |
| { |
| "manifest_id": "https://calculator.apps.chrome/", |
| "run_on_os_login": "run_windowed", |
| "prevent_close_after_run_on_os_login": true |
| } |
| ])") |
| ->GetList() |
| .Clone(); |
| profile->GetPrefs()->SetList(prefs::kWebAppSettings, |
| std::move(web_app_settings)); |
| |
| base::Value::List web_app_install_list = base::JSONReader::Read(R"([ |
| { |
| "url": "https://calculator.apps.chrome/", |
| "default_launch_container": "window" |
| } |
| ])") |
| ->GetList() |
| .Clone(); |
| profile->GetPrefs()->SetList(prefs::kWebAppInstallForceList, |
| std::move(web_app_install_list)); |
| |
| observer.Wait(); |
| } |
| |
| // Open a PWA. |
| Browser* app_browser = |
| web_app::LaunchWebAppBrowserAndWait(profile, ash::kCalculatorAppId); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Remove unclosability setting. The browser should still not be restored |
| // because the app window was not tracked when the browser was closed. |
| profile->GetPrefs()->SetList(prefs::kWebAppSettings, base::Value::List()); |
| |
| // Now that SessionServices are off, we can close stuff to simulate a closure. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now trigger a restore. |
| // We need to start up the services again before restoring. |
| StartupServices(profile); |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| EXPECT_NE(browser->type(), Browser::Type::TYPE_APP); |
| } |
| EXPECT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, DontRestoreUnclosableApp) { |
| Profile* profile = browser()->profile(); |
| |
| { |
| web_app::WebAppTestInstallObserver observer(profile); |
| observer.BeginListening({ash::kCalculatorAppId}); |
| |
| base::Value::List web_app_install_list = base::JSONReader::Read(R"([ |
| { |
| "url": "https://calculator.apps.chrome/", |
| "default_launch_container": "window" |
| } |
| ])") |
| ->GetList() |
| .Clone(); |
| |
| profile->GetPrefs()->SetList(prefs::kWebAppInstallForceList, |
| std::move(web_app_install_list)); |
| |
| observer.Wait(); |
| } |
| |
| // Open a PWA. |
| Browser* app_browser = |
| web_app::LaunchWebAppBrowserAndWait(profile, ash::kCalculatorAppId); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Now that SessionServices are off, we can close stuff to simulate a closure. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now trigger a restore. |
| // We need to start up the services again before restoring. |
| StartupServices(profile); |
| |
| { |
| base::Value::List web_app_settings = base::JSONReader::Read(R"([ |
| { |
| "manifest_id": "https://calculator.apps.chrome/", |
| "run_on_os_login": "run_windowed", |
| "prevent_close_after_run_on_os_login": true |
| } |
| ])") |
| ->GetList() |
| .Clone(); |
| profile->GetPrefs()->SetList(prefs::kWebAppSettings, |
| std::move(web_app_settings)); |
| } |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| EXPECT_NE(browser->type(), Browser::Type::TYPE_APP); |
| } |
| EXPECT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // This is disabled on mac pending http://crbug.com/1194201 |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_IsolatedFromBrowserRestore DISABLED_IsolatedFromBrowserRestore |
| #else |
| #define MAYBE_IsolatedFromBrowserRestore IsolatedFromBrowserRestore |
| #endif |
| // This test ensures browser sessions are preserved no matter what happens to |
| // apps. In particular this is important when apps are still open when there are |
| // no browser windows open, then a browser reopens. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, |
| MAYBE_IsolatedFromBrowserRestore) { |
| Profile* profile = browser()->profile(); |
| auto example_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| |
| // Add a tab so we can recognize if this browser gets restored correctly. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Open a PWA. |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| // App #1 |
| web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| // Close the browser. |
| CloseBrowserSynchronously(browser()); |
| |
| // Open a few more PWAs. |
| web_app::LaunchWebAppBrowserAndWait(profile, app_id); // App #2 |
| web_app::LaunchWebAppBrowserAndWait(profile, app_id); // #3 |
| web_app::LaunchWebAppBrowserAndWait(profile, app_id); // #4 |
| |
| // Verify there are 4 apps. |
| ASSERT_EQ(4u, BrowserList::GetInstance()->size()); |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| ASSERT_EQ(browser->type(), Browser::Type::TYPE_APP); |
| EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(browser, app_id)); |
| } |
| |
| // Now open the browser window. |
| // This should be treated the same as a browser opening when nothing, |
| // i.e. it will trigger a browser restore. |
| SessionRestoreTestHelper restore_observer; |
| chrome::NewEmptyWindow(profile); |
| restore_observer.Wait(); |
| |
| // Ensure there's 4 apps and 1 restored window |
| int apps = 0; |
| int browsers = 0; |
| Browser* restored_browser = nullptr; |
| ASSERT_EQ(5u, BrowserList::GetInstance()->size()); |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_NORMAL) { |
| restored_browser = browser; |
| browsers++; |
| } else if (browser->type() == Browser::Type::TYPE_APP) { |
| apps++; |
| } |
| } |
| ASSERT_EQ(apps, 4); |
| ASSERT_EQ(browsers, 1); |
| ASSERT_NE(restored_browser, nullptr); |
| |
| // now check we restored the browser correctly. |
| EXPECT_EQ(restored_browser->tab_strip_model()->count(), 2); |
| auto tab1_url = |
| restored_browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(); |
| auto tab2_url = |
| restored_browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(); |
| EXPECT_EQ(tab2_url, example_url2); |
| } |
| |
| // This is disabled on mac pending http://crbug.com/1194201 |
| // These tests currently fail on linux due to http://crbug.com/1196493. |
| // To keep the coverage from the rest of the test, we disable the failing check |
| // on linux for window-maximization. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RestoreAppMinimized DISABLED_RestoreAppMinimized |
| #else |
| #define MAYBE_RestoreAppMinimized RestoreAppMinimized |
| #endif |
| // This test minimizes an app, ensures it restores correctly. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, MAYBE_RestoreAppMinimized) { |
| Profile* profile = browser()->profile(); |
| auto example_url = GURL("https://www.example.com"); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Open a PWA. |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| app_browser->window()->Minimize(); |
| ASSERT_TRUE(ui_test_utils::WaitForMinimized(app_browser)); |
| EXPECT_TRUE(app_browser->window()->IsMinimized()); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| // Now that SessionServices are off, we can close stuff to simulate a closure. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| StartupServices(profile); |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| // It opens up the browser and the app. |
| app_browser = nullptr; |
| Browser* normal_browser = nullptr; |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_APP) { |
| EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(browser, app_id)); |
| #if !BUILDFLAG(IS_LINUX) |
| EXPECT_TRUE(ui_test_utils::WaitForMinimized(browser)); |
| EXPECT_TRUE(browser->window()->IsMinimized()); |
| #endif |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(), |
| example_url); |
| app_browser = browser; |
| } else { |
| normal_browser = browser; |
| } |
| } |
| |
| ASSERT_NE(app_browser, nullptr); |
| ASSERT_NE(normal_browser, nullptr); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| // This is disabled on mac pending http://crbug.com/1194201 |
| // These tests currently fail on linux due to http://crbug.com/1196493. |
| // In order to keep the coverage from the rest of the test, the checks that |
| // fail on linux are explicitly disabled. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RestoreMaximizedApp DISABLED_RestoreMaximizedApp |
| #else |
| #define MAYBE_RestoreMaximizedApp RestoreMaximizedApp |
| #endif |
| // This test maximizes an app, ensures it restores correctly. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, MAYBE_RestoreMaximizedApp) { |
| Profile* profile = browser()->profile(); |
| auto example_url = GURL("https://www.example.com"); |
| |
| // Open a PWA. |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| ASSERT_TRUE(app_browser); |
| |
| // Maximize. |
| app_browser->window()->Maximize(); |
| ASSERT_TRUE(ui_test_utils::WaitForMaximized(app_browser)); |
| EXPECT_TRUE(app_browser->window()->IsMaximized()); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Now that SessionServices are off, we can close stuff to simulate a closure. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now trigger a restore. |
| // We need to start up the services again before restoring. |
| StartupServices(profile); |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| app_browser = nullptr; |
| Browser* normal_browser = nullptr; |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_APP) { |
| EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(browser, app_id)); |
| #if !BUILDFLAG(IS_LINUX) |
| EXPECT_TRUE(ui_test_utils::WaitForMaximized(browser)); |
| EXPECT_TRUE(browser->window()->IsMaximized()); |
| #endif |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(), |
| example_url); |
| app_browser = browser; |
| } else { |
| normal_browser = browser; |
| } |
| } |
| |
| // It opens up the browser and the app. |
| ASSERT_NE(app_browser, nullptr); |
| ASSERT_NE(normal_browser, nullptr); |
| profile = normal_browser->profile(); |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| #if !BUILDFLAG(IS_MAC) |
| // This test does not make sense on mac, since when apps are opened, |
| // the browser must open. This test opens an app when nothing is open, |
| // then closes it. Then opens a browser to ensure the user's previous browser |
| // session was preserved. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, |
| OpeningAppDoesNotAffectBrowserSession) { |
| Profile* profile = browser()->profile(); |
| auto example_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| |
| // Add two tabs to the normal browser. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| // 'open the browser' again |
| StartupServices(profile); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Open a PWA. |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| CloseBrowserSynchronously(app_browser); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now restore. |
| SessionRestore::RestoreSession( |
| profile, nullptr, |
| SessionRestore::SYNCHRONOUS | SessionRestore::RESTORE_BROWSER, {}); |
| |
| // There's just one window open at the moment. |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| // Check we got all the tabs back. |
| Browser* browser = BrowserList::GetInstance()->get(0); |
| EXPECT_TRUE(browser->type() == Browser::Type::TYPE_NORMAL); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(), |
| example_url); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(2)->GetURL(), |
| example_url2); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| #endif // !BUILDFLAG(IS_MAC) |
| |
| // This tests if an app being the last remaining window does not interfere |
| // with the last browser window being restored later. |
| // This test also tests that apps aren't restored on normal startups. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, |
| AppWindowsDontIntefereWithBrowserSessionRestore) { |
| auto example_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| auto app_url = GURL("https://www.example3.com"); |
| |
| // Add two tabs to the normal browser. So we can tell it restored correctly. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| Profile* profile = browser()->profile(); |
| |
| // Open a PWA. |
| webapps::AppId app_id = InstallPWA(profile, app_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| // App and 3 tab browser. |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| |
| // Don't kill the test. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Note the ordering here is important. The browser is closed first, |
| // then the app. This means previously, nothing would be restored. |
| CloseBrowserSynchronously(browser()); |
| CloseBrowserSynchronously(app_browser); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now open a browser window. |
| // This should be treated the same as a browser opening when nothing, |
| // i.e. it will trigger a browser restore. |
| SessionRestoreTestHelper restore_observer; |
| chrome::NewEmptyWindow(profile); |
| restore_observer.Wait(); |
| |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| // Check we got all the tabs back. |
| Browser* browser = BrowserList::GetInstance()->get(0); |
| EXPECT_EQ(browser->tab_strip_model()->count(), 3); |
| EXPECT_TRUE(browser->type() == Browser::Type::TYPE_NORMAL); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(), |
| example_url); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(2)->GetURL(), |
| example_url2); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| // This test ensures AppSessionService is notified of app restorations |
| // correctly. |
| // TODO(crbug.com/398704258): Re-enable this test |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, |
| DISABLED_CtrlShiftTRestoresAppsCorrectly) { |
| Profile* profile = browser()->profile(); |
| auto example_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| auto example_url3 = GURL("https://www.example3.com"); |
| |
| // Install 3 PWAs. |
| webapps::AppId app_id = InstallPWA(profile, example_url); |
| webapps::AppId app_id2 = InstallPWA(profile, example_url2); |
| webapps::AppId app_id3 = InstallPWA(profile, example_url3); |
| |
| // Open all 3, browser 2 is app_popup. |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| Browser* app_browser2 = web_app::LaunchWebAppBrowserAndWait( |
| profile, app_id2, WindowOpenDisposition::NEW_POPUP); |
| Browser* app_browser3 = web_app::LaunchWebAppBrowserAndWait(profile, app_id3); |
| |
| // 3 apps + basic browser. |
| ASSERT_EQ(4u, BrowserList::GetInstance()->size()); |
| |
| // Close all 3. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(app_browser2); |
| CloseBrowserSynchronously(app_browser3); |
| |
| // Just the basic browser. |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| // Ctrl-Shift-T 3 times. |
| chrome::RestoreTab(browser()); |
| chrome::RestoreTab(browser()); |
| chrome::RestoreTab(browser()); |
| |
| // Ensure there's 4. Three apps, plus 1 basic test browser. |
| bool app1_seen = false; |
| bool app2_seen = false; |
| bool app3_seen = false; |
| ASSERT_EQ(4u, BrowserList::GetInstance()->size()); |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (web_app::AppBrowserController::IsForWebApp(browser, app_id)) { |
| EXPECT_FALSE(app1_seen); |
| EXPECT_TRUE(browser->is_type_app()); |
| app1_seen = true; |
| } else if (web_app::AppBrowserController::IsForWebApp(browser, app_id2)) { |
| EXPECT_FALSE(app2_seen); |
| EXPECT_TRUE(browser->is_type_app_popup()); |
| app2_seen = true; |
| } else if (web_app::AppBrowserController::IsForWebApp(browser, app_id3)) { |
| EXPECT_FALSE(app3_seen); |
| EXPECT_TRUE(browser->is_type_app()); |
| app3_seen = true; |
| } |
| } |
| EXPECT_TRUE(app1_seen); |
| EXPECT_TRUE(app2_seen); |
| EXPECT_TRUE(app3_seen); |
| } |
| |
| // Request a no app restore and ensure no app was reopened. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, NoAppRestore) { |
| Profile* profile = browser()->profile(); |
| |
| auto app_url = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| webapps::AppId app_id = InstallPWA(profile, app_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| |
| // Don't kill the test. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| CloseBrowserSynchronously(browser()); |
| |
| ShutdownServices(profile); |
| |
| CloseBrowserSynchronously(app_browser); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // 'open the browser' again |
| StartupServices(profile); |
| |
| // Now restore. |
| SessionRestore::RestoreSession( |
| profile, nullptr, |
| SessionRestore::SYNCHRONOUS | SessionRestore::RESTORE_BROWSER, {}); |
| |
| ASSERT_EQ(1u, BrowserList::GetInstance()->size()); |
| |
| // Check we got all the tabs back. |
| Browser* browser = BrowserList::GetInstance()->get(0); |
| EXPECT_EQ(browser->tab_strip_model()->count(), 2); |
| EXPECT_TRUE(browser->type() == Browser::Type::TYPE_NORMAL); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(), |
| example_url2); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| // Do a complex scenario that should only restore an app. |
| // Have a browser session saved in disk, then open and close two separate |
| // apps in sequence. Now try to restore that browser. |
| IN_PROC_BROWSER_TEST_F(AppSessionRestoreTest, InvokeTwoAppsThenRestore) { |
| Profile* profile = browser()->profile(); |
| auto app_url = GURL("https://www.example.com"); |
| auto app_url2 = GURL("https://www.example.com"); |
| auto example_url2 = GURL("https://www.example2.com"); |
| |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), example_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Don't kill the test. |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| webapps::AppId app_id = InstallPWA(profile, app_url); |
| webapps::AppId app_id2 = InstallPWA(profile, app_url2); |
| |
| CloseBrowserSynchronously(browser()); |
| |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| CloseBrowserSynchronously(app_browser); |
| |
| app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id2); |
| CloseBrowserSynchronously(app_browser); |
| |
| // Now open a browser window. |
| // This should be treated the same as a browser opening when nothing, |
| // i.e. it will trigger a browser restore. |
| SessionRestoreTestHelper restore_observer; |
| chrome::NewEmptyWindow(profile); |
| restore_observer.Wait(); |
| |
| // Check we got all the tabs back. |
| Browser* browser = BrowserList::GetInstance()->get(0); |
| EXPECT_EQ(browser->tab_strip_model()->count(), 2); |
| EXPECT_TRUE(browser->type() == Browser::Type::TYPE_NORMAL); |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(1)->GetURL(), |
| example_url2); |
| |
| keep_alive.reset(); |
| profile_keep_alive.reset(); |
| } |
| |
| class SessionRestoreNavigationApiTest : public SessionRestoreTest { |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreNavigationApiTest, |
| ReferrerPolicyUrlCensored) { |
| // Tests start with one browser window; navigate it to url 1. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl1(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetWebContentsAt(0); |
| EXPECT_EQ(GetUrl1(), contents->GetLastCommittedURL()); |
| |
| // Navigate the tab to url 2. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl2(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(GetUrl2(), contents->GetLastCommittedURL()); |
| |
| // Apply a referrer policy of "no-referrer" for url 2. |
| const char kNoReferrerJS[] = |
| "let meta = document.createElement('meta');" |
| "meta.name = 'referrer';" |
| "meta.content = 'no-referrer';" |
| "document.head.appendChild(meta)"; |
| EXPECT_TRUE(content::ExecJs(contents, kNoReferrerJS)); |
| |
| // Navigate the tab to url 3. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GetUrl3(), WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(GetUrl3(), contents->GetLastCommittedURL()); |
| |
| // Ensure that url2 is censored in navigation due to the no-referrer policy, |
| // but url1 isn't. |
| const char kCheckEntry1Js[] = "navigation.entries()[0].url == null;"; |
| const char kCheckEntry2Js[] = "navigation.entries()[1].url == null;"; |
| EXPECT_EQ(false, content::EvalJs(contents, kCheckEntry1Js)); |
| EXPECT_EQ(true, content::EvalJs(contents, kCheckEntry2Js)); |
| |
| // Simulate an exit by shutting down the session service. If we don't do this |
| // the first window close is treated as though the user closed the window |
| // and won't be restored. |
| SessionServiceFactory::ShutdownForProfile(browser()->profile()); |
| |
| // Then close all the browsers and "restart" Chromium. |
| QuitBrowserAndRestore(browser()); |
| |
| // url2 should still be censored in navigation after restore, url1 should not. |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| content::WebContents* restored_contents = |
| active_browser_list_->get(0)->tab_strip_model()->GetWebContentsAt(0); |
| EXPECT_EQ(GetUrl3(), restored_contents->GetLastCommittedURL()); |
| const char kCheckEntriesLength[] = "navigation.entries().length;"; |
| int entries_length = |
| content::EvalJs(restored_contents, kCheckEntriesLength).ExtractInt(); |
| EXPECT_EQ(entries_length, 3); |
| EXPECT_EQ(false, content::EvalJs(restored_contents, kCheckEntry1Js)); |
| EXPECT_EQ(true, content::EvalJs(restored_contents, kCheckEntry2Js)); |
| } |
| |
| class TabbedAppSessionRestoreTest : public AppSessionRestoreTest { |
| public: |
| TabbedAppSessionRestoreTest() = default; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_{ |
| blink::features::kDesktopPWAsTabStrip}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(TabbedAppSessionRestoreTest, RestorePinnedAppTab) { |
| Profile* profile = browser()->profile(); |
| GURL app_url = GURL("https://www.example.com"); |
| webapps::AppId app_id = InstallTabbedPWA(profile, app_url); |
| Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id); |
| TabStripModel* tab_strip = app_browser->tab_strip_model(); |
| |
| // Expect a tabbed app was opened with a pinned tab. |
| EXPECT_TRUE(web_app::WebAppProvider::GetForTest(profile) |
| ->registrar_unsafe() |
| .IsTabbedWindowModeEnabled(app_id)); |
| EXPECT_TRUE(tab_strip->IsTabPinned(0)); |
| |
| // Add a regular tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| app_browser, GURL("https://www.example.com/2"), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| EXPECT_EQ(tab_strip->count(), 2); |
| EXPECT_FALSE(tab_strip->IsTabPinned(1)); |
| |
| // App browser and normal browser. |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| |
| // Pretend to 'close the browser'. |
| // Just shutdown the services as we would if the browser is shutting down for |
| // real. |
| ShutdownServices(profile); |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| |
| // Now that SessionServices are off, we can close stuff to simulate a closure. |
| CloseBrowserSynchronously(app_browser); |
| CloseBrowserSynchronously(browser()); |
| |
| ASSERT_EQ(0u, BrowserList::GetInstance()->size()); |
| |
| // Now trigger a restore. |
| // We need to start up the services again before restoring. |
| StartupServices(profile); |
| |
| SessionRestore::RestoreSession(profile, nullptr, |
| SessionRestore::SYNCHRONOUS | |
| SessionRestore::RESTORE_APPS | |
| SessionRestore::RESTORE_BROWSER, |
| {}); |
| |
| // App and browser restored. |
| ASSERT_EQ(2u, BrowserList::GetInstance()->size()); |
| // Check the tabbed app was restored with the pinned tab. |
| bool app_checked = false; |
| for (Browser* browser : *(BrowserList::GetInstance())) { |
| if (browser->type() == Browser::Type::TYPE_APP) { |
| EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(browser, app_id)); |
| EXPECT_TRUE(web_app::WebAppProvider::GetForTest(browser->profile()) |
| ->registrar_unsafe() |
| .IsTabbedWindowModeEnabled(app_id)); |
| |
| EXPECT_EQ(browser->tab_strip_model()->GetWebContentsAt(0)->GetURL(), |
| app_url); |
| EXPECT_EQ(browser->tab_strip_model()->count(), 2); |
| EXPECT_TRUE(browser->tab_strip_model()->IsTabPinned(0)); |
| EXPECT_FALSE(browser->tab_strip_model()->IsTabPinned(1)); |
| |
| EXPECT_TRUE(browser->app_controller()->GetPinnedHomeTab()); |
| app_checked = true; |
| } |
| } |
| EXPECT_TRUE(app_checked); |
| } |
| |
| class SessionRestoreStaleSessionCookieDeletionTest : public SessionRestoreTest { |
| public: |
| SessionRestoreStaleSessionCookieDeletionTest() |
| : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
| https_server_.AddDefaultHandlers(GetChromeTestDataDir()); |
| ASSERT_TRUE(https_server_.Start()); |
| SessionRestoreTest::SetUpOnMainThread(); |
| } |
| |
| net::EmbeddedTestServer* https_server() { return &https_server_; } |
| |
| bool SetCookie(const std::string& name, |
| const GURL& url, |
| base::Time expiration, |
| base::Time last_access_and_update) { |
| network::mojom::CookieManager* cookie_manager = |
| browser() |
| ->profile() |
| ->GetDefaultStoragePartition() |
| ->GetCookieManagerForBrowserProcess(); |
| std::unique_ptr<net::CanonicalCookie> cookie = |
| net::CanonicalCookie::CreateUnsafeCookieForTesting( |
| name, "test", url.host(), "/", |
| /*creation=*/base::Time::Now(), expiration, |
| /*last_access=*/last_access_and_update, |
| /*last_update=*/last_access_and_update, /*secure=*/true, |
| /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION, |
| net::COOKIE_PRIORITY_MEDIUM, /*partition_key=*/std::nullopt, |
| net::CookieSourceScheme::kSecure); |
| EXPECT_TRUE(cookie->IsCanonicalForFromStorage()); |
| base::test::TestFuture<net::CookieAccessResult> future; |
| cookie_manager->SetCanonicalCookie(*cookie, url, |
| net::CookieOptions::MakeAllInclusive(), |
| future.GetCallback()); |
| return future.Take().status.IsInclude(); |
| } |
| |
| bool HasCookie(Browser* browser, const std::string& name) { |
| network::mojom::CookieManager* cookie_manager = |
| browser->profile() |
| ->GetDefaultStoragePartition() |
| ->GetCookieManagerForBrowserProcess(); |
| base::test::TestFuture<const std::vector<net::CanonicalCookie>&> future; |
| cookie_manager->GetAllCookies(future.GetCallback()); |
| for (const net::CanonicalCookie& cookie : future.Take()) { |
| if (cookie.Name() == name) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private: |
| net::EmbeddedTestServer https_server_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestoreStaleSessionCookieDeletionTest, |
| CookieStorage) { |
| GURL open_page = https_server()->GetURL("a.test", "/empty.html"); |
| GURL other_page = https_server()->GetURL("b.test", "/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), open_page)); |
| // We write every combination of: |
| // (open page, other page) x (persistent, session) x (stale cookie, cookie) |
| // We define stale cookies to be those not accessed or updated in the past 7 |
| // days. |
| ASSERT_TRUE(SetCookie("open_page_persistent_cookie", open_page, |
| /*expiration=*/base::Time::Now() + base::Days(1000), |
| /*last_access_and_update=*/base::Time::Now())); |
| ASSERT_TRUE( |
| SetCookie("open_page_persistent_stale_cookie", open_page, |
| /*expiration=*/base::Time::Now() + base::Days(1000), |
| /*last_access_and_update=*/base::Time::Now() - base::Days(8))); |
| ASSERT_TRUE(SetCookie("open_page_session_cookie", open_page, |
| /*expiration=*/base::Time(), |
| /*last_access_and_update=*/base::Time::Now())); |
| ASSERT_TRUE( |
| SetCookie("open_page_session_stale_cookie", open_page, |
| /*expiration=*/base::Time(), |
| /*last_access_and_update=*/base::Time::Now() - base::Days(8))); |
| ASSERT_TRUE(SetCookie("other_page_persistent_cookie", other_page, |
| /*expiration=*/base::Time::Now() + base::Days(1000), |
| /*last_access_and_update=*/base::Time::Now())); |
| ASSERT_TRUE( |
| SetCookie("other_page_persistent_stale_cookie", other_page, |
| /*expiration=*/base::Time::Now() + base::Days(1000), |
| /*last_access_and_update=*/base::Time::Now() - base::Days(8))); |
| ASSERT_TRUE(SetCookie("other_page_session_cookie", other_page, |
| /*expiration=*/base::Time(), |
| /*last_access_and_update=*/base::Time::Now())); |
| ASSERT_TRUE( |
| SetCookie("other_page_session_stale_cookie", other_page, |
| /*expiration=*/base::Time(), |
| /*last_access_and_update=*/base::Time::Now() - base::Days(8))); |
| Browser* new_browser = QuitBrowserAndRestore(browser()); |
| ASSERT_EQ(1u, active_browser_list_->size()); |
| ASSERT_EQ(open_page, |
| new_browser->tab_strip_model()->GetActiveWebContents()->GetURL()); |
| // No cookies should have been cleared except for the stale session cookie on |
| // a page that wasn't restored. |
| EXPECT_TRUE(HasCookie(new_browser, "open_page_persistent_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "open_page_persistent_stale_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "open_page_session_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "open_page_session_stale_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "other_page_persistent_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "other_page_persistent_stale_cookie")); |
| EXPECT_TRUE(HasCookie(new_browser, "other_page_session_cookie")); |
| EXPECT_FALSE(HasCookie(new_browser, "other_page_session_stale_cookie")); |
| } |
| |
| class SavedTabGroupSessionRestoreTest : public SessionRestoreTest { |
| public: |
| void WaitForPostedTasks() { |
| // Post a dummy task in the current thread and wait for its completion so |
| // that any already posted tasks are completed. |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| }; |
| |
| // This test simulates migrating from V1 of SavedTabGroups to V2. A user may |
| // have unsaved groups at the time they update the browser. We must ensure all |
| // groups are saved by default correctly. See crbug.com/344016224. |
| IN_PROC_BROWSER_TEST_F(SavedTabGroupSessionRestoreTest, |
| UnsavedGroupDefaultSavedAfterBrowserRestart) { |
| // Add a second tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(chrome::kChromeUINewTabPageURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Add the tab to a group. We use the restore version of the AddToGroup |
| // function to prevent the group from being saved by default. The group id sis |
| // respun when restoring to prevent collisions. Just create an arbitrary one |
| // for now. |
| browser()->tab_strip_model()->AddToGroupForRestore( |
| {0}, tab_groups::TabGroupId::GenerateNew()); |
| WaitForPostedTasks(); |
| |
| // Expect no groups have been saved at this point. |
| tab_groups::TabGroupSyncService* service = |
| tab_groups::TabGroupSyncServiceFactory::GetForProfile( |
| browser()->profile()); |
| ASSERT_TRUE(service); |
| |
| service->SetIsInitializedForTesting(false); |
| WaitForPostedTasks(); |
| |
| // Close the browser and restore the last session |
| Browser* restored = QuitBrowserAndRestore(browser()); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| const int tabs = tab_strip_model->count(); |
| |
| ASSERT_EQ(2, tabs); |
| |
| service->SetIsInitializedForTesting(true); |
| WaitForPostedTasks(); |
| |
| // Expect the unsaved group has been saved at this point. |
| EXPECT_EQ(1u, service->GetAllGroups().size()); |
| } |
| |
| // This test simulates creating a default group (using the default group color |
| // and no title). Ensure on restart there was no duplicated groups and that the |
| // restored group is connected to the saved group. |
| IN_PROC_BROWSER_TEST_F(SavedTabGroupSessionRestoreTest, |
| NoDuplicatesOfDefaultSavedGroupAfterBrowserRestart) { |
| // Add a second tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(chrome::kChromeUINewTabPageURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Expect no groups have been saved at this point. |
| tab_groups::TabGroupSyncService* service = |
| tab_groups::TabGroupSyncServiceFactory::GetForProfile( |
| browser()->profile()); |
| ASSERT_TRUE(service); |
| |
| service->SetIsInitializedForTesting(true); |
| EXPECT_EQ(0u, service->GetAllGroups().size()); |
| |
| // Add the tab to a new group. |
| browser()->tab_strip_model()->AddToNewGroup({0}); |
| WaitForPostedTasks(); |
| |
| // Expect the newly created to be saved at this point. |
| EXPECT_EQ(1u, service->GetAllGroups().size()); |
| |
| // Close the browser and restore the last session |
| Browser* restored = QuitBrowserAndRestore(browser()); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| const int tabs = tab_strip_model->count(); |
| ASSERT_EQ(2, tabs); |
| |
| // Expect the restored browser to still have 1 saved group i.e. no duplicates. |
| EXPECT_EQ(1u, service->GetAllGroups().size()); |
| } |
| |
| // This test simulates creating multiple groups with different visual data |
| // and ensuring on restart they are restored with the same information. |
| IN_PROC_BROWSER_TEST_F(SavedTabGroupSessionRestoreTest, |
| MultipleSavedGroupsAfterBrowserRestart) { |
| // Add a second tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(chrome::kChromeUINewTabPageURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Add a third tab. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(chrome::kChromeUINewTabPageURL), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // Expect no groups have been saved at this point. |
| tab_groups::TabGroupSyncService* service = |
| tab_groups::TabGroupSyncServiceFactory::GetForProfile( |
| browser()->profile()); |
| ASSERT_TRUE(service); |
| service->SetIsInitializedForTesting(true); |
| |
| // Add the tab to a new groups. |
| auto group1 = browser()->tab_strip_model()->AddToNewGroup({0}); |
| base::Uuid group1_saved_guid = service->GetGroup(group1)->saved_guid(); |
| |
| auto group2 = browser()->tab_strip_model()->AddToNewGroup({1}); |
| base::Uuid group2_saved_guid = service->GetGroup(group2)->saved_guid(); |
| |
| // Expect the newly created to be saved at this point. |
| EXPECT_EQ(2u, service->GetAllGroups().size()); |
| |
| // Update the visual data of the new groups. |
| browser()->tab_strip_model()->ChangeTabGroupVisuals( |
| group1, |
| tab_groups::TabGroupVisualData(u"Group1", |
| tab_groups::TabGroupColorId::kGrey), |
| true); |
| |
| browser()->tab_strip_model()->ChangeTabGroupVisuals( |
| group2, |
| tab_groups::TabGroupVisualData(u"Group2", |
| tab_groups::TabGroupColorId::kBlue), |
| true); |
| |
| // Close the browser and restore the last session |
| Browser* restored = QuitBrowserAndRestore(browser()); |
| TabStripModel* tab_strip_model = restored->tab_strip_model(); |
| const int tabs = tab_strip_model->count(); |
| ASSERT_EQ(3, tabs); |
| |
| // Expect the restored browser to still has 2 saved group. |
| EXPECT_EQ(2u, service->GetAllGroups().size()); |
| |
| std::optional<tab_groups::SavedTabGroup> saved_group1 = |
| service->GetGroup(group1_saved_guid); |
| ASSERT_TRUE(saved_group1); |
| ASSERT_TRUE(saved_group1->local_group_id().has_value()); |
| auto* local_group1 = tab_strip_model->group_model()->GetTabGroup( |
| saved_group1->local_group_id().value()); |
| EXPECT_EQ(u"Group1", local_group1->visual_data()->title()); |
| EXPECT_EQ(tab_groups::TabGroupColorId::kGrey, |
| local_group1->visual_data()->color()); |
| |
| std::optional<tab_groups::SavedTabGroup> saved_group2 = |
| service->GetGroup(group2_saved_guid); |
| ASSERT_TRUE(saved_group2); |
| ASSERT_TRUE(saved_group2->local_group_id().has_value()); |
| auto* local_group2 = tab_strip_model->group_model()->GetTabGroup( |
| saved_group2->local_group_id().value()); |
| EXPECT_EQ(u"Group2", local_group2->visual_data()->title()); |
| EXPECT_EQ(tab_groups::TabGroupColorId::kBlue, |
| local_group2->visual_data()->color()); |
| } |