persistent_tab_restore_service_browsertest.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
1// Copyright (c) 2012 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/profiles/profile.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/browser/ui/browser_window.h" 20#include "chrome/common/url_constants.h" 21#include "chrome/test/base/chrome_render_view_host_test_harness.h" 22#include "chrome/test/base/chrome_render_view_test.h" 23#include "chrome/test/base/in_process_browser_test.h" 24#include "chrome/test/base/testing_profile.h" 25#include "components/sessions/serialized_navigation_entry_test_helper.h" 26#include "content/public/browser/browser_thread.h" 27#include "content/public/browser/navigation_controller.h" 28#include "content/public/browser/navigation_entry.h" 29#include "content/public/browser/notification_service.h" 30#include "content/public/browser/notification_types.h" 31#include "content/public/browser/web_contents.h" 32#include "content/public/test/render_view_test.h" 33#include "content/public/test/test_utils.h" 34#include "content/public/test/web_contents_tester.h" 35#include "testing/gtest/include/gtest/gtest.h" 36 37typedef TabRestoreService::Tab Tab; 38typedef TabRestoreService::Window Window; 39 40using content::NavigationEntry; 41using content::WebContentsTester; 42using sessions::SerializedNavigationEntry; 43using sessions::SerializedNavigationEntryTestHelper; 44 45// Create subclass that overrides TimeNow so that we can control the time used 46// for closed tabs and windows. 47class PersistentTabRestoreTimeFactory : public TabRestoreService::TimeFactory { 48 public: 49 PersistentTabRestoreTimeFactory() : time_(base::Time::Now()) {} 50 51 virtual ~PersistentTabRestoreTimeFactory() {} 52 53 virtual base::Time TimeNow() OVERRIDE { 54 return time_; 55 } 56 57 private: 58 base::Time time_; 59}; 60 61class PersistentTabRestoreServiceTest : public ChromeRenderViewHostTestHarness { 62 public: 63 PersistentTabRestoreServiceTest() 64 : url1_("http://1"), 65 url2_("http://2"), 66 url3_("http://3"), 67 user_agent_override_( 68 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19" 69 " (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19") { 70 } 71 72 virtual ~PersistentTabRestoreServiceTest() { 73 } 74 75 protected: 76 enum { 77 kMaxEntries = TabRestoreServiceHelper::kMaxEntries, 78 }; 79 80 // testing::Test: 81 virtual void SetUp() OVERRIDE { 82 ChromeRenderViewHostTestHarness::SetUp(); 83 time_factory_ = new PersistentTabRestoreTimeFactory(); 84 service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); 85 } 86 87 virtual void TearDown() OVERRIDE { 88 service_->Shutdown(); 89 service_.reset(); 90 delete time_factory_; 91 ChromeRenderViewHostTestHarness::TearDown(); 92 } 93 94 TabRestoreService::Entries* mutable_entries() { 95 return service_->mutable_entries(); 96 } 97 98 void PruneEntries() { 99 service_->PruneEntries(); 100 } 101 102 void AddThreeNavigations() { 103 // Navigate to three URLs. 104 NavigateAndCommit(url1_); 105 NavigateAndCommit(url2_); 106 NavigateAndCommit(url3_); 107 } 108 109 void NavigateToIndex(int index) { 110 // Navigate back. We have to do this song and dance as NavigationController 111 // isn't happy if you navigate immediately while going back. 112 controller().GoToIndex(index); 113 WebContentsTester::For(web_contents())->CommitPendingNavigation(); 114 } 115 116 void RecreateService() { 117 // Must set service to null first so that it is destroyed before the new 118 // one is created. 119 service_->Shutdown(); 120 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 121 service_.reset(); 122 service_.reset(new PersistentTabRestoreService(profile(), time_factory_)); 123 SynchronousLoadTabsFromLastSession(); 124 } 125 126 // Adds a window with one tab and url to the profile's session service. 127 // If |pinned| is true, the tab is marked as pinned in the session service. 128 void AddWindowWithOneTabToSessionService(bool pinned) { 129 SessionService* session_service = 130 SessionServiceFactory::GetForProfile(profile()); 131 SessionID tab_id; 132 SessionID window_id; 133 session_service->SetWindowType( 134 window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL); 135 session_service->SetTabWindow(window_id, tab_id); 136 session_service->SetTabIndexInWindow(window_id, tab_id, 0); 137 session_service->SetSelectedTabInWindow(window_id, 0); 138 if (pinned) 139 session_service->SetPinnedState(window_id, tab_id, true); 140 session_service->UpdateTabNavigation( 141 window_id, tab_id, 142 SerializedNavigationEntryTestHelper::CreateNavigation( 143 url1_.spec(), "title")); 144 } 145 146 // Creates a SessionService and assigns it to the Profile. The SessionService 147 // is configured with a single window with a single tab pointing at url1_ by 148 // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the 149 // tab is marked as pinned in the session service. 150 void CreateSessionServiceWithOneWindow(bool pinned) { 151 // The profile takes ownership of this. 152 SessionService* session_service = new SessionService(profile()); 153 SessionServiceFactory::SetForTestProfile(profile(), session_service); 154 155 AddWindowWithOneTabToSessionService(pinned); 156 157 // Set this, otherwise previous session won't be loaded. 158 profile()->set_last_session_exited_cleanly(false); 159 } 160 161 void SynchronousLoadTabsFromLastSession() { 162 // Ensures that the load is complete before continuing. 163 service_->LoadTabsFromLastSession(); 164 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 165 base::RunLoop().RunUntilIdle(); 166 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 167 } 168 169 GURL url1_; 170 GURL url2_; 171 GURL url3_; 172 std::string user_agent_override_; 173 scoped_ptr<PersistentTabRestoreService> service_; 174 PersistentTabRestoreTimeFactory* time_factory_; 175}; 176 177namespace { 178 179class TestTabRestoreServiceObserver : public TabRestoreServiceObserver { 180 public: 181 TestTabRestoreServiceObserver() : got_loaded_(false) {} 182 183 void clear_got_loaded() { got_loaded_ = false; } 184 bool got_loaded() const { return got_loaded_; } 185 186 // TabRestoreServiceObserver: 187 virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE { 188 } 189 virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE { 190 } 191 virtual void TabRestoreServiceLoaded(TabRestoreService* service) OVERRIDE { 192 got_loaded_ = true; 193 } 194 195 private: 196 // Was TabRestoreServiceLoaded() invoked? 197 bool got_loaded_; 198 199 DISALLOW_COPY_AND_ASSIGN(TestTabRestoreServiceObserver); 200}; 201 202} // namespace 203 204TEST_F(PersistentTabRestoreServiceTest, Basic) { 205 AddThreeNavigations(); 206 207 // Have the service record the tab. 208 service_->CreateHistoricalTab(web_contents(), -1); 209 210 // Make sure an entry was created. 211 ASSERT_EQ(1U, service_->entries().size()); 212 213 // Make sure the entry matches. 214 TabRestoreService::Entry* entry = service_->entries().front(); 215 ASSERT_EQ(TabRestoreService::TAB, entry->type); 216 Tab* tab = static_cast<Tab*>(entry); 217 EXPECT_FALSE(tab->pinned); 218 EXPECT_TRUE(tab->extension_app_id.empty()); 219 ASSERT_EQ(3U, tab->navigations.size()); 220 EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); 221 EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); 222 EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); 223 EXPECT_EQ("", tab->user_agent_override); 224 EXPECT_EQ(2, tab->current_navigation_index); 225 EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), 226 tab->timestamp.ToInternalValue()); 227 228 NavigateToIndex(1); 229 230 // And check again, but set the user agent override this time. 231 web_contents()->SetUserAgentOverride(user_agent_override_); 232 service_->CreateHistoricalTab(web_contents(), -1); 233 234 // There should be two entries now. 235 ASSERT_EQ(2U, service_->entries().size()); 236 237 // Make sure the entry matches. 238 entry = service_->entries().front(); 239 ASSERT_EQ(TabRestoreService::TAB, entry->type); 240 tab = static_cast<Tab*>(entry); 241 EXPECT_FALSE(tab->pinned); 242 ASSERT_EQ(3U, tab->navigations.size()); 243 EXPECT_EQ(url1_, tab->navigations[0].virtual_url()); 244 EXPECT_EQ(url2_, tab->navigations[1].virtual_url()); 245 EXPECT_EQ(url3_, tab->navigations[2].virtual_url()); 246 EXPECT_EQ(user_agent_override_, tab->user_agent_override); 247 EXPECT_EQ(1, tab->current_navigation_index); 248 EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), 249 tab->timestamp.ToInternalValue()); 250} 251 252// Make sure TabRestoreService doesn't create an entry for a tab with no 253// navigations. 254TEST_F(PersistentTabRestoreServiceTest, DontCreateEmptyTab) { 255 service_->CreateHistoricalTab(web_contents(), -1); 256 EXPECT_TRUE(service_->entries().empty()); 257} 258 259// Tests restoring a single tab. 260TEST_F(PersistentTabRestoreServiceTest, Restore) { 261 AddThreeNavigations(); 262 263 // Have the service record the tab. 264 service_->CreateHistoricalTab(web_contents(), -1); 265 266 // Recreate the service and have it load the tabs. 267 RecreateService(); 268 269 // One entry should be created. 270 ASSERT_EQ(1U, service_->entries().size()); 271 272 // And verify the entry. 273 PersistentTabRestoreService::Entry* entry = service_->entries().front(); 274 ASSERT_EQ(TabRestoreService::TAB, entry->type); 275 Tab* tab = static_cast<Tab*>(entry); 276 EXPECT_FALSE(tab->pinned); 277 ASSERT_EQ(3U, tab->navigations.size()); 278 EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); 279 EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); 280 EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); 281 EXPECT_EQ(2, tab->current_navigation_index); 282 EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), 283 tab->timestamp.ToInternalValue()); 284} 285 286// Tests restoring a single pinned tab. 287TEST_F(PersistentTabRestoreServiceTest, RestorePinnedAndApp) { 288 AddThreeNavigations(); 289 290 // Have the service record the tab. 291 service_->CreateHistoricalTab(web_contents(), -1); 292 293 // One entry should be created. 294 ASSERT_EQ(1U, service_->entries().size()); 295 296 // We have to explicitly mark the tab as pinned as there is no browser for 297 // these tests. 298 TabRestoreService::Entry* entry = service_->entries().front(); 299 ASSERT_EQ(TabRestoreService::TAB, entry->type); 300 Tab* tab = static_cast<Tab*>(entry); 301 tab->pinned = true; 302 const std::string extension_app_id("test"); 303 tab->extension_app_id = extension_app_id; 304 305 // Recreate the service and have it load the tabs. 306 RecreateService(); 307 308 // One entry should be created. 309 ASSERT_EQ(1U, service_->entries().size()); 310 311 // And verify the entry. 312 entry = service_->entries().front(); 313 ASSERT_EQ(TabRestoreService::TAB, entry->type); 314 tab = static_cast<Tab*>(entry); 315 EXPECT_TRUE(tab->pinned); 316 ASSERT_EQ(3U, tab->navigations.size()); 317 EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); 318 EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); 319 EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); 320 EXPECT_EQ(2, tab->current_navigation_index); 321 EXPECT_TRUE(extension_app_id == tab->extension_app_id); 322} 323 324// We only restore apps on chromeos. 325#if defined(USE_AURA) 326 327typedef InProcessBrowserTest PersistentTabRestoreServiceBrowserTest; 328 329IN_PROC_BROWSER_TEST_F(PersistentTabRestoreServiceBrowserTest, RestoreApp) { 330 Profile* profile = browser()->profile(); 331 TabRestoreService* trs = TabRestoreServiceFactory::GetForProfile(profile); 332 const char* app_name = "TestApp"; 333 334 Browser* app_browser = CreateBrowserForApp(app_name, profile); 335 app_browser->window()->Close(); 336 content::WindowedNotificationObserver observer( 337 chrome::NOTIFICATION_BROWSER_CLOSED, 338 content::Source<Browser>(app_browser)); 339 observer.Wait(); 340 341 // One entry should be created. 342 ASSERT_EQ(1U, trs->entries().size()); 343 const TabRestoreService::Entry* restored_entry = trs->entries().front(); 344 345 // It should be a window with an app. 346 ASSERT_EQ(TabRestoreService::WINDOW, restored_entry->type); 347 const Window* restored_window = 348 static_cast<const Window*>(restored_entry); 349 EXPECT_EQ(app_name, restored_window->app_name); 350} 351#endif // defined(USE_AURA) 352 353// Make sure we persist entries to disk that have post data. 354TEST_F(PersistentTabRestoreServiceTest, DontPersistPostData) { 355 AddThreeNavigations(); 356 controller().GetEntryAtIndex(0)->SetHasPostData(true); 357 controller().GetEntryAtIndex(1)->SetHasPostData(true); 358 controller().GetEntryAtIndex(2)->SetHasPostData(true); 359 360 // Have the service record the tab. 361 service_->CreateHistoricalTab(web_contents(), -1); 362 ASSERT_EQ(1U, service_->entries().size()); 363 364 // Recreate the service and have it load the tabs. 365 RecreateService(); 366 367 // One entry should be created. 368 ASSERT_EQ(1U, service_->entries().size()); 369 370 const TabRestoreService::Entry* restored_entry = service_->entries().front(); 371 ASSERT_EQ(TabRestoreService::TAB, restored_entry->type); 372 373 const Tab* restored_tab = 374 static_cast<const Tab*>(restored_entry); 375 // There should be 3 navs. 376 ASSERT_EQ(3U, restored_tab->navigations.size()); 377 EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), 378 restored_tab->timestamp.ToInternalValue()); 379} 380 381// Make sure we don't persist entries to disk that have post data. This 382// differs from DontPersistPostData1 in that all the navigations have post 383// data, so that nothing should be persisted. 384TEST_F(PersistentTabRestoreServiceTest, DontLoadTwice) { 385 AddThreeNavigations(); 386 387 // Have the service record the tab. 388 service_->CreateHistoricalTab(web_contents(), -1); 389 ASSERT_EQ(1U, service_->entries().size()); 390 391 // Recreate the service and have it load the tabs. 392 RecreateService(); 393 394 SynchronousLoadTabsFromLastSession(); 395 396 // There should only be one entry. 397 ASSERT_EQ(1U, service_->entries().size()); 398} 399 400// Makes sure we load the previous session as necessary. 401TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSession) { 402 CreateSessionServiceWithOneWindow(false); 403 404 SessionServiceFactory::GetForProfile(profile())-> 405 MoveCurrentSessionToLastSession(); 406 407 EXPECT_FALSE(service_->IsLoaded()); 408 409 TestTabRestoreServiceObserver observer; 410 service_->AddObserver(&observer); 411 SynchronousLoadTabsFromLastSession(); 412 EXPECT_TRUE(observer.got_loaded()); 413 service_->RemoveObserver(&observer); 414 415 // Make sure we get back one entry with one tab whose url is url1. 416 ASSERT_EQ(1U, service_->entries().size()); 417 TabRestoreService::Entry* entry2 = service_->entries().front(); 418 ASSERT_EQ(TabRestoreService::WINDOW, entry2->type); 419 TabRestoreService::Window* window = 420 static_cast<TabRestoreService::Window*>(entry2); 421 ASSERT_EQ(1U, window->tabs.size()); 422 EXPECT_EQ(0, window->timestamp.ToInternalValue()); 423 EXPECT_EQ(0, window->selected_tab_index); 424 ASSERT_EQ(1U, window->tabs[0].navigations.size()); 425 EXPECT_EQ(0, window->tabs[0].current_navigation_index); 426 EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); 427 EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); 428} 429 430// Makes sure we don't attempt to load previous sessions after a restore. 431TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterRestore) { 432 CreateSessionServiceWithOneWindow(false); 433 434 SessionServiceFactory::GetForProfile(profile())-> 435 MoveCurrentSessionToLastSession(); 436 437 profile()->set_restored_last_session(true); 438 439 SynchronousLoadTabsFromLastSession(); 440 441 // Because we restored a session PersistentTabRestoreService shouldn't load 442 // the tabs. 443 ASSERT_EQ(0U, service_->entries().size()); 444} 445 446// Makes sure we don't attempt to load previous sessions after a clean exit. 447TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterCleanExit) { 448 CreateSessionServiceWithOneWindow(false); 449 450 SessionServiceFactory::GetForProfile(profile())-> 451 MoveCurrentSessionToLastSession(); 452 453 profile()->set_last_session_exited_cleanly(true); 454 455 SynchronousLoadTabsFromLastSession(); 456 457 ASSERT_EQ(0U, service_->entries().size()); 458} 459 460TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabs) { 461 CreateSessionServiceWithOneWindow(false); 462 463 SessionServiceFactory::GetForProfile(profile())-> 464 MoveCurrentSessionToLastSession(); 465 466 AddThreeNavigations(); 467 468 service_->CreateHistoricalTab(web_contents(), -1); 469 470 RecreateService(); 471 472 // We should get back two entries, one from the previous session and one from 473 // the tab restore service. The previous session entry should be first. 474 ASSERT_EQ(2U, service_->entries().size()); 475 // The first entry should come from the session service. 476 TabRestoreService::Entry* entry = service_->entries().front(); 477 ASSERT_EQ(TabRestoreService::WINDOW, entry->type); 478 TabRestoreService::Window* window = 479 static_cast<TabRestoreService::Window*>(entry); 480 ASSERT_EQ(1U, window->tabs.size()); 481 EXPECT_EQ(0, window->selected_tab_index); 482 EXPECT_EQ(0, window->timestamp.ToInternalValue()); 483 ASSERT_EQ(1U, window->tabs[0].navigations.size()); 484 EXPECT_EQ(0, window->tabs[0].current_navigation_index); 485 EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); 486 EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); 487 488 // Then the closed tab. 489 entry = *(++service_->entries().begin()); 490 ASSERT_EQ(TabRestoreService::TAB, entry->type); 491 Tab* tab = static_cast<Tab*>(entry); 492 ASSERT_FALSE(tab->pinned); 493 ASSERT_EQ(3U, tab->navigations.size()); 494 EXPECT_EQ(2, tab->current_navigation_index); 495 EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(), 496 tab->timestamp.ToInternalValue()); 497 EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); 498 EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); 499 EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); 500} 501 502// Make sure pinned state is correctly loaded from session service. 503TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabsPinned) { 504 CreateSessionServiceWithOneWindow(true); 505 506 SessionServiceFactory::GetForProfile(profile())-> 507 MoveCurrentSessionToLastSession(); 508 509 AddThreeNavigations(); 510 511 service_->CreateHistoricalTab(web_contents(), -1); 512 513 RecreateService(); 514 515 // We should get back two entries, one from the previous session and one from 516 // the tab restore service. The previous session entry should be first. 517 ASSERT_EQ(2U, service_->entries().size()); 518 // The first entry should come from the session service. 519 TabRestoreService::Entry* entry = service_->entries().front(); 520 ASSERT_EQ(TabRestoreService::WINDOW, entry->type); 521 TabRestoreService::Window* window = 522 static_cast<TabRestoreService::Window*>(entry); 523 ASSERT_EQ(1U, window->tabs.size()); 524 EXPECT_EQ(0, window->selected_tab_index); 525 EXPECT_TRUE(window->tabs[0].pinned); 526 ASSERT_EQ(1U, window->tabs[0].navigations.size()); 527 EXPECT_EQ(0, window->tabs[0].current_navigation_index); 528 EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); 529 530 // Then the closed tab. 531 entry = *(++service_->entries().begin()); 532 ASSERT_EQ(TabRestoreService::TAB, entry->type); 533 Tab* tab = static_cast<Tab*>(entry); 534 ASSERT_FALSE(tab->pinned); 535 ASSERT_EQ(3U, tab->navigations.size()); 536 EXPECT_EQ(2, tab->current_navigation_index); 537 EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url()); 538 EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url()); 539 EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url()); 540} 541 542// Creates kMaxEntries + 1 windows in the session service and makes sure we only 543// get back kMaxEntries on restore. 544TEST_F(PersistentTabRestoreServiceTest, ManyWindowsInSessionService) { 545 CreateSessionServiceWithOneWindow(false); 546 547 for (size_t i = 0; i < kMaxEntries; ++i) 548 AddWindowWithOneTabToSessionService(false); 549 550 SessionServiceFactory::GetForProfile(profile())-> 551 MoveCurrentSessionToLastSession(); 552 553 AddThreeNavigations(); 554 555 service_->CreateHistoricalTab(web_contents(), -1); 556 557 RecreateService(); 558 559 // We should get back kMaxEntries entries. We added more, but 560 // TabRestoreService only allows up to kMaxEntries. 561 ASSERT_EQ(kMaxEntries, service_->entries().size()); 562 563 // The first entry should come from the session service. 564 TabRestoreService::Entry* entry = service_->entries().front(); 565 ASSERT_EQ(TabRestoreService::WINDOW, entry->type); 566 TabRestoreService::Window* window = 567 static_cast<TabRestoreService::Window*>(entry); 568 ASSERT_EQ(1U, window->tabs.size()); 569 EXPECT_EQ(0, window->selected_tab_index); 570 EXPECT_EQ(0, window->timestamp.ToInternalValue()); 571 ASSERT_EQ(1U, window->tabs[0].navigations.size()); 572 EXPECT_EQ(0, window->tabs[0].current_navigation_index); 573 EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue()); 574 EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url()); 575} 576 577// Makes sure we restore timestamps correctly. 578TEST_F(PersistentTabRestoreServiceTest, TimestampSurvivesRestore) { 579 base::Time tab_timestamp(base::Time::FromInternalValue(123456789)); 580 581 AddThreeNavigations(); 582 583 // Have the service record the tab. 584 service_->CreateHistoricalTab(web_contents(), -1); 585 586 // Make sure an entry was created. 587 ASSERT_EQ(1U, service_->entries().size()); 588 589 // Make sure the entry matches. 590 std::vector<SerializedNavigationEntry> old_navigations; 591 { 592 // |entry|/|tab| doesn't survive after RecreateService(). 593 TabRestoreService::Entry* entry = service_->entries().front(); 594 ASSERT_EQ(TabRestoreService::TAB, entry->type); 595 Tab* tab = static_cast<Tab*>(entry); 596 tab->timestamp = tab_timestamp; 597 old_navigations = tab->navigations; 598 } 599 600 EXPECT_EQ(3U, old_navigations.size()); 601 for (size_t i = 0; i < old_navigations.size(); ++i) { 602 EXPECT_FALSE(old_navigations[i].timestamp().is_null()); 603 } 604 605 // Set this, otherwise previous session won't be loaded. 606 profile()->set_last_session_exited_cleanly(false); 607 608 RecreateService(); 609 610 // One entry should be created. 611 ASSERT_EQ(1U, service_->entries().size()); 612 613 // And verify the entry. 614 TabRestoreService::Entry* restored_entry = service_->entries().front(); 615 ASSERT_EQ(TabRestoreService::TAB, restored_entry->type); 616 Tab* restored_tab = 617 static_cast<Tab*>(restored_entry); 618 EXPECT_EQ(tab_timestamp.ToInternalValue(), 619 restored_tab->timestamp.ToInternalValue()); 620 ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size()); 621 for (size_t i = 0; i < restored_tab->navigations.size(); ++i) { 622 EXPECT_EQ(old_navigations[i].timestamp(), 623 restored_tab->navigations[i].timestamp()); 624 } 625} 626 627TEST_F(PersistentTabRestoreServiceTest, PruneEntries) { 628 service_->ClearEntries(); 629 ASSERT_TRUE(service_->entries().empty()); 630 631 const size_t max_entries = kMaxEntries; 632 for (size_t i = 0; i < max_entries + 5; i++) { 633 SerializedNavigationEntry navigation = 634 SerializedNavigationEntryTestHelper::CreateNavigation( 635 base::StringPrintf("http://%d", static_cast<int>(i)), 636 base::StringPrintf("%d", static_cast<int>(i))); 637 638 Tab* tab = new Tab(); 639 tab->navigations.push_back(navigation); 640 tab->current_navigation_index = 0; 641 642 mutable_entries()->push_back(tab); 643 } 644 645 // Only keep kMaxEntries around. 646 EXPECT_EQ(max_entries + 5, service_->entries().size()); 647 PruneEntries(); 648 EXPECT_EQ(max_entries, service_->entries().size()); 649 // Pruning again does nothing. 650 PruneEntries(); 651 EXPECT_EQ(max_entries, service_->entries().size()); 652 653 // Prune older first. 654 const char kRecentUrl[] = "http://recent"; 655 SerializedNavigationEntry navigation = 656 SerializedNavigationEntryTestHelper::CreateNavigation(kRecentUrl, 657 "Most recent"); 658 Tab* tab = new Tab(); 659 tab->navigations.push_back(navigation); 660 tab->current_navigation_index = 0; 661 mutable_entries()->push_front(tab); 662 EXPECT_EQ(max_entries + 1, service_->entries().size()); 663 PruneEntries(); 664 EXPECT_EQ(max_entries, service_->entries().size()); 665 EXPECT_EQ(GURL(kRecentUrl), 666 static_cast<Tab*>(service_->entries().front())-> 667 navigations[0].virtual_url()); 668 669 // Ignore NTPs. 670 navigation = SerializedNavigationEntryTestHelper::CreateNavigation( 671 chrome::kChromeUINewTabURL, "New tab"); 672 673 tab = new Tab(); 674 tab->navigations.push_back(navigation); 675 tab->current_navigation_index = 0; 676 mutable_entries()->push_front(tab); 677 678 EXPECT_EQ(max_entries + 1, service_->entries().size()); 679 PruneEntries(); 680 EXPECT_EQ(max_entries, service_->entries().size()); 681 EXPECT_EQ(GURL(kRecentUrl), 682 static_cast<Tab*>(service_->entries().front())-> 683 navigations[0].virtual_url()); 684 685 // Don't prune pinned NTPs. 686 tab = new Tab(); 687 tab->pinned = true; 688 tab->current_navigation_index = 0; 689 tab->navigations.push_back(navigation); 690 mutable_entries()->push_front(tab); 691 EXPECT_EQ(max_entries + 1, service_->entries().size()); 692 PruneEntries(); 693 EXPECT_EQ(max_entries, service_->entries().size()); 694 EXPECT_EQ(GURL(chrome::kChromeUINewTabURL), 695 static_cast<Tab*>(service_->entries().front())-> 696 navigations[0].virtual_url()); 697 698 // Don't prune NTPs that have multiple navigations. 699 // (Erase the last NTP first.) 700 delete service_->entries().front(); 701 mutable_entries()->erase(mutable_entries()->begin()); 702 tab = new Tab(); 703 tab->current_navigation_index = 1; 704 tab->navigations.push_back(navigation); 705 tab->navigations.push_back(navigation); 706 mutable_entries()->push_front(tab); 707 EXPECT_EQ(max_entries, service_->entries().size()); 708 PruneEntries(); 709 EXPECT_EQ(max_entries, service_->entries().size()); 710 EXPECT_EQ(GURL(chrome::kChromeUINewTabURL), 711 static_cast<Tab*>(service_->entries().front())-> 712 navigations[1].virtual_url()); 713} 714 715// Regression test for crbug.com/106082 716TEST_F(PersistentTabRestoreServiceTest, PruneIsCalled) { 717 CreateSessionServiceWithOneWindow(false); 718 719 SessionServiceFactory::GetForProfile(profile())-> 720 MoveCurrentSessionToLastSession(); 721 722 profile()->set_restored_last_session(true); 723 724 const size_t max_entries = kMaxEntries; 725 for (size_t i = 0; i < max_entries + 5; i++) { 726 NavigateAndCommit( 727 GURL(base::StringPrintf("http://%d", static_cast<int>(i)))); 728 service_->CreateHistoricalTab(web_contents(), -1); 729 } 730 731 EXPECT_EQ(max_entries, service_->entries().size()); 732 // This should not crash. 733 SynchronousLoadTabsFromLastSession(); 734 EXPECT_EQ(max_entries, service_->entries().size()); 735} 736 737// Makes sure invoking LoadTabsFromLastSession() when the max number of entries 738// have been added results in IsLoaded() returning true and notifies observers. 739TEST_F(PersistentTabRestoreServiceTest, GoToLoadedWhenHaveMaxEntries) { 740 const size_t max_entries = kMaxEntries; 741 for (size_t i = 0; i < max_entries + 5; i++) { 742 NavigateAndCommit( 743 GURL(base::StringPrintf("http://%d", static_cast<int>(i)))); 744 service_->CreateHistoricalTab(web_contents(), -1); 745 } 746 747 EXPECT_FALSE(service_->IsLoaded()); 748 TestTabRestoreServiceObserver observer; 749 service_->AddObserver(&observer); 750 EXPECT_EQ(max_entries, service_->entries().size()); 751 SynchronousLoadTabsFromLastSession(); 752 EXPECT_TRUE(observer.got_loaded()); 753 EXPECT_TRUE(service_->IsLoaded()); 754 service_->RemoveObserver(&observer); 755} 756