• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
TabRestoreTimeFactory()21   TabRestoreTimeFactory() : time_(base::Time::Now()) {}
22 
~TabRestoreTimeFactory()23   virtual ~TabRestoreTimeFactory() {}
24 
TimeNow()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:
TabRestoreServiceTest()35   TabRestoreServiceTest() {
36     url1_ = GURL("http://1");
37     url2_ = GURL("http://2");
38     url3_ = GURL("http://3");
39   }
40 
~TabRestoreServiceTest()41   ~TabRestoreServiceTest() {
42   }
43 
44  protected:
45   // testing::Test overrides
SetUp()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 
TearDown()53   virtual void TearDown() {
54     service_ = NULL;
55     delete time_factory_;
56     RenderViewHostTestHarness::TearDown();
57     WebKit::shutdown();
58   }
59 
AddThreeNavigations()60   void AddThreeNavigations() {
61     // Navigate to three URLs.
62     NavigateAndCommit(url1_);
63     NavigateAndCommit(url2_);
64     NavigateAndCommit(url3_);
65   }
66 
NavigateToIndex(int index)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 
RecreateService()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.
AddWindowWithOneTabToSessionService(bool pinned)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.
CreateSessionServiceWithOneWindow(bool pinned)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 
TEST_F(TabRestoreServiceTest,Basic)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.
TEST_F(TabRestoreServiceTest,DontCreateEmptyTab)169 TEST_F(TabRestoreServiceTest, DontCreateEmptyTab) {
170   service_->CreateHistoricalTab(&controller(), -1);
171   EXPECT_TRUE(service_->entries().empty());
172 }
173 
174 // Tests restoring a single tab.
TEST_F(TabRestoreServiceTest,Restore)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.
TEST_F(TabRestoreServiceTest,RestorePinnedAndApp)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.
TEST_F(TabRestoreServiceTest,DontPersistPostData)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.
TEST_F(TabRestoreServiceTest,DontLoadTwice)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.
TEST_F(TabRestoreServiceTest,LoadPreviousSession)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.
TEST_F(TabRestoreServiceTest,DontLoadAfterRestore)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.
TEST_F(TabRestoreServiceTest,DontLoadAfterCleanExit)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 
TEST_F(TabRestoreServiceTest,LoadPreviousSessionAndTabs)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.
TEST_F(TabRestoreServiceTest,LoadPreviousSessionAndTabsPinned)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.
TEST_F(TabRestoreServiceTest,ManyWindowsInSessionService)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.
TEST_F(TabRestoreServiceTest,TimestampSurvivesRestore)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