• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
6 
7 #include <cstring>  // memcpy
8 #include <vector>
9 
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/stl_util.h"
18 #include "base/task/cancelable_task_tracker.h"
19 #include "base/time/time.h"
20 #include "chrome/browser/common/cancelable_request.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/sessions/base_session_service.h"
23 #include "chrome/browser/sessions/session_command.h"
24 #include "chrome/browser/sessions/session_service.h"
25 #include "chrome/browser/sessions/session_service_factory.h"
26 #include "chrome/browser/sessions/tab_restore_service_factory.h"
27 #include "content/public/browser/session_storage_namespace.h"
28 
29 namespace {
30 
31 // Only written if the tab is pinned.
32 typedef bool PinnedStatePayload;
33 
34 typedef int32 RestoredEntryPayload;
35 
36 typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
37 
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;
42   int32 index;
43 };
44 
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;
51   int32 num_tabs;
52 };
53 
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 {
57   int64 timestamp;
58 };
59 
60 // Payload used for the start of a tab close.
61 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
62   int64 timestamp;
63 };
64 
65 // Used to indicate what has loaded.
66 enum LoadState {
67   // Indicates we haven't loaded anything.
68   NOT_LOADED           = 1 << 0,
69 
70   // Indicates we've asked for the last sessions and tabs but haven't gotten the
71   // result back yet.
72   LOADING              = 1 << 2,
73 
74   // Indicates we finished loading the last tabs (but not necessarily the last
75   // session).
76   LOADED_LAST_TABS     = 1 << 3,
77 
78   // Indicates we finished loading the last session (but not necessarily the
79   // last tabs).
80   LOADED_LAST_SESSION  = 1 << 4
81 };
82 
83 // Identifier for commands written to file. The ordering in the file is as
84 // follows:
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
91 //   navigation entry).
92 // . When the user closes a window a kCommandSelectedNavigationInTab command
93 //   is written out and followed by n tab closed sequences (as previoulsy
94 //   described).
95 // . When the user restores an entry a command of type kCommandRestoredEntry
96 //   is written.
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;
105 
106 // Number of entries (not commands) before we clobber the file and write
107 // everything.
108 const int kEntriesPerReset = 40;
109 
110 const size_t kMaxEntries = TabRestoreServiceHelper::kMaxEntries;
111 
112 }  // namespace
113 
114 // PersistentTabRestoreService::Delegate ---------------------------------------
115 
116 // Implements the link between the tab restore service and the session backend.
117 class PersistentTabRestoreService::Delegate
118     : public BaseSessionService,
119       public TabRestoreServiceHelper::Observer {
120  public:
121   explicit Delegate(Profile* profile);
122 
123   virtual ~Delegate();
124 
125   // BaseSessionService:
126   virtual void Save() OVERRIDE;
127 
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;
134 
set_tab_restore_service_helper(TabRestoreServiceHelper * tab_restore_service_helper)135   void set_tab_restore_service_helper(
136       TabRestoreServiceHelper* tab_restore_service_helper) {
137     tab_restore_service_helper_ = tab_restore_service_helper;
138   }
139 
140   void LoadTabsFromLastSession();
141 
142   bool IsLoaded() const;
143 
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);
147 
148   void Shutdown();
149 
150   // Schedules the commands for a window close.
151   void ScheduleCommandsForWindow(const Window& window);
152 
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);
156 
157   // Creates a window close command.
158   static SessionCommand* CreateWindowCommand(SessionID::id_type id,
159                                              int selected_tab_index,
160                                              int num_tabs,
161                                              base::Time timestamp);
162 
163   // Creates a tab close command.
164   static SessionCommand* CreateSelectedNavigationInTabCommand(
165       SessionID::id_type tab_id,
166       int32 index,
167       base::Time timestamp);
168 
169   // Creates a restore command.
170   static SessionCommand* CreateRestoredEntryCommand(
171     SessionID::id_type entry_id);
172 
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);
178 
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);
183 
184   // Populates |loaded_entries| with Entries from |commands|.
185   void CreateEntriesFromCommands(const std::vector<SessionCommand*>& commands,
186                                  std::vector<Entry*>* loaded_entries);
187 
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);
191 
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);
198 
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,
202                                            Window* window);
203 
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();
208 
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);
215 
216  private:
217   TabRestoreServiceHelper* tab_restore_service_helper_;
218 
219   // The number of entries to write.
220   int entries_to_write_;
221 
222   // Number of entries we've written.
223   int entries_written_;
224 
225   // Whether we've loaded the last session.
226   int load_state_;
227 
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_;
232 
233   // Used when loading previous tabs/session and open tabs/session.
234   base::CancelableTaskTracker cancelable_task_tracker_;
235 
236   DISALLOW_COPY_AND_ASSIGN(Delegate);
237 };
238 
Delegate(Profile * profile)239 PersistentTabRestoreService::Delegate::Delegate(Profile* profile)
240     : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
241                          base::FilePath()),
242       tab_restore_service_helper_(NULL),
243       entries_to_write_(0),
244       entries_written_(0),
245       load_state_(NOT_LOADED) {
246 }
247 
~Delegate()248 PersistentTabRestoreService::Delegate::~Delegate() {}
249 
Save()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);
258   }
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) {
267       Entry* entry = *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);
273       } else {
274         ScheduleCommandsForWindow(*static_cast<Window*>(entry));
275       }
276       entries_written_++;
277     }
278   }
279   if (pending_reset())
280     entries_written_ = 0;
281   BaseSessionService::Save();
282 }
283 
OnClearEntries()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));
289 
290   entries_to_write_ = 0;
291 
292   // Schedule a pending reset so that we nuke the file on next write.
293   set_pending_reset(true);
294 
295   // Schedule a command, otherwise if there are no pending commands Save does
296   // nothing.
297   ScheduleCommand(CreateRestoredEntryCommand(1));
298 }
299 
OnRestoreEntryById(SessionID::id_type id,Entries::const_iterator entry_iterator)300 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
301     SessionID::id_type id,
302     Entries::const_iterator entry_iterator) {
303   size_t index = 0;
304   const Entries& entries = tab_restore_service_helper_->entries();
305   for (Entries::const_iterator j = entries.begin();
306        j != entry_iterator && j != entries.end();
307        ++j, ++index) {}
308   if (static_cast<int>(index) < entries_to_write_)
309     entries_to_write_--;
310 
311   ScheduleCommand(CreateRestoredEntryCommand(id));
312 }
313 
OnAddEntry()314 void PersistentTabRestoreService::Delegate::OnAddEntry() {
315   // Start the save timer, when it fires we'll generate the commands.
316   StartSaveTimer();
317   entries_to_write_++;
318 }
319 
LoadTabsFromLastSession()320 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
321   if (load_state_ != NOT_LOADED)
322     return;
323 
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);
328     LoadStateChanged();
329     return;
330   }
331 
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;
336 #else
337   load_state_ = LOADING;
338 
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
348     // saved them).
349     session_service->GetLastSession(
350         base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)),
351         &cancelable_task_tracker_);
352   } else {
353     load_state_ |= LOADED_LAST_SESSION;
354   }
355 #endif
356 
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_);
363 }
364 
IsLoaded() const365 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
366   return !(load_state_ & (NOT_LOADED | LOADING));
367 }
368 
369 // static
CreateEntriesFromWindows(std::vector<SessionWindow * > * windows,std::vector<Entry * > * entries)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());
377   }
378 }
379 
Shutdown()380 void PersistentTabRestoreService::Delegate::Shutdown() {
381   if (backend())
382     Save();
383 }
384 
ScheduleCommandsForWindow(const Window & window)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) {
393       valid_tab_count++;
394     } else if (static_cast<int>(i) < selected_tab) {
395       real_selected_tab--;
396     }
397   }
398   if (valid_tab_count == 0)
399     return;  // No tabs to persist.
400 
401   ScheduleCommand(
402       CreateWindowCommand(window.id,
403                           std::min(real_selected_tab, valid_tab_count - 1),
404                           valid_tab_count,
405                           window.timestamp));
406 
407   if (!window.app_name.empty()) {
408     ScheduleCommand(
409         CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
410                                       window.id,
411                                       window.app_name));
412   }
413 
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);
418   }
419 }
420 
ScheduleCommandsForTab(const Tab & tab,int selected_index)421 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
422     const Tab& tab,
423     int selected_index) {
424   const std::vector<sessions::SerializedNavigationEntry>& navigations =
425       tab.navigations;
426   int max_index = static_cast<int>(navigations.size());
427 
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++;
436     }
437   }
438 
439   // Write the command that identifies the selected tab.
440   ScheduleCommand(
441       CreateSelectedNavigationInTabCommand(tab.id,
442                                            valid_count_before_selected,
443                                            tab.timestamp));
444 
445   if (tab.pinned) {
446     PinnedStatePayload payload = true;
447     SessionCommand* command =
448         new SessionCommand(kCommandPinnedState, sizeof(payload));
449     memcpy(command->contents(), &payload, sizeof(payload));
450     ScheduleCommand(command);
451   }
452 
453   if (!tab.extension_app_id.empty()) {
454     ScheduleCommand(
455         CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id,
456                                           tab.extension_app_id));
457   }
458 
459   if (!tab.user_agent_override.empty()) {
460     ScheduleCommand(
461         CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
462                                              tab.id, tab.user_agent_override));
463   }
464 
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())) {
469       ScheduleCommand(
470           CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
471                                            navigations[i]));
472     }
473   }
474 }
475 
476 // static
CreateWindowCommand(SessionID::id_type id,int selected_tab_index,int num_tabs,base::Time timestamp)477 SessionCommand* PersistentTabRestoreService::Delegate::CreateWindowCommand(
478     SessionID::id_type id,
479     int selected_tab_index,
480     int num_tabs,
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();
490 
491   SessionCommand* command =
492       new SessionCommand(kCommandWindow, sizeof(payload));
493   memcpy(command->contents(), &payload, sizeof(payload));
494   return command;
495 }
496 
497 // static
498 SessionCommand*
CreateSelectedNavigationInTabCommand(SessionID::id_type tab_id,int32 index,base::Time timestamp)499 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
500     SessionID::id_type tab_id,
501     int32 index,
502     base::Time timestamp) {
503   SelectedNavigationInTabPayload2 payload;
504   payload.id = tab_id;
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));
510   return command;
511 }
512 
513 // static
514 SessionCommand*
CreateRestoredEntryCommand(SessionID::id_type entry_id)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));
521   return command;
522 }
523 
GetSelectedNavigationIndexToPersist(const Tab & tab)524 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
525     const Tab& tab) {
526   const std::vector<sessions::SerializedNavigationEntry>& navigations =
527       tab.navigations;
528   int selected_index = tab.current_navigation_index;
529   int max_index = static_cast<int>(navigations.size());
530 
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())) {
535     selected_index--;
536   }
537 
538   if (selected_index != -1)
539     return selected_index;
540 
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())) {
545     selected_index++;
546   }
547 
548   return (selected_index == max_index) ? -1 : selected_index;
549 }
550 
OnGotLastSessionCommands(ScopedVector<SessionCommand> commands)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(),
557                           entries.end());
558   load_state_ |= LOADED_LAST_TABS;
559   LoadStateChanged();
560 }
561 
CreateEntriesFromCommands(const std::vector<SessionCommand * > & commands,std::vector<Entry * > * loaded_entries)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)
566     return;
567 
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
584           // tabs in a window.
585           return;
586         }
587 
588         current_tab = NULL;
589         current_window = NULL;
590 
591         RestoredEntryPayload payload;
592         if (!command.GetPayload(&payload, sizeof(payload)))
593           return;
594         RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
595         break;
596       }
597 
598       case kCommandWindow: {
599         WindowPayload2 payload;
600         if (pending_window_tabs > 0) {
601           // Should never receive a window command while waiting for all the
602           // tabs in a window.
603           return;
604         }
605 
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)))
611             return;
612 
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;
620         }
621 
622         pending_window_tabs = payload.num_tabs;
623         if (pending_window_tabs <= 0) {
624           // Should always have at least 1 tab. Likely indicates corruption.
625           return;
626         }
627 
628         RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
629 
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;
636         break;
637       }
638 
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)))
644             return;
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;
650         }
651 
652         if (pending_window_tabs > 0) {
653           if (!current_window) {
654             // We should have created a window already.
655             NOTREACHED();
656             return;
657           }
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;
662         } else {
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);
669         }
670         current_tab->current_navigation_index = payload.index;
671         break;
672       }
673 
674       case kCommandUpdateTabNavigation: {
675         if (!current_tab) {
676           // Should be in a tab when we get this.
677           return;
678         }
679         current_tab->navigations.resize(current_tab->navigations.size() + 1);
680         SessionID::id_type tab_id;
681         if (!RestoreUpdateTabNavigationCommand(
682             command, &current_tab->navigations.back(), &tab_id)) {
683           return;
684         }
685         break;
686       }
687 
688       case kCommandPinnedState: {
689         if (!current_tab) {
690           // Should be in a tab when we get this.
691           return;
692         }
693         // NOTE: payload doesn't matter. kCommandPinnedState is only written if
694         // tab is pinned.
695         current_tab->pinned = true;
696         break;
697       }
698 
699       case kCommandSetWindowAppName: {
700         if (!current_window) {
701           // We should have created a window already.
702           NOTREACHED();
703           return;
704         }
705 
706         SessionID::id_type window_id;
707         std::string app_name;
708         if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
709           return;
710 
711         current_window->app_name.swap(app_name);
712         break;
713       }
714 
715       case kCommandSetExtensionAppID: {
716         if (!current_tab) {
717           // Should be in a tab when we get this.
718           return;
719         }
720         SessionID::id_type tab_id;
721         std::string extension_app_id;
722         if (!RestoreSetTabExtensionAppIDCommand(command, &tab_id,
723                                                 &extension_app_id)) {
724           return;
725         }
726         current_tab->extension_app_id.swap(extension_app_id);
727         break;
728       }
729 
730       case kCommandSetTabUserAgentOverride: {
731         if (!current_tab) {
732           // Should be in a tab when we get this.
733           return;
734         }
735         SessionID::id_type tab_id;
736         std::string user_agent_override;
737         if (!RestoreSetTabUserAgentOverrideCommand(command, &tab_id,
738                                                    &user_agent_override)) {
739           return;
740         }
741         current_tab->user_agent_override.swap(user_agent_override);
742         break;
743       }
744 
745       default:
746         // Unknown type, usually indicates corruption of file. Ignore it.
747         return;
748     }
749   }
750 
751   // If there was corruption some of the entries won't be valid.
752   ValidateAndDeleteEmptyEntries(&(entries.get()));
753 
754   loaded_entries->swap(entries.get());
755 }
756 
757 // static
ValidateAndDeleteEmptyEntries(std::vector<Entry * > * entries)758 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
759     std::vector<Entry*>* entries) {
760   std::vector<Entry*> valid_entries;
761   std::vector<Entry*> invalid_entries;
762 
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);
768     else
769       invalid_entries.push_back(*i);
770   }
771   // NOTE: at this point the entries are ordered with newest at the front.
772   entries->swap(valid_entries);
773 
774   // Delete the remaining entries.
775   STLDeleteElements(&invalid_entries);
776 }
777 
OnGotPreviousSession(ScopedVector<SessionWindow> windows,SessionID::id_type ignored_active_window)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(),
785                           entries.end());
786   load_state_ |= LOADED_LAST_SESSION;
787   LoadStateChanged();
788 }
789 
ConvertSessionWindowToWindow(SessionWindow * session_window,Window * window)790 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
791     SessionWindow* session_window,
792     Window* 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();
803     }
804   }
805   if (window->tabs.empty())
806     return false;
807 
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();
812   return true;
813 }
814 
LoadStateChanged()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.
819     return;
820   }
821 
822   // We're done loading.
823   load_state_ ^= LOADING;
824 
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();
829     return;
830   }
831 
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
835     // kMaxEntries.
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());
842   }
843 
844   // And add them.
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);
848   }
849 
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();
853 
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();
858 
859   tab_restore_service_helper_->PruneEntries();
860   tab_restore_service_helper_->NotifyTabsChanged();
861 
862   tab_restore_service_helper_->NotifyLoaded();
863 }
864 
RemoveEntryByID(SessionID::id_type id,IDToEntry * id_to_entry,std::vector<TabRestoreService::Entry * > * entries)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));
874     delete i->second;
875     id_to_entry->erase(i);
876     return;
877   }
878 
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();
882        ++i) {
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
889         // list.
890         if ((*j).id == id) {
891           window->tabs.erase(j);
892           return;
893         }
894       }
895     }
896   }
897 }
898 
899 // PersistentTabRestoreService -------------------------------------------------
900 
PersistentTabRestoreService(Profile * profile,TimeFactory * time_factory)901 PersistentTabRestoreService::PersistentTabRestoreService(
902     Profile* profile,
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_);
907 }
908 
~PersistentTabRestoreService()909 PersistentTabRestoreService::~PersistentTabRestoreService() {}
910 
AddObserver(TabRestoreServiceObserver * observer)911 void PersistentTabRestoreService::AddObserver(
912     TabRestoreServiceObserver* observer) {
913   helper_.AddObserver(observer);
914 }
915 
RemoveObserver(TabRestoreServiceObserver * observer)916 void PersistentTabRestoreService::RemoveObserver(
917     TabRestoreServiceObserver* observer) {
918   helper_.RemoveObserver(observer);
919 }
920 
CreateHistoricalTab(content::WebContents * contents,int index)921 void PersistentTabRestoreService::CreateHistoricalTab(
922     content::WebContents* contents,
923     int index) {
924   helper_.CreateHistoricalTab(contents, index);
925 }
926 
BrowserClosing(TabRestoreServiceDelegate * delegate)927 void PersistentTabRestoreService::BrowserClosing(
928     TabRestoreServiceDelegate* delegate) {
929   helper_.BrowserClosing(delegate);
930 }
931 
BrowserClosed(TabRestoreServiceDelegate * delegate)932 void PersistentTabRestoreService::BrowserClosed(
933     TabRestoreServiceDelegate* delegate) {
934   helper_.BrowserClosed(delegate);
935 }
936 
ClearEntries()937 void PersistentTabRestoreService::ClearEntries() {
938   helper_.ClearEntries();
939 }
940 
entries() const941 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
942   return helper_.entries();
943 }
944 
945 std::vector<content::WebContents*>
RestoreMostRecentEntry(TabRestoreServiceDelegate * delegate,chrome::HostDesktopType host_desktop_type)946 PersistentTabRestoreService::RestoreMostRecentEntry(
947     TabRestoreServiceDelegate* delegate,
948     chrome::HostDesktopType host_desktop_type) {
949   return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
950 }
951 
RemoveTabEntryById(SessionID::id_type id)952 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
953     SessionID::id_type id) {
954   return helper_.RemoveTabEntryById(id);
955 }
956 
957 std::vector<content::WebContents*>
RestoreEntryById(TabRestoreServiceDelegate * delegate,SessionID::id_type id,chrome::HostDesktopType host_desktop_type,WindowOpenDisposition disposition)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);
964 }
965 
IsLoaded() const966 bool PersistentTabRestoreService::IsLoaded() const {
967   return delegate_->IsLoaded();
968 }
969 
DeleteLastSession()970 void PersistentTabRestoreService::DeleteLastSession() {
971   return delegate_->DeleteLastSession();
972 }
973 
Shutdown()974 void PersistentTabRestoreService::Shutdown() {
975   return delegate_->Shutdown();
976 }
977 
LoadTabsFromLastSession()978 void PersistentTabRestoreService::LoadTabsFromLastSession() {
979   delegate_->LoadTabsFromLastSession();
980 }
981 
mutable_entries()982 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
983   return &helper_.entries_;
984 }
985 
PruneEntries()986 void PersistentTabRestoreService::PruneEntries() {
987   helper_.PruneEntries();
988 }
989 
BuildServiceInstanceFor(content::BrowserContext * profile) const990 KeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor(
991     content::BrowserContext* profile) const {
992   return new PersistentTabRestoreService(static_cast<Profile*>(profile), NULL);
993 }
994