• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
ShowStateToPersistedShowState(ui::WindowShowState 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.
PersistedShowStateToShowState(int state)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).
MigrateClosedPayload(const SessionCommand & command,ClosedPayload * payload)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  
SessionService(Profile * profile)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  
SessionService(const base::FilePath & save_path)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  
~SessionService()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  
RestoreIfNecessary(const std::vector<GURL> & urls_to_open)261  bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open) {
262    return RestoreIfNecessary(urls_to_open, NULL);
263  }
264  
ResetFromCurrentBrowsers()265  void SessionService::ResetFromCurrentBrowsers() {
266    ScheduleReset();
267  }
268  
MoveCurrentSessionToLastSession()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  
SetTabWindow(const SessionID & window_id,const SessionID & tab_id)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  
SetWindowBounds(const SessionID & window_id,const gfx::Rect & bounds,ui::WindowShowState show_state)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  
SetTabIndexInWindow(const SessionID & window_id,const SessionID & tab_id,int new_index)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  
SetPinnedState(const SessionID & window_id,const SessionID & tab_id,bool is_pinned)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  
TabClosed(const SessionID & window_id,const SessionID & tab_id,bool closed_by_user_gesture)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  
WindowOpened(Browser * browser)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  
WindowClosing(const SessionID & window_id)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  
WindowClosed(const SessionID & window_id)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  
SetWindowType(const SessionID & window_id,Browser::Type type,AppType app_type)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  
SetWindowAppName(const SessionID & window_id,const std::string & app_name)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  
TabNavigationPathPrunedFromBack(const SessionID & window_id,const SessionID & tab_id,int count)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  
TabNavigationPathPrunedFromFront(const SessionID & window_id,const SessionID & tab_id,int count)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  
UpdateTabNavigation(const SessionID & window_id,const SessionID & tab_id,const SerializedNavigationEntry & navigation)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  
TabRestored(WebContents * tab,bool pinned)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  
SetSelectedNavigationIndex(const SessionID & window_id,const SessionID & tab_id,int index)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  
SetSelectedTabInWindow(const SessionID & window_id,int index)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  
SetTabUserAgentOverride(const SessionID & window_id,const SessionID & tab_id,const std::string & user_agent_override)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  
GetLastSession(const SessionCallback & callback,base::CancelableTaskTracker * tracker)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  
Save()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  
Init()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  
processed_any_commands()608  bool SessionService::processed_any_commands() {
609    return backend()->inited() || !pending_commands().empty();
610  }
611  
ShouldNewWindowStartSession()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  
RestoreIfNecessary(const std::vector<GURL> & urls_to_open,Browser * browser)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  
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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  
OnBrowserSetLastActive(Browser * browser)761  void SessionService::OnBrowserSetLastActive(Browser* browser) {
762    if (ShouldTrackBrowser(browser))
763      ScheduleCommand(CreateSetActiveWindowCommand(browser->session_id()));
764  }
765  
SetTabExtensionAppID(const SessionID & window_id,const SessionID & tab_id,const std::string & extension_app_id)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  
CreateSetSelectedTabInWindow(const SessionID & window_id,int index)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  
CreateSetTabWindowCommand(const SessionID & window_id,const SessionID & tab_id)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  
CreateSetWindowBoundsCommand(const SessionID & window_id,const gfx::Rect & bounds,ui::WindowShowState show_state)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  
CreateSetTabIndexInWindowCommand(const SessionID & tab_id,int new_index)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  
CreateTabClosedCommand(const SessionID::id_type tab_id)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  
CreateWindowClosedCommand(const SessionID::id_type window_id)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  
CreateSetSelectedNavigationIndexCommand(const SessionID & tab_id,int index)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  
CreateSetWindowTypeCommand(const SessionID & window_id,WindowType type)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  
CreatePinnedStateCommand(const SessionID & tab_id,bool is_pinned)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  
CreateSessionStorageAssociatedCommand(const SessionID & tab_id,const std::string & session_storage_persistent_id)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  
CreateSetActiveWindowCommand(const SessionID & window_id)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  
OnGotSessionCommands(const SessionCallback & callback,ScopedVector<SessionCommand> commands)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  
RestoreSessionFromCommands(const std::vector<SessionCommand * > & commands,std::vector<SessionWindow * > * valid_windows,SessionID::id_type * active_window_id)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  
UpdateSelectedTabIndex(std::vector<SessionWindow * > * windows)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  
GetWindow(SessionID::id_type window_id,IdToSessionWindow * windows)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  
GetTab(SessionID::id_type tab_id,IdToSessionTab * tabs)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
FindClosestNavigationWithIndex(std::vector<SerializedNavigationEntry> * navigations,int index)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.
WindowOrderSortFunction(const SessionWindow * w1,const SessionWindow * w2)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.
TabVisualIndexSortFunction(const SessionTab * t1,const SessionTab * t2)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  
SortTabsBasedOnVisualOrderAndPrune(std::map<int,SessionWindow * > * windows,std::vector<SessionWindow * > * valid_windows)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  
AddTabsToWindows(std::map<int,SessionTab * > * tabs,std::map<int,SessionWindow * > * windows)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  
CreateTabsAndWindows(const std::vector<SessionCommand * > & data,std::map<int,SessionTab * > * tabs,std::map<int,SessionWindow * > * windows,SessionID::id_type * active_window_id)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  
BuildCommandsForTab(const SessionID & window_id,WebContents * tab,int index_in_window,bool is_pinned,std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range)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  
BuildCommandsForBrowser(Browser * browser,std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range,std::set<SessionID::id_type> * windows_to_track)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  
BuildCommandsFromBrowsers(std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range,std::set<SessionID::id_type> * windows_to_track)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  
ScheduleReset()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  
ReplacePendingCommand(SessionCommand * command)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  
ScheduleCommand(SessionCommand * command)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  
CommitPendingCloses()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  
IsOnlyOneTabLeft() const1566  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  
HasOpenTrackableBrowsers(const SessionID & window_id) const1589  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  
ShouldTrackChangesToWindow(const SessionID & window_id) const1608  bool SessionService::ShouldTrackChangesToWindow(
1609      const SessionID& window_id) const {
1610    return windows_tracking_.find(window_id.id()) != windows_tracking_.end();
1611  }
1612  
ShouldTrackBrowser(Browser * browser) const1613  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  
should_track_changes_for_browser_type(Browser::Type type,AppType app_type)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  
WindowTypeForBrowserType(Browser::Type type)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  
BrowserTypeForWindowType(WindowType type)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  
RecordSessionUpdateHistogramData(int type,base::TimeTicks * last_updated_time)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  
RecordUpdatedTabClosed(base::TimeDelta delta,bool use_long_period)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  
RecordUpdatedNavListPruned(base::TimeDelta delta,bool use_long_period)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  
RecordUpdatedNavEntryCommit(base::TimeDelta delta,bool use_long_period)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  
RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,bool use_long_period)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  
RecordUpdatedSaveTime(base::TimeDelta delta,bool use_long_period)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  
TabInserted(WebContents * contents)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  
TabClosing(WebContents * contents)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  
MaybeDeleteSessionOnlyData()1837  void SessionService::MaybeDeleteSessionOnlyData() {
1838    // Don't try anything if we're testing.  The browser_process is not fully
1839    // created and DeleteSession will crash if we actually attempt it.
1840    if (!profile() || profile()->AsTestingProfile())
1841      return;
1842  
1843    // Clear session data if the last window for a profile has been closed and
1844    // closing the last window would normally close Chrome, unless background mode
1845    // is active.  Tests don't have a background_mode_manager.
1846    if (has_open_trackable_browsers_ ||
1847        browser_defaults::kBrowserAliveWithNoWindows ||
1848        g_browser_process->background_mode_manager()->IsBackgroundModeActive()) {
1849      return;
1850    }
1851  
1852    // Check for any open windows for the current profile that we aren't tracking.
1853    for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1854      if ((*it)->profile() == profile())
1855        return;
1856    }
1857    DeleteSessionOnlyData(profile());
1858  }
1859