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/time/time.h"
19 #include "chrome/browser/common/cancelable_request.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/base_session_service.h"
22 #include "chrome/browser/sessions/session_command.h"
23 #include "chrome/browser/sessions/session_service.h"
24 #include "chrome/browser/sessions/session_service_factory.h"
25 #include "chrome/browser/sessions/tab_restore_service_factory.h"
26 #include "chrome/common/cancelable_task_tracker.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 // Implements the link between the tab restore service and the session backend.
117 class PersistentTabRestoreService::Delegate
118 : public BaseSessionService
,
119 public TabRestoreServiceHelper::Observer
{
121 explicit Delegate(Profile
* profile
);
125 // BaseSessionService:
126 virtual void Save() OVERRIDE
;
128 // TabRestoreServiceHelper::Observer:
129 virtual void OnClearEntries() OVERRIDE
;
130 virtual void OnRestoreEntryById(
131 SessionID::id_type id
,
132 Entries::const_iterator entry_iterator
) OVERRIDE
;
133 virtual 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 bool IsLoaded() const;
144 // Creates and add entries to |entries| for each of the windows in |windows|.
145 static void CreateEntriesFromWindows(std::vector
<SessionWindow
*>* windows
,
146 std::vector
<Entry
*>* entries
);
150 // Schedules the commands for a window close.
151 void ScheduleCommandsForWindow(const Window
& window
);
153 // Schedules the commands for a tab close. |selected_index| gives the index of
154 // the selected navigation.
155 void ScheduleCommandsForTab(const Tab
& tab
, int selected_index
);
157 // Creates a window close command.
158 static SessionCommand
* CreateWindowCommand(SessionID::id_type id
,
159 int selected_tab_index
,
161 base::Time timestamp
);
163 // Creates a tab close command.
164 static SessionCommand
* CreateSelectedNavigationInTabCommand(
165 SessionID::id_type tab_id
,
167 base::Time timestamp
);
169 // Creates a restore command.
170 static SessionCommand
* CreateRestoredEntryCommand(
171 SessionID::id_type entry_id
);
173 // Returns the index to persist as the selected index. This is the same as
174 // |tab.current_navigation_index| unless the entry at
175 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
176 // valid navigation to persist.
177 int GetSelectedNavigationIndexToPersist(const Tab
& tab
);
179 // Invoked when we've loaded the session commands that identify the previously
180 // closed tabs. This creates entries, adds them to staging_entries_, and
181 // invokes LoadState.
182 void OnGotLastSessionCommands(ScopedVector
<SessionCommand
> commands
);
184 // Populates |loaded_entries| with Entries from |commands|.
185 void CreateEntriesFromCommands(const std::vector
<SessionCommand
*>& commands
,
186 std::vector
<Entry
*>* loaded_entries
);
188 // Validates all entries in |entries|, deleting any with no navigations. This
189 // also deletes any entries beyond the max number of entries we can hold.
190 static void ValidateAndDeleteEmptyEntries(std::vector
<Entry
*>* entries
);
192 // Callback from SessionService when we've received the windows from the
193 // previous session. This creates and add entries to |staging_entries_| and
194 // invokes LoadStateChanged. |ignored_active_window| is ignored because we
195 // don't need to restore activation.
196 void OnGotPreviousSession(ScopedVector
<SessionWindow
> windows
,
197 SessionID::id_type ignored_active_window
);
199 // Converts a SessionWindow into a Window, returning true on success. We use 0
200 // as the timestamp here since we do not know when the window/tab was closed.
201 static bool ConvertSessionWindowToWindow(SessionWindow
* session_window
,
204 // Invoked when previous tabs or session is loaded. If both have finished
205 // loading the entries in |staging_entries_| are added to entries and
206 // observers are notified.
207 void LoadStateChanged();
209 // If |id_to_entry| contains an entry for |id| the corresponding entry is
210 // deleted and removed from both |id_to_entry| and |entries|. This is used
211 // when creating entries from the backend file.
212 void RemoveEntryByID(SessionID::id_type id
,
213 IDToEntry
* id_to_entry
,
214 std::vector
<TabRestoreService::Entry
*>* entries
);
217 TabRestoreServiceHelper
* tab_restore_service_helper_
;
219 // The number of entries to write.
220 int entries_to_write_
;
222 // Number of entries we've written.
223 int entries_written_
;
225 // Whether we've loaded the last session.
228 // Results from previously closed tabs/sessions is first added here. When the
229 // results from both us and the session restore service have finished loading
230 // LoadStateChanged is invoked, which adds these entries to entries_.
231 ScopedVector
<Entry
> staging_entries_
;
233 // Used when loading previous tabs/session and open tabs/session.
234 CancelableTaskTracker cancelable_task_tracker_
;
236 DISALLOW_COPY_AND_ASSIGN(Delegate
);
239 PersistentTabRestoreService::Delegate::Delegate(Profile
* profile
)
240 : BaseSessionService(BaseSessionService::TAB_RESTORE
, profile
,
242 tab_restore_service_helper_(NULL
),
243 entries_to_write_(0),
245 load_state_(NOT_LOADED
) {
248 PersistentTabRestoreService::Delegate::~Delegate() {}
250 void PersistentTabRestoreService::Delegate::Save() {
251 const Entries
& entries
= tab_restore_service_helper_
->entries();
252 int to_write_count
= std::min(entries_to_write_
,
253 static_cast<int>(entries
.size()));
254 entries_to_write_
= 0;
255 if (entries_written_
+ to_write_count
> kEntriesPerReset
) {
256 to_write_count
= entries
.size();
257 set_pending_reset(true);
259 if (to_write_count
) {
260 // Write the to_write_count most recently added entries out. The most
261 // recently added entry is at the front, so we use a reverse iterator to
262 // write in the order the entries were added.
263 Entries::const_reverse_iterator i
= entries
.rbegin();
264 DCHECK(static_cast<size_t>(to_write_count
) <= entries
.size());
265 std::advance(i
, entries
.size() - static_cast<int>(to_write_count
));
266 for (; i
!= entries
.rend(); ++i
) {
268 if (entry
->type
== TAB
) {
269 Tab
* tab
= static_cast<Tab
*>(entry
);
270 int selected_index
= GetSelectedNavigationIndexToPersist(*tab
);
271 if (selected_index
!= -1)
272 ScheduleCommandsForTab(*tab
, selected_index
);
274 ScheduleCommandsForWindow(*static_cast<Window
*>(entry
));
280 entries_written_
= 0;
281 BaseSessionService::Save();
284 void PersistentTabRestoreService::Delegate::OnClearEntries() {
285 // Mark all the tabs as closed so that we don't attempt to restore them.
286 const Entries
& entries
= tab_restore_service_helper_
->entries();
287 for (Entries::const_iterator i
= entries
.begin(); i
!= entries
.end(); ++i
)
288 ScheduleCommand(CreateRestoredEntryCommand((*i
)->id
));
290 entries_to_write_
= 0;
292 // Schedule a pending reset so that we nuke the file on next write.
293 set_pending_reset(true);
295 // Schedule a command, otherwise if there are no pending commands Save does
297 ScheduleCommand(CreateRestoredEntryCommand(1));
300 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
301 SessionID::id_type id
,
302 Entries::const_iterator entry_iterator
) {
304 const Entries
& entries
= tab_restore_service_helper_
->entries();
305 for (Entries::const_iterator j
= entries
.begin();
306 j
!= entry_iterator
&& j
!= entries
.end();
308 if (static_cast<int>(index
) < entries_to_write_
)
311 ScheduleCommand(CreateRestoredEntryCommand(id
));
314 void PersistentTabRestoreService::Delegate::OnAddEntry() {
315 // Start the save timer, when it fires we'll generate the commands.
320 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
321 if (load_state_
!= NOT_LOADED
)
324 if (tab_restore_service_helper_
->entries().size() == kMaxEntries
) {
325 // We already have the max number of entries we can take. There is no point
326 // in attempting to load since we'll just drop the results. Skip to loaded.
327 load_state_
= (LOADING
| LOADED_LAST_SESSION
| LOADED_LAST_TABS
);
332 #if !defined(ENABLE_SESSION_SERVICE)
333 // If sessions are not stored in the SessionService, default to
334 // |LOADED_LAST_SESSION| state.
335 load_state_
= LOADING
| LOADED_LAST_SESSION
;
337 load_state_
= LOADING
;
339 SessionService
* session_service
=
340 SessionServiceFactory::GetForProfile(profile());
341 Profile::ExitType exit_type
= profile()->GetLastSessionExitType();
342 if (!profile()->restored_last_session() && session_service
&&
343 (exit_type
== Profile::EXIT_CRASHED
||
344 exit_type
== Profile::EXIT_SESSION_ENDED
)) {
345 // The previous session crashed and wasn't restored, or was a forced
346 // shutdown. Both of which won't have notified us of the browser close so
347 // that we need to load the windows from session service (which will have
349 session_service
->GetLastSession(
350 base::Bind(&Delegate::OnGotPreviousSession
, base::Unretained(this)),
351 &cancelable_task_tracker_
);
353 load_state_
|= LOADED_LAST_SESSION
;
357 // Request the tabs closed in the last session. If the last session crashed,
358 // this won't contain the tabs/window that were open at the point of the
359 // crash (the call to GetLastSession above requests those).
360 ScheduleGetLastSessionCommands(
361 base::Bind(&Delegate::OnGotLastSessionCommands
, base::Unretained(this)),
362 &cancelable_task_tracker_
);
365 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
366 return !(load_state_
& (NOT_LOADED
| LOADING
));
370 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
371 std::vector
<SessionWindow
*>* windows
,
372 std::vector
<Entry
*>* entries
) {
373 for (size_t i
= 0; i
< windows
->size(); ++i
) {
374 scoped_ptr
<Window
> window(new Window());
375 if (ConvertSessionWindowToWindow((*windows
)[i
], window
.get()))
376 entries
->push_back(window
.release());
380 void PersistentTabRestoreService::Delegate::Shutdown() {
385 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
386 const Window
& window
) {
387 DCHECK(!window
.tabs
.empty());
388 int selected_tab
= window
.selected_tab_index
;
389 int valid_tab_count
= 0;
390 int real_selected_tab
= selected_tab
;
391 for (size_t i
= 0; i
< window
.tabs
.size(); ++i
) {
392 if (GetSelectedNavigationIndexToPersist(window
.tabs
[i
]) != -1) {
394 } else if (static_cast<int>(i
) < selected_tab
) {
398 if (valid_tab_count
== 0)
399 return; // No tabs to persist.
402 CreateWindowCommand(window
.id
,
403 std::min(real_selected_tab
, valid_tab_count
- 1),
407 if (!window
.app_name
.empty()) {
409 CreateSetWindowAppNameCommand(kCommandSetWindowAppName
,
414 for (size_t i
= 0; i
< window
.tabs
.size(); ++i
) {
415 int selected_index
= GetSelectedNavigationIndexToPersist(window
.tabs
[i
]);
416 if (selected_index
!= -1)
417 ScheduleCommandsForTab(window
.tabs
[i
], selected_index
);
421 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
423 int selected_index
) {
424 const std::vector
<sessions::SerializedNavigationEntry
>& navigations
=
426 int max_index
= static_cast<int>(navigations
.size());
428 // Determine the first navigation we'll persist.
429 int valid_count_before_selected
= 0;
430 int first_index_to_persist
= selected_index
;
431 for (int i
= selected_index
- 1; i
>= 0 &&
432 valid_count_before_selected
< max_persist_navigation_count
; --i
) {
433 if (ShouldTrackEntry(navigations
[i
].virtual_url())) {
434 first_index_to_persist
= i
;
435 valid_count_before_selected
++;
439 // Write the command that identifies the selected tab.
441 CreateSelectedNavigationInTabCommand(tab
.id
,
442 valid_count_before_selected
,
446 PinnedStatePayload payload
= true;
447 SessionCommand
* command
=
448 new SessionCommand(kCommandPinnedState
, sizeof(payload
));
449 memcpy(command
->contents(), &payload
, sizeof(payload
));
450 ScheduleCommand(command
);
453 if (!tab
.extension_app_id
.empty()) {
455 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID
, tab
.id
,
456 tab
.extension_app_id
));
459 if (!tab
.user_agent_override
.empty()) {
461 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride
,
462 tab
.id
, tab
.user_agent_override
));
465 // Then write the navigations.
466 for (int i
= first_index_to_persist
, wrote_count
= 0;
467 i
< max_index
&& wrote_count
< 2 * max_persist_navigation_count
; ++i
) {
468 if (ShouldTrackEntry(navigations
[i
].virtual_url())) {
470 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation
, tab
.id
,
477 SessionCommand
* PersistentTabRestoreService::Delegate::CreateWindowCommand(
478 SessionID::id_type id
,
479 int selected_tab_index
,
481 base::Time timestamp
) {
482 WindowPayload2 payload
;
483 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
484 // uninitialized memory in the struct.
485 memset(&payload
, 0, sizeof(payload
));
486 payload
.window_id
= id
;
487 payload
.selected_tab_index
= selected_tab_index
;
488 payload
.num_tabs
= num_tabs
;
489 payload
.timestamp
= timestamp
.ToInternalValue();
491 SessionCommand
* command
=
492 new SessionCommand(kCommandWindow
, sizeof(payload
));
493 memcpy(command
->contents(), &payload
, sizeof(payload
));
499 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
500 SessionID::id_type tab_id
,
502 base::Time timestamp
) {
503 SelectedNavigationInTabPayload2 payload
;
505 payload
.index
= index
;
506 payload
.timestamp
= timestamp
.ToInternalValue();
507 SessionCommand
* command
=
508 new SessionCommand(kCommandSelectedNavigationInTab
, sizeof(payload
));
509 memcpy(command
->contents(), &payload
, sizeof(payload
));
515 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
516 SessionID::id_type entry_id
) {
517 RestoredEntryPayload payload
= entry_id
;
518 SessionCommand
* command
=
519 new SessionCommand(kCommandRestoredEntry
, sizeof(payload
));
520 memcpy(command
->contents(), &payload
, sizeof(payload
));
524 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
526 const std::vector
<sessions::SerializedNavigationEntry
>& navigations
=
528 int selected_index
= tab
.current_navigation_index
;
529 int max_index
= static_cast<int>(navigations
.size());
531 // Find the first navigation to persist. We won't persist the selected
532 // navigation if ShouldTrackEntry returns false.
533 while (selected_index
>= 0 &&
534 !ShouldTrackEntry(navigations
[selected_index
].virtual_url())) {
538 if (selected_index
!= -1)
539 return selected_index
;
541 // Couldn't find a navigation to persist going back, go forward.
542 selected_index
= tab
.current_navigation_index
+ 1;
543 while (selected_index
< max_index
&&
544 !ShouldTrackEntry(navigations
[selected_index
].virtual_url())) {
548 return (selected_index
== max_index
) ? -1 : selected_index
;
551 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
552 ScopedVector
<SessionCommand
> commands
) {
553 std::vector
<Entry
*> entries
;
554 CreateEntriesFromCommands(commands
.get(), &entries
);
555 // Closed tabs always go to the end.
556 staging_entries_
.insert(staging_entries_
.end(), entries
.begin(),
558 load_state_
|= LOADED_LAST_TABS
;
562 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
563 const std::vector
<SessionCommand
*>& commands
,
564 std::vector
<Entry
*>* loaded_entries
) {
565 if (tab_restore_service_helper_
->entries().size() == kMaxEntries
)
568 // Iterate through the commands populating entries and id_to_entry.
569 ScopedVector
<Entry
> entries
;
570 IDToEntry id_to_entry
;
571 // If non-null we're processing the navigations of this tab.
572 Tab
* current_tab
= NULL
;
573 // If non-null we're processing the tabs of this window.
574 Window
* current_window
= NULL
;
575 // If > 0, we've gotten a window command but not all the tabs yet.
576 int pending_window_tabs
= 0;
577 for (std::vector
<SessionCommand
*>::const_iterator i
= commands
.begin();
578 i
!= commands
.end(); ++i
) {
579 const SessionCommand
& command
= *(*i
);
580 switch (command
.id()) {
581 case kCommandRestoredEntry
: {
582 if (pending_window_tabs
> 0) {
583 // Should never receive a restored command while waiting for all the
589 current_window
= NULL
;
591 RestoredEntryPayload payload
;
592 if (!command
.GetPayload(&payload
, sizeof(payload
)))
594 RemoveEntryByID(payload
, &id_to_entry
, &(entries
.get()));
598 case kCommandWindow
: {
599 WindowPayload2 payload
;
600 if (pending_window_tabs
> 0) {
601 // Should never receive a window command while waiting for all the
606 // Try the new payload first
607 if (!command
.GetPayload(&payload
, sizeof(payload
))) {
608 // then the old payload
609 WindowPayload old_payload
;
610 if (!command
.GetPayload(&old_payload
, sizeof(old_payload
)))
613 // Copy the old payload data to the new payload.
614 payload
.window_id
= old_payload
.window_id
;
615 payload
.selected_tab_index
= old_payload
.selected_tab_index
;
616 payload
.num_tabs
= old_payload
.num_tabs
;
617 // Since we don't have a time use time 0 which is used to mark as an
618 // unknown timestamp.
619 payload
.timestamp
= 0;
622 pending_window_tabs
= payload
.num_tabs
;
623 if (pending_window_tabs
<= 0) {
624 // Should always have at least 1 tab. Likely indicates corruption.
628 RemoveEntryByID(payload
.window_id
, &id_to_entry
, &(entries
.get()));
630 current_window
= new Window();
631 current_window
->selected_tab_index
= payload
.selected_tab_index
;
632 current_window
->timestamp
=
633 base::Time::FromInternalValue(payload
.timestamp
);
634 entries
.push_back(current_window
);
635 id_to_entry
[payload
.window_id
] = current_window
;
639 case kCommandSelectedNavigationInTab
: {
640 SelectedNavigationInTabPayload2 payload
;
641 if (!command
.GetPayload(&payload
, sizeof(payload
))) {
642 SelectedNavigationInTabPayload old_payload
;
643 if (!command
.GetPayload(&old_payload
, sizeof(old_payload
)))
645 payload
.id
= old_payload
.id
;
646 payload
.index
= old_payload
.index
;
647 // Since we don't have a time use time 0 which is used to mark as an
648 // unknown timestamp.
649 payload
.timestamp
= 0;
652 if (pending_window_tabs
> 0) {
653 if (!current_window
) {
654 // We should have created a window already.
658 current_window
->tabs
.resize(current_window
->tabs
.size() + 1);
659 current_tab
= &(current_window
->tabs
.back());
660 if (--pending_window_tabs
== 0)
661 current_window
= NULL
;
663 RemoveEntryByID(payload
.id
, &id_to_entry
, &(entries
.get()));
664 current_tab
= new Tab();
665 id_to_entry
[payload
.id
] = current_tab
;
666 current_tab
->timestamp
=
667 base::Time::FromInternalValue(payload
.timestamp
);
668 entries
.push_back(current_tab
);
670 current_tab
->current_navigation_index
= payload
.index
;
674 case kCommandUpdateTabNavigation
: {
676 // Should be in a tab when we get this.
679 current_tab
->navigations
.resize(current_tab
->navigations
.size() + 1);
680 SessionID::id_type tab_id
;
681 if (!RestoreUpdateTabNavigationCommand(
682 command
, ¤t_tab
->navigations
.back(), &tab_id
)) {
688 case kCommandPinnedState
: {
690 // Should be in a tab when we get this.
693 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
695 current_tab
->pinned
= true;
699 case kCommandSetWindowAppName
: {
700 if (!current_window
) {
701 // We should have created a window already.
706 SessionID::id_type window_id
;
707 std::string app_name
;
708 if (!RestoreSetWindowAppNameCommand(command
, &window_id
, &app_name
))
711 current_window
->app_name
.swap(app_name
);
715 case kCommandSetExtensionAppID
: {
717 // Should be in a tab when we get this.
720 SessionID::id_type tab_id
;
721 std::string extension_app_id
;
722 if (!RestoreSetTabExtensionAppIDCommand(command
, &tab_id
,
723 &extension_app_id
)) {
726 current_tab
->extension_app_id
.swap(extension_app_id
);
730 case kCommandSetTabUserAgentOverride
: {
732 // Should be in a tab when we get this.
735 SessionID::id_type tab_id
;
736 std::string user_agent_override
;
737 if (!RestoreSetTabUserAgentOverrideCommand(command
, &tab_id
,
738 &user_agent_override
)) {
741 current_tab
->user_agent_override
.swap(user_agent_override
);
746 // Unknown type, usually indicates corruption of file. Ignore it.
751 // If there was corruption some of the entries won't be valid.
752 ValidateAndDeleteEmptyEntries(&(entries
.get()));
754 loaded_entries
->swap(entries
.get());
758 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
759 std::vector
<Entry
*>* entries
) {
760 std::vector
<Entry
*> valid_entries
;
761 std::vector
<Entry
*> invalid_entries
;
763 // Iterate from the back so that we keep the most recently closed entries.
764 for (std::vector
<Entry
*>::reverse_iterator i
= entries
->rbegin();
765 i
!= entries
->rend(); ++i
) {
766 if (TabRestoreServiceHelper::ValidateEntry(*i
))
767 valid_entries
.push_back(*i
);
769 invalid_entries
.push_back(*i
);
771 // NOTE: at this point the entries are ordered with newest at the front.
772 entries
->swap(valid_entries
);
774 // Delete the remaining entries.
775 STLDeleteElements(&invalid_entries
);
778 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
779 ScopedVector
<SessionWindow
> windows
,
780 SessionID::id_type ignored_active_window
) {
781 std::vector
<Entry
*> entries
;
782 CreateEntriesFromWindows(&windows
.get(), &entries
);
783 // Previous session tabs go first.
784 staging_entries_
.insert(staging_entries_
.begin(), entries
.begin(),
786 load_state_
|= LOADED_LAST_SESSION
;
790 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
791 SessionWindow
* session_window
,
793 for (size_t i
= 0; i
< session_window
->tabs
.size(); ++i
) {
794 if (!session_window
->tabs
[i
]->navigations
.empty()) {
795 window
->tabs
.resize(window
->tabs
.size() + 1);
796 Tab
& tab
= window
->tabs
.back();
797 tab
.pinned
= session_window
->tabs
[i
]->pinned
;
798 tab
.navigations
.swap(session_window
->tabs
[i
]->navigations
);
799 tab
.current_navigation_index
=
800 session_window
->tabs
[i
]->current_navigation_index
;
801 tab
.extension_app_id
= session_window
->tabs
[i
]->extension_app_id
;
802 tab
.timestamp
= base::Time();
805 if (window
->tabs
.empty())
808 window
->selected_tab_index
=
809 std::min(session_window
->selected_tab_index
,
810 static_cast<int>(window
->tabs
.size() - 1));
811 window
->timestamp
= base::Time();
815 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
816 if ((load_state_
& (LOADED_LAST_TABS
| LOADED_LAST_SESSION
)) !=
817 (LOADED_LAST_TABS
| LOADED_LAST_SESSION
)) {
818 // Still waiting on previous session or previous tabs.
822 // We're done loading.
823 load_state_
^= LOADING
;
825 const Entries
& entries
= tab_restore_service_helper_
->entries();
826 if (staging_entries_
.empty() || entries
.size() >= kMaxEntries
) {
827 staging_entries_
.clear();
828 tab_restore_service_helper_
->NotifyLoaded();
832 if (staging_entries_
.size() + entries
.size() > kMaxEntries
) {
833 // If we add all the staged entries we'll end up with more than
834 // kMaxEntries. Delete entries such that we only end up with at most
836 int surplus
= kMaxEntries
- entries
.size();
837 CHECK_LE(0, surplus
);
838 CHECK_GE(static_cast<int>(staging_entries_
.size()), surplus
);
839 staging_entries_
.erase(
840 staging_entries_
.begin() + (kMaxEntries
- entries
.size()),
841 staging_entries_
.end());
845 for (size_t i
= 0; i
< staging_entries_
.size(); ++i
) {
846 staging_entries_
[i
]->from_last_session
= true;
847 tab_restore_service_helper_
->AddEntry(staging_entries_
[i
], false, false);
850 // AddEntry takes ownership of the entry, need to clear out entries so that
851 // it doesn't delete them.
852 staging_entries_
.weak_clear();
854 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
855 // correctly write out the entries when Save is invoked (Save starts from
856 // the front, not the end and we just added the entries to the end).
857 entries_to_write_
= staging_entries_
.size();
859 tab_restore_service_helper_
->PruneEntries();
860 tab_restore_service_helper_
->NotifyTabsChanged();
862 tab_restore_service_helper_
->NotifyLoaded();
865 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
866 SessionID::id_type id
,
867 IDToEntry
* id_to_entry
,
868 std::vector
<TabRestoreService::Entry
*>* entries
) {
869 // Look for the entry in the map. If it is present, erase it from both
870 // collections and return.
871 IDToEntry::iterator i
= id_to_entry
->find(id
);
872 if (i
!= id_to_entry
->end()) {
873 entries
->erase(std::find(entries
->begin(), entries
->end(), i
->second
));
875 id_to_entry
->erase(i
);
879 // Otherwise, loop over all items in the map and see if any of the Windows
880 // have Tabs with the |id|.
881 for (IDToEntry::iterator i
= id_to_entry
->begin(); i
!= id_to_entry
->end();
883 if (i
->second
->type
== TabRestoreService::WINDOW
) {
884 TabRestoreService::Window
* window
=
885 static_cast<TabRestoreService::Window
*>(i
->second
);
886 std::vector
<TabRestoreService::Tab
>::iterator j
= window
->tabs
.begin();
887 for ( ; j
!= window
->tabs
.end(); ++j
) {
888 // If the ID matches one of this window's tabs, remove it from the
891 window
->tabs
.erase(j
);
899 // PersistentTabRestoreService -------------------------------------------------
901 PersistentTabRestoreService::PersistentTabRestoreService(
903 TimeFactory
* time_factory
)
904 : delegate_(new Delegate(profile
)),
905 helper_(this, delegate_
.get(), profile
, time_factory
) {
906 delegate_
->set_tab_restore_service_helper(&helper_
);
909 PersistentTabRestoreService::~PersistentTabRestoreService() {}
911 void PersistentTabRestoreService::AddObserver(
912 TabRestoreServiceObserver
* observer
) {
913 helper_
.AddObserver(observer
);
916 void PersistentTabRestoreService::RemoveObserver(
917 TabRestoreServiceObserver
* observer
) {
918 helper_
.RemoveObserver(observer
);
921 void PersistentTabRestoreService::CreateHistoricalTab(
922 content::WebContents
* contents
,
924 helper_
.CreateHistoricalTab(contents
, index
);
927 void PersistentTabRestoreService::BrowserClosing(
928 TabRestoreServiceDelegate
* delegate
) {
929 helper_
.BrowserClosing(delegate
);
932 void PersistentTabRestoreService::BrowserClosed(
933 TabRestoreServiceDelegate
* delegate
) {
934 helper_
.BrowserClosed(delegate
);
937 void PersistentTabRestoreService::ClearEntries() {
938 helper_
.ClearEntries();
941 const TabRestoreService::Entries
& PersistentTabRestoreService::entries() const {
942 return helper_
.entries();
945 std::vector
<content::WebContents
*>
946 PersistentTabRestoreService::RestoreMostRecentEntry(
947 TabRestoreServiceDelegate
* delegate
,
948 chrome::HostDesktopType host_desktop_type
) {
949 return helper_
.RestoreMostRecentEntry(delegate
, host_desktop_type
);
952 TabRestoreService::Tab
* PersistentTabRestoreService::RemoveTabEntryById(
953 SessionID::id_type id
) {
954 return helper_
.RemoveTabEntryById(id
);
957 std::vector
<content::WebContents
*>
958 PersistentTabRestoreService::RestoreEntryById(
959 TabRestoreServiceDelegate
* delegate
,
960 SessionID::id_type id
,
961 chrome::HostDesktopType host_desktop_type
,
962 WindowOpenDisposition disposition
) {
963 return helper_
.RestoreEntryById(delegate
, id
, host_desktop_type
, disposition
);
966 bool PersistentTabRestoreService::IsLoaded() const {
967 return delegate_
->IsLoaded();
970 void PersistentTabRestoreService::DeleteLastSession() {
971 return delegate_
->DeleteLastSession();
974 void PersistentTabRestoreService::Shutdown() {
975 return delegate_
->Shutdown();
978 void PersistentTabRestoreService::LoadTabsFromLastSession() {
979 delegate_
->LoadTabsFromLastSession();
982 TabRestoreService::Entries
* PersistentTabRestoreService::mutable_entries() {
983 return &helper_
.entries_
;
986 void PersistentTabRestoreService::PruneEntries() {
987 helper_
.PruneEntries();
990 BrowserContextKeyedService
* TabRestoreServiceFactory::BuildServiceInstanceFor(
991 content::BrowserContext
* profile
) const {
992 return new PersistentTabRestoreService(static_cast<Profile
*>(profile
), NULL
);