Home | History | Annotate | Download | only in sessions
      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/time/time.h"
     19 #include "chrome/browser/common/cancelable_request.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/sessions/base_session_service.h"
     22 #include "chrome/browser/sessions/session_command.h"
     23 #include "chrome/browser/sessions/session_service.h"
     24 #include "chrome/browser/sessions/session_service_factory.h"
     25 #include "chrome/browser/sessions/tab_restore_service_factory.h"
     26 #include "chrome/common/cancelable_task_tracker.h"
     27 #include "content/public/browser/session_storage_namespace.h"
     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 
    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   CancelableTaskTracker cancelable_task_tracker_;
    235 
    236   DISALLOW_COPY_AND_ASSIGN(Delegate);
    237 };
    238 
    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 
    248 PersistentTabRestoreService::Delegate::~Delegate() {}
    249 
    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 
    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 
    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 
    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 
    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 
    365 bool PersistentTabRestoreService::Delegate::IsLoaded() const {
    366   return !(load_state_ & (NOT_LOADED | LOADING));
    367 }
    368 
    369 // static
    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 
    380 void PersistentTabRestoreService::Delegate::Shutdown() {
    381   if (backend())
    382     Save();
    383 }
    384 
    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 
    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
    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*
    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*
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    909 PersistentTabRestoreService::~PersistentTabRestoreService() {}
    910 
    911 void PersistentTabRestoreService::AddObserver(
    912     TabRestoreServiceObserver* observer) {
    913   helper_.AddObserver(observer);
    914 }
    915 
    916 void PersistentTabRestoreService::RemoveObserver(
    917     TabRestoreServiceObserver* observer) {
    918   helper_.RemoveObserver(observer);
    919 }
    920 
    921 void PersistentTabRestoreService::CreateHistoricalTab(
    922     content::WebContents* contents,
    923     int index) {
    924   helper_.CreateHistoricalTab(contents, index);
    925 }
    926 
    927 void PersistentTabRestoreService::BrowserClosing(
    928     TabRestoreServiceDelegate* delegate) {
    929   helper_.BrowserClosing(delegate);
    930 }
    931 
    932 void PersistentTabRestoreService::BrowserClosed(
    933     TabRestoreServiceDelegate* delegate) {
    934   helper_.BrowserClosed(delegate);
    935 }
    936 
    937 void PersistentTabRestoreService::ClearEntries() {
    938   helper_.ClearEntries();
    939 }
    940 
    941 const TabRestoreService::Entries& PersistentTabRestoreService::entries() const {
    942   return helper_.entries();
    943 }
    944 
    945 std::vector<content::WebContents*>
    946 PersistentTabRestoreService::RestoreMostRecentEntry(
    947     TabRestoreServiceDelegate* delegate,
    948     chrome::HostDesktopType host_desktop_type) {
    949   return helper_.RestoreMostRecentEntry(delegate, host_desktop_type);
    950 }
    951 
    952 TabRestoreService::Tab* PersistentTabRestoreService::RemoveTabEntryById(
    953     SessionID::id_type id) {
    954   return helper_.RemoveTabEntryById(id);
    955 }
    956 
    957 std::vector<content::WebContents*>
    958     PersistentTabRestoreService::RestoreEntryById(
    959       TabRestoreServiceDelegate* delegate,
    960       SessionID::id_type id,
    961       chrome::HostDesktopType host_desktop_type,
    962       WindowOpenDisposition disposition) {
    963   return helper_.RestoreEntryById(delegate, id, host_desktop_type, disposition);
    964 }
    965 
    966 bool PersistentTabRestoreService::IsLoaded() const {
    967   return delegate_->IsLoaded();
    968 }
    969 
    970 void PersistentTabRestoreService::DeleteLastSession() {
    971   return delegate_->DeleteLastSession();
    972 }
    973 
    974 void PersistentTabRestoreService::Shutdown() {
    975   return delegate_->Shutdown();
    976 }
    977 
    978 void PersistentTabRestoreService::LoadTabsFromLastSession() {
    979   delegate_->LoadTabsFromLastSession();
    980 }
    981 
    982 TabRestoreService::Entries* PersistentTabRestoreService::mutable_entries() {
    983   return &helper_.entries_;
    984 }
    985 
    986 void PersistentTabRestoreService::PruneEntries() {
    987   helper_.PruneEntries();
    988 }
    989 
    990 BrowserContextKeyedService* TabRestoreServiceFactory::BuildServiceInstanceFor(
    991     content::BrowserContext* profile) const {
    992   return new PersistentTabRestoreService(static_cast<Profile*>(profile), NULL);
    993 }
    994