Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sessions / persistent_tab_restore_service.cc
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/profiles/profile.h"
21 #include "chrome/browser/sessions/base_session_service.h"
22 #include "chrome/browser/sessions/base_session_service_commands.h"
23 #include "chrome/browser/sessions/base_session_service_delegate_impl.h"
24 #include "chrome/browser/sessions/session_command.h"
25 #include "chrome/browser/sessions/session_service_factory.h"
26 #include "chrome/browser/sessions/tab_restore_service_factory.h"
27 #include "content/public/browser/session_storage_namespace.h"
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 // This restore service will create and own a BaseSessionService and implement
117 // the required BaseSessionServiceDelegateImpl.
118 class PersistentTabRestoreService::Delegate
119     : public BaseSessionServiceDelegateImpl,
120       public TabRestoreServiceHelper::Observer {
121  public:
122   explicit Delegate(Profile* profile);
123
124   ~Delegate() override;
125
126   // BaseSessionServiceDelegateImpl:
127   void OnWillSaveCommands() override;
128
129   // TabRestoreServiceHelper::Observer:
130   void OnClearEntries() override;
131   void OnRestoreEntryById(SessionID::id_type id,
132                           Entries::const_iterator entry_iterator) override;
133   void OnAddEntry() override;
134
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   void DeleteLastSession();
143
144   bool IsLoaded() const;
145
146   // Creates and add entries to |entries| for each of the windows in |windows|.
147   static void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows,
148                                        std::vector<Entry*>* entries);
149
150   void Shutdown();
151
152   // Schedules the commands for a window close.
153   void ScheduleCommandsForWindow(const Window& window);
154
155   // Schedules the commands for a tab close. |selected_index| gives the index of
156   // the selected navigation.
157   void ScheduleCommandsForTab(const Tab& tab, int selected_index);
158
159   // Creates a window close command.
160   static scoped_ptr<SessionCommand> CreateWindowCommand(SessionID::id_type id,
161                                                         int selected_tab_index,
162                                                         int num_tabs,
163                                                         base::Time timestamp);
164
165   // Creates a tab close command.
166   static scoped_ptr<SessionCommand> CreateSelectedNavigationInTabCommand(
167       SessionID::id_type tab_id,
168       int32 index,
169       base::Time timestamp);
170
171   // Creates a restore command.
172   static scoped_ptr<SessionCommand> CreateRestoredEntryCommand(
173     SessionID::id_type entry_id);
174
175   // Returns the index to persist as the selected index. This is the same as
176   // |tab.current_navigation_index| unless the entry at
177   // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if no
178   // valid navigation to persist.
179   int GetSelectedNavigationIndexToPersist(const Tab& tab);
180
181   // Invoked when we've loaded the session commands that identify the previously
182   // closed tabs. This creates entries, adds them to staging_entries_, and
183   // invokes LoadState.
184   void OnGotLastSessionCommands(ScopedVector<SessionCommand> commands);
185
186   // Populates |loaded_entries| with Entries from |commands|.
187   void CreateEntriesFromCommands(const std::vector<SessionCommand*>& commands,
188                                  std::vector<Entry*>* loaded_entries);
189
190   // Validates all entries in |entries|, deleting any with no navigations. This
191   // also deletes any entries beyond the max number of entries we can hold.
192   static void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
193
194   // Callback from BaseSessionService when we've received the windows from the
195   // previous session. This creates and add entries to |staging_entries_| and
196   // invokes LoadStateChanged. |ignored_active_window| is ignored because we
197   // don't need to restore activation.
198   void OnGotPreviousSession(ScopedVector<SessionWindow> windows,
199                             SessionID::id_type ignored_active_window);
200
201   // Converts a SessionWindow into a Window, returning true on success. We use 0
202   // as the timestamp here since we do not know when the window/tab was closed.
203   static bool ConvertSessionWindowToWindow(SessionWindow* session_window,
204                                            Window* window);
205
206   // Invoked when previous tabs or session is loaded. If both have finished
207   // loading the entries in |staging_entries_| are added to entries and
208   // observers are notified.
209   void LoadStateChanged();
210
211   // If |id_to_entry| contains an entry for |id| the corresponding entry is
212   // deleted and removed from both |id_to_entry| and |entries|. This is used
213   // when creating entries from the backend file.
214   void RemoveEntryByID(SessionID::id_type id,
215                        IDToEntry* id_to_entry,
216                        std::vector<TabRestoreService::Entry*>* entries);
217
218  private:
219   scoped_ptr<BaseSessionService> base_session_service_;
220
221   // The associated profile.
222   Profile* profile_;
223
224   TabRestoreServiceHelper* tab_restore_service_helper_;
225
226   // The number of entries to write.
227   int entries_to_write_;
228
229   // Number of entries we've written.
230   int entries_written_;
231
232   // Whether we've loaded the last session.
233   int load_state_;
234
235   // Results from previously closed tabs/sessions is first added here. When the
236   // results from both us and the session restore service have finished loading
237   // LoadStateChanged is invoked, which adds these entries to entries_.
238   ScopedVector<Entry> staging_entries_;
239
240   // Used when loading previous tabs/session and open tabs/session.
241   base::CancelableTaskTracker cancelable_task_tracker_;
242
243   DISALLOW_COPY_AND_ASSIGN(Delegate);
244 };
245
246 PersistentTabRestoreService::Delegate::Delegate(Profile* profile)
247     : BaseSessionServiceDelegateImpl(true),
248       base_session_service_(
249           new BaseSessionService(BaseSessionService::TAB_RESTORE,
250                                  profile->GetPath(),
251                                  this)),
252       profile_(profile),
253       tab_restore_service_helper_(NULL),
254       entries_to_write_(0),
255       entries_written_(0),
256       load_state_(NOT_LOADED) {
257   // We should never be created when incognito.
258   DCHECK(!profile->IsOffTheRecord());
259 }
260
261 PersistentTabRestoreService::Delegate::~Delegate() {}
262
263 void PersistentTabRestoreService::Delegate::OnWillSaveCommands() {
264   const Entries& entries = tab_restore_service_helper_->entries();
265   int to_write_count = std::min(entries_to_write_,
266                                 static_cast<int>(entries.size()));
267   entries_to_write_ = 0;
268   if (entries_written_ + to_write_count > kEntriesPerReset) {
269     to_write_count = entries.size();
270     base_session_service_->set_pending_reset(true);
271   }
272   if (to_write_count) {
273     // Write the to_write_count most recently added entries out. The most
274     // recently added entry is at the front, so we use a reverse iterator to
275     // write in the order the entries were added.
276     Entries::const_reverse_iterator i = entries.rbegin();
277     DCHECK(static_cast<size_t>(to_write_count) <= entries.size());
278     std::advance(i, entries.size() - static_cast<int>(to_write_count));
279     for (; i != entries.rend(); ++i) {
280       Entry* entry = *i;
281       if (entry->type == TAB) {
282         Tab* tab = static_cast<Tab*>(entry);
283         int selected_index = GetSelectedNavigationIndexToPersist(*tab);
284         if (selected_index != -1)
285           ScheduleCommandsForTab(*tab, selected_index);
286       } else {
287         ScheduleCommandsForWindow(*static_cast<Window*>(entry));
288       }
289       entries_written_++;
290     }
291   }
292   if (base_session_service_->pending_reset())
293     entries_written_ = 0;
294 }
295
296 void PersistentTabRestoreService::Delegate::OnClearEntries() {
297   // Mark all the tabs as closed so that we don't attempt to restore them.
298   const Entries& entries = tab_restore_service_helper_->entries();
299   for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
300     base_session_service_->ScheduleCommand(
301         CreateRestoredEntryCommand((*i)->id).Pass());
302
303   entries_to_write_ = 0;
304
305   // Schedule a pending reset so that we nuke the file on next write.
306   base_session_service_->set_pending_reset(true);
307
308   // Schedule a command, otherwise if there are no pending commands Save does
309   // nothing.
310   base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(1).Pass());
311 }
312
313 void PersistentTabRestoreService::Delegate::OnRestoreEntryById(
314     SessionID::id_type id,
315     Entries::const_iterator entry_iterator) {
316   size_t index = 0;
317   const Entries& entries = tab_restore_service_helper_->entries();
318   for (Entries::const_iterator j = entries.begin();
319        j != entry_iterator && j != entries.end();
320        ++j, ++index) {}
321   if (static_cast<int>(index) < entries_to_write_)
322     entries_to_write_--;
323
324   base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(id).Pass());
325 }
326
327 void PersistentTabRestoreService::Delegate::OnAddEntry() {
328   // Start the save timer, when it fires we'll generate the commands.
329   base_session_service_->StartSaveTimer();
330   entries_to_write_++;
331 }
332
333 void PersistentTabRestoreService::Delegate::LoadTabsFromLastSession() {
334   if (load_state_ != NOT_LOADED)
335     return;
336
337   if (tab_restore_service_helper_->entries().size() == kMaxEntries) {
338     // We already have the max number of entries we can take. There is no point
339     // in attempting to load since we'll just drop the results. Skip to loaded.
340     load_state_ = (LOADING | LOADED_LAST_SESSION | LOADED_LAST_TABS);
341     LoadStateChanged();
342     return;
343   }
344
345 #if !defined(ENABLE_SESSION_SERVICE)
346   // If sessions are not stored in the SessionService, default to
347   // |LOADED_LAST_SESSION| state.
348   load_state_ = LOADING | LOADED_LAST_SESSION;
349 #else
350   load_state_ = LOADING;
351
352   SessionService* session_service =
353       SessionServiceFactory::GetForProfile(profile_);
354   Profile::ExitType exit_type = profile_->GetLastSessionExitType();
355   if (!profile_->restored_last_session() && session_service &&
356       (exit_type == Profile::EXIT_CRASHED ||
357        exit_type == Profile::EXIT_SESSION_ENDED)) {
358     // The previous session crashed and wasn't restored, or was a forced
359     // shutdown. Both of which won't have notified us of the browser close so
360     // that we need to load the windows from session service (which will have
361     // saved them).
362     session_service->GetLastSession(
363         base::Bind(&Delegate::OnGotPreviousSession, base::Unretained(this)),
364         &cancelable_task_tracker_);
365   } else {
366     load_state_ |= LOADED_LAST_SESSION;
367   }
368 #endif
369
370   // Request the tabs closed in the last session. If the last session crashed,
371   // this won't contain the tabs/window that were open at the point of the
372   // crash (the call to GetLastSession above requests those).
373   base_session_service_->ScheduleGetLastSessionCommands(
374       base::Bind(&Delegate::OnGotLastSessionCommands, base::Unretained(this)),
375       &cancelable_task_tracker_);
376 }
377
378 void PersistentTabRestoreService::Delegate::DeleteLastSession() {
379   base_session_service_->DeleteLastSession();
380 }
381
382 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
383   return !(load_state_ & (NOT_LOADED | LOADING));
384 }
385
386 // static
387 void PersistentTabRestoreService::Delegate::CreateEntriesFromWindows(
388     std::vector<SessionWindow*>* windows,
389     std::vector<Entry*>* entries) {
390   for (size_t i = 0; i < windows->size(); ++i) {
391     scoped_ptr<Window> window(new Window());
392     if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
393       entries->push_back(window.release());
394   }
395 }
396
397 void PersistentTabRestoreService::Delegate::Shutdown() {
398   base_session_service_->Save();
399 }
400
401 void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
402     const Window& window) {
403   DCHECK(!window.tabs.empty());
404   int selected_tab = window.selected_tab_index;
405   int valid_tab_count = 0;
406   int real_selected_tab = selected_tab;
407   for (size_t i = 0; i < window.tabs.size(); ++i) {
408     if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
409       valid_tab_count++;
410     } else if (static_cast<int>(i) < selected_tab) {
411       real_selected_tab--;
412     }
413   }
414   if (valid_tab_count == 0)
415     return;  // No tabs to persist.
416
417   base_session_service_->ScheduleCommand(
418       CreateWindowCommand(window.id,
419                           std::min(real_selected_tab, valid_tab_count - 1),
420                           valid_tab_count,
421                           window.timestamp).Pass());
422
423   if (!window.app_name.empty()) {
424     base_session_service_->ScheduleCommand(
425         CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
426                                       window.id,
427                                       window.app_name).Pass());
428   }
429
430   for (size_t i = 0; i < window.tabs.size(); ++i) {
431     int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
432     if (selected_index != -1)
433       ScheduleCommandsForTab(window.tabs[i], selected_index);
434   }
435 }
436
437 void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
438     const Tab& tab,
439     int selected_index) {
440   const std::vector<sessions::SerializedNavigationEntry>& navigations =
441       tab.navigations;
442   int max_index = static_cast<int>(navigations.size());
443
444   // Determine the first navigation we'll persist.
445   int valid_count_before_selected = 0;
446   int first_index_to_persist = selected_index;
447   for (int i = selected_index - 1; i >= 0 &&
448        valid_count_before_selected < gMaxPersistNavigationCount; --i) {
449     if (ShouldTrackEntry(navigations[i].virtual_url())) {
450       first_index_to_persist = i;
451       valid_count_before_selected++;
452     }
453   }
454
455   // Write the command that identifies the selected tab.
456   base_session_service_->ScheduleCommand(
457       CreateSelectedNavigationInTabCommand(tab.id,
458                                            valid_count_before_selected,
459                                            tab.timestamp).Pass());
460
461   if (tab.pinned) {
462     PinnedStatePayload payload = true;
463     scoped_ptr<SessionCommand> command(
464         new SessionCommand(kCommandPinnedState, sizeof(payload)));
465     memcpy(command->contents(), &payload, sizeof(payload));
466     base_session_service_->ScheduleCommand(command.Pass());
467   }
468
469   if (!tab.extension_app_id.empty()) {
470     base_session_service_->ScheduleCommand(
471         CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID,
472                                           tab.id,
473                                           tab.extension_app_id).Pass());
474   }
475
476   if (!tab.user_agent_override.empty()) {
477     base_session_service_->ScheduleCommand(
478         CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
479                                              tab.id,
480                                              tab.user_agent_override).Pass());
481   }
482
483   // Then write the navigations.
484   for (int i = first_index_to_persist, wrote_count = 0;
485        wrote_count < 2 * gMaxPersistNavigationCount && i < max_index; ++i) {
486     if (ShouldTrackEntry(navigations[i].virtual_url())) {
487       base_session_service_->ScheduleCommand(
488           CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
489                                            tab.id,
490                                            navigations[i]));
491     }
492   }
493 }
494
495 // static
496 scoped_ptr<SessionCommand>
497 PersistentTabRestoreService::Delegate::CreateWindowCommand(
498     SessionID::id_type id,
499     int selected_tab_index,
500     int num_tabs,
501     base::Time timestamp) {
502   WindowPayload2 payload;
503   // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
504   // uninitialized memory in the struct.
505   memset(&payload, 0, sizeof(payload));
506   payload.window_id = id;
507   payload.selected_tab_index = selected_tab_index;
508   payload.num_tabs = num_tabs;
509   payload.timestamp = timestamp.ToInternalValue();
510
511   scoped_ptr<SessionCommand> command(
512       new SessionCommand(kCommandWindow, sizeof(payload)));
513   memcpy(command->contents(), &payload, sizeof(payload));
514   return command;
515 }
516
517 // static
518 scoped_ptr<SessionCommand>
519 PersistentTabRestoreService::Delegate::CreateSelectedNavigationInTabCommand(
520     SessionID::id_type tab_id,
521     int32 index,
522     base::Time timestamp) {
523   SelectedNavigationInTabPayload2 payload;
524   payload.id = tab_id;
525   payload.index = index;
526   payload.timestamp = timestamp.ToInternalValue();
527   scoped_ptr<SessionCommand> command(
528       new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)));
529   memcpy(command->contents(), &payload, sizeof(payload));
530   return command;
531 }
532
533 // static
534 scoped_ptr<SessionCommand>
535 PersistentTabRestoreService::Delegate::CreateRestoredEntryCommand(
536     SessionID::id_type entry_id) {
537   RestoredEntryPayload payload = entry_id;
538   scoped_ptr<SessionCommand> command(
539       new SessionCommand(kCommandRestoredEntry, sizeof(payload)));
540   memcpy(command->contents(), &payload, sizeof(payload));
541   return command;
542 }
543
544 int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
545     const Tab& tab) {
546   const std::vector<sessions::SerializedNavigationEntry>& navigations =
547       tab.navigations;
548   int selected_index = tab.current_navigation_index;
549   int max_index = static_cast<int>(navigations.size());
550
551   // Find the first navigation to persist. We won't persist the selected
552   // navigation if ShouldTrackEntry returns false.
553   while (selected_index >= 0 &&
554          !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
555     selected_index--;
556   }
557
558   if (selected_index != -1)
559     return selected_index;
560
561   // Couldn't find a navigation to persist going back, go forward.
562   selected_index = tab.current_navigation_index + 1;
563   while (selected_index < max_index &&
564          !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
565     selected_index++;
566   }
567
568   return (selected_index == max_index) ? -1 : selected_index;
569 }
570
571 void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
572     ScopedVector<SessionCommand> commands) {
573   std::vector<Entry*> entries;
574   CreateEntriesFromCommands(commands.get(), &entries);
575   // Closed tabs always go to the end.
576   staging_entries_.insert(staging_entries_.end(), entries.begin(),
577                           entries.end());
578   load_state_ |= LOADED_LAST_TABS;
579   LoadStateChanged();
580 }
581
582 void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
583     const std::vector<SessionCommand*>& commands,
584     std::vector<Entry*>* loaded_entries) {
585   if (tab_restore_service_helper_->entries().size() == kMaxEntries)
586     return;
587
588   // Iterate through the commands populating entries and id_to_entry.
589   ScopedVector<Entry> entries;
590   IDToEntry id_to_entry;
591   // If non-null we're processing the navigations of this tab.
592   Tab* current_tab = NULL;
593   // If non-null we're processing the tabs of this window.
594   Window* current_window = NULL;
595   // If > 0, we've gotten a window command but not all the tabs yet.
596   int pending_window_tabs = 0;
597   for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
598        i != commands.end(); ++i) {
599     const SessionCommand& command = *(*i);
600     switch (command.id()) {
601       case kCommandRestoredEntry: {
602         if (pending_window_tabs > 0) {
603           // Should never receive a restored command while waiting for all the
604           // tabs in a window.
605           return;
606         }
607
608         current_tab = NULL;
609         current_window = NULL;
610
611         RestoredEntryPayload payload;
612         if (!command.GetPayload(&payload, sizeof(payload)))
613           return;
614         RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
615         break;
616       }
617
618       case kCommandWindow: {
619         WindowPayload2 payload;
620         if (pending_window_tabs > 0) {
621           // Should never receive a window command while waiting for all the
622           // tabs in a window.
623           return;
624         }
625
626         // Try the new payload first
627         if (!command.GetPayload(&payload, sizeof(payload))) {
628           // then the old payload
629           WindowPayload old_payload;
630           if (!command.GetPayload(&old_payload, sizeof(old_payload)))
631             return;
632
633           // Copy the old payload data to the new payload.
634           payload.window_id = old_payload.window_id;
635           payload.selected_tab_index = old_payload.selected_tab_index;
636           payload.num_tabs = old_payload.num_tabs;
637           // Since we don't have a time use time 0 which is used to mark as an
638           // unknown timestamp.
639           payload.timestamp = 0;
640         }
641
642         pending_window_tabs = payload.num_tabs;
643         if (pending_window_tabs <= 0) {
644           // Should always have at least 1 tab. Likely indicates corruption.
645           return;
646         }
647
648         RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
649
650         current_window = new Window();
651         current_window->selected_tab_index = payload.selected_tab_index;
652         current_window->timestamp =
653             base::Time::FromInternalValue(payload.timestamp);
654         entries.push_back(current_window);
655         id_to_entry[payload.window_id] = current_window;
656         break;
657       }
658
659       case kCommandSelectedNavigationInTab: {
660         SelectedNavigationInTabPayload2 payload;
661         if (!command.GetPayload(&payload, sizeof(payload))) {
662           SelectedNavigationInTabPayload old_payload;
663           if (!command.GetPayload(&old_payload, sizeof(old_payload)))
664             return;
665           payload.id = old_payload.id;
666           payload.index = old_payload.index;
667           // Since we don't have a time use time 0 which is used to mark as an
668           // unknown timestamp.
669           payload.timestamp = 0;
670         }
671
672         if (pending_window_tabs > 0) {
673           if (!current_window) {
674             // We should have created a window already.
675             NOTREACHED();
676             return;
677           }
678           current_window->tabs.resize(current_window->tabs.size() + 1);
679           current_tab = &(current_window->tabs.back());
680           if (--pending_window_tabs == 0)
681             current_window = NULL;
682         } else {
683           RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
684           current_tab = new Tab();
685           id_to_entry[payload.id] = current_tab;
686           current_tab->timestamp =
687               base::Time::FromInternalValue(payload.timestamp);
688           entries.push_back(current_tab);
689         }
690         current_tab->current_navigation_index = payload.index;
691         break;
692       }
693
694       case kCommandUpdateTabNavigation: {
695         if (!current_tab) {
696           // Should be in a tab when we get this.
697           return;
698         }
699         current_tab->navigations.resize(current_tab->navigations.size() + 1);
700         SessionID::id_type tab_id;
701         if (!RestoreUpdateTabNavigationCommand(command,
702                                                &current_tab->navigations.back(),
703                                                &tab_id)) {
704           return;
705         }
706         break;
707       }
708
709       case kCommandPinnedState: {
710         if (!current_tab) {
711           // Should be in a tab when we get this.
712           return;
713         }
714         // NOTE: payload doesn't matter. kCommandPinnedState is only written if
715         // tab is pinned.
716         current_tab->pinned = true;
717         break;
718       }
719
720       case kCommandSetWindowAppName: {
721         if (!current_window) {
722           // We should have created a window already.
723           NOTREACHED();
724           return;
725         }
726
727         SessionID::id_type window_id;
728         std::string app_name;
729         if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
730           return;
731
732         current_window->app_name.swap(app_name);
733         break;
734       }
735
736       case kCommandSetExtensionAppID: {
737         if (!current_tab) {
738           // Should be in a tab when we get this.
739           return;
740         }
741         SessionID::id_type tab_id;
742         std::string extension_app_id;
743         if (!RestoreSetTabExtensionAppIDCommand(command,
744                                                 &tab_id,
745                                                 &extension_app_id)) {
746           return;
747         }
748         current_tab->extension_app_id.swap(extension_app_id);
749         break;
750       }
751
752       case kCommandSetTabUserAgentOverride: {
753         if (!current_tab) {
754           // Should be in a tab when we get this.
755           return;
756         }
757         SessionID::id_type tab_id;
758         std::string user_agent_override;
759         if (!RestoreSetTabUserAgentOverrideCommand(command,
760                                                    &tab_id,
761                                                    &user_agent_override)) {
762           return;
763         }
764         current_tab->user_agent_override.swap(user_agent_override);
765         break;
766       }
767
768       default:
769         // Unknown type, usually indicates corruption of file. Ignore it.
770         return;
771     }
772   }
773
774   // If there was corruption some of the entries won't be valid.
775   ValidateAndDeleteEmptyEntries(&(entries.get()));
776
777   loaded_entries->swap(entries.get());
778 }
779
780 // static
781 void PersistentTabRestoreService::Delegate::ValidateAndDeleteEmptyEntries(
782     std::vector<Entry*>* entries) {
783   std::vector<Entry*> valid_entries;
784   std::vector<Entry*> invalid_entries;
785
786   // Iterate from the back so that we keep the most recently closed entries.
787   for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
788        i != entries->rend(); ++i) {
789     if (TabRestoreServiceHelper::ValidateEntry(*i))
790       valid_entries.push_back(*i);
791     else
792       invalid_entries.push_back(*i);
793   }
794   // NOTE: at this point the entries are ordered with newest at the front.
795   entries->swap(valid_entries);
796
797   // Delete the remaining entries.
798   STLDeleteElements(&invalid_entries);
799 }
800
801 void PersistentTabRestoreService::Delegate::OnGotPreviousSession(
802     ScopedVector<SessionWindow> windows,
803     SessionID::id_type ignored_active_window) {
804   std::vector<Entry*> entries;
805   CreateEntriesFromWindows(&windows.get(), &entries);
806   // Previous session tabs go first.
807   staging_entries_.insert(staging_entries_.begin(), entries.begin(),
808                           entries.end());
809   load_state_ |= LOADED_LAST_SESSION;
810   LoadStateChanged();
811 }
812
813 bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
814     SessionWindow* session_window,
815     Window* window) {
816   for (size_t i = 0; i < session_window->tabs.size(); ++i) {
817     if (!session_window->tabs[i]->navigations.empty()) {
818       window->tabs.resize(window->tabs.size() + 1);
819       Tab& tab = window->tabs.back();
820       tab.pinned = session_window->tabs[i]->pinned;
821       tab.navigations.swap(session_window->tabs[i]->navigations);
822       tab.current_navigation_index =
823           session_window->tabs[i]->current_navigation_index;
824       tab.extension_app_id = session_window->tabs[i]->extension_app_id;
825       tab.timestamp = base::Time();
826     }
827   }
828   if (window->tabs.empty())
829     return false;
830
831   window->selected_tab_index =
832       std::min(session_window->selected_tab_index,
833                static_cast<int>(window->tabs.size() - 1));
834   window->timestamp = base::Time();
835   return true;
836 }
837
838 void PersistentTabRestoreService::Delegate::LoadStateChanged() {
839   if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
840       (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
841     // Still waiting on previous session or previous tabs.
842     return;
843   }
844
845   // We're done loading.
846   load_state_ ^= LOADING;
847
848   const Entries& entries = tab_restore_service_helper_->entries();
849   if (staging_entries_.empty() || entries.size() >= kMaxEntries) {
850     staging_entries_.clear();
851     tab_restore_service_helper_->NotifyLoaded();
852     return;
853   }
854
855   if (staging_entries_.size() + entries.size() > kMaxEntries) {
856     // If we add all the staged entries we'll end up with more than
857     // kMaxEntries. Delete entries such that we only end up with at most
858     // kMaxEntries.
859     int surplus = kMaxEntries - entries.size();
860     CHECK_LE(0, surplus);
861     CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
862     staging_entries_.erase(
863         staging_entries_.begin() + (kMaxEntries - entries.size()),
864         staging_entries_.end());
865   }
866
867   // And add them.
868   for (size_t i = 0; i < staging_entries_.size(); ++i) {
869     staging_entries_[i]->from_last_session = true;
870     tab_restore_service_helper_->AddEntry(staging_entries_[i], false, false);
871   }
872
873   // AddEntry takes ownership of the entry, need to clear out entries so that
874   // it doesn't delete them.
875   staging_entries_.weak_clear();
876
877   // Make it so we rewrite all the tabs. We need to do this otherwise we won't
878   // correctly write out the entries when Save is invoked (Save starts from
879   // the front, not the end and we just added the entries to the end).
880   entries_to_write_ = staging_entries_.size();
881
882   tab_restore_service_helper_->PruneEntries();
883   tab_restore_service_helper_->NotifyTabsChanged();
884
885   tab_restore_service_helper_->NotifyLoaded();
886 }
887
888 void PersistentTabRestoreService::Delegate::RemoveEntryByID(
889     SessionID::id_type id,
890     IDToEntry* id_to_entry,
891     std::vector<TabRestoreService::Entry*>* entries) {
892   // Look for the entry in the map. If it is present, erase it from both
893   // collections and return.
894   IDToEntry::iterator i = id_to_entry->find(id);
895   if (i != id_to_entry->end()) {
896     entries->erase(std::find(entries->begin(), entries->end(), i->second));
897     delete i->second;
898     id_to_entry->erase(i);
899     return;
900   }
901
902   // Otherwise, loop over all items in the map and see if any of the Windows
903   // have Tabs with the |id|.
904   for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
905        ++i) {
906     if (i->second->type == TabRestoreService::WINDOW) {
907       TabRestoreService::Window* window =
908           static_cast<TabRestoreService::Window*>(i->second);
909       std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
910       for ( ; j != window->tabs.end(); ++j) {
911         // If the ID matches one of this window's tabs, remove it from the
912         // list.
913         if ((*j).id == id) {
914           window->tabs.erase(j);
915           return;
916         }
917       }
918     }
919   }
920 }
921
922 // PersistentTabRestoreService -------------------------------------------------
923
924 PersistentTabRestoreService::PersistentTabRestoreService(
925     Profile* profile,
926     TimeFactory* time_factory)
927     : delegate_(new Delegate(profile)),
928       helper_(this, delegate_.get(), profile, time_factory) {
929   delegate_->set_tab_restore_service_helper(&helper_);
930 }
931
932 PersistentTabRestoreService::~PersistentTabRestoreService() {}
933
934 void PersistentTabRestoreService::AddObserver(
935     TabRestoreServiceObserver* observer) {
936   helper_.AddObserver(observer);
937 }
938
939 void PersistentTabRestoreService::RemoveObserver(
940     TabRestoreServiceObserver* observer) {
941   helper_.RemoveObserver(observer);
942 }
943
944 void PersistentTabRestoreService::CreateHistoricalTab(
945     content::WebContents* contents,
946     int index) {
947   helper_.CreateHistoricalTab(contents, index);
948 }
949
950 void PersistentTabRestoreService::BrowserClosing(
951     TabRestoreServiceDelegate* delegate) {
952   helper_.BrowserClosing(delegate);
953 }
954
955 void PersistentTabRestoreService::BrowserClosed(
956     TabRestoreServiceDelegate* delegate) {
957   helper_.BrowserClosed(delegate);
958 }
959
960 void PersistentTabRestoreService::ClearEntries() {
961   helper_.ClearEntries();
962 }
963
964 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
965   return helper_.entries();
966 }
967
968 std::vector<content::WebContents*>
969 PersistentTabRestoreService::RestoreMostRecentEntry(
970     TabRestoreServiceDelegate* delegate,
971     chrome::HostDesktopType host_desktop_type) {
972   return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
973 }
974
975 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
976     SessionID::id_type id) {
977   return helper_.RemoveTabEntryById(id);
978 }
979
980 std::vector<content::WebContents*>
981     PersistentTabRestoreService::RestoreEntryById(
982       TabRestoreServiceDelegate* delegate,
983       SessionID::id_type id,
984       chrome::HostDesktopType host_desktop_type,
985       WindowOpenDisposition disposition) {
986   return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition);
987 }
988
989 bool PersistentTabRestoreService::IsLoaded() const {
990   return delegate_->IsLoaded();
991 }
992
993 void PersistentTabRestoreService::DeleteLastSession() {
994   return delegate_->DeleteLastSession();
995 }
996
997 void PersistentTabRestoreService::Shutdown() {
998   return delegate_->Shutdown();
999 }
1000
1001 void PersistentTabRestoreService::LoadTabsFromLastSession() {
1002   delegate_->LoadTabsFromLastSession();
1003 }
1004
1005 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
1006   return &helper_.entries_;
1007 }
1008
1009 void PersistentTabRestoreService::PruneEntries() {
1010   helper_.PruneEntries();
1011 }
1012
1013 KeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor(
1014     content::BrowserContext* profile) const {
1015   return new PersistentTabRestoreService(static_cast<Profile*>(profile), NULL);
1016 }