| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/sessions/persistent_tab_restore_service.h" |
| |
| #include "base/compiler_specific.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/search.h" |
| #include "chrome/browser/sessions/session_service.h" |
| #include "chrome/browser/sessions/session_service_factory.h" |
| #include "chrome/browser/sessions/session_types.h" |
| #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| #include "chrome/browser/sessions/tab_restore_service_observer.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/chrome_render_view_test.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/sessions/serialized_navigation_entry_test_helper.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/render_view_test.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| typedef TabRestoreService::Tab Tab; |
| typedef TabRestoreService::Window Window; |
| |
| using content::NavigationEntry; |
| using content::WebContentsTester; |
| using sessions::SerializedNavigationEntry; |
| using sessions::SerializedNavigationEntryTestHelper; |
| |
| // Create subclass that overrides TimeNow so that we can control the time used |
| // for closed tabs and windows. |
| class PersistentTabRestoreTimeFactory : public TabRestoreService::TimeFactory { |
| public: |
| PersistentTabRestoreTimeFactory() : time_(base::Time::Now()) {} |
| |
| virtual ~PersistentTabRestoreTimeFactory() {} |
| |
| virtual base::Time TimeNow() OVERRIDE { |
| return time_; |
| } |
| |
| private: |
| base::Time time_; |
| }; |
| |
| class PersistentTabRestoreServiceTest : public ChromeRenderViewHostTestHarness { |
| public: |
| PersistentTabRestoreServiceTest() |
| : url1_("http://1"), |
| url2_("http://2"), |
| url3_("http://3"), |
| user_agent_override_( |
| "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19" |
| " (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19") { |
| } |
| |
| virtual ~PersistentTabRestoreServiceTest() { |
| } |
| |
| protected: |
| enum { |
| kMaxEntries = TabRestoreServiceHelper::kMaxEntries, |
| }; |
| |
| // testing::Test: |
| virtual void SetUp() OVERRIDE { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| time_factory_ = new PersistentTabRestoreTimeFactory(); |
| service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| service_->Shutdown(); |
| service_.reset(); |
| delete time_factory_; |
| ChromeRenderViewHostTestHarness::TearDown(); |
| } |
| |
| TabRestoreService::Entries* mutable_entries() { |
| return service_->mutable_entries(); |
| } |
| |
| void PruneEntries() { |
| service_->PruneEntries(); |
| } |
| |
| void AddThreeNavigations() { |
| // Navigate to three URLs. |
| NavigateAndCommit(url1_); |
| NavigateAndCommit(url2_); |
| NavigateAndCommit(url3_); |
| } |
| |
| void NavigateToIndex(int index) { |
| // Navigate back. We have to do this song and dance as NavigationController |
| // isn't happy if you navigate immediately while going back. |
| controller().GoToIndex(index); |
| WebContentsTester::For(web_contents())->CommitPendingNavigation(); |
| } |
| |
| void RecreateService() { |
| // Must set service to null first so that it is destroyed before the new |
| // one is created. |
| service_->Shutdown(); |
| content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| service_.reset(); |
| service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); |
| SynchronousLoadTabsFromLastSession(); |
| } |
| |
| // Adds a window with one tab and url to the profile's session service. |
| // If |pinned| is true, the tab is marked as pinned in the session service. |
| void AddWindowWithOneTabToSessionService(bool pinned) { |
| SessionService* session_service = |
| SessionServiceFactory::GetForProfile(profile()); |
| SessionID tab_id; |
| SessionID window_id; |
| session_service->SetWindowType( |
| window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL); |
| session_service->SetTabWindow(window_id, tab_id); |
| session_service->SetTabIndexInWindow(window_id, tab_id, 0); |
| session_service->SetSelectedTabInWindow(window_id, 0); |
| if (pinned) |
| session_service->SetPinnedState(window_id, tab_id, true); |
| session_service->UpdateTabNavigation( |
| window_id, tab_id, |
| SerializedNavigationEntryTestHelper::CreateNavigation( |
| url1_.spec(), "title")); |
| } |
| |
| // Creates a SessionService and assigns it to the Profile. The SessionService |
| // is configured with a single window with a single tab pointing at url1_ by |
| // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the |
| // tab is marked as pinned in the session service. |
| void CreateSessionServiceWithOneWindow(bool pinned) { |
| // The profile takes ownership of this. |
| SessionService* session_service = new SessionService(profile()); |
| SessionServiceFactory::SetForTestProfile(profile(), session_service); |
| |
| AddWindowWithOneTabToSessionService(pinned); |
| |
| // Set this, otherwise previous session won't be loaded. |
| profile()->set_last_session_exited_cleanly(false); |
| } |
| |
| void SynchronousLoadTabsFromLastSession() { |
| // Ensures that the load is complete before continuing. |
| service_->LoadTabsFromLastSession(); |
| content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| base::RunLoop().RunUntilIdle(); |
| content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| } |
| |
| GURL url1_; |
| GURL url2_; |
| GURL url3_; |
| std::string user_agent_override_; |
| scoped_ptr<PersistentTabRestoreService> service_; |
| PersistentTabRestoreTimeFactory* time_factory_; |
| }; |
| |
| namespace { |
| |
| class TestTabRestoreServiceObserver : public TabRestoreServiceObserver { |
| public: |
| TestTabRestoreServiceObserver() : got_loaded_(false) {} |
| |
| void clear_got_loaded() { got_loaded_ = false; } |
| bool got_loaded() const { return got_loaded_; } |
| |
| // TabRestoreServiceObserver: |
| virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE { |
| } |
| virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE { |
| } |
| virtual void TabRestoreServiceLoaded(TabRestoreService* service) OVERRIDE { |
| got_loaded_ = true; |
| } |
| |
| private: |
| // Was TabRestoreServiceLoaded() invoked? |
| bool got_loaded_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestTabRestoreServiceObserver); |
| }; |
| |
| } // namespace |
| |
| TEST_F(PersistentTabRestoreServiceTest, Basic) { |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // Make sure an entry was created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // Make sure the entry matches. |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| EXPECT_FALSE(tab->pinned); |
| EXPECT_TRUE(tab->extension_app_id.empty()); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); |
| EXPECT_EQ("", tab->user_agent_override); |
| EXPECT_EQ(2, tab->current_navigation_index); |
| EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), |
| tab->timestamp.ToInternalValue()); |
| |
| NavigateToIndex(1); |
| |
| // And check again, but set the user agent override this time. |
| web_contents()->SetUserAgentOverride(user_agent_override_); |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // There should be two entries now. |
| ASSERT_EQ(2U, service_->entries().size()); |
| |
| // Make sure the entry matches. |
| entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| tab = static_cast<Tab*>(entry); |
| EXPECT_FALSE(tab->pinned); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_EQ(url1_, tab->navigations[0].virtual_url()); |
| EXPECT_EQ(url2_, tab->navigations[1].virtual_url()); |
| EXPECT_EQ(url3_, tab->navigations[2].virtual_url()); |
| EXPECT_EQ(user_agent_override_, tab->user_agent_override); |
| EXPECT_EQ(1, tab->current_navigation_index); |
| EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), |
| tab->timestamp.ToInternalValue()); |
| } |
| |
| // Make sure TabRestoreService doesn't create an entry for a tab with no |
| // navigations. |
| TEST_F(PersistentTabRestoreServiceTest, DontCreateEmptyTab) { |
| service_->CreateHistoricalTab(web_contents(), -1); |
| EXPECT_TRUE(service_->entries().empty()); |
| } |
| |
| // Tests restoring a single tab. |
| TEST_F(PersistentTabRestoreServiceTest, Restore) { |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // Recreate the service and have it load the tabs. |
| RecreateService(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // And verify the entry. |
| PersistentTabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| EXPECT_FALSE(tab->pinned); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); |
| EXPECT_EQ(2, tab->current_navigation_index); |
| EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), |
| tab->timestamp.ToInternalValue()); |
| } |
| |
| // Tests restoring a single pinned tab. |
| TEST_F(PersistentTabRestoreServiceTest, RestorePinnedAndApp) { |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // We have to explicitly mark the tab as pinned as there is no browser for |
| // these tests. |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| tab->pinned = true; |
| const std::string extension_app_id("test"); |
| tab->extension_app_id = extension_app_id; |
| |
| // Recreate the service and have it load the tabs. |
| RecreateService(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // And verify the entry. |
| entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| tab = static_cast<Tab*>(entry); |
| EXPECT_TRUE(tab->pinned); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); |
| EXPECT_EQ(2, tab->current_navigation_index); |
| EXPECT_TRUE(extension_app_id == tab->extension_app_id); |
| } |
| |
| // We only restore apps on chromeos. |
| #if defined(USE_AURA) |
| |
| typedef InProcessBrowserTest PersistentTabRestoreServiceBrowserTest; |
| |
| IN_PROC_BROWSER_TEST_F(PersistentTabRestoreServiceBrowserTest, RestoreApp) { |
| Profile* profile = browser()->profile(); |
| TabRestoreService* trs = TabRestoreServiceFactory::GetForProfile(profile); |
| const char* app_name = "TestApp"; |
| |
| Browser* app_browser = CreateBrowserForApp(app_name, profile); |
| app_browser->window()->Close(); |
| content::WindowedNotificationObserver observer( |
| chrome::NOTIFICATION_BROWSER_CLOSED, |
| content::Source<Browser>(app_browser)); |
| observer.Wait(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, trs->entries().size()); |
| const TabRestoreService::Entry* restored_entry = trs->entries().front(); |
| |
| // It should be a window with an app. |
| ASSERT_EQ(TabRestoreService::WINDOW, restored_entry->type); |
| const Window* restored_window = |
| static_cast<const Window*>(restored_entry); |
| EXPECT_EQ(app_name, restored_window->app_name); |
| } |
| #endif // defined(USE_AURA) |
| |
| // Make sure we persist entries to disk that have post data. |
| TEST_F(PersistentTabRestoreServiceTest, DontPersistPostData) { |
| AddThreeNavigations(); |
| controller().GetEntryAtIndex(0)->SetHasPostData(true); |
| controller().GetEntryAtIndex(1)->SetHasPostData(true); |
| controller().GetEntryAtIndex(2)->SetHasPostData(true); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // Recreate the service and have it load the tabs. |
| RecreateService(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| const TabRestoreService::Entry* restored_entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, restored_entry->type); |
| |
| const Tab* restored_tab = |
| static_cast<const Tab*>(restored_entry); |
| // There should be 3 navs. |
| ASSERT_EQ(3U, restored_tab->navigations.size()); |
| EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), |
| restored_tab->timestamp.ToInternalValue()); |
| } |
| |
| // Make sure we don't persist entries to disk that have post data. This |
| // differs from DontPersistPostData1 in that all the navigations have post |
| // data, so that nothing should be persisted. |
| TEST_F(PersistentTabRestoreServiceTest, DontLoadTwice) { |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // Recreate the service and have it load the tabs. |
| RecreateService(); |
| |
| SynchronousLoadTabsFromLastSession(); |
| |
| // There should only be one entry. |
| ASSERT_EQ(1U, service_->entries().size()); |
| } |
| |
| // Makes sure we load the previous session as necessary. |
| TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSession) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| EXPECT_FALSE(service_->IsLoaded()); |
| |
| TestTabRestoreServiceObserver observer; |
| service_->AddObserver(&observer); |
| SynchronousLoadTabsFromLastSession(); |
| EXPECT_TRUE(observer.got_loaded()); |
| service_->RemoveObserver(&observer); |
| |
| // Make sure we get back one entry with one tab whose url is url1. |
| ASSERT_EQ(1U, service_->entries().size()); |
| TabRestoreService::Entry* entry2 = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::WINDOW, entry2->type); |
| TabRestoreService::Window* window = |
| static_cast<TabRestoreService::Window*>(entry2); |
| ASSERT_EQ(1U, window->tabs.size()); |
| EXPECT_EQ(0, window->timestamp.ToInternalValue()); |
| EXPECT_EQ(0, window->selected_tab_index); |
| ASSERT_EQ(1U, window->tabs[0].navigations.size()); |
| EXPECT_EQ(0, window->tabs[0].current_navigation_index); |
| EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); |
| EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); |
| } |
| |
| // Makes sure we don't attempt to load previous sessions after a restore. |
| TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterRestore) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| profile()->set_restored_last_session(true); |
| |
| SynchronousLoadTabsFromLastSession(); |
| |
| // Because we restored a session PersistentTabRestoreService shouldn't load |
| // the tabs. |
| ASSERT_EQ(0U, service_->entries().size()); |
| } |
| |
| // Makes sure we don't attempt to load previous sessions after a clean exit. |
| TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterCleanExit) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| profile()->set_last_session_exited_cleanly(true); |
| |
| SynchronousLoadTabsFromLastSession(); |
| |
| ASSERT_EQ(0U, service_->entries().size()); |
| } |
| |
| TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabs) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| AddThreeNavigations(); |
| |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| RecreateService(); |
| |
| // We should get back two entries, one from the previous session and one from |
| // the tab restore service. The previous session entry should be first. |
| ASSERT_EQ(2U, service_->entries().size()); |
| // The first entry should come from the session service. |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::WINDOW, entry->type); |
| TabRestoreService::Window* window = |
| static_cast<TabRestoreService::Window*>(entry); |
| ASSERT_EQ(1U, window->tabs.size()); |
| EXPECT_EQ(0, window->selected_tab_index); |
| EXPECT_EQ(0, window->timestamp.ToInternalValue()); |
| ASSERT_EQ(1U, window->tabs[0].navigations.size()); |
| EXPECT_EQ(0, window->tabs[0].current_navigation_index); |
| EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); |
| EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); |
| |
| // Then the closed tab. |
| entry = *(++service_->entries().begin()); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| ASSERT_FALSE(tab->pinned); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_EQ(2, tab->current_navigation_index); |
| EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), |
| tab->timestamp.ToInternalValue()); |
| EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); |
| } |
| |
| // Make sure pinned state is correctly loaded from session service. |
| TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabsPinned) { |
| CreateSessionServiceWithOneWindow(true); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| AddThreeNavigations(); |
| |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| RecreateService(); |
| |
| // We should get back two entries, one from the previous session and one from |
| // the tab restore service. The previous session entry should be first. |
| ASSERT_EQ(2U, service_->entries().size()); |
| // The first entry should come from the session service. |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::WINDOW, entry->type); |
| TabRestoreService::Window* window = |
| static_cast<TabRestoreService::Window*>(entry); |
| ASSERT_EQ(1U, window->tabs.size()); |
| EXPECT_EQ(0, window->selected_tab_index); |
| EXPECT_TRUE(window->tabs[0].pinned); |
| ASSERT_EQ(1U, window->tabs[0].navigations.size()); |
| EXPECT_EQ(0, window->tabs[0].current_navigation_index); |
| EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); |
| |
| // Then the closed tab. |
| entry = *(++service_->entries().begin()); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| ASSERT_FALSE(tab->pinned); |
| ASSERT_EQ(3U, tab->navigations.size()); |
| EXPECT_EQ(2, tab->current_navigation_index); |
| EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); |
| EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); |
| EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); |
| } |
| |
| // Creates kMaxEntries + 1 windows in the session service and makes sure we only |
| // get back kMaxEntries on restore. |
| TEST_F(PersistentTabRestoreServiceTest, ManyWindowsInSessionService) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| for (size_t i = 0; i < kMaxEntries; ++i) |
| AddWindowWithOneTabToSessionService(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| AddThreeNavigations(); |
| |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| RecreateService(); |
| |
| // We should get back kMaxEntries entries. We added more, but |
| // TabRestoreService only allows up to kMaxEntries. |
| ASSERT_EQ(kMaxEntries, service_->entries().size()); |
| |
| // The first entry should come from the session service. |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::WINDOW, entry->type); |
| TabRestoreService::Window* window = |
| static_cast<TabRestoreService::Window*>(entry); |
| ASSERT_EQ(1U, window->tabs.size()); |
| EXPECT_EQ(0, window->selected_tab_index); |
| EXPECT_EQ(0, window->timestamp.ToInternalValue()); |
| ASSERT_EQ(1U, window->tabs[0].navigations.size()); |
| EXPECT_EQ(0, window->tabs[0].current_navigation_index); |
| EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); |
| EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); |
| } |
| |
| // Makes sure we restore timestamps correctly. |
| TEST_F(PersistentTabRestoreServiceTest, TimestampSurvivesRestore) { |
| base::Time tab_timestamp(base::Time::FromInternalValue(123456789)); |
| |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // Make sure an entry was created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // Make sure the entry matches. |
| std::vector<SerializedNavigationEntry> old_navigations; |
| { |
| // |entry|/|tab| doesn't survive after RecreateService(). |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| tab->timestamp = tab_timestamp; |
| old_navigations = tab->navigations; |
| } |
| |
| EXPECT_EQ(3U, old_navigations.size()); |
| for (size_t i = 0; i < old_navigations.size(); ++i) { |
| EXPECT_FALSE(old_navigations[i].timestamp().is_null()); |
| } |
| |
| // Set this, otherwise previous session won't be loaded. |
| profile()->set_last_session_exited_cleanly(false); |
| |
| RecreateService(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // And verify the entry. |
| TabRestoreService::Entry* restored_entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, restored_entry->type); |
| Tab* restored_tab = |
| static_cast<Tab*>(restored_entry); |
| EXPECT_EQ(tab_timestamp.ToInternalValue(), |
| restored_tab->timestamp.ToInternalValue()); |
| ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size()); |
| for (size_t i = 0; i < restored_tab->navigations.size(); ++i) { |
| EXPECT_EQ(old_navigations[i].timestamp(), |
| restored_tab->navigations[i].timestamp()); |
| } |
| } |
| |
| // Makes sure we restore status codes correctly. |
| TEST_F(PersistentTabRestoreServiceTest, StatusCodesSurviveRestore) { |
| AddThreeNavigations(); |
| |
| // Have the service record the tab. |
| service_->CreateHistoricalTab(web_contents(), -1); |
| |
| // Make sure an entry was created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // Make sure the entry matches. |
| std::vector<sessions::SerializedNavigationEntry> old_navigations; |
| { |
| // |entry|/|tab| doesn't survive after RecreateService(). |
| TabRestoreService::Entry* entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, entry->type); |
| Tab* tab = static_cast<Tab*>(entry); |
| old_navigations = tab->navigations; |
| } |
| |
| EXPECT_EQ(3U, old_navigations.size()); |
| for (size_t i = 0; i < old_navigations.size(); ++i) { |
| EXPECT_EQ(200, old_navigations[i].http_status_code()); |
| } |
| |
| // Set this, otherwise previous session won't be loaded. |
| profile()->set_last_session_exited_cleanly(false); |
| |
| RecreateService(); |
| |
| // One entry should be created. |
| ASSERT_EQ(1U, service_->entries().size()); |
| |
| // And verify the entry. |
| TabRestoreService::Entry* restored_entry = service_->entries().front(); |
| ASSERT_EQ(TabRestoreService::TAB, restored_entry->type); |
| Tab* restored_tab = |
| static_cast<Tab*>(restored_entry); |
| ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size()); |
| for (size_t i = 0; i < restored_tab->navigations.size(); ++i) { |
| EXPECT_EQ(200, restored_tab->navigations[i].http_status_code()); |
| } |
| } |
| |
| TEST_F(PersistentTabRestoreServiceTest, PruneEntries) { |
| service_->ClearEntries(); |
| ASSERT_TRUE(service_->entries().empty()); |
| |
| const size_t max_entries = kMaxEntries; |
| for (size_t i = 0; i < max_entries + 5; i++) { |
| SerializedNavigationEntry navigation = |
| SerializedNavigationEntryTestHelper::CreateNavigation( |
| base::StringPrintf("http://%d", static_cast<int>(i)), |
| base::StringPrintf("%d", static_cast<int>(i))); |
| |
| Tab* tab = new Tab(); |
| tab->navigations.push_back(navigation); |
| tab->current_navigation_index = 0; |
| |
| mutable_entries()->push_back(tab); |
| } |
| |
| // Only keep kMaxEntries around. |
| EXPECT_EQ(max_entries + 5, service_->entries().size()); |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| // Pruning again does nothing. |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| |
| // Prune older first. |
| const char kRecentUrl[] = "http://recent"; |
| SerializedNavigationEntry navigation = |
| SerializedNavigationEntryTestHelper::CreateNavigation(kRecentUrl, |
| "Most recent"); |
| Tab* tab = new Tab(); |
| tab->navigations.push_back(navigation); |
| tab->current_navigation_index = 0; |
| mutable_entries()->push_front(tab); |
| EXPECT_EQ(max_entries + 1, service_->entries().size()); |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| EXPECT_EQ(GURL(kRecentUrl), |
| static_cast<Tab*>(service_->entries().front())-> |
| navigations[0].virtual_url()); |
| |
| // Ignore NTPs. |
| navigation = SerializedNavigationEntryTestHelper::CreateNavigation( |
| chrome::kChromeUINewTabURL, "New tab"); |
| |
| tab = new Tab(); |
| tab->navigations.push_back(navigation); |
| tab->current_navigation_index = 0; |
| mutable_entries()->push_front(tab); |
| |
| EXPECT_EQ(max_entries + 1, service_->entries().size()); |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| EXPECT_EQ(GURL(kRecentUrl), |
| static_cast<Tab*>(service_->entries().front())-> |
| navigations[0].virtual_url()); |
| |
| // Don't prune pinned NTPs. |
| tab = new Tab(); |
| tab->pinned = true; |
| tab->current_navigation_index = 0; |
| tab->navigations.push_back(navigation); |
| mutable_entries()->push_front(tab); |
| EXPECT_EQ(max_entries + 1, service_->entries().size()); |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| EXPECT_TRUE(chrome::IsNTPURL( |
| static_cast<Tab*>(service_->entries().front())-> |
| navigations[0].virtual_url(), profile())); |
| |
| // Don't prune NTPs that have multiple navigations. |
| // (Erase the last NTP first.) |
| delete service_->entries().front(); |
| mutable_entries()->erase(mutable_entries()->begin()); |
| tab = new Tab(); |
| tab->current_navigation_index = 1; |
| tab->navigations.push_back(navigation); |
| tab->navigations.push_back(navigation); |
| mutable_entries()->push_front(tab); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| PruneEntries(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| EXPECT_TRUE(chrome::IsNTPURL( |
| static_cast<Tab*>(service_->entries().front())-> |
| navigations[1].virtual_url(), profile())); |
| } |
| |
| // Regression test for crbug.com/106082 |
| TEST_F(PersistentTabRestoreServiceTest, PruneIsCalled) { |
| CreateSessionServiceWithOneWindow(false); |
| |
| SessionServiceFactory::GetForProfile(profile())-> |
| MoveCurrentSessionToLastSession(); |
| |
| profile()->set_restored_last_session(true); |
| |
| const size_t max_entries = kMaxEntries; |
| for (size_t i = 0; i < max_entries + 5; i++) { |
| NavigateAndCommit( |
| GURL(base::StringPrintf("http://%d", static_cast<int>(i)))); |
| service_->CreateHistoricalTab(web_contents(), -1); |
| } |
| |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| // This should not crash. |
| SynchronousLoadTabsFromLastSession(); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| } |
| |
| // Makes sure invoking LoadTabsFromLastSession() when the max number of entries |
| // have been added results in IsLoaded() returning true and notifies observers. |
| TEST_F(PersistentTabRestoreServiceTest, GoToLoadedWhenHaveMaxEntries) { |
| const size_t max_entries = kMaxEntries; |
| for (size_t i = 0; i < max_entries + 5; i++) { |
| NavigateAndCommit( |
| GURL(base::StringPrintf("http://%d", static_cast<int>(i)))); |
| service_->CreateHistoricalTab(web_contents(), -1); |
| } |
| |
| EXPECT_FALSE(service_->IsLoaded()); |
| TestTabRestoreServiceObserver observer; |
| service_->AddObserver(&observer); |
| EXPECT_EQ(max_entries, service_->entries().size()); |
| SynchronousLoadTabsFromLastSession(); |
| EXPECT_TRUE(observer.got_loaded()); |
| EXPECT_TRUE(service_->IsLoaded()); |
| service_->RemoveObserver(&observer); |
| } |