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