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.
5 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
7 #include <cstring> // memcpy
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 "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/base_session_service.h"
22 #include "chrome/browser/sessions/base_session_service_commands.h"
23 #include "chrome/browser/sessions/base_session_service_delegate_impl.h"
24 #include "chrome/browser/sessions/session_command.h"
25 #include "chrome/browser/sessions/session_service_factory.h"
26 #include "chrome/browser/sessions/tab_restore_service_factory.h"
27 #include "content/public/browser/session_storage_namespace.h"
31 // Only written if the tab is pinned.
32 typedef bool PinnedStatePayload;
34 typedef int32 RestoredEntryPayload;
36 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
38 // Payload used for the start of a tab close. This is the old struct that is
39 // used for backwards compat when it comes to reading the session files.
40 struct SelectedNavigationInTabPayload {
41 SessionID::id_type id;
45 // Payload used for the start of a window close. This is the old struct that is
46 // used for backwards compat when it comes to reading the session files. This
47 // struct must be POD, because we memset the contents.
48 struct WindowPayload {
49 SessionID::id_type window_id;
50 int32 selected_tab_index;
54 // Payload used for the start of a window close. This struct must be POD,
55 // because we memset the contents.
56 struct WindowPayload2 : WindowPayload {
60 // Payload used for the start of a tab close.
61 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
65 // Used to indicate what has loaded.
67 // Indicates we haven't loaded anything.
70 // Indicates we've asked for the last sessions and tabs but haven't gotten the
74 // Indicates we finished loading the last tabs (but not necessarily the last
76 LOADED_LAST_TABS = 1 << 3,
78 // Indicates we finished loading the last session (but not necessarily the
80 LOADED_LAST_SESSION = 1 << 4
83 // Identifier for commands written to file. The ordering in the file is as
85 // . When the user closes a tab a command of type
86 // kCommandSelectedNavigationInTab is written identifying the tab and
87 // the selected index, then a kCommandPinnedState command if the tab was
88 // pinned and kCommandSetExtensionAppID if the tab has an app id and
89 // the user agent override if it was using one. This is
90 // followed by any number of kCommandUpdateTabNavigation commands (1 per
92 // . When the user closes a window a kCommandSelectedNavigationInTab command
93 // is written out and followed by n tab closed sequences (as previoulsy
95 // . When the user restores an entry a command of type kCommandRestoredEntry
97 const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
98 const SessionCommand::id_type kCommandRestoredEntry = 2;
99 const SessionCommand::id_type kCommandWindow = 3;
100 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
101 const SessionCommand::id_type kCommandPinnedState = 5;
102 const SessionCommand::id_type kCommandSetExtensionAppID = 6;
103 const SessionCommand::id_type kCommandSetWindowAppName = 7;
104 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
106 // Number of entries (not commands) before we clobber the file and write
108 const int kEntriesPerReset = 40;
110 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries;
114 // PersistentTabRestoreService::Delegate ---------------------------------------
116 // This restore service will create and own a BaseSessionService and implement
117 // the required BaseSessionServiceDelegateImpl.
118 class PersistentTabRestoreService::Delegate
119 : public BaseSessionServiceDelegateImpl,
120 public TabRestoreServiceHelper::Observer {
122 explicit Delegate(Profile* profile);
124 ~Delegate() override;
126 // BaseSessionServiceDelegateImpl:
127 void OnWillSaveCommands() override;
129 // TabRestoreServiceHelper::Observer:
130 void OnClearEntries() override;
131 void OnRestoreEntryById(SessionID::id_type id,
132 Entries::const_iterator entry_iterator) override;
133 void OnAddEntry() override;
135 void set_tab_restore_service_helper(
136 TabRestoreServiceHelper* tab_restore_service_helper) {
137 tab_restore_service_helper_ = tab_restore_service_helper;
140 void LoadTabsFromLastSession();
142 void DeleteLastSession();
144 bool IsLoaded() const;
146 // Creates and add entries to |entries| for each of the windows in |windows|.
147 static void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows,
148 std::vector<Entry*>* entries);
152 // Schedules the commands for a window close.
153 void ScheduleCommandsForWindow(const Window& window);
155 // Schedules the commands for a tab close. |selected_index| gives the index of
156 // the selected navigation.
157 void ScheduleCommandsForTab(const Tab& tab, int selected_index);
159 // Creates a window close command.
160 static scoped_ptr<SessionCommand> CreateWindowCommand(SessionID::id_type id,
161 int selected_tab_index,
163 base::Time timestamp);
165 // Creates a tab close command.
166 static scoped_ptr<SessionCommand> CreateSelectedNavigationInTabCommand(
167 SessionID::id_type tab_id,
169 base::Time timestamp);
171 // Creates a restore command.
172 static scoped_ptr<SessionCommand> CreateRestoredEntryCommand(
173 SessionID::id_type entry_id);
175 // Returns the index to persist as the selected index. This is the same as
176 // |tab.current_navigation_index| unless the entry at
177 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
178 // valid navigation to persist.
179 int GetSelectedNavigationIndexToPersist(const Tab& tab);
181 // Invoked when we've loaded the session commands that identify the previously
182 // closed tabs. This creates entries, adds them to staging_entries_, and
183 // invokes LoadState.
184 void OnGotLastSessionCommands(ScopedVector<SessionCommand> commands);
186 // Populates |loaded_entries| with Entries from |commands|.
187 void CreateEntriesFromCommands(const std::vector<SessionCommand*>& commands,
188 std::vector<Entry*>* loaded_entries);
190 // Validates all entries in |entries|, deleting any with no navigations. This
191 // also deletes any entries beyond the max number of entries we can hold.
192 static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
194 // Callback from BaseSessionService when we've received the windows from the
195 // previous session. This creates and add entries to |staging_entries_| and
196 // invokes LoadStateChanged. |ignored_active_window| is ignored because we
197 // don't need to restore activation.
198 void OnGotPreviousSession(ScopedVector<SessionWindow> windows,
199 SessionID::id_type ignored_active_window);
201 // Converts a SessionWindow into a Window, returning true on success. We use 0
202 // as the timestamp here since we do not know when the window/tab was closed.
203 static bool ConvertSessionWindowToWindow(SessionWindow* session_window,
206 // Invoked when previous tabs or session is loaded. If both have finished
207 // loading the entries in |staging_entries_| are added to entries and
208 // observers are notified.
209 void LoadStateChanged();
211 // If |id_to_entry| contains an entry for |id| the corresponding entry is
212 // deleted and removed from both |id_to_entry| and |entries|. This is used
213 // when creating entries from the backend file.
214 void RemoveEntryByID(SessionID::id_type id,
215 IDToEntry* id_to_entry,
216 std::vector<TabRestoreService::Entry*>* entries);
219 scoped_ptr<BaseSessionService> base_session_service_;
221 // The associated profile.
224 TabRestoreServiceHelper* tab_restore_service_helper_;
226 // The number of entries to write.
227 int entries_to_write_;
229 // Number of entries we've written.
230 int entries_written_;
232 // Whether we've loaded the last session.
235 // Results from previously closed tabs/sessions is first added here. When the
236 // results from both us and the session restore service have finished loading
237 // LoadStateChanged is invoked, which adds these entries to entries_.
238 ScopedVector<Entry> staging_entries_;
240 // Used when loading previous tabs/session and open tabs/session.
241 base::CancelableTaskTracker cancelable_task_tracker_;
243 DISALLOW_COPY_AND_ASSIGN(Delegate);
246 PersistentTabRestoreService::Delegate::Delegate(Profile* profile)
247 : BaseSessionServiceDelegateImpl(true),
248 base_session_service_(
249 new BaseSessionService(BaseSessionService::TAB_RESTORE,
253 tab_restore_service_helper_(NULL),
254 entries_to_write_(0),
256 load_state_(NOT_LOADED) {
257 // We should never be created when incognito.
258 DCHECK(!profile->IsOffTheRecord());
261 PersistentTabRestoreService::Delegate::~Delegate() {}
263 void PersistentTabRestoreService::Delegate::OnWillSaveCommands() {
264 const Entries& entries = tab_restore_service_helper_->entries();
265 int to_write_count = std::min(entries_to_write_,
266 static_cast<int>(entries.size()));
267 entries_to_write_ = 0;
268 if (entries_written_ + to_write_count > kEntriesPerReset) {
269 to_write_count = entries.size();
270 base_session_service_->set_pending_reset(true);
272 if (to_write_count) {
273 // Write the to_write_count most recently added entries out. The most
274 // recently added entry is at the front, so we use a reverse iterator to
275 // write in the order the entries were added.
276 Entries::const_reverse_iterator i = entries.rbegin();
277 DCHECK(static_cast<size_t>(to_write_count) <= entries.size());
278 std::advance(i, entries.size() - static_cast<int>(to_write_count));
279 for (; i != entries.rend(); ++i) {
281 if (entry->type == TAB) {
282 Tab* tab = static_cast<Tab*>(entry);
283 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
284 if (selected_index != -1)
285 ScheduleCommandsForTab(*tab, selected_index);
287 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
292 if (base_session_service_->pending_reset())
293 entries_written_ = 0;
296 void PersistentTabRestoreService::Delegate::OnClearEntries() {
297 // Mark all the tabs as closed so that we don't attempt to restore them.
298 const Entries& entries = tab_restore_service_helper_->entries();
299 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
300 base_session_service_->ScheduleCommand(
301 CreateRestoredEntryCommand((*i)->id).Pass());
303 entries_to_write_ = 0;
305 // Schedule a pending reset so that we nuke the file on next write.
306 base_session_service_->set_pending_reset(true);
308 // Schedule a command, otherwise if there are no pending commands Save does
310 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(1).Pass());
313 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
314 SessionID::id_type id,
315 Entries::const_iterator entry_iterator) {
317 const Entries& entries = tab_restore_service_helper_->entries();
318 for (Entries::const_iterator j = entries.begin();
319 j != entry_iterator && j != entries.end();
321 if (static_cast<int>(index) < entries_to_write_)
324 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(id).Pass());
327 void PersistentTabRestoreService::Delegate::OnAddEntry() {
328 // Start the save timer, when it fires we'll generate the commands.
329 base_session_service_->StartSaveTimer();
333 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
334 if (load_state_ != NOT_LOADED)
337 if (tab_restore_service_helper_->entries().size() == kMaxEntries) {
338 // We already have the max number of entries we can take. There is no point
339 // in attempting to load since we'll just drop the results. Skip to loaded.
340 load_state_ = (LOADING | LOADED_LAST_SESSION | LOADED_LAST_TABS);
345 #if !defined(ENABLE_SESSION_SERVICE)
346 // If sessions are not stored in the SessionService, default to
347 // |LOADED_LAST_SESSION| state.
348 load_state_ = LOADING | LOADED_LAST_SESSION;
350 load_state_ = LOADING;
352 SessionService* session_service =
353 SessionServiceFactory::GetForProfile(profile_);
354 Profile::ExitType exit_type = profile_->GetLastSessionExitType();
355 if (!profile_->restored_last_session() && session_service &&
356 (exit_type == Profile::EXIT_CRASHED ||
357 exit_type == Profile::EXIT_SESSION_ENDED)) {
358 // The previous session crashed and wasn't restored, or was a forced
359 // shutdown. Both of which won't have notified us of the browser close so
360 // that we need to load the windows from session service (which will have
362 session_service->GetLastSession(
363 base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)),
364 &cancelable_task_tracker_);
366 load_state_ |= LOADED_LAST_SESSION;
370 // Request the tabs closed in the last session. If the last session crashed,
371 // this won't contain the tabs/window that were open at the point of the
372 // crash (the call to GetLastSession above requests those).
373 base_session_service_->ScheduleGetLastSessionCommands(
374 base::Bind(&Delegate::OnGotLastSessionCommands, base::Unretained(this)),
375 &cancelable_task_tracker_);
378 void PersistentTabRestoreService::Delegate::DeleteLastSession() {
379 base_session_service_->DeleteLastSession();
382 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
383 return !(load_state_ & (NOT_LOADED | LOADING));
387 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
388 std::vector<SessionWindow*>* windows,
389 std::vector<Entry*>* entries) {
390 for (size_t i = 0; i < windows->size(); ++i) {
391 scoped_ptr<Window> window(new Window());
392 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
393 entries->push_back(window.release());
397 void PersistentTabRestoreService::Delegate::Shutdown() {
398 base_session_service_->Save();
401 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
402 const Window& window) {
403 DCHECK(!window.tabs.empty());
404 int selected_tab = window.selected_tab_index;
405 int valid_tab_count = 0;
406 int real_selected_tab = selected_tab;
407 for (size_t i = 0; i < window.tabs.size(); ++i) {
408 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
410 } else if (static_cast<int>(i) < selected_tab) {
414 if (valid_tab_count == 0)
415 return; // No tabs to persist.
417 base_session_service_->ScheduleCommand(
418 CreateWindowCommand(window.id,
419 std::min(real_selected_tab, valid_tab_count - 1),
421 window.timestamp).Pass());
423 if (!window.app_name.empty()) {
424 base_session_service_->ScheduleCommand(
425 CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
427 window.app_name).Pass());
430 for (size_t i = 0; i < window.tabs.size(); ++i) {
431 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
432 if (selected_index != -1)
433 ScheduleCommandsForTab(window.tabs[i], selected_index);
437 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
439 int selected_index) {
440 const std::vector<sessions::SerializedNavigationEntry>& navigations =
442 int max_index = static_cast<int>(navigations.size());
444 // Determine the first navigation we'll persist.
445 int valid_count_before_selected = 0;
446 int first_index_to_persist = selected_index;
447 for (int i = selected_index - 1; i >= 0 &&
448 valid_count_before_selected < gMaxPersistNavigationCount; --i) {
449 if (ShouldTrackEntry(navigations[i].virtual_url())) {
450 first_index_to_persist = i;
451 valid_count_before_selected++;
455 // Write the command that identifies the selected tab.
456 base_session_service_->ScheduleCommand(
457 CreateSelectedNavigationInTabCommand(tab.id,
458 valid_count_before_selected,
459 tab.timestamp).Pass());
462 PinnedStatePayload payload = true;
463 scoped_ptr<SessionCommand> command(
464 new SessionCommand(kCommandPinnedState, sizeof(payload)));
465 memcpy(command->contents(), &payload, sizeof(payload));
466 base_session_service_->ScheduleCommand(command.Pass());
469 if (!tab.extension_app_id.empty()) {
470 base_session_service_->ScheduleCommand(
471 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID,
473 tab.extension_app_id).Pass());
476 if (!tab.user_agent_override.empty()) {
477 base_session_service_->ScheduleCommand(
478 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
480 tab.user_agent_override).Pass());
483 // Then write the navigations.
484 for (int i = first_index_to_persist, wrote_count = 0;
485 wrote_count < 2 * gMaxPersistNavigationCount && i < max_index; ++i) {
486 if (ShouldTrackEntry(navigations[i].virtual_url())) {
487 base_session_service_->ScheduleCommand(
488 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
496 scoped_ptr<SessionCommand>
497 PersistentTabRestoreService::Delegate::CreateWindowCommand(
498 SessionID::id_type id,
499 int selected_tab_index,
501 base::Time timestamp) {
502 WindowPayload2 payload;
503 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
504 // uninitialized memory in the struct.
505 memset(&payload, 0, sizeof(payload));
506 payload.window_id = id;
507 payload.selected_tab_index = selected_tab_index;
508 payload.num_tabs = num_tabs;
509 payload.timestamp = timestamp.ToInternalValue();
511 scoped_ptr<SessionCommand> command(
512 new SessionCommand(kCommandWindow, sizeof(payload)));
513 memcpy(command->contents(), &payload, sizeof(payload));
518 scoped_ptr<SessionCommand>
519 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
520 SessionID::id_type tab_id,
522 base::Time timestamp) {
523 SelectedNavigationInTabPayload2 payload;
525 payload.index = index;
526 payload.timestamp = timestamp.ToInternalValue();
527 scoped_ptr<SessionCommand> command(
528 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)));
529 memcpy(command->contents(), &payload, sizeof(payload));
534 scoped_ptr<SessionCommand>
535 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
536 SessionID::id_type entry_id) {
537 RestoredEntryPayload payload = entry_id;
538 scoped_ptr<SessionCommand> command(
539 new SessionCommand(kCommandRestoredEntry, sizeof(payload)));
540 memcpy(command->contents(), &payload, sizeof(payload));
544 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
546 const std::vector<sessions::SerializedNavigationEntry>& navigations =
548 int selected_index = tab.current_navigation_index;
549 int max_index = static_cast<int>(navigations.size());
551 // Find the first navigation to persist. We won't persist the selected
552 // navigation if ShouldTrackEntry returns false.
553 while (selected_index >= 0 &&
554 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
558 if (selected_index != -1)
559 return selected_index;
561 // Couldn't find a navigation to persist going back, go forward.
562 selected_index = tab.current_navigation_index + 1;
563 while (selected_index < max_index &&
564 !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
568 return (selected_index == max_index) ? -1 : selected_index;
571 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
572 ScopedVector<SessionCommand> commands) {
573 std::vector<Entry*> entries;
574 CreateEntriesFromCommands(commands.get(), &entries);
575 // Closed tabs always go to the end.
576 staging_entries_.insert(staging_entries_.end(), entries.begin(),
578 load_state_ |= LOADED_LAST_TABS;
582 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
583 const std::vector<SessionCommand*>& commands,
584 std::vector<Entry*>* loaded_entries) {
585 if (tab_restore_service_helper_->entries().size() == kMaxEntries)
588 // Iterate through the commands populating entries and id_to_entry.
589 ScopedVector<Entry> entries;
590 IDToEntry id_to_entry;
591 // If non-null we're processing the navigations of this tab.
592 Tab* current_tab = NULL;
593 // If non-null we're processing the tabs of this window.
594 Window* current_window = NULL;
595 // If > 0, we've gotten a window command but not all the tabs yet.
596 int pending_window_tabs = 0;
597 for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
598 i != commands.end(); ++i) {
599 const SessionCommand& command = *(*i);
600 switch (command.id()) {
601 case kCommandRestoredEntry: {
602 if (pending_window_tabs > 0) {
603 // Should never receive a restored command while waiting for all the
609 current_window = NULL;
611 RestoredEntryPayload payload;
612 if (!command.GetPayload(&payload, sizeof(payload)))
614 RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
618 case kCommandWindow: {
619 WindowPayload2 payload;
620 if (pending_window_tabs > 0) {
621 // Should never receive a window command while waiting for all the
626 // Try the new payload first
627 if (!command.GetPayload(&payload, sizeof(payload))) {
628 // then the old payload
629 WindowPayload old_payload;
630 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
633 // Copy the old payload data to the new payload.
634 payload.window_id = old_payload.window_id;
635 payload.selected_tab_index = old_payload.selected_tab_index;
636 payload.num_tabs = old_payload.num_tabs;
637 // Since we don't have a time use time 0 which is used to mark as an
638 // unknown timestamp.
639 payload.timestamp = 0;
642 pending_window_tabs = payload.num_tabs;
643 if (pending_window_tabs <= 0) {
644 // Should always have at least 1 tab. Likely indicates corruption.
648 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
650 current_window = new Window();
651 current_window->selected_tab_index = payload.selected_tab_index;
652 current_window->timestamp =
653 base::Time::FromInternalValue(payload.timestamp);
654 entries.push_back(current_window);
655 id_to_entry[payload.window_id] = current_window;
659 case kCommandSelectedNavigationInTab: {
660 SelectedNavigationInTabPayload2 payload;
661 if (!command.GetPayload(&payload, sizeof(payload))) {
662 SelectedNavigationInTabPayload old_payload;
663 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
665 payload.id = old_payload.id;
666 payload.index = old_payload.index;
667 // Since we don't have a time use time 0 which is used to mark as an
668 // unknown timestamp.
669 payload.timestamp = 0;
672 if (pending_window_tabs > 0) {
673 if (!current_window) {
674 // We should have created a window already.
678 current_window->tabs.resize(current_window->tabs.size() + 1);
679 current_tab = &(current_window->tabs.back());
680 if (--pending_window_tabs == 0)
681 current_window = NULL;
683 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
684 current_tab = new Tab();
685 id_to_entry[payload.id] = current_tab;
686 current_tab->timestamp =
687 base::Time::FromInternalValue(payload.timestamp);
688 entries.push_back(current_tab);
690 current_tab->current_navigation_index = payload.index;
694 case kCommandUpdateTabNavigation: {
696 // Should be in a tab when we get this.
699 current_tab->navigations.resize(current_tab->navigations.size() + 1);
700 SessionID::id_type tab_id;
701 if (!RestoreUpdateTabNavigationCommand(command,
702 ¤t_tab->navigations.back(),
709 case kCommandPinnedState: {
711 // Should be in a tab when we get this.
714 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
716 current_tab->pinned = true;
720 case kCommandSetWindowAppName: {
721 if (!current_window) {
722 // We should have created a window already.
727 SessionID::id_type window_id;
728 std::string app_name;
729 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
732 current_window->app_name.swap(app_name);
736 case kCommandSetExtensionAppID: {
738 // Should be in a tab when we get this.
741 SessionID::id_type tab_id;
742 std::string extension_app_id;
743 if (!RestoreSetTabExtensionAppIDCommand(command,
745 &extension_app_id)) {
748 current_tab->extension_app_id.swap(extension_app_id);
752 case kCommandSetTabUserAgentOverride: {
754 // Should be in a tab when we get this.
757 SessionID::id_type tab_id;
758 std::string user_agent_override;
759 if (!RestoreSetTabUserAgentOverrideCommand(command,
761 &user_agent_override)) {
764 current_tab->user_agent_override.swap(user_agent_override);
769 // Unknown type, usually indicates corruption of file. Ignore it.
774 // If there was corruption some of the entries won't be valid.
775 ValidateAndDeleteEmptyEntries(&(entries.get()));
777 loaded_entries->swap(entries.get());
781 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
782 std::vector<Entry*>* entries) {
783 std::vector<Entry*> valid_entries;
784 std::vector<Entry*> invalid_entries;
786 // Iterate from the back so that we keep the most recently closed entries.
787 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
788 i != entries->rend(); ++i) {
789 if (TabRestoreServiceHelper::ValidateEntry(*i))
790 valid_entries.push_back(*i);
792 invalid_entries.push_back(*i);
794 // NOTE: at this point the entries are ordered with newest at the front.
795 entries->swap(valid_entries);
797 // Delete the remaining entries.
798 STLDeleteElements(&invalid_entries);
801 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
802 ScopedVector<SessionWindow> windows,
803 SessionID::id_type ignored_active_window) {
804 std::vector<Entry*> entries;
805 CreateEntriesFromWindows(&windows.get(), &entries);
806 // Previous session tabs go first.
807 staging_entries_.insert(staging_entries_.begin(), entries.begin(),
809 load_state_ |= LOADED_LAST_SESSION;
813 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
814 SessionWindow* session_window,
816 for (size_t i = 0; i < session_window->tabs.size(); ++i) {
817 if (!session_window->tabs[i]->navigations.empty()) {
818 window->tabs.resize(window->tabs.size() + 1);
819 Tab& tab = window->tabs.back();
820 tab.pinned = session_window->tabs[i]->pinned;
821 tab.navigations.swap(session_window->tabs[i]->navigations);
822 tab.current_navigation_index =
823 session_window->tabs[i]->current_navigation_index;
824 tab.extension_app_id = session_window->tabs[i]->extension_app_id;
825 tab.timestamp = base::Time();
828 if (window->tabs.empty())
831 window->selected_tab_index =
832 std::min(session_window->selected_tab_index,
833 static_cast<int>(window->tabs.size() - 1));
834 window->timestamp = base::Time();
838 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
839 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
840 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
841 // Still waiting on previous session or previous tabs.
845 // We're done loading.
846 load_state_ ^= LOADING;
848 const Entries& entries = tab_restore_service_helper_->entries();
849 if (staging_entries_.empty() || entries.size() >= kMaxEntries) {
850 staging_entries_.clear();
851 tab_restore_service_helper_->NotifyLoaded();
855 if (staging_entries_.size() + entries.size() > kMaxEntries) {
856 // If we add all the staged entries we'll end up with more than
857 // kMaxEntries. Delete entries such that we only end up with at most
859 int surplus = kMaxEntries - entries.size();
860 CHECK_LE(0, surplus);
861 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
862 staging_entries_.erase(
863 staging_entries_.begin() + (kMaxEntries - entries.size()),
864 staging_entries_.end());
868 for (size_t i = 0; i < staging_entries_.size(); ++i) {
869 staging_entries_[i]->from_last_session = true;
870 tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false);
873 // AddEntry takes ownership of the entry, need to clear out entries so that
874 // it doesn't delete them.
875 staging_entries_.weak_clear();
877 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
878 // correctly write out the entries when Save is invoked (Save starts from
879 // the front, not the end and we just added the entries to the end).
880 entries_to_write_ = staging_entries_.size();
882 tab_restore_service_helper_->PruneEntries();
883 tab_restore_service_helper_->NotifyTabsChanged();
885 tab_restore_service_helper_->NotifyLoaded();
888 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
889 SessionID::id_type id,
890 IDToEntry* id_to_entry,
891 std::vector<TabRestoreService::Entry*>* entries) {
892 // Look for the entry in the map. If it is present, erase it from both
893 // collections and return.
894 IDToEntry::iterator i = id_to_entry->find(id);
895 if (i != id_to_entry->end()) {
896 entries->erase(std::find(entries->begin(), entries->end(), i->second));
898 id_to_entry->erase(i);
902 // Otherwise, loop over all items in the map and see if any of the Windows
903 // have Tabs with the |id|.
904 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
906 if (i->second->type == TabRestoreService::WINDOW) {
907 TabRestoreService::Window* window =
908 static_cast<TabRestoreService::Window*>(i->second);
909 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
910 for ( ; j != window->tabs.end(); ++j) {
911 // If the ID matches one of this window's tabs, remove it from the
914 window->tabs.erase(j);
922 // PersistentTabRestoreService -------------------------------------------------
924 PersistentTabRestoreService::PersistentTabRestoreService(
926 TimeFactory* time_factory)
927 : delegate_(new Delegate(profile)),
928 helper_(this, delegate_.get(), profile, time_factory) {
929 delegate_->set_tab_restore_service_helper(&helper_);
932 PersistentTabRestoreService::~PersistentTabRestoreService() {}
934 void PersistentTabRestoreService::AddObserver(
935 TabRestoreServiceObserver* observer) {
936 helper_.AddObserver(observer);
939 void PersistentTabRestoreService::RemoveObserver(
940 TabRestoreServiceObserver* observer) {
941 helper_.RemoveObserver(observer);
944 void PersistentTabRestoreService::CreateHistoricalTab(
945 content::WebContents* contents,
947 helper_.CreateHistoricalTab(contents, index);
950 void PersistentTabRestoreService::BrowserClosing(
951 TabRestoreServiceDelegate* delegate) {
952 helper_.BrowserClosing(delegate);
955 void PersistentTabRestoreService::BrowserClosed(
956 TabRestoreServiceDelegate* delegate) {
957 helper_.BrowserClosed(delegate);
960 void PersistentTabRestoreService::ClearEntries() {
961 helper_.ClearEntries();
964 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
965 return helper_.entries();
968 std::vector<content::WebContents*>
969 PersistentTabRestoreService::RestoreMostRecentEntry(
970 TabRestoreServiceDelegate* delegate,
971 chrome::HostDesktopType host_desktop_type) {
972 return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
975 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
976 SessionID::id_type id) {
977 return helper_.RemoveTabEntryById(id);
980 std::vector<content::WebContents*>
981 PersistentTabRestoreService::RestoreEntryById(
982 TabRestoreServiceDelegate* delegate,
983 SessionID::id_type id,
984 chrome::HostDesktopType host_desktop_type,
985 WindowOpenDisposition disposition) {
986 return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition);
989 bool PersistentTabRestoreService::IsLoaded() const {
990 return delegate_->IsLoaded();
993 void PersistentTabRestoreService::DeleteLastSession() {
994 return delegate_->DeleteLastSession();
997 void PersistentTabRestoreService::Shutdown() {
998 return delegate_->Shutdown();
1001 void PersistentTabRestoreService::LoadTabsFromLastSession() {
1002 delegate_->LoadTabsFromLastSession();
1005 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
1006 return &helper_.entries_;
1009 void PersistentTabRestoreService::PruneEntries() {
1010 helper_.PruneEntries();
1013 KeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor(
1014 content::BrowserContext* profile) const {
1015 return new PersistentTabRestoreService(static_cast<Profile*>(profile), NULL);