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