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