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/session_service.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <utility>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/command_line.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/metrics/histogram.h"
     17 #include "base/pickle.h"
     18 #include "base/threading/thread.h"
     19 #include "chrome/browser/background/background_mode_manager.h"
     20 #include "chrome/browser/browser_process.h"
     21 #include "chrome/browser/chrome_notification_types.h"
     22 #include "chrome/browser/defaults.h"
     23 #include "chrome/browser/extensions/tab_helper.h"
     24 #include "chrome/browser/prefs/session_startup_pref.h"
     25 #include "chrome/browser/profiles/profile.h"
     26 #include "chrome/browser/sessions/session_backend.h"
     27 #include "chrome/browser/sessions/session_command.h"
     28 #include "chrome/browser/sessions/session_data_deleter.h"
     29 #include "chrome/browser/sessions/session_restore.h"
     30 #include "chrome/browser/sessions/session_tab_helper.h"
     31 #include "chrome/browser/sessions/session_types.h"
     32 #include "chrome/browser/ui/browser_iterator.h"
     33 #include "chrome/browser/ui/browser_list.h"
     34 #include "chrome/browser/ui/browser_tabstrip.h"
     35 #include "chrome/browser/ui/browser_window.h"
     36 #include "chrome/browser/ui/host_desktop.h"
     37 #include "chrome/browser/ui/startup/startup_browser_creator.h"
     38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     39 #include "components/startup_metric_utils/startup_metric_utils.h"
     40 #include "content/public/browser/navigation_details.h"
     41 #include "content/public/browser/navigation_entry.h"
     42 #include "content/public/browser/notification_details.h"
     43 #include "content/public/browser/notification_service.h"
     44 #include "content/public/browser/session_storage_namespace.h"
     45 #include "content/public/browser/web_contents.h"
     46 #include "extensions/common/extension.h"
     47 
     48 #if defined(OS_MACOSX)
     49 #include "chrome/browser/app_controller_mac.h"
     50 #endif
     51 
     52 using base::Time;
     53 using content::NavigationEntry;
     54 using content::WebContents;
     55 using sessions::SerializedNavigationEntry;
     56 
     57 // Identifier for commands written to file.
     58 static const SessionCommand::id_type kCommandSetTabWindow = 0;
     59 // OBSOLETE Superseded by kCommandSetWindowBounds3.
     60 // static const SessionCommand::id_type kCommandSetWindowBounds = 1;
     61 static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2;
     62 // Original kCommandTabClosed/kCommandWindowClosed. See comment in
     63 // MigrateClosedPayload for details on why they were replaced.
     64 static const SessionCommand::id_type kCommandTabClosedObsolete = 3;
     65 static const SessionCommand::id_type kCommandWindowClosedObsolete = 4;
     66 static const SessionCommand::id_type
     67     kCommandTabNavigationPathPrunedFromBack = 5;
     68 static const SessionCommand::id_type kCommandUpdateTabNavigation = 6;
     69 static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7;
     70 static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8;
     71 static const SessionCommand::id_type kCommandSetWindowType = 9;
     72 // OBSOLETE Superseded by kCommandSetWindowBounds3. Except for data migration.
     73 // static const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
     74 static const SessionCommand::id_type
     75     kCommandTabNavigationPathPrunedFromFront = 11;
     76 static const SessionCommand::id_type kCommandSetPinnedState = 12;
     77 static const SessionCommand::id_type kCommandSetExtensionAppID = 13;
     78 static const SessionCommand::id_type kCommandSetWindowBounds3 = 14;
     79 static const SessionCommand::id_type kCommandSetWindowAppName = 15;
     80 static const SessionCommand::id_type kCommandTabClosed = 16;
     81 static const SessionCommand::id_type kCommandWindowClosed = 17;
     82 static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 18;
     83 static const SessionCommand::id_type kCommandSessionStorageAssociated = 19;
     84 static const SessionCommand::id_type kCommandSetActiveWindow = 20;
     85 
     86 // Every kWritesPerReset commands triggers recreating the file.
     87 static const int kWritesPerReset = 250;
     88 
     89 namespace {
     90 
     91 // Various payload structures.
     92 struct ClosedPayload {
     93   SessionID::id_type id;
     94   int64 close_time;
     95 };
     96 
     97 struct WindowBoundsPayload2 {
     98   SessionID::id_type window_id;
     99   int32 x;
    100   int32 y;
    101   int32 w;
    102   int32 h;
    103   bool is_maximized;
    104 };
    105 
    106 struct WindowBoundsPayload3 {
    107   SessionID::id_type window_id;
    108   int32 x;
    109   int32 y;
    110   int32 w;
    111   int32 h;
    112   int32 show_state;
    113 };
    114 
    115 typedef SessionID::id_type ActiveWindowPayload;
    116 
    117 struct IDAndIndexPayload {
    118   SessionID::id_type id;
    119   int32 index;
    120 };
    121 
    122 typedef IDAndIndexPayload TabIndexInWindowPayload;
    123 
    124 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload;
    125 
    126 typedef IDAndIndexPayload SelectedNavigationIndexPayload;
    127 
    128 typedef IDAndIndexPayload SelectedTabInIndexPayload;
    129 
    130 typedef IDAndIndexPayload WindowTypePayload;
    131 
    132 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
    133 
    134 struct PinnedStatePayload {
    135   SessionID::id_type tab_id;
    136   bool pinned_state;
    137 };
    138 
    139 // Returns the show state to store to disk based |state|.
    140 ui::WindowShowState AdjustShowState(ui::WindowShowState state) {
    141   switch (state) {
    142     case ui::SHOW_STATE_NORMAL:
    143     case ui::SHOW_STATE_MINIMIZED:
    144     case ui::SHOW_STATE_MAXIMIZED:
    145     case ui::SHOW_STATE_FULLSCREEN:
    146     case ui::SHOW_STATE_DETACHED:
    147       return state;
    148 
    149     case ui::SHOW_STATE_DEFAULT:
    150     case ui::SHOW_STATE_INACTIVE:
    151     case ui::SHOW_STATE_END:
    152       return ui::SHOW_STATE_NORMAL;
    153   }
    154   return ui::SHOW_STATE_NORMAL;
    155 }
    156 
    157 // Migrates a |ClosedPayload|, returning true on success (migration was
    158 // necessary and happened), or false (migration was not necessary or was not
    159 // successful).
    160 bool MigrateClosedPayload(const SessionCommand& command,
    161                           ClosedPayload* payload) {
    162 #if defined(OS_CHROMEOS)
    163   // Pre M17 versions of chromeos were 32bit. Post M17 is 64 bit. Apparently the
    164   // 32 bit versions of chrome on pre M17 resulted in a sizeof 12 for the
    165   // ClosedPayload, where as post M17 64-bit gives a sizeof 16 (presumably the
    166   // struct is padded).
    167   if ((command.id() == kCommandWindowClosedObsolete ||
    168        command.id() == kCommandTabClosedObsolete) &&
    169       command.size() == 12 && sizeof(payload->id) == 4 &&
    170       sizeof(payload->close_time) == 8) {
    171     memcpy(&payload->id, command.contents(), 4);
    172     memcpy(&payload->close_time, command.contents() + 4, 8);
    173     return true;
    174   } else {
    175     return false;
    176   }
    177 #else
    178   return false;
    179 #endif
    180 }
    181 
    182 }  // namespace
    183 
    184 // SessionService -------------------------------------------------------------
    185 
    186 SessionService::SessionService(Profile* profile)
    187     : BaseSessionService(SESSION_RESTORE, profile, base::FilePath()),
    188       has_open_trackable_browsers_(false),
    189       move_on_new_browser_(false),
    190       save_delay_in_millis_(base::TimeDelta::FromMilliseconds(2500)),
    191       save_delay_in_mins_(base::TimeDelta::FromMinutes(10)),
    192       save_delay_in_hrs_(base::TimeDelta::FromHours(8)),
    193       force_browser_not_alive_with_no_windows_(false) {
    194   Init();
    195 }
    196 
    197 SessionService::SessionService(const base::FilePath& save_path)
    198     : BaseSessionService(SESSION_RESTORE, NULL, save_path),
    199       has_open_trackable_browsers_(false),
    200       move_on_new_browser_(false),
    201       save_delay_in_millis_(base::TimeDelta::FromMilliseconds(2500)),
    202       save_delay_in_mins_(base::TimeDelta::FromMinutes(10)),
    203       save_delay_in_hrs_(base::TimeDelta::FromHours(8)),
    204       force_browser_not_alive_with_no_windows_(false)  {
    205   Init();
    206 }
    207 
    208 SessionService::~SessionService() {
    209   // The BrowserList should outlive the SessionService since it's static and
    210   // the SessionService is a BrowserContextKeyedService.
    211   BrowserList::RemoveObserver(this);
    212   Save();
    213 }
    214 
    215 bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open) {
    216   return RestoreIfNecessary(urls_to_open, NULL);
    217 }
    218 
    219 void SessionService::ResetFromCurrentBrowsers() {
    220   ScheduleReset();
    221 }
    222 
    223 void SessionService::MoveCurrentSessionToLastSession() {
    224   pending_tab_close_ids_.clear();
    225   window_closing_ids_.clear();
    226   pending_window_close_ids_.clear();
    227 
    228   Save();
    229 
    230   RunTaskOnBackendThread(
    231       FROM_HERE, base::Bind(&SessionBackend::MoveCurrentSessionToLastSession,
    232                             backend()));
    233 }
    234 
    235 void SessionService::SetTabWindow(const SessionID& window_id,
    236                                   const SessionID& tab_id) {
    237   if (!ShouldTrackChangesToWindow(window_id))
    238     return;
    239 
    240   ScheduleCommand(CreateSetTabWindowCommand(window_id, tab_id));
    241 }
    242 
    243 void SessionService::SetWindowBounds(const SessionID& window_id,
    244                                      const gfx::Rect& bounds,
    245                                      ui::WindowShowState show_state) {
    246   if (!ShouldTrackChangesToWindow(window_id))
    247     return;
    248 
    249   ScheduleCommand(CreateSetWindowBoundsCommand(window_id, bounds, show_state));
    250 }
    251 
    252 void SessionService::SetTabIndexInWindow(const SessionID& window_id,
    253                                          const SessionID& tab_id,
    254                                          int new_index) {
    255   if (!ShouldTrackChangesToWindow(window_id))
    256     return;
    257 
    258   ScheduleCommand(CreateSetTabIndexInWindowCommand(tab_id, new_index));
    259 }
    260 
    261 void SessionService::SetPinnedState(const SessionID& window_id,
    262                                     const SessionID& tab_id,
    263                                     bool is_pinned) {
    264   if (!ShouldTrackChangesToWindow(window_id))
    265     return;
    266 
    267   ScheduleCommand(CreatePinnedStateCommand(tab_id, is_pinned));
    268 }
    269 
    270 void SessionService::TabClosed(const SessionID& window_id,
    271                                const SessionID& tab_id,
    272                                bool closed_by_user_gesture) {
    273   if (!tab_id.id())
    274     return;  // Hapens when the tab is replaced.
    275 
    276   if (!ShouldTrackChangesToWindow(window_id))
    277     return;
    278 
    279   IdToRange::iterator i = tab_to_available_range_.find(tab_id.id());
    280   if (i != tab_to_available_range_.end())
    281     tab_to_available_range_.erase(i);
    282 
    283   if (find(pending_window_close_ids_.begin(), pending_window_close_ids_.end(),
    284            window_id.id()) != pending_window_close_ids_.end()) {
    285     // Tab is in last window. Don't commit it immediately, instead add it to the
    286     // list of tabs to close. If the user creates another window, the close is
    287     // committed.
    288     pending_tab_close_ids_.insert(tab_id.id());
    289   } else if (find(window_closing_ids_.begin(), window_closing_ids_.end(),
    290                   window_id.id()) != window_closing_ids_.end() ||
    291              !IsOnlyOneTabLeft() ||
    292              closed_by_user_gesture) {
    293     // Close is the result of one of the following:
    294     // . window close (and it isn't the last window).
    295     // . closing a tab and there are other windows/tabs open.
    296     // . closed by a user gesture.
    297     // In all cases we need to mark the tab as explicitly closed.
    298     ScheduleCommand(CreateTabClosedCommand(tab_id.id()));
    299   } else {
    300     // User closed the last tab in the last tabbed browser. Don't mark the
    301     // tab closed.
    302     pending_tab_close_ids_.insert(tab_id.id());
    303     has_open_trackable_browsers_ = false;
    304   }
    305 }
    306 
    307 void SessionService::WindowOpened(Browser* browser) {
    308   if (!ShouldTrackBrowser(browser))
    309     return;
    310 
    311   AppType app_type = browser->is_app() ? TYPE_APP : TYPE_NORMAL;
    312   RestoreIfNecessary(std::vector<GURL>(), browser);
    313   SetWindowType(browser->session_id(), browser->type(), app_type);
    314   SetWindowAppName(browser->session_id(), browser->app_name());
    315 }
    316 
    317 void SessionService::WindowClosing(const SessionID& window_id) {
    318   if (!ShouldTrackChangesToWindow(window_id))
    319     return;
    320 
    321   // The window is about to close. If there are other tabbed browsers with the
    322   // same original profile commit the close immediately.
    323   //
    324   // NOTE: if the user chooses the exit menu item session service is destroyed
    325   // and this code isn't hit.
    326   if (has_open_trackable_browsers_) {
    327     // Closing a window can never make has_open_trackable_browsers_ go from
    328     // false to true, so only update it if already true.
    329     has_open_trackable_browsers_ = HasOpenTrackableBrowsers(window_id);
    330   }
    331   if (should_record_close_as_pending())
    332     pending_window_close_ids_.insert(window_id.id());
    333   else
    334     window_closing_ids_.insert(window_id.id());
    335 }
    336 
    337 void SessionService::WindowClosed(const SessionID& window_id) {
    338   if (!ShouldTrackChangesToWindow(window_id)) {
    339     // The last window may be one that is not tracked.
    340     MaybeDeleteSessionOnlyData();
    341     return;
    342   }
    343 
    344   windows_tracking_.erase(window_id.id());
    345 
    346   if (window_closing_ids_.find(window_id.id()) != window_closing_ids_.end()) {
    347     window_closing_ids_.erase(window_id.id());
    348     ScheduleCommand(CreateWindowClosedCommand(window_id.id()));
    349   } else if (pending_window_close_ids_.find(window_id.id()) ==
    350              pending_window_close_ids_.end()) {
    351     // We'll hit this if user closed the last tab in a window.
    352     has_open_trackable_browsers_ = HasOpenTrackableBrowsers(window_id);
    353     if (should_record_close_as_pending())
    354       pending_window_close_ids_.insert(window_id.id());
    355     else
    356       ScheduleCommand(CreateWindowClosedCommand(window_id.id()));
    357   }
    358   MaybeDeleteSessionOnlyData();
    359 }
    360 
    361 void SessionService::SetWindowType(const SessionID& window_id,
    362                                    Browser::Type type,
    363                                    AppType app_type) {
    364   if (!should_track_changes_for_browser_type(type, app_type))
    365     return;
    366 
    367   windows_tracking_.insert(window_id.id());
    368 
    369   // The user created a new tabbed browser with our profile. Commit any
    370   // pending closes.
    371   CommitPendingCloses();
    372 
    373   has_open_trackable_browsers_ = true;
    374   move_on_new_browser_ = true;
    375 
    376   ScheduleCommand(
    377       CreateSetWindowTypeCommand(window_id, WindowTypeForBrowserType(type)));
    378 }
    379 
    380 void SessionService::SetWindowAppName(
    381     const SessionID& window_id,
    382     const std::string& app_name) {
    383   if (!ShouldTrackChangesToWindow(window_id))
    384     return;
    385 
    386   ScheduleCommand(CreateSetTabExtensionAppIDCommand(
    387                       kCommandSetWindowAppName,
    388                       window_id.id(),
    389                       app_name));
    390 }
    391 
    392 void SessionService::TabNavigationPathPrunedFromBack(const SessionID& window_id,
    393                                                      const SessionID& tab_id,
    394                                                      int count) {
    395   if (!ShouldTrackChangesToWindow(window_id))
    396     return;
    397 
    398   TabNavigationPathPrunedFromBackPayload payload = { 0 };
    399   payload.id = tab_id.id();
    400   payload.index = count;
    401   SessionCommand* command =
    402       new SessionCommand(kCommandTabNavigationPathPrunedFromBack,
    403                          sizeof(payload));
    404   memcpy(command->contents(), &payload, sizeof(payload));
    405   ScheduleCommand(command);
    406 }
    407 
    408 void SessionService::TabNavigationPathPrunedFromFront(
    409     const SessionID& window_id,
    410     const SessionID& tab_id,
    411     int count) {
    412   if (!ShouldTrackChangesToWindow(window_id))
    413     return;
    414 
    415   // Update the range of indices.
    416   if (tab_to_available_range_.find(tab_id.id()) !=
    417       tab_to_available_range_.end()) {
    418     std::pair<int, int>& range = tab_to_available_range_[tab_id.id()];
    419     range.first = std::max(0, range.first - count);
    420     range.second = std::max(0, range.second - count);
    421   }
    422 
    423   TabNavigationPathPrunedFromFrontPayload payload = { 0 };
    424   payload.id = tab_id.id();
    425   payload.index = count;
    426   SessionCommand* command =
    427       new SessionCommand(kCommandTabNavigationPathPrunedFromFront,
    428                          sizeof(payload));
    429   memcpy(command->contents(), &payload, sizeof(payload));
    430   ScheduleCommand(command);
    431 }
    432 
    433 void SessionService::UpdateTabNavigation(
    434     const SessionID& window_id,
    435     const SessionID& tab_id,
    436     const SerializedNavigationEntry& navigation) {
    437   if (!ShouldTrackEntry(navigation.virtual_url()) ||
    438       !ShouldTrackChangesToWindow(window_id)) {
    439     return;
    440   }
    441 
    442   if (tab_to_available_range_.find(tab_id.id()) !=
    443       tab_to_available_range_.end()) {
    444     std::pair<int, int>& range = tab_to_available_range_[tab_id.id()];
    445     range.first = std::min(navigation.index(), range.first);
    446     range.second = std::max(navigation.index(), range.second);
    447   }
    448   ScheduleCommand(CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
    449                                                    tab_id.id(), navigation));
    450 }
    451 
    452 void SessionService::TabRestored(WebContents* tab, bool pinned) {
    453   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(tab);
    454   if (!ShouldTrackChangesToWindow(session_tab_helper->window_id()))
    455     return;
    456 
    457   BuildCommandsForTab(session_tab_helper->window_id(), tab, -1,
    458                       pinned, &pending_commands(), NULL);
    459   StartSaveTimer();
    460 }
    461 
    462 void SessionService::SetSelectedNavigationIndex(const SessionID& window_id,
    463                                                 const SessionID& tab_id,
    464                                                 int index) {
    465   if (!ShouldTrackChangesToWindow(window_id))
    466     return;
    467 
    468   if (tab_to_available_range_.find(tab_id.id()) !=
    469       tab_to_available_range_.end()) {
    470     if (index < tab_to_available_range_[tab_id.id()].first ||
    471         index > tab_to_available_range_[tab_id.id()].second) {
    472       // The new index is outside the range of what we've archived, schedule
    473       // a reset.
    474       ResetFromCurrentBrowsers();
    475       return;
    476     }
    477   }
    478   ScheduleCommand(CreateSetSelectedNavigationIndexCommand(tab_id, index));
    479 }
    480 
    481 void SessionService::SetSelectedTabInWindow(const SessionID& window_id,
    482                                             int index) {
    483   if (!ShouldTrackChangesToWindow(window_id))
    484     return;
    485 
    486   ScheduleCommand(CreateSetSelectedTabInWindow(window_id, index));
    487 }
    488 
    489 void SessionService::SetTabUserAgentOverride(
    490     const SessionID& window_id,
    491     const SessionID& tab_id,
    492     const std::string& user_agent_override) {
    493   if (!ShouldTrackChangesToWindow(window_id))
    494     return;
    495 
    496   ScheduleCommand(CreateSetTabUserAgentOverrideCommand(
    497       kCommandSetTabUserAgentOverride, tab_id.id(), user_agent_override));
    498 }
    499 
    500 CancelableTaskTracker::TaskId SessionService::GetLastSession(
    501     const SessionCallback& callback,
    502     CancelableTaskTracker* tracker) {
    503   // OnGotSessionCommands maps the SessionCommands to browser state, then run
    504   // the callback.
    505   return ScheduleGetLastSessionCommands(
    506       base::Bind(&SessionService::OnGotSessionCommands,
    507                  base::Unretained(this), callback),
    508       tracker);
    509 }
    510 
    511 void SessionService::Save() {
    512   bool had_commands = !pending_commands().empty();
    513   BaseSessionService::Save();
    514   if (had_commands) {
    515     RecordSessionUpdateHistogramData(chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
    516                                      &last_updated_save_time_);
    517     content::NotificationService::current()->Notify(
    518         chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
    519         content::Source<Profile>(profile()),
    520         content::NotificationService::NoDetails());
    521   }
    522 }
    523 
    524 void SessionService::Init() {
    525   // Register for the notifications we're interested in.
    526   registrar_.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED,
    527                  content::NotificationService::AllSources());
    528   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED,
    529                  content::NotificationService::AllSources());
    530   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
    531                  content::NotificationService::AllSources());
    532   registrar_.Add(
    533       this, chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
    534       content::NotificationService::AllSources());
    535 
    536   BrowserList::AddObserver(this);
    537 }
    538 
    539 bool SessionService::processed_any_commands() {
    540   return backend()->inited() || !pending_commands().empty();
    541 }
    542 
    543 bool SessionService::ShouldNewWindowStartSession() {
    544   // ChromeOS and OSX have different ideas of application lifetime than
    545   // the other platforms.
    546   // On ChromeOS opening a new window should never start a new session.
    547 #if defined(OS_CHROMEOS)
    548   if (!force_browser_not_alive_with_no_windows_)
    549     return false;
    550 #endif
    551   if (!has_open_trackable_browsers_ &&
    552       !StartupBrowserCreator::InSynchronousProfileLaunch() &&
    553       !SessionRestore::IsRestoring(profile())
    554 #if defined(OS_MACOSX)
    555       // On OSX, a new window should not start a new session if it was opened
    556       // from the dock or the menubar.
    557       && !app_controller_mac::IsOpeningNewWindow()
    558 #endif  // OS_MACOSX
    559       ) {
    560     return true;
    561   }
    562   return false;
    563 }
    564 
    565 bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
    566                                         Browser* browser) {
    567   if (ShouldNewWindowStartSession()) {
    568     // We're going from no tabbed browsers to a tabbed browser (and not in
    569     // process startup), restore the last session.
    570     if (move_on_new_browser_) {
    571       // Make the current session the last.
    572       MoveCurrentSessionToLastSession();
    573       move_on_new_browser_ = false;
    574     }
    575     SessionStartupPref pref = StartupBrowserCreator::GetSessionStartupPref(
    576         *CommandLine::ForCurrentProcess(), profile());
    577     if (pref.type == SessionStartupPref::LAST) {
    578       SessionRestore::RestoreSession(
    579           profile(), browser,
    580           browser ? browser->host_desktop_type() : chrome::GetActiveDesktop(),
    581           browser ? 0 : SessionRestore::ALWAYS_CREATE_TABBED_BROWSER,
    582           urls_to_open);
    583       return true;
    584     }
    585   }
    586   return false;
    587 }
    588 
    589 void SessionService::Observe(int type,
    590                              const content::NotificationSource& source,
    591                              const content::NotificationDetails& details) {
    592   // All of our messages have the NavigationController as the source.
    593   switch (type) {
    594     case content::NOTIFICATION_NAV_LIST_PRUNED: {
    595       WebContents* web_contents =
    596           content::Source<content::NavigationController>(source).ptr()->
    597               GetWebContents();
    598       SessionTabHelper* session_tab_helper =
    599           SessionTabHelper::FromWebContents(web_contents);
    600       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
    601         return;
    602       content::Details<content::PrunedDetails> pruned_details(details);
    603       if (pruned_details->from_front) {
    604         TabNavigationPathPrunedFromFront(
    605             session_tab_helper->window_id(),
    606             session_tab_helper->session_id(),
    607             pruned_details->count);
    608       } else {
    609         TabNavigationPathPrunedFromBack(
    610             session_tab_helper->window_id(),
    611             session_tab_helper->session_id(),
    612             web_contents->GetController().GetEntryCount());
    613       }
    614       RecordSessionUpdateHistogramData(type,
    615                                        &last_updated_nav_list_pruned_time_);
    616       break;
    617     }
    618 
    619     case content::NOTIFICATION_NAV_ENTRY_CHANGED: {
    620       WebContents* web_contents =
    621           content::Source<content::NavigationController>(source).ptr()->
    622               GetWebContents();
    623       SessionTabHelper* session_tab_helper =
    624           SessionTabHelper::FromWebContents(web_contents);
    625       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
    626         return;
    627       content::Details<content::EntryChangedDetails> changed(details);
    628       const SerializedNavigationEntry navigation =
    629           SerializedNavigationEntry::FromNavigationEntry(
    630               changed->index, *changed->changed_entry);
    631       UpdateTabNavigation(session_tab_helper->window_id(),
    632                           session_tab_helper->session_id(),
    633                           navigation);
    634       break;
    635     }
    636 
    637     case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
    638       WebContents* web_contents =
    639           content::Source<content::NavigationController>(source).ptr()->
    640               GetWebContents();
    641       SessionTabHelper* session_tab_helper =
    642           SessionTabHelper::FromWebContents(web_contents);
    643       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
    644         return;
    645       int current_entry_index =
    646           web_contents->GetController().GetCurrentEntryIndex();
    647       SetSelectedNavigationIndex(
    648           session_tab_helper->window_id(),
    649           session_tab_helper->session_id(),
    650           current_entry_index);
    651       const SerializedNavigationEntry navigation =
    652           SerializedNavigationEntry::FromNavigationEntry(
    653               current_entry_index,
    654               *web_contents->GetController().GetEntryAtIndex(
    655                   current_entry_index));
    656       UpdateTabNavigation(
    657           session_tab_helper->window_id(),
    658           session_tab_helper->session_id(),
    659           navigation);
    660       content::Details<content::LoadCommittedDetails> changed(details);
    661       if (changed->type == content::NAVIGATION_TYPE_NEW_PAGE ||
    662         changed->type == content::NAVIGATION_TYPE_EXISTING_PAGE) {
    663         RecordSessionUpdateHistogramData(type,
    664                                          &last_updated_nav_entry_commit_time_);
    665       }
    666       break;
    667     }
    668 
    669     case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
    670       extensions::TabHelper* extension_tab_helper =
    671           content::Source<extensions::TabHelper>(source).ptr();
    672       if (extension_tab_helper->web_contents()->GetBrowserContext() !=
    673               profile()) {
    674         return;
    675       }
    676       if (extension_tab_helper->extension_app()) {
    677         SessionTabHelper* session_tab_helper =
    678             SessionTabHelper::FromWebContents(
    679                 extension_tab_helper->web_contents());
    680         SetTabExtensionAppID(session_tab_helper->window_id(),
    681                              session_tab_helper->session_id(),
    682                              extension_tab_helper->extension_app()->id());
    683       }
    684       break;
    685     }
    686 
    687     default:
    688       NOTREACHED();
    689   }
    690 }
    691 
    692 void SessionService::OnBrowserSetLastActive(Browser* browser) {
    693   if (ShouldTrackBrowser(browser))
    694     ScheduleCommand(CreateSetActiveWindowCommand(browser->session_id()));
    695 }
    696 
    697 void SessionService::SetTabExtensionAppID(
    698     const SessionID& window_id,
    699     const SessionID& tab_id,
    700     const std::string& extension_app_id) {
    701   if (!ShouldTrackChangesToWindow(window_id))
    702     return;
    703 
    704   ScheduleCommand(CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID,
    705       tab_id.id(), extension_app_id));
    706 }
    707 
    708 SessionCommand* SessionService::CreateSetSelectedTabInWindow(
    709     const SessionID& window_id,
    710     int index) {
    711   SelectedTabInIndexPayload payload = { 0 };
    712   payload.id = window_id.id();
    713   payload.index = index;
    714   SessionCommand* command = new SessionCommand(kCommandSetSelectedTabInIndex,
    715                                  sizeof(payload));
    716   memcpy(command->contents(), &payload, sizeof(payload));
    717   return command;
    718 }
    719 
    720 SessionCommand* SessionService::CreateSetTabWindowCommand(
    721     const SessionID& window_id,
    722     const SessionID& tab_id) {
    723   SessionID::id_type payload[] = { window_id.id(), tab_id.id() };
    724   SessionCommand* command =
    725       new SessionCommand(kCommandSetTabWindow, sizeof(payload));
    726   memcpy(command->contents(), payload, sizeof(payload));
    727   return command;
    728 }
    729 
    730 SessionCommand* SessionService::CreateSetWindowBoundsCommand(
    731     const SessionID& window_id,
    732     const gfx::Rect& bounds,
    733     ui::WindowShowState show_state) {
    734   WindowBoundsPayload3 payload = { 0 };
    735   payload.window_id = window_id.id();
    736   payload.x = bounds.x();
    737   payload.y = bounds.y();
    738   payload.w = bounds.width();
    739   payload.h = bounds.height();
    740   payload.show_state = AdjustShowState(show_state);
    741   SessionCommand* command = new SessionCommand(kCommandSetWindowBounds3,
    742                                                sizeof(payload));
    743   memcpy(command->contents(), &payload, sizeof(payload));
    744   return command;
    745 }
    746 
    747 SessionCommand* SessionService::CreateSetTabIndexInWindowCommand(
    748     const SessionID& tab_id,
    749     int new_index) {
    750   TabIndexInWindowPayload payload = { 0 };
    751   payload.id = tab_id.id();
    752   payload.index = new_index;
    753   SessionCommand* command =
    754       new SessionCommand(kCommandSetTabIndexInWindow, sizeof(payload));
    755   memcpy(command->contents(), &payload, sizeof(payload));
    756   return command;
    757 }
    758 
    759 SessionCommand* SessionService::CreateTabClosedCommand(
    760     const SessionID::id_type tab_id) {
    761   ClosedPayload payload;
    762   // Because of what appears to be a compiler bug setting payload to {0} doesn't
    763   // set the padding to 0, resulting in Purify reporting an UMR when we write
    764   // the structure to disk. To avoid this we explicitly memset the struct.
    765   memset(&payload, 0, sizeof(payload));
    766   payload.id = tab_id;
    767   payload.close_time = Time::Now().ToInternalValue();
    768   SessionCommand* command =
    769       new SessionCommand(kCommandTabClosed, sizeof(payload));
    770   memcpy(command->contents(), &payload, sizeof(payload));
    771   return command;
    772 }
    773 
    774 SessionCommand* SessionService::CreateWindowClosedCommand(
    775     const SessionID::id_type window_id) {
    776   ClosedPayload payload;
    777   // See comment in CreateTabClosedCommand as to why we do this.
    778   memset(&payload, 0, sizeof(payload));
    779   payload.id = window_id;
    780   payload.close_time = Time::Now().ToInternalValue();
    781   SessionCommand* command =
    782       new SessionCommand(kCommandWindowClosed, sizeof(payload));
    783   memcpy(command->contents(), &payload, sizeof(payload));
    784   return command;
    785 }
    786 
    787 SessionCommand* SessionService::CreateSetSelectedNavigationIndexCommand(
    788     const SessionID& tab_id,
    789     int index) {
    790   SelectedNavigationIndexPayload payload = { 0 };
    791   payload.id = tab_id.id();
    792   payload.index = index;
    793   SessionCommand* command = new SessionCommand(
    794       kCommandSetSelectedNavigationIndex, sizeof(payload));
    795   memcpy(command->contents(), &payload, sizeof(payload));
    796   return command;
    797 }
    798 
    799 SessionCommand* SessionService::CreateSetWindowTypeCommand(
    800     const SessionID& window_id,
    801     WindowType type) {
    802   WindowTypePayload payload = { 0 };
    803   payload.id = window_id.id();
    804   payload.index = static_cast<int32>(type);
    805   SessionCommand* command = new SessionCommand(
    806       kCommandSetWindowType, sizeof(payload));
    807   memcpy(command->contents(), &payload, sizeof(payload));
    808   return command;
    809 }
    810 
    811 SessionCommand* SessionService::CreatePinnedStateCommand(
    812     const SessionID& tab_id,
    813     bool is_pinned) {
    814   PinnedStatePayload payload = { 0 };
    815   payload.tab_id = tab_id.id();
    816   payload.pinned_state = is_pinned;
    817   SessionCommand* command =
    818       new SessionCommand(kCommandSetPinnedState, sizeof(payload));
    819   memcpy(command->contents(), &payload, sizeof(payload));
    820   return command;
    821 }
    822 
    823 SessionCommand* SessionService::CreateSessionStorageAssociatedCommand(
    824     const SessionID& tab_id,
    825     const std::string& session_storage_persistent_id) {
    826   Pickle pickle;
    827   pickle.WriteInt(tab_id.id());
    828   pickle.WriteString(session_storage_persistent_id);
    829   return new SessionCommand(kCommandSessionStorageAssociated, pickle);
    830 }
    831 
    832 SessionCommand* SessionService::CreateSetActiveWindowCommand(
    833     const SessionID& window_id) {
    834   ActiveWindowPayload payload = 0;
    835   payload = window_id.id();
    836   SessionCommand* command =
    837       new SessionCommand(kCommandSetActiveWindow, sizeof(payload));
    838   memcpy(command->contents(), &payload, sizeof(payload));
    839   return command;
    840 }
    841 
    842 void SessionService::OnGotSessionCommands(
    843     const SessionCallback& callback,
    844     ScopedVector<SessionCommand> commands) {
    845   ScopedVector<SessionWindow> valid_windows;
    846   SessionID::id_type active_window_id = 0;
    847 
    848   RestoreSessionFromCommands(
    849       commands.get(), &valid_windows.get(), &active_window_id);
    850   callback.Run(valid_windows.Pass(), active_window_id);
    851 }
    852 
    853 void SessionService::RestoreSessionFromCommands(
    854     const std::vector<SessionCommand*>& commands,
    855     std::vector<SessionWindow*>* valid_windows,
    856     SessionID::id_type* active_window_id) {
    857   std::map<int, SessionTab*> tabs;
    858   std::map<int, SessionWindow*> windows;
    859 
    860   VLOG(1) << "RestoreSessionFromCommands " << commands.size();
    861   if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
    862     AddTabsToWindows(&tabs, &windows);
    863     SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows);
    864     UpdateSelectedTabIndex(valid_windows);
    865   }
    866   STLDeleteValues(&tabs);
    867   // Don't delete conents of windows, that is done by the caller as all
    868   // valid windows are added to valid_windows.
    869 }
    870 
    871 void SessionService::UpdateSelectedTabIndex(
    872     std::vector<SessionWindow*>* windows) {
    873   for (std::vector<SessionWindow*>::const_iterator i = windows->begin();
    874        i != windows->end(); ++i) {
    875     // See note in SessionWindow as to why we do this.
    876     int new_index = 0;
    877     for (std::vector<SessionTab*>::const_iterator j = (*i)->tabs.begin();
    878          j != (*i)->tabs.end(); ++j) {
    879       if ((*j)->tab_visual_index == (*i)->selected_tab_index) {
    880         new_index = static_cast<int>(j - (*i)->tabs.begin());
    881         break;
    882       }
    883     }
    884     (*i)->selected_tab_index = new_index;
    885   }
    886 }
    887 
    888 SessionWindow* SessionService::GetWindow(
    889     SessionID::id_type window_id,
    890     IdToSessionWindow* windows) {
    891   std::map<int, SessionWindow*>::iterator i = windows->find(window_id);
    892   if (i == windows->end()) {
    893     SessionWindow* window = new SessionWindow();
    894     window->window_id.set_id(window_id);
    895     (*windows)[window_id] = window;
    896     return window;
    897   }
    898   return i->second;
    899 }
    900 
    901 SessionTab* SessionService::GetTab(
    902     SessionID::id_type tab_id,
    903     IdToSessionTab* tabs) {
    904   DCHECK(tabs);
    905   std::map<int, SessionTab*>::iterator i = tabs->find(tab_id);
    906   if (i == tabs->end()) {
    907     SessionTab* tab = new SessionTab();
    908     tab->tab_id.set_id(tab_id);
    909     (*tabs)[tab_id] = tab;
    910     return tab;
    911   }
    912   return i->second;
    913 }
    914 
    915 std::vector<SerializedNavigationEntry>::iterator
    916   SessionService::FindClosestNavigationWithIndex(
    917     std::vector<SerializedNavigationEntry>* navigations,
    918     int index) {
    919   DCHECK(navigations);
    920   for (std::vector<SerializedNavigationEntry>::iterator
    921            i = navigations->begin(); i != navigations->end(); ++i) {
    922     if (i->index() >= index)
    923       return i;
    924   }
    925   return navigations->end();
    926 }
    927 
    928 // Function used in sorting windows. Sorting is done based on window id. As
    929 // window ids increment for each new window, this effectively sorts by creation
    930 // time.
    931 static bool WindowOrderSortFunction(const SessionWindow* w1,
    932                                     const SessionWindow* w2) {
    933   return w1->window_id.id() < w2->window_id.id();
    934 }
    935 
    936 // Compares the two tabs based on visual index.
    937 static bool TabVisualIndexSortFunction(const SessionTab* t1,
    938                                        const SessionTab* t2) {
    939   const int delta = t1->tab_visual_index - t2->tab_visual_index;
    940   return delta == 0 ? (t1->tab_id.id() < t2->tab_id.id()) : (delta < 0);
    941 }
    942 
    943 void SessionService::SortTabsBasedOnVisualOrderAndPrune(
    944     std::map<int, SessionWindow*>* windows,
    945     std::vector<SessionWindow*>* valid_windows) {
    946   std::map<int, SessionWindow*>::iterator i = windows->begin();
    947   while (i != windows->end()) {
    948     SessionWindow* window = i->second;
    949     AppType app_type = window->app_name.empty() ? TYPE_NORMAL : TYPE_APP;
    950     if (window->tabs.empty() || window->is_constrained ||
    951         !should_track_changes_for_browser_type(
    952             static_cast<Browser::Type>(window->type),
    953             app_type)) {
    954       delete window;
    955       windows->erase(i++);
    956     } else {
    957       // Valid window; sort the tabs and add it to the list of valid windows.
    958       std::sort(window->tabs.begin(), window->tabs.end(),
    959                 &TabVisualIndexSortFunction);
    960       // Otherwise, add the window such that older windows appear first.
    961       if (valid_windows->empty()) {
    962         valid_windows->push_back(window);
    963       } else {
    964         valid_windows->insert(
    965             std::upper_bound(valid_windows->begin(), valid_windows->end(),
    966                              window, &WindowOrderSortFunction),
    967             window);
    968       }
    969       ++i;
    970     }
    971   }
    972 }
    973 
    974 void SessionService::AddTabsToWindows(std::map<int, SessionTab*>* tabs,
    975                                       std::map<int, SessionWindow*>* windows) {
    976   VLOG(1) << "AddTabsToWindws";
    977   VLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
    978   std::map<int, SessionTab*>::iterator i = tabs->begin();
    979   while (i != tabs->end()) {
    980     SessionTab* tab = i->second;
    981     if (tab->window_id.id() && !tab->navigations.empty()) {
    982       SessionWindow* window = GetWindow(tab->window_id.id(), windows);
    983       window->tabs.push_back(tab);
    984       tabs->erase(i++);
    985 
    986       // See note in SessionTab as to why we do this.
    987       std::vector<SerializedNavigationEntry>::iterator j =
    988           FindClosestNavigationWithIndex(&(tab->navigations),
    989                                          tab->current_navigation_index);
    990       if (j == tab->navigations.end()) {
    991         tab->current_navigation_index =
    992             static_cast<int>(tab->navigations.size() - 1);
    993       } else {
    994         tab->current_navigation_index =
    995             static_cast<int>(j - tab->navigations.begin());
    996       }
    997     } else {
    998       // Never got a set tab index in window, or tabs are empty, nothing
    999       // to do.
   1000       ++i;
   1001     }
   1002   }
   1003 }
   1004 
   1005 bool SessionService::CreateTabsAndWindows(
   1006     const std::vector<SessionCommand*>& data,
   1007     std::map<int, SessionTab*>* tabs,
   1008     std::map<int, SessionWindow*>* windows,
   1009     SessionID::id_type* active_window_id) {
   1010   // If the file is corrupt (command with wrong size, or unknown command), we
   1011   // still return true and attempt to restore what we we can.
   1012   VLOG(1) << "CreateTabsAndWindows";
   1013 
   1014   startup_metric_utils::ScopedSlowStartupUMA
   1015       scoped_timer("Startup.SlowStartupSessionServiceCreateTabsAndWindows");
   1016 
   1017   for (std::vector<SessionCommand*>::const_iterator i = data.begin();
   1018        i != data.end(); ++i) {
   1019     const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
   1020     const SessionCommand* command = *i;
   1021 
   1022     VLOG(1) << "Read command " << (int) command->id();
   1023     switch (command->id()) {
   1024       case kCommandSetTabWindow: {
   1025         SessionID::id_type payload[2];
   1026         if (!command->GetPayload(payload, sizeof(payload))) {
   1027           VLOG(1) << "Failed reading command " << command->id();
   1028           return true;
   1029         }
   1030         GetTab(payload[1], tabs)->window_id.set_id(payload[0]);
   1031         break;
   1032       }
   1033 
   1034       // This is here for forward migration only.  New data is saved with
   1035       // |kCommandSetWindowBounds3|.
   1036       case kCommandSetWindowBounds2: {
   1037         WindowBoundsPayload2 payload;
   1038         if (!command->GetPayload(&payload, sizeof(payload))) {
   1039           VLOG(1) << "Failed reading command " << command->id();
   1040           return true;
   1041         }
   1042         GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
   1043                                                               payload.y,
   1044                                                               payload.w,
   1045                                                               payload.h);
   1046         GetWindow(payload.window_id, windows)->show_state =
   1047             payload.is_maximized ?
   1048                 ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL;
   1049         break;
   1050       }
   1051 
   1052       case kCommandSetWindowBounds3: {
   1053         WindowBoundsPayload3 payload;
   1054         if (!command->GetPayload(&payload, sizeof(payload))) {
   1055           VLOG(1) << "Failed reading command " << command->id();
   1056           return true;
   1057         }
   1058         GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
   1059                                                               payload.y,
   1060                                                               payload.w,
   1061                                                               payload.h);
   1062         // SHOW_STATE_INACTIVE is not persisted.
   1063         ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
   1064         if (payload.show_state > ui::SHOW_STATE_DEFAULT &&
   1065             payload.show_state < ui::SHOW_STATE_END &&
   1066             payload.show_state != ui::SHOW_STATE_INACTIVE) {
   1067           show_state = static_cast<ui::WindowShowState>(payload.show_state);
   1068         } else {
   1069           NOTREACHED();
   1070         }
   1071         GetWindow(payload.window_id, windows)->show_state = show_state;
   1072         break;
   1073       }
   1074 
   1075       case kCommandSetTabIndexInWindow: {
   1076         TabIndexInWindowPayload payload;
   1077         if (!command->GetPayload(&payload, sizeof(payload))) {
   1078           VLOG(1) << "Failed reading command " << command->id();
   1079           return true;
   1080         }
   1081         GetTab(payload.id, tabs)->tab_visual_index = payload.index;
   1082         break;
   1083       }
   1084 
   1085       case kCommandTabClosedObsolete:
   1086       case kCommandWindowClosedObsolete:
   1087       case kCommandTabClosed:
   1088       case kCommandWindowClosed: {
   1089         ClosedPayload payload;
   1090         if (!command->GetPayload(&payload, sizeof(payload)) &&
   1091             !MigrateClosedPayload(*command, &payload)) {
   1092           VLOG(1) << "Failed reading command " << command->id();
   1093           return true;
   1094         }
   1095         if (command->id() == kCommandTabClosed ||
   1096             command->id() == kCommandTabClosedObsolete) {
   1097           delete GetTab(payload.id, tabs);
   1098           tabs->erase(payload.id);
   1099         } else {
   1100           delete GetWindow(payload.id, windows);
   1101           windows->erase(payload.id);
   1102         }
   1103         break;
   1104       }
   1105 
   1106       case kCommandTabNavigationPathPrunedFromBack: {
   1107         TabNavigationPathPrunedFromBackPayload payload;
   1108         if (!command->GetPayload(&payload, sizeof(payload))) {
   1109           VLOG(1) << "Failed reading command " << command->id();
   1110           return true;
   1111         }
   1112         SessionTab* tab = GetTab(payload.id, tabs);
   1113         tab->navigations.erase(
   1114             FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
   1115             tab->navigations.end());
   1116         break;
   1117       }
   1118 
   1119       case kCommandTabNavigationPathPrunedFromFront: {
   1120         TabNavigationPathPrunedFromFrontPayload payload;
   1121         if (!command->GetPayload(&payload, sizeof(payload)) ||
   1122             payload.index <= 0) {
   1123           VLOG(1) << "Failed reading command " << command->id();
   1124           return true;
   1125         }
   1126         SessionTab* tab = GetTab(payload.id, tabs);
   1127 
   1128         // Update the selected navigation index.
   1129         tab->current_navigation_index =
   1130             std::max(-1, tab->current_navigation_index - payload.index);
   1131 
   1132         // And update the index of existing navigations.
   1133         for (std::vector<SerializedNavigationEntry>::iterator
   1134                  i = tab->navigations.begin();
   1135              i != tab->navigations.end();) {
   1136           i->set_index(i->index() - payload.index);
   1137           if (i->index() < 0)
   1138             i = tab->navigations.erase(i);
   1139           else
   1140             ++i;
   1141         }
   1142         break;
   1143       }
   1144 
   1145       case kCommandUpdateTabNavigation: {
   1146         SerializedNavigationEntry navigation;
   1147         SessionID::id_type tab_id;
   1148         if (!RestoreUpdateTabNavigationCommand(
   1149                 *command, &navigation, &tab_id)) {
   1150           VLOG(1) << "Failed reading command " << command->id();
   1151           return true;
   1152         }
   1153         SessionTab* tab = GetTab(tab_id, tabs);
   1154         std::vector<SerializedNavigationEntry>::iterator i =
   1155             FindClosestNavigationWithIndex(&(tab->navigations),
   1156                                            navigation.index());
   1157         if (i != tab->navigations.end() && i->index() == navigation.index())
   1158           *i = navigation;
   1159         else
   1160           tab->navigations.insert(i, navigation);
   1161         break;
   1162       }
   1163 
   1164       case kCommandSetSelectedNavigationIndex: {
   1165         SelectedNavigationIndexPayload payload;
   1166         if (!command->GetPayload(&payload, sizeof(payload))) {
   1167           VLOG(1) << "Failed reading command " << command->id();
   1168           return true;
   1169         }
   1170         GetTab(payload.id, tabs)->current_navigation_index = payload.index;
   1171         break;
   1172       }
   1173 
   1174       case kCommandSetSelectedTabInIndex: {
   1175         SelectedTabInIndexPayload payload;
   1176         if (!command->GetPayload(&payload, sizeof(payload))) {
   1177           VLOG(1) << "Failed reading command " << command->id();
   1178           return true;
   1179         }
   1180         GetWindow(payload.id, windows)->selected_tab_index = payload.index;
   1181         break;
   1182       }
   1183 
   1184       case kCommandSetWindowType: {
   1185         WindowTypePayload payload;
   1186         if (!command->GetPayload(&payload, sizeof(payload))) {
   1187           VLOG(1) << "Failed reading command " << command->id();
   1188           return true;
   1189         }
   1190         GetWindow(payload.id, windows)->is_constrained = false;
   1191         GetWindow(payload.id, windows)->type =
   1192             BrowserTypeForWindowType(
   1193                 static_cast<WindowType>(payload.index));
   1194         break;
   1195       }
   1196 
   1197       case kCommandSetPinnedState: {
   1198         PinnedStatePayload payload;
   1199         if (!command->GetPayload(&payload, sizeof(payload))) {
   1200           VLOG(1) << "Failed reading command " << command->id();
   1201           return true;
   1202         }
   1203         GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state;
   1204         break;
   1205       }
   1206 
   1207       case kCommandSetWindowAppName: {
   1208         SessionID::id_type window_id;
   1209         std::string app_name;
   1210         if (!RestoreSetWindowAppNameCommand(*command, &window_id, &app_name))
   1211           return true;
   1212 
   1213         GetWindow(window_id, windows)->app_name.swap(app_name);
   1214         break;
   1215       }
   1216 
   1217       case kCommandSetExtensionAppID: {
   1218         SessionID::id_type tab_id;
   1219         std::string extension_app_id;
   1220         if (!RestoreSetTabExtensionAppIDCommand(
   1221                 *command, &tab_id, &extension_app_id)) {
   1222           VLOG(1) << "Failed reading command " << command->id();
   1223           return true;
   1224         }
   1225 
   1226         GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
   1227         break;
   1228       }
   1229 
   1230       case kCommandSetTabUserAgentOverride: {
   1231         SessionID::id_type tab_id;
   1232         std::string user_agent_override;
   1233         if (!RestoreSetTabUserAgentOverrideCommand(
   1234                 *command, &tab_id, &user_agent_override)) {
   1235           return true;
   1236         }
   1237 
   1238         GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override);
   1239         break;
   1240       }
   1241 
   1242       case kCommandSessionStorageAssociated: {
   1243         scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
   1244         SessionID::id_type command_tab_id;
   1245         std::string session_storage_persistent_id;
   1246         PickleIterator iter(*command_pickle.get());
   1247         if (!command_pickle->ReadInt(&iter, &command_tab_id) ||
   1248             !command_pickle->ReadString(&iter, &session_storage_persistent_id))
   1249           return true;
   1250         // Associate the session storage back.
   1251         GetTab(command_tab_id, tabs)->session_storage_persistent_id =
   1252             session_storage_persistent_id;
   1253         break;
   1254       }
   1255 
   1256       case kCommandSetActiveWindow: {
   1257         ActiveWindowPayload payload;
   1258         if (!command->GetPayload(&payload, sizeof(payload))) {
   1259           VLOG(1) << "Failed reading command " << command->id();
   1260           return true;
   1261         }
   1262         *active_window_id = payload;
   1263         break;
   1264       }
   1265 
   1266       default:
   1267         VLOG(1) << "Failed reading an unknown command " << command->id();
   1268         return true;
   1269     }
   1270   }
   1271   return true;
   1272 }
   1273 
   1274 void SessionService::BuildCommandsForTab(const SessionID& window_id,
   1275                                          WebContents* tab,
   1276                                          int index_in_window,
   1277                                          bool is_pinned,
   1278                                          std::vector<SessionCommand*>* commands,
   1279                                          IdToRange* tab_to_available_range) {
   1280   DCHECK(tab && commands && window_id.id());
   1281   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(tab);
   1282   const SessionID& session_id(session_tab_helper->session_id());
   1283   commands->push_back(CreateSetTabWindowCommand(window_id, session_id));
   1284 
   1285   const int current_index = tab->GetController().GetCurrentEntryIndex();
   1286   const int min_index = std::max(0,
   1287                                  current_index - max_persist_navigation_count);
   1288   const int max_index =
   1289       std::min(current_index + max_persist_navigation_count,
   1290                tab->GetController().GetEntryCount());
   1291   const int pending_index = tab->GetController().GetPendingEntryIndex();
   1292   if (tab_to_available_range) {
   1293     (*tab_to_available_range)[session_id.id()] =
   1294         std::pair<int, int>(min_index, max_index);
   1295   }
   1296 
   1297   if (is_pinned) {
   1298     commands->push_back(CreatePinnedStateCommand(session_id, true));
   1299   }
   1300 
   1301   extensions::TabHelper* extensions_tab_helper =
   1302       extensions::TabHelper::FromWebContents(tab);
   1303   if (extensions_tab_helper->extension_app()) {
   1304     commands->push_back(
   1305         CreateSetTabExtensionAppIDCommand(
   1306             kCommandSetExtensionAppID, session_id.id(),
   1307             extensions_tab_helper->extension_app()->id()));
   1308   }
   1309 
   1310   const std::string& ua_override = tab->GetUserAgentOverride();
   1311   if (!ua_override.empty()) {
   1312     commands->push_back(
   1313         CreateSetTabUserAgentOverrideCommand(
   1314             kCommandSetTabUserAgentOverride, session_id.id(), ua_override));
   1315   }
   1316 
   1317   for (int i = min_index; i < max_index; ++i) {
   1318     const NavigationEntry* entry = (i == pending_index) ?
   1319         tab->GetController().GetPendingEntry() :
   1320         tab->GetController().GetEntryAtIndex(i);
   1321     DCHECK(entry);
   1322     if (ShouldTrackEntry(entry->GetVirtualURL())) {
   1323       const SerializedNavigationEntry navigation =
   1324           SerializedNavigationEntry::FromNavigationEntry(i, *entry);
   1325       commands->push_back(
   1326           CreateUpdateTabNavigationCommand(
   1327               kCommandUpdateTabNavigation, session_id.id(), navigation));
   1328     }
   1329   }
   1330   commands->push_back(
   1331       CreateSetSelectedNavigationIndexCommand(session_id, current_index));
   1332 
   1333   if (index_in_window != -1) {
   1334     commands->push_back(
   1335         CreateSetTabIndexInWindowCommand(session_id, index_in_window));
   1336   }
   1337 
   1338   // Record the association between the sessionStorage namespace and the tab.
   1339   content::SessionStorageNamespace* session_storage_namespace =
   1340       tab->GetController().GetDefaultSessionStorageNamespace();
   1341   ScheduleCommand(CreateSessionStorageAssociatedCommand(
   1342       session_tab_helper->session_id(),
   1343       session_storage_namespace->persistent_id()));
   1344 }
   1345 
   1346 void SessionService::BuildCommandsForBrowser(
   1347     Browser* browser,
   1348     std::vector<SessionCommand*>* commands,
   1349     IdToRange* tab_to_available_range,
   1350     std::set<SessionID::id_type>* windows_to_track) {
   1351   DCHECK(browser && commands);
   1352   DCHECK(browser->session_id().id());
   1353 
   1354   commands->push_back(
   1355       CreateSetWindowBoundsCommand(browser->session_id(),
   1356                                    browser->window()->GetRestoredBounds(),
   1357                                    browser->window()->GetRestoredState()));
   1358 
   1359   commands->push_back(CreateSetWindowTypeCommand(
   1360       browser->session_id(), WindowTypeForBrowserType(browser->type())));
   1361 
   1362   if (!browser->app_name().empty()) {
   1363     commands->push_back(CreateSetWindowAppNameCommand(
   1364         kCommandSetWindowAppName,
   1365         browser->session_id().id(),
   1366         browser->app_name()));
   1367   }
   1368 
   1369   windows_to_track->insert(browser->session_id().id());
   1370   TabStripModel* tab_strip = browser->tab_strip_model();
   1371   for (int i = 0; i < tab_strip->count(); ++i) {
   1372     WebContents* tab = tab_strip->GetWebContentsAt(i);
   1373     DCHECK(tab);
   1374     BuildCommandsForTab(browser->session_id(), tab, i,
   1375                         tab_strip->IsTabPinned(i),
   1376                         commands, tab_to_available_range);
   1377   }
   1378 
   1379   commands->push_back(
   1380       CreateSetSelectedTabInWindow(browser->session_id(),
   1381                                    browser->tab_strip_model()->active_index()));
   1382 }
   1383 
   1384 void SessionService::BuildCommandsFromBrowsers(
   1385     std::vector<SessionCommand*>* commands,
   1386     IdToRange* tab_to_available_range,
   1387     std::set<SessionID::id_type>* windows_to_track) {
   1388   DCHECK(commands);
   1389   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
   1390     Browser* browser = *it;
   1391     // Make sure the browser has tabs and a window. Browser's destructor
   1392     // removes itself from the BrowserList. When a browser is closed the
   1393     // destructor is not necessarily run immediately. This means it's possible
   1394     // for us to get a handle to a browser that is about to be removed. If
   1395     // the tab count is 0 or the window is NULL, the browser is about to be
   1396     // deleted, so we ignore it.
   1397     if (ShouldTrackBrowser(browser) && browser->tab_strip_model()->count() &&
   1398         browser->window()) {
   1399       BuildCommandsForBrowser(browser, commands, tab_to_available_range,
   1400                               windows_to_track);
   1401     }
   1402   }
   1403 }
   1404 
   1405 void SessionService::ScheduleReset() {
   1406   set_pending_reset(true);
   1407   STLDeleteElements(&pending_commands());
   1408   tab_to_available_range_.clear();
   1409   windows_tracking_.clear();
   1410   BuildCommandsFromBrowsers(&pending_commands(), &tab_to_available_range_,
   1411                             &windows_tracking_);
   1412   if (!windows_tracking_.empty()) {
   1413     // We're lazily created on startup and won't get an initial batch of
   1414     // SetWindowType messages. Set these here to make sure our state is correct.
   1415     has_open_trackable_browsers_ = true;
   1416     move_on_new_browser_ = true;
   1417   }
   1418   StartSaveTimer();
   1419 }
   1420 
   1421 bool SessionService::ReplacePendingCommand(SessionCommand* command) {
   1422   // We optimize page navigations, which can happen quite frequently and
   1423   // are expensive. And activation is like Highlander, there can only be one!
   1424   if (command->id() != kCommandUpdateTabNavigation &&
   1425       command->id() != kCommandSetActiveWindow) {
   1426     return false;
   1427   }
   1428   for (std::vector<SessionCommand*>::reverse_iterator i =
   1429        pending_commands().rbegin(); i != pending_commands().rend(); ++i) {
   1430     SessionCommand* existing_command = *i;
   1431     if (command->id() == kCommandUpdateTabNavigation &&
   1432         existing_command->id() == kCommandUpdateTabNavigation) {
   1433       scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
   1434       PickleIterator iterator(*command_pickle);
   1435       SessionID::id_type command_tab_id;
   1436       int command_nav_index;
   1437       if (!command_pickle->ReadInt(&iterator, &command_tab_id) ||
   1438           !command_pickle->ReadInt(&iterator, &command_nav_index)) {
   1439         return false;
   1440       }
   1441       SessionID::id_type existing_tab_id;
   1442       int existing_nav_index;
   1443       {
   1444         // Creating a pickle like this means the Pickle references the data from
   1445         // the command. Make sure we delete the pickle before the command, else
   1446         // the pickle references deleted memory.
   1447         scoped_ptr<Pickle> existing_pickle(existing_command->PayloadAsPickle());
   1448         iterator = PickleIterator(*existing_pickle);
   1449         if (!existing_pickle->ReadInt(&iterator, &existing_tab_id) ||
   1450             !existing_pickle->ReadInt(&iterator, &existing_nav_index)) {
   1451           return false;
   1452         }
   1453       }
   1454       if (existing_tab_id == command_tab_id &&
   1455           existing_nav_index == command_nav_index) {
   1456         // existing_command is an update for the same tab/index pair. Replace
   1457         // it with the new one. We need to add to the end of the list just in
   1458         // case there is a prune command after the update command.
   1459         delete existing_command;
   1460         pending_commands().erase(i.base() - 1);
   1461         pending_commands().push_back(command);
   1462         return true;
   1463       }
   1464       return false;
   1465     }
   1466     if (command->id() == kCommandSetActiveWindow &&
   1467         existing_command->id() == kCommandSetActiveWindow) {
   1468       *i = command;
   1469       delete existing_command;
   1470       return true;
   1471     }
   1472   }
   1473   return false;
   1474 }
   1475 
   1476 void SessionService::ScheduleCommand(SessionCommand* command) {
   1477   DCHECK(command);
   1478   if (ReplacePendingCommand(command))
   1479     return;
   1480   BaseSessionService::ScheduleCommand(command);
   1481   // Don't schedule a reset on tab closed/window closed. Otherwise we may
   1482   // lose tabs/windows we want to restore from if we exit right after this.
   1483   if (!pending_reset() && pending_window_close_ids_.empty() &&
   1484       commands_since_reset() >= kWritesPerReset &&
   1485       (command->id() != kCommandTabClosed &&
   1486        command->id() != kCommandWindowClosed)) {
   1487     ScheduleReset();
   1488   }
   1489 }
   1490 
   1491 void SessionService::CommitPendingCloses() {
   1492   for (PendingTabCloseIDs::iterator i = pending_tab_close_ids_.begin();
   1493        i != pending_tab_close_ids_.end(); ++i) {
   1494     ScheduleCommand(CreateTabClosedCommand(*i));
   1495   }
   1496   pending_tab_close_ids_.clear();
   1497 
   1498   for (PendingWindowCloseIDs::iterator i = pending_window_close_ids_.begin();
   1499        i != pending_window_close_ids_.end(); ++i) {
   1500     ScheduleCommand(CreateWindowClosedCommand(*i));
   1501   }
   1502   pending_window_close_ids_.clear();
   1503 }
   1504 
   1505 bool SessionService::IsOnlyOneTabLeft() const {
   1506   if (!profile()) {
   1507     // We're testing, always return false.
   1508     return false;
   1509   }
   1510 
   1511   int window_count = 0;
   1512   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
   1513     Browser* browser = *it;
   1514     const SessionID::id_type window_id = browser->session_id().id();
   1515     if (ShouldTrackBrowser(browser) &&
   1516         window_closing_ids_.find(window_id) == window_closing_ids_.end()) {
   1517       if (++window_count > 1)
   1518         return false;
   1519       // By the time this is invoked the tab has been removed. As such, we use
   1520       // > 0 here rather than > 1.
   1521       if (browser->tab_strip_model()->count() > 0)
   1522         return false;
   1523     }
   1524   }
   1525   return true;
   1526 }
   1527 
   1528 bool SessionService::HasOpenTrackableBrowsers(
   1529     const SessionID& window_id) const {
   1530   if (!profile()) {
   1531     // We're testing, always return false.
   1532     return true;
   1533   }
   1534 
   1535   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
   1536     Browser* browser = *it;
   1537     const SessionID::id_type browser_id = browser->session_id().id();
   1538     if (browser_id != window_id.id() &&
   1539         window_closing_ids_.find(browser_id) == window_closing_ids_.end() &&
   1540         ShouldTrackBrowser(browser)) {
   1541       return true;
   1542     }
   1543   }
   1544   return false;
   1545 }
   1546 
   1547 bool SessionService::ShouldTrackChangesToWindow(
   1548     const SessionID& window_id) const {
   1549   return windows_tracking_.find(window_id.id()) != windows_tracking_.end();
   1550 }
   1551 
   1552 bool SessionService::ShouldTrackBrowser(Browser* browser) const {
   1553   AppType app_type = browser->is_app() ? TYPE_APP : TYPE_NORMAL;
   1554   return browser->profile() == profile() &&
   1555          should_track_changes_for_browser_type(browser->type(), app_type);
   1556 }
   1557 
   1558 bool SessionService::should_track_changes_for_browser_type(Browser::Type type,
   1559                                                            AppType app_type) {
   1560 #if defined(OS_CHROMEOS)
   1561   // Restore app popups for chromeos alone.
   1562   if (type == Browser::TYPE_POPUP && app_type == TYPE_APP)
   1563     return true;
   1564 #endif
   1565 
   1566   return type == Browser::TYPE_TABBED ||
   1567         (type == Browser::TYPE_POPUP && browser_defaults::kRestorePopups);
   1568 }
   1569 
   1570 SessionService::WindowType SessionService::WindowTypeForBrowserType(
   1571     Browser::Type type) {
   1572   switch (type) {
   1573     case Browser::TYPE_POPUP:
   1574       return TYPE_POPUP;
   1575     case Browser::TYPE_TABBED:
   1576       return TYPE_TABBED;
   1577     default:
   1578       DCHECK(false);
   1579       return TYPE_TABBED;
   1580   }
   1581 }
   1582 
   1583 Browser::Type SessionService::BrowserTypeForWindowType(WindowType type) {
   1584   switch (type) {
   1585     case TYPE_POPUP:
   1586       return Browser::TYPE_POPUP;
   1587     case TYPE_TABBED:
   1588     default:
   1589       return Browser::TYPE_TABBED;
   1590   }
   1591 }
   1592 
   1593 void SessionService::RecordSessionUpdateHistogramData(int type,
   1594     base::TimeTicks* last_updated_time) {
   1595   if (!last_updated_time->is_null()) {
   1596     base::TimeDelta delta = base::TimeTicks::Now() - *last_updated_time;
   1597     // We're interested in frequent updates periods longer than
   1598     // 10 minutes.
   1599     bool use_long_period = false;
   1600     if (delta >= save_delay_in_mins_) {
   1601       use_long_period = true;
   1602     }
   1603     switch (type) {
   1604       case chrome::NOTIFICATION_SESSION_SERVICE_SAVED :
   1605         RecordUpdatedSaveTime(delta, use_long_period);
   1606         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
   1607         break;
   1608       case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
   1609         RecordUpdatedTabClosed(delta, use_long_period);
   1610         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
   1611         break;
   1612       case content::NOTIFICATION_NAV_LIST_PRUNED:
   1613         RecordUpdatedNavListPruned(delta, use_long_period);
   1614         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
   1615         break;
   1616       case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
   1617         RecordUpdatedNavEntryCommit(delta, use_long_period);
   1618         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
   1619         break;
   1620       default:
   1621         NOTREACHED() << "Bad type sent to RecordSessionUpdateHistogramData";
   1622         break;
   1623     }
   1624   }
   1625   (*last_updated_time) = base::TimeTicks::Now();
   1626 }
   1627 
   1628 void SessionService::RecordUpdatedTabClosed(base::TimeDelta delta,
   1629                                             bool use_long_period) {
   1630   std::string name("SessionRestore.TabClosedPeriod");
   1631   UMA_HISTOGRAM_CUSTOM_TIMES(name,
   1632       delta,
   1633       // 2500ms is the default save delay.
   1634       save_delay_in_millis_,
   1635       save_delay_in_mins_,
   1636       50);
   1637   if (use_long_period) {
   1638     std::string long_name_("SessionRestore.TabClosedLongPeriod");
   1639     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
   1640         delta,
   1641         save_delay_in_mins_,
   1642         save_delay_in_hrs_,
   1643         50);
   1644   }
   1645 }
   1646 
   1647 void SessionService::RecordUpdatedNavListPruned(base::TimeDelta delta,
   1648                                                 bool use_long_period) {
   1649   std::string name("SessionRestore.NavigationListPrunedPeriod");
   1650   UMA_HISTOGRAM_CUSTOM_TIMES(name,
   1651       delta,
   1652       // 2500ms is the default save delay.
   1653       save_delay_in_millis_,
   1654       save_delay_in_mins_,
   1655       50);
   1656   if (use_long_period) {
   1657     std::string long_name_("SessionRestore.NavigationListPrunedLongPeriod");
   1658     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
   1659         delta,
   1660         save_delay_in_mins_,
   1661         save_delay_in_hrs_,
   1662         50);
   1663   }
   1664 }
   1665 
   1666 void SessionService::RecordUpdatedNavEntryCommit(base::TimeDelta delta,
   1667                                                  bool use_long_period) {
   1668   std::string name("SessionRestore.NavEntryCommittedPeriod");
   1669   UMA_HISTOGRAM_CUSTOM_TIMES(name,
   1670       delta,
   1671       // 2500ms is the default save delay.
   1672       save_delay_in_millis_,
   1673       save_delay_in_mins_,
   1674       50);
   1675   if (use_long_period) {
   1676     std::string long_name_("SessionRestore.NavEntryCommittedLongPeriod");
   1677     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
   1678         delta,
   1679         save_delay_in_mins_,
   1680         save_delay_in_hrs_,
   1681         50);
   1682   }
   1683 }
   1684 
   1685 void SessionService::RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,
   1686                                                          bool use_long_period) {
   1687   std::string name("SessionRestore.NavOrTabUpdatePeriod");
   1688   UMA_HISTOGRAM_CUSTOM_TIMES(name,
   1689       delta,
   1690       // 2500ms is the default save delay.
   1691       save_delay_in_millis_,
   1692       save_delay_in_mins_,
   1693       50);
   1694   if (use_long_period) {
   1695     std::string long_name_("SessionRestore.NavOrTabUpdateLongPeriod");
   1696     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
   1697         delta,
   1698         save_delay_in_mins_,
   1699         save_delay_in_hrs_,
   1700         50);
   1701   }
   1702 }
   1703 
   1704 void SessionService::RecordUpdatedSaveTime(base::TimeDelta delta,
   1705                                            bool use_long_period) {
   1706   std::string name("SessionRestore.SavePeriod");
   1707   UMA_HISTOGRAM_CUSTOM_TIMES(name,
   1708       delta,
   1709       // 2500ms is the default save delay.
   1710       save_delay_in_millis_,
   1711       save_delay_in_mins_,
   1712       50);
   1713   if (use_long_period) {
   1714     std::string long_name_("SessionRestore.SaveLongPeriod");
   1715     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
   1716         delta,
   1717         save_delay_in_mins_,
   1718         save_delay_in_hrs_,
   1719         50);
   1720   }
   1721 }
   1722 
   1723 void SessionService::TabInserted(WebContents* contents) {
   1724   SessionTabHelper* session_tab_helper =
   1725       SessionTabHelper::FromWebContents(contents);
   1726   if (!ShouldTrackChangesToWindow(session_tab_helper->window_id()))
   1727     return;
   1728   SetTabWindow(session_tab_helper->window_id(),
   1729                session_tab_helper->session_id());
   1730   extensions::TabHelper* extensions_tab_helper =
   1731       extensions::TabHelper::FromWebContents(contents);
   1732   if (extensions_tab_helper &&
   1733       extensions_tab_helper->extension_app()) {
   1734     SetTabExtensionAppID(
   1735         session_tab_helper->window_id(),
   1736         session_tab_helper->session_id(),
   1737         extensions_tab_helper->extension_app()->id());
   1738   }
   1739 
   1740   // Record the association between the SessionStorageNamespace and the
   1741   // tab.
   1742   //
   1743   // TODO(ajwong): This should be processing the whole map rather than
   1744   // just the default. This in particular will not work for tabs with only
   1745   // isolated apps which won't have a default partition.
   1746   content::SessionStorageNamespace* session_storage_namespace =
   1747       contents->GetController().GetDefaultSessionStorageNamespace();
   1748   ScheduleCommand(CreateSessionStorageAssociatedCommand(
   1749       session_tab_helper->session_id(),
   1750       session_storage_namespace->persistent_id()));
   1751   session_storage_namespace->SetShouldPersist(true);
   1752 }
   1753 
   1754 void SessionService::TabClosing(WebContents* contents) {
   1755   // Allow the associated sessionStorage to get deleted; it won't be needed
   1756   // in the session restore.
   1757   content::SessionStorageNamespace* session_storage_namespace =
   1758       contents->GetController().GetDefaultSessionStorageNamespace();
   1759   session_storage_namespace->SetShouldPersist(false);
   1760   SessionTabHelper* session_tab_helper =
   1761       SessionTabHelper::FromWebContents(contents);
   1762   TabClosed(session_tab_helper->window_id(),
   1763             session_tab_helper->session_id(),
   1764             contents->GetClosedByUserGesture());
   1765   RecordSessionUpdateHistogramData(content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
   1766                                    &last_updated_tab_closed_time_);
   1767 }
   1768 
   1769 void SessionService::MaybeDeleteSessionOnlyData() {
   1770   // Clear session data if the last window for a profile has been closed and
   1771   // closing the last window would normally close Chrome, unless background mode
   1772   // is active.
   1773   if (has_open_trackable_browsers_ ||
   1774       browser_defaults::kBrowserAliveWithNoWindows ||
   1775       g_browser_process->background_mode_manager()->IsBackgroundModeActive()) {
   1776     return;
   1777   }
   1778 
   1779   // Check for any open windows for the current profile that we aren't tracking.
   1780   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
   1781     if ((*it)->profile() == profile())
   1782       return;
   1783   }
   1784   DeleteSessionOnlyData(profile());
   1785 }
   1786