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 <cstring> // memcpy 8 #include <vector> 9 10 #include "base/basictypes.h" 11 #include "base/bind.h" 12 #include "base/compiler_specific.h" 13 #include "base/files/file_path.h" 14 #include "base/logging.h" 15 #include "base/memory/ref_counted.h" 16 #include "base/memory/scoped_vector.h" 17 #include "base/stl_util.h" 18 #include "base/task/cancelable_task_tracker.h" 19 #include "base/time/time.h" 20 #include "chrome/browser/common/cancelable_request.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/sessions/base_session_service.h" 23 #include "chrome/browser/sessions/session_command.h" 24 #include "chrome/browser/sessions/session_service.h" 25 #include "chrome/browser/sessions/session_service_factory.h" 26 #include "chrome/browser/sessions/tab_restore_service_factory.h" 27 #include "content/public/browser/session_storage_namespace.h" 28 29 namespace { 30 31 // Only written if the tab is pinned. 32 typedef bool PinnedStatePayload; 33 34 typedef int32 RestoredEntryPayload; 35 36 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry; 37 38 // Payload used for the start of a tab close. This is the old struct that is 39 // used for backwards compat when it comes to reading the session files. 40 struct SelectedNavigationInTabPayload { 41 SessionID::id_type id; 42 int32 index; 43 }; 44 45 // Payload used for the start of a window close. This is the old struct that is 46 // used for backwards compat when it comes to reading the session files. This 47 // struct must be POD, because we memset the contents. 48 struct WindowPayload { 49 SessionID::id_type window_id; 50 int32 selected_tab_index; 51 int32 num_tabs; 52 }; 53 54 // Payload used for the start of a window close. This struct must be POD, 55 // because we memset the contents. 56 struct WindowPayload2 : WindowPayload { 57 int64 timestamp; 58 }; 59 60 // Payload used for the start of a tab close. 61 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload { 62 int64 timestamp; 63 }; 64 65 // Used to indicate what has loaded. 66 enum LoadState { 67 // Indicates we haven't loaded anything. 68 NOT_LOADED = 1 << 0, 69 70 // Indicates we've asked for the last sessions and tabs but haven't gotten the 71 // result back yet. 72 LOADING = 1 << 2, 73 74 // Indicates we finished loading the last tabs (but not necessarily the last 75 // session). 76 LOADED_LAST_TABS = 1 << 3, 77 78 // Indicates we finished loading the last session (but not necessarily the 79 // last tabs). 80 LOADED_LAST_SESSION = 1 << 4 81 }; 82 83 // Identifier for commands written to file. The ordering in the file is as 84 // follows: 85 // . When the user closes a tab a command of type 86 // kCommandSelectedNavigationInTab is written identifying the tab and 87 // the selected index, then a kCommandPinnedState command if the tab was 88 // pinned and kCommandSetExtensionAppID if the tab has an app id and 89 // the user agent override if it was using one. This is 90 // followed by any number of kCommandUpdateTabNavigation commands (1 per 91 // navigation entry). 92 // . When the user closes a window a kCommandSelectedNavigationInTab command 93 // is written out and followed by n tab closed sequences (as previoulsy 94 // described). 95 // . When the user restores an entry a command of type kCommandRestoredEntry 96 // is written. 97 const SessionCommand::id_type kCommandUpdateTabNavigation = 1; 98 const SessionCommand::id_type kCommandRestoredEntry = 2; 99 const SessionCommand::id_type kCommandWindow = 3; 100 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4; 101 const SessionCommand::id_type kCommandPinnedState = 5; 102 const SessionCommand::id_type kCommandSetExtensionAppID = 6; 103 const SessionCommand::id_type kCommandSetWindowAppName = 7; 104 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8; 105 106 // Number of entries (not commands) before we clobber the file and write 107 // everything. 108 const int kEntriesPerReset = 40; 109 110 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries; 111 112 } // namespace 113 114 // PersistentTabRestoreService::Delegate --------------------------------------- 115 116 // Implements the link between the tab restore service and the session backend. 117 class PersistentTabRestoreService::Delegate 118 : public BaseSessionService, 119 public TabRestoreServiceHelper::Observer { 120 public: 121 explicit Delegate(Profile* profile); 122 123 virtual ~Delegate(); 124 125 // BaseSessionService: 126 virtual void Save() OVERRIDE; 127 128 // TabRestoreServiceHelper::Observer: 129 virtual void OnClearEntries() OVERRIDE; 130 virtual void OnRestoreEntryById( 131 SessionID::id_type id, 132 Entries::const_iterator entry_iterator) OVERRIDE; 133 virtual void OnAddEntry() OVERRIDE; 134 135 void set_tab_restore_service_helper( 136 TabRestoreServiceHelper* tab_restore_service_helper) { 137 tab_restore_service_helper_ = tab_restore_service_helper; 138 } 139 140 void LoadTabsFromLastSession(); 141 142 bool IsLoaded() const; 143 144 // Creates and add entries to |entries| for each of the windows in |windows|. 145 static void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows, 146 std::vector<Entry*>* entries); 147 148 void Shutdown(); 149 150 // Schedules the commands for a window close. 151 void ScheduleCommandsForWindow(const Window& window); 152 153 // Schedules the commands for a tab close. |selected_index| gives the index of 154 // the selected navigation. 155 void ScheduleCommandsForTab(const Tab& tab, int selected_index); 156 157 // Creates a window close command. 158 static SessionCommand* CreateWindowCommand(SessionID::id_type id, 159 int selected_tab_index, 160 int num_tabs, 161 base::Time timestamp); 162 163 // Creates a tab close command. 164 static SessionCommand* CreateSelectedNavigationInTabCommand( 165 SessionID::id_type tab_id, 166 int32 index, 167 base::Time timestamp); 168 169 // Creates a restore command. 170 static SessionCommand* CreateRestoredEntryCommand( 171 SessionID::id_type entry_id); 172 173 // Returns the index to persist as the selected index. This is the same as 174 // |tab.current_navigation_index| unless the entry at 175 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no 176 // valid navigation to persist. 177 int GetSelectedNavigationIndexToPersist(const Tab& tab); 178 179 // Invoked when we've loaded the session commands that identify the previously 180 // closed tabs. This creates entries, adds them to staging_entries_, and 181 // invokes LoadState. 182 void OnGotLastSessionCommands(ScopedVector<SessionCommand> commands); 183 184 // Populates |loaded_entries| with Entries from |commands|. 185 void CreateEntriesFromCommands(const std::vector<SessionCommand*>& commands, 186 std::vector<Entry*>* loaded_entries); 187 188 // Validates all entries in |entries|, deleting any with no navigations. This 189 // also deletes any entries beyond the max number of entries we can hold. 190 static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries); 191 192 // Callback from SessionService when we've received the windows from the 193 // previous session. This creates and add entries to |staging_entries_| and 194 // invokes LoadStateChanged. |ignored_active_window| is ignored because we 195 // don't need to restore activation. 196 void OnGotPreviousSession(ScopedVector<SessionWindow> windows, 197 SessionID::id_type ignored_active_window); 198 199 // Converts a SessionWindow into a Window, returning true on success. We use 0 200 // as the timestamp here since we do not know when the window/tab was closed. 201 static bool ConvertSessionWindowToWindow(SessionWindow* session_window, 202 Window* window); 203 204 // Invoked when previous tabs or session is loaded. If both have finished 205 // loading the entries in |staging_entries_| are added to entries and 206 // observers are notified. 207 void LoadStateChanged(); 208 209 // If |id_to_entry| contains an entry for |id| the corresponding entry is 210 // deleted and removed from both |id_to_entry| and |entries|. This is used 211 // when creating entries from the backend file. 212 void RemoveEntryByID(SessionID::id_type id, 213 IDToEntry* id_to_entry, 214 std::vector<TabRestoreService::Entry*>* entries); 215 216 private: 217 TabRestoreServiceHelper* tab_restore_service_helper_; 218 219 // The number of entries to write. 220 int entries_to_write_; 221 222 // Number of entries we've written. 223 int entries_written_; 224 225 // Whether we've loaded the last session. 226 int load_state_; 227 228 // Results from previously closed tabs/sessions is first added here. When the 229 // results from both us and the session restore service have finished loading 230 // LoadStateChanged is invoked, which adds these entries to entries_. 231 ScopedVector<Entry> staging_entries_; 232 233 // Used when loading previous tabs/session and open tabs/session. 234 base::CancelableTaskTracker cancelable_task_tracker_; 235 236 DISALLOW_COPY_AND_ASSIGN(Delegate); 237 }; 238 239 PersistentTabRestoreService::Delegate::Delegate(Profile* profile) 240 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile, 241 base::FilePath()), 242 tab_restore_service_helper_(NULL), 243 entries_to_write_(0), 244 entries_written_(0), 245 load_state_(NOT_LOADED) { 246 } 247 248 PersistentTabRestoreService::Delegate::~Delegate() {} 249 250 void PersistentTabRestoreService::Delegate::Save() { 251 const Entries& entries = tab_restore_service_helper_->entries(); 252 int to_write_count = std::min(entries_to_write_, 253 static_cast<int>(entries.size())); 254 entries_to_write_ = 0; 255 if (entries_written_ + to_write_count > kEntriesPerReset) { 256 to_write_count = entries.size(); 257 set_pending_reset(true); 258 } 259 if (to_write_count) { 260 // Write the to_write_count most recently added entries out. The most 261 // recently added entry is at the front, so we use a reverse iterator to 262 // write in the order the entries were added. 263 Entries::const_reverse_iterator i = entries.rbegin(); 264 DCHECK(static_cast<size_t>(to_write_count) <= entries.size()); 265 std::advance(i, entries.size() - static_cast<int>(to_write_count)); 266 for (; i != entries.rend(); ++i) { 267 Entry* entry = *i; 268 if (entry->type == TAB) { 269 Tab* tab = static_cast<Tab*>(entry); 270 int selected_index = GetSelectedNavigationIndexToPersist(*tab); 271 if (selected_index != -1) 272 ScheduleCommandsForTab(*tab, selected_index); 273 } else { 274 ScheduleCommandsForWindow(*static_cast<Window*>(entry)); 275 } 276 entries_written_++; 277 } 278 } 279 if (pending_reset()) 280 entries_written_ = 0; 281 BaseSessionService::Save(); 282 } 283 284 void PersistentTabRestoreService::Delegate::OnClearEntries() { 285 // Mark all the tabs as closed so that we don't attempt to restore them. 286 const Entries& entries = tab_restore_service_helper_->entries(); 287 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i) 288 ScheduleCommand(CreateRestoredEntryCommand((*i)->id)); 289 290 entries_to_write_ = 0; 291 292 // Schedule a pending reset so that we nuke the file on next write. 293 set_pending_reset(true); 294 295 // Schedule a command, otherwise if there are no pending commands Save does 296 // nothing. 297 ScheduleCommand(CreateRestoredEntryCommand(1)); 298 } 299 300 void PersistentTabRestoreService::Delegate::OnRestoreEntryById( 301 SessionID::id_type id, 302 Entries::const_iterator entry_iterator) { 303 size_t index = 0; 304 const Entries& entries = tab_restore_service_helper_->entries(); 305 for (Entries::const_iterator j = entries.begin(); 306 j != entry_iterator && j != entries.end(); 307 ++j, ++index) {} 308 if (static_cast<int>(index) < entries_to_write_) 309 entries_to_write_--; 310 311 ScheduleCommand(CreateRestoredEntryCommand(id)); 312 } 313 314 void PersistentTabRestoreService::Delegate::OnAddEntry() { 315 // Start the save timer, when it fires we'll generate the commands. 316 StartSaveTimer(); 317 entries_to_write_++; 318 } 319 320 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() { 321 if (load_state_ != NOT_LOADED) 322 return; 323 324 if (tab_restore_service_helper_->entries().size() == kMaxEntries) { 325 // We already have the max number of entries we can take. There is no point 326 // in attempting to load since we'll just drop the results. Skip to loaded. 327 load_state_ = (LOADING | LOADED_LAST_SESSION | LOADED_LAST_TABS); 328 LoadStateChanged(); 329 return; 330 } 331 332 #if !defined(ENABLE_SESSION_SERVICE) 333 // If sessions are not stored in the SessionService, default to 334 // |LOADED_LAST_SESSION| state. 335 load_state_ = LOADING | LOADED_LAST_SESSION; 336 #else 337 load_state_ = LOADING; 338 339 SessionService* session_service = 340 SessionServiceFactory::GetForProfile(profile()); 341 Profile::ExitType exit_type = profile()->GetLastSessionExitType(); 342 if (!profile()->restored_last_session() && session_service && 343 (exit_type == Profile::EXIT_CRASHED || 344 exit_type == Profile::EXIT_SESSION_ENDED)) { 345 // The previous session crashed and wasn't restored, or was a forced 346 // shutdown. Both of which won't have notified us of the browser close so 347 // that we need to load the windows from session service (which will have 348 // saved them). 349 session_service->GetLastSession( 350 base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)), 351 &cancelable_task_tracker_); 352 } else { 353 load_state_ |= LOADED_LAST_SESSION; 354 } 355 #endif 356 357 // Request the tabs closed in the last session. If the last session crashed, 358 // this won't contain the tabs/window that were open at the point of the 359 // crash (the call to GetLastSession above requests those). 360 ScheduleGetLastSessionCommands( 361 base::Bind(&Delegate::OnGotLastSessionCommands, base::Unretained(this)), 362 &cancelable_task_tracker_); 363 } 364 365 bool PersistentTabRestoreService::Delegate::IsLoaded() const { 366 return !(load_state_ & (NOT_LOADED | LOADING)); 367 } 368 369 // static 370 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows( 371 std::vector<SessionWindow*>* windows, 372 std::vector<Entry*>* entries) { 373 for (size_t i = 0; i < windows->size(); ++i) { 374 scoped_ptr<Window> window(new Window()); 375 if (ConvertSessionWindowToWindow((*windows)[i], window.get())) 376 entries->push_back(window.release()); 377 } 378 } 379 380 void PersistentTabRestoreService::Delegate::Shutdown() { 381 if (backend()) 382 Save(); 383 } 384 385 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow( 386 const Window& window) { 387 DCHECK(!window.tabs.empty()); 388 int selected_tab = window.selected_tab_index; 389 int valid_tab_count = 0; 390 int real_selected_tab = selected_tab; 391 for (size_t i = 0; i < window.tabs.size(); ++i) { 392 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) { 393 valid_tab_count++; 394 } else if (static_cast<int>(i) < selected_tab) { 395 real_selected_tab--; 396 } 397 } 398 if (valid_tab_count == 0) 399 return; // No tabs to persist. 400 401 ScheduleCommand( 402 CreateWindowCommand(window.id, 403 std::min(real_selected_tab, valid_tab_count - 1), 404 valid_tab_count, 405 window.timestamp)); 406 407 if (!window.app_name.empty()) { 408 ScheduleCommand( 409 CreateSetWindowAppNameCommand(kCommandSetWindowAppName, 410 window.id, 411 window.app_name)); 412 } 413 414 for (size_t i = 0; i < window.tabs.size(); ++i) { 415 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]); 416 if (selected_index != -1) 417 ScheduleCommandsForTab(window.tabs[i], selected_index); 418 } 419 } 420 421 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab( 422 const Tab& tab, 423 int selected_index) { 424 const std::vector<sessions::SerializedNavigationEntry>& navigations = 425 tab.navigations; 426 int max_index = static_cast<int>(navigations.size()); 427 428 // Determine the first navigation we'll persist. 429 int valid_count_before_selected = 0; 430 int first_index_to_persist = selected_index; 431 for (int i = selected_index - 1; i >= 0 && 432 valid_count_before_selected < max_persist_navigation_count; --i) { 433 if (ShouldTrackEntry(navigations[i].virtual_url())) { 434 first_index_to_persist = i; 435 valid_count_before_selected++; 436 } 437 } 438 439 // Write the command that identifies the selected tab. 440 ScheduleCommand( 441 CreateSelectedNavigationInTabCommand(tab.id, 442 valid_count_before_selected, 443 tab.timestamp)); 444 445 if (tab.pinned) { 446 PinnedStatePayload payload = true; 447 SessionCommand* command = 448 new SessionCommand(kCommandPinnedState, sizeof(payload)); 449 memcpy(command->contents(), &payload, sizeof(payload)); 450 ScheduleCommand(command); 451 } 452 453 if (!tab.extension_app_id.empty()) { 454 ScheduleCommand( 455 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id, 456 tab.extension_app_id)); 457 } 458 459 if (!tab.user_agent_override.empty()) { 460 ScheduleCommand( 461 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride, 462 tab.id, tab.user_agent_override)); 463 } 464 465 // Then write the navigations. 466 for (int i = first_index_to_persist, wrote_count = 0; 467 i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) { 468 if (ShouldTrackEntry(navigations[i].virtual_url())) { 469 ScheduleCommand( 470 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id, 471 navigations[i])); 472 } 473 } 474 } 475 476 // static 477 SessionCommand* PersistentTabRestoreService::Delegate::CreateWindowCommand( 478 SessionID::id_type id, 479 int selected_tab_index, 480 int num_tabs, 481 base::Time timestamp) { 482 WindowPayload2 payload; 483 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of 484 // uninitialized memory in the struct. 485 memset(&payload, 0, sizeof(payload)); 486 payload.window_id = id; 487 payload.selected_tab_index = selected_tab_index; 488 payload.num_tabs = num_tabs; 489 payload.timestamp = timestamp.ToInternalValue(); 490 491 SessionCommand* command = 492 new SessionCommand(kCommandWindow, sizeof(payload)); 493 memcpy(command->contents(), &payload, sizeof(payload)); 494 return command; 495 } 496 497 // static 498 SessionCommand* 499 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand( 500 SessionID::id_type tab_id, 501 int32 index, 502 base::Time timestamp) { 503 SelectedNavigationInTabPayload2 payload; 504 payload.id = tab_id; 505 payload.index = index; 506 payload.timestamp = timestamp.ToInternalValue(); 507 SessionCommand* command = 508 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)); 509 memcpy(command->contents(), &payload, sizeof(payload)); 510 return command; 511 } 512 513 // static 514 SessionCommand* 515 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand( 516 SessionID::id_type entry_id) { 517 RestoredEntryPayload payload = entry_id; 518 SessionCommand* command = 519 new SessionCommand(kCommandRestoredEntry, sizeof(payload)); 520 memcpy(command->contents(), &payload, sizeof(payload)); 521 return command; 522 } 523 524 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist( 525 const Tab& tab) { 526 const std::vector<sessions::SerializedNavigationEntry>& navigations = 527 tab.navigations; 528 int selected_index = tab.current_navigation_index; 529 int max_index = static_cast<int>(navigations.size()); 530 531 // Find the first navigation to persist. We won't persist the selected 532 // navigation if ShouldTrackEntry returns false. 533 while (selected_index >= 0 && 534 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { 535 selected_index--; 536 } 537 538 if (selected_index != -1) 539 return selected_index; 540 541 // Couldn't find a navigation to persist going back, go forward. 542 selected_index = tab.current_navigation_index + 1; 543 while (selected_index < max_index && 544 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { 545 selected_index++; 546 } 547 548 return (selected_index == max_index) ? -1 : selected_index; 549 } 550 551 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands( 552 ScopedVector<SessionCommand> commands) { 553 std::vector<Entry*> entries; 554 CreateEntriesFromCommands(commands.get(), &entries); 555 // Closed tabs always go to the end. 556 staging_entries_.insert(staging_entries_.end(), entries.begin(), 557 entries.end()); 558 load_state_ |= LOADED_LAST_TABS; 559 LoadStateChanged(); 560 } 561 562 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands( 563 const std::vector<SessionCommand*>& commands, 564 std::vector<Entry*>* loaded_entries) { 565 if (tab_restore_service_helper_->entries().size() == kMaxEntries) 566 return; 567 568 // Iterate through the commands populating entries and id_to_entry. 569 ScopedVector<Entry> entries; 570 IDToEntry id_to_entry; 571 // If non-null we're processing the navigations of this tab. 572 Tab* current_tab = NULL; 573 // If non-null we're processing the tabs of this window. 574 Window* current_window = NULL; 575 // If > 0, we've gotten a window command but not all the tabs yet. 576 int pending_window_tabs = 0; 577 for (std::vector<SessionCommand*>::const_iterator i = commands.begin(); 578 i != commands.end(); ++i) { 579 const SessionCommand& command = *(*i); 580 switch (command.id()) { 581 case kCommandRestoredEntry: { 582 if (pending_window_tabs > 0) { 583 // Should never receive a restored command while waiting for all the 584 // tabs in a window. 585 return; 586 } 587 588 current_tab = NULL; 589 current_window = NULL; 590 591 RestoredEntryPayload payload; 592 if (!command.GetPayload(&payload, sizeof(payload))) 593 return; 594 RemoveEntryByID(payload, &id_to_entry, &(entries.get())); 595 break; 596 } 597 598 case kCommandWindow: { 599 WindowPayload2 payload; 600 if (pending_window_tabs > 0) { 601 // Should never receive a window command while waiting for all the 602 // tabs in a window. 603 return; 604 } 605 606 // Try the new payload first 607 if (!command.GetPayload(&payload, sizeof(payload))) { 608 // then the old payload 609 WindowPayload old_payload; 610 if (!command.GetPayload(&old_payload, sizeof(old_payload))) 611 return; 612 613 // Copy the old payload data to the new payload. 614 payload.window_id = old_payload.window_id; 615 payload.selected_tab_index = old_payload.selected_tab_index; 616 payload.num_tabs = old_payload.num_tabs; 617 // Since we don't have a time use time 0 which is used to mark as an 618 // unknown timestamp. 619 payload.timestamp = 0; 620 } 621 622 pending_window_tabs = payload.num_tabs; 623 if (pending_window_tabs <= 0) { 624 // Should always have at least 1 tab. Likely indicates corruption. 625 return; 626 } 627 628 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get())); 629 630 current_window = new Window(); 631 current_window->selected_tab_index = payload.selected_tab_index; 632 current_window->timestamp = 633 base::Time::FromInternalValue(payload.timestamp); 634 entries.push_back(current_window); 635 id_to_entry[payload.window_id] = current_window; 636 break; 637 } 638 639 case kCommandSelectedNavigationInTab: { 640 SelectedNavigationInTabPayload2 payload; 641 if (!command.GetPayload(&payload, sizeof(payload))) { 642 SelectedNavigationInTabPayload old_payload; 643 if (!command.GetPayload(&old_payload, sizeof(old_payload))) 644 return; 645 payload.id = old_payload.id; 646 payload.index = old_payload.index; 647 // Since we don't have a time use time 0 which is used to mark as an 648 // unknown timestamp. 649 payload.timestamp = 0; 650 } 651 652 if (pending_window_tabs > 0) { 653 if (!current_window) { 654 // We should have created a window already. 655 NOTREACHED(); 656 return; 657 } 658 current_window->tabs.resize(current_window->tabs.size() + 1); 659 current_tab = &(current_window->tabs.back()); 660 if (--pending_window_tabs == 0) 661 current_window = NULL; 662 } else { 663 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get())); 664 current_tab = new Tab(); 665 id_to_entry[payload.id] = current_tab; 666 current_tab->timestamp = 667 base::Time::FromInternalValue(payload.timestamp); 668 entries.push_back(current_tab); 669 } 670 current_tab->current_navigation_index = payload.index; 671 break; 672 } 673 674 case kCommandUpdateTabNavigation: { 675 if (!current_tab) { 676 // Should be in a tab when we get this. 677 return; 678 } 679 current_tab->navigations.resize(current_tab->navigations.size() + 1); 680 SessionID::id_type tab_id; 681 if (!RestoreUpdateTabNavigationCommand( 682 command, ¤t_tab->navigations.back(), &tab_id)) { 683 return; 684 } 685 break; 686 } 687 688 case kCommandPinnedState: { 689 if (!current_tab) { 690 // Should be in a tab when we get this. 691 return; 692 } 693 // NOTE: payload doesn't matter. kCommandPinnedState is only written if 694 // tab is pinned. 695 current_tab->pinned = true; 696 break; 697 } 698 699 case kCommandSetWindowAppName: { 700 if (!current_window) { 701 // We should have created a window already. 702 NOTREACHED(); 703 return; 704 } 705 706 SessionID::id_type window_id; 707 std::string app_name; 708 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name)) 709 return; 710 711 current_window->app_name.swap(app_name); 712 break; 713 } 714 715 case kCommandSetExtensionAppID: { 716 if (!current_tab) { 717 // Should be in a tab when we get this. 718 return; 719 } 720 SessionID::id_type tab_id; 721 std::string extension_app_id; 722 if (!RestoreSetTabExtensionAppIDCommand(command, &tab_id, 723 &extension_app_id)) { 724 return; 725 } 726 current_tab->extension_app_id.swap(extension_app_id); 727 break; 728 } 729 730 case kCommandSetTabUserAgentOverride: { 731 if (!current_tab) { 732 // Should be in a tab when we get this. 733 return; 734 } 735 SessionID::id_type tab_id; 736 std::string user_agent_override; 737 if (!RestoreSetTabUserAgentOverrideCommand(command, &tab_id, 738 &user_agent_override)) { 739 return; 740 } 741 current_tab->user_agent_override.swap(user_agent_override); 742 break; 743 } 744 745 default: 746 // Unknown type, usually indicates corruption of file. Ignore it. 747 return; 748 } 749 } 750 751 // If there was corruption some of the entries won't be valid. 752 ValidateAndDeleteEmptyEntries(&(entries.get())); 753 754 loaded_entries->swap(entries.get()); 755 } 756 757 // static 758 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries( 759 std::vector<Entry*>* entries) { 760 std::vector<Entry*> valid_entries; 761 std::vector<Entry*> invalid_entries; 762 763 // Iterate from the back so that we keep the most recently closed entries. 764 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin(); 765 i != entries->rend(); ++i) { 766 if (TabRestoreServiceHelper::ValidateEntry(*i)) 767 valid_entries.push_back(*i); 768 else 769 invalid_entries.push_back(*i); 770 } 771 // NOTE: at this point the entries are ordered with newest at the front. 772 entries->swap(valid_entries); 773 774 // Delete the remaining entries. 775 STLDeleteElements(&invalid_entries); 776 } 777 778 void PersistentTabRestoreService::Delegate::OnGotPreviousSession( 779 ScopedVector<SessionWindow> windows, 780 SessionID::id_type ignored_active_window) { 781 std::vector<Entry*> entries; 782 CreateEntriesFromWindows(&windows.get(), &entries); 783 // Previous session tabs go first. 784 staging_entries_.insert(staging_entries_.begin(), entries.begin(), 785 entries.end()); 786 load_state_ |= LOADED_LAST_SESSION; 787 LoadStateChanged(); 788 } 789 790 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow( 791 SessionWindow* session_window, 792 Window* window) { 793 for (size_t i = 0; i < session_window->tabs.size(); ++i) { 794 if (!session_window->tabs[i]->navigations.empty()) { 795 window->tabs.resize(window->tabs.size() + 1); 796 Tab& tab = window->tabs.back(); 797 tab.pinned = session_window->tabs[i]->pinned; 798 tab.navigations.swap(session_window->tabs[i]->navigations); 799 tab.current_navigation_index = 800 session_window->tabs[i]->current_navigation_index; 801 tab.extension_app_id = session_window->tabs[i]->extension_app_id; 802 tab.timestamp = base::Time(); 803 } 804 } 805 if (window->tabs.empty()) 806 return false; 807 808 window->selected_tab_index = 809 std::min(session_window->selected_tab_index, 810 static_cast<int>(window->tabs.size() - 1)); 811 window->timestamp = base::Time(); 812 return true; 813 } 814 815 void PersistentTabRestoreService::Delegate::LoadStateChanged() { 816 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) != 817 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) { 818 // Still waiting on previous session or previous tabs. 819 return; 820 } 821 822 // We're done loading. 823 load_state_ ^= LOADING; 824 825 const Entries& entries = tab_restore_service_helper_->entries(); 826 if (staging_entries_.empty() || entries.size() >= kMaxEntries) { 827 staging_entries_.clear(); 828 tab_restore_service_helper_->NotifyLoaded(); 829 return; 830 } 831 832 if (staging_entries_.size() + entries.size() > kMaxEntries) { 833 // If we add all the staged entries we'll end up with more than 834 // kMaxEntries. Delete entries such that we only end up with at most 835 // kMaxEntries. 836 int surplus = kMaxEntries - entries.size(); 837 CHECK_LE(0, surplus); 838 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus); 839 staging_entries_.erase( 840 staging_entries_.begin() + (kMaxEntries - entries.size()), 841 staging_entries_.end()); 842 } 843 844 // And add them. 845 for (size_t i = 0; i < staging_entries_.size(); ++i) { 846 staging_entries_[i]->from_last_session = true; 847 tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false); 848 } 849 850 // AddEntry takes ownership of the entry, need to clear out entries so that 851 // it doesn't delete them. 852 staging_entries_.weak_clear(); 853 854 // Make it so we rewrite all the tabs. We need to do this otherwise we won't 855 // correctly write out the entries when Save is invoked (Save starts from 856 // the front, not the end and we just added the entries to the end). 857 entries_to_write_ = staging_entries_.size(); 858 859 tab_restore_service_helper_->PruneEntries(); 860 tab_restore_service_helper_->NotifyTabsChanged(); 861 862 tab_restore_service_helper_->NotifyLoaded(); 863 } 864 865 void PersistentTabRestoreService::Delegate::RemoveEntryByID( 866 SessionID::id_type id, 867 IDToEntry* id_to_entry, 868 std::vector<TabRestoreService::Entry*>* entries) { 869 // Look for the entry in the map. If it is present, erase it from both 870 // collections and return. 871 IDToEntry::iterator i = id_to_entry->find(id); 872 if (i != id_to_entry->end()) { 873 entries->erase(std::find(entries->begin(), entries->end(), i->second)); 874 delete i->second; 875 id_to_entry->erase(i); 876 return; 877 } 878 879 // Otherwise, loop over all items in the map and see if any of the Windows 880 // have Tabs with the |id|. 881 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end(); 882 ++i) { 883 if (i->second->type == TabRestoreService::WINDOW) { 884 TabRestoreService::Window* window = 885 static_cast<TabRestoreService::Window*>(i->second); 886 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin(); 887 for ( ; j != window->tabs.end(); ++j) { 888 // If the ID matches one of this window's tabs, remove it from the 889 // list. 890 if ((*j).id == id) { 891 window->tabs.erase(j); 892 return; 893 } 894 } 895 } 896 } 897 } 898 899 // PersistentTabRestoreService ------------------------------------------------- 900 901 PersistentTabRestoreService::PersistentTabRestoreService( 902 Profile* profile, 903 TimeFactory* time_factory) 904 : delegate_(new Delegate(profile)), 905 helper_(this, delegate_.get(), profile, time_factory) { 906 delegate_->set_tab_restore_service_helper(&helper_); 907 } 908 909 PersistentTabRestoreService::~PersistentTabRestoreService() {} 910 911 void PersistentTabRestoreService::AddObserver( 912 TabRestoreServiceObserver* observer) { 913 helper_.AddObserver(observer); 914 } 915 916 void PersistentTabRestoreService::RemoveObserver( 917 TabRestoreServiceObserver* observer) { 918 helper_.RemoveObserver(observer); 919 } 920 921 void PersistentTabRestoreService::CreateHistoricalTab( 922 content::WebContents* contents, 923 int index) { 924 helper_.CreateHistoricalTab(contents, index); 925 } 926 927 void PersistentTabRestoreService::BrowserClosing( 928 TabRestoreServiceDelegate* delegate) { 929 helper_.BrowserClosing(delegate); 930 } 931 932 void PersistentTabRestoreService::BrowserClosed( 933 TabRestoreServiceDelegate* delegate) { 934 helper_.BrowserClosed(delegate); 935 } 936 937 void PersistentTabRestoreService::ClearEntries() { 938 helper_.ClearEntries(); 939 } 940 941 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const { 942 return helper_.entries(); 943 } 944 945 std::vector<content::WebContents*> 946 PersistentTabRestoreService::RestoreMostRecentEntry( 947 TabRestoreServiceDelegate* delegate, 948 chrome::HostDesktopType host_desktop_type) { 949 return helper_.RestoreMostRecentEntry(delegate, host_desktop_type); 950 } 951 952 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById( 953 SessionID::id_type id) { 954 return helper_.RemoveTabEntryById(id); 955 } 956 957 std::vector<content::WebContents*> 958 PersistentTabRestoreService::RestoreEntryById( 959 TabRestoreServiceDelegate* delegate, 960 SessionID::id_type id, 961 chrome::HostDesktopType host_desktop_type, 962 WindowOpenDisposition disposition) { 963 return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition); 964 } 965 966 bool PersistentTabRestoreService::IsLoaded() const { 967 return delegate_->IsLoaded(); 968 } 969 970 void PersistentTabRestoreService::DeleteLastSession() { 971 return delegate_->DeleteLastSession(); 972 } 973 974 void PersistentTabRestoreService::Shutdown() { 975 return delegate_->Shutdown(); 976 } 977 978 void PersistentTabRestoreService::LoadTabsFromLastSession() { 979 delegate_->LoadTabsFromLastSession(); 980 } 981 982 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() { 983 return &helper_.entries_; 984 } 985 986 void PersistentTabRestoreService::PruneEntries() { 987 helper_.PruneEntries(); 988 } 989 990 KeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor( 991 content::BrowserContext* profile) const { 992 return new PersistentTabRestoreService(static_cast<Profile*>(profile), NULL); 993 } 994