[sessions]: Componentize TabRestore code
[chromium-blink-merge.git] / components / sessions / core / persistent_tab_restore_service.cc
blobddf4e4994193ba15f3aff5577ef7f638bd0049a8
1 // Copyright 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.
5 #include "components/sessions/core/persistent_tab_restore_service.h"
7 #include <cstring> // memcpy
8 #include <vector>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/stl_util.h"
18 #include "base/task/cancelable_task_tracker.h"
19 #include "base/time/time.h"
20 #include "components/sessions/base_session_service.h"
21 #include "components/sessions/base_session_service_commands.h"
22 #include "components/sessions/base_session_service_delegate.h"
23 #include "components/sessions/core/session_constants.h"
24 #include "components/sessions/session_command.h"
26 using sessions::LiveTab;
28 namespace {
30 // Only written if the tab is pinned.
31 typedef bool PinnedStatePayload;
33 typedef int32 RestoredEntryPayload;
35 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
37 // Payload used for the start of a tab close. This is the old struct that is
38 // used for backwards compat when it comes to reading the session files.
39 struct SelectedNavigationInTabPayload {
40 SessionID::id_type id;
41 int32 index;
44 // Payload used for the start of a window close. This is the old struct that is
45 // used for backwards compat when it comes to reading the session files. This
46 // struct must be POD, because we memset the contents.
47 struct WindowPayload {
48 SessionID::id_type window_id;
49 int32 selected_tab_index;
50 int32 num_tabs;
53 // Payload used for the start of a window close. This struct must be POD,
54 // because we memset the contents.
55 struct WindowPayload2 : WindowPayload {
56 int64 timestamp;
59 // Payload used for the start of a tab close.
60 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
61 int64 timestamp;
64 // Used to indicate what has loaded.
65 enum LoadState {
66 // Indicates we haven't loaded anything.
67 NOT_LOADED = 1 << 0,
69 // Indicates we've asked for the last sessions and tabs but haven't gotten the
70 // result back yet.
71 LOADING = 1 << 2,
73 // Indicates we finished loading the last tabs (but not necessarily the last
74 // session).
75 LOADED_LAST_TABS = 1 << 3,
77 // Indicates we finished loading the last session (but not necessarily the
78 // last tabs).
79 LOADED_LAST_SESSION = 1 << 4
82 // Identifier for commands written to file. The ordering in the file is as
83 // follows:
84 // . When the user closes a tab a command of type
85 // kCommandSelectedNavigationInTab is written identifying the tab and
86 // the selected index, then a kCommandPinnedState command if the tab was
87 // pinned and kCommandSetExtensionAppID if the tab has an app id and
88 // the user agent override if it was using one. This is
89 // followed by any number of kCommandUpdateTabNavigation commands (1 per
90 // navigation entry).
91 // . When the user closes a window a kCommandSelectedNavigationInTab command
92 // is written out and followed by n tab closed sequences (as previoulsy
93 // described).
94 // . When the user restores an entry a command of type kCommandRestoredEntry
95 // is written.
96 const sessions::SessionCommand::id_type kCommandUpdateTabNavigation = 1;
97 const sessions::SessionCommand::id_type kCommandRestoredEntry = 2;
98 const sessions::SessionCommand::id_type kCommandWindow = 3;
99 const sessions::SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
100 const sessions::SessionCommand::id_type kCommandPinnedState = 5;
101 const sessions::SessionCommand::id_type kCommandSetExtensionAppID = 6;
102 const sessions::SessionCommand::id_type kCommandSetWindowAppName = 7;
103 const sessions::SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
105 // Number of entries (not commands) before we clobber the file and write
106 // everything.
107 const int kEntriesPerReset = 40;
109 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries;
111 } // namespace
113 // PersistentTabRestoreService::Delegate ---------------------------------------
115 // This restore service will create and own a BaseSessionService and implement
116 // the required sessions::BaseSessionServiceDelegate.
117 class PersistentTabRestoreService::Delegate
118 : public sessions::BaseSessionServiceDelegate,
119 public TabRestoreServiceHelper::Observer {
120 public:
121 explicit Delegate(sessions::TabRestoreServiceClient* client);
123 ~Delegate() override;
125 // sessions::BaseSessionServiceDelegate:
126 base::SequencedWorkerPool* GetBlockingPool() override;
127 bool ShouldUseDelayedSave() override;
128 void OnWillSaveCommands() override;
130 // TabRestoreServiceHelper::Observer:
131 void OnClearEntries() override;
132 void OnRestoreEntryById(SessionID::id_type id,
133 Entries::const_iterator entry_iterator) override;
134 void OnAddEntry() override;
136 void set_tab_restore_service_helper(
137 TabRestoreServiceHelper* tab_restore_service_helper) {
138 tab_restore_service_helper_ = tab_restore_service_helper;
141 void LoadTabsFromLastSession();
143 void DeleteLastSession();
145 bool IsLoaded() const;
147 // Creates and add entries to |entries| for each of the windows in |windows|.
148 static void CreateEntriesFromWindows(
149 std::vector<sessions::SessionWindow*>* windows,
150 std::vector<Entry*>* entries);
152 void Shutdown();
154 // Schedules the commands for a window close.
155 void ScheduleCommandsForWindow(const Window& window);
157 // Schedules the commands for a tab close. |selected_index| gives the index of
158 // the selected navigation.
159 void ScheduleCommandsForTab(const Tab& tab, int selected_index);
161 // Creates a window close command.
162 static scoped_ptr<sessions::SessionCommand> CreateWindowCommand(
163 SessionID::id_type id,
164 int selected_tab_index,
165 int num_tabs,
166 base::Time timestamp);
168 // Creates a tab close command.
169 static scoped_ptr<sessions::SessionCommand>
170 CreateSelectedNavigationInTabCommand(
171 SessionID::id_type tab_id,
172 int32 index,
173 base::Time timestamp);
175 // Creates a restore command.
176 static scoped_ptr<sessions::SessionCommand> CreateRestoredEntryCommand(
177 SessionID::id_type entry_id);
179 // Returns the index to persist as the selected index. This is the same as
180 // |tab.current_navigation_index| unless the entry at
181 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
182 // valid navigation to persist.
183 int GetSelectedNavigationIndexToPersist(const Tab& tab);
185 // Invoked when we've loaded the session commands that identify the previously
186 // closed tabs. This creates entries, adds them to staging_entries_, and
187 // invokes LoadState.
188 void OnGotLastSessionCommands(
189 ScopedVector<sessions::SessionCommand> commands);
191 // Populates |loaded_entries| with Entries from |commands|.
192 void CreateEntriesFromCommands(
193 const std::vector<sessions::SessionCommand*>& commands,
194 std::vector<Entry*>* loaded_entries);
196 // Validates all entries in |entries|, deleting any with no navigations. This
197 // also deletes any entries beyond the max number of entries we can hold.
198 static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
200 // Callback from BaseSessionService when we've received the windows from the
201 // previous session. This creates and add entries to |staging_entries_| and
202 // invokes LoadStateChanged. |ignored_active_window| is ignored because we
203 // don't need to restore activation.
204 void OnGotPreviousSession(ScopedVector<sessions::SessionWindow> windows,
205 SessionID::id_type ignored_active_window);
207 // Converts a SessionWindow into a Window, returning true on success. We use 0
208 // as the timestamp here since we do not know when the window/tab was closed.
209 static bool ConvertSessionWindowToWindow(
210 sessions::SessionWindow* session_window,
211 Window* window);
213 // Invoked when previous tabs or session is loaded. If both have finished
214 // loading the entries in |staging_entries_| are added to entries and
215 // observers are notified.
216 void LoadStateChanged();
218 // If |id_to_entry| contains an entry for |id| the corresponding entry is
219 // deleted and removed from both |id_to_entry| and |entries|. This is used
220 // when creating entries from the backend file.
221 void RemoveEntryByID(SessionID::id_type id,
222 IDToEntry* id_to_entry,
223 std::vector<TabRestoreService::Entry*>* entries);
225 private:
226 // The associated client.
227 sessions::TabRestoreServiceClient* client_;
229 scoped_ptr<sessions::BaseSessionService> base_session_service_;
231 TabRestoreServiceHelper* tab_restore_service_helper_;
233 // The number of entries to write.
234 int entries_to_write_;
236 // Number of entries we've written.
237 int entries_written_;
239 // Whether we've loaded the last session.
240 int load_state_;
242 // Results from previously closed tabs/sessions is first added here. When the
243 // results from both us and the session restore service have finished loading
244 // LoadStateChanged is invoked, which adds these entries to entries_.
245 ScopedVector<Entry> staging_entries_;
247 // Used when loading previous tabs/session and open tabs/session.
248 base::CancelableTaskTracker cancelable_task_tracker_;
250 DISALLOW_COPY_AND_ASSIGN(Delegate);
253 PersistentTabRestoreService::Delegate::Delegate(
254 sessions::TabRestoreServiceClient* client)
255 : client_(client),
256 base_session_service_(new sessions::BaseSessionService(
257 sessions::BaseSessionService::TAB_RESTORE,
258 client_->GetPathToSaveTo(),
259 this)),
260 tab_restore_service_helper_(NULL),
261 entries_to_write_(0),
262 entries_written_(0),
263 load_state_(NOT_LOADED) {}
265 PersistentTabRestoreService::Delegate::~Delegate() {}
267 base::SequencedWorkerPool*
268 PersistentTabRestoreService::Delegate::GetBlockingPool() {
269 return client_->GetBlockingPool();
272 bool PersistentTabRestoreService::Delegate::ShouldUseDelayedSave() {
273 return true;
276 void PersistentTabRestoreService::Delegate::OnWillSaveCommands() {
277 const Entries& entries = tab_restore_service_helper_->entries();
278 int to_write_count = std::min(entries_to_write_,
279 static_cast<int>(entries.size()));
280 entries_to_write_ = 0;
281 if (entries_written_ + to_write_count > kEntriesPerReset) {
282 to_write_count = entries.size();
283 base_session_service_->set_pending_reset(true);
285 if (to_write_count) {
286 // Write the to_write_count most recently added entries out. The most
287 // recently added entry is at the front, so we use a reverse iterator to
288 // write in the order the entries were added.
289 Entries::const_reverse_iterator i = entries.rbegin();
290 DCHECK(static_cast<size_t>(to_write_count) <= entries.size());
291 std::advance(i, entries.size() - static_cast<int>(to_write_count));
292 for (; i != entries.rend(); ++i) {
293 Entry* entry = *i;
294 if (entry->type == TAB) {
295 Tab* tab = static_cast<Tab*>(entry);
296 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
297 if (selected_index != -1)
298 ScheduleCommandsForTab(*tab, selected_index);
299 } else {
300 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
302 entries_written_++;
305 if (base_session_service_->pending_reset())
306 entries_written_ = 0;
309 void PersistentTabRestoreService::Delegate::OnClearEntries() {
310 // Mark all the tabs as closed so that we don't attempt to restore them.
311 const Entries& entries = tab_restore_service_helper_->entries();
312 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
313 base_session_service_->ScheduleCommand(
314 CreateRestoredEntryCommand((*i)->id).Pass());
316 entries_to_write_ = 0;
318 // Schedule a pending reset so that we nuke the file on next write.
319 base_session_service_->set_pending_reset(true);
321 // Schedule a command, otherwise if there are no pending commands Save does
322 // nothing.
323 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(1).Pass());
326 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
327 SessionID::id_type id,
328 Entries::const_iterator entry_iterator) {
329 size_t index = 0;
330 const Entries& entries = tab_restore_service_helper_->entries();
331 for (Entries::const_iterator j = entries.begin();
332 j != entry_iterator && j != entries.end();
333 ++j, ++index) {}
334 if (static_cast<int>(index) < entries_to_write_)
335 entries_to_write_--;
337 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(id).Pass());
340 void PersistentTabRestoreService::Delegate::OnAddEntry() {
341 // Start the save timer, when it fires we'll generate the commands.
342 base_session_service_->StartSaveTimer();
343 entries_to_write_++;
346 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
347 if (load_state_ != NOT_LOADED)
348 return;
350 if (tab_restore_service_helper_->entries().size() == kMaxEntries) {
351 // We already have the max number of entries we can take. There is no point
352 // in attempting to load since we'll just drop the results. Skip to loaded.
353 load_state_ = (LOADING | LOADED_LAST_SESSION | LOADED_LAST_TABS);
354 LoadStateChanged();
355 return;
358 load_state_ = LOADING;
359 if (client_->HasLastSession()) {
360 client_->GetLastSession(
361 base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)),
362 &cancelable_task_tracker_);
363 } else {
364 load_state_ |= LOADED_LAST_SESSION;
367 // Request the tabs closed in the last session. If the last session crashed,
368 // this won't contain the tabs/window that were open at the point of the
369 // crash (the call to GetLastSession above requests those).
370 base_session_service_->ScheduleGetLastSessionCommands(
371 base::Bind(&Delegate::OnGotLastSessionCommands, base::Unretained(this)),
372 &cancelable_task_tracker_);
375 void PersistentTabRestoreService::Delegate::DeleteLastSession() {
376 base_session_service_->DeleteLastSession();
379 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
380 return !(load_state_ & (NOT_LOADED | LOADING));
383 // static
384 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
385 std::vector<sessions::SessionWindow*>* windows,
386 std::vector<Entry*>* entries) {
387 for (size_t i = 0; i < windows->size(); ++i) {
388 scoped_ptr<Window> window(new Window());
389 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
390 entries->push_back(window.release());
394 void PersistentTabRestoreService::Delegate::Shutdown() {
395 base_session_service_->Save();
398 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
399 const Window& window) {
400 DCHECK(!window.tabs.empty());
401 int selected_tab = window.selected_tab_index;
402 int valid_tab_count = 0;
403 int real_selected_tab = selected_tab;
404 for (size_t i = 0; i < window.tabs.size(); ++i) {
405 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
406 valid_tab_count++;
407 } else if (static_cast<int>(i) < selected_tab) {
408 real_selected_tab--;
411 if (valid_tab_count == 0)
412 return; // No tabs to persist.
414 base_session_service_->ScheduleCommand(
415 CreateWindowCommand(window.id,
416 std::min(real_selected_tab, valid_tab_count - 1),
417 valid_tab_count,
418 window.timestamp).Pass());
420 if (!window.app_name.empty()) {
421 base_session_service_->ScheduleCommand(
422 sessions::CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
423 window.id,
424 window.app_name).Pass());
427 for (size_t i = 0; i < window.tabs.size(); ++i) {
428 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
429 if (selected_index != -1)
430 ScheduleCommandsForTab(window.tabs[i], selected_index);
434 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
435 const Tab& tab,
436 int selected_index) {
437 const std::vector<sessions::SerializedNavigationEntry>& navigations =
438 tab.navigations;
439 int max_index = static_cast<int>(navigations.size());
441 // Determine the first navigation we'll persist.
442 int valid_count_before_selected = 0;
443 int first_index_to_persist = selected_index;
444 for (int i = selected_index - 1;
445 i >= 0 &&
446 valid_count_before_selected < sessions::gMaxPersistNavigationCount;
447 --i) {
448 if (client_->ShouldTrackURLForRestore(navigations[i].virtual_url())) {
449 first_index_to_persist = i;
450 valid_count_before_selected++;
454 // Write the command that identifies the selected tab.
455 base_session_service_->ScheduleCommand(
456 CreateSelectedNavigationInTabCommand(tab.id,
457 valid_count_before_selected,
458 tab.timestamp).Pass());
460 if (tab.pinned) {
461 PinnedStatePayload payload = true;
462 scoped_ptr<sessions::SessionCommand> command(
463 new sessions::SessionCommand(kCommandPinnedState, sizeof(payload)));
464 memcpy(command->contents(), &payload, sizeof(payload));
465 base_session_service_->ScheduleCommand(command.Pass());
468 if (!tab.extension_app_id.empty()) {
469 base_session_service_->ScheduleCommand(
470 sessions::CreateSetTabExtensionAppIDCommand(
471 kCommandSetExtensionAppID,
472 tab.id,
473 tab.extension_app_id).Pass());
476 if (!tab.user_agent_override.empty()) {
477 base_session_service_->ScheduleCommand(
478 sessions::CreateSetTabUserAgentOverrideCommand(
479 kCommandSetTabUserAgentOverride,
480 tab.id,
481 tab.user_agent_override).Pass());
484 // Then write the navigations.
485 for (int i = first_index_to_persist, wrote_count = 0;
486 wrote_count < 2 * sessions::gMaxPersistNavigationCount && i < max_index;
487 ++i) {
488 if (client_->ShouldTrackURLForRestore(navigations[i].virtual_url())) {
489 base_session_service_->ScheduleCommand(
490 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
491 tab.id,
492 navigations[i]));
497 // static
498 scoped_ptr<sessions::SessionCommand>
499 PersistentTabRestoreService::Delegate::CreateWindowCommand(
500 SessionID::id_type id,
501 int selected_tab_index,
502 int num_tabs,
503 base::Time timestamp) {
504 WindowPayload2 payload;
505 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
506 // uninitialized memory in the struct.
507 memset(&payload, 0, sizeof(payload));
508 payload.window_id = id;
509 payload.selected_tab_index = selected_tab_index;
510 payload.num_tabs = num_tabs;
511 payload.timestamp = timestamp.ToInternalValue();
513 scoped_ptr<sessions::SessionCommand> command(
514 new sessions::SessionCommand(kCommandWindow, sizeof(payload)));
515 memcpy(command->contents(), &payload, sizeof(payload));
516 return command;
519 // static
520 scoped_ptr<sessions::SessionCommand>
521 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
522 SessionID::id_type tab_id,
523 int32 index,
524 base::Time timestamp) {
525 SelectedNavigationInTabPayload2 payload;
526 payload.id = tab_id;
527 payload.index = index;
528 payload.timestamp = timestamp.ToInternalValue();
529 scoped_ptr<sessions::SessionCommand> command(
530 new sessions::SessionCommand(
531 kCommandSelectedNavigationInTab, sizeof(payload)));
532 memcpy(command->contents(), &payload, sizeof(payload));
533 return command;
536 // static
537 scoped_ptr<sessions::SessionCommand>
538 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
539 SessionID::id_type entry_id) {
540 RestoredEntryPayload payload = entry_id;
541 scoped_ptr<sessions::SessionCommand> command(
542 new sessions::SessionCommand(kCommandRestoredEntry, sizeof(payload)));
543 memcpy(command->contents(), &payload, sizeof(payload));
544 return command;
547 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
548 const Tab& tab) {
549 const std::vector<sessions::SerializedNavigationEntry>& navigations =
550 tab.navigations;
551 int selected_index = tab.current_navigation_index;
552 int max_index = static_cast<int>(navigations.size());
554 // Find the first navigation to persist. We won't persist the selected
555 // navigation if client_->ShouldTrackURLForRestore returns false.
556 while (selected_index >= 0 &&
557 !client_->ShouldTrackURLForRestore(
558 navigations[selected_index].virtual_url())) {
559 selected_index--;
562 if (selected_index != -1)
563 return selected_index;
565 // Couldn't find a navigation to persist going back, go forward.
566 selected_index = tab.current_navigation_index + 1;
567 while (selected_index < max_index &&
568 !client_->ShouldTrackURLForRestore(
569 navigations[selected_index].virtual_url())) {
570 selected_index++;
573 return (selected_index == max_index) ? -1 : selected_index;
576 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
577 ScopedVector<sessions::SessionCommand> commands) {
578 std::vector<Entry*> entries;
579 CreateEntriesFromCommands(commands.get(), &entries);
580 // Closed tabs always go to the end.
581 staging_entries_.insert(staging_entries_.end(), entries.begin(),
582 entries.end());
583 load_state_ |= LOADED_LAST_TABS;
584 LoadStateChanged();
587 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
588 const std::vector<sessions::SessionCommand*>& commands,
589 std::vector<Entry*>* loaded_entries) {
590 if (tab_restore_service_helper_->entries().size() == kMaxEntries)
591 return;
593 // Iterate through the commands populating entries and id_to_entry.
594 ScopedVector<Entry> entries;
595 IDToEntry id_to_entry;
596 // If non-null we're processing the navigations of this tab.
597 Tab* current_tab = NULL;
598 // If non-null we're processing the tabs of this window.
599 Window* current_window = NULL;
600 // If > 0, we've gotten a window command but not all the tabs yet.
601 int pending_window_tabs = 0;
602 for (std::vector<sessions::SessionCommand*>::const_iterator i =
603 commands.begin(); i != commands.end(); ++i) {
604 const sessions::SessionCommand& command = *(*i);
605 switch (command.id()) {
606 case kCommandRestoredEntry: {
607 if (pending_window_tabs > 0) {
608 // Should never receive a restored command while waiting for all the
609 // tabs in a window.
610 return;
613 current_tab = NULL;
614 current_window = NULL;
616 RestoredEntryPayload payload;
617 if (!command.GetPayload(&payload, sizeof(payload)))
618 return;
619 RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
620 break;
623 case kCommandWindow: {
624 WindowPayload2 payload;
625 if (pending_window_tabs > 0) {
626 // Should never receive a window command while waiting for all the
627 // tabs in a window.
628 return;
631 // Try the new payload first
632 if (!command.GetPayload(&payload, sizeof(payload))) {
633 // then the old payload
634 WindowPayload old_payload;
635 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
636 return;
638 // Copy the old payload data to the new payload.
639 payload.window_id = old_payload.window_id;
640 payload.selected_tab_index = old_payload.selected_tab_index;
641 payload.num_tabs = old_payload.num_tabs;
642 // Since we don't have a time use time 0 which is used to mark as an
643 // unknown timestamp.
644 payload.timestamp = 0;
647 pending_window_tabs = payload.num_tabs;
648 if (pending_window_tabs <= 0) {
649 // Should always have at least 1 tab. Likely indicates corruption.
650 return;
653 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
655 current_window = new Window();
656 current_window->selected_tab_index = payload.selected_tab_index;
657 current_window->timestamp =
658 base::Time::FromInternalValue(payload.timestamp);
659 entries.push_back(current_window);
660 id_to_entry[payload.window_id] = current_window;
661 break;
664 case kCommandSelectedNavigationInTab: {
665 SelectedNavigationInTabPayload2 payload;
666 if (!command.GetPayload(&payload, sizeof(payload))) {
667 SelectedNavigationInTabPayload old_payload;
668 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
669 return;
670 payload.id = old_payload.id;
671 payload.index = old_payload.index;
672 // Since we don't have a time use time 0 which is used to mark as an
673 // unknown timestamp.
674 payload.timestamp = 0;
677 if (pending_window_tabs > 0) {
678 if (!current_window) {
679 // We should have created a window already.
680 NOTREACHED();
681 return;
683 current_window->tabs.resize(current_window->tabs.size() + 1);
684 current_tab = &(current_window->tabs.back());
685 if (--pending_window_tabs == 0)
686 current_window = NULL;
687 } else {
688 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
689 current_tab = new Tab();
690 id_to_entry[payload.id] = current_tab;
691 current_tab->timestamp =
692 base::Time::FromInternalValue(payload.timestamp);
693 entries.push_back(current_tab);
695 current_tab->current_navigation_index = payload.index;
696 break;
699 case kCommandUpdateTabNavigation: {
700 if (!current_tab) {
701 // Should be in a tab when we get this.
702 return;
704 current_tab->navigations.resize(current_tab->navigations.size() + 1);
705 SessionID::id_type tab_id;
706 if (!RestoreUpdateTabNavigationCommand(command,
707 &current_tab->navigations.back(),
708 &tab_id)) {
709 return;
711 break;
714 case kCommandPinnedState: {
715 if (!current_tab) {
716 // Should be in a tab when we get this.
717 return;
719 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
720 // tab is pinned.
721 current_tab->pinned = true;
722 break;
725 case kCommandSetWindowAppName: {
726 if (!current_window) {
727 // We should have created a window already.
728 NOTREACHED();
729 return;
732 SessionID::id_type window_id;
733 std::string app_name;
734 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
735 return;
737 current_window->app_name.swap(app_name);
738 break;
741 case kCommandSetExtensionAppID: {
742 if (!current_tab) {
743 // Should be in a tab when we get this.
744 return;
746 SessionID::id_type tab_id;
747 std::string extension_app_id;
748 if (!RestoreSetTabExtensionAppIDCommand(command,
749 &tab_id,
750 &extension_app_id)) {
751 return;
753 current_tab->extension_app_id.swap(extension_app_id);
754 break;
757 case kCommandSetTabUserAgentOverride: {
758 if (!current_tab) {
759 // Should be in a tab when we get this.
760 return;
762 SessionID::id_type tab_id;
763 std::string user_agent_override;
764 if (!RestoreSetTabUserAgentOverrideCommand(command,
765 &tab_id,
766 &user_agent_override)) {
767 return;
769 current_tab->user_agent_override.swap(user_agent_override);
770 break;
773 default:
774 // Unknown type, usually indicates corruption of file. Ignore it.
775 return;
779 // If there was corruption some of the entries won't be valid.
780 ValidateAndDeleteEmptyEntries(&(entries.get()));
782 loaded_entries->swap(entries.get());
785 // static
786 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
787 std::vector<Entry*>* entries) {
788 std::vector<Entry*> valid_entries;
789 std::vector<Entry*> invalid_entries;
791 // Iterate from the back so that we keep the most recently closed entries.
792 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
793 i != entries->rend(); ++i) {
794 if (TabRestoreServiceHelper::ValidateEntry(*i))
795 valid_entries.push_back(*i);
796 else
797 invalid_entries.push_back(*i);
799 // NOTE: at this point the entries are ordered with newest at the front.
800 entries->swap(valid_entries);
802 // Delete the remaining entries.
803 STLDeleteElements(&invalid_entries);
806 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
807 ScopedVector<sessions::SessionWindow> windows,
808 SessionID::id_type ignored_active_window) {
809 std::vector<Entry*> entries;
810 CreateEntriesFromWindows(&windows.get(), &entries);
811 // Previous session tabs go first.
812 staging_entries_.insert(staging_entries_.begin(), entries.begin(),
813 entries.end());
814 load_state_ |= LOADED_LAST_SESSION;
815 LoadStateChanged();
818 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
819 sessions::SessionWindow* session_window,
820 Window* window) {
821 for (size_t i = 0; i < session_window->tabs.size(); ++i) {
822 if (!session_window->tabs[i]->navigations.empty()) {
823 window->tabs.resize(window->tabs.size() + 1);
824 Tab& tab = window->tabs.back();
825 tab.pinned = session_window->tabs[i]->pinned;
826 tab.navigations.swap(session_window->tabs[i]->navigations);
827 tab.current_navigation_index =
828 session_window->tabs[i]->current_navigation_index;
829 tab.extension_app_id = session_window->tabs[i]->extension_app_id;
830 tab.timestamp = base::Time();
833 if (window->tabs.empty())
834 return false;
836 window->selected_tab_index =
837 std::min(session_window->selected_tab_index,
838 static_cast<int>(window->tabs.size() - 1));
839 window->timestamp = base::Time();
840 return true;
843 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
844 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
845 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
846 // Still waiting on previous session or previous tabs.
847 return;
850 // We're done loading.
851 load_state_ ^= LOADING;
853 const Entries& entries = tab_restore_service_helper_->entries();
854 if (staging_entries_.empty() || entries.size() >= kMaxEntries) {
855 staging_entries_.clear();
856 tab_restore_service_helper_->NotifyLoaded();
857 return;
860 if (staging_entries_.size() + entries.size() > kMaxEntries) {
861 // If we add all the staged entries we'll end up with more than
862 // kMaxEntries. Delete entries such that we only end up with at most
863 // kMaxEntries.
864 int surplus = kMaxEntries - entries.size();
865 CHECK_LE(0, surplus);
866 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
867 staging_entries_.erase(
868 staging_entries_.begin() + (kMaxEntries - entries.size()),
869 staging_entries_.end());
872 // And add them.
873 for (size_t i = 0; i < staging_entries_.size(); ++i) {
874 staging_entries_[i]->from_last_session = true;
875 tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false);
878 // AddEntry takes ownership of the entry, need to clear out entries so that
879 // it doesn't delete them.
880 staging_entries_.weak_clear();
882 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
883 // correctly write out the entries when Save is invoked (Save starts from
884 // the front, not the end and we just added the entries to the end).
885 entries_to_write_ = staging_entries_.size();
887 tab_restore_service_helper_->PruneEntries();
888 tab_restore_service_helper_->NotifyTabsChanged();
890 tab_restore_service_helper_->NotifyLoaded();
893 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
894 SessionID::id_type id,
895 IDToEntry* id_to_entry,
896 std::vector<TabRestoreService::Entry*>* entries) {
897 // Look for the entry in the map. If it is present, erase it from both
898 // collections and return.
899 IDToEntry::iterator i = id_to_entry->find(id);
900 if (i != id_to_entry->end()) {
901 entries->erase(std::find(entries->begin(), entries->end(), i->second));
902 delete i->second;
903 id_to_entry->erase(i);
904 return;
907 // Otherwise, loop over all items in the map and see if any of the Windows
908 // have Tabs with the |id|.
909 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
910 ++i) {
911 if (i->second->type == TabRestoreService::WINDOW) {
912 TabRestoreService::Window* window =
913 static_cast<TabRestoreService::Window*>(i->second);
914 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
915 for ( ; j != window->tabs.end(); ++j) {
916 // If the ID matches one of this window's tabs, remove it from the
917 // list.
918 if ((*j).id == id) {
919 window->tabs.erase(j);
920 return;
927 // PersistentTabRestoreService -------------------------------------------------
929 PersistentTabRestoreService::PersistentTabRestoreService(
930 scoped_ptr<sessions::TabRestoreServiceClient> client,
931 TimeFactory* time_factory)
932 : client_(client.Pass()),
933 delegate_(new Delegate(client_.get())),
934 helper_(this, delegate_.get(), client_.get(), time_factory) {
935 delegate_->set_tab_restore_service_helper(&helper_);
938 PersistentTabRestoreService::~PersistentTabRestoreService() {}
940 void PersistentTabRestoreService::AddObserver(
941 TabRestoreServiceObserver* observer) {
942 helper_.AddObserver(observer);
945 void PersistentTabRestoreService::RemoveObserver(
946 TabRestoreServiceObserver* observer) {
947 helper_.RemoveObserver(observer);
950 void PersistentTabRestoreService::CreateHistoricalTab(LiveTab* live_tab,
951 int index) {
952 helper_.CreateHistoricalTab(live_tab, index);
955 void PersistentTabRestoreService::BrowserClosing(
956 TabRestoreServiceDelegate* delegate) {
957 helper_.BrowserClosing(delegate);
960 void PersistentTabRestoreService::BrowserClosed(
961 TabRestoreServiceDelegate* delegate) {
962 helper_.BrowserClosed(delegate);
965 void PersistentTabRestoreService::ClearEntries() {
966 helper_.ClearEntries();
969 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
970 return helper_.entries();
973 std::vector<LiveTab*> PersistentTabRestoreService::RestoreMostRecentEntry(
974 TabRestoreServiceDelegate* delegate,
975 int host_desktop_type) {
976 return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
979 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
980 SessionID::id_type id) {
981 return helper_.RemoveTabEntryById(id);
984 std::vector<LiveTab*> PersistentTabRestoreService::RestoreEntryById(
985 TabRestoreServiceDelegate* delegate,
986 SessionID::id_type id,
987 int host_desktop_type,
988 WindowOpenDisposition disposition) {
989 return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition);
992 bool PersistentTabRestoreService::IsLoaded() const {
993 return delegate_->IsLoaded();
996 void PersistentTabRestoreService::DeleteLastSession() {
997 return delegate_->DeleteLastSession();
1000 void PersistentTabRestoreService::Shutdown() {
1001 return delegate_->Shutdown();
1004 void PersistentTabRestoreService::LoadTabsFromLastSession() {
1005 delegate_->LoadTabsFromLastSession();
1008 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
1009 return &helper_.entries_;
1012 void PersistentTabRestoreService::PruneEntries() {
1013 helper_.PruneEntries();