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