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
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
;
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
;
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
;
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
{
59 // Payload used for the start of a tab close.
60 struct SelectedNavigationInTabPayload2
: SelectedNavigationInTabPayload
{
64 // Used to indicate what has loaded.
66 // Indicates we haven't loaded anything.
69 // Indicates we've asked for the last sessions and tabs but haven't gotten the
73 // Indicates we finished loading the last tabs (but not necessarily the last
75 LOADED_LAST_TABS
= 1 << 3,
77 // Indicates we finished loading the last session (but not necessarily the
79 LOADED_LAST_SESSION
= 1 << 4
82 // Identifier for commands written to file. The ordering in the file is as
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
91 // . When the user closes a window a kCommandSelectedNavigationInTab command
92 // is written out and followed by n tab closed sequences (as previoulsy
94 // . When the user restores an entry a command of type kCommandRestoredEntry
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
107 const int kEntriesPerReset
= 40;
109 const size_t kMaxEntries
= TabRestoreServiceHelper::kMaxEntries
;
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
{
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
);
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
,
166 base::Time timestamp
);
168 // Creates a tab close command.
169 static scoped_ptr
<sessions::SessionCommand
>
170 CreateSelectedNavigationInTabCommand(
171 SessionID::id_type tab_id
,
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
,
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
);
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.
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
)
256 base_session_service_(new sessions::BaseSessionService(
257 sessions::BaseSessionService::TAB_RESTORE
,
258 client_
->GetPathToSaveTo(),
260 tab_restore_service_helper_(NULL
),
261 entries_to_write_(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() {
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
) {
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
);
300 ScheduleCommandsForWindow(*static_cast<Window
*>(entry
));
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
323 base_session_service_
->ScheduleCommand(CreateRestoredEntryCommand(1).Pass());
326 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
327 SessionID::id_type id
,
328 Entries::const_iterator entry_iterator
) {
330 const Entries
& entries
= tab_restore_service_helper_
->entries();
331 for (Entries::const_iterator j
= entries
.begin();
332 j
!= entry_iterator
&& j
!= entries
.end();
334 if (static_cast<int>(index
) < 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();
346 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
347 if (load_state_
!= NOT_LOADED
)
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
);
358 load_state_
= LOADING
;
359 if (client_
->HasLastSession()) {
360 client_
->GetLastSession(
361 base::Bind(&Delegate::OnGotPreviousSession
, base::Unretained(this)),
362 &cancelable_task_tracker_
);
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
));
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) {
407 } else if (static_cast<int>(i
) < 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),
418 window
.timestamp
).Pass());
420 if (!window
.app_name
.empty()) {
421 base_session_service_
->ScheduleCommand(
422 sessions::CreateSetWindowAppNameCommand(kCommandSetWindowAppName
,
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(
436 int selected_index
) {
437 const std::vector
<sessions::SerializedNavigationEntry
>& 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;
446 valid_count_before_selected
< sessions::gMaxPersistNavigationCount
;
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());
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
,
473 tab
.extension_app_id
).Pass());
476 if (!tab
.user_agent_override
.empty()) {
477 base_session_service_
->ScheduleCommand(
478 sessions::CreateSetTabUserAgentOverrideCommand(
479 kCommandSetTabUserAgentOverride
,
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
;
488 if (client_
->ShouldTrackURLForRestore(navigations
[i
].virtual_url())) {
489 base_session_service_
->ScheduleCommand(
490 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation
,
498 scoped_ptr
<sessions::SessionCommand
>
499 PersistentTabRestoreService::Delegate::CreateWindowCommand(
500 SessionID::id_type id
,
501 int selected_tab_index
,
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
));
520 scoped_ptr
<sessions::SessionCommand
>
521 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
522 SessionID::id_type tab_id
,
524 base::Time timestamp
) {
525 SelectedNavigationInTabPayload2 payload
;
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
));
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
));
547 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
549 const std::vector
<sessions::SerializedNavigationEntry
>& 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())) {
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())) {
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(),
583 load_state_
|= LOADED_LAST_TABS
;
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
)
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
614 current_window
= NULL
;
616 RestoredEntryPayload payload
;
617 if (!command
.GetPayload(&payload
, sizeof(payload
)))
619 RemoveEntryByID(payload
, &id_to_entry
, &(entries
.get()));
623 case kCommandWindow
: {
624 WindowPayload2 payload
;
625 if (pending_window_tabs
> 0) {
626 // Should never receive a window command while waiting for all the
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
)))
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.
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
;
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
)))
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.
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
;
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
;
699 case kCommandUpdateTabNavigation
: {
701 // Should be in a tab when we get this.
704 current_tab
->navigations
.resize(current_tab
->navigations
.size() + 1);
705 SessionID::id_type tab_id
;
706 if (!RestoreUpdateTabNavigationCommand(command
,
707 ¤t_tab
->navigations
.back(),
714 case kCommandPinnedState
: {
716 // Should be in a tab when we get this.
719 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
721 current_tab
->pinned
= true;
725 case kCommandSetWindowAppName
: {
726 if (!current_window
) {
727 // We should have created a window already.
732 SessionID::id_type window_id
;
733 std::string app_name
;
734 if (!RestoreSetWindowAppNameCommand(command
, &window_id
, &app_name
))
737 current_window
->app_name
.swap(app_name
);
741 case kCommandSetExtensionAppID
: {
743 // Should be in a tab when we get this.
746 SessionID::id_type tab_id
;
747 std::string extension_app_id
;
748 if (!RestoreSetTabExtensionAppIDCommand(command
,
750 &extension_app_id
)) {
753 current_tab
->extension_app_id
.swap(extension_app_id
);
757 case kCommandSetTabUserAgentOverride
: {
759 // Should be in a tab when we get this.
762 SessionID::id_type tab_id
;
763 std::string user_agent_override
;
764 if (!RestoreSetTabUserAgentOverrideCommand(command
,
766 &user_agent_override
)) {
769 current_tab
->user_agent_override
.swap(user_agent_override
);
774 // Unknown type, usually indicates corruption of file. Ignore it.
779 // If there was corruption some of the entries won't be valid.
780 ValidateAndDeleteEmptyEntries(&(entries
.get()));
782 loaded_entries
->swap(entries
.get());
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
);
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(),
814 load_state_
|= LOADED_LAST_SESSION
;
818 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
819 sessions::SessionWindow
* session_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())
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();
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.
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();
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
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());
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
));
903 id_to_entry
->erase(i
);
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();
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
919 window
->tabs
.erase(j
);
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
,
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();