Home | History | Annotate | Download | only in sessions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/run_loop.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/threading/sequenced_worker_pool.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/sessions/session_service.h"
     15 #include "chrome/browser/sessions/session_service_factory.h"
     16 #include "chrome/browser/sessions/session_types.h"
     17 #include "chrome/browser/sessions/tab_restore_service_factory.h"
     18 #include "chrome/browser/sessions/tab_restore_service_observer.h"
     19 #include "chrome/browser/ui/browser_window.h"
     20 #include "chrome/common/url_constants.h"
     21 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     22 #include "chrome/test/base/chrome_render_view_test.h"
     23 #include "chrome/test/base/in_process_browser_test.h"
     24 #include "chrome/test/base/testing_profile.h"
     25 #include "components/sessions/serialized_navigation_entry_test_helper.h"
     26 #include "content/public/browser/browser_thread.h"
     27 #include "content/public/browser/navigation_controller.h"
     28 #include "content/public/browser/navigation_entry.h"
     29 #include "content/public/browser/notification_service.h"
     30 #include "content/public/browser/notification_types.h"
     31 #include "content/public/browser/web_contents.h"
     32 #include "content/public/test/render_view_test.h"
     33 #include "content/public/test/test_utils.h"
     34 #include "content/public/test/web_contents_tester.h"
     35 #include "testing/gtest/include/gtest/gtest.h"
     36 
     37 typedef TabRestoreService::Tab Tab;
     38 typedef TabRestoreService::Window Window;
     39 
     40 using content::NavigationEntry;
     41 using content::WebContentsTester;
     42 using sessions::SerializedNavigationEntry;
     43 using sessions::SerializedNavigationEntryTestHelper;
     44 
     45 // Create subclass that overrides TimeNow so that we can control the time used
     46 // for closed tabs and windows.
     47 class PersistentTabRestoreTimeFactory : public TabRestoreService::TimeFactory {
     48  public:
     49   PersistentTabRestoreTimeFactory() : time_(base::Time::Now()) {}
     50 
     51   virtual ~PersistentTabRestoreTimeFactory() {}
     52 
     53   virtual base::Time TimeNow() OVERRIDE {
     54     return time_;
     55   }
     56 
     57  private:
     58   base::Time time_;
     59 };
     60 
     61 class PersistentTabRestoreServiceTest : public ChromeRenderViewHostTestHarness {
     62  public:
     63   PersistentTabRestoreServiceTest()
     64     : url1_("http://1"),
     65       url2_("http://2"),
     66       url3_("http://3"),
     67       user_agent_override_(
     68           "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19"
     69           " (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19") {
     70   }
     71 
     72   virtual ~PersistentTabRestoreServiceTest() {
     73   }
     74 
     75  protected:
     76   enum {
     77     kMaxEntries = TabRestoreServiceHelper::kMaxEntries,
     78   };
     79 
     80   // testing::Test:
     81   virtual void SetUp() OVERRIDE {
     82     ChromeRenderViewHostTestHarness::SetUp();
     83     time_factory_ = new PersistentTabRestoreTimeFactory();
     84     service_.reset(new PersistentTabRestoreService(profile(), time_factory_));
     85   }
     86 
     87   virtual void TearDown() OVERRIDE {
     88     service_->Shutdown();
     89     service_.reset();
     90     delete time_factory_;
     91     ChromeRenderViewHostTestHarness::TearDown();
     92   }
     93 
     94   TabRestoreService::Entries* mutable_entries() {
     95     return service_->mutable_entries();
     96   }
     97 
     98   void PruneEntries() {
     99     service_->PruneEntries();
    100   }
    101 
    102   void AddThreeNavigations() {
    103     // Navigate to three URLs.
    104     NavigateAndCommit(url1_);
    105     NavigateAndCommit(url2_);
    106     NavigateAndCommit(url3_);
    107   }
    108 
    109   void NavigateToIndex(int index) {
    110     // Navigate back. We have to do this song and dance as NavigationController
    111     // isn't happy if you navigate immediately while going back.
    112     controller().GoToIndex(index);
    113     WebContentsTester::For(web_contents())->CommitPendingNavigation();
    114   }
    115 
    116   void RecreateService() {
    117     // Must set service to null first so that it is destroyed before the new
    118     // one is created.
    119     service_->Shutdown();
    120     content::BrowserThread::GetBlockingPool()->FlushForTesting();
    121     service_.reset();
    122     service_.reset(new PersistentTabRestoreService(profile(), time_factory_));
    123     SynchronousLoadTabsFromLastSession();
    124   }
    125 
    126   // Adds a window with one tab and url to the profile's session service.
    127   // If |pinned| is true, the tab is marked as pinned in the session service.
    128   void AddWindowWithOneTabToSessionService(bool pinned) {
    129     SessionService* session_service =
    130         SessionServiceFactory::GetForProfile(profile());
    131     SessionID tab_id;
    132     SessionID window_id;
    133     session_service->SetWindowType(
    134         window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
    135     session_service->SetTabWindow(window_id, tab_id);
    136     session_service->SetTabIndexInWindow(window_id, tab_id, 0);
    137     session_service->SetSelectedTabInWindow(window_id, 0);
    138     if (pinned)
    139       session_service->SetPinnedState(window_id, tab_id, true);
    140     session_service->UpdateTabNavigation(
    141         window_id, tab_id,
    142         SerializedNavigationEntryTestHelper::CreateNavigation(
    143             url1_.spec(), "title"));
    144   }
    145 
    146   // Creates a SessionService and assigns it to the Profile. The SessionService
    147   // is configured with a single window with a single tab pointing at url1_ by
    148   // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the
    149   // tab is marked as pinned in the session service.
    150   void CreateSessionServiceWithOneWindow(bool pinned) {
    151     // The profile takes ownership of this.
    152     SessionService* session_service = new SessionService(profile());
    153     SessionServiceFactory::SetForTestProfile(profile(), session_service);
    154 
    155     AddWindowWithOneTabToSessionService(pinned);
    156 
    157     // Set this, otherwise previous session won't be loaded.
    158     profile()->set_last_session_exited_cleanly(false);
    159   }
    160 
    161   void SynchronousLoadTabsFromLastSession() {
    162     // Ensures that the load is complete before continuing.
    163     service_->LoadTabsFromLastSession();
    164     content::BrowserThread::GetBlockingPool()->FlushForTesting();
    165     base::RunLoop().RunUntilIdle();
    166     content::BrowserThread::GetBlockingPool()->FlushForTesting();
    167   }
    168 
    169   GURL url1_;
    170   GURL url2_;
    171   GURL url3_;
    172   std::string user_agent_override_;
    173   scoped_ptr<PersistentTabRestoreService> service_;
    174   PersistentTabRestoreTimeFactory* time_factory_;
    175 };
    176 
    177 namespace {
    178 
    179 class TestTabRestoreServiceObserver : public TabRestoreServiceObserver {
    180  public:
    181   TestTabRestoreServiceObserver() : got_loaded_(false) {}
    182 
    183   void clear_got_loaded() { got_loaded_ = false; }
    184   bool got_loaded() const { return got_loaded_; }
    185 
    186   // TabRestoreServiceObserver:
    187   virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE {
    188   }
    189   virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE {
    190   }
    191   virtual void TabRestoreServiceLoaded(TabRestoreService* service) OVERRIDE {
    192     got_loaded_ = true;
    193   }
    194 
    195  private:
    196   // Was TabRestoreServiceLoaded() invoked?
    197   bool got_loaded_;
    198 
    199   DISALLOW_COPY_AND_ASSIGN(TestTabRestoreServiceObserver);
    200 };
    201 
    202 }  // namespace
    203 
    204 TEST_F(PersistentTabRestoreServiceTest, Basic) {
    205   AddThreeNavigations();
    206 
    207   // Have the service record the tab.
    208   service_->CreateHistoricalTab(web_contents(), -1);
    209 
    210   // Make sure an entry was created.
    211   ASSERT_EQ(1U, service_->entries().size());
    212 
    213   // Make sure the entry matches.
    214   TabRestoreService::Entry* entry = service_->entries().front();
    215   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    216   Tab* tab = static_cast<Tab*>(entry);
    217   EXPECT_FALSE(tab->pinned);
    218   EXPECT_TRUE(tab->extension_app_id.empty());
    219   ASSERT_EQ(3U, tab->navigations.size());
    220   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    221   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    222   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    223   EXPECT_EQ("", tab->user_agent_override);
    224   EXPECT_EQ(2, tab->current_navigation_index);
    225   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    226             tab->timestamp.ToInternalValue());
    227 
    228   NavigateToIndex(1);
    229 
    230   // And check again, but set the user agent override this time.
    231   web_contents()->SetUserAgentOverride(user_agent_override_);
    232   service_->CreateHistoricalTab(web_contents(), -1);
    233 
    234   // There should be two entries now.
    235   ASSERT_EQ(2U, service_->entries().size());
    236 
    237   // Make sure the entry matches.
    238   entry = service_->entries().front();
    239   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    240   tab = static_cast<Tab*>(entry);
    241   EXPECT_FALSE(tab->pinned);
    242   ASSERT_EQ(3U, tab->navigations.size());
    243   EXPECT_EQ(url1_, tab->navigations[0].virtual_url());
    244   EXPECT_EQ(url2_, tab->navigations[1].virtual_url());
    245   EXPECT_EQ(url3_, tab->navigations[2].virtual_url());
    246   EXPECT_EQ(user_agent_override_, tab->user_agent_override);
    247   EXPECT_EQ(1, tab->current_navigation_index);
    248   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    249             tab->timestamp.ToInternalValue());
    250 }
    251 
    252 // Make sure TabRestoreService doesn't create an entry for a tab with no
    253 // navigations.
    254 TEST_F(PersistentTabRestoreServiceTest, DontCreateEmptyTab) {
    255   service_->CreateHistoricalTab(web_contents(), -1);
    256   EXPECT_TRUE(service_->entries().empty());
    257 }
    258 
    259 // Tests restoring a single tab.
    260 TEST_F(PersistentTabRestoreServiceTest, Restore) {
    261   AddThreeNavigations();
    262 
    263   // Have the service record the tab.
    264   service_->CreateHistoricalTab(web_contents(), -1);
    265 
    266   // Recreate the service and have it load the tabs.
    267   RecreateService();
    268 
    269   // One entry should be created.
    270   ASSERT_EQ(1U, service_->entries().size());
    271 
    272   // And verify the entry.
    273   PersistentTabRestoreService::Entry* entry = service_->entries().front();
    274   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    275   Tab* tab = static_cast<Tab*>(entry);
    276   EXPECT_FALSE(tab->pinned);
    277   ASSERT_EQ(3U, tab->navigations.size());
    278   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    279   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    280   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    281   EXPECT_EQ(2, tab->current_navigation_index);
    282   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    283             tab->timestamp.ToInternalValue());
    284 }
    285 
    286 // Tests restoring a single pinned tab.
    287 TEST_F(PersistentTabRestoreServiceTest, RestorePinnedAndApp) {
    288   AddThreeNavigations();
    289 
    290   // Have the service record the tab.
    291   service_->CreateHistoricalTab(web_contents(), -1);
    292 
    293   // One entry should be created.
    294   ASSERT_EQ(1U, service_->entries().size());
    295 
    296   // We have to explicitly mark the tab as pinned as there is no browser for
    297   // these tests.
    298   TabRestoreService::Entry* entry = service_->entries().front();
    299   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    300   Tab* tab = static_cast<Tab*>(entry);
    301   tab->pinned = true;
    302   const std::string extension_app_id("test");
    303   tab->extension_app_id = extension_app_id;
    304 
    305   // Recreate the service and have it load the tabs.
    306   RecreateService();
    307 
    308   // One entry should be created.
    309   ASSERT_EQ(1U, service_->entries().size());
    310 
    311   // And verify the entry.
    312   entry = service_->entries().front();
    313   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    314   tab = static_cast<Tab*>(entry);
    315   EXPECT_TRUE(tab->pinned);
    316   ASSERT_EQ(3U, tab->navigations.size());
    317   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    318   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    319   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    320   EXPECT_EQ(2, tab->current_navigation_index);
    321   EXPECT_TRUE(extension_app_id == tab->extension_app_id);
    322 }
    323 
    324 // We only restore apps on chromeos.
    325 #if defined(USE_AURA)
    326 
    327 typedef InProcessBrowserTest PersistentTabRestoreServiceBrowserTest;
    328 
    329 IN_PROC_BROWSER_TEST_F(PersistentTabRestoreServiceBrowserTest, RestoreApp) {
    330   Profile* profile = browser()->profile();
    331   TabRestoreService* trs = TabRestoreServiceFactory::GetForProfile(profile);
    332   const char* app_name = "TestApp";
    333 
    334   Browser* app_browser = CreateBrowserForApp(app_name, profile);
    335   app_browser->window()->Close();
    336   content::WindowedNotificationObserver observer(
    337       chrome::NOTIFICATION_BROWSER_CLOSED,
    338       content::Source<Browser>(app_browser));
    339   observer.Wait();
    340 
    341   // One entry should be created.
    342   ASSERT_EQ(1U, trs->entries().size());
    343   const TabRestoreService::Entry* restored_entry = trs->entries().front();
    344 
    345   // It should be a window with an app.
    346   ASSERT_EQ(TabRestoreService::WINDOW, restored_entry->type);
    347   const Window* restored_window =
    348       static_cast<const Window*>(restored_entry);
    349   EXPECT_EQ(app_name, restored_window->app_name);
    350 }
    351 #endif  // defined(USE_AURA)
    352 
    353 // Make sure we persist entries to disk that have post data.
    354 TEST_F(PersistentTabRestoreServiceTest, DontPersistPostData) {
    355   AddThreeNavigations();
    356   controller().GetEntryAtIndex(0)->SetHasPostData(true);
    357   controller().GetEntryAtIndex(1)->SetHasPostData(true);
    358   controller().GetEntryAtIndex(2)->SetHasPostData(true);
    359 
    360   // Have the service record the tab.
    361   service_->CreateHistoricalTab(web_contents(), -1);
    362   ASSERT_EQ(1U, service_->entries().size());
    363 
    364   // Recreate the service and have it load the tabs.
    365   RecreateService();
    366 
    367   // One entry should be created.
    368   ASSERT_EQ(1U, service_->entries().size());
    369 
    370   const TabRestoreService::Entry* restored_entry = service_->entries().front();
    371   ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
    372 
    373   const Tab* restored_tab =
    374       static_cast<const Tab*>(restored_entry);
    375   // There should be 3 navs.
    376   ASSERT_EQ(3U, restored_tab->navigations.size());
    377   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    378             restored_tab->timestamp.ToInternalValue());
    379 }
    380 
    381 // Make sure we don't persist entries to disk that have post data. This
    382 // differs from DontPersistPostData1 in that all the navigations have post
    383 // data, so that nothing should be persisted.
    384 TEST_F(PersistentTabRestoreServiceTest, DontLoadTwice) {
    385   AddThreeNavigations();
    386 
    387   // Have the service record the tab.
    388   service_->CreateHistoricalTab(web_contents(), -1);
    389   ASSERT_EQ(1U, service_->entries().size());
    390 
    391   // Recreate the service and have it load the tabs.
    392   RecreateService();
    393 
    394   SynchronousLoadTabsFromLastSession();
    395 
    396   // There should only be one entry.
    397   ASSERT_EQ(1U, service_->entries().size());
    398 }
    399 
    400 // Makes sure we load the previous session as necessary.
    401 TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSession) {
    402   CreateSessionServiceWithOneWindow(false);
    403 
    404   SessionServiceFactory::GetForProfile(profile())->
    405       MoveCurrentSessionToLastSession();
    406 
    407   EXPECT_FALSE(service_->IsLoaded());
    408 
    409   TestTabRestoreServiceObserver observer;
    410   service_->AddObserver(&observer);
    411   SynchronousLoadTabsFromLastSession();
    412   EXPECT_TRUE(observer.got_loaded());
    413   service_->RemoveObserver(&observer);
    414 
    415   // Make sure we get back one entry with one tab whose url is url1.
    416   ASSERT_EQ(1U, service_->entries().size());
    417   TabRestoreService::Entry* entry2 = service_->entries().front();
    418   ASSERT_EQ(TabRestoreService::WINDOW, entry2->type);
    419   TabRestoreService::Window* window =
    420       static_cast<TabRestoreService::Window*>(entry2);
    421   ASSERT_EQ(1U, window->tabs.size());
    422   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    423   EXPECT_EQ(0, window->selected_tab_index);
    424   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    425   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    426   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    427   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    428 }
    429 
    430 // Makes sure we don't attempt to load previous sessions after a restore.
    431 TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterRestore) {
    432   CreateSessionServiceWithOneWindow(false);
    433 
    434   SessionServiceFactory::GetForProfile(profile())->
    435       MoveCurrentSessionToLastSession();
    436 
    437   profile()->set_restored_last_session(true);
    438 
    439   SynchronousLoadTabsFromLastSession();
    440 
    441   // Because we restored a session PersistentTabRestoreService shouldn't load
    442   // the tabs.
    443   ASSERT_EQ(0U, service_->entries().size());
    444 }
    445 
    446 // Makes sure we don't attempt to load previous sessions after a clean exit.
    447 TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterCleanExit) {
    448   CreateSessionServiceWithOneWindow(false);
    449 
    450   SessionServiceFactory::GetForProfile(profile())->
    451       MoveCurrentSessionToLastSession();
    452 
    453   profile()->set_last_session_exited_cleanly(true);
    454 
    455   SynchronousLoadTabsFromLastSession();
    456 
    457   ASSERT_EQ(0U, service_->entries().size());
    458 }
    459 
    460 TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabs) {
    461   CreateSessionServiceWithOneWindow(false);
    462 
    463   SessionServiceFactory::GetForProfile(profile())->
    464       MoveCurrentSessionToLastSession();
    465 
    466   AddThreeNavigations();
    467 
    468   service_->CreateHistoricalTab(web_contents(), -1);
    469 
    470   RecreateService();
    471 
    472   // We should get back two entries, one from the previous session and one from
    473   // the tab restore service. The previous session entry should be first.
    474   ASSERT_EQ(2U, service_->entries().size());
    475   // The first entry should come from the session service.
    476   TabRestoreService::Entry* entry = service_->entries().front();
    477   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    478   TabRestoreService::Window* window =
    479       static_cast<TabRestoreService::Window*>(entry);
    480   ASSERT_EQ(1U, window->tabs.size());
    481   EXPECT_EQ(0, window->selected_tab_index);
    482   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    483   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    484   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    485   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    486   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    487 
    488   // Then the closed tab.
    489   entry = *(++service_->entries().begin());
    490   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    491   Tab* tab = static_cast<Tab*>(entry);
    492   ASSERT_FALSE(tab->pinned);
    493   ASSERT_EQ(3U, tab->navigations.size());
    494   EXPECT_EQ(2, tab->current_navigation_index);
    495   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    496             tab->timestamp.ToInternalValue());
    497   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    498   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    499   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    500 }
    501 
    502 // Make sure pinned state is correctly loaded from session service.
    503 TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabsPinned) {
    504   CreateSessionServiceWithOneWindow(true);
    505 
    506   SessionServiceFactory::GetForProfile(profile())->
    507       MoveCurrentSessionToLastSession();
    508 
    509   AddThreeNavigations();
    510 
    511   service_->CreateHistoricalTab(web_contents(), -1);
    512 
    513   RecreateService();
    514 
    515   // We should get back two entries, one from the previous session and one from
    516   // the tab restore service. The previous session entry should be first.
    517   ASSERT_EQ(2U, service_->entries().size());
    518   // The first entry should come from the session service.
    519   TabRestoreService::Entry* entry = service_->entries().front();
    520   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    521   TabRestoreService::Window* window =
    522       static_cast<TabRestoreService::Window*>(entry);
    523   ASSERT_EQ(1U, window->tabs.size());
    524   EXPECT_EQ(0, window->selected_tab_index);
    525   EXPECT_TRUE(window->tabs[0].pinned);
    526   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    527   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    528   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    529 
    530   // Then the closed tab.
    531   entry = *(++service_->entries().begin());
    532   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    533   Tab* tab = static_cast<Tab*>(entry);
    534   ASSERT_FALSE(tab->pinned);
    535   ASSERT_EQ(3U, tab->navigations.size());
    536   EXPECT_EQ(2, tab->current_navigation_index);
    537   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    538   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    539   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    540 }
    541 
    542 // Creates kMaxEntries + 1 windows in the session service and makes sure we only
    543 // get back kMaxEntries on restore.
    544 TEST_F(PersistentTabRestoreServiceTest, ManyWindowsInSessionService) {
    545   CreateSessionServiceWithOneWindow(false);
    546 
    547   for (size_t i = 0; i < kMaxEntries; ++i)
    548     AddWindowWithOneTabToSessionService(false);
    549 
    550   SessionServiceFactory::GetForProfile(profile())->
    551       MoveCurrentSessionToLastSession();
    552 
    553   AddThreeNavigations();
    554 
    555   service_->CreateHistoricalTab(web_contents(), -1);
    556 
    557   RecreateService();
    558 
    559   // We should get back kMaxEntries entries. We added more, but
    560   // TabRestoreService only allows up to kMaxEntries.
    561   ASSERT_EQ(kMaxEntries, service_->entries().size());
    562 
    563   // The first entry should come from the session service.
    564   TabRestoreService::Entry* entry = service_->entries().front();
    565   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    566   TabRestoreService::Window* window =
    567       static_cast<TabRestoreService::Window*>(entry);
    568   ASSERT_EQ(1U, window->tabs.size());
    569   EXPECT_EQ(0, window->selected_tab_index);
    570   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    571   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    572   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    573   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    574   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    575 }
    576 
    577 // Makes sure we restore timestamps correctly.
    578 TEST_F(PersistentTabRestoreServiceTest, TimestampSurvivesRestore) {
    579   base::Time tab_timestamp(base::Time::FromInternalValue(123456789));
    580 
    581   AddThreeNavigations();
    582 
    583   // Have the service record the tab.
    584   service_->CreateHistoricalTab(web_contents(), -1);
    585 
    586   // Make sure an entry was created.
    587   ASSERT_EQ(1U, service_->entries().size());
    588 
    589   // Make sure the entry matches.
    590   std::vector<SerializedNavigationEntry> old_navigations;
    591   {
    592     // |entry|/|tab| doesn't survive after RecreateService().
    593     TabRestoreService::Entry* entry = service_->entries().front();
    594     ASSERT_EQ(TabRestoreService::TAB, entry->type);
    595     Tab* tab = static_cast<Tab*>(entry);
    596     tab->timestamp = tab_timestamp;
    597     old_navigations = tab->navigations;
    598   }
    599 
    600   EXPECT_EQ(3U, old_navigations.size());
    601   for (size_t i = 0; i < old_navigations.size(); ++i) {
    602     EXPECT_FALSE(old_navigations[i].timestamp().is_null());
    603   }
    604 
    605   // Set this, otherwise previous session won't be loaded.
    606   profile()->set_last_session_exited_cleanly(false);
    607 
    608   RecreateService();
    609 
    610   // One entry should be created.
    611   ASSERT_EQ(1U, service_->entries().size());
    612 
    613   // And verify the entry.
    614   TabRestoreService::Entry* restored_entry = service_->entries().front();
    615   ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
    616   Tab* restored_tab =
    617       static_cast<Tab*>(restored_entry);
    618   EXPECT_EQ(tab_timestamp.ToInternalValue(),
    619             restored_tab->timestamp.ToInternalValue());
    620   ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size());
    621   for (size_t i = 0; i < restored_tab->navigations.size(); ++i) {
    622     EXPECT_EQ(old_navigations[i].timestamp(),
    623               restored_tab->navigations[i].timestamp());
    624   }
    625 }
    626 
    627 TEST_F(PersistentTabRestoreServiceTest, PruneEntries) {
    628   service_->ClearEntries();
    629   ASSERT_TRUE(service_->entries().empty());
    630 
    631   const size_t max_entries = kMaxEntries;
    632   for (size_t i = 0; i < max_entries + 5; i++) {
    633     SerializedNavigationEntry navigation =
    634         SerializedNavigationEntryTestHelper::CreateNavigation(
    635             base::StringPrintf("http://%d", static_cast<int>(i)),
    636             base::StringPrintf("%d", static_cast<int>(i)));
    637 
    638     Tab* tab = new Tab();
    639     tab->navigations.push_back(navigation);
    640     tab->current_navigation_index = 0;
    641 
    642     mutable_entries()->push_back(tab);
    643   }
    644 
    645   // Only keep kMaxEntries around.
    646   EXPECT_EQ(max_entries + 5, service_->entries().size());
    647   PruneEntries();
    648   EXPECT_EQ(max_entries, service_->entries().size());
    649   // Pruning again does nothing.
    650   PruneEntries();
    651   EXPECT_EQ(max_entries, service_->entries().size());
    652 
    653   // Prune older first.
    654   const char kRecentUrl[] = "http://recent";
    655   SerializedNavigationEntry navigation =
    656       SerializedNavigationEntryTestHelper::CreateNavigation(kRecentUrl,
    657                                                             "Most recent");
    658   Tab* tab = new Tab();
    659   tab->navigations.push_back(navigation);
    660   tab->current_navigation_index = 0;
    661   mutable_entries()->push_front(tab);
    662   EXPECT_EQ(max_entries + 1, service_->entries().size());
    663   PruneEntries();
    664   EXPECT_EQ(max_entries, service_->entries().size());
    665   EXPECT_EQ(GURL(kRecentUrl),
    666       static_cast<Tab*>(service_->entries().front())->
    667           navigations[0].virtual_url());
    668 
    669   // Ignore NTPs.
    670   navigation = SerializedNavigationEntryTestHelper::CreateNavigation(
    671       chrome::kChromeUINewTabURL, "New tab");
    672 
    673   tab = new Tab();
    674   tab->navigations.push_back(navigation);
    675   tab->current_navigation_index = 0;
    676   mutable_entries()->push_front(tab);
    677 
    678   EXPECT_EQ(max_entries + 1, service_->entries().size());
    679   PruneEntries();
    680   EXPECT_EQ(max_entries, service_->entries().size());
    681   EXPECT_EQ(GURL(kRecentUrl),
    682       static_cast<Tab*>(service_->entries().front())->
    683           navigations[0].virtual_url());
    684 
    685   // Don't prune pinned NTPs.
    686   tab = new Tab();
    687   tab->pinned = true;
    688   tab->current_navigation_index = 0;
    689   tab->navigations.push_back(navigation);
    690   mutable_entries()->push_front(tab);
    691   EXPECT_EQ(max_entries + 1, service_->entries().size());
    692   PruneEntries();
    693   EXPECT_EQ(max_entries, service_->entries().size());
    694   EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
    695       static_cast<Tab*>(service_->entries().front())->
    696           navigations[0].virtual_url());
    697 
    698   // Don't prune NTPs that have multiple navigations.
    699   // (Erase the last NTP first.)
    700   delete service_->entries().front();
    701   mutable_entries()->erase(mutable_entries()->begin());
    702   tab = new Tab();
    703   tab->current_navigation_index = 1;
    704   tab->navigations.push_back(navigation);
    705   tab->navigations.push_back(navigation);
    706   mutable_entries()->push_front(tab);
    707   EXPECT_EQ(max_entries, service_->entries().size());
    708   PruneEntries();
    709   EXPECT_EQ(max_entries, service_->entries().size());
    710   EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
    711       static_cast<Tab*>(service_->entries().front())->
    712           navigations[1].virtual_url());
    713 }
    714 
    715 // Regression test for crbug.com/106082
    716 TEST_F(PersistentTabRestoreServiceTest, PruneIsCalled) {
    717   CreateSessionServiceWithOneWindow(false);
    718 
    719   SessionServiceFactory::GetForProfile(profile())->
    720       MoveCurrentSessionToLastSession();
    721 
    722   profile()->set_restored_last_session(true);
    723 
    724   const size_t max_entries = kMaxEntries;
    725   for (size_t i = 0; i < max_entries + 5; i++) {
    726     NavigateAndCommit(
    727         GURL(base::StringPrintf("http://%d", static_cast<int>(i))));
    728     service_->CreateHistoricalTab(web_contents(), -1);
    729   }
    730 
    731   EXPECT_EQ(max_entries, service_->entries().size());
    732   // This should not crash.
    733   SynchronousLoadTabsFromLastSession();
    734   EXPECT_EQ(max_entries, service_->entries().size());
    735 }
    736 
    737 // Makes sure invoking LoadTabsFromLastSession() when the max number of entries
    738 // have been added results in IsLoaded() returning true and notifies observers.
    739 TEST_F(PersistentTabRestoreServiceTest, GoToLoadedWhenHaveMaxEntries) {
    740   const size_t max_entries = kMaxEntries;
    741   for (size_t i = 0; i < max_entries + 5; i++) {
    742     NavigateAndCommit(
    743         GURL(base::StringPrintf("http://%d", static_cast<int>(i))));
    744     service_->CreateHistoricalTab(web_contents(), -1);
    745   }
    746 
    747   EXPECT_FALSE(service_->IsLoaded());
    748   TestTabRestoreServiceObserver observer;
    749   service_->AddObserver(&observer);
    750   EXPECT_EQ(max_entries, service_->entries().size());
    751   SynchronousLoadTabsFromLastSession();
    752   EXPECT_TRUE(observer.got_loaded());
    753   EXPECT_TRUE(service_->IsLoaded());
    754   service_->RemoveObserver(&observer);
    755 }
    756