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

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

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

Powered by Google App Engine
This is Rietveld 408576698