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 |