Home | History | Annotate | Download | only in sessions
      1 // Copyright (c) 2010 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/session_types.h"
      6 #include "chrome/browser/sessions/session_service.h"
      7 #include "chrome/browser/sessions/tab_restore_service.h"
      8 #include "chrome/test/render_view_test.h"
      9 #include "chrome/test/testing_profile.h"
     10 #include "content/browser/renderer_host/test_render_view_host.h"
     11 #include "content/browser/tab_contents/navigation_controller.h"
     12 #include "content/browser/tab_contents/navigation_entry.h"
     13 #include "content/browser/tab_contents/test_tab_contents.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
     16 
     17 // Create subclass that overrides TimeNow so that we can control the time used
     18 // for closed tabs and windows.
     19 class TabRestoreTimeFactory : public TabRestoreService::TimeFactory {
     20  public:
     21   TabRestoreTimeFactory() : time_(base::Time::Now()) {}
     22 
     23   virtual ~TabRestoreTimeFactory() {}
     24 
     25   virtual base::Time TimeNow() {
     26     return time_;
     27   }
     28 
     29  private:
     30   base::Time time_;
     31 };
     32 
     33 class TabRestoreServiceTest : public RenderViewHostTestHarness {
     34  public:
     35   TabRestoreServiceTest() {
     36     url1_ = GURL("http://1");
     37     url2_ = GURL("http://2");
     38     url3_ = GURL("http://3");
     39   }
     40 
     41   ~TabRestoreServiceTest() {
     42   }
     43 
     44  protected:
     45   // testing::Test overrides
     46   virtual void SetUp() {
     47     RenderViewHostTestHarness::SetUp();
     48     time_factory_ = new TabRestoreTimeFactory();
     49     service_ = new TabRestoreService(profile(), time_factory_);
     50     WebKit::initialize(&webkitclient_);
     51   }
     52 
     53   virtual void TearDown() {
     54     service_ = NULL;
     55     delete time_factory_;
     56     RenderViewHostTestHarness::TearDown();
     57     WebKit::shutdown();
     58   }
     59 
     60   void AddThreeNavigations() {
     61     // Navigate to three URLs.
     62     NavigateAndCommit(url1_);
     63     NavigateAndCommit(url2_);
     64     NavigateAndCommit(url3_);
     65   }
     66 
     67   void NavigateToIndex(int index) {
     68     // Navigate back. We have to do this song and dance as NavigationController
     69     // isn't happy if you navigate immediately while going back.
     70     controller().GoToIndex(index);
     71     contents()->CommitPendingNavigation();
     72   }
     73 
     74   void RecreateService() {
     75     // Must set service to null first so that it is destroyed before the new
     76     // one is created.
     77     service_ = NULL;
     78     service_ = new TabRestoreService(profile(), time_factory_);
     79     service_->LoadTabsFromLastSession();
     80   }
     81 
     82   // Adds a window with one tab and url to the profile's session service.
     83   // If |pinned| is true, the tab is marked as pinned in the session service.
     84   void AddWindowWithOneTabToSessionService(bool pinned) {
     85     SessionService* session_service = profile()->GetSessionService();
     86     SessionID tab_id;
     87     SessionID window_id;
     88     session_service->SetWindowType(window_id, Browser::TYPE_NORMAL);
     89     session_service->SetTabWindow(window_id, tab_id);
     90     session_service->SetTabIndexInWindow(window_id, tab_id, 0);
     91     session_service->SetSelectedTabInWindow(window_id, 0);
     92     if (pinned)
     93       session_service->SetPinnedState(window_id, tab_id, true);
     94     NavigationEntry entry;
     95     entry.set_url(url1_);
     96     session_service->UpdateTabNavigation(window_id, tab_id, 0, entry);
     97   }
     98 
     99   // Creates a SessionService and assigns it to the Profile. The SessionService
    100   // is configured with a single window with a single tab pointing at url1_ by
    101   // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the
    102   // tab is marked as pinned in the session service.
    103   void CreateSessionServiceWithOneWindow(bool pinned) {
    104     // The profile takes ownership of this.
    105     SessionService* session_service = new SessionService(profile());
    106     profile()->set_session_service(session_service);
    107 
    108     AddWindowWithOneTabToSessionService(pinned);
    109 
    110     // Set this, otherwise previous session won't be loaded.
    111     profile()->set_last_session_exited_cleanly(false);
    112   }
    113 
    114   GURL url1_;
    115   GURL url2_;
    116   GURL url3_;
    117   scoped_refptr<TabRestoreService> service_;
    118   TabRestoreTimeFactory* time_factory_;
    119   RenderViewTest::RendererWebKitClientImplNoSandbox webkitclient_;
    120 };
    121 
    122 TEST_F(TabRestoreServiceTest, Basic) {
    123   AddThreeNavigations();
    124 
    125   // Have the service record the tab.
    126   service_->CreateHistoricalTab(&controller(), -1);
    127 
    128   // Make sure an entry was created.
    129   ASSERT_EQ(1U, service_->entries().size());
    130 
    131   // Make sure the entry matches.
    132   TabRestoreService::Entry* entry = service_->entries().front();
    133   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    134   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    135   EXPECT_FALSE(tab->pinned);
    136   EXPECT_TRUE(tab->extension_app_id.empty());
    137   ASSERT_EQ(3U, tab->navigations.size());
    138   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    139   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    140   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    141   EXPECT_EQ(2, tab->current_navigation_index);
    142   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    143             tab->timestamp.ToInternalValue());
    144 
    145   NavigateToIndex(1);
    146 
    147   // And check again.
    148   service_->CreateHistoricalTab(&controller(), -1);
    149 
    150   // There should be two entries now.
    151   ASSERT_EQ(2U, service_->entries().size());
    152 
    153   // Make sure the entry matches
    154   entry = service_->entries().front();
    155   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    156   tab = static_cast<TabRestoreService::Tab*>(entry);
    157   EXPECT_FALSE(tab->pinned);
    158   ASSERT_EQ(3U, tab->navigations.size());
    159   EXPECT_EQ(url1_, tab->navigations[0].virtual_url());
    160   EXPECT_EQ(url2_, tab->navigations[1].virtual_url());
    161   EXPECT_EQ(url3_, tab->navigations[2].virtual_url());
    162   EXPECT_EQ(1, tab->current_navigation_index);
    163   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    164             tab->timestamp.ToInternalValue());
    165 }
    166 
    167 // Make sure TabRestoreService doesn't create an entry for a tab with no
    168 // navigations.
    169 TEST_F(TabRestoreServiceTest, DontCreateEmptyTab) {
    170   service_->CreateHistoricalTab(&controller(), -1);
    171   EXPECT_TRUE(service_->entries().empty());
    172 }
    173 
    174 // Tests restoring a single tab.
    175 TEST_F(TabRestoreServiceTest, Restore) {
    176   AddThreeNavigations();
    177 
    178   // Have the service record the tab.
    179   service_->CreateHistoricalTab(&controller(), -1);
    180 
    181   // Recreate the service and have it load the tabs.
    182   RecreateService();
    183 
    184   // One entry should be created.
    185   ASSERT_EQ(1U, service_->entries().size());
    186 
    187   // And verify the entry.
    188   TabRestoreService::Entry* entry = service_->entries().front();
    189   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    190   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    191   EXPECT_FALSE(tab->pinned);
    192   ASSERT_EQ(3U, tab->navigations.size());
    193   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    194   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    195   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    196   EXPECT_EQ(2, tab->current_navigation_index);
    197   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    198             tab->timestamp.ToInternalValue());
    199 }
    200 
    201 // Tests restoring a single pinned tab.
    202 TEST_F(TabRestoreServiceTest, RestorePinnedAndApp) {
    203   AddThreeNavigations();
    204 
    205   // Have the service record the tab.
    206   service_->CreateHistoricalTab(&controller(), -1);
    207 
    208   // One entry should be created.
    209   ASSERT_EQ(1U, service_->entries().size());
    210 
    211   // We have to explicitly mark the tab as pinned as there is no browser for
    212   // these tests.
    213   TabRestoreService::Entry* entry = service_->entries().front();
    214   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    215   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    216   tab->pinned = true;
    217   const std::string extension_app_id("test");
    218   tab->extension_app_id = extension_app_id;
    219 
    220   // Recreate the service and have it load the tabs.
    221   RecreateService();
    222 
    223   // One entry should be created.
    224   ASSERT_EQ(1U, service_->entries().size());
    225 
    226   // And verify the entry.
    227   entry = service_->entries().front();
    228   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    229   tab = static_cast<TabRestoreService::Tab*>(entry);
    230   EXPECT_TRUE(tab->pinned);
    231   ASSERT_EQ(3U, tab->navigations.size());
    232   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    233   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    234   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    235   EXPECT_EQ(2, tab->current_navigation_index);
    236   EXPECT_TRUE(extension_app_id == tab->extension_app_id);
    237 }
    238 
    239 // Make sure we persist entries to disk that have post data.
    240 TEST_F(TabRestoreServiceTest, DontPersistPostData) {
    241   AddThreeNavigations();
    242   controller().GetEntryAtIndex(0)->set_has_post_data(true);
    243   controller().GetEntryAtIndex(1)->set_has_post_data(true);
    244   controller().GetEntryAtIndex(2)->set_has_post_data(true);
    245 
    246   // Have the service record the tab.
    247   service_->CreateHistoricalTab(&controller(), -1);
    248   ASSERT_EQ(1U, service_->entries().size());
    249 
    250   // Recreate the service and have it load the tabs.
    251   RecreateService();
    252 
    253   // One entry should be created.
    254   ASSERT_EQ(1U, service_->entries().size());
    255 
    256   const TabRestoreService::Entry* restored_entry = service_->entries().front();
    257   ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
    258 
    259   const TabRestoreService::Tab* restored_tab =
    260       static_cast<const TabRestoreService::Tab*>(restored_entry);
    261   // There should be 3 navs.
    262   ASSERT_EQ(3U, restored_tab->navigations.size());
    263   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    264             restored_tab->timestamp.ToInternalValue());
    265 }
    266 
    267 // Make sure we don't persist entries to disk that have post data. This
    268 // differs from DontPersistPostData1 in that all the navigations have post
    269 // data, so that nothing should be persisted.
    270 TEST_F(TabRestoreServiceTest, DontLoadTwice) {
    271   AddThreeNavigations();
    272 
    273   // Have the service record the tab.
    274   service_->CreateHistoricalTab(&controller(), -1);
    275   ASSERT_EQ(1U, service_->entries().size());
    276 
    277   // Recreate the service and have it load the tabs.
    278   RecreateService();
    279 
    280   service_->LoadTabsFromLastSession();
    281 
    282   // There should only be one entry.
    283   ASSERT_EQ(1U, service_->entries().size());
    284 }
    285 
    286 // Makes sure we load the previous session as necessary.
    287 TEST_F(TabRestoreServiceTest, LoadPreviousSession) {
    288   CreateSessionServiceWithOneWindow(false);
    289 
    290   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    291 
    292   service_->LoadTabsFromLastSession();
    293 
    294   // Make sure we get back one entry with one tab whose url is url1.
    295   ASSERT_EQ(1U, service_->entries().size());
    296   TabRestoreService::Entry* entry2 = service_->entries().front();
    297   ASSERT_EQ(TabRestoreService::WINDOW, entry2->type);
    298   TabRestoreService::Window* window =
    299       static_cast<TabRestoreService::Window*>(entry2);
    300   ASSERT_EQ(1U, window->tabs.size());
    301   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    302   EXPECT_EQ(0, window->selected_tab_index);
    303   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    304   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    305   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    306   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    307 }
    308 
    309 // Makes sure we don't attempt to load previous sessions after a restore.
    310 TEST_F(TabRestoreServiceTest, DontLoadAfterRestore) {
    311   CreateSessionServiceWithOneWindow(false);
    312 
    313   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    314 
    315   profile()->set_restored_last_session(true);
    316 
    317   service_->LoadTabsFromLastSession();
    318 
    319   // Because we restored a session TabRestoreService shouldn't load the tabs.
    320   ASSERT_EQ(0U, service_->entries().size());
    321 }
    322 
    323 // Makes sure we don't attempt to load previous sessions after a clean exit.
    324 TEST_F(TabRestoreServiceTest, DontLoadAfterCleanExit) {
    325   CreateSessionServiceWithOneWindow(false);
    326 
    327   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    328 
    329   profile()->set_last_session_exited_cleanly(true);
    330 
    331   service_->LoadTabsFromLastSession();
    332 
    333   ASSERT_EQ(0U, service_->entries().size());
    334 }
    335 
    336 TEST_F(TabRestoreServiceTest, LoadPreviousSessionAndTabs) {
    337   CreateSessionServiceWithOneWindow(false);
    338 
    339   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    340 
    341   AddThreeNavigations();
    342 
    343   service_->CreateHistoricalTab(&controller(), -1);
    344 
    345   RecreateService();
    346 
    347   // We should get back two entries, one from the previous session and one from
    348   // the tab restore service. The previous session entry should be first.
    349   ASSERT_EQ(2U, service_->entries().size());
    350   // The first entry should come from the session service.
    351   TabRestoreService::Entry* entry = service_->entries().front();
    352   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    353   TabRestoreService::Window* window =
    354       static_cast<TabRestoreService::Window*>(entry);
    355   ASSERT_EQ(1U, window->tabs.size());
    356   EXPECT_EQ(0, window->selected_tab_index);
    357   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    358   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    359   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    360   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    361   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    362 
    363   // Then the closed tab.
    364   entry = *(++service_->entries().begin());
    365   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    366   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    367   ASSERT_FALSE(tab->pinned);
    368   ASSERT_EQ(3U, tab->navigations.size());
    369   EXPECT_EQ(2, tab->current_navigation_index);
    370   EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
    371             tab->timestamp.ToInternalValue());
    372   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    373   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    374   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    375 }
    376 
    377 // Make sure pinned state is correctly loaded from session service.
    378 TEST_F(TabRestoreServiceTest, LoadPreviousSessionAndTabsPinned) {
    379   CreateSessionServiceWithOneWindow(true);
    380 
    381   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    382 
    383   AddThreeNavigations();
    384 
    385   service_->CreateHistoricalTab(&controller(), -1);
    386 
    387   RecreateService();
    388 
    389   // We should get back two entries, one from the previous session and one from
    390   // the tab restore service. The previous session entry should be first.
    391   ASSERT_EQ(2U, service_->entries().size());
    392   // The first entry should come from the session service.
    393   TabRestoreService::Entry* entry = service_->entries().front();
    394   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    395   TabRestoreService::Window* window =
    396       static_cast<TabRestoreService::Window*>(entry);
    397   ASSERT_EQ(1U, window->tabs.size());
    398   EXPECT_EQ(0, window->selected_tab_index);
    399   EXPECT_TRUE(window->tabs[0].pinned);
    400   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    401   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    402   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    403 
    404   // Then the closed tab.
    405   entry = *(++service_->entries().begin());
    406   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    407   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    408   ASSERT_FALSE(tab->pinned);
    409   ASSERT_EQ(3U, tab->navigations.size());
    410   EXPECT_EQ(2, tab->current_navigation_index);
    411   EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
    412   EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
    413   EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
    414 }
    415 
    416 // Creates TabRestoreService::kMaxEntries + 1 windows in the session service
    417 // and makes sure we only get back TabRestoreService::kMaxEntries on restore.
    418 TEST_F(TabRestoreServiceTest, ManyWindowsInSessionService) {
    419   CreateSessionServiceWithOneWindow(false);
    420 
    421   for (size_t i = 0; i < TabRestoreService::kMaxEntries; ++i)
    422     AddWindowWithOneTabToSessionService(false);
    423 
    424   profile()->GetSessionService()->MoveCurrentSessionToLastSession();
    425 
    426   AddThreeNavigations();
    427 
    428   service_->CreateHistoricalTab(&controller(), -1);
    429 
    430   RecreateService();
    431 
    432   // We should get back kMaxEntries entries. We added more, but
    433   // TabRestoreService only allows up to kMaxEntries.
    434   ASSERT_EQ(TabRestoreService::kMaxEntries, service_->entries().size());
    435 
    436   // The first entry should come from the session service.
    437   TabRestoreService::Entry* entry = service_->entries().front();
    438   ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
    439   TabRestoreService::Window* window =
    440       static_cast<TabRestoreService::Window*>(entry);
    441   ASSERT_EQ(1U, window->tabs.size());
    442   EXPECT_EQ(0, window->selected_tab_index);
    443   EXPECT_EQ(0, window->timestamp.ToInternalValue());
    444   ASSERT_EQ(1U, window->tabs[0].navigations.size());
    445   EXPECT_EQ(0, window->tabs[0].current_navigation_index);
    446   EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
    447   EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
    448 }
    449 
    450 // Makes sure we restore the time stamp correctly.
    451 TEST_F(TabRestoreServiceTest, TimestampSurvivesRestore) {
    452   base::Time tab_timestamp(base::Time::FromInternalValue(123456789));
    453 
    454   AddThreeNavigations();
    455 
    456   // Have the service record the tab.
    457   service_->CreateHistoricalTab(&controller(), -1);
    458 
    459   // Make sure an entry was created.
    460   ASSERT_EQ(1U, service_->entries().size());
    461 
    462   // Make sure the entry matches.
    463   TabRestoreService::Entry* entry = service_->entries().front();
    464   ASSERT_EQ(TabRestoreService::TAB, entry->type);
    465   TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
    466   tab->timestamp = tab_timestamp;
    467 
    468   // Set this, otherwise previous session won't be loaded.
    469   profile()->set_last_session_exited_cleanly(false);
    470 
    471   RecreateService();
    472 
    473   // One entry should be created.
    474   ASSERT_EQ(1U, service_->entries().size());
    475 
    476   // And verify the entry.
    477   TabRestoreService::Entry* restored_entry = service_->entries().front();
    478   ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
    479   TabRestoreService::Tab* restored_tab =
    480       static_cast<TabRestoreService::Tab*>(restored_entry);
    481   EXPECT_EQ(tab_timestamp.ToInternalValue(),
    482             restored_tab->timestamp.ToInternalValue());
    483 }
    484