session_service.h revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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#ifndef CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
6#define CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
7#pragma once
8
9#include <map>
10#include <string>
11
12#include "base/basictypes.h"
13#include "base/callback.h"
14#include "base/time.h"
15#include "chrome/browser/browser.h"
16#include "chrome/browser/browser_list.h"
17#include "chrome/browser/defaults.h"
18#include "chrome/browser/sessions/base_session_service.h"
19#include "chrome/browser/sessions/session_id.h"
20#include "chrome/common/notification_observer.h"
21#include "chrome/common/notification_registrar.h"
22
23class NavigationController;
24class NavigationEntry;
25class Profile;
26class SessionCommand;
27struct SessionTab;
28struct SessionWindow;
29
30// SessionService ------------------------------------------------------------
31
32// SessionService is responsible for maintaining the state of open windows
33// and tabs so that they can be restored at a later date. The state of the
34// currently open browsers is referred to as the current session.
35//
36// SessionService supports restoring from the previous or last session. The
37// previous session typically corresponds to the last run of the browser, but
38// not always. For example, if the user has a tabbed browser and app window
39// running, closes the tabbed browser, then creates a new tabbed browser the
40// current session is made the last session and the current session reset. This
41// is done to provide the illusion that app windows run in separate processes.
42//
43// SessionService itself maintains a set of SessionCommands that allow
44// SessionService to rebuild the open state of the browser (as
45// SessionWindow, SessionTab and TabNavigation). The commands are periodically
46// flushed to SessionBackend and written to a file. Every so often
47// SessionService rebuilds the contents of the file from the open state
48// of the browser.
49class SessionService : public BaseSessionService,
50                       public NotificationObserver {
51  friend class SessionServiceTestHelper;
52 public:
53  // Creates a SessionService for the specified profile.
54  explicit SessionService(Profile* profile);
55  // For testing.
56  explicit SessionService(const FilePath& save_path);
57
58  // Invoke at a point when you think session restore might occur. For example,
59  // during startup and window creation this is invoked to see if a session
60  // needs to be restored. If a session needs to be restored it is done so
61  // asynchronously and true is returned. If false is returned the session was
62  // not restored and the caller needs to create a new window.
63  bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open);
64
65  // Resets the contents of the file from the current state of all open
66  // browsers whose profile matches our profile.
67  void ResetFromCurrentBrowsers();
68
69  // Moves the current session to the last session. This is useful when a
70  // checkpoint occurs, such as when the user launches the app and no tabbed
71  // browsers are running.
72  void MoveCurrentSessionToLastSession();
73
74  // Associates a tab with a window.
75  void SetTabWindow(const SessionID& window_id,
76                    const SessionID& tab_id);
77
78  // Sets the bounds of a window.
79  void SetWindowBounds(const SessionID& window_id,
80                       const gfx::Rect& bounds,
81                       bool is_maximized);
82
83  // Sets the visual index of the tab in its parent window.
84  void SetTabIndexInWindow(const SessionID& window_id,
85                           const SessionID& tab_id,
86                           int new_index);
87
88  // Sets the pinned state of the tab.
89  void SetPinnedState(const SessionID& window_id,
90                      const SessionID& tab_id,
91                      bool is_pinned);
92
93  // Notification that a tab has been closed. |closed_by_user_gesture| comes
94  // from |TabContents::closed_by_user_gesture|; see it for details.
95  //
96  // Note: this is invoked from the NavigationController's destructor, which is
97  // after the actual tab has been removed.
98  void TabClosed(const SessionID& window_id,
99                 const SessionID& tab_id,
100                 bool closed_by_user_gesture);
101
102  // Notification the window is about to close.
103  void WindowClosing(const SessionID& window_id);
104
105  // Notification a window has finished closing.
106  void WindowClosed(const SessionID& window_id);
107
108  // Sets the type of window. In order for the contents of a window to be
109  // tracked SetWindowType must be invoked with a type we track
110  // (should_track_changes_for_browser_type returns true).
111  void SetWindowType(const SessionID& window_id, Browser::Type type);
112
113  // Invoked when the NavigationController has removed entries from the back of
114  // the list. |count| gives the number of entries in the navigation controller.
115  void TabNavigationPathPrunedFromBack(const SessionID& window_id,
116                                       const SessionID& tab_id,
117                                       int count);
118
119  // Invoked when the NavigationController has removed entries from the front of
120  // the list. |count| gives the number of entries that were removed.
121  void TabNavigationPathPrunedFromFront(const SessionID& window_id,
122                                        const SessionID& tab_id,
123                                        int count);
124
125  // Updates the navigation entry for the specified tab.
126  void UpdateTabNavigation(const SessionID& window_id,
127                           const SessionID& tab_id,
128                           int index,
129                           const NavigationEntry& entry);
130
131  // Notification that a tab has restored its entries or a closed tab is being
132  // reused.
133  void TabRestored(NavigationController* controller,
134                   bool pinned);
135
136  // Sets the index of the selected entry in the navigation controller for the
137  // specified tab.
138  void SetSelectedNavigationIndex(const SessionID& window_id,
139                                  const SessionID& tab_id,
140                                  int index);
141
142  // Sets the index of the selected tab in the specified window.
143  void SetSelectedTabInWindow(const SessionID& window_id, int index);
144
145  // Callback from GetSavedSession of GetLastSession.
146  //
147  // The contents of the supplied vector are deleted after the callback is
148  // notified. To take ownership of the vector clear it before returning.
149  //
150  // The time gives the time the session was closed.
151  typedef Callback2<Handle, std::vector<SessionWindow*>*>::Type
152      SessionCallback;
153
154  // Fetches the contents of the last session, notifying the callback when
155  // done. If the callback is supplied an empty vector of SessionWindows
156  // it means the session could not be restored.
157  //
158  // The created request does NOT directly invoke the callback, rather the
159  // callback invokes OnGotSessionCommands from which we map the
160  // SessionCommands to browser state, then notify the callback.
161  Handle GetLastSession(CancelableRequestConsumerBase* consumer,
162                        SessionCallback* callback);
163
164  // Fetches the contents of the current session, notifying the callback when
165  // done. If the callback is supplied an empty vector of SessionWindows
166  // it means the session could not be restored.
167  //
168  // The created request does NOT directly invoke the callback, rather the
169  // callback invokes OnGotSessionCommands from which we map the
170  // SessionCommands to browser state, then notify the callback.
171  Handle GetCurrentSession(CancelableRequestConsumerBase* consumer,
172                           SessionCallback* callback);
173
174  // Overridden from BaseSessionService because we want some UMA reporting on
175  // session update activities.
176  virtual void Save();
177
178 private:
179  typedef std::map<SessionID::id_type, std::pair<int, int> > IdToRange;
180  typedef std::map<SessionID::id_type, SessionTab*> IdToSessionTab;
181  typedef std::map<SessionID::id_type, SessionWindow*> IdToSessionWindow;
182
183
184  virtual ~SessionService();
185
186  // These types mirror Browser::Type, but are re-defined here because these
187  // specific enumeration _values_ are written into the session database and
188  // are needed to maintain forward compatibility.
189  enum WindowType {
190    TYPE_NORMAL = 0,
191    TYPE_POPUP = 1,
192    TYPE_APP = 2,
193    TYPE_APP_POPUP = TYPE_APP + TYPE_POPUP,
194    TYPE_DEVTOOLS = TYPE_APP + 4,
195    TYPE_APP_PANEL = TYPE_APP + 8
196  };
197
198  void Init();
199
200  // Implementation of RestoreIfNecessary. If |browser| is non-null and we need
201  // to restore, the tabs are added to it, otherwise a new browser is created.
202  bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
203                          Browser* browser);
204
205  virtual void Observe(NotificationType type,
206                       const NotificationSource& source,
207                       const NotificationDetails& details);
208
209  // Sets the application extension id of the specified tab.
210  void SetTabExtensionAppID(const SessionID& window_id,
211                            const SessionID& tab_id,
212                            const std::string& extension_app_id);
213
214  // Methods to create the various commands. It is up to the caller to delete
215  // the returned the SessionCommand* object.
216  SessionCommand* CreateSetSelectedTabInWindow(const SessionID& window_id,
217                                               int index);
218
219  SessionCommand* CreateSetTabWindowCommand(const SessionID& window_id,
220                                            const SessionID& tab_id);
221
222  SessionCommand* CreateSetWindowBoundsCommand(const SessionID& window_id,
223                                               const gfx::Rect& bounds,
224                                               bool is_maximized);
225
226  SessionCommand* CreateSetTabIndexInWindowCommand(const SessionID& tab_id,
227                                                   int new_index);
228
229  SessionCommand* CreateTabClosedCommand(SessionID::id_type tab_id);
230
231  SessionCommand* CreateWindowClosedCommand(SessionID::id_type tab_id);
232
233  SessionCommand* CreateSetSelectedNavigationIndexCommand(
234      const SessionID& tab_id,
235      int index);
236
237  SessionCommand* CreateSetWindowTypeCommand(const SessionID& window_id,
238                                             WindowType type);
239
240  SessionCommand* CreatePinnedStateCommand(const SessionID& tab_id,
241                                           bool is_pinned);
242
243  // Callback from the backend for getting the commands from the previous
244  // or save file. Converts the commands in SessionWindows and notifies
245  // the real callback.
246  void OnGotSessionCommands(
247      Handle handle,
248      scoped_refptr<InternalGetCommandsRequest> request);
249
250  // Converts the commands into SessionWindows. On return any valid
251  // windows are added to valid_windows. It is up to the caller to delete
252  // the windows added to valid_windows.
253  //
254  // If ignore_recent_closes is true, any window/tab closes within in a certain
255  // time frame are ignored.
256  void RestoreSessionFromCommands(const std::vector<SessionCommand*>& commands,
257                                  std::vector<SessionWindow*>* valid_windows);
258
259  // Iterates through the vector updating the selected_tab_index of each
260  // SessionWindow based on the actual tabs that were restored.
261  void UpdateSelectedTabIndex(std::vector<SessionWindow*>* windows);
262
263  // Returns the window in windows with the specified id. If a window does
264  // not exist, one is created.
265  SessionWindow* GetWindow(SessionID::id_type window_id,
266                           IdToSessionWindow* windows);
267
268  // Returns the tab with the specified id in tabs. If a tab does not exist,
269  // it is created.
270  SessionTab* GetTab(SessionID::id_type tab_id,
271                     IdToSessionTab* tabs);
272
273  // Returns an iterator into navigations pointing to the navigation whose
274  // index matches |index|. If no navigation index matches |index|, the first
275  // navigation with an index > |index| is returned.
276  //
277  // This assumes the navigations are ordered by index in ascending order.
278  std::vector<TabNavigation>::iterator FindClosestNavigationWithIndex(
279      std::vector<TabNavigation>* navigations,
280      int index);
281
282  // Does the following:
283  // . Deletes and removes any windows with no tabs or windows with types other
284  //   than tabbed_browser or browser. NOTE: constrained windows that have
285  //   been dragged out are of type browser. As such, this preserves any dragged
286  //   out constrained windows (aka popups that have been dragged out).
287  // . Sorts the tabs in windows with valid tabs based on the tabs
288  //   visual order, and adds the valid windows to windows.
289  void SortTabsBasedOnVisualOrderAndPrune(
290      std::map<int, SessionWindow*>* windows,
291      std::vector<SessionWindow*>* valid_windows);
292
293  // Adds tabs to their parent window based on the tab's window_id. This
294  // ignores tabs with no navigations.
295  void AddTabsToWindows(std::map<int, SessionTab*>* tabs,
296                        std::map<int, SessionWindow*>* windows);
297
298  // Creates tabs and windows from the specified commands. The created tabs
299  // and windows are added to |tabs| and |windows| respectively. It is up to
300  // the caller to delete the tabs and windows added to |tabs| and |windows|.
301  //
302  // This does NOT add any created SessionTabs to SessionWindow.tabs, that is
303  // done by AddTabsToWindows.
304  bool CreateTabsAndWindows(const std::vector<SessionCommand*>& data,
305                            std::map<int, SessionTab*>* tabs,
306                            std::map<int, SessionWindow*>* windows);
307
308  // Adds commands to commands that will recreate the state of the specified
309  // NavigationController. This adds at most kMaxNavigationCountToPersist
310  // navigations (in each direction from the current navigation index).
311  // A pair is added to tab_to_available_range indicating the range of
312  // indices that were written.
313  void BuildCommandsForTab(
314      const SessionID& window_id,
315      NavigationController* controller,
316      int index_in_window,
317      bool is_pinned,
318      std::vector<SessionCommand*>* commands,
319      IdToRange* tab_to_available_range);
320
321  // Adds commands to create the specified browser, and invokes
322  // BuildCommandsForTab for each of the tabs in the browser. This ignores
323  // any tabs not in the profile we were created with.
324  void BuildCommandsForBrowser(
325      Browser* browser,
326      std::vector<SessionCommand*>* commands,
327      IdToRange* tab_to_available_range,
328      std::set<SessionID::id_type>* windows_to_track);
329
330  // Iterates over all the known browsers invoking BuildCommandsForBrowser.
331  // This only adds browsers that should be tracked
332  // (should_track_changes_for_browser_type returns true). All browsers that
333  // are tracked are added to windows_to_track (as long as it is non-null).
334  void BuildCommandsFromBrowsers(
335      std::vector<SessionCommand*>* commands,
336      IdToRange* tab_to_available_range,
337      std::set<SessionID::id_type>* windows_to_track);
338
339  // Schedules a reset. A reset means the contents of the file are recreated
340  // from the state of the browser.
341  void ScheduleReset();
342
343  // Searches for a pending command that can be replaced with command.
344  // If one is found, pending command is removed, command is added to
345  // the pending commands and true is returned.
346  bool ReplacePendingCommand(SessionCommand* command);
347
348  // Schedules the specified command. This method takes ownership of the
349  // command.
350  void ScheduleCommand(SessionCommand* command);
351
352  // Converts all pending tab/window closes to commands and schedules them.
353  void CommitPendingCloses();
354
355  // Returns true if there is only one window open with a single tab that shares
356  // our profile.
357  bool IsOnlyOneTabLeft();
358
359  // Returns true if there are open trackable browser windows whose ids do
360  // match |window_id| with our profile. A trackable window is a window from
361  // which |should_track_changes_for_browser_type| returns true. See
362  // |should_track_changes_for_browser_type| for details.
363  bool HasOpenTrackableBrowsers(const SessionID& window_id);
364
365  // Returns true if changes to tabs in the specified window should be tracked.
366  bool ShouldTrackChangesToWindow(const SessionID& window_id);
367
368  // Returns true if we track changes to the specified browser type.
369  static bool should_track_changes_for_browser_type(Browser::Type type) {
370    return type == Browser::TYPE_NORMAL ||
371        (type == Browser::TYPE_POPUP && browser_defaults::kRestorePopups);
372  }
373
374  // Returns true if we should record a window close as pending.
375  // |has_open_trackable_browsers_| must be up-to-date before calling this.
376  bool should_record_close_as_pending() const {
377    // When this is called, the browser window being closed is still open, hence
378    // still in the browser list. If there is a browser window other than the
379    // one being closed but no trackable windows, then the others must be App
380    // windows or similar. In this case, we record the close as pending.
381    return !has_open_trackable_browsers_ &&
382        (!browser_defaults::kBrowserAliveWithNoWindows ||
383         BrowserList::size() > 1);
384  }
385
386  // Call when certain session relevant notifications
387  // (tab_closed, nav_list_pruned) occur.  In addition, this is
388  // currently called when Save() is called to compare how often the
389  // session data is currently saved verses when we may want to save it.
390  // It records the data in UMA stats.
391  void RecordSessionUpdateHistogramData(NotificationType type,
392    base::TimeTicks* last_updated_time);
393
394  // Helper methods to record the histogram data
395  void RecordUpdatedTabClosed(base::TimeDelta delta, bool use_long_period);
396  void RecordUpdatedNavListPruned(base::TimeDelta delta, bool use_long_period);
397  void RecordUpdatedNavEntryCommit(base::TimeDelta delta, bool use_long_period);
398  void RecordUpdatedSaveTime(base::TimeDelta delta, bool use_long_period);
399  void RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,
400                                           bool use_long_period);
401
402  // Convert back/forward between the Browser and SessionService DB window
403  // types.
404  static WindowType WindowTypeForBrowserType(Browser::Type type);
405  static Browser::Type BrowserTypeForWindowType(WindowType type);
406
407  NotificationRegistrar registrar_;
408
409  // Maps from session tab id to the range of navigation entries that has
410  // been written to disk.
411  //
412  // This is only used if not all the navigation entries have been
413  // written.
414  IdToRange tab_to_available_range_;
415
416  // When the user closes the last window, where the last window is the
417  // last tabbed browser and no more tabbed browsers are open with the same
418  // profile, the window ID is added here. These IDs are only committed (which
419  // marks them as closed) if the user creates a new tabbed browser.
420  typedef std::set<SessionID::id_type> PendingWindowCloseIDs;
421  PendingWindowCloseIDs pending_window_close_ids_;
422
423  // Set of tabs that have been closed by way of the last window or last tab
424  // closing, but not yet committed.
425  typedef std::set<SessionID::id_type> PendingTabCloseIDs;
426  PendingTabCloseIDs pending_tab_close_ids_;
427
428  // When a window other than the last window (see description of
429  // pending_window_close_ids) is closed, the id is added to this set.
430  typedef std::set<SessionID::id_type> WindowClosingIDs;
431  WindowClosingIDs window_closing_ids_;
432
433  // Set of windows we're tracking changes to. This is only browsers that
434  // return true from should_track_changes_for_browser_type.
435  typedef std::set<SessionID::id_type> WindowsTracking;
436  WindowsTracking windows_tracking_;
437
438  // Are there any open trackable browsers?
439  bool has_open_trackable_browsers_;
440
441  // If true and a new tabbed browser is created and there are no opened tabbed
442  // browser (has_open_trackable_browsers_ is false), then the current session
443  // is made the previous session. See description above class for details on
444  // current/previous session.
445  bool move_on_new_browser_;
446
447  // Used for reporting frequency of session alteriing operations.
448  base::TimeTicks last_updated_tab_closed_time_;
449  base::TimeTicks last_updated_nav_list_pruned_time_;
450  base::TimeTicks last_updated_nav_entry_commit_time_;
451  base::TimeTicks last_updated_save_time_;
452
453  // Constants used in calculating histogram data.
454  const base::TimeDelta save_delay_in_millis_;
455  const base::TimeDelta save_delay_in_mins_;
456  const base::TimeDelta save_delay_in_hrs_;
457
458  DISALLOW_COPY_AND_ASSIGN(SessionService);
459};
460
461#endif  // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
462