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.
5 #ifndef CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
6 #define CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
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"
34 class NavigationEntry
;
38 // SessionService ------------------------------------------------------------
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.
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.
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
58 class SessionService
: public BaseSessionService
,
60 public content::NotificationObserver
,
61 public chrome::BrowserListObserver
{
62 friend class SessionServiceTestHelper
;
64 // Used to distinguish an application window from a normal one.
70 // Creates a SessionService for the specified profile.
71 explicit SessionService(Profile
* profile
);
73 explicit SessionService(const base::FilePath
& save_path
);
75 virtual ~SessionService();
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();
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
);
90 // Resets the contents of the file from the current state of all open
91 // browsers whose profile matches our profile.
92 void ResetFromCurrentBrowsers();
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();
99 // Associates a tab with a window.
100 void SetTabWindow(const SessionID
& window_id
,
101 const SessionID
& tab_id
);
103 // Sets the bounds of a window.
104 void SetWindowBounds(const SessionID
& window_id
,
105 const gfx::Rect
& bounds
,
106 ui::WindowShowState show_state
);
108 // Sets the visual index of the tab in its parent window.
109 void SetTabIndexInWindow(const SessionID
& window_id
,
110 const SessionID
& tab_id
,
113 // Sets the pinned state of the tab.
114 void SetPinnedState(const SessionID
& window_id
,
115 const SessionID
& tab_id
,
118 // Notification that a tab has been closed. |closed_by_user_gesture| comes
119 // from |WebContents::closed_by_user_gesture|; see it for details.
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
);
127 // Notification a window has opened.
128 void WindowOpened(Browser
* browser
);
130 // Notification the window is about to close.
131 void WindowClosing(const SessionID
& window_id
);
133 // Notification a window has finished closing.
134 void WindowClosed(const SessionID
& window_id
);
136 // Called when a tab is inserted.
137 void TabInserted(content::WebContents
* contents
);
139 // Called when a tab is closing.
140 void TabClosing(content::WebContents
* contents
);
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
,
149 // Sets the application name of the specified window.
150 void SetWindowAppName(const SessionID
& window_id
,
151 const std::string
& app_name
);
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
,
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
,
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
);
171 // Notification that a tab has restored its entries or a closed tab is being
173 void TabRestored(content::WebContents
* tab
, bool pinned
);
175 // Sets the index of the selected entry in the navigation controller for the
177 void SetSelectedNavigationIndex(const SessionID
& window_id
,
178 const SessionID
& tab_id
,
181 // Sets the index of the selected tab in the specified window.
182 void SetSelectedTabInWindow(const SessionID
& window_id
, int index
);
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
);
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
)>
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
);
201 // Overridden from BaseSessionService because we want some UMA reporting on
202 // session update activities.
203 virtual void Save() OVERRIDE
;
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
);
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
;
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.
227 // Returns true if we have scheduled any commands, or any scheduled commands
229 bool processed_any_commands();
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
,
236 virtual void Observe(int type
,
237 const content::NotificationSource
& source
,
238 const content::NotificationDetails
& details
) OVERRIDE
;
240 // chrome::BrowserListObserver
241 virtual void OnBrowserAdded(Browser
* browser
) OVERRIDE
{}
242 virtual void OnBrowserRemoved(Browser
* browser
) OVERRIDE
{}
243 virtual void OnBrowserSetLastActive(Browser
* browser
) OVERRIDE
;
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
);
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
,
255 SessionCommand
* CreateSetTabWindowCommand(const SessionID
& window_id
,
256 const SessionID
& tab_id
);
258 SessionCommand
* CreateSetWindowBoundsCommand(const SessionID
& window_id
,
259 const gfx::Rect
& bounds
,
260 ui::WindowShowState show_state
);
262 SessionCommand
* CreateSetTabIndexInWindowCommand(const SessionID
& tab_id
,
265 SessionCommand
* CreateTabClosedCommand(SessionID::id_type tab_id
);
267 SessionCommand
* CreateWindowClosedCommand(SessionID::id_type tab_id
);
269 SessionCommand
* CreateSetSelectedNavigationIndexCommand(
270 const SessionID
& tab_id
,
273 SessionCommand
* CreateSetWindowTypeCommand(const SessionID
& window_id
,
276 SessionCommand
* CreatePinnedStateCommand(const SessionID
& tab_id
,
279 SessionCommand
* CreateSessionStorageAssociatedCommand(
280 const SessionID
& tab_id
,
281 const std::string
& session_storage_persistent_id
);
283 SessionCommand
* CreateSetActiveWindowCommand(const SessionID
& window_id
);
285 // Converts |commands| to SessionWindows and notifies the callback.
286 void OnGotSessionCommands(const SessionCallback
& callback
,
287 ScopedVector
<SessionCommand
> commands
);
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
);
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
);
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
);
307 // Returns the tab with the specified id in tabs. If a tab does not exist,
309 SessionTab
* GetTab(SessionID::id_type tab_id
,
310 IdToSessionTab
* tabs
);
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.
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
,
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
);
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
);
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|.
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
);
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
,
360 std::vector
<SessionCommand
*>* commands
,
361 IdToRange
* tab_to_available_range
);
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(
368 std::vector
<SessionCommand
*>* commands
,
369 IdToRange
* tab_to_available_range
,
370 std::set
<SessionID::id_type
>* windows_to_track
);
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
);
381 // Schedules a reset. A reset means the contents of the file are recreated
382 // from the state of the browser.
383 void ScheduleReset();
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
);
390 // Schedules the specified command. This method takes ownership of the
392 virtual void ScheduleCommand(SessionCommand
* command
) OVERRIDE
;
394 // Converts all pending tab/window closes to commands and schedules them.
395 void CommitPendingCloses();
397 // Returns true if there is only one window open with a single tab that shares
399 bool IsOnlyOneTabLeft() const;
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;
407 // Returns true if changes to tabs in the specified window should be tracked.
408 bool ShouldTrackChangesToWindow(const SessionID
& window_id
) const;
410 // Returns true if we track changes to the specified browser.
411 bool ShouldTrackBrowser(Browser
* browser
) const;
413 // Returns true if we track changes to the specified browser type.
414 static bool should_track_changes_for_browser_type(
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
);
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
);
434 // Deletes session data if no windows are open for the current profile.
435 void MaybeDeleteSessionOnlyData();
437 // Convert back/forward between the Browser and SessionService DB window
439 static WindowType
WindowTypeForBrowserType(Browser::Type type
);
440 static Browser::Type
BrowserTypeForWindowType(WindowType type
);
442 content::NotificationRegistrar registrar_
;
444 // Maps from session tab id to the range of navigation entries that has
445 // been written to disk.
447 // This is only used if not all the navigation entries have been
449 IdToRange tab_to_available_range_
;
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_
;
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_
;
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_
;
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_
;
473 // Are there any open trackable browsers?
474 bool has_open_trackable_browsers_
;
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_
;
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_
;
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_
;
493 // For browser_tests, since we want to simulate the browser shutting down
495 bool force_browser_not_alive_with_no_windows_
;
497 base::WeakPtrFactory
<SessionService
> weak_factory_
;
499 DISALLOW_COPY_AND_ASSIGN(SessionService
);
502 #endif // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_