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