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