Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / components / sessions / core / persistent_tab_restore_service.cc
blob6a8f171ff59deb0d70c53a2d6b0e875d3fd6b826
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/sessions/core/persistent_tab_restore_service.h"
7 #include <cstring> // memcpy
8 #include <vector>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/stl_util.h"
18 #include "base/task/cancelable_task_tracker.h"
19 #include "base/time/time.h"
20 #include "components/sessions/base_session_service.h"
21 #include "components/sessions/base_session_service_commands.h"
22 #include "components/sessions/base_session_service_delegate.h"
23 #include "components/sessions/core/session_constants.h"
24 #include "components/sessions/session_command.h"
26 namespace sessions {
28 namespace {
30 // Only written if the tab is pinned.
31 typedef bool PinnedStatePayload;
33 typedef int32 RestoredEntryPayload;
35 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
37 // Payload used for the start of a tab close. This is the old struct that is
38 // used for backwards compat when it comes to reading the session files.
39 struct SelectedNavigationInTabPayload {
40 SessionID::id_type id;
41 int32 index;
44 // Payload used for the start of a window close. This is the old struct that is
45 // used for backwards compat when it comes to reading the session files. This
46 // struct must be POD, because we memset the contents.
47 struct WindowPayload {
48 SessionID::id_type window_id;
49 int32 selected_tab_index;
50 int32 num_tabs;
53 // Payload used for the start of a window close. This struct must be POD,
54 // because we memset the contents.
55 struct WindowPayload2 : WindowPayload {
56 int64 timestamp;
59 // Payload used for the start of a tab close.
60 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
61 int64 timestamp;
64 // Used to indicate what has loaded.
65 enum LoadState {
66 // Indicates we haven't loaded anything.
67 NOT_LOADED = 1 << 0,
69 // Indicates we've asked for the last sessions and tabs but haven't gotten the
70 // result back yet.
71 LOADING = 1 << 2,
73 // Indicates we finished loading the last tabs (but not necessarily the last
74 // session).
75 LOADED_LAST_TABS = 1 << 3,
77 // Indicates we finished loading the last session (but not necessarily the
78 // last tabs).
79 LOADED_LAST_SESSION = 1 << 4
82 // Identifier for commands written to file. The ordering in the file is as
83 // follows:
84 // . When the user closes a tab a command of type
85 // kCommandSelectedNavigationInTab is written identifying the tab and
86 // the selected index, then a kCommandPinnedState command if the tab was
87 // pinned and kCommandSetExtensionAppID if the tab has an app id and
88 // the user agent override if it was using one. This is
89 // followed by any number of kCommandUpdateTabNavigation commands (1 per
90 // navigation entry).
91 // . When the user closes a window a kCommandSelectedNavigationInTab command
92 // is written out and followed by n tab closed sequences (as previoulsy
93 // described).
94 // . When the user restores an entry a command of type kCommandRestoredEntry
95 // is written.
96 const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
97 const SessionCommand::id_type kCommandRestoredEntry = 2;
98 const SessionCommand::id_type kCommandWindow = 3;
99 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
100 const SessionCommand::id_type kCommandPinnedState = 5;
101 const SessionCommand::id_type kCommandSetExtensionAppID = 6;
102 const SessionCommand::id_type kCommandSetWindowAppName = 7;
103 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
105 // Number of entries (not commands) before we clobber the file and write
106 // everything.
107 const int kEntriesPerReset = 40;
109 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries;
111 } // namespace
113 // PersistentTabRestoreService::Delegate ---------------------------------------
115 // This restore service will create and own a BaseSessionService and implement
116 // the required BaseSessionServiceDelegate.
117 class PersistentTabRestoreService::Delegate
118 : public BaseSessionServiceDelegate,
119 public TabRestoreServiceHelper::Observer {
120 public:
121 explicit Delegate(TabRestoreServiceClient* client);
123 ~Delegate() override;
125 // 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(std::vector<SessionWindow*>* windows,
149 std::vector<Entry*>* entries);
151 void Shutdown();
153 // Schedules the commands for a window close.
154 void ScheduleCommandsForWindow(const Window& window);
156 // Schedules the commands for a tab close. |selected_index| gives the index of
157 // the selected navigation.
158 void ScheduleCommandsForTab(const Tab& tab, int selected_index);
160 // Creates a window close command.
161 static scoped_ptr<SessionCommand> CreateWindowCommand(SessionID::id_type id,
162 int selected_tab_index,
163 int num_tabs,
164 base::Time timestamp);
166 // Creates a tab close command.
167 static scoped_ptr<SessionCommand> CreateSelectedNavigationInTabCommand(
168 SessionID::id_type tab_id,
169 int32 index,
170 base::Time timestamp);
172 // Creates a restore command.
173 static scoped_ptr<SessionCommand> CreateRestoredEntryCommand(
174 SessionID::id_type entry_id);
176 // Returns the index to persist as the selected index. This is the same as
177 // |tab.current_navigation_index| unless the entry at
178 // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
179 // valid navigation to persist.
180 int GetSelectedNavigationIndexToPersist(const Tab& tab);
182 // Invoked when we've loaded the session commands that identify the previously
183 // closed tabs. This creates entries, adds them to staging_entries_, and
184 // invokes LoadState.
185 void OnGotLastSessionCommands(ScopedVector<SessionCommand> commands);
187 // Populates |loaded_entries| with Entries from |commands|.
188 void CreateEntriesFromCommands(const std::vector<SessionCommand*>& commands,
189 std::vector<Entry*>* loaded_entries);
191 // Validates all entries in |entries|, deleting any with no navigations. This
192 // also deletes any entries beyond the max number of entries we can hold.
193 static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
195 // Callback from BaseSessionService when we've received the windows from the
196 // previous session. This creates and add entries to |staging_entries_| and
197 // invokes LoadStateChanged. |ignored_active_window| is ignored because we
198 // don't need to restore activation.
199 void OnGotPreviousSession(ScopedVector<SessionWindow> windows,
200 SessionID::id_type ignored_active_window);
202 // Converts a SessionWindow into a Window, returning true on success. We use 0
203 // as the timestamp here since we do not know when the window/tab was closed.
204 static bool ConvertSessionWindowToWindow(SessionWindow* session_window,
205 Window* window);
207 // Invoked when previous tabs or session is loaded. If both have finished
208 // loading the entries in |staging_entries_| are added to entries and
209 // observers are notified.
210 void LoadStateChanged();
212 // If |id_to_entry| contains an entry for |id| the corresponding entry is
213 // deleted and removed from both |id_to_entry| and |entries|. This is used
214 // when creating entries from the backend file.
215 void RemoveEntryByID(SessionID::id_type id,
216 IDToEntry* id_to_entry,
217 std::vector<TabRestoreService::Entry*>* entries);
219 private:
220 // The associated client.
221 TabRestoreServiceClient* client_;
223 scoped_ptr<BaseSessionService> base_session_service_;
225 TabRestoreServiceHelper* tab_restore_service_helper_;
227 // The number of entries to write.
228 int entries_to_write_;
230 // Number of entries we've written.
231 int entries_written_;
233 // Whether we've loaded the last session.
234 int load_state_;
236 // Results from previously closed tabs/sessions is first added here. When the
237 // results from both us and the session restore service have finished loading
238 // LoadStateChanged is invoked, which adds these entries to entries_.
239 ScopedVector<Entry> staging_entries_;
241 // Used when loading previous tabs/session and open tabs/session.
242 base::CancelableTaskTracker cancelable_task_tracker_;
244 DISALLOW_COPY_AND_ASSIGN(Delegate);
247 PersistentTabRestoreService::Delegate::Delegate(TabRestoreServiceClient* client)
248 : client_(client),
249 base_session_service_(
250 new BaseSessionService(BaseSessionService::TAB_RESTORE,
251 client_->GetPathToSaveTo(),
252 this)),
253 tab_restore_service_helper_(NULL),
254 entries_to_write_(0),
255 entries_written_(0),
256 load_state_(NOT_LOADED) {}
258 PersistentTabRestoreService::Delegate::~Delegate() {}
260 base::SequencedWorkerPool*
261 PersistentTabRestoreService::Delegate::GetBlockingPool() {
262 return client_->GetBlockingPool();
265 bool PersistentTabRestoreService::Delegate::ShouldUseDelayedSave() {
266 return true;
269 void PersistentTabRestoreService::Delegate::OnWillSaveCommands() {
270 const Entries& entries = tab_restore_service_helper_->entries();
271 int to_write_count = std::min(entries_to_write_,
272 static_cast<int>(entries.size()));
273 entries_to_write_ = 0;
274 if (entries_written_ + to_write_count > kEntriesPerReset) {
275 to_write_count = entries.size();
276 base_session_service_->set_pending_reset(true);
278 if (to_write_count) {
279 // Write the to_write_count most recently added entries out. The most
280 // recently added entry is at the front, so we use a reverse iterator to
281 // write in the order the entries were added.
282 Entries::const_reverse_iterator i = entries.rbegin();
283 DCHECK(static_cast<size_t>(to_write_count) <= entries.size());
284 std::advance(i, entries.size() - static_cast<int>(to_write_count));
285 for (; i != entries.rend(); ++i) {
286 Entry* entry = *i;
287 if (entry->type == TAB) {
288 Tab* tab = static_cast<Tab*>(entry);
289 int selected_index = GetSelectedNavigationIndexToPersist(*tab);
290 if (selected_index != -1)
291 ScheduleCommandsForTab(*tab, selected_index);
292 } else {
293 ScheduleCommandsForWindow(*static_cast<Window*>(entry));
295 entries_written_++;
298 if (base_session_service_->pending_reset())
299 entries_written_ = 0;
302 void PersistentTabRestoreService::Delegate::OnClearEntries() {
303 // Mark all the tabs as closed so that we don't attempt to restore them.
304 const Entries& entries = tab_restore_service_helper_->entries();
305 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
306 base_session_service_->ScheduleCommand(
307 CreateRestoredEntryCommand((*i)->id).Pass());
309 entries_to_write_ = 0;
311 // Schedule a pending reset so that we nuke the file on next write.
312 base_session_service_->set_pending_reset(true);
314 // Schedule a command, otherwise if there are no pending commands Save does
315 // nothing.
316 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(1).Pass());
319 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
320 SessionID::id_type id,
321 Entries::const_iterator entry_iterator) {
322 size_t index = 0;
323 const Entries& entries = tab_restore_service_helper_->entries();
324 for (Entries::const_iterator j = entries.begin();
325 j != entry_iterator && j != entries.end();
326 ++j, ++index) {}
327 if (static_cast<int>(index) < entries_to_write_)
328 entries_to_write_--;
330 base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(id).Pass());
333 void PersistentTabRestoreService::Delegate::OnAddEntry() {
334 // Start the save timer, when it fires we'll generate the commands.
335 base_session_service_->StartSaveTimer();
336 entries_to_write_++;
339 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
340 if (load_state_ != NOT_LOADED)
341 return;
343 if (tab_restore_service_helper_->entries().size() == kMaxEntries) {
344 // We already have the max number of entries we can take. There is no point
345 // in attempting to load since we'll just drop the results. Skip to loaded.
346 load_state_ = (LOADING | LOADED_LAST_SESSION | LOADED_LAST_TABS);
347 LoadStateChanged();
348 return;
351 load_state_ = LOADING;
352 if (client_->HasLastSession()) {
353 client_->GetLastSession(
354 base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)),
355 &cancelable_task_tracker_);
356 } else {
357 load_state_ |= LOADED_LAST_SESSION;
360 // Request the tabs closed in the last session. If the last session crashed,
361 // this won't contain the tabs/window that were open at the point of the
362 // crash (the call to GetLastSession above requests those).
363 base_session_service_->ScheduleGetLastSessionCommands(
364 base::Bind(&Delegate::OnGotLastSessionCommands, base::Unretained(this)),
365 &cancelable_task_tracker_);
368 void PersistentTabRestoreService::Delegate::DeleteLastSession() {
369 base_session_service_->DeleteLastSession();
372 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
373 return !(load_state_ & (NOT_LOADED | LOADING));
376 // static
377 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
378 std::vector<SessionWindow*>* windows,
379 std::vector<Entry*>* entries) {
380 for (size_t i = 0; i < windows->size(); ++i) {
381 scoped_ptr<Window> window(new Window());
382 if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
383 entries->push_back(window.release());
387 void PersistentTabRestoreService::Delegate::Shutdown() {
388 base_session_service_->Save();
391 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
392 const Window& window) {
393 DCHECK(!window.tabs.empty());
394 int selected_tab = window.selected_tab_index;
395 int valid_tab_count = 0;
396 int real_selected_tab = selected_tab;
397 for (size_t i = 0; i < window.tabs.size(); ++i) {
398 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
399 valid_tab_count++;
400 } else if (static_cast<int>(i) < selected_tab) {
401 real_selected_tab--;
404 if (valid_tab_count == 0)
405 return; // No tabs to persist.
407 base_session_service_->ScheduleCommand(
408 CreateWindowCommand(window.id,
409 std::min(real_selected_tab, valid_tab_count - 1),
410 valid_tab_count,
411 window.timestamp).Pass());
413 if (!window.app_name.empty()) {
414 base_session_service_->ScheduleCommand(
415 CreateSetWindowAppNameCommand(kCommandSetWindowAppName, window.id,
416 window.app_name)
417 .Pass());
420 for (size_t i = 0; i < window.tabs.size(); ++i) {
421 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
422 if (selected_index != -1)
423 ScheduleCommandsForTab(window.tabs[i], selected_index);
427 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
428 const Tab& tab,
429 int selected_index) {
430 const std::vector<SerializedNavigationEntry>& navigations = tab.navigations;
431 int max_index = static_cast<int>(navigations.size());
433 // Determine the first navigation we'll persist.
434 int valid_count_before_selected = 0;
435 int first_index_to_persist = selected_index;
436 for (int i = selected_index - 1;
437 i >= 0 && valid_count_before_selected < gMaxPersistNavigationCount;
438 --i) {
439 if (client_->ShouldTrackURLForRestore(navigations[i].virtual_url())) {
440 first_index_to_persist = i;
441 valid_count_before_selected++;
445 // Write the command that identifies the selected tab.
446 base_session_service_->ScheduleCommand(
447 CreateSelectedNavigationInTabCommand(tab.id,
448 valid_count_before_selected,
449 tab.timestamp).Pass());
451 if (tab.pinned) {
452 PinnedStatePayload payload = true;
453 scoped_ptr<SessionCommand> command(
454 new SessionCommand(kCommandPinnedState, sizeof(payload)));
455 memcpy(command->contents(), &payload, sizeof(payload));
456 base_session_service_->ScheduleCommand(command.Pass());
459 if (!tab.extension_app_id.empty()) {
460 base_session_service_->ScheduleCommand(
461 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id,
462 tab.extension_app_id)
463 .Pass());
466 if (!tab.user_agent_override.empty()) {
467 base_session_service_->ScheduleCommand(
468 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
469 tab.id, tab.user_agent_override)
470 .Pass());
473 // Then write the navigations.
474 for (int i = first_index_to_persist, wrote_count = 0;
475 wrote_count < 2 * gMaxPersistNavigationCount && i < max_index; ++i) {
476 if (client_->ShouldTrackURLForRestore(navigations[i].virtual_url())) {
477 base_session_service_->ScheduleCommand(
478 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
479 tab.id,
480 navigations[i]));
485 // static
486 scoped_ptr<SessionCommand>
487 PersistentTabRestoreService::Delegate::CreateWindowCommand(
488 SessionID::id_type id,
489 int selected_tab_index,
490 int num_tabs,
491 base::Time timestamp) {
492 WindowPayload2 payload;
493 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
494 // uninitialized memory in the struct.
495 memset(&payload, 0, sizeof(payload));
496 payload.window_id = id;
497 payload.selected_tab_index = selected_tab_index;
498 payload.num_tabs = num_tabs;
499 payload.timestamp = timestamp.ToInternalValue();
501 scoped_ptr<SessionCommand> command(
502 new SessionCommand(kCommandWindow, sizeof(payload)));
503 memcpy(command->contents(), &payload, sizeof(payload));
504 return command;
507 // static
508 scoped_ptr<SessionCommand>
509 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
510 SessionID::id_type tab_id,
511 int32 index,
512 base::Time timestamp) {
513 SelectedNavigationInTabPayload2 payload;
514 payload.id = tab_id;
515 payload.index = index;
516 payload.timestamp = timestamp.ToInternalValue();
517 scoped_ptr<SessionCommand> command(
518 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)));
519 memcpy(command->contents(), &payload, sizeof(payload));
520 return command;
523 // static
524 scoped_ptr<SessionCommand>
525 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
526 SessionID::id_type entry_id) {
527 RestoredEntryPayload payload = entry_id;
528 scoped_ptr<SessionCommand> command(
529 new SessionCommand(kCommandRestoredEntry, sizeof(payload)));
530 memcpy(command->contents(), &payload, sizeof(payload));
531 return command;
534 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
535 const Tab& tab) {
536 const std::vector<SerializedNavigationEntry>& navigations = tab.navigations;
537 int selected_index = tab.current_navigation_index;
538 int max_index = static_cast<int>(navigations.size());
540 // Find the first navigation to persist. We won't persist the selected
541 // navigation if client_->ShouldTrackURLForRestore returns false.
542 while (selected_index >= 0 &&
543 !client_->ShouldTrackURLForRestore(
544 navigations[selected_index].virtual_url())) {
545 selected_index--;
548 if (selected_index != -1)
549 return selected_index;
551 // Couldn't find a navigation to persist going back, go forward.
552 selected_index = tab.current_navigation_index + 1;
553 while (selected_index < max_index &&
554 !client_->ShouldTrackURLForRestore(
555 navigations[selected_index].virtual_url())) {
556 selected_index++;
559 return (selected_index == max_index) ? -1 : selected_index;
562 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
563 ScopedVector<SessionCommand> commands) {
564 std::vector<Entry*> entries;
565 CreateEntriesFromCommands(commands.get(), &entries);
566 // Closed tabs always go to the end.
567 staging_entries_.insert(staging_entries_.end(), entries.begin(),
568 entries.end());
569 load_state_ |= LOADED_LAST_TABS;
570 LoadStateChanged();
573 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
574 const std::vector<SessionCommand*>& commands,
575 std::vector<Entry*>* loaded_entries) {
576 if (tab_restore_service_helper_->entries().size() == kMaxEntries)
577 return;
579 // Iterate through the commands populating entries and id_to_entry.
580 ScopedVector<Entry> entries;
581 IDToEntry id_to_entry;
582 // If non-null we're processing the navigations of this tab.
583 Tab* current_tab = NULL;
584 // If non-null we're processing the tabs of this window.
585 Window* current_window = NULL;
586 // If > 0, we've gotten a window command but not all the tabs yet.
587 int pending_window_tabs = 0;
588 for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
589 i != commands.end(); ++i) {
590 const SessionCommand& command = *(*i);
591 switch (command.id()) {
592 case kCommandRestoredEntry: {
593 if (pending_window_tabs > 0) {
594 // Should never receive a restored command while waiting for all the
595 // tabs in a window.
596 return;
599 current_tab = NULL;
600 current_window = NULL;
602 RestoredEntryPayload payload;
603 if (!command.GetPayload(&payload, sizeof(payload)))
604 return;
605 RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
606 break;
609 case kCommandWindow: {
610 WindowPayload2 payload;
611 if (pending_window_tabs > 0) {
612 // Should never receive a window command while waiting for all the
613 // tabs in a window.
614 return;
617 // Try the new payload first
618 if (!command.GetPayload(&payload, sizeof(payload))) {
619 // then the old payload
620 WindowPayload old_payload;
621 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
622 return;
624 // Copy the old payload data to the new payload.
625 payload.window_id = old_payload.window_id;
626 payload.selected_tab_index = old_payload.selected_tab_index;
627 payload.num_tabs = old_payload.num_tabs;
628 // Since we don't have a time use time 0 which is used to mark as an
629 // unknown timestamp.
630 payload.timestamp = 0;
633 pending_window_tabs = payload.num_tabs;
634 if (pending_window_tabs <= 0) {
635 // Should always have at least 1 tab. Likely indicates corruption.
636 return;
639 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
641 current_window = new Window();
642 current_window->selected_tab_index = payload.selected_tab_index;
643 current_window->timestamp =
644 base::Time::FromInternalValue(payload.timestamp);
645 entries.push_back(current_window);
646 id_to_entry[payload.window_id] = current_window;
647 break;
650 case kCommandSelectedNavigationInTab: {
651 SelectedNavigationInTabPayload2 payload;
652 if (!command.GetPayload(&payload, sizeof(payload))) {
653 SelectedNavigationInTabPayload old_payload;
654 if (!command.GetPayload(&old_payload, sizeof(old_payload)))
655 return;
656 payload.id = old_payload.id;
657 payload.index = old_payload.index;
658 // Since we don't have a time use time 0 which is used to mark as an
659 // unknown timestamp.
660 payload.timestamp = 0;
663 if (pending_window_tabs > 0) {
664 if (!current_window) {
665 // We should have created a window already.
666 NOTREACHED();
667 return;
669 current_window->tabs.resize(current_window->tabs.size() + 1);
670 current_tab = &(current_window->tabs.back());
671 if (--pending_window_tabs == 0)
672 current_window = NULL;
673 } else {
674 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
675 current_tab = new Tab();
676 id_to_entry[payload.id] = current_tab;
677 current_tab->timestamp =
678 base::Time::FromInternalValue(payload.timestamp);
679 entries.push_back(current_tab);
681 current_tab->current_navigation_index = payload.index;
682 break;
685 case kCommandUpdateTabNavigation: {
686 if (!current_tab) {
687 // Should be in a tab when we get this.
688 return;
690 current_tab->navigations.resize(current_tab->navigations.size() + 1);
691 SessionID::id_type tab_id;
692 if (!RestoreUpdateTabNavigationCommand(command,
693 &current_tab->navigations.back(),
694 &tab_id)) {
695 return;
697 break;
700 case kCommandPinnedState: {
701 if (!current_tab) {
702 // Should be in a tab when we get this.
703 return;
705 // NOTE: payload doesn't matter. kCommandPinnedState is only written if
706 // tab is pinned.
707 current_tab->pinned = true;
708 break;
711 case kCommandSetWindowAppName: {
712 if (!current_window) {
713 // We should have created a window already.
714 NOTREACHED();
715 return;
718 SessionID::id_type window_id;
719 std::string app_name;
720 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
721 return;
723 current_window->app_name.swap(app_name);
724 break;
727 case kCommandSetExtensionAppID: {
728 if (!current_tab) {
729 // Should be in a tab when we get this.
730 return;
732 SessionID::id_type tab_id;
733 std::string extension_app_id;
734 if (!RestoreSetTabExtensionAppIDCommand(command,
735 &tab_id,
736 &extension_app_id)) {
737 return;
739 current_tab->extension_app_id.swap(extension_app_id);
740 break;
743 case kCommandSetTabUserAgentOverride: {
744 if (!current_tab) {
745 // Should be in a tab when we get this.
746 return;
748 SessionID::id_type tab_id;
749 std::string user_agent_override;
750 if (!RestoreSetTabUserAgentOverrideCommand(command,
751 &tab_id,
752 &user_agent_override)) {
753 return;
755 current_tab->user_agent_override.swap(user_agent_override);
756 break;
759 default:
760 // Unknown type, usually indicates corruption of file. Ignore it.
761 return;
765 // If there was corruption some of the entries won't be valid.
766 ValidateAndDeleteEmptyEntries(&(entries.get()));
768 loaded_entries->swap(entries.get());
771 // static
772 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
773 std::vector<Entry*>* entries) {
774 std::vector<Entry*> valid_entries;
775 std::vector<Entry*> invalid_entries;
777 // Iterate from the back so that we keep the most recently closed entries.
778 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
779 i != entries->rend(); ++i) {
780 if (TabRestoreServiceHelper::ValidateEntry(*i))
781 valid_entries.push_back(*i);
782 else
783 invalid_entries.push_back(*i);
785 // NOTE: at this point the entries are ordered with newest at the front.
786 entries->swap(valid_entries);
788 // Delete the remaining entries.
789 STLDeleteElements(&invalid_entries);
792 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
793 ScopedVector<SessionWindow> windows,
794 SessionID::id_type ignored_active_window) {
795 std::vector<Entry*> entries;
796 CreateEntriesFromWindows(&windows.get(), &entries);
797 // Previous session tabs go first.
798 staging_entries_.insert(staging_entries_.begin(), entries.begin(),
799 entries.end());
800 load_state_ |= LOADED_LAST_SESSION;
801 LoadStateChanged();
804 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
805 SessionWindow* session_window,
806 Window* window) {
807 for (size_t i = 0; i < session_window->tabs.size(); ++i) {
808 if (!session_window->tabs[i]->navigations.empty()) {
809 window->tabs.resize(window->tabs.size() + 1);
810 Tab& tab = window->tabs.back();
811 tab.pinned = session_window->tabs[i]->pinned;
812 tab.navigations.swap(session_window->tabs[i]->navigations);
813 tab.current_navigation_index =
814 session_window->tabs[i]->current_navigation_index;
815 tab.extension_app_id = session_window->tabs[i]->extension_app_id;
816 tab.timestamp = base::Time();
819 if (window->tabs.empty())
820 return false;
822 window->selected_tab_index =
823 std::min(session_window->selected_tab_index,
824 static_cast<int>(window->tabs.size() - 1));
825 window->timestamp = base::Time();
826 return true;
829 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
830 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
831 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
832 // Still waiting on previous session or previous tabs.
833 return;
836 // We're done loading.
837 load_state_ ^= LOADING;
839 const Entries& entries = tab_restore_service_helper_->entries();
840 if (staging_entries_.empty() || entries.size() >= kMaxEntries) {
841 staging_entries_.clear();
842 tab_restore_service_helper_->NotifyLoaded();
843 return;
846 if (staging_entries_.size() + entries.size() > kMaxEntries) {
847 // If we add all the staged entries we'll end up with more than
848 // kMaxEntries. Delete entries such that we only end up with at most
849 // kMaxEntries.
850 int surplus = kMaxEntries - entries.size();
851 CHECK_LE(0, surplus);
852 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
853 staging_entries_.erase(
854 staging_entries_.begin() + (kMaxEntries - entries.size()),
855 staging_entries_.end());
858 // And add them.
859 for (size_t i = 0; i < staging_entries_.size(); ++i) {
860 staging_entries_[i]->from_last_session = true;
861 tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false);
864 // AddEntry takes ownership of the entry, need to clear out entries so that
865 // it doesn't delete them.
866 staging_entries_.weak_clear();
868 // Make it so we rewrite all the tabs. We need to do this otherwise we won't
869 // correctly write out the entries when Save is invoked (Save starts from
870 // the front, not the end and we just added the entries to the end).
871 entries_to_write_ = staging_entries_.size();
873 tab_restore_service_helper_->PruneEntries();
874 tab_restore_service_helper_->NotifyTabsChanged();
876 tab_restore_service_helper_->NotifyLoaded();
879 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
880 SessionID::id_type id,
881 IDToEntry* id_to_entry,
882 std::vector<TabRestoreService::Entry*>* entries) {
883 // Look for the entry in the map. If it is present, erase it from both
884 // collections and return.
885 IDToEntry::iterator i = id_to_entry->find(id);
886 if (i != id_to_entry->end()) {
887 entries->erase(std::find(entries->begin(), entries->end(), i->second));
888 delete i->second;
889 id_to_entry->erase(i);
890 return;
893 // Otherwise, loop over all items in the map and see if any of the Windows
894 // have Tabs with the |id|.
895 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
896 ++i) {
897 if (i->second->type == TabRestoreService::WINDOW) {
898 TabRestoreService::Window* window =
899 static_cast<TabRestoreService::Window*>(i->second);
900 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
901 for ( ; j != window->tabs.end(); ++j) {
902 // If the ID matches one of this window's tabs, remove it from the
903 // list.
904 if ((*j).id == id) {
905 window->tabs.erase(j);
906 return;
913 // PersistentTabRestoreService -------------------------------------------------
915 PersistentTabRestoreService::PersistentTabRestoreService(
916 scoped_ptr<TabRestoreServiceClient> client,
917 TimeFactory* time_factory)
918 : client_(client.Pass()),
919 delegate_(new Delegate(client_.get())),
920 helper_(this, delegate_.get(), client_.get(), time_factory) {
921 delegate_->set_tab_restore_service_helper(&helper_);
924 PersistentTabRestoreService::~PersistentTabRestoreService() {}
926 void PersistentTabRestoreService::AddObserver(
927 TabRestoreServiceObserver* observer) {
928 helper_.AddObserver(observer);
931 void PersistentTabRestoreService::RemoveObserver(
932 TabRestoreServiceObserver* observer) {
933 helper_.RemoveObserver(observer);
936 void PersistentTabRestoreService::CreateHistoricalTab(LiveTab* live_tab,
937 int index) {
938 helper_.CreateHistoricalTab(live_tab, index);
941 void PersistentTabRestoreService::BrowserClosing(
942 TabRestoreServiceDelegate* delegate) {
943 helper_.BrowserClosing(delegate);
946 void PersistentTabRestoreService::BrowserClosed(
947 TabRestoreServiceDelegate* delegate) {
948 helper_.BrowserClosed(delegate);
951 void PersistentTabRestoreService::ClearEntries() {
952 helper_.ClearEntries();
955 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
956 return helper_.entries();
959 std::vector<LiveTab*> PersistentTabRestoreService::RestoreMostRecentEntry(
960 TabRestoreServiceDelegate* delegate,
961 int host_desktop_type) {
962 return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
965 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
966 SessionID::id_type id) {
967 return helper_.RemoveTabEntryById(id);
970 std::vector<LiveTab*> PersistentTabRestoreService::RestoreEntryById(
971 TabRestoreServiceDelegate* delegate,
972 SessionID::id_type id,
973 int host_desktop_type,
974 WindowOpenDisposition disposition) {
975 return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition);
978 bool PersistentTabRestoreService::IsLoaded() const {
979 return delegate_->IsLoaded();
982 void PersistentTabRestoreService::DeleteLastSession() {
983 return delegate_->DeleteLastSession();
986 void PersistentTabRestoreService::Shutdown() {
987 return delegate_->Shutdown();
990 void PersistentTabRestoreService::LoadTabsFromLastSession() {
991 delegate_->LoadTabsFromLastSession();
994 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
995 return &helper_.entries_;
998 void PersistentTabRestoreService::PruneEntries() {
999 helper_.PruneEntries();
1002 } // namespace sessions