| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/sessions/session_service.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <vector> |
| |
| #include "base/containers/adapters.h" |
| #include "base/containers/contains.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/atomic_flag.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/platform_thread.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/defaults.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/sessions/session_service_base_test_helper.h" |
| #include "chrome/browser/sessions/session_service_log.h" |
| #include "chrome/browser/sessions/session_service_test_helper.h" |
| #include "chrome/browser/signin/signin_util.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/test/base/browser_with_test_window_test.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "components/sessions/content/content_serialized_navigation_builder.h" |
| #include "components/sessions/content/content_test_helper.h" |
| #include "components/sessions/content/session_tab_helper.h" |
| #include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
| #include "components/sessions/core/session_command.h" |
| #include "components/sessions/core/session_id.h" |
| #include "components/sessions/core/session_types.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_id.h" |
| #include "components/tabs/public/split_tab_visual_data.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/page_state/page_state.h" |
| #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/mojom/window_show_state.mojom.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "chromeos/components/kiosk/kiosk_test_utils.h" |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| using content::NavigationEntry; |
| using sessions::ContentTestHelper; |
| using sessions::SerializedNavigationEntry; |
| using sessions::SerializedNavigationEntryTestHelper; |
| |
| class SessionServiceTest : public BrowserWithTestWindowTest { |
| public: |
| SessionServiceTest() : window_bounds(0, 1, 2, 3) { |
| scoped_feature_list_.InitWithFeatures( |
| {features::kSideBySide, features::kSideBySideSessionRestore}, {}); |
| } |
| |
| protected: |
| void SetUp() override { |
| BrowserWithTestWindowTest::SetUp(); |
| |
| session_service_ = std::make_unique<SessionService>(browser()->profile()); |
| helper_.SetService(session_service_.get()); |
| |
| service()->SetWindowType(window_id, Browser::TYPE_NORMAL); |
| service()->SetWindowBounds(window_id, window_bounds, |
| ui::mojom::WindowShowState::kNormal); |
| service()->SetWindowWorkspace(window_id, window_workspace); |
| } |
| |
| void TearDown() override { |
| DestroySessionService(); |
| BrowserWithTestWindowTest::TearDown(); |
| } |
| |
| std::optional<SessionServiceEvent> FindMostRecentEventOfType( |
| SessionServiceEventLogType type) { |
| auto events = GetSessionServiceEvents(browser()->profile()); |
| for (const SessionServiceEvent& event : base::Reversed(events)) { |
| if (event.type == type) |
| return event; |
| } |
| return std::nullopt; |
| } |
| |
| void DestroySessionService() { |
| // Destroy the SessionService first as it may post tasks. |
| session_service_.reset(); |
| // This flushes tasks. |
| helper_.SetService(nullptr); |
| } |
| |
| void UpdateNavigation(SessionID window_session_id, |
| SessionID tab_id, |
| const SerializedNavigationEntry& navigation, |
| bool select) { |
| service()->UpdateTabNavigation(window_session_id, tab_id, navigation); |
| if (select) { |
| service()->SetSelectedNavigationIndex(window_session_id, tab_id, |
| navigation.index()); |
| } |
| } |
| |
| SessionID CreateTabWithTestNavigationData(SessionID window_session_id, |
| int visual_index) { |
| const SessionID tab_id = SessionID::NewUnique(); |
| const SerializedNavigationEntry nav = |
| SerializedNavigationEntryTestHelper::CreateNavigationForTest(); |
| helper_.PrepareTabInWindow(window_session_id, tab_id, visual_index, true); |
| UpdateNavigation(window_session_id, tab_id, nav, true); |
| return tab_id; |
| } |
| |
| void ReadWindows( |
| std::vector<std::unique_ptr<sessions::SessionWindow>>* windows, |
| SessionID* active_window_id, |
| std::string* platform_session_id, |
| std::set<SessionID>* discarded_window_ids) { |
| DestroySessionService(); |
| |
| session_service_ = std::make_unique<SessionService>(browser()->profile()); |
| helper_.SetService(session_service_.get()); |
| |
| SessionID dummy_active_window_id = SessionID::InvalidValue(); |
| SessionID* non_null_active_window_id = |
| active_window_id ? active_window_id : &dummy_active_window_id; |
| std::string dummy_platform_session_id{}; |
| std::string* non_null_platform_session_id = |
| platform_session_id ? platform_session_id : &dummy_platform_session_id; |
| std::set<SessionID> dummy_discarded_window_ids; |
| std::set<SessionID>* non_null_discarded_window_ids = |
| discarded_window_ids ? discarded_window_ids |
| : &dummy_discarded_window_ids; |
| |
| helper_.ReadWindows(windows, non_null_active_window_id, |
| non_null_platform_session_id, |
| non_null_discarded_window_ids); |
| } |
| |
| // Configures the session service with one window with one tab and a single |
| // navigation. If |pinned_state| is true or |write_always| is true, the |
| // pinned state of the tab is updated. The session service is then recreated |
| // and the pinned state of the read back tab is returned. |
| bool CreateAndWriteSessionWithOneTab(bool pinned_state, bool write_always) { |
| SessionID tab_id = SessionID::NewUnique(); |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| if (pinned_state || write_always) |
| helper_.service()->SetPinnedState(window_id, tab_id, pinned_state); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| EXPECT_EQ(1U, windows.size()); |
| if (HasFatalFailure()) |
| return false; |
| EXPECT_EQ(1U, windows[0]->tabs.size()); |
| if (HasFatalFailure()) |
| return false; |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| |
| return tab->pinned; |
| } |
| |
| void CreateAndWriteSessionWithTwoWindows(SessionID window2_id, |
| SessionID tab1_id, |
| SessionID tab2_id, |
| SerializedNavigationEntry* nav1, |
| SerializedNavigationEntry* nav2) { |
| *nav1 = ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| *nav2 = ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| helper_.PrepareTabInWindow(window_id, tab1_id, 0, true); |
| UpdateNavigation(window_id, tab1_id, *nav1, true); |
| |
| const gfx::Rect window2_bounds(3, 4, 5, 6); |
| service()->SetWindowType(window2_id, Browser::TYPE_NORMAL); |
| service()->SetWindowBounds(window2_id, window2_bounds, |
| ui::mojom::WindowShowState::kMaximized); |
| helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true); |
| UpdateNavigation(window2_id, tab2_id, *nav2, true); |
| } |
| |
| SessionService* service() { return helper_.service(); } |
| |
| const gfx::Rect window_bounds; |
| |
| const std::string window_workspace = "abc"; |
| |
| const SessionID window_id = SessionID::NewUnique(); |
| |
| std::unique_ptr<SessionService> session_service_; |
| SessionServiceTestHelper helper_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| TEST_F(SessionServiceTest, Basic) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntryTestHelper::SetOriginalRequestURL( |
| GURL("http://original.request.com"), &nav1); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| service()->SetPlatformSessionIdForTesting("some-platform-session-id"); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| std::set<SessionID> discarded; |
| std::string platform_session_id; |
| ReadWindows(&windows, nullptr, &platform_session_id, &discarded); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_TRUE(window_bounds == windows[0]->bounds); |
| ASSERT_EQ(window_workspace, windows[0]->workspace); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(sessions::SessionWindow::TYPE_NORMAL, windows[0]->type); |
| ASSERT_EQ("some-platform-session-id", platform_session_id); |
| ASSERT_EQ(0U, discarded.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| } |
| |
| // Make sure we persist post entries. |
| TEST_F(SessionServiceTest, PersistPostData) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| helper_.AssertSingleWindowWithSingleTab(windows, 1); |
| } |
| |
| TEST_F(SessionServiceTest, ClosingTabStaysClosed) { |
| SessionID tab_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| ASSERT_NE(tab_id, tab2_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| helper_.PrepareTabInWindow(window_id, tab2_id, 1, false); |
| UpdateNavigation(window_id, tab2_id, nav2, true); |
| service()->TabClosed(window_id, tab2_id); |
| |
| EXPECT_TRUE(helper_.GetHasOpenTrackableBrowsers()); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| EXPECT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, CloseSingleTabClosesWindowAndTab) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| helper_.SetIsOnlyOneTabLeft(true); |
| |
| service()->TabClosed(window_id, tab_id); |
| |
| EXPECT_FALSE(helper_.GetHasOpenTrackableBrowsers()); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| std::set<SessionID> discarded; |
| ReadWindows(&windows, nullptr, nullptr, &discarded); |
| |
| EXPECT_TRUE(windows.empty()); |
| EXPECT_EQ(1U, discarded.size()); |
| EXPECT_TRUE(discarded.contains(window_id)); |
| } |
| |
| TEST_F(SessionServiceTest, Pruning) { |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| for (int i = 0; i < 6; ++i) { |
| SerializedNavigationEntry* nav = (i % 2) == 0 ? &nav1 : &nav2; |
| nav->set_index(i); |
| UpdateNavigation(window_id, tab_id, *nav, true); |
| } |
| |
| // Set available range for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 5)); |
| |
| service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */, |
| 3 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(2, available_range.second); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| // We left the selected index at 5, then pruned. When rereading the |
| // index should get reset to last valid navigation, which is 2. |
| helper_.AssertTabEquals(window_id, tab_id, 0, 2, 3, *tab); |
| |
| ASSERT_EQ(3u, tab->navigations.size()); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| helper_.AssertNavigationEquals(nav2, tab->navigations[1]); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[2]); |
| } |
| |
| TEST_F(SessionServiceTest, TwoWindows) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| SerializedNavigationEntry nav1; |
| SerializedNavigationEntry nav2; |
| |
| CreateAndWriteSessionWithTwoWindows( |
| window2_id, tab1_id, tab2_id, &nav1, &nav2); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(2U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(0, windows[1]->selected_tab_index); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(1U, windows[1]->tabs.size()); |
| |
| sessions::SessionTab* rt1; |
| sessions::SessionTab* rt2; |
| if (windows[0]->window_id == window_id) { |
| ASSERT_EQ(window2_id, windows[1]->window_id); |
| ASSERT_EQ(ui::mojom::WindowShowState::kNormal, windows[0]->show_state); |
| ASSERT_EQ(ui::mojom::WindowShowState::kMaximized, windows[1]->show_state); |
| rt1 = windows[0]->tabs[0].get(); |
| rt2 = windows[1]->tabs[0].get(); |
| } else { |
| ASSERT_EQ(window2_id, windows[0]->window_id); |
| ASSERT_EQ(window_id, windows[1]->window_id); |
| ASSERT_EQ(ui::mojom::WindowShowState::kMaximized, windows[0]->show_state); |
| ASSERT_EQ(ui::mojom::WindowShowState::kNormal, windows[1]->show_state); |
| rt1 = windows[1]->tabs[0].get(); |
| rt2 = windows[0]->tabs[0].get(); |
| } |
| sessions::SessionTab* tab = rt1; |
| helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| |
| tab = rt2; |
| helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav2, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, WindowWithNoTabsGetsPruned) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| |
| helper_.PrepareTabInWindow(window_id, tab1_id, 0, true); |
| UpdateNavigation(window_id, tab1_id, nav1, true); |
| |
| const gfx::Rect window2_bounds(3, 4, 5, 6); |
| service()->SetWindowType(window2_id, Browser::TYPE_NORMAL); |
| service()->SetWindowBounds(window2_id, window2_bounds, |
| ui::mojom::WindowShowState::kNormal); |
| helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| std::set<SessionID> discarded; |
| ReadWindows(&windows, nullptr, nullptr, &discarded); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| |
| ASSERT_EQ(1U, discarded.size()); |
| ASSERT_TRUE(discarded.contains(window2_id)); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, ClosingLastWindowDoesntCloseTabs) { |
| SessionID tab_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| ASSERT_NE(tab_id, tab2_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| helper_.PrepareTabInWindow(window_id, tab2_id, 1, false); |
| UpdateNavigation(window_id, tab2_id, nav2, true); |
| |
| helper_.SetHasOpenTrackableBrowsers(false); |
| |
| service()->WindowClosing(window_id); |
| service()->WindowClosed(window_id); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| EXPECT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(2U, windows[0]->tabs.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| |
| tab = windows[0]->tabs[1].get(); |
| helper_.AssertTabEquals(window_id, tab2_id, 1, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav2, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, ClosingSecondWindowClosesTabs) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| ASSERT_NE(tab1_id, tab2_id); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| CreateAndWriteSessionWithTwoWindows(window2_id, tab1_id, tab2_id, &nav1, |
| &nav2); |
| |
| service()->WindowClosing(window2_id); |
| service()->WindowClosed(window2_id); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| std::set<SessionID> discarded; |
| ReadWindows(&windows, nullptr, nullptr, &discarded); |
| |
| ASSERT_EQ(1U, windows.size()); |
| EXPECT_EQ(0, windows[0]->selected_tab_index); |
| EXPECT_EQ(window_id, windows[0]->window_id); |
| EXPECT_EQ(1U, windows[0]->tabs.size()); |
| |
| ASSERT_EQ(1U, discarded.size()); |
| EXPECT_TRUE(discarded.contains(window2_id)); |
| } |
| |
| TEST_F(SessionServiceTest, LockingWindowRemembersAll) { |
| signin_util::ScopedForceSigninSetterForTesting force_signin_setter(true); |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| SerializedNavigationEntry nav1; |
| SerializedNavigationEntry nav2; |
| |
| CreateAndWriteSessionWithTwoWindows( |
| window2_id, tab1_id, tab2_id, &nav1, &nav2); |
| |
| ASSERT_TRUE(service()->profile()); |
| ProfileManager* manager = g_browser_process->profile_manager(); |
| ASSERT_TRUE(manager); |
| ProfileAttributesEntry* entry = |
| manager->GetProfileAttributesStorage().GetProfileAttributesWithPath( |
| service()->profile()->GetPath()); |
| ASSERT_NE(entry, nullptr); |
| entry->LockForceSigninProfile(true); |
| |
| service()->WindowClosing(window_id); |
| service()->WindowClosed(window_id); |
| service()->WindowClosing(window2_id); |
| service()->WindowClosed(window2_id); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(2U, windows.size()); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(1U, windows[1]->tabs.size()); |
| } |
| |
| TEST_F(SessionServiceTest, WindowCloseCommittedAfterNavigate) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| ASSERT_NE(window2_id, window_id); |
| |
| service()->SetWindowType(window2_id, Browser::TYPE_NORMAL); |
| service()->SetWindowBounds(window2_id, window_bounds, |
| ui::mojom::WindowShowState::kNormal); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation("http://google2.com", "abcd"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false); |
| UpdateNavigation(window2_id, tab2_id, nav2, true); |
| |
| service()->WindowClosing(window2_id); |
| service()->TabClosed(window2_id, tab2_id); |
| service()->WindowClosed(window2_id); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, RemoveUnusedRestoreWindowsTest) { |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows_list; |
| windows_list.push_back(std::make_unique<sessions::SessionWindow>()); |
| windows_list.back()->type = sessions::SessionWindow::TYPE_NORMAL; |
| windows_list.push_back(std::make_unique<sessions::SessionWindow>()); |
| windows_list.back()->type = sessions::SessionWindow::TYPE_DEVTOOLS; |
| |
| service()->RemoveUnusedRestoreWindows(&windows_list); |
| ASSERT_EQ(1U, windows_list.size()); |
| EXPECT_EQ(sessions::SessionWindow::TYPE_NORMAL, windows_list[0]->type); |
| } |
| |
| // Tests pruning from the front. |
| TEST_F(SessionServiceTest, PruneFromFront) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| // Add 5 navigations, with the 4th selected. |
| for (int i = 0; i < 5; ++i) { |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(i), "a"); |
| nav.set_index(i); |
| UpdateNavigation(window_id, tab_id, nav, (i == 3)); |
| } |
| |
| // Set available range for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4)); |
| |
| // Prune the first two navigations from the front. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */, |
| 2 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(2, available_range.second); |
| |
| // Read back in. |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| // There shouldn't be an app id. |
| EXPECT_TRUE(windows[0]->tabs[0]->extension_app_id.empty()); |
| |
| // We should be left with three navigations, the 2nd selected. |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| ASSERT_EQ(1, tab->current_navigation_index); |
| EXPECT_EQ(3U, tab->navigations.size()); |
| EXPECT_TRUE(GURL(base_url + base::NumberToString(2)) == |
| tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(GURL(base_url + base::NumberToString(3)) == |
| tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(GURL(base_url + base::NumberToString(4)) == |
| tab->navigations[2].virtual_url()); |
| } |
| |
| // Tests pruning from the middle. |
| TEST_F(SessionServiceTest, PruneFromMiddle) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| // Add 5 navigations, with the 4th selected. |
| for (int i = 0; i < 5; ++i) { |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(i), "a"); |
| nav.set_index(i); |
| UpdateNavigation(window_id, tab_id, nav, (i == 3)); |
| } |
| |
| // Set available range for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4)); |
| |
| // Prune two navigations starting from second. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 1 /* index */, |
| 2 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(2, available_range.second); |
| |
| // Read back in. |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| // There shouldn't be an app id. |
| EXPECT_TRUE(windows[0]->tabs[0]->extension_app_id.empty()); |
| |
| // We should be left with three navigations, the 2nd selected. |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| ASSERT_EQ(1, tab->current_navigation_index); |
| EXPECT_EQ(3U, tab->navigations.size()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(0)), |
| tab->navigations[0].virtual_url()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(3)), |
| tab->navigations[1].virtual_url()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(4)), |
| tab->navigations[2].virtual_url()); |
| } |
| |
| // Tests possible computations of available ranges. |
| TEST_F(SessionServiceTest, AvailableRanges) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| // Set available range to a subset for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 1. Test when range starts after the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 1 /* index */, |
| 2 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(2, available_range.first); |
| EXPECT_EQ(5, available_range.second); |
| |
| // Set back available range. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 2. Test when range is before the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 8 /* index */, |
| 2 /* count */); |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(4, available_range.first); |
| EXPECT_EQ(7, available_range.second); |
| |
| // Set back available range. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 3. Test when range is within the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */, |
| 5 /* count */); |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(0, available_range.second); |
| |
| // Set back available range. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 4. Test when only range.first is within the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */, |
| 3 /* count */); |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(3, available_range.first); |
| EXPECT_EQ(4, available_range.second); |
| |
| // Set back available range. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 4. Test when only range.second is within the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 5 /* index */, |
| 3 /* count */); |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(4, available_range.first); |
| EXPECT_EQ(4, available_range.second); |
| |
| // Set back available range. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7)); |
| |
| // 4. Test when only range contains all the pruned entries. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 5 /* index */, |
| 2 /* count */); |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(4, available_range.first); |
| EXPECT_EQ(5, available_range.second); |
| } |
| |
| // Prunes from front so that we have no entries. |
| TEST_F(SessionServiceTest, PruneToEmpty) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| // Add 5 navigations, with the 4th selected. |
| for (int i = 0; i < 5; ++i) { |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(i), "a"); |
| nav.set_index(i); |
| UpdateNavigation(window_id, tab_id, nav, (i == 3)); |
| } |
| |
| // Set available range for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4)); |
| |
| // Prune all navigations from the front. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */, |
| 5 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(0, available_range.second); |
| |
| // Read back in. |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| std::set<SessionID> discarded; |
| ReadWindows(&windows, nullptr, nullptr, &discarded); |
| |
| ASSERT_EQ(0U, windows.size()); |
| ASSERT_EQ(1U, discarded.size()); |
| } |
| |
| // Don't set the pinned state and make sure the pinned value is false. |
| TEST_F(SessionServiceTest, PinnedDefaultsToFalse) { |
| EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, false)); |
| } |
| |
| // Explicitly set the pinned state to false and make sure we get back false. |
| TEST_F(SessionServiceTest, PinnedFalseWhenSetToFalse) { |
| EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, true)); |
| } |
| |
| // Explicitly set the pinned state to true and make sure we get back true. |
| TEST_F(SessionServiceTest, PinnedTrue) { |
| EXPECT_TRUE(CreateAndWriteSessionWithOneTab(true, true)); |
| } |
| |
| // Make sure application extension ids are persisted. |
| TEST_F(SessionServiceTest, PersistApplicationExtensionID) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| std::string app_id("foo"); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| helper_.SetTabExtensionAppID(window_id, tab_id, app_id); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| helper_.AssertSingleWindowWithSingleTab(windows, 1); |
| EXPECT_TRUE(app_id == windows[0]->tabs[0]->extension_app_id); |
| } |
| |
| // Check that user agent overrides are persisted. |
| TEST_F(SessionServiceTest, PersistUserAgentOverrides) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| std::string user_agent_override = "Mozilla/5.0 (X11; Linux x86_64) " |
| "AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 " |
| "Safari/535.19"; |
| blink::UserAgentMetadata client_hints_override; |
| client_hints_override.brand_version_list.emplace_back("Chrome", "18"); |
| client_hints_override.full_version = "18.0.1025.45"; |
| client_hints_override.platform = "Linux"; |
| client_hints_override.architecture = "x86_64"; |
| // Doesn't have to match, just needs to be different than the default |
| client_hints_override.bitness = "8"; |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntryTestHelper::SetIsOverridingUserAgent(true, &nav1); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| sessions::SerializedUserAgentOverride serialized_override; |
| serialized_override.ua_string_override = user_agent_override; |
| serialized_override.opaque_ua_metadata_override = |
| blink::UserAgentMetadata::Marshal(client_hints_override); |
| helper_.SetTabUserAgentOverride(window_id, tab_id, serialized_override); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| helper_.AssertSingleWindowWithSingleTab(windows, 1); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| EXPECT_TRUE(user_agent_override == |
| tab->user_agent_override.ua_string_override); |
| EXPECT_TRUE(blink::UserAgentMetadata::Marshal(client_hints_override) == |
| tab->user_agent_override.opaque_ua_metadata_override); |
| } |
| |
| TEST_F(SessionServiceTest, PersistExtraData) { |
| SessionID tab_id = SessionID::NewUnique(); |
| constexpr char kSampleKey[] = "test"; |
| constexpr char kSampleValue[] = "true"; |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| helper_.service()->AddWindowExtraData(window_id, kSampleKey, kSampleValue); |
| helper_.service()->AddTabExtraData(window_id, tab_id, kSampleKey, |
| kSampleValue); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| EXPECT_EQ(1U, windows.size()); |
| EXPECT_EQ(1U, windows[0]->tabs.size()); |
| EXPECT_EQ(1U, windows[0]->extra_data.size()); |
| EXPECT_EQ(kSampleValue, windows[0]->extra_data[kSampleKey]); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| EXPECT_EQ(1U, tab->extra_data.size()); |
| EXPECT_EQ(kSampleValue, tab->extra_data[kSampleKey]); |
| } |
| |
| // Verifies SetWindowBounds maps SHOW_STATE_DEFAULT to SHOW_STATE_NORMAL. |
| TEST_F(SessionServiceTest, DontPersistDefault) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| service()->SetWindowBounds(window_id, window_bounds, |
| ui::mojom::WindowShowState::kDefault); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| ASSERT_EQ(1U, windows.size()); |
| EXPECT_EQ(ui::mojom::WindowShowState::kNormal, windows[0]->show_state); |
| } |
| |
| TEST_F(SessionServiceTest, KeepPostDataWithoutPasswords) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| |
| // Create a TabNavigation containing page_state and representing a POST |
| // request. |
| std::string post_data = "data"; |
| std::unique_ptr<content::NavigationEntry> entry1 = |
| content::NavigationEntry::Create(); |
| entry1->SetURL(GURL("http://google.com")); |
| entry1->SetTitle(u"title1"); |
| entry1->SetHasPostData(true); |
| entry1->SetPostData(network::ResourceRequestBody::CreateFromCopyOfBytes( |
| base::as_byte_span(post_data))); |
| SerializedNavigationEntry nav1 = |
| sessions::ContentSerializedNavigationBuilder::FromNavigationEntry( |
| 0 /* == index*/, entry1.get()); |
| |
| // Create a TabNavigation containing page_state and representing a normal |
| // request. |
| std::unique_ptr<content::NavigationEntry> entry2 = |
| content::NavigationEntry::Create(); |
| entry2->SetURL(GURL("http://google.com/nopost")); |
| entry2->SetTitle(u"title2"); |
| entry2->SetHasPostData(false); |
| SerializedNavigationEntry nav2 = |
| sessions::ContentSerializedNavigationBuilder::FromNavigationEntry( |
| 1 /* == index*/, entry2.get()); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| UpdateNavigation(window_id, tab_id, nav2, true); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| helper_.AssertSingleWindowWithSingleTab(windows, 2); |
| |
| // Expected: the page state of both navigations was saved and restored. |
| ASSERT_EQ(2u, windows[0]->tabs[0]->navigations.size()); |
| { |
| SCOPED_TRACE("Comparing |nav1| and |navigations[0]|"); |
| helper_.AssertNavigationEquals(nav1, windows[0]->tabs[0]->navigations[0]); |
| } |
| { |
| SCOPED_TRACE("Comparing |nav2| and |navigations[1]|"); |
| helper_.AssertNavigationEquals(nav2, windows[0]->tabs[0]->navigations[1]); |
| } |
| } |
| |
| TEST_F(SessionServiceTest, RemovePostDataWithPasswords) { |
| SessionID tab_id = SessionID::NewUnique(); |
| ASSERT_NE(window_id, tab_id); |
| |
| // Create a page state representing a HTTP body with posted passwords. |
| blink::PageState page_state = |
| blink::PageState::CreateForTesting(GURL(), true, "data", nullptr); |
| |
| // Create a TabNavigation containing page_state and representing a POST |
| // request with passwords. |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "title"); |
| SerializedNavigationEntryTestHelper::SetEncodedPageState( |
| page_state.ToEncodedData(), &nav1); |
| SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1); |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| helper_.AssertSingleWindowWithSingleTab(windows, 1); |
| |
| // Expected: the HTTP body was removed from the page state of the POST |
| // navigation with passwords. |
| EXPECT_NE(page_state.ToEncodedData(), |
| windows[0]->tabs[0]->navigations[0].encoded_page_state()); |
| } |
| |
| TEST_F(SessionServiceTest, ReplacePendingNavigation) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| // Add 5 navigations, some with the same index |
| for (int i = 0; i < 5; ++i) { |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(i), "a"); |
| nav.set_index(i / 2); |
| UpdateNavigation(window_id, tab_id, nav, true); |
| } |
| |
| // Read back in. |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| // The ones with index 0, and 2 should have been replaced by 1 and 3. |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| EXPECT_EQ(3U, windows[0]->tabs[0]->navigations.size()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(1)), |
| windows[0]->tabs[0]->navigations[0].virtual_url()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(3)), |
| windows[0]->tabs[0]->navigations[1].virtual_url()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(4)), |
| windows[0]->tabs[0]->navigations[2].virtual_url()); |
| } |
| |
| TEST_F(SessionServiceTest, ReplacePendingNavigationAndPrune) { |
| const std::string base_url("http://google.com/"); |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| |
| for (int i = 0; i < 5; ++i) { |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(i), "a"); |
| nav.set_index(i); |
| UpdateNavigation(window_id, tab_id, nav, true); |
| } |
| |
| // Set available range for testing. |
| helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4)); |
| |
| // Prune all those navigations. |
| helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */, |
| 5 /* count */); |
| |
| std::pair<int, int> available_range; |
| EXPECT_TRUE(helper_.GetAvailableRange(tab_id, &available_range)); |
| EXPECT_EQ(0, available_range.first); |
| EXPECT_EQ(0, available_range.second); |
| |
| // Add another navigation to replace the last one. |
| SerializedNavigationEntry nav = ContentTestHelper::CreateNavigation( |
| base_url + base::NumberToString(5), "a"); |
| nav.set_index(4); |
| UpdateNavigation(window_id, tab_id, nav, true); |
| |
| // Read back in. |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| // We should still have that last navigation at the end, |
| // even though it replaced one that was set before the prune. |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(1U, windows[0]->tabs[0]->navigations.size()); |
| EXPECT_EQ(GURL(base_url + base::NumberToString(5)), |
| windows[0]->tabs[0]->navigations[0].virtual_url()); |
| } |
| |
| TEST_F(SessionServiceTest, RestoreActivation1) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| SerializedNavigationEntry nav1; |
| SerializedNavigationEntry nav2; |
| |
| CreateAndWriteSessionWithTwoWindows( |
| window2_id, tab1_id, tab2_id, &nav1, &nav2); |
| |
| service()->ScheduleCommand( |
| sessions::CreateSetActiveWindowCommand(window2_id)); |
| service()->ScheduleCommand(sessions::CreateSetActiveWindowCommand(window_id)); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| SessionID active_window_id = SessionID::InvalidValue(); |
| ReadWindows(&windows, &active_window_id, nullptr, nullptr); |
| EXPECT_EQ(window_id, active_window_id); |
| } |
| |
| // It's easier to have two separate tests with setup/teardown than to manualy |
| // reset the state for the different flavors of the test. |
| TEST_F(SessionServiceTest, RestoreActivation2) { |
| SessionID window2_id = SessionID::NewUnique(); |
| SessionID tab1_id = SessionID::NewUnique(); |
| SessionID tab2_id = SessionID::NewUnique(); |
| SerializedNavigationEntry nav1; |
| SerializedNavigationEntry nav2; |
| |
| CreateAndWriteSessionWithTwoWindows( |
| window2_id, tab1_id, tab2_id, &nav1, &nav2); |
| |
| service()->ScheduleCommand( |
| sessions::CreateSetActiveWindowCommand(window2_id)); |
| service()->ScheduleCommand(sessions::CreateSetActiveWindowCommand(window_id)); |
| service()->ScheduleCommand( |
| sessions::CreateSetActiveWindowCommand(window2_id)); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| SessionID active_window_id = SessionID::InvalidValue(); |
| std::string platform_session_id; |
| ReadWindows(&windows, &active_window_id, &platform_session_id, nullptr); |
| EXPECT_EQ(window2_id, active_window_id); |
| EXPECT_TRUE(platform_session_id.empty()); |
| } |
| |
| // Makes sure sessions doesn't track certain urls. |
| TEST_F(SessionServiceTest, IgnoreBlockedUrls) { |
| SessionID tab_id = SessionID::NewUnique(); |
| |
| SerializedNavigationEntry nav1 = |
| ContentTestHelper::CreateNavigation("http://google.com", "abc"); |
| SerializedNavigationEntry nav2 = |
| ContentTestHelper::CreateNavigation(chrome::kChromeUIQuitURL, "quit"); |
| SerializedNavigationEntry nav3 = ContentTestHelper::CreateNavigation( |
| chrome::kChromeUIRestartURL, "restart"); |
| |
| helper_.PrepareTabInWindow(window_id, tab_id, 0, true); |
| UpdateNavigation(window_id, tab_id, nav1, true); |
| UpdateNavigation(window_id, tab_id, nav2, true); |
| UpdateNavigation(window_id, tab_id, nav3, true); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(0, windows[0]->selected_tab_index); |
| ASSERT_EQ(window_id, windows[0]->window_id); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab); |
| helper_.AssertNavigationEquals(nav1, tab->navigations[0]); |
| } |
| |
| TEST_F(SessionServiceTest, TabGroupDefaultsToNone) { |
| CreateTabWithTestNavigationData(window_id, 0); |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(1U, windows[0]->tabs.size()); |
| ASSERT_EQ(0U, windows[0]->tab_groups.size()); |
| |
| // Verify that the recorded tab has no group. |
| sessions::SessionTab* tab = windows[0]->tabs[0].get(); |
| EXPECT_EQ(std::nullopt, tab->group); |
| } |
| |
| TEST_F(SessionServiceTest, TabGroupsSaved) { |
| const tab_groups::TabGroupId group1 = tab_groups::TabGroupId::GenerateNew(); |
| const tab_groups::TabGroupId group2 = tab_groups::TabGroupId::GenerateNew(); |
| constexpr int kNumTabs = 5; |
| const std::array<std::optional<tab_groups::TabGroupId>, kNumTabs> groups = { |
| std::nullopt, group1, group1, std::nullopt, group2}; |
| |
| // Create |kNumTabs| tabs with group IDs in |groups|. |
| for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) { |
| const SessionID tab_id = |
| CreateTabWithTestNavigationData(window_id, tab_ndx); |
| service()->SetTabGroup(window_id, tab_id, groups[tab_ndx]); |
| } |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(kNumTabs, static_cast<int>(windows[0]->tabs.size())); |
| ASSERT_EQ(2U, windows[0]->tab_groups.size()); |
| |
| for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) { |
| sessions::SessionTab* tab = windows[0]->tabs[tab_ndx].get(); |
| EXPECT_EQ(groups[tab_ndx], tab->group); |
| } |
| } |
| |
| TEST_F(SessionServiceTest, TabGroupMetadataSaved) { |
| constexpr int kNumGroups = 2; |
| const std::array<tab_groups::TabGroupId, kNumGroups> group_ids = { |
| tab_groups::TabGroupId::GenerateNew(), |
| tab_groups::TabGroupId::GenerateNew()}; |
| const std::array<tab_groups::TabGroupVisualData, kNumGroups> visual_data = { |
| tab_groups::TabGroupVisualData(u"Foo", |
| tab_groups::TabGroupColorId::kBlue), |
| tab_groups::TabGroupVisualData(u"Bar", |
| tab_groups::TabGroupColorId::kGreen)}; |
| const std::array<std::optional<std::string>, kNumGroups> saved_guids = { |
| base::Uuid::GenerateRandomV4().AsLowercaseString(), std::nullopt}; |
| |
| // Create |kNumGroups| tab groups, each with one tab. |
| for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) { |
| const SessionID tab_id = |
| CreateTabWithTestNavigationData(window_id, group_ndx); |
| service()->SetTabGroup(window_id, tab_id, group_ids[group_ndx]); |
| |
| if (saved_guids[group_ndx]) { |
| service()->AddSavedTabGroupsMapping(group_ids[group_ndx], |
| saved_guids[group_ndx].value()); |
| } |
| |
| service()->SetTabGroupMetadata(window_id, group_ids[group_ndx], |
| &visual_data[group_ndx]); |
| } |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(2U, windows[0]->tabs.size()); |
| ASSERT_EQ(2U, windows[0]->tab_groups.size()); |
| |
| // There's no guaranteed order in |SessionWindow::tab_groups|, so use a map. |
| base::flat_map<tab_groups::TabGroupId, sessions::SessionTabGroup*> tab_groups; |
| for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) { |
| tab_groups.emplace(windows[0]->tab_groups[group_ndx]->id, |
| windows[0]->tab_groups[group_ndx].get()); |
| } |
| |
| for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) { |
| const tab_groups::TabGroupId group_id = group_ids[group_ndx]; |
| ASSERT_TRUE(base::Contains(tab_groups, group_id)); |
| EXPECT_EQ(visual_data[group_ndx], tab_groups[group_id]->visual_data); |
| if (tab_groups[group_id]->saved_guid.has_value()) { |
| EXPECT_EQ(saved_guids[group_ndx], |
| tab_groups[group_id]->saved_guid.value()); |
| } else { |
| EXPECT_EQ(std::nullopt, tab_groups[group_id]->saved_guid); |
| } |
| } |
| } |
| |
| TEST_F(SessionServiceTest, SplitTabSaved) { |
| const split_tabs::SplitTabId split_one = |
| split_tabs::SplitTabId::GenerateNew(); |
| const split_tabs::SplitTabId split_two = |
| split_tabs::SplitTabId::GenerateNew(); |
| constexpr int kNumTabs = 6; |
| const std::array<std::optional<split_tabs::SplitTabId>, kNumTabs> splits = { |
| std::nullopt, split_one, split_one, std::nullopt, split_two, split_two}; |
| |
| // Create `kNumTabs` tabs with split IDs in `splits`. |
| for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) { |
| const SessionID tab_id = |
| CreateTabWithTestNavigationData(window_id, tab_ndx); |
| service()->SetSplitTab(window_id, tab_id, splits[tab_ndx]); |
| } |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(kNumTabs, static_cast<int>(windows[0]->tabs.size())); |
| ASSERT_EQ(2U, windows[0]->split_tabs.size()); |
| |
| for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) { |
| sessions::SessionTab* tab = windows[0]->tabs[tab_ndx].get(); |
| EXPECT_EQ(splits[tab_ndx], tab->split_id); |
| } |
| } |
| |
| TEST_F(SessionServiceTest, SplitTabDataSaved) { |
| constexpr int kNumSplitTabs = 2; |
| const std::array<split_tabs::SplitTabId, kNumSplitTabs> split_ids = { |
| split_tabs::SplitTabId::GenerateNew(), |
| split_tabs::SplitTabId::GenerateNew()}; |
| |
| const std::array<split_tabs::SplitTabVisualData, kNumSplitTabs> visual_data = |
| {split_tabs::SplitTabVisualData(split_tabs::SplitTabLayout::kHorizontal, |
| 0.2), |
| split_tabs::SplitTabVisualData(split_tabs::SplitTabLayout::kVertical, |
| 0.7)}; |
| |
| int tab_ndx = 0; |
| // Create `kNumSplitTabs` split tabs. |
| for (int split_ndx = 0; split_ndx < kNumSplitTabs; ++split_ndx) { |
| const SessionID tab_id_one = |
| CreateTabWithTestNavigationData(window_id, tab_ndx++); |
| const SessionID tab_id_two = |
| CreateTabWithTestNavigationData(window_id, tab_ndx++); |
| |
| service()->SetSplitTab(window_id, tab_id_one, split_ids[split_ndx]); |
| service()->SetSplitTab(window_id, tab_id_two, split_ids[split_ndx]); |
| |
| service()->SetSplitTabData(window_id, split_ids[split_ndx], |
| &visual_data[split_ndx]); |
| } |
| |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows; |
| ReadWindows(&windows, nullptr, nullptr, nullptr); |
| |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_EQ(4U, windows[0]->tabs.size()); |
| ASSERT_EQ(2U, windows[0]->split_tabs.size()); |
| |
| // There's no guaranteed order in `SessionWindow::split_tabs`, so use a map. |
| base::flat_map<split_tabs::SplitTabId, sessions::SessionSplitTab*> |
| split_tab_map; |
| for (int split_ndx = 0; split_ndx < kNumSplitTabs; ++split_ndx) { |
| split_tab_map.emplace(windows[0]->split_tabs[split_ndx]->id_, |
| windows[0]->split_tabs[split_ndx].get()); |
| } |
| |
| for (int split_ndx = 0; split_ndx < kNumSplitTabs; ++split_ndx) { |
| const split_tabs::SplitTabId split_id = split_ids[split_ndx]; |
| ASSERT_TRUE(base::Contains(split_tab_map, split_id)); |
| EXPECT_EQ(visual_data[split_ndx], |
| split_tab_map[split_id]->split_visual_data_); |
| } |
| } |
| |
| TEST_F(SessionServiceTest, Workspace) { |
| auto* test_browser_window = |
| static_cast<TestBrowserWindow*>(browser()->window()); |
| test_browser_window->set_workspace(window_workspace); |
| AddTab(browser(), GURL("http://foo/1")); |
| // Force a reset, to verify that SessionService::BuildCommandsForBrowser |
| // handles workspaces correctly. |
| service()->ResetFromCurrentBrowsers(); |
| |
| sessions::CommandStorageManager* command_storage_manager = |
| service()->GetCommandStorageManagerForTest(); |
| const std::vector<std::unique_ptr<sessions::SessionCommand>>& |
| pending_commands = command_storage_manager->pending_commands(); |
| bool found_workspace_command = false; |
| std::unique_ptr<sessions::SessionCommand> workspace_command = |
| sessions::CreateSetWindowWorkspaceCommand(browser()->session_id(), |
| window_workspace); |
| for (const auto& command : pending_commands) { |
| if (command->id() == workspace_command->id() && |
| command->contents_as_string_piece() == |
| workspace_command->contents_as_string_piece()) { |
| found_workspace_command = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found_workspace_command); |
| } |
| |
| // Tests that the workspace is saved in the browser session during |
| // `SessionService::WindowOpened(),` called in `Browser()` constructor to |
| // save the current workspace to newly created browser. |
| TEST_F(SessionServiceTest, WorkspaceSavedOnOpened) { |
| const std::string workspace = "xyz"; |
| auto* test_browser_window = |
| static_cast<TestBrowserWindow*>(browser()->window()); |
| test_browser_window->set_workspace(workspace); |
| service()->WindowOpened(browser()); |
| |
| sessions::CommandStorageManager* command_storage_manager = |
| service()->GetCommandStorageManagerForTest(); |
| const std::vector<std::unique_ptr<sessions::SessionCommand>>& |
| pending_commands = command_storage_manager->pending_commands(); |
| bool found_workspace_command = false; |
| std::unique_ptr<sessions::SessionCommand> workspace_command = |
| sessions::CreateSetWindowWorkspaceCommand(browser()->session_id(), |
| workspace); |
| for (const auto& command : pending_commands) { |
| if (command->id() == workspace_command->id() && |
| command->contents_as_string_piece() == |
| workspace_command->contents_as_string_piece()) { |
| found_workspace_command = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found_workspace_command); |
| } |
| |
| // Tests that the visible on all workspaces state is saved during |
| // SessionService::BuildCommandsForBrowser. |
| TEST_F(SessionServiceTest, VisibleOnAllWorkspaces) { |
| auto* test_browser_window = |
| static_cast<TestBrowserWindow*>(browser()->window()); |
| test_browser_window->set_visible_on_all_workspaces(true); |
| // Force a reset, to verify that SessionService::BuildCommandsForBrowser |
| // handles workspaces correctly. |
| AddTab(browser(), GURL("http://foo/1")); |
| service()->ResetFromCurrentBrowsers(); |
| |
| sessions::CommandStorageManager* command_storage_manager = |
| service()->GetCommandStorageManagerForTest(); |
| const std::vector<std::unique_ptr<sessions::SessionCommand>>& |
| pending_commands = command_storage_manager->pending_commands(); |
| bool found_visible_on_all_workspaces_command = false; |
| std::unique_ptr<sessions::SessionCommand> visible_on_all_workspaces_command = |
| sessions::CreateSetWindowVisibleOnAllWorkspacesCommand( |
| browser()->session_id(), |
| /*visible_on_all_workspaces=*/true); |
| for (const auto& command : pending_commands) { |
| if (command->id() == visible_on_all_workspaces_command->id() && |
| command->contents_as_string_piece() == |
| visible_on_all_workspaces_command->contents_as_string_piece()) { |
| found_visible_on_all_workspaces_command = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found_visible_on_all_workspaces_command); |
| } |
| |
| TEST_F(SessionServiceTest, PinnedAfterReset) { |
| AddTab(browser(), GURL("http://foo/1")); |
| browser()->tab_strip_model()->SetTabPinned(0, true); |
| // Force a reset, to verify that SessionService::BuildCommandsForBrowser |
| // handles pinned tabs correctly. |
| service()->ResetFromCurrentBrowsers(); |
| |
| sessions::CommandStorageManager* command_storage_manager = |
| service()->GetCommandStorageManagerForTest(); |
| const std::vector<std::unique_ptr<sessions::SessionCommand>>& |
| pending_commands = command_storage_manager->pending_commands(); |
| bool found_pinned_command = false; |
| |
| sessions::SessionTabHelper* session_tab_helper = |
| sessions::SessionTabHelper::FromWebContents( |
| browser()->tab_strip_model()->GetWebContentsAt(0)); |
| std::unique_ptr<sessions::SessionCommand> pinned_command = |
| sessions::CreatePinnedStateCommand(session_tab_helper->session_id(), |
| true); |
| |
| for (const auto& command : pending_commands) { |
| if (command->id() == pinned_command->id() && |
| command->contents_as_string_piece() == |
| pinned_command->contents_as_string_piece()) { |
| found_pinned_command = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found_pinned_command); |
| } |
| |
| // Functions used by GetSessionsAndDestroy. |
| namespace { |
| |
| void OnGotPreviousSession( |
| std::vector<std::unique_ptr<sessions::SessionWindow>> windows, |
| SessionID ignored_active_window, |
| bool error_reading) { |
| FAIL() << "SessionService was destroyed, this shouldn't be reached."; |
| } |
| |
| // Blocks until |keep_waiting| is false. |
| void SimulateWaitForTesting(const base::AtomicFlag* flag) { |
| // Ideally this code would use WaitableEvent, but that triggers a DCHECK in |
| // thread_restrictions. Rather than inject a trait only for the test this |
| // code uses yield. |
| while (!flag->IsSet()) |
| base::PlatformThread::YieldCurrentThread(); |
| } |
| |
| } // namespace |
| |
| // Verifies that SessionService::GetLastSession() works correctly if the |
| // SessionService is deleted during processing. To verify the problematic case |
| // does the following: |
| // 1. Sends a task to the background thread that blocks. |
| // 2. Asks SessionService for the last session commands. This is blocked by 1. |
| // 3. Posts another task to the background thread, this too is blocked by 1. |
| // 4. Deletes SessionService. |
| // 5. Signals the semaphore that 2 and 3 are waiting on, allowing |
| // GetLastSession() to continue. |
| // 6. runs the message loop, this is quit when the task scheduled in 3 posts |
| // back to the ui thread to quit the run loop. |
| // The call to get the previous session should never be invoked because the |
| // SessionService was destroyed before SessionService could process the results. |
| TEST_F(SessionServiceTest, GetSessionsAndDestroy) { |
| base::AtomicFlag flag; |
| base::RunLoop run_loop; |
| helper_.RunTaskOnBackendThread( |
| FROM_HERE, |
| base::BindOnce(&SimulateWaitForTesting, base::Unretained(&flag))); |
| service()->GetLastSession(base::BindOnce(&OnGotPreviousSession)); |
| helper_.RunTaskOnBackendThread(FROM_HERE, run_loop.QuitClosure()); |
| // Don't use DestroySessionService() as that runs the MessageLoop, which |
| // this test needs to control. |
| session_service_.reset(); |
| flag.Set(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(SessionServiceTest, LogExit) { |
| EXPECT_FALSE(FindMostRecentEventOfType(SessionServiceEventLogType::kExit)); |
| helper_.SetHasOpenTrackableBrowsers(false); |
| service()->WindowClosing(window_id); |
| auto exit_event = |
| FindMostRecentEventOfType(SessionServiceEventLogType::kExit); |
| ASSERT_TRUE(exit_event); |
| EXPECT_EQ(1, exit_event->data.exit.window_count); |
| EXPECT_EQ(browser()->tab_strip_model()->count(), |
| exit_event->data.exit.tab_count); |
| |
| // Create another window, which should remove the exit. |
| SessionID window2_id = SessionID::NewUnique(); |
| service()->SetWindowType(window2_id, Browser::TYPE_NORMAL); |
| EXPECT_FALSE(FindMostRecentEventOfType(SessionServiceEventLogType::kExit)); |
| } |
| |
| TEST_F(SessionServiceTest, OnErrorWritingSessionCommands) { |
| helper_.SaveNow(); |
| EXPECT_FALSE(helper_.HasPendingReset()); |
| EXPECT_FALSE(helper_.HasPendingSave()); |
| EXPECT_FALSE( |
| FindMostRecentEventOfType(SessionServiceEventLogType::kWriteError)); |
| service()->OnErrorWritingSessionCommands(); |
| EXPECT_TRUE(helper_.HasPendingReset()); |
| EXPECT_TRUE(helper_.HasPendingSave()); |
| auto write_error_event = |
| FindMostRecentEventOfType(SessionServiceEventLogType::kWriteError); |
| ASSERT_TRUE(write_error_event); |
| EXPECT_EQ(1, write_error_event->data.write_error.error_count); |
| EXPECT_EQ(0, write_error_event->data.write_error.unrecoverable_error_count); |
| } |
| |
| TEST_F(SessionServiceTest, OnErrorWritingSessionCommandsUnrecoverable) { |
| helper_.SaveNow(); |
| service()->WindowClosing(window_id); |
| EXPECT_FALSE( |
| FindMostRecentEventOfType(SessionServiceEventLogType::kWriteError)); |
| service()->OnErrorWritingSessionCommands(); |
| EXPECT_TRUE(helper_.HasPendingReset()); |
| EXPECT_TRUE(helper_.HasPendingSave()); |
| auto write_error_event = |
| FindMostRecentEventOfType(SessionServiceEventLogType::kWriteError); |
| ASSERT_TRUE(write_error_event); |
| EXPECT_EQ(1, write_error_event->data.write_error.error_count); |
| EXPECT_EQ(0, write_error_event->data.write_error.unrecoverable_error_count); |
| } |
| |
| TEST_F(SessionServiceTest, DisableSaving) { |
| // Disable saving has to be done early on. |
| helper_.SetSavingEnabled(false); |
| helper_.SaveNow(); |
| EXPECT_EQ(0, helper_.command_storage_manager()->commands_since_reset()); |
| EXPECT_FALSE(helper_.did_save_commands_at_least_once()); |
| |
| // Schedule another command, it should not trigger any saving. |
| const SessionID window2_id = SessionID::NewUnique(); |
| service()->SetWindowType(window2_id, Browser::TYPE_NORMAL); |
| EXPECT_FALSE(helper_.command_storage_manager()->HasPendingSave()); |
| EXPECT_TRUE(helper_.command_storage_manager()->pending_commands().empty()); |
| helper_.SaveNow(); |
| EXPECT_EQ(0, helper_.command_storage_manager()->commands_since_reset()); |
| EXPECT_FALSE(helper_.did_save_commands_at_least_once()); |
| |
| // Turn on saving, and a rebuild should be scheduled. |
| helper_.SetSavingEnabled(true); |
| EXPECT_TRUE(helper_.command_storage_manager()->HasPendingSave()); |
| EXPECT_TRUE(helper_.command_storage_manager()->pending_reset()); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| class SessionServiceKioskTest : public SessionServiceTest { |
| public: |
| std::optional<std::string> GetDefaultProfileName() override { |
| return "test@kiosk-apps.device-local.localhost"; |
| } |
| |
| void LogIn(std::string_view email, const GaiaId& gaia_id) override { |
| chromeos::SetUpFakeChromeAppKioskSession(std::string(email)); |
| } |
| }; |
| |
| TEST_F(SessionServiceTest, OpenedWindowNotRestored) { |
| // These preparation is necessary for `ShouldRestore` function to return true |
| // in the regular user session. |
| helper_.SetHasOpenTrackableBrowsers(false); |
| service()->WindowClosing(window_id); |
| service()->WindowClosed(window_id); |
| // Make sure `ShouldRestore` returns true for the regular user session. |
| EXPECT_TRUE(session_service_->ShouldRestore(browser())); |
| } |
| |
| TEST_F(SessionServiceKioskTest, OpenedWindowNotRestored) { |
| helper_.SetHasOpenTrackableBrowsers(false); |
| service()->WindowClosing(window_id); |
| service()->WindowClosed(window_id); |
| // Make sure `ShouldRestore` returns true for the kiosk user session. |
| EXPECT_FALSE(session_service_->ShouldRestore(browser())); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |