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