| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <iterator> | 6 #include <iterator> |
| 7 | 7 |
| 8 #include "chrome/browser/sessions/tab_restore_service.h" | 8 #include "chrome/browser/sessions/tab_restore_service.h" |
| 9 | 9 |
| 10 #include "chrome/browser/browser_list.h" | 10 #include "chrome/browser/browser_list.h" |
| 11 #include "chrome/browser/navigation_controller.h" | 11 #include "chrome/browser/navigation_controller.h" |
| 12 #include "chrome/browser/navigation_entry.h" | 12 #include "chrome/browser/navigation_entry.h" |
| 13 #include "chrome/browser/profile.h" | 13 #include "chrome/browser/profile.h" |
| 14 #include "chrome/browser/sessions/session_backend.h" | 14 #include "chrome/browser/sessions/session_backend.h" |
| 15 #include "chrome/browser/sessions/session_service.h" |
| 15 #include "chrome/common/scoped_vector.h" | 16 #include "chrome/common/scoped_vector.h" |
| 16 #include "chrome/common/stl_util-inl.h" | 17 #include "chrome/common/stl_util-inl.h" |
| 17 | 18 |
| 18 using base::Time; | 19 using base::Time; |
| 19 | 20 |
| 20 // Entry ---------------------------------------------------------------------- | 21 // Entry ---------------------------------------------------------------------- |
| 21 | 22 |
| 22 // ID of the next Entry. | 23 // ID of the next Entry. |
| 23 static SessionID::id_type next_entry_id = 1; | 24 static SessionID::id_type next_entry_id = 1; |
| 24 | 25 |
| 25 TabRestoreService::Entry::Entry() : id(next_entry_id++), type(TAB) {} | 26 TabRestoreService::Entry::Entry() : id(next_entry_id++), type(TAB) {} |
| 26 | 27 |
| 27 TabRestoreService::Entry::Entry(Type type) : id(next_entry_id++), type(type) {} | 28 TabRestoreService::Entry::Entry(Type type) : id(next_entry_id++), type(type) {} |
| 28 | 29 |
| 29 // TabRestoreService ---------------------------------------------------------- | 30 // TabRestoreService ---------------------------------------------------------- |
| 30 | 31 |
| 31 // Max number of entries we'll keep around. | 32 // static |
| 32 static const size_t kMaxEntries = 10; | 33 const size_t TabRestoreService::kMaxEntries = 10; |
| 33 | 34 |
| 34 // Identifier for commands written to file. | 35 // Identifier for commands written to file. |
| 35 // The ordering in the file is as follows: | 36 // The ordering in the file is as follows: |
| 36 // . When the user closes a tab a command of type | 37 // . When the user closes a tab a command of type |
| 37 // kCommandSelectedNavigationInTab is written identifying the tab and | 38 // kCommandSelectedNavigationInTab is written identifying the tab and |
| 38 // the selected index. This is followed by any number of | 39 // the selected index. This is followed by any number of |
| 39 // kCommandUpdateTabNavigation commands (1 per navigation entry). | 40 // kCommandUpdateTabNavigation commands (1 per navigation entry). |
| 40 // . When the user closes a window a kCommandSelectedNavigationInTab command | 41 // . When the user closes a window a kCommandSelectedNavigationInTab command |
| 41 // is written out and followed by n tab closed sequences (as previoulsy | 42 // is written out and followed by n tab closed sequences (as previoulsy |
| 42 // described). | 43 // described). |
| (...skipping 14 matching lines...) Expand all Loading... |
| 57 | 58 |
| 58 typedef int32 RestoredEntryPayload; | 59 typedef int32 RestoredEntryPayload; |
| 59 | 60 |
| 60 // Payload used for the start of a window close. | 61 // Payload used for the start of a window close. |
| 61 struct WindowPayload { | 62 struct WindowPayload { |
| 62 SessionID::id_type window_id; | 63 SessionID::id_type window_id; |
| 63 int32 selected_tab_index; | 64 int32 selected_tab_index; |
| 64 int32 num_tabs; | 65 int32 num_tabs; |
| 65 }; | 66 }; |
| 66 | 67 |
| 67 // Paylowed used for the start of a tab close. | 68 // Payload used for the start of a tab close. |
| 68 struct SelectedNavigationInTabPayload { | 69 struct SelectedNavigationInTabPayload { |
| 69 SessionID::id_type id; | 70 SessionID::id_type id; |
| 70 int32 index; | 71 int32 index; |
| 71 }; | 72 }; |
| 72 | 73 |
| 73 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry; | 74 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry; |
| 74 | 75 |
| 75 // If |id_to_entry| contains an entry for |id| the corresponding entry is | 76 // If |id_to_entry| contains an entry for |id| the corresponding entry is |
| 76 // deleted and removed from both |id_to_entry| and |entries|. This is used | 77 // deleted and removed from both |id_to_entry| and |entries|. This is used |
| 77 // when creating entries from the backend file. | 78 // when creating entries from the backend file. |
| 78 void RemoveEntryByID(SessionID::id_type id, | 79 void RemoveEntryByID(SessionID::id_type id, |
| 79 IDToEntry* id_to_entry, | 80 IDToEntry* id_to_entry, |
| 80 std::vector<TabRestoreService::Entry*>* entries) { | 81 std::vector<TabRestoreService::Entry*>* entries) { |
| 81 IDToEntry::iterator i = id_to_entry->find(id); | 82 IDToEntry::iterator i = id_to_entry->find(id); |
| 82 if (i == id_to_entry->end()) | 83 if (i == id_to_entry->end()) |
| 83 return; | 84 return; |
| 84 | 85 |
| 85 entries->erase(std::find(entries->begin(), entries->end(), i->second)); | 86 entries->erase(std::find(entries->begin(), entries->end(), i->second)); |
| 86 delete i->second; | 87 delete i->second; |
| 87 id_to_entry->erase(i); | 88 id_to_entry->erase(i); |
| 88 } | 89 } |
| 89 | 90 |
| 90 } // namespace | 91 } // namespace |
| 91 | 92 |
| 92 TabRestoreService::TabRestoreService(Profile* profile) | 93 TabRestoreService::TabRestoreService(Profile* profile) |
| 93 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile, | 94 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile, |
| 94 std::wstring()), | 95 std::wstring()), |
| 95 loaded_last_session_(false), | 96 load_state_(NOT_LOADED), |
| 96 restoring_(false), | 97 restoring_(false), |
| 97 reached_max_(false), | 98 reached_max_(false), |
| 98 entries_to_write_(0), | 99 entries_to_write_(0), |
| 99 entries_written_(0) { | 100 entries_written_(0) { |
| 100 } | 101 } |
| 101 | 102 |
| 102 TabRestoreService::~TabRestoreService() { | 103 TabRestoreService::~TabRestoreService() { |
| 103 if (backend()) | 104 if (backend()) |
| 104 Save(); | 105 Save(); |
| 105 | 106 |
| 106 FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this)); | 107 FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this)); |
| 107 STLDeleteElements(&entries_); | 108 STLDeleteElements(&entries_); |
| 109 STLDeleteElements(&staging_entries_); |
| 108 } | 110 } |
| 109 | 111 |
| 110 void TabRestoreService::AddObserver(Observer* observer) { | 112 void TabRestoreService::AddObserver(Observer* observer) { |
| 111 observer_list_.AddObserver(observer); | 113 observer_list_.AddObserver(observer); |
| 112 } | 114 } |
| 113 | 115 |
| 114 void TabRestoreService::RemoveObserver(Observer* observer) { | 116 void TabRestoreService::RemoveObserver(Observer* observer) { |
| 115 observer_list_.RemoveObserver(observer); | 117 observer_list_.RemoveObserver(observer); |
| 116 } | 118 } |
| 117 | 119 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 } else { | 237 } else { |
| 236 NOTREACHED(); | 238 NOTREACHED(); |
| 237 } | 239 } |
| 238 } | 240 } |
| 239 delete entry; | 241 delete entry; |
| 240 restoring_ = false; | 242 restoring_ = false; |
| 241 NotifyTabsChanged(); | 243 NotifyTabsChanged(); |
| 242 } | 244 } |
| 243 | 245 |
| 244 void TabRestoreService::LoadTabsFromLastSession() { | 246 void TabRestoreService::LoadTabsFromLastSession() { |
| 245 if (loaded_last_session_ || reached_max_) | 247 if (load_state_ != NOT_LOADED || reached_max_) |
| 246 return; | 248 return; |
| 247 | 249 |
| 248 loaded_last_session_ = true; | 250 load_state_ = LOADING; |
| 249 | 251 |
| 252 if (!profile()->restored_last_session() && |
| 253 !profile()->DidLastSessionExitCleanly() && |
| 254 profile()->GetSessionService()) { |
| 255 // The previous session crashed and wasn't restored. Load the tabs/windows |
| 256 // that were open at the point of crash from the session service. |
| 257 profile()->GetSessionService()->GetLastSession( |
| 258 &load_consumer_, |
| 259 NewCallback(this, &TabRestoreService::OnGotPreviousSession)); |
| 260 } else { |
| 261 load_state_ |= LOADED_LAST_SESSION; |
| 262 } |
| 263 |
| 264 // Request the tabs closed in the last session. If the last session crashed, |
| 265 // this won't contain the tabs/window that were open at the point of the |
| 266 // crash (the call to GetLastSession above requests those). |
| 250 ScheduleGetLastSessionCommands( | 267 ScheduleGetLastSessionCommands( |
| 251 new InternalGetCommandsRequest( | 268 new InternalGetCommandsRequest( |
| 252 NewCallback(this, &TabRestoreService::OnGotLastSessionCommands)), | 269 NewCallback(this, &TabRestoreService::OnGotLastSessionCommands)), |
| 253 &load_tabs_consumer_); | 270 &load_consumer_); |
| 254 } | 271 } |
| 255 | 272 |
| 256 void TabRestoreService::Save() { | 273 void TabRestoreService::Save() { |
| 257 int to_write_count = std::min(entries_to_write_, | 274 int to_write_count = std::min(entries_to_write_, |
| 258 static_cast<int>(entries_.size())); | 275 static_cast<int>(entries_.size())); |
| 259 entries_to_write_ = 0; | 276 entries_to_write_ = 0; |
| 260 if (entries_written_ + to_write_count > kEntriesPerReset) { | 277 if (entries_written_ + to_write_count > kEntriesPerReset) { |
| 261 to_write_count = entries_.size(); | 278 to_write_count = entries_.size(); |
| 262 set_pending_reset(true); | 279 set_pending_reset(true); |
| 263 } | 280 } |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 !ShouldTrackEntry(navigations[selected_index])) { | 476 !ShouldTrackEntry(navigations[selected_index])) { |
| 460 selected_index++; | 477 selected_index++; |
| 461 } | 478 } |
| 462 | 479 |
| 463 return (selected_index == max_index) ? -1 : selected_index; | 480 return (selected_index == max_index) ? -1 : selected_index; |
| 464 } | 481 } |
| 465 | 482 |
| 466 void TabRestoreService::OnGotLastSessionCommands( | 483 void TabRestoreService::OnGotLastSessionCommands( |
| 467 Handle handle, | 484 Handle handle, |
| 468 scoped_refptr<InternalGetCommandsRequest> request) { | 485 scoped_refptr<InternalGetCommandsRequest> request) { |
| 486 std::vector<Entry*> entries; |
| 487 CreateEntriesFromCommands(request, &entries); |
| 488 // Closed tabs always go to the end. |
| 489 staging_entries_.insert(staging_entries_.end(), entries.begin(), |
| 490 entries.end()); |
| 491 load_state_ |= LOADED_LAST_TABS; |
| 492 LoadStateChanged(); |
| 493 } |
| 494 |
| 495 void TabRestoreService::CreateEntriesFromCommands( |
| 496 scoped_refptr<InternalGetCommandsRequest> request, |
| 497 std::vector<Entry*>* loaded_entries) { |
| 469 if (request->canceled() || entries_.size() == kMaxEntries) | 498 if (request->canceled() || entries_.size() == kMaxEntries) |
| 470 return; | 499 return; |
| 471 | 500 |
| 472 std::vector<SessionCommand*>& commands = request->commands; | 501 std::vector<SessionCommand*>& commands = request->commands; |
| 473 // Iterate through the commands populating entries and id_to_entry. | 502 // Iterate through the commands populating entries and id_to_entry. |
| 474 ScopedVector<Entry> entries; | 503 ScopedVector<Entry> entries; |
| 475 IDToEntry id_to_entry; | 504 IDToEntry id_to_entry; |
| 476 // If non-null we're processing the navigations of this tab. | 505 // If non-null we're processing the navigations of this tab. |
| 477 Tab* current_tab = NULL; | 506 Tab* current_tab = NULL; |
| 478 // If non-null we're processing the tabs of this window. | 507 // If non-null we're processing the tabs of this window. |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 568 default: | 597 default: |
| 569 // Unknown type, usually indicates corruption of file. Ignore it. | 598 // Unknown type, usually indicates corruption of file. Ignore it. |
| 570 return; | 599 return; |
| 571 } | 600 } |
| 572 } | 601 } |
| 573 | 602 |
| 574 // If there was corruption some of the entries won't be valid. Prune any | 603 // If there was corruption some of the entries won't be valid. Prune any |
| 575 // entries with no navigations. | 604 // entries with no navigations. |
| 576 ValidateAndDeleteEmptyEntries(&(entries.get())); | 605 ValidateAndDeleteEmptyEntries(&(entries.get())); |
| 577 | 606 |
| 578 if (entries->empty()) | 607 loaded_entries->swap(entries.get()); |
| 579 return; | |
| 580 | |
| 581 // And add them. | |
| 582 for (size_t i = 0; i < entries->size(); ++i) | |
| 583 AddEntry(entries[i], false, false); | |
| 584 | |
| 585 // AddEntry takes ownership of the entry, need to clear out entries so that | |
| 586 // it doesn't delete them. | |
| 587 entries->clear(); | |
| 588 | |
| 589 // Make it so we rewrite all the tabs. We need to do this otherwise we won't | |
| 590 // correctly write out the entries when Save is invoked (Save starts from | |
| 591 // the front, not the end and we just added the entries to the end). | |
| 592 entries_to_write_ = entries_.size(); | |
| 593 | |
| 594 PruneAndNotify(); | |
| 595 } | 608 } |
| 596 | 609 |
| 597 bool TabRestoreService::ValidateTab(Tab* tab) { | 610 bool TabRestoreService::ValidateTab(Tab* tab) { |
| 598 if (tab->navigations.empty()) | 611 if (tab->navigations.empty()) |
| 599 return false; | 612 return false; |
| 600 | 613 |
| 601 tab->current_navigation_index = | 614 tab->current_navigation_index = |
| 602 std::max(0, std::min(tab->current_navigation_index, | 615 std::max(0, std::min(tab->current_navigation_index, |
| 603 static_cast<int>(tab->navigations.size()) - 1)); | 616 static_cast<int>(tab->navigations.size()) - 1)); |
| 604 return true; | 617 return true; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 valid_entries.push_back(*i); | 653 valid_entries.push_back(*i); |
| 641 else | 654 else |
| 642 invalid_entries.push_back(*i); | 655 invalid_entries.push_back(*i); |
| 643 } | 656 } |
| 644 // NOTE: at this point the entries are ordered with newest at the front. | 657 // NOTE: at this point the entries are ordered with newest at the front. |
| 645 entries->swap(valid_entries); | 658 entries->swap(valid_entries); |
| 646 | 659 |
| 647 // Delete the remaining entries. | 660 // Delete the remaining entries. |
| 648 STLDeleteElements(&invalid_entries); | 661 STLDeleteElements(&invalid_entries); |
| 649 } | 662 } |
| 663 |
| 664 void TabRestoreService::OnGotPreviousSession( |
| 665 Handle handle, |
| 666 std::vector<SessionWindow*>* windows) { |
| 667 std::vector<Entry*> entries; |
| 668 CreateEntriesFromWindows(windows, &entries); |
| 669 // Previous session tabs go first. |
| 670 staging_entries_.insert(staging_entries_.begin(), entries.begin(), |
| 671 entries.end()); |
| 672 load_state_ |= LOADED_LAST_SESSION; |
| 673 LoadStateChanged(); |
| 674 } |
| 675 |
| 676 void TabRestoreService::CreateEntriesFromWindows( |
| 677 std::vector<SessionWindow*>* windows, |
| 678 std::vector<Entry*>* entries) { |
| 679 for (size_t i = 0; i < windows->size(); ++i) { |
| 680 scoped_ptr<Window> window(new Window()); |
| 681 if (ConvertSessionWindowToWindow((*windows)[i], window.get())) |
| 682 entries->push_back(window.release()); |
| 683 } |
| 684 } |
| 685 |
| 686 bool TabRestoreService::ConvertSessionWindowToWindow( |
| 687 SessionWindow* session_window, |
| 688 Window* window) { |
| 689 for (size_t i = 0; i < session_window->tabs.size(); ++i) { |
| 690 if (!session_window->tabs[i]->navigations.empty()) { |
| 691 window->tabs.resize(window->tabs.size() + 1); |
| 692 Tab& tab = window->tabs.back(); |
| 693 tab.navigations.swap(session_window->tabs[i]->navigations); |
| 694 tab.current_navigation_index = |
| 695 session_window->tabs[i]->current_navigation_index; |
| 696 } |
| 697 } |
| 698 if (window->tabs.empty()) |
| 699 return false; |
| 700 |
| 701 window->selected_tab_index = |
| 702 std::min(session_window->selected_tab_index, |
| 703 static_cast<int>(window->tabs.size() - 1)); |
| 704 return true; |
| 705 } |
| 706 |
| 707 void TabRestoreService::LoadStateChanged() { |
| 708 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) != |
| 709 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) { |
| 710 // Still waiting on previous session or previous tabs. |
| 711 return; |
| 712 } |
| 713 |
| 714 // We're done loading. |
| 715 load_state_ ^= LOADING; |
| 716 |
| 717 if (staging_entries_.empty() || reached_max_) { |
| 718 STLDeleteElements(&staging_entries_); |
| 719 return; |
| 720 } |
| 721 |
| 722 if (staging_entries_.size() + entries_.size() > kMaxEntries) { |
| 723 // If we add all the staged entries we'll end up with more than |
| 724 // kMaxEntries. Delete entries such that we only end up with |
| 725 // at most kMaxEntries. |
| 726 DCHECK(entries_.size() < kMaxEntries); |
| 727 STLDeleteContainerPointers( |
| 728 staging_entries_.begin() + (kMaxEntries - entries_.size()), |
| 729 staging_entries_.end()); |
| 730 staging_entries_.erase( |
| 731 staging_entries_.begin() + (kMaxEntries - entries_.size()), |
| 732 staging_entries_.end()); |
| 733 } |
| 734 |
| 735 // And add them. |
| 736 for (size_t i = 0; i < staging_entries_.size(); ++i) |
| 737 AddEntry(staging_entries_[i], false, false); |
| 738 |
| 739 // AddEntry takes ownership of the entry, need to clear out entries so that |
| 740 // it doesn't delete them. |
| 741 staging_entries_.clear(); |
| 742 |
| 743 // Make it so we rewrite all the tabs. We need to do this otherwise we won't |
| 744 // correctly write out the entries when Save is invoked (Save starts from |
| 745 // the front, not the end and we just added the entries to the end). |
| 746 entries_to_write_ = staging_entries_.size(); |
| 747 |
| 748 PruneAndNotify(); |
| 749 } |
| OLD | NEW |