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