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 <string> 8 9#include "base/compiler_specific.h" 10#include "base/strings/stringprintf.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/threading/sequenced_worker_pool.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/sessions/session_service.h" 15#include "chrome/browser/sessions/session_service_factory.h" 16#include "chrome/browser/sessions/session_types.h" 17#include "chrome/browser/sessions/tab_restore_service_factory.h" 18#include "chrome/browser/sessions/tab_restore_service_observer.h" 19#include "chrome/common/url_constants.h" 20#include "chrome/test/base/chrome_render_view_host_test_harness.h" 21#include "chrome/test/base/chrome_render_view_test.h" 22#include "chrome/test/base/testing_profile.h" 23#include "components/sessions/serialized_navigation_entry_test_helper.h" 24#include "content/public/browser/browser_thread.h" 25#include "content/public/browser/navigation_controller.h" 26#include "content/public/browser/navigation_entry.h" 27#include "content/public/browser/notification_service.h" 28#include "content/public/browser/notification_types.h" 29#include "content/public/browser/web_contents.h" 30#include "content/public/test/render_view_test.h" 31#include "content/public/test/test_utils.h" 32#include "content/public/test/web_contents_tester.h" 33#include "testing/gtest/include/gtest/gtest.h" 34 35typedef TabRestoreService::Tab Tab; 36typedef TabRestoreService::Window Window; 37 38using content::NavigationEntry; 39using content::WebContentsTester; 40using sessions::SerializedNavigationEntry; 41using sessions::SerializedNavigationEntryTestHelper; 42 43// Create subclass that overrides TimeNow so that we can control the time used 44// for closed tabs and windows. 45class PersistentTabRestoreTimeFactory : public TabRestoreService::TimeFactory { 46 public: 47 PersistentTabRestoreTimeFactory() : time_(base::Time::Now()) {} 48 49 virtual ~PersistentTabRestoreTimeFactory() {} 50 51 virtual base::Time TimeNow() OVERRIDE { 52 return time_; 53 } 54 55 private: 56 base::Time time_; 57}; 58 59class PersistentTabRestoreServiceTest : public ChromeRenderViewHostTestHarness { 60 public: 61 PersistentTabRestoreServiceTest() 62 : url1_("http://1"), 63 url2_("http://2"), 64 url3_("http://3"), 65 user_agent_override_( 66 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19" 67 " (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19"), 68 time_factory_(NULL) { 69 } 70 71 virtual ~PersistentTabRestoreServiceTest() { 72 } 73 74 protected: 75 enum { 76 kMaxEntries = TabRestoreServiceHelper::kMaxEntries, 77 }; 78 79 // testing::Test: 80 virtual void SetUp() OVERRIDE { 81 ChromeRenderViewHostTestHarness::SetUp(); 82 time_factory_ = new PersistentTabRestoreTimeFactory(); 83 service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); 84 } 85 86 virtual void TearDown() OVERRIDE { 87 service_->Shutdown(); 88 service_.reset(); 89 delete time_factory_; 90 ChromeRenderViewHostTestHarness::TearDown(); 91 } 92 93 TabRestoreService::Entries* mutable_entries() { 94 return service_->mutable_entries(); 95 } 96 97 void PruneEntries() { 98 service_->PruneEntries(); 99 } 100 101 void AddThreeNavigations() { 102 // Navigate to three URLs. 103 NavigateAndCommit(url1_); 104 NavigateAndCommit(url2_); 105 NavigateAndCommit(url3_); 106 } 107 108 void NavigateToIndex(int index) { 109 // Navigate back. We have to do this song and dance as NavigationController 110 // isn't happy if you navigate immediately while going back. 111 controller().GoToIndex(index); 112 WebContentsTester::For(web_contents())->CommitPendingNavigation(); 113 } 114 115 void RecreateService() { 116 // Must set service to null first so that it is destroyed before the new 117 // one is created. 118 service_->Shutdown(); 119 content::RunAllBlockingPoolTasksUntilIdle(); 120 service_.reset(); 121 service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); 122 SynchronousLoadTabsFromLastSession(); 123 } 124 125 // Adds a window with one tab and url to the profile's session service. 126 // If |pinned| is true, the tab is marked as pinned in the session service. 127 void AddWindowWithOneTabToSessionService(bool pinned) { 128 SessionService* session_service = 129 SessionServiceFactory::GetForProfile(profile()); 130 SessionID tab_id; 131 SessionID window_id; 132 session_service->SetWindowType( 133 window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL); 134 session_service->SetTabWindow(window_id, tab_id); 135 session_service->SetTabIndexInWindow(window_id, tab_id, 0); 136 session_service->SetSelectedTabInWindow(window_id, 0); 137 if (pinned) 138 session_service->SetPinnedState(window_id, tab_id, true); 139 session_service->UpdateTabNavigation( 140 window_id, tab_id, 141 SerializedNavigationEntryTestHelper::CreateNavigation( 142 url1_.spec(), "title")); 143 } 144 145 // Creates a SessionService and assigns it to the Profile. The SessionService 146 // is configured with a single window with a single tab pointing at url1_ by 147 // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the 148 // tab is marked as pinned in the session service. 149 void CreateSessionServiceWithOneWindow(bool pinned) { 150 // The profile takes ownership of this. 151 SessionService* session_service = new SessionService(profile()); 152 SessionServiceFactory::SetForTestProfile(profile(), session_service); 153 154 AddWindowWithOneTabToSessionService(pinned); 155 156 // Set this, otherwise previous session won't be loaded. 157 profile()->set_last_session_exited_cleanly(false); 158 } 159 160 void SynchronousLoadTabsFromLastSession() { 161 // Ensures that the load is complete before continuing. 162 service_->LoadTabsFromLastSession(); 163 content::RunAllBlockingPoolTasksUntilIdle(); 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 174namespace { 175 176class 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 201TEST_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. 251TEST_F(PersistentTabRestoreServiceTest, DontCreateEmptyTab) { 252 service_->CreateHistoricalTab(web_contents(), -1); 253 EXPECT_TRUE(service_->entries().empty()); 254} 255 256// Tests restoring a single tab. 257TEST_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. 284TEST_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. 322TEST_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. 352TEST_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. 369TEST_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. 399TEST_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. 415TEST_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 428TEST_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. 471TEST_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. 512TEST_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. 546TEST_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. 596TEST_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 639TEST_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 728TEST_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. 751TEST_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