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

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: Split TRS into InMemoryTRS and PersistentTRS Created 8 years, 2 months 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
6
7 #include <cstring> // memcpy
8 #include <vector>
9
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/file_path.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/stl_util.h"
16 #include "base/time.h"
17 #include "chrome/browser/sessions/session_command.h"
18 #include "content/public/browser/session_storage_namespace.h"
19
20 namespace {
21
22 // Only written if the tab is pinned.
23 typedef bool PinnedStatePayload;
24
25 typedef int32 RestoredEntryPayload;
26
27 // Payload used for the start of a tab close. This is the old struct that is
28 // used for backwards compat when it comes to reading the session files.
29 struct SelectedNavigationInTabPayload {
30 SessionID::id_type id;
31 int32 index;
32 };
33
34 // Payload used for the start of a window close. This is the old struct that is
35 // used for backwards compat when it comes to reading the session files. This
36 // struct must be POD, because we memset the contents.
37 struct WindowPayload {
38 SessionID::id_type window_id;
39 int32 selected_tab_index;
40 int32 num_tabs;
41 };
42
43 // Payload used for the start of a window close. This struct must be POD,
44 // because we memset the contents.
45 struct WindowPayload2 : WindowPayload {
46 int64 timestamp;
47 };
48
49 // Payload used for the start of a tab close.
50 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
51 int64 timestamp;
52 };
53
54 // Used to indicate what has loaded.
55 enum LoadState {
56 // Indicates we haven't loaded anything.
57 NOT_LOADED = 1 << 0,
58
59 // Indicates we've asked for the last sessions and tabs but haven't gotten
60 // the result back yet.
61 LOADING = 1 << 2,
62
63 // Indicates we finished loading the last tabs (but not necessarily the
64 // last session).
65 LOADED_LAST_TABS = 1 << 3,
66
67 // Indicates we finished loading the last session (but not necessarily the
68 // last tabs).
69 LOADED_LAST_SESSION = 1 << 4
70 };
71
72 // Identifier for commands written to file.
73 // The ordering in the file is as follows:
74 // . When the user closes a tab a command of type
75 // kCommandSelectedNavigationInTab is written identifying the tab and
76 // the selected index, then a kCommandPinnedState command if the tab was
77 // pinned and kCommandSetExtensionAppID if the tab has an app id and
78 // the user agent override if it was using one. This is
79 // followed by any number of kCommandUpdateTabNavigation commands (1 per
80 // navigation entry).
81 // . When the user closes a window a kCommandSelectedNavigationInTab command
82 // is written out and followed by n tab closed sequences (as previoulsy
83 // described).
84 // . When the user restores an entry a command of type kCommandRestoredEntry
85 // is written.
86 const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
87 const SessionCommand::id_type kCommandRestoredEntry = 2;
88 const SessionCommand::id_type kCommandWindow = 3;
89 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
90 const SessionCommand::id_type kCommandPinnedState = 5;
91 const SessionCommand::id_type kCommandSetExtensionAppID = 6;
92 const SessionCommand::id_type kCommandSetWindowAppName = 7;
93 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
94
95 // Number of entries (not commands) before we clobber the file and write
96 // everything.
97 const int kEntriesPerReset = 40;
98
99 SessionCommand* CreateRestoredEntryCommand(SessionID::id_type entry_id) {
100 RestoredEntryPayload payload = entry_id;
101 SessionCommand* command =
102 new SessionCommand(kCommandRestoredEntry, sizeof(payload));
103 memcpy(command->contents(), &payload, sizeof(payload));
104 return command;
105 }
106
107 SessionCommand* CreateSelectedNavigationInTabCommand(SessionID::id_type tab_id,
108 int32 index,
109 base::Time timestamp) {
110 SelectedNavigationInTabPayload2 payload;
111 payload.id = tab_id;
112 payload.index = index;
113 payload.timestamp = timestamp.ToInternalValue();
114 SessionCommand* command =
115 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload));
116 memcpy(command->contents(), &payload, sizeof(payload));
117 return command;
118 }
119
120 SessionCommand* CreateWindowCommand(SessionID::id_type id,
121 int selected_tab_index,
122 int num_tabs,
123 base::Time timestamp) {
124 WindowPayload2 payload;
125 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
126 // uninitialized memory in the struct.
127 memset(&payload, 0, sizeof(payload));
128 payload.window_id = id;
129 payload.selected_tab_index = selected_tab_index;
130 payload.num_tabs = num_tabs;
131 payload.timestamp = timestamp.ToInternalValue();
132
133 SessionCommand* command =
134 new SessionCommand(kCommandWindow, sizeof(payload));
135 memcpy(command->contents(), &payload, sizeof(payload));
136 return command;
137 }
138
139
140 } // namespace
141
142 PersistentTabRestoreService::PersistentTabRestoreService(
143 Profile* profile,
144 TimeFactory* time_factory)
145 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
146 FilePath()),
147 InMemoryTabRestoreService(profile, time_factory),
148 entries_to_write_(0),
149 entries_written_(0),
150 load_state_(NOT_LOADED) {
151 }
152
153 PersistentTabRestoreService::~PersistentTabRestoreService() {
154 STLDeleteElements(&staging_entries_);
155 }
156
157 void PersistentTabRestoreService::Save() {
158 int to_write_count = std::min(entries_to_write_,
159 static_cast<int>(entries().size()));
160 entries_to_write_ = 0;
161 if (entries_written_ + to_write_count > kEntriesPerReset) {
162 to_write_count = entries().size();
163 set_pending_reset(true);
164 }
165 if (to_write_count) {
166 // Write the to_write_count most recently added entries out. The most
167 // recently added entry is at the front, so we use a reverse iterator to
168 // write in the order the entries were added.
169 Entries::const_reverse_iterator i = entries().rbegin();
170 DCHECK(static_cast<size_t>(to_write_count) <= entries().size());
171 std::advance(i, entries().size() - static_cast<int>(to_write_count));
172 for (; i != entries().rend(); ++i) {
173 Entry* entry = *i;
174 if (entry->type == TAB) {
175 Tab* tab = static_cast<Tab*>(entry);
176 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
177 if (selected_index != -1)
178 ScheduleCommandsForTab(*tab, selected_index);
179 } else {
180 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
181 }
182 entries_written_++;
183 }
184 }
185 if (pending_reset())
186 entries_written_ = 0;
187 BaseSessionService::Save();
188 }
189
190 bool PersistentTabRestoreService::IsLoaded() const {
191 return !(load_state_ & (NOT_LOADED | LOADING));
192 }
193
194 void PersistentTabRestoreService::Shutdown() {
195 if (backend())
196 Save();
197 }
198
199 void PersistentTabRestoreService::OnClearEntries() {
200 const Entries& entries = TabRestoreService::entries();
201 // Mark all the tabs as closed so that we don't attempt to restore them.
202 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
203 ScheduleCommand(CreateRestoredEntryCommand((*i)->id));
204
205 entries_to_write_ = 0;
206
207 // Schedule a pending reset so that we nuke the file on next write.
208 set_pending_reset(true);
209
210 // Schedule a command, otherwise if there are no pending commands Save does
211 // nothing.
212 ScheduleCommand(CreateRestoredEntryCommand(1));
213 }
214
215 void PersistentTabRestoreService::OnRestoreEntryById(
216 SessionID::id_type id,
217 Entries::const_iterator entry_iterator) {
218 const Entries& entries = TabRestoreService::entries();
219 size_t index = 0;
220 for (Entries::const_iterator j = entries.begin();
221 j != entry_iterator && j != entries.end();
222 ++j, ++index) {}
223 if (static_cast<int>(index) < entries_to_write_)
224 entries_to_write_--;
225
226 ScheduleCommand(CreateRestoredEntryCommand(id));
227 }
228
229 void PersistentTabRestoreService::OnAddEntry() {
230 // Start the save timer, when it fires we'll generate the commands.
231 StartSaveTimer();
232 entries_to_write_++;
233 }
234
235 int PersistentTabRestoreService::GetSelectedNavigationIndexToPersist(
236 const Tab& tab) {
237 const std::vector<TabNavigation>& navigations = tab.navigations;
238 int selected_index = tab.current_navigation_index;
239 int max_index = static_cast<int>(navigations.size());
240
241 // Find the first navigation to persist. We won't persist the selected
242 // navigation if ShouldTrackEntry returns false.
243 while (selected_index >= 0 &&
244 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
245 selected_index--;
246 }
247
248 if (selected_index != -1)
249 return selected_index;
250
251 // Couldn't find a navigation to persist going back, go forward.
252 selected_index = tab.current_navigation_index + 1;
253 while (selected_index < max_index &&
254 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
255 selected_index++;
256 }
257
258 return (selected_index == max_index) ? -1 : selected_index;
259 }
260
261 void PersistentTabRestoreService::ScheduleCommandsForTab(const Tab& tab,
262 int selected_index) {
263 const std::vector<TabNavigation>& navigations = tab.navigations;
264 int max_index = static_cast<int>(navigations.size());
265
266 // Determine the first navigation we'll persist.
267 int valid_count_before_selected = 0;
268 int first_index_to_persist = selected_index;
269 for (int i = selected_index - 1; i >= 0 &&
270 valid_count_before_selected < max_persist_navigation_count; --i) {
271 if (ShouldTrackEntry(navigations[i].virtual_url())) {
272 first_index_to_persist = i;
273 valid_count_before_selected++;
274 }
275 }
276
277 // Write the command that identifies the selected tab.
278 ScheduleCommand(
279 CreateSelectedNavigationInTabCommand(tab.id,
280 valid_count_before_selected,
281 tab.timestamp));
282
283 if (tab.pinned) {
284 PinnedStatePayload payload = true;
285 SessionCommand* command =
286 new SessionCommand(kCommandPinnedState, sizeof(payload));
287 memcpy(command->contents(), &payload, sizeof(payload));
288 ScheduleCommand(command);
289 }
290
291 if (!tab.extension_app_id.empty()) {
292 ScheduleCommand(
293 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id,
294 tab.extension_app_id));
295 }
296
297 if (!tab.user_agent_override.empty()) {
298 ScheduleCommand(
299 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
300 tab.id, tab.user_agent_override));
301 }
302
303 // Then write the navigations.
304 for (int i = first_index_to_persist, wrote_count = 0;
305 i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) {
306 if (ShouldTrackEntry(navigations[i].virtual_url())) {
307 ScheduleCommand(
308 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
309 navigations[i]));
310 }
311 }
312 }
313
314 void PersistentTabRestoreService::ScheduleCommandsForWindow(
315 const Window& window) {
316 DCHECK(!window.tabs.empty());
317 int selected_tab = window.selected_tab_index;
318 int valid_tab_count = 0;
319 int real_selected_tab = selected_tab;
320 for (size_t i = 0; i < window.tabs.size(); ++i) {
321 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
322 valid_tab_count++;
323 } else if (static_cast<int>(i) < selected_tab) {
324 real_selected_tab--;
325 }
326 }
327 if (valid_tab_count == 0)
328 return; // No tabs to persist.
329
330 ScheduleCommand(
331 CreateWindowCommand(window.id,
332 std::min(real_selected_tab, valid_tab_count - 1),
333 valid_tab_count,
334 window.timestamp));
335
336 if (!window.app_name.empty()) {
337 ScheduleCommand(
338 CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
339 window.id,
340 window.app_name));
341 }
342
343 for (size_t i = 0; i < window.tabs.size(); ++i) {
344 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
345 if (selected_index != -1)
346 ScheduleCommandsForTab(window.tabs[i], selected_index);
347 }
348 }
349
350 void PersistentTabRestoreService::OnGotPreviousSession(
351 Handle handle,
352 std::vector<SessionWindow*>* windows,
353 SessionID::id_type ignored_active_window) {
354 std::vector<Entry*> entries;
355 CreateEntriesFromWindows(windows, &entries);
356 // Previous session tabs go first.
357 staging_entries_.insert(staging_entries_.begin(), entries.begin(),
358 entries.end());
359 load_state_ |= LOADED_LAST_SESSION;
360 LoadStateChanged();
361 }
362
363 void PersistentTabRestoreService::OnGotLastSessionCommands(
364 Handle handle,
365 scoped_refptr<InternalGetCommandsRequest> request) {
366 std::vector<Entry*> entries;
367 CreateEntriesFromCommands(request, &entries);
368 // Closed tabs always go to the end.
369 staging_entries_.insert(staging_entries_.end(), entries.begin(),
370 entries.end());
371 load_state_ |= LOADED_LAST_TABS;
372 LoadStateChanged();
373 }
374
375 void PersistentTabRestoreService::CreateEntriesFromCommands(
376 scoped_refptr<InternalGetCommandsRequest> request,
377 std::vector<Entry*>* loaded_entries) {
378 if (request->canceled() || entries().size() == kMaxEntries)
379 return;
380
381 std::vector<SessionCommand*>& commands = request->commands;
382 // Iterate through the commands populating entries and id_to_entry.
383 ScopedVector<Entry> entries;
384 IDToEntry id_to_entry;
385 // If non-null we're processing the navigations of this tab.
386 Tab* current_tab = NULL;
387 // If non-null we're processing the tabs of this window.
388 Window* current_window = NULL;
389 // If > 0, we've gotten a window command but not all the tabs yet.
390 int pending_window_tabs = 0;
391 for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
392 i != commands.end(); ++i) {
393 const SessionCommand& command = *(*i);
394 switch (command.id()) {
395 case kCommandRestoredEntry: {
396 if (pending_window_tabs > 0) {
397 // Should never receive a restored command while waiting for all the
398 // tabs in a window.
399 return;
400 }
401
402 current_tab = NULL;
403 current_window = NULL;
404
405 RestoredEntryPayload payload;
406 if (!command.GetPayload(&payload, sizeof(payload)))
407 return;
408 RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
409 break;
410 }
411
412 case kCommandWindow: {
413 WindowPayload2 payload;
414 if (pending_window_tabs > 0) {
415 // Should never receive a window command while waiting for all the
416 // tabs in a window.
417 return;
418 }
419
420 // Try the new payload first
421 if (!command.GetPayload(&payload, sizeof(payload))) {
422 // then the old payload
423 WindowPayload old_payload;
424 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
425 return;
426
427 // Copy the old payload data to the new payload.
428 payload.window_id = old_payload.window_id;
429 payload.selected_tab_index = old_payload.selected_tab_index;
430 payload.num_tabs = old_payload.num_tabs;
431 // Since we don't have a time use time 0 which is used to mark as an
432 // unknown timestamp.
433 payload.timestamp = 0;
434 }
435
436 pending_window_tabs = payload.num_tabs;
437 if (pending_window_tabs <= 0) {
438 // Should always have at least 1 tab. Likely indicates corruption.
439 return;
440 }
441
442 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
443
444 current_window = new Window();
445 current_window->selected_tab_index = payload.selected_tab_index;
446 current_window->timestamp =
447 base::Time::FromInternalValue(payload.timestamp);
448 entries.push_back(current_window);
449 id_to_entry[payload.window_id] = current_window;
450 break;
451 }
452
453 case kCommandSelectedNavigationInTab: {
454 SelectedNavigationInTabPayload2 payload;
455 if (!command.GetPayload(&payload, sizeof(payload))) {
456 SelectedNavigationInTabPayload old_payload;
457 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
458 return;
459 payload.id = old_payload.id;
460 payload.index = old_payload.index;
461 // Since we don't have a time use time 0 which is used to mark as an
462 // unknown timestamp.
463 payload.timestamp = 0;
464 }
465
466 if (pending_window_tabs > 0) {
467 if (!current_window) {
468 // We should have created a window already.
469 NOTREACHED();
470 return;
471 }
472 current_window->tabs.resize(current_window->tabs.size() + 1);
473 current_tab = &(current_window->tabs.back());
474 if (--pending_window_tabs == 0)
475 current_window = NULL;
476 } else {
477 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
478 current_tab = new Tab();
479 id_to_entry[payload.id] = current_tab;
480 current_tab->timestamp =
481 base::Time::FromInternalValue(payload.timestamp);
482 entries.push_back(current_tab);
483 }
484 current_tab->current_navigation_index = payload.index;
485 break;
486 }
487
488 case kCommandUpdateTabNavigation: {
489 if (!current_tab) {
490 // Should be in a tab when we get this.
491 return;
492 }
493 current_tab->navigations.resize(current_tab->navigations.size() + 1);
494 SessionID::id_type tab_id;
495 if (!RestoreUpdateTabNavigationCommand(
496 command, &current_tab->navigations.back(), &tab_id)) {
497 return;
498 }
499 break;
500 }
501
502 case kCommandPinnedState: {
503 if (!current_tab) {
504 // Should be in a tab when we get this.
505 return;
506 }
507 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
508 // tab is pinned.
509 current_tab->pinned = true;
510 break;
511 }
512
513 case kCommandSetWindowAppName: {
514 if (!current_window) {
515 // We should have created a window already.
516 NOTREACHED();
517 return;
518 }
519
520 SessionID::id_type window_id;
521 std::string app_name;
522 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
523 return;
524
525 current_window->app_name.swap(app_name);
526 break;
527 }
528
529 case kCommandSetExtensionAppID: {
530 if (!current_tab) {
531 // Should be in a tab when we get this.
532 return;
533 }
534 SessionID::id_type tab_id;
535 std::string extension_app_id;
536 if (!RestoreSetTabExtensionAppIDCommand(command, &tab_id,
537 &extension_app_id)) {
538 return;
539 }
540 current_tab->extension_app_id.swap(extension_app_id);
541 break;
542 }
543
544 case kCommandSetTabUserAgentOverride: {
545 if (!current_tab) {
546 // Should be in a tab when we get this.
547 return;
548 }
549 SessionID::id_type tab_id;
550 std::string user_agent_override;
551 if (!RestoreSetTabUserAgentOverrideCommand(command, &tab_id,
552 &user_agent_override)) {
553 return;
554 }
555 current_tab->user_agent_override.swap(user_agent_override);
556 break;
557 }
558
559 default:
560 // Unknown type, usually indicates corruption of file. Ignore it.
561 return;
562 }
563 }
564
565 // If there was corruption some of the entries won't be valid.
566 ValidateAndDeleteEmptyEntries(&(entries.get()));
567
568 loaded_entries->swap(entries.get());
569 }
570
571 void PersistentTabRestoreService::CreateEntriesFromWindows(
572 std::vector<SessionWindow*>* windows,
573 std::vector<Entry*>* entries) {
574 for (size_t i = 0; i < windows->size(); ++i) {
575 scoped_ptr<Window> window(new Window());
576 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
577 entries->push_back(window.release());
578 }
579 }
580
581 void PersistentTabRestoreService::RemoveEntryByID(
582 SessionID::id_type id,
583 IDToEntry* id_to_entry,
584 std::vector<TabRestoreService::Entry*>* entries) {
585 // Look for the entry in the map. If it is present, erase it from both
586 // collections and return.
587 IDToEntry::iterator i = id_to_entry->find(id);
588 if (i != id_to_entry->end()) {
589 entries->erase(std::find(entries->begin(), entries->end(), i->second));
590 delete i->second;
591 id_to_entry->erase(i);
592 return;
593 }
594
595 // Otherwise, loop over all items in the map and see if any of the Windows
596 // have Tabs with the |id|.
597 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
598 ++i) {
599 if (i->second->type == TabRestoreService::WINDOW) {
600 TabRestoreService::Window* window =
601 static_cast<TabRestoreService::Window*>(i->second);
602 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
603 for ( ; j != window->tabs.end(); ++j) {
604 // If the ID matches one of this window's tabs, remove it from the list.
605 if ((*j).id == id) {
606 window->tabs.erase(j);
607 return;
608 }
609 }
610 }
611 }
612 }
613
614 // static
615 void PersistentTabRestoreService::ValidateAndDeleteEmptyEntries(
616 std::vector<Entry*>* entries) {
617 std::vector<Entry*> valid_entries;
618 std::vector<Entry*> invalid_entries;
619
620 // Iterate from the back so that we keep the most recently closed entries.
621 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
622 i != entries->rend(); ++i) {
623 if (ValidateEntry(*i))
624 valid_entries.push_back(*i);
625 else
626 invalid_entries.push_back(*i);
627 }
628 // NOTE: at this point the entries are ordered with newest at the front.
629 entries->swap(valid_entries);
630
631 // Delete the remaining entries.
632 STLDeleteElements(&invalid_entries);
633 }
634
635 void PersistentTabRestoreService::LoadTabsFromLastSession() {
636 if (load_state_ != NOT_LOADED || entries().size() == kMaxEntries)
637 return;
638
639 #if !defined(ENABLE_SESSION_SERVICE)
640 // If sessions are not stored in the SessionService, default to
641 // |LOADED_LAST_SESSION| state.
642 load_state_ = LOADING | LOADED_LAST_SESSION;
643 #else
644 load_state_ = LOADING;
645
646 SessionService* session_service =
647 SessionServiceFactory::GetForProfile(profile_);
648 Profile::ExitType exit_type = profile()->GetLastSessionExitType();
649 if (!profile()->restored_last_session() && session_service &&
650 (exit_type == Profile::EXIT_CRASHED ||
651 exit_type == Profile::EXIT_SESSION_ENDED)) {
652 // The previous session crashed and wasn't restored, or was a forced
653 // shutdown. Both of which won't have notified us of the browser close so
654 // that we need to load the windows from session service (which will have
655 // saved them).
656 session_service->GetLastSession(
657 &crash_consumer_,
658 base::Bind(&PersistentTabRestoreService::OnGotPreviousSession,
659 base::Unretained(this)));
660 } else {
661 load_state_ |= LOADED_LAST_SESSION;
662 }
663 #endif
664
665 // Request the tabs closed in the last session. If the last session crashed,
666 // this won't contain the tabs/window that were open at the point of the
667 // crash (the call to GetLastSession above requests those).
668 ScheduleGetLastSessionCommands(
669 new InternalGetCommandsRequest(
670 base::Bind(&PersistentTabRestoreService::OnGotLastSessionCommands,
671 base::Unretained(this))),
672 &load_consumer_);
673 }
674
675 void PersistentTabRestoreService::LoadStateChanged() {
676 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
677 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
678 // Still waiting on previous session or previous tabs.
679 return;
680 }
681
682 // We're done loading.
683 load_state_ ^= LOADING;
684
685 const size_t entries_size = entries().size();
686 if (staging_entries_.empty() || entries_size >= kMaxEntries) {
687 STLDeleteElements(&staging_entries_);
688 return;
689 }
690
691 if (staging_entries_.size() + entries_size > kMaxEntries) {
692 // If we add all the staged entries we'll end up with more than
693 // kMaxEntries. Delete entries such that we only end up with
694 // at most kMaxEntries.
695 int surplus = kMaxEntries - entries_size;
696 CHECK_LE(0, surplus);
697 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
698 STLDeleteContainerPointers(
699 staging_entries_.begin() + (kMaxEntries - entries_size),
700 staging_entries_.end());
701 staging_entries_.erase(
702 staging_entries_.begin() + (kMaxEntries - entries_size),
703 staging_entries_.end());
704 }
705
706 // And add them.
707 for (size_t i = 0; i < staging_entries_.size(); ++i) {
708 staging_entries_[i]->from_last_session = true;
709 AddEntry(staging_entries_[i], false, false);
710 }
711
712 // AddEntry takes ownership of the entry, need to clear out entries so that
713 // it doesn't delete them.
714 staging_entries_.clear();
715
716 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
717 // correctly write out the entries when Save is invoked (Save starts from
718 // the front, not the end and we just added the entries to the end).
719 entries_to_write_ = staging_entries_.size();
720
721 PruneEntries();
722 NotifyTabsChanged();
723 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698