Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(450)

Side by Side Diff: chrome/browser/sessions/persistent_tab_restore_service.cc

Issue 10989027: Split TabRestoreService into InMemoryTRS and PersistentTRS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Scott's comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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 "chrome/browser/sessions/tab_restore_service.h" 5 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
6 6
7 #include <algorithm> 7 #include <cstring> // memcpy
8 #include <iterator> 8 #include <vector>
9 #include <map>
10 9
10 #include "base/basictypes.h"
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/bind_helpers.h" 12 #include "base/compiler_specific.h"
13 #include "base/callback.h" 13 #include "base/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_vector.h" 16 #include "base/memory/scoped_vector.h"
15 #include "base/metrics/histogram.h"
16 #include "base/stl_util.h" 17 #include "base/stl_util.h"
17 #include "chrome/browser/extensions/extension_service.h" 18 #include "base/time.h"
18 #include "chrome/browser/extensions/tab_helper.h" 19 #include "chrome/browser/common/cancelable_request.h"
19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/base_session_service.h"
20 #include "chrome/browser/sessions/session_command.h" 22 #include "chrome/browser/sessions/session_command.h"
21 #include "chrome/browser/sessions/session_service.h" 23 #include "chrome/browser/sessions/session_service.h"
22 #include "chrome/browser/sessions/session_service_factory.h" 24 #include "chrome/browser/sessions/session_service_factory.h"
23 #include "chrome/browser/sessions/session_types.h" 25 #include "chrome/browser/sessions/tab_restore_service_factory.h"
24 #include "chrome/browser/sessions/tab_restore_service_delegate.h" 26 #include "content/public/browser/session_storage_namespace.h"
25 #include "chrome/browser/sessions/tab_restore_service_observer.h"
26 #include "chrome/browser/ui/tab_contents/tab_contents.h"
27 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
28 #include "chrome/common/extensions/extension.h"
29 #include "chrome/common/extensions/extension_constants.h"
30 #include "chrome/common/url_constants.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/web_contents.h"
34 27
35 using base::Time; 28 namespace {
36 using content::NavigationController;
37 using content::NavigationEntry;
38 using content::WebContents;
39 29
40 // TimeFactory----------------------------------------------------------------- 30 // Only written if the tab is pinned.
31 typedef bool PinnedStatePayload;
41 32
42 TabRestoreService::TimeFactory::~TimeFactory() {} 33 typedef int32 RestoredEntryPayload;
43 34
44 // Entry ---------------------------------------------------------------------- 35 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
45 36
46 // ID of the next Entry. 37 // Payload used for the start of a tab close. This is the old struct that is
47 static SessionID::id_type next_entry_id = 1; 38 // used for backwards compat when it comes to reading the session files.
39 struct SelectedNavigationInTabPayload {
40 SessionID::id_type id;
41 int32 index;
42 };
48 43
49 TabRestoreService::Entry::Entry() 44 // Payload used for the start of a window close. This is the old struct that is
50 : id(next_entry_id++), 45 // used for backwards compat when it comes to reading the session files. This
51 type(TAB), 46 // struct must be POD, because we memset the contents.
52 from_last_session(false) {} 47 struct WindowPayload {
48 SessionID::id_type window_id;
49 int32 selected_tab_index;
50 int32 num_tabs;
51 };
53 52
54 TabRestoreService::Entry::Entry(Type type) 53 // Payload used for the start of a window close. This struct must be POD,
55 : id(next_entry_id++), 54 // because we memset the contents.
56 type(type), 55 struct WindowPayload2 : WindowPayload {
57 from_last_session(false) {} 56 int64 timestamp;
57 };
58 58
59 TabRestoreService::Entry::~Entry() {} 59 // Payload used for the start of a tab close.
60 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
61 int64 timestamp;
62 };
60 63
61 // TabRestoreService ---------------------------------------------------------- 64 // Used to indicate what has loaded.
65 enum LoadState {
66 // Indicates we haven't loaded anything.
67 NOT_LOADED = 1 << 0,
62 68
63 // static 69 // Indicates we've asked for the last sessions and tabs but haven't gotten the
64 const size_t TabRestoreService::kMaxEntries = 25; 70 // result back yet.
71 LOADING = 1 << 2,
65 72
66 // Identifier for commands written to file. 73 // Indicates we finished loading the last tabs (but not necessarily the last
67 // The ordering in the file is as follows: 74 // session).
75 LOADED_LAST_TABS = 1 << 3,
76
77 // Indicates we finished loading the last session (but not necessarily the
78 // last tabs).
79 LOADED_LAST_SESSION = 1 << 4
80 };
81
82 // Identifier for commands written to file. The ordering in the file is as
83 // follows:
68 // . When the user closes a tab a command of type 84 // . When the user closes a tab a command of type
69 // kCommandSelectedNavigationInTab is written identifying the tab and 85 // kCommandSelectedNavigationInTab is written identifying the tab and
70 // the selected index, then a kCommandPinnedState command if the tab was 86 // the selected index, then a kCommandPinnedState command if the tab was
71 // pinned and kCommandSetExtensionAppID if the tab has an app id and 87 // pinned and kCommandSetExtensionAppID if the tab has an app id and
72 // the user agent override if it was using one. This is 88 // the user agent override if it was using one. This is
73 // followed by any number of kCommandUpdateTabNavigation commands (1 per 89 // followed by any number of kCommandUpdateTabNavigation commands (1 per
74 // navigation entry). 90 // navigation entry).
75 // . When the user closes a window a kCommandSelectedNavigationInTab command 91 // . When the user closes a window a kCommandSelectedNavigationInTab command
76 // is written out and followed by n tab closed sequences (as previoulsy 92 // is written out and followed by n tab closed sequences (as previoulsy
77 // described). 93 // described).
78 // . When the user restores an entry a command of type kCommandRestoredEntry 94 // . When the user restores an entry a command of type kCommandRestoredEntry
79 // is written. 95 // is written.
80 static const SessionCommand::id_type kCommandUpdateTabNavigation = 1; 96 const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
81 static const SessionCommand::id_type kCommandRestoredEntry = 2; 97 const SessionCommand::id_type kCommandRestoredEntry = 2;
82 static const SessionCommand::id_type kCommandWindow = 3; 98 const SessionCommand::id_type kCommandWindow = 3;
83 static const SessionCommand::id_type kCommandSelectedNavigationInTab = 4; 99 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
84 static const SessionCommand::id_type kCommandPinnedState = 5; 100 const SessionCommand::id_type kCommandPinnedState = 5;
85 static const SessionCommand::id_type kCommandSetExtensionAppID = 6; 101 const SessionCommand::id_type kCommandSetExtensionAppID = 6;
86 static const SessionCommand::id_type kCommandSetWindowAppName = 7; 102 const SessionCommand::id_type kCommandSetWindowAppName = 7;
87 static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8; 103 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
88 104
89 // Number of entries (not commands) before we clobber the file and write 105 // Number of entries (not commands) before we clobber the file and write
90 // everything. 106 // everything.
91 static const int kEntriesPerReset = 40; 107 const int kEntriesPerReset = 40;
92 108
93 namespace { 109 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries;
94 110
95 // Payload structures. 111 } // namespace
96 112
97 typedef int32 RestoredEntryPayload; 113 // PersistentTabRestoreService::Delegate ---------------------------------------
98 114
99 // Payload used for the start of a window close. This is the old struct that is 115 class PersistentTabRestoreService::Delegate
sky 2012/10/25 00:45:44 Add description.
Philippe 2012/10/25 15:37:07 Done. Let me know if you think we can add more det
100 // used for backwards compat when it comes to reading the session files. This 116 : public BaseSessionService,
101 // struct must be POD, because we memset the contents. 117 public TabRestoreServiceHelper::Observer {
102 struct WindowPayload { 118 public:
103 SessionID::id_type window_id; 119 explicit Delegate(Profile* profile);
104 int32 selected_tab_index; 120
105 int32 num_tabs; 121 virtual ~Delegate();
122
123 // BaseSessionService:
124 virtual void Save() OVERRIDE;
125
126 // TabRestoreServiceHelper::Observer:
127 virtual void OnClearEntries() OVERRIDE;
128 virtual void OnRestoreEntryById(
129 SessionID::id_type id,
130 Entries::const_iterator entry_iterator) OVERRIDE;
131 virtual void OnAddEntry() OVERRIDE;
132
133 void set_tab_restore_service_helper(
134 TabRestoreServiceHelper* tab_restore_service_helper) {
135 tab_restore_service_helper_ = tab_restore_service_helper;
136 }
137
138 void LoadTabsFromLastSession();
139
140 bool IsLoaded() const;
141
142 // Creates and add entries to |entries| for each of the windows in |windows|.
143 static void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows,
144 std::vector<Entry*>* entries);
145
146 void Shutdown();
147
148 // Schedules the commands for a window close.
149 void ScheduleCommandsForWindow(const Window& window);
150
151 // Schedules the commands for a tab close. |selected_index| gives the index of
152 // the selected navigation.
153 void ScheduleCommandsForTab(const Tab& tab, int selected_index);
154
155 // Creates a window close command.
156 static SessionCommand* CreateWindowCommand(SessionID::id_type id,
157 int selected_tab_index,
158 int num_tabs,
159 base::Time timestamp);
160
161 // Creates a tab close command.
162 static SessionCommand* CreateSelectedNavigationInTabCommand(
163 SessionID::id_type tab_id,
164 int32 index,
165 base::Time timestamp);
166
167 // Creates a restore command.
168 static SessionCommand* CreateRestoredEntryCommand(
169 SessionID::id_type entry_id);
170
171 // Returns the index to persist as the selected index. This is the same as
172 // |tab.current_navigation_index| unless the entry at
173 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
174 // valid navigation to persist.
175 int GetSelectedNavigationIndexToPersist(const Tab& tab);
176
177 // Invoked when we've loaded the session commands that identify the previously
178 // closed tabs. This creates entries, adds them to staging_entries_, and
179 // invokes LoadState.
180 void OnGotLastSessionCommands(
181 Handle handle,
182 scoped_refptr<InternalGetCommandsRequest> request);
183
184 // Populates |loaded_entries| with Entries from |request|.
185 void CreateEntriesFromCommands(
186 scoped_refptr<InternalGetCommandsRequest> request,
187 std::vector<Entry*>* loaded_entries);
188
189 // Validates all entries in |entries|, deleting any with no navigations. This
190 // also deletes any entries beyond the max number of entries we can hold.
191 static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
192
193 // Callback from SessionService when we've received the windows from the
194 // previous session. This creates and add entries to |staging_entries_| and
195 // invokes LoadStateChanged. |ignored_active_window| is ignored because we
196 // don't need to restore activation.
197 void OnGotPreviousSession(Handle handle,
198 std::vector<SessionWindow*>* windows,
199 SessionID::id_type ignored_active_window);
200
201 // Converts a SessionWindow into a Window, returning true on success. We use 0
202 // as the timestamp here since we do not know when the window/tab was closed.
203 static bool ConvertSessionWindowToWindow(SessionWindow* session_window,
204 Window* window);
205
206 // Invoked when previous tabs or session is loaded. If both have finished
207 // loading the entries in |staging_entries_| are added to entries and
208 // observers are notified.
209 void LoadStateChanged();
210
211 // If |id_to_entry| contains an entry for |id| the corresponding entry is
212 // deleted and removed from both |id_to_entry| and |entries|. This is used
213 // when creating entries from the backend file.
214 void RemoveEntryByID(SessionID::id_type id,
215 IDToEntry* id_to_entry,
216 std::vector<TabRestoreService::Entry*>* entries);
217
218 private:
219 TabRestoreServiceHelper* tab_restore_service_helper_;
220
221 // The number of entries to write.
222 int entries_to_write_;
223
224 // Number of entries we've written.
225 int entries_written_;
226
227 // Whether we've loaded the last session.
228 int load_state_;
229
230 // Results from previously closed tabs/sessions is first added here. When the
231 // results from both us and the session restore service have finished loading
232 // LoadStateChanged is invoked, which adds these entries to entries_.
233 std::vector<Entry*> staging_entries_;
234
235 // Used when loading previous tabs/session.
236 CancelableRequestConsumer load_consumer_;
237
238 // Used when loading open tabs/session when recovering from a crash.
239 CancelableRequestConsumer crash_consumer_;
240
241 DISALLOW_COPY_AND_ASSIGN(Delegate);
106 }; 242 };
107 243
108 // Payload used for the start of a tab close. This is the old struct that is 244 PersistentTabRestoreService::Delegate::Delegate(Profile* profile)
109 // used for backwards compat when it comes to reading the session files. 245 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
110 struct SelectedNavigationInTabPayload { 246 FilePath()),
111 SessionID::id_type id; 247 tab_restore_service_helper_(NULL),
112 int32 index; 248 entries_to_write_(0),
113 }; 249 entries_written_(0),
114 250 load_state_(NOT_LOADED) {
115 // Payload used for the start of a window close. This struct must be POD, 251 }
116 // because we memset the contents. 252
117 struct WindowPayload2 : WindowPayload { 253 PersistentTabRestoreService::Delegate::~Delegate() {
118 int64 timestamp; 254 STLDeleteElements(&staging_entries_);
119 }; 255 }
120 256
121 // Payload used for the start of a tab close. 257 void PersistentTabRestoreService::Delegate::Save() {
122 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload { 258 const Entries& entries = tab_restore_service_helper_->entries();
123 int64 timestamp; 259 int to_write_count = std::min(entries_to_write_,
124 }; 260 static_cast<int>(entries.size()));
125 261 entries_to_write_ = 0;
126 // Only written if the tab is pinned. 262 if (entries_written_ + to_write_count > kEntriesPerReset) {
127 typedef bool PinnedStatePayload; 263 to_write_count = entries.size();
128 264 set_pending_reset(true);
129 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
130
131 // If |id_to_entry| contains an entry for |id| the corresponding entry is
132 // deleted and removed from both |id_to_entry| and |entries|. This is used
133 // when creating entries from the backend file.
134 void RemoveEntryByID(SessionID::id_type id,
135 IDToEntry* id_to_entry,
136 std::vector<TabRestoreService::Entry*>* entries) {
137 // Look for the entry in the map. If it is present, erase it from both
138 // collections and return.
139 IDToEntry::iterator i = id_to_entry->find(id);
140 if (i != id_to_entry->end()) {
141 entries->erase(std::find(entries->begin(), entries->end(), i->second));
142 delete i->second;
143 id_to_entry->erase(i);
144 return;
145 } 265 }
146 266 if (to_write_count) {
147 // Otherwise, loop over all items in the map and see if any of the Windows 267 // Write the to_write_count most recently added entries out. The most
148 // have Tabs with the |id|. 268 // recently added entry is at the front, so we use a reverse iterator to
149 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end(); 269 // write in the order the entries were added.
150 ++i) { 270 Entries::const_reverse_iterator i = entries.rbegin();
151 if (i->second->type == TabRestoreService::WINDOW) { 271 DCHECK(static_cast<size_t>(to_write_count) <= entries.size());
152 TabRestoreService::Window* window = 272 std::advance(i, entries.size() - static_cast<int>(to_write_count));
153 static_cast<TabRestoreService::Window*>(i->second); 273 for (; i != entries.rend(); ++i) {
154 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin(); 274 Entry* entry = *i;
155 for ( ; j != window->tabs.end(); ++j) { 275 if (entry->type == TAB) {
156 // If the ID matches one of this window's tabs, remove it from the list. 276 Tab* tab = static_cast<Tab*>(entry);
157 if ((*j).id == id) { 277 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
158 window->tabs.erase(j); 278 if (selected_index != -1)
159 return; 279 ScheduleCommandsForTab(*tab, selected_index);
160 } 280 } else {
281 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
161 } 282 }
283 entries_written_++;
162 } 284 }
163 } 285 }
286 if (pending_reset())
287 entries_written_ = 0;
288 BaseSessionService::Save();
164 } 289 }
165 290
166 void RecordAppLaunch(Profile* profile, const TabRestoreService::Tab& tab) { 291 void PersistentTabRestoreService::Delegate::OnClearEntries() {
167 GURL url = tab.navigations.at(tab.current_navigation_index).virtual_url();
168 DCHECK(profile->GetExtensionService());
169 if (!profile->GetExtensionService()->IsInstalledApp(url))
170 return;
171
172 AppLauncherHandler::RecordAppLaunchType(
173 extension_misc::APP_LAUNCH_NTP_RECENTLY_CLOSED);
174 }
175
176 } // namespace
177
178 TabRestoreService::Tab::Tab()
179 : Entry(TAB),
180 current_navigation_index(-1),
181 browser_id(0),
182 tabstrip_index(-1),
183 pinned(false) {
184 }
185
186 TabRestoreService::Tab::~Tab() {
187 }
188
189 TabRestoreService::Window::Window() : Entry(WINDOW), selected_tab_index(-1) {
190 }
191
192 TabRestoreService::Window::~Window() {
193 }
194
195 TabRestoreService::TabRestoreService(Profile* profile,
196 TabRestoreService::TimeFactory* time_factory)
197 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
198 FilePath()),
199 load_state_(NOT_LOADED),
200 restoring_(false),
201 entries_to_write_(0),
202 entries_written_(0),
203 time_factory_(time_factory) {
204 }
205
206 TabRestoreService::~TabRestoreService() {
207 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
208 TabRestoreServiceDestroyed(this));
209 STLDeleteElements(&entries_);
210 STLDeleteElements(&staging_entries_);
211 time_factory_ = NULL;
212 }
213
214 void TabRestoreService::AddObserver(TabRestoreServiceObserver* observer) {
215 observer_list_.AddObserver(observer);
216 }
217
218 void TabRestoreService::RemoveObserver(TabRestoreServiceObserver* observer) {
219 observer_list_.RemoveObserver(observer);
220 }
221
222 void TabRestoreService::CreateHistoricalTab(content::WebContents* contents,
223 int index) {
224 if (restoring_)
225 return;
226
227 TabRestoreServiceDelegate* delegate =
228 TabRestoreServiceDelegate::FindDelegateForWebContents(contents);
229 if (closing_delegates_.find(delegate) != closing_delegates_.end())
230 return;
231
232 scoped_ptr<Tab> local_tab(new Tab());
233 PopulateTab(local_tab.get(), index, delegate, &contents->GetController());
234 if (local_tab->navigations.empty())
235 return;
236
237 AddEntry(local_tab.release(), true, true);
238 }
239
240 void TabRestoreService::BrowserClosing(TabRestoreServiceDelegate* delegate) {
241 closing_delegates_.insert(delegate);
242
243 scoped_ptr<Window> window(new Window());
244 window->selected_tab_index = delegate->GetSelectedIndex();
245 window->timestamp = TimeNow();
246 window->app_name = delegate->GetAppName();
247
248 // Don't use std::vector::resize() because it will push copies of an empty tab
249 // into the vector, which will give all tabs in a window the same ID.
250 for (int i = 0; i < delegate->GetTabCount(); ++i) {
251 window->tabs.push_back(Tab());
252 }
253 size_t entry_index = 0;
254 for (int tab_index = 0; tab_index < delegate->GetTabCount(); ++tab_index) {
255 PopulateTab(&(window->tabs[entry_index]),
256 tab_index,
257 delegate,
258 &delegate->GetWebContentsAt(tab_index)->GetController());
259 if (window->tabs[entry_index].navigations.empty()) {
260 window->tabs.erase(window->tabs.begin() + entry_index);
261 } else {
262 window->tabs[entry_index].browser_id = delegate->GetSessionID().id();
263 entry_index++;
264 }
265 }
266 if (window->tabs.size() == 1 && window->app_name.empty()) {
267 // Short-circuit creating a Window if only 1 tab was present. This fixes
268 // http://crbug.com/56744. Copy the Tab because it's owned by an object on
269 // the stack.
270 AddEntry(new Tab(window->tabs[0]), true, true);
271 } else if (!window->tabs.empty()) {
272 window->selected_tab_index =
273 std::min(static_cast<int>(window->tabs.size() - 1),
274 window->selected_tab_index);
275 AddEntry(window.release(), true, true);
276 }
277 }
278
279 void TabRestoreService::BrowserClosed(TabRestoreServiceDelegate* delegate) {
280 closing_delegates_.erase(delegate);
281 }
282
283 void TabRestoreService::ClearEntries() {
284 // Mark all the tabs as closed so that we don't attempt to restore them. 292 // Mark all the tabs as closed so that we don't attempt to restore them.
285 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) 293 const Entries& entries = tab_restore_service_helper_->entries();
294 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
286 ScheduleCommand(CreateRestoredEntryCommand((*i)->id)); 295 ScheduleCommand(CreateRestoredEntryCommand((*i)->id));
287 296
288 entries_to_write_ = 0; 297 entries_to_write_ = 0;
289 298
290 // Schedule a pending reset so that we nuke the file on next write. 299 // Schedule a pending reset so that we nuke the file on next write.
291 set_pending_reset(true); 300 set_pending_reset(true);
292 301
293 // Schedule a command, otherwise if there are no pending commands Save does 302 // Schedule a command, otherwise if there are no pending commands Save does
294 // nothing. 303 // nothing.
295 ScheduleCommand(CreateRestoredEntryCommand(1)); 304 ScheduleCommand(CreateRestoredEntryCommand(1));
296
297 STLDeleteElements(&entries_);
298 NotifyTabsChanged();
299 } 305 }
300 306
301 const TabRestoreService::Entries& TabRestoreService::entries() const { 307 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
302 return entries_; 308 SessionID::id_type id,
303 } 309 Entries::const_iterator entry_iterator) {
304
305 void TabRestoreService::RestoreMostRecentEntry(
306 TabRestoreServiceDelegate* delegate) {
307 if (entries_.empty())
308 return;
309
310 RestoreEntryById(delegate, entries_.front()->id, UNKNOWN);
311 }
312
313 TabRestoreService::Tab* TabRestoreService::RemoveTabEntryById(
314 SessionID::id_type id) {
315 Entries::iterator i = GetEntryIteratorById(id);
316 if (i == entries_.end())
317 return NULL;
318
319 Entry* entry = *i;
320 if (entry->type != TAB)
321 return NULL;
322
323 Tab* tab = static_cast<Tab*>(entry);
324 entries_.erase(i);
325 return tab;
326 }
327
328 void TabRestoreService::RestoreEntryById(TabRestoreServiceDelegate* delegate,
329 SessionID::id_type id,
330 WindowOpenDisposition disposition) {
331 Entries::iterator i = GetEntryIteratorById(id);
332 if (i == entries_.end()) {
333 // Don't hoark here, we allow an invalid id.
334 return;
335 }
336
337 size_t index = 0; 310 size_t index = 0;
338 for (Entries::iterator j = entries_.begin(); j != i && j != entries_.end(); 311 const Entries& entries = tab_restore_service_helper_->entries();
312 for (Entries::const_iterator j = entries.begin();
313 j != entry_iterator && j != entries.end();
339 ++j, ++index) {} 314 ++j, ++index) {}
340 if (static_cast<int>(index) < entries_to_write_) 315 if (static_cast<int>(index) < entries_to_write_)
341 entries_to_write_--; 316 entries_to_write_--;
342 317
343 ScheduleCommand(CreateRestoredEntryCommand(id)); 318 ScheduleCommand(CreateRestoredEntryCommand(id));
344
345 restoring_ = true;
346 Entry* entry = *i;
347
348 // If the entry's ID does not match the ID that is being restored, then the
349 // entry is a window from which a single tab will be restored.
350 bool restoring_tab_in_window = entry->id != id;
351
352 if (!restoring_tab_in_window) {
353 entries_.erase(i);
354 i = entries_.end();
355 }
356
357 // |delegate| will be NULL in cases where one isn't already available (eg,
358 // when invoked on Mac OS X with no windows open). In this case, create a
359 // new browser into which we restore the tabs.
360 if (entry->type == TAB) {
361 Tab* tab = static_cast<Tab*>(entry);
362 delegate = RestoreTab(*tab, delegate, disposition);
363 delegate->ShowBrowserWindow();
364 } else if (entry->type == WINDOW) {
365 TabRestoreServiceDelegate* current_delegate = delegate;
366 Window* window = static_cast<Window*>(entry);
367
368 // When restoring a window, either the entire window can be restored, or a
369 // single tab within it. If the entry's ID matches the one to restore, then
370 // the entire window will be restored.
371 if (!restoring_tab_in_window) {
372 delegate = TabRestoreServiceDelegate::Create(profile(), window->app_name);
373 for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
374 const Tab& tab = window->tabs[tab_i];
375 WebContents* restored_tab =
376 delegate->AddRestoredTab(tab.navigations, delegate->GetTabCount(),
377 tab.current_navigation_index,
378 tab.extension_app_id,
379 static_cast<int>(tab_i) ==
380 window->selected_tab_index,
381 tab.pinned, tab.from_last_session,
382 tab.session_storage_namespace,
383 tab.user_agent_override);
384 if (restored_tab) {
385 restored_tab->GetController().LoadIfNecessary();
386 RecordAppLaunch(profile(), tab);
387 }
388 }
389 // All the window's tabs had the same former browser_id.
390 if (window->tabs[0].has_browser()) {
391 UpdateTabBrowserIDs(window->tabs[0].browser_id,
392 delegate->GetSessionID().id());
393 }
394 } else {
395 // Restore a single tab from the window. Find the tab that matches the ID
396 // in the window and restore it.
397 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
398 tab_i != window->tabs.end(); ++tab_i) {
399 const Tab& tab = *tab_i;
400 if (tab.id == id) {
401 delegate = RestoreTab(tab, delegate, disposition);
402 window->tabs.erase(tab_i);
403 // If restoring the tab leaves the window with nothing else, delete it
404 // as well.
405 if (!window->tabs.size()) {
406 entries_.erase(i);
407 delete entry;
408 } else {
409 // Update the browser ID of the rest of the tabs in the window so if
410 // any one is restored, it goes into the same window as the tab
411 // being restored now.
412 UpdateTabBrowserIDs(tab.browser_id,
413 delegate->GetSessionID().id());
414 for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
415 tab_j != window->tabs.end(); ++tab_j) {
416 (*tab_j).browser_id = delegate->GetSessionID().id();
417 }
418 }
419 break;
420 }
421 }
422 }
423 delegate->ShowBrowserWindow();
424
425 if (disposition == CURRENT_TAB && current_delegate &&
426 current_delegate->GetActiveWebContents()) {
427 current_delegate->CloseTab();
428 }
429 } else {
430 NOTREACHED();
431 }
432
433 if (!restoring_tab_in_window) {
434 delete entry;
435 }
436
437 restoring_ = false;
438 NotifyTabsChanged();
439 } 319 }
440 320
441 void TabRestoreService::LoadTabsFromLastSession() { 321 void PersistentTabRestoreService::Delegate::OnAddEntry() {
442 if (load_state_ != NOT_LOADED || entries_.size() == kMaxEntries) 322 // Start the save timer, when it fires we'll generate the commands.
323 StartSaveTimer();
324 entries_to_write_++;
325 }
326
327 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
328 if (load_state_ != NOT_LOADED ||
329 tab_restore_service_helper_->entries().size() == kMaxEntries)
443 return; 330 return;
444 331
445 #if !defined(ENABLE_SESSION_SERVICE) 332 #if !defined(ENABLE_SESSION_SERVICE)
446 // If sessions are not stored in the SessionService, default to 333 // If sessions are not stored in the SessionService, default to
447 // |LOADED_LAST_SESSION| state. 334 // |LOADED_LAST_SESSION| state.
448 load_state_ = LOADING | LOADED_LAST_SESSION; 335 load_state_ = LOADING | LOADED_LAST_SESSION;
449 #else 336 #else
450 load_state_ = LOADING; 337 load_state_ = LOADING;
451 338
452 SessionService* session_service = 339 SessionService* session_service =
453 SessionServiceFactory::GetForProfile(profile()); 340 SessionServiceFactory::GetForProfile(profile());
454 Profile::ExitType exit_type = profile()->GetLastSessionExitType(); 341 Profile::ExitType exit_type = profile()->GetLastSessionExitType();
455 if (!profile()->restored_last_session() && session_service && 342 if (!profile()->restored_last_session() && session_service &&
456 (exit_type == Profile::EXIT_CRASHED || 343 (exit_type == Profile::EXIT_CRASHED ||
457 exit_type == Profile::EXIT_SESSION_ENDED)) { 344 exit_type == Profile::EXIT_SESSION_ENDED)) {
458 // The previous session crashed and wasn't restored, or was a forced 345 // The previous session crashed and wasn't restored, or was a forced
459 // shutdown. Both of which won't have notified us of the browser close so 346 // shutdown. Both of which won't have notified us of the browser close so
460 // that we need to load the windows from session service (which will have 347 // that we need to load the windows from session service (which will have
461 // saved them). 348 // saved them).
462 session_service->GetLastSession( 349 session_service->GetLastSession(
463 &crash_consumer_, 350 &crash_consumer_,
464 base::Bind(&TabRestoreService::OnGotPreviousSession, 351 base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)));
465 base::Unretained(this)));
466 } else { 352 } else {
467 load_state_ |= LOADED_LAST_SESSION; 353 load_state_ |= LOADED_LAST_SESSION;
468 } 354 }
469 #endif 355 #endif
470 356
471 // Request the tabs closed in the last session. If the last session crashed, 357 // Request the tabs closed in the last session. If the last session crashed,
472 // this won't contain the tabs/window that were open at the point of the 358 // this won't contain the tabs/window that were open at the point of the
473 // crash (the call to GetLastSession above requests those). 359 // crash (the call to GetLastSession above requests those).
474 ScheduleGetLastSessionCommands( 360 ScheduleGetLastSessionCommands(
475 new InternalGetCommandsRequest( 361 new InternalGetCommandsRequest(
476 base::Bind(&TabRestoreService::OnGotLastSessionCommands, 362 base::Bind(&Delegate::OnGotLastSessionCommands,
477 base::Unretained(this))), 363 base::Unretained(this))),
478 &load_consumer_); 364 &load_consumer_);
479 } 365 }
480 366
481 bool TabRestoreService::IsLoaded() const { 367 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
482 return !(load_state_ & (NOT_LOADED | LOADING)); 368 return !(load_state_ & (NOT_LOADED | LOADING));
483 } 369 }
484 370
485 void TabRestoreService::Shutdown() { 371 // static
372 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
373 std::vector<SessionWindow*>* windows,
374 std::vector<Entry*>* entries) {
375 for (size_t i = 0; i < windows->size(); ++i) {
376 scoped_ptr<Window> window(new Window());
377 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
378 entries->push_back(window.release());
379 }
380 }
381
382 void PersistentTabRestoreService::Delegate::Shutdown() {
486 if (backend()) 383 if (backend())
487 Save(); 384 Save();
488 } 385 }
489 386
490 void TabRestoreService::Save() { 387 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
491 int to_write_count = std::min(entries_to_write_, 388 const Window& window) {
492 static_cast<int>(entries_.size()));
493 entries_to_write_ = 0;
494 if (entries_written_ + to_write_count > kEntriesPerReset) {
495 to_write_count = entries_.size();
496 set_pending_reset(true);
497 }
498 if (to_write_count) {
499 // Write the to_write_count most recently added entries out. The most
500 // recently added entry is at the front, so we use a reverse iterator to
501 // write in the order the entries were added.
502 Entries::reverse_iterator i = entries_.rbegin();
503 DCHECK(static_cast<size_t>(to_write_count) <= entries_.size());
504 std::advance(i, entries_.size() - static_cast<int>(to_write_count));
505 for (; i != entries_.rend(); ++i) {
506 Entry* entry = *i;
507 if (entry->type == TAB) {
508 Tab* tab = static_cast<Tab*>(entry);
509 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
510 if (selected_index != -1)
511 ScheduleCommandsForTab(*tab, selected_index);
512 } else {
513 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
514 }
515 entries_written_++;
516 }
517 }
518 if (pending_reset())
519 entries_written_ = 0;
520 BaseSessionService::Save();
521 }
522
523 void TabRestoreService::PopulateTab(Tab* tab,
524 int index,
525 TabRestoreServiceDelegate* delegate,
526 NavigationController* controller) {
527 const int pending_index = controller->GetPendingEntryIndex();
528 int entry_count = controller->GetEntryCount();
529 if (entry_count == 0 && pending_index == 0)
530 entry_count++;
531 tab->navigations.resize(static_cast<int>(entry_count));
532 for (int i = 0; i < entry_count; ++i) {
533 NavigationEntry* entry = (i == pending_index) ?
534 controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
535 tab->navigations[i] =
536 TabNavigation::FromNavigationEntry(i, *entry);
537 }
538 tab->timestamp = TimeNow();
539 tab->current_navigation_index = controller->GetCurrentEntryIndex();
540 if (tab->current_navigation_index == -1 && entry_count > 0)
541 tab->current_navigation_index = 0;
542 tab->tabstrip_index = index;
543
544 TabContents* tab_contents =
545 TabContents::FromWebContents(controller->GetWebContents());
546 // tab_contents is NULL in some browser tests.
547 if (tab_contents) {
548 const extensions::Extension* extension =
549 extensions::TabHelper::FromWebContents(controller->GetWebContents())->
550 extension_app();
551 if (extension)
552 tab->extension_app_id = extension->id();
553 }
554
555 tab->user_agent_override =
556 controller->GetWebContents()->GetUserAgentOverride();
557
558 // TODO(ajwong): This does not correctly handle storage for isolated apps.
559 tab->session_storage_namespace =
560 controller->GetDefaultSessionStorageNamespace();
561
562 // Delegate may be NULL during unit tests.
563 if (delegate) {
564 tab->browser_id = delegate->GetSessionID().id();
565 tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
566 }
567 }
568
569 void TabRestoreService::NotifyTabsChanged() {
570 FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
571 TabRestoreServiceChanged(this));
572 }
573
574 void TabRestoreService::AddEntry(Entry* entry, bool notify, bool to_front) {
575 if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
576 delete entry;
577 return;
578 }
579
580 if (to_front)
581 entries_.push_front(entry);
582 else
583 entries_.push_back(entry);
584
585 PruneEntries();
586
587 if (notify)
588 NotifyTabsChanged();
589
590 // Start the save timer, when it fires we'll generate the commands.
591 StartSaveTimer();
592 entries_to_write_++;
593 }
594
595 void TabRestoreService::PruneEntries() {
596 Entries new_entries;
597
598 for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
599 iter != entries_.end(); ++iter) {
600 TabRestoreService::Entry* entry = *iter;
601
602 if (FilterEntry(entry) &&
603 new_entries.size() < kMaxEntries) {
604 new_entries.push_back(entry);
605 } else {
606 delete entry;
607 }
608 }
609
610 entries_ = new_entries;
611 }
612
613 TabRestoreService::Entries::iterator TabRestoreService::GetEntryIteratorById(
614 SessionID::id_type id) {
615 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
616 if ((*i)->id == id)
617 return i;
618
619 // For Window entries, see if the ID matches a tab. If so, report the window
620 // as the Entry.
621 if ((*i)->type == WINDOW) {
622 std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
623 for (std::vector<Tab>::iterator j = tabs.begin();
624 j != tabs.end(); ++j) {
625 if ((*j).id == id) {
626 return i;
627 }
628 }
629 }
630 }
631 return entries_.end();
632 }
633
634 void TabRestoreService::ScheduleCommandsForWindow(const Window& window) {
635 DCHECK(!window.tabs.empty()); 389 DCHECK(!window.tabs.empty());
636 int selected_tab = window.selected_tab_index; 390 int selected_tab = window.selected_tab_index;
637 int valid_tab_count = 0; 391 int valid_tab_count = 0;
638 int real_selected_tab = selected_tab; 392 int real_selected_tab = selected_tab;
639 for (size_t i = 0; i < window.tabs.size(); ++i) { 393 for (size_t i = 0; i < window.tabs.size(); ++i) {
640 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) { 394 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
641 valid_tab_count++; 395 valid_tab_count++;
642 } else if (static_cast<int>(i) < selected_tab) { 396 } else if (static_cast<int>(i) < selected_tab) {
643 real_selected_tab--; 397 real_selected_tab--;
644 } 398 }
(...skipping 14 matching lines...) Expand all
659 window.app_name)); 413 window.app_name));
660 } 414 }
661 415
662 for (size_t i = 0; i < window.tabs.size(); ++i) { 416 for (size_t i = 0; i < window.tabs.size(); ++i) {
663 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]); 417 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
664 if (selected_index != -1) 418 if (selected_index != -1)
665 ScheduleCommandsForTab(window.tabs[i], selected_index); 419 ScheduleCommandsForTab(window.tabs[i], selected_index);
666 } 420 }
667 } 421 }
668 422
669 void TabRestoreService::ScheduleCommandsForTab(const Tab& tab, 423 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
670 int selected_index) { 424 const Tab& tab,
425 int selected_index) {
671 const std::vector<TabNavigation>& navigations = tab.navigations; 426 const std::vector<TabNavigation>& navigations = tab.navigations;
672 int max_index = static_cast<int>(navigations.size()); 427 int max_index = static_cast<int>(navigations.size());
673 428
674 // Determine the first navigation we'll persist. 429 // Determine the first navigation we'll persist.
675 int valid_count_before_selected = 0; 430 int valid_count_before_selected = 0;
676 int first_index_to_persist = selected_index; 431 int first_index_to_persist = selected_index;
677 for (int i = selected_index - 1; i >= 0 && 432 for (int i = selected_index - 1; i >= 0 &&
678 valid_count_before_selected < max_persist_navigation_count; --i) { 433 valid_count_before_selected < max_persist_navigation_count; --i) {
679 if (ShouldTrackEntry(navigations[i].virtual_url())) { 434 if (ShouldTrackEntry(navigations[i].virtual_url())) {
680 first_index_to_persist = i; 435 first_index_to_persist = i;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
712 for (int i = first_index_to_persist, wrote_count = 0; 467 for (int i = first_index_to_persist, wrote_count = 0;
713 i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) { 468 i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) {
714 if (ShouldTrackEntry(navigations[i].virtual_url())) { 469 if (ShouldTrackEntry(navigations[i].virtual_url())) {
715 ScheduleCommand( 470 ScheduleCommand(
716 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id, 471 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
717 navigations[i])); 472 navigations[i]));
718 } 473 }
719 } 474 }
720 } 475 }
721 476
722 SessionCommand* TabRestoreService::CreateWindowCommand(SessionID::id_type id, 477 // static
723 int selected_tab_index, 478 SessionCommand* PersistentTabRestoreService::Delegate::CreateWindowCommand(
724 int num_tabs, 479 SessionID::id_type id,
725 Time timestamp) { 480 int selected_tab_index,
481 int num_tabs,
482 base::Time timestamp) {
726 WindowPayload2 payload; 483 WindowPayload2 payload;
727 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of 484 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
728 // uninitialized memory in the struct. 485 // uninitialized memory in the struct.
729 memset(&payload, 0, sizeof(payload)); 486 memset(&payload, 0, sizeof(payload));
730 payload.window_id = id; 487 payload.window_id = id;
731 payload.selected_tab_index = selected_tab_index; 488 payload.selected_tab_index = selected_tab_index;
732 payload.num_tabs = num_tabs; 489 payload.num_tabs = num_tabs;
733 payload.timestamp = timestamp.ToInternalValue(); 490 payload.timestamp = timestamp.ToInternalValue();
734 491
735 SessionCommand* command = 492 SessionCommand* command =
736 new SessionCommand(kCommandWindow, sizeof(payload)); 493 new SessionCommand(kCommandWindow, sizeof(payload));
737 memcpy(command->contents(), &payload, sizeof(payload)); 494 memcpy(command->contents(), &payload, sizeof(payload));
738 return command; 495 return command;
739 } 496 }
740 497
741 SessionCommand* TabRestoreService::CreateSelectedNavigationInTabCommand( 498 // static
499 SessionCommand*
500 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
742 SessionID::id_type tab_id, 501 SessionID::id_type tab_id,
743 int32 index, 502 int32 index,
744 Time timestamp) { 503 base::Time timestamp) {
745 SelectedNavigationInTabPayload2 payload; 504 SelectedNavigationInTabPayload2 payload;
746 payload.id = tab_id; 505 payload.id = tab_id;
747 payload.index = index; 506 payload.index = index;
748 payload.timestamp = timestamp.ToInternalValue(); 507 payload.timestamp = timestamp.ToInternalValue();
749 SessionCommand* command = 508 SessionCommand* command =
750 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)); 509 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload));
751 memcpy(command->contents(), &payload, sizeof(payload)); 510 memcpy(command->contents(), &payload, sizeof(payload));
752 return command; 511 return command;
753 } 512 }
754 513
755 SessionCommand* TabRestoreService::CreateRestoredEntryCommand( 514 // static
515 SessionCommand*
516 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
756 SessionID::id_type entry_id) { 517 SessionID::id_type entry_id) {
757 RestoredEntryPayload payload = entry_id; 518 RestoredEntryPayload payload = entry_id;
758 SessionCommand* command = 519 SessionCommand* command =
759 new SessionCommand(kCommandRestoredEntry, sizeof(payload)); 520 new SessionCommand(kCommandRestoredEntry, sizeof(payload));
760 memcpy(command->contents(), &payload, sizeof(payload)); 521 memcpy(command->contents(), &payload, sizeof(payload));
761 return command; 522 return command;
762 } 523 }
763 524
764 int TabRestoreService::GetSelectedNavigationIndexToPersist(const Tab& tab) { 525 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
526 const Tab& tab) {
765 const std::vector<TabNavigation>& navigations = tab.navigations; 527 const std::vector<TabNavigation>& navigations = tab.navigations;
766 int selected_index = tab.current_navigation_index; 528 int selected_index = tab.current_navigation_index;
767 int max_index = static_cast<int>(navigations.size()); 529 int max_index = static_cast<int>(navigations.size());
768 530
769 // Find the first navigation to persist. We won't persist the selected 531 // Find the first navigation to persist. We won't persist the selected
770 // navigation if ShouldTrackEntry returns false. 532 // navigation if ShouldTrackEntry returns false.
771 while (selected_index >= 0 && 533 while (selected_index >= 0 &&
772 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { 534 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
773 selected_index--; 535 selected_index--;
774 } 536 }
775 537
776 if (selected_index != -1) 538 if (selected_index != -1)
777 return selected_index; 539 return selected_index;
778 540
779 // Couldn't find a navigation to persist going back, go forward. 541 // Couldn't find a navigation to persist going back, go forward.
780 selected_index = tab.current_navigation_index + 1; 542 selected_index = tab.current_navigation_index + 1;
781 while (selected_index < max_index && 543 while (selected_index < max_index &&
782 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { 544 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
783 selected_index++; 545 selected_index++;
784 } 546 }
785 547
786 return (selected_index == max_index) ? -1 : selected_index; 548 return (selected_index == max_index) ? -1 : selected_index;
787 } 549 }
788 550
789 void TabRestoreService::OnGotLastSessionCommands( 551 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
790 Handle handle, 552 Handle handle,
791 scoped_refptr<InternalGetCommandsRequest> request) { 553 scoped_refptr<InternalGetCommandsRequest> request) {
792 std::vector<Entry*> entries; 554 std::vector<Entry*> entries;
793 CreateEntriesFromCommands(request, &entries); 555 CreateEntriesFromCommands(request, &entries);
794 // Closed tabs always go to the end. 556 // Closed tabs always go to the end.
795 staging_entries_.insert(staging_entries_.end(), entries.begin(), 557 staging_entries_.insert(staging_entries_.end(), entries.begin(),
796 entries.end()); 558 entries.end());
797 load_state_ |= LOADED_LAST_TABS; 559 load_state_ |= LOADED_LAST_TABS;
798 LoadStateChanged(); 560 LoadStateChanged();
799 } 561 }
800 562
801 void TabRestoreService::CreateEntriesFromCommands( 563 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
802 scoped_refptr<InternalGetCommandsRequest> request, 564 scoped_refptr<InternalGetCommandsRequest> request,
803 std::vector<Entry*>* loaded_entries) { 565 std::vector<Entry*>* loaded_entries) {
804 if (request->canceled() || entries_.size() == kMaxEntries) 566 if (request->canceled() ||
567 tab_restore_service_helper_->entries().size() == kMaxEntries)
805 return; 568 return;
806 569
807 std::vector<SessionCommand*>& commands = request->commands; 570 std::vector<SessionCommand*>& commands = request->commands;
808 // Iterate through the commands populating entries and id_to_entry. 571 // Iterate through the commands populating entries and id_to_entry.
809 ScopedVector<Entry> entries; 572 ScopedVector<Entry> entries;
810 IDToEntry id_to_entry; 573 IDToEntry id_to_entry;
811 // If non-null we're processing the navigations of this tab. 574 // If non-null we're processing the navigations of this tab.
812 Tab* current_tab = NULL; 575 Tab* current_tab = NULL;
813 // If non-null we're processing the tabs of this window. 576 // If non-null we're processing the tabs of this window.
814 Window* current_window = NULL; 577 Window* current_window = NULL;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 pending_window_tabs = payload.num_tabs; 625 pending_window_tabs = payload.num_tabs;
863 if (pending_window_tabs <= 0) { 626 if (pending_window_tabs <= 0) {
864 // Should always have at least 1 tab. Likely indicates corruption. 627 // Should always have at least 1 tab. Likely indicates corruption.
865 return; 628 return;
866 } 629 }
867 630
868 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get())); 631 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
869 632
870 current_window = new Window(); 633 current_window = new Window();
871 current_window->selected_tab_index = payload.selected_tab_index; 634 current_window->selected_tab_index = payload.selected_tab_index;
872 current_window->timestamp = Time::FromInternalValue(payload.timestamp); 635 current_window->timestamp =
636 base::Time::FromInternalValue(payload.timestamp);
873 entries.push_back(current_window); 637 entries.push_back(current_window);
874 id_to_entry[payload.window_id] = current_window; 638 id_to_entry[payload.window_id] = current_window;
875 break; 639 break;
876 } 640 }
877 641
878 case kCommandSelectedNavigationInTab: { 642 case kCommandSelectedNavigationInTab: {
879 SelectedNavigationInTabPayload2 payload; 643 SelectedNavigationInTabPayload2 payload;
880 if (!command.GetPayload(&payload, sizeof(payload))) { 644 if (!command.GetPayload(&payload, sizeof(payload))) {
881 SelectedNavigationInTabPayload old_payload; 645 SelectedNavigationInTabPayload old_payload;
882 if (!command.GetPayload(&old_payload, sizeof(old_payload))) 646 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
(...skipping 12 matching lines...) Expand all
895 return; 659 return;
896 } 660 }
897 current_window->tabs.resize(current_window->tabs.size() + 1); 661 current_window->tabs.resize(current_window->tabs.size() + 1);
898 current_tab = &(current_window->tabs.back()); 662 current_tab = &(current_window->tabs.back());
899 if (--pending_window_tabs == 0) 663 if (--pending_window_tabs == 0)
900 current_window = NULL; 664 current_window = NULL;
901 } else { 665 } else {
902 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get())); 666 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
903 current_tab = new Tab(); 667 current_tab = new Tab();
904 id_to_entry[payload.id] = current_tab; 668 id_to_entry[payload.id] = current_tab;
905 current_tab->timestamp = Time::FromInternalValue(payload.timestamp); 669 current_tab->timestamp =
670 base::Time::FromInternalValue(payload.timestamp);
906 entries.push_back(current_tab); 671 entries.push_back(current_tab);
907 } 672 }
908 current_tab->current_navigation_index = payload.index; 673 current_tab->current_navigation_index = payload.index;
909 break; 674 break;
910 } 675 }
911 676
912 case kCommandUpdateTabNavigation: { 677 case kCommandUpdateTabNavigation: {
913 if (!current_tab) { 678 if (!current_tab) {
914 // Should be in a tab when we get this. 679 // Should be in a tab when we get this.
915 return; 680 return;
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
985 return; 750 return;
986 } 751 }
987 } 752 }
988 753
989 // If there was corruption some of the entries won't be valid. 754 // If there was corruption some of the entries won't be valid.
990 ValidateAndDeleteEmptyEntries(&(entries.get())); 755 ValidateAndDeleteEmptyEntries(&(entries.get()));
991 756
992 loaded_entries->swap(entries.get()); 757 loaded_entries->swap(entries.get());
993 } 758 }
994 759
995 TabRestoreServiceDelegate* TabRestoreService::RestoreTab( 760 // static
996 const Tab& tab, 761 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
997 TabRestoreServiceDelegate* delegate,
998 WindowOpenDisposition disposition) {
999 if (disposition == CURRENT_TAB && delegate) {
1000 delegate->ReplaceRestoredTab(tab.navigations,
1001 tab.current_navigation_index,
1002 tab.from_last_session,
1003 tab.extension_app_id,
1004 tab.session_storage_namespace,
1005 tab.user_agent_override);
1006 } else {
1007 // We only respsect the tab's original browser if there's no disposition.
1008 if (disposition == UNKNOWN && tab.has_browser())
1009 delegate = TabRestoreServiceDelegate::FindDelegateWithID(tab.browser_id);
1010
1011 int tab_index = -1;
1012
1013 // |delegate| will be NULL in cases where one isn't already available (eg,
1014 // when invoked on Mac OS X with no windows open). In this case, create a
1015 // new browser into which we restore the tabs.
1016 if (delegate && disposition != NEW_WINDOW) {
1017 tab_index = tab.tabstrip_index;
1018 } else {
1019 delegate = TabRestoreServiceDelegate::Create(profile(), std::string());
1020 if (tab.has_browser())
1021 UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
1022 }
1023
1024 // Place the tab at the end if the tab index is no longer valid or
1025 // we were passed a specific disposition.
1026 if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
1027 disposition != UNKNOWN) {
1028 tab_index = delegate->GetTabCount();
1029 }
1030
1031 WebContents* web_contents = delegate->AddRestoredTab(
1032 tab.navigations,
1033 tab_index,
1034 tab.current_navigation_index,
1035 tab.extension_app_id,
1036 disposition != NEW_BACKGROUND_TAB,
1037 tab.pinned,
1038 tab.from_last_session,
1039 tab.session_storage_namespace,
1040 tab.user_agent_override);
1041 web_contents->GetController().LoadIfNecessary();
1042 }
1043 RecordAppLaunch(profile(), tab);
1044 return delegate;
1045 }
1046
1047
1048 bool TabRestoreService::ValidateTab(Tab* tab) {
1049 if (tab->navigations.empty())
1050 return false;
1051
1052 tab->current_navigation_index =
1053 std::max(0, std::min(tab->current_navigation_index,
1054 static_cast<int>(tab->navigations.size()) - 1));
1055
1056 return true;
1057 }
1058
1059 bool TabRestoreService::ValidateWindow(Window* window) {
1060 window->selected_tab_index =
1061 std::max(0, std::min(window->selected_tab_index,
1062 static_cast<int>(window->tabs.size() - 1)));
1063
1064 int i = 0;
1065 for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
1066 tab_i != window->tabs.end();) {
1067 if (!ValidateTab(&(*tab_i))) {
1068 tab_i = window->tabs.erase(tab_i);
1069 if (i < window->selected_tab_index)
1070 window->selected_tab_index--;
1071 else if (i == window->selected_tab_index)
1072 window->selected_tab_index = 0;
1073 } else {
1074 ++tab_i;
1075 ++i;
1076 }
1077 }
1078
1079 if (window->tabs.empty())
1080 return false;
1081
1082 return true;
1083 }
1084
1085 bool TabRestoreService::ValidateEntry(Entry* entry) {
1086 if (entry->type == TAB)
1087 return ValidateTab(static_cast<Tab*>(entry));
1088
1089 if (entry->type == WINDOW)
1090 return ValidateWindow(static_cast<Window*>(entry));
1091
1092 NOTREACHED();
1093 return false;
1094 }
1095
1096 bool TabRestoreService::IsTabInteresting(const Tab* tab) {
1097 if (tab->navigations.empty())
1098 return false;
1099
1100 if (tab->navigations.size() > 1)
1101 return true;
1102
1103 return tab->pinned ||
1104 tab->navigations.at(0).virtual_url() !=
1105 GURL(chrome::kChromeUINewTabURL);
1106 }
1107
1108 bool TabRestoreService::IsWindowInteresting(const Window* window) {
1109 if (window->tabs.empty())
1110 return false;
1111
1112 if (window->tabs.size() > 1)
1113 return true;
1114
1115 return IsTabInteresting(&window->tabs[0]);
1116 }
1117
1118 bool TabRestoreService::FilterEntry(Entry* entry) {
1119 if (!ValidateEntry(entry))
1120 return false;
1121
1122 if (entry->type == TAB)
1123 return IsTabInteresting(static_cast<Tab*>(entry));
1124 else if (entry->type == WINDOW)
1125 return IsWindowInteresting(static_cast<Window*>(entry));
1126
1127 NOTREACHED();
1128 return false;
1129 }
1130
1131 void TabRestoreService::ValidateAndDeleteEmptyEntries(
1132 std::vector<Entry*>* entries) { 762 std::vector<Entry*>* entries) {
1133 std::vector<Entry*> valid_entries; 763 std::vector<Entry*> valid_entries;
1134 std::vector<Entry*> invalid_entries; 764 std::vector<Entry*> invalid_entries;
1135 765
1136 // Iterate from the back so that we keep the most recently closed entries. 766 // Iterate from the back so that we keep the most recently closed entries.
1137 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin(); 767 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
1138 i != entries->rend(); ++i) { 768 i != entries->rend(); ++i) {
1139 if (ValidateEntry(*i)) 769 if (TabRestoreServiceHelper::ValidateEntry(*i))
1140 valid_entries.push_back(*i); 770 valid_entries.push_back(*i);
1141 else 771 else
1142 invalid_entries.push_back(*i); 772 invalid_entries.push_back(*i);
1143 } 773 }
1144 // NOTE: at this point the entries are ordered with newest at the front. 774 // NOTE: at this point the entries are ordered with newest at the front.
1145 entries->swap(valid_entries); 775 entries->swap(valid_entries);
1146 776
1147 // Delete the remaining entries. 777 // Delete the remaining entries.
1148 STLDeleteElements(&invalid_entries); 778 STLDeleteElements(&invalid_entries);
1149 } 779 }
1150 780
1151 void TabRestoreService::UpdateTabBrowserIDs(SessionID::id_type old_id, 781 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
1152 SessionID::id_type new_id) {
1153 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
1154 Entry* entry = *i;
1155 if (entry->type == TAB) {
1156 Tab* tab = static_cast<Tab*>(entry);
1157 if (tab->browser_id == old_id)
1158 tab->browser_id = new_id;
1159 }
1160 }
1161 }
1162
1163 void TabRestoreService::OnGotPreviousSession(
1164 Handle handle, 782 Handle handle,
1165 std::vector<SessionWindow*>* windows, 783 std::vector<SessionWindow*>* windows,
1166 SessionID::id_type ignored_active_window) { 784 SessionID::id_type ignored_active_window) {
1167 std::vector<Entry*> entries; 785 std::vector<Entry*> entries;
1168 CreateEntriesFromWindows(windows, &entries); 786 CreateEntriesFromWindows(windows, &entries);
1169 // Previous session tabs go first. 787 // Previous session tabs go first.
1170 staging_entries_.insert(staging_entries_.begin(), entries.begin(), 788 staging_entries_.insert(staging_entries_.begin(), entries.begin(),
1171 entries.end()); 789 entries.end());
1172 load_state_ |= LOADED_LAST_SESSION; 790 load_state_ |= LOADED_LAST_SESSION;
1173 LoadStateChanged(); 791 LoadStateChanged();
1174 } 792 }
1175 793
1176 void TabRestoreService::CreateEntriesFromWindows( 794 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
1177 std::vector<SessionWindow*>* windows,
1178 std::vector<Entry*>* entries) {
1179 for (size_t i = 0; i < windows->size(); ++i) {
1180 scoped_ptr<Window> window(new Window());
1181 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
1182 entries->push_back(window.release());
1183 }
1184 }
1185
1186 bool TabRestoreService::ConvertSessionWindowToWindow(
1187 SessionWindow* session_window, 795 SessionWindow* session_window,
1188 Window* window) { 796 Window* window) {
1189 for (size_t i = 0; i < session_window->tabs.size(); ++i) { 797 for (size_t i = 0; i < session_window->tabs.size(); ++i) {
1190 if (!session_window->tabs[i]->navigations.empty()) { 798 if (!session_window->tabs[i]->navigations.empty()) {
1191 window->tabs.resize(window->tabs.size() + 1); 799 window->tabs.resize(window->tabs.size() + 1);
1192 Tab& tab = window->tabs.back(); 800 Tab& tab = window->tabs.back();
1193 tab.pinned = session_window->tabs[i]->pinned; 801 tab.pinned = session_window->tabs[i]->pinned;
1194 tab.navigations.swap(session_window->tabs[i]->navigations); 802 tab.navigations.swap(session_window->tabs[i]->navigations);
1195 tab.current_navigation_index = 803 tab.current_navigation_index =
1196 session_window->tabs[i]->current_navigation_index; 804 session_window->tabs[i]->current_navigation_index;
1197 tab.extension_app_id = session_window->tabs[i]->extension_app_id; 805 tab.extension_app_id = session_window->tabs[i]->extension_app_id;
1198 tab.timestamp = Time(); 806 tab.timestamp = base::Time();
1199 } 807 }
1200 } 808 }
1201 if (window->tabs.empty()) 809 if (window->tabs.empty())
1202 return false; 810 return false;
1203 811
1204 window->selected_tab_index = 812 window->selected_tab_index =
1205 std::min(session_window->selected_tab_index, 813 std::min(session_window->selected_tab_index,
1206 static_cast<int>(window->tabs.size() - 1)); 814 static_cast<int>(window->tabs.size() - 1));
1207 window->timestamp = Time(); 815 window->timestamp = base::Time();
1208 return true; 816 return true;
1209 } 817 }
1210 818
1211 void TabRestoreService::LoadStateChanged() { 819 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
1212 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) != 820 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
1213 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) { 821 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
1214 // Still waiting on previous session or previous tabs. 822 // Still waiting on previous session or previous tabs.
1215 return; 823 return;
1216 } 824 }
1217 825
1218 // We're done loading. 826 // We're done loading.
1219 load_state_ ^= LOADING; 827 load_state_ ^= LOADING;
1220 828
1221 if (staging_entries_.empty() || entries_.size() >= kMaxEntries) { 829 const Entries& entries = tab_restore_service_helper_->entries();
830 if (staging_entries_.empty() || entries.size() >= kMaxEntries) {
1222 STLDeleteElements(&staging_entries_); 831 STLDeleteElements(&staging_entries_);
1223 return; 832 return;
1224 } 833 }
1225 834
1226 if (staging_entries_.size() + entries_.size() > kMaxEntries) { 835 if (staging_entries_.size() + entries.size() > kMaxEntries) {
1227 // If we add all the staged entries we'll end up with more than 836 // If we add all the staged entries we'll end up with more than
1228 // kMaxEntries. Delete entries such that we only end up with 837 // kMaxEntries. Delete entries such that we only end up with at most
1229 // at most kMaxEntries. 838 // kMaxEntries.
1230 int surplus = kMaxEntries - entries_.size(); 839 int surplus = kMaxEntries - entries.size();
1231 CHECK_LE(0, surplus); 840 CHECK_LE(0, surplus);
1232 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus); 841 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
1233 STLDeleteContainerPointers( 842 STLDeleteContainerPointers(
1234 staging_entries_.begin() + (kMaxEntries - entries_.size()), 843 staging_entries_.begin() + (kMaxEntries - entries.size()),
1235 staging_entries_.end()); 844 staging_entries_.end());
1236 staging_entries_.erase( 845 staging_entries_.erase(
1237 staging_entries_.begin() + (kMaxEntries - entries_.size()), 846 staging_entries_.begin() + (kMaxEntries - entries.size()),
1238 staging_entries_.end()); 847 staging_entries_.end());
1239 } 848 }
1240 849
1241 // And add them. 850 // And add them.
1242 for (size_t i = 0; i < staging_entries_.size(); ++i) { 851 for (size_t i = 0; i < staging_entries_.size(); ++i) {
1243 staging_entries_[i]->from_last_session = true; 852 staging_entries_[i]->from_last_session = true;
1244 AddEntry(staging_entries_[i], false, false); 853 tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false);
1245 } 854 }
1246 855
1247 // AddEntry takes ownership of the entry, need to clear out entries so that 856 // AddEntry takes ownership of the entry, need to clear out entries so that
1248 // it doesn't delete them. 857 // it doesn't delete them.
1249 staging_entries_.clear(); 858 staging_entries_.clear();
1250 859
1251 // Make it so we rewrite all the tabs. We need to do this otherwise we won't 860 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
1252 // correctly write out the entries when Save is invoked (Save starts from 861 // correctly write out the entries when Save is invoked (Save starts from
1253 // the front, not the end and we just added the entries to the end). 862 // the front, not the end and we just added the entries to the end).
1254 entries_to_write_ = staging_entries_.size(); 863 entries_to_write_ = staging_entries_.size();
1255 864
1256 PruneEntries(); 865 tab_restore_service_helper_->PruneEntries();
1257 NotifyTabsChanged(); 866 tab_restore_service_helper_->NotifyTabsChanged();
1258 } 867 }
1259 868
1260 Time TabRestoreService::TimeNow() const { 869 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
1261 return time_factory_ ? time_factory_->TimeNow() : Time::Now(); 870 SessionID::id_type id,
871 IDToEntry* id_to_entry,
872 std::vector<TabRestoreService::Entry*>* entries) {
873 // Look for the entry in the map. If it is present, erase it from both
874 // collections and return.
875 IDToEntry::iterator i = id_to_entry->find(id);
876 if (i != id_to_entry->end()) {
877 entries->erase(std::find(entries->begin(), entries->end(), i->second));
878 delete i->second;
879 id_to_entry->erase(i);
880 return;
881 }
882
883 // Otherwise, loop over all items in the map and see if any of the Windows
884 // have Tabs with the |id|.
885 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
886 ++i) {
887 if (i->second->type == TabRestoreService::WINDOW) {
888 TabRestoreService::Window* window =
889 static_cast<TabRestoreService::Window*>(i->second);
890 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
891 for ( ; j != window->tabs.end(); ++j) {
892 // If the ID matches one of this window's tabs, remove it from the
893 // list.
894 if ((*j).id == id) {
895 window->tabs.erase(j);
896 return;
897 }
898 }
899 }
900 }
1262 } 901 }
902
903 // PersistentTabRestoreService -------------------------------------------------
904
905 PersistentTabRestoreService::PersistentTabRestoreService(
906 Profile* profile,
907 TimeFactory* time_factory)
908 : delegate_(new Delegate(profile)),
909 ALLOW_THIS_IN_INITIALIZER_LIST(
910 helper_(this, delegate_.get(), profile, time_factory)) {
911 delegate_->set_tab_restore_service_helper(&helper_);
912 }
913
914 PersistentTabRestoreService::~PersistentTabRestoreService() {}
915
916 void PersistentTabRestoreService::AddObserver(
917 TabRestoreServiceObserver* observer) {
918 helper_.AddObserver(observer);
919 }
920
921 void PersistentTabRestoreService::RemoveObserver(
922 TabRestoreServiceObserver* observer) {
923 helper_.RemoveObserver(observer);
924 }
925
926 void PersistentTabRestoreService::CreateHistoricalTab(
927 content::WebContents* contents,
928 int index) {
929 helper_.CreateHistoricalTab(contents, index);
930 }
931
932 void PersistentTabRestoreService::BrowserClosing(
933 TabRestoreServiceDelegate* delegate) {
934 helper_.BrowserClosing(delegate);
935 }
936
937 void PersistentTabRestoreService::BrowserClosed(
938 TabRestoreServiceDelegate* delegate) {
939 helper_.BrowserClosed(delegate);
940 }
941
942 void PersistentTabRestoreService::ClearEntries() {
943 helper_.ClearEntries();
944 }
945
946 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
947 return helper_.entries();
948 }
949
950 void PersistentTabRestoreService::RestoreMostRecentEntry(
951 TabRestoreServiceDelegate* delegate) {
952 helper_.RestoreMostRecentEntry(delegate);
953 }
954
955 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
956 SessionID::id_type id) {
957 return helper_.RemoveTabEntryById(id);
958 }
959
960 void PersistentTabRestoreService::RestoreEntryById(
961 TabRestoreServiceDelegate* delegate,
962 SessionID::id_type id,
963 WindowOpenDisposition disposition) {
964 helper_.RestoreEntryById(delegate, id, disposition);
965 }
966
967 bool PersistentTabRestoreService::IsLoaded() const {
968 return delegate_->IsLoaded();
969 }
970
971 void PersistentTabRestoreService::DeleteLastSession() {
972 return delegate_->DeleteLastSession();
973 }
974
975 void PersistentTabRestoreService::Shutdown() {
976 return delegate_->Shutdown();
977 }
978
979 void PersistentTabRestoreService::LoadTabsFromLastSession() {
980 delegate_->LoadTabsFromLastSession();
981 }
982
983 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
984 return &helper_.entries_;
985 }
986
987 void PersistentTabRestoreService::PruneEntries() {
988 helper_.PruneEntries();
989 }
990
991 ProfileKeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor(
992 Profile* profile) const {
993 return new PersistentTabRestoreService(profile, NULL);
994 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698