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