// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
#define CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
#pragma once
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/time.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/sessions/base_session_service.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
class NavigationController;
class NavigationEntry;
class Profile;
class SessionCommand;
struct SessionTab;
struct SessionWindow;
// SessionService ------------------------------------------------------------
// SessionService is responsible for maintaining the state of open windows
// and tabs so that they can be restored at a later date. The state of the
// currently open browsers is referred to as the current session.
//
// SessionService supports restoring from the last session. The last session
// typically corresponds to the last run of the browser, but not always. For
// example, if the user has a tabbed browser and app window running, closes the
// tabbed browser, then creates a new tabbed browser the current session is made
// the last session and the current session reset. This is done to provide the
// illusion that app windows run in separate processes. Similar behavior occurs
// with incognito windows.
//
// SessionService itself maintains a set of SessionCommands that allow
// SessionService to rebuild the open state of the browser (as
// SessionWindow, SessionTab and TabNavigation). The commands are periodically
// flushed to SessionBackend and written to a file. Every so often
// SessionService rebuilds the contents of the file from the open state
// of the browser.
class SessionService : public BaseSessionService,
public NotificationObserver {
friend class SessionServiceTestHelper;
public:
// Creates a SessionService for the specified profile.
explicit SessionService(Profile* profile);
// For testing.
explicit SessionService(const FilePath& save_path);
// Invoke at a point when you think session restore might occur. For example,
// during startup and window creation this is invoked to see if a session
// needs to be restored. If a session needs to be restored it is done so
// asynchronously and true is returned. If false is returned the session was
// not restored and the caller needs to create a new window.
bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open);
// Resets the contents of the file from the current state of all open
// browsers whose profile matches our profile.
void ResetFromCurrentBrowsers();
// Moves the current session to the last session. This is useful when a
// checkpoint occurs, such as when the user launches the app and no tabbed
// browsers are running.
void MoveCurrentSessionToLastSession();
// Associates a tab with a window.
void SetTabWindow(const SessionID& window_id,
const SessionID& tab_id);
// Sets the bounds of a window.
void SetWindowBounds(const SessionID& window_id,
const gfx::Rect& bounds,
bool is_maximized);
// Sets the visual index of the tab in its parent window.
void SetTabIndexInWindow(const SessionID& window_id,
const SessionID& tab_id,
int new_index);
// Sets the pinned state of the tab.
void SetPinnedState(const SessionID& window_id,
const SessionID& tab_id,
bool is_pinned);
// Notification that a tab has been closed. |closed_by_user_gesture| comes
// from |TabContents::closed_by_user_gesture|; see it for details.
//
// Note: this is invoked from the NavigationController's destructor, which is
// after the actual tab has been removed.
void TabClosed(const SessionID& window_id,
const SessionID& tab_id,
bool closed_by_user_gesture);
// Notification the window is about to close.
void WindowClosing(const SessionID& window_id);
// Notification a window has finished closing.
void WindowClosed(const SessionID& window_id);
// Sets the type of window. In order for the contents of a window to be
// tracked SetWindowType must be invoked with a type we track
// (should_track_changes_for_browser_type returns true).
void SetWindowType(const SessionID& window_id, Browser::Type type);
// Invoked when the NavigationController has removed entries from the back of
// the list. |count| gives the number of entries in the navigation controller.
void TabNavigationPathPrunedFromBack(const SessionID& window_id,
const SessionID& tab_id,
int count);
// Invoked when the NavigationController has removed entries from the front of
// the list. |count| gives the number of entries that were removed.
void TabNavigationPathPrunedFromFront(const SessionID& window_id,
const SessionID& tab_id,
int count);
// Updates the navigation entry for the specified tab.
void UpdateTabNavigation(const SessionID& window_id,
const SessionID& tab_id,
int index,
const NavigationEntry& entry);
// Notification that a tab has restored its entries or a closed tab is being
// reused.
void TabRestored(NavigationController* controller,
bool pinned);
// Sets the index of the selected entry in the navigation controller for the
// specified tab.
void SetSelectedNavigationIndex(const SessionID& window_id,
const SessionID& tab_id,
int index);
// Sets the index of the selected tab in the specified window.
void SetSelectedTabInWindow(const SessionID& window_id, int index);
// Callback from GetSavedSession of GetLastSession.
//
// The contents of the supplied vector are deleted after the callback is
// notified. To take ownership of the vector clear it before returning.
//
// The time gives the time the session was closed.
typedef Callback2<Handle, std::vector<SessionWindow*>*>::Type
SessionCallback;
// Fetches the contents of the last session, notifying the callback when
// done. If the callback is supplied an empty vector of SessionWindows
// it means the session could not be restored.
//
// The created request does NOT directly invoke the callback, rather the
// callback invokes OnGotSessionCommands from which we map the
// SessionCommands to browser state, then notify the callback.
Handle GetLastSession(CancelableRequestConsumerBase* consumer,
SessionCallback* callback);
// Fetches the contents of the current session, notifying the callback when
// done. If the callback is supplied an empty vector of SessionWindows
// it means the session could not be restored.
//
// The created request does NOT directly invoke the callback, rather the
// callback invokes OnGotSessionCommands from which we map the
// SessionCommands to browser state, then notify the callback.
Handle GetCurrentSession(CancelableRequestConsumerBase* consumer,
SessionCallback* callback);
// Overridden from BaseSessionService because we want some UMA reporting on
// session update activities.
virtual void Save();
private:
typedef std::map<SessionID::id_type, std::pair<int, int> > IdToRange;
typedef std::map<SessionID::id_type, SessionTab*> IdToSessionTab;
typedef std::map<SessionID::id_type, SessionWindow*> IdToSessionWindow;
virtual ~SessionService();
// These types mirror Browser::Type, but are re-defined here because these
// specific enumeration _values_ are written into the session database and
// are needed to maintain forward compatibility.
enum WindowType {
TYPE_NORMAL = 0,
TYPE_POPUP = 1,
TYPE_APP = 2,
TYPE_APP_POPUP = TYPE_APP + TYPE_POPUP,
TYPE_DEVTOOLS = TYPE_APP + 4,
TYPE_APP_PANEL = TYPE_APP + 8
};
void Init();
// Implementation of RestoreIfNecessary. If |browser| is non-null and we need
// to restore, the tabs are added to it, otherwise a new browser is created.
bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
Browser* browser);
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
// Sets the application extension id of the specified tab.
void SetTabExtensionAppID(const SessionID& window_id,
const SessionID& tab_id,
const std::string& extension_app_id);
// Methods to create the various commands. It is up to the caller to delete
// the returned the SessionCommand* object.
SessionCommand* CreateSetSelectedTabInWindow(const SessionID& window_id,
int index);
SessionCommand* CreateSetTabWindowCommand(const SessionID& window_id,
const SessionID& tab_id);
SessionCommand* CreateSetWindowBoundsCommand(const SessionID& window_id,
const gfx::Rect& bounds,
bool is_maximized);
SessionCommand* CreateSetTabIndexInWindowCommand(const SessionID& tab_id,
int new_index);
SessionCommand* CreateTabClosedCommand(SessionID::id_type tab_id);
SessionCommand* CreateWindowClosedCommand(SessionID::id_type tab_id);
SessionCommand* CreateSetSelectedNavigationIndexCommand(
const SessionID& tab_id,
int index);
SessionCommand* CreateSetWindowTypeCommand(const SessionID& window_id,
WindowType type);
SessionCommand* CreatePinnedStateCommand(const SessionID& tab_id,
bool is_pinned);
// Callback from the backend for getting the commands from the save file.
// Converts the commands in SessionWindows and notifies the real callback.
void OnGotSessionCommands(
Handle handle,
scoped_refptr<InternalGetCommandsRequest> request);
// Converts the commands into SessionWindows. On return any valid
// windows are added to valid_windows. It is up to the caller to delete
// the windows added to valid_windows.
//
// If ignore_recent_closes is true, any window/tab closes within in a certain
// time frame are ignored.
void RestoreSessionFromCommands(const std::vector<SessionCommand*>& commands,
std::vector<SessionWindow*>* valid_windows);
// Iterates through the vector updating the selected_tab_index of each
// SessionWindow based on the actual tabs that were restored.
void UpdateSelectedTabIndex(std::vector<SessionWindow*>* windows);
// Returns the window in windows with the specified id. If a window does
// not exist, one is created.
SessionWindow* GetWindow(SessionID::id_type window_id,
IdToSessionWindow* windows);
// Returns the tab with the specified id in tabs. If a tab does not exist,
// it is created.
SessionTab* GetTab(SessionID::id_type tab_id,
IdToSessionTab* tabs);
// Returns an iterator into navigations pointing to the navigation whose
// index matches |index|. If no navigation index matches |index|, the first
// navigation with an index > |index| is returned.
//
// This assumes the navigations are ordered by index in ascending order.
std::vector<TabNavigation>::iterator FindClosestNavigationWithIndex(
std::vector<TabNavigation>* navigations,
int index);
// Does the following:
// . Deletes and removes any windows with no tabs or windows with types other
// than tabbed_browser or browser. NOTE: constrained windows that have
// been dragged out are of type browser. As such, this preserves any dragged
// out constrained windows (aka popups that have been dragged out).
// . Sorts the tabs in windows with valid tabs based on the tabs
// visual order, and adds the valid windows to windows.
void SortTabsBasedOnVisualOrderAndPrune(
std::map<int, SessionWindow*>* windows,
std::vector<SessionWindow*>* valid_windows);
// Adds tabs to their parent window based on the tab's window_id. This
// ignores tabs with no navigations.
void AddTabsToWindows(std::map<int, SessionTab*>* tabs,
std::map<int, SessionWindow*>* windows);
// Creates tabs and windows from the specified commands. The created tabs
// and windows are added to |tabs| and |windows| respectively. It is up to
// the caller to delete the tabs and windows added to |tabs| and |windows|.
//
// This does NOT add any created SessionTabs to SessionWindow.tabs, that is
// done by AddTabsToWindows.
bool CreateTabsAndWindows(const std::vector<SessionCommand*>& data,
std::map<int, SessionTab*>* tabs,
std::map<int, SessionWindow*>* windows);
// Adds commands to commands that will recreate the state of the specified
// NavigationController. This adds at most kMaxNavigationCountToPersist
// navigations (in each direction from the current navigation index).
// A pair is added to tab_to_available_range indicating the range of
// indices that were written.
void BuildCommandsForTab(
const SessionID& window_id,
NavigationController* controller,
int index_in_window,
bool is_pinned,
std::vector<SessionCommand*>* commands,
IdToRange* tab_to_available_range);
// Adds commands to create the specified browser, and invokes
// BuildCommandsForTab for each of the tabs in the browser. This ignores
// any tabs not in the profile we were created with.
void BuildCommandsForBrowser(
Browser* browser,
std::vector<SessionCommand*>* commands,
IdToRange* tab_to_available_range,
std::set<SessionID::id_type>* windows_to_track);
// Iterates over all the known browsers invoking BuildCommandsForBrowser.
// This only adds browsers that should be tracked
// (should_track_changes_for_browser_type returns true). All browsers that
// are tracked are added to windows_to_track (as long as it is non-null).
void BuildCommandsFromBrowsers(
std::vector<SessionCommand*>* commands,
IdToRange* tab_to_available_range,
std::set<SessionID::id_type>* windows_to_track);
// Schedules a reset. A reset means the contents of the file are recreated
// from the state of the browser.
void ScheduleReset();
// Searches for a pending command that can be replaced with command.
// If one is found, pending command is removed, command is added to
// the pending commands and true is returned.
bool ReplacePendingCommand(SessionCommand* command);
// Schedules the specified command. This method takes ownership of the
// command.
virtual void ScheduleCommand(SessionCommand* command);
// Converts all pending tab/window closes to commands and schedules them.
void CommitPendingCloses();
// Returns true if there is only one window open with a single tab that shares
// our profile.
bool IsOnlyOneTabLeft();
// Returns true if there are open trackable browser windows whose ids do
// match |window_id| with our profile. A trackable window is a window from
// which |should_track_changes_for_browser_type| returns true. See
// |should_track_changes_for_browser_type| for details.
bool HasOpenTrackableBrowsers(const SessionID& window_id);
// Returns true if changes to tabs in the specified window should be tracked.
bool ShouldTrackChangesToWindow(const SessionID& window_id);
// Returns true if we track changes to the specified browser type.
static bool should_track_changes_for_browser_type(Browser::Type type) {
return type == Browser::TYPE_NORMAL ||
(type == Browser::TYPE_POPUP && browser_defaults::kRestorePopups);
}
// Returns true if we should record a window close as pending.
// |has_open_trackable_browsers_| must be up-to-date before calling this.
bool should_record_close_as_pending() const {
// When this is called, the browser window being closed is still open, hence
// still in the browser list. If there is a browser window other than the
// one being closed but no trackable windows, then the others must be App
// windows or similar. In this case, we record the close as pending.
return !has_open_trackable_browsers_ &&
(!browser_defaults::kBrowserAliveWithNoWindows ||
BrowserList::size() > 1);
}
// Call when certain session relevant notifications
// (tab_closed, nav_list_pruned) occur. In addition, this is
// currently called when Save() is called to compare how often the
// session data is currently saved verses when we may want to save it.
// It records the data in UMA stats.
void RecordSessionUpdateHistogramData(NotificationType type,
base::TimeTicks* last_updated_time);
// Helper methods to record the histogram data
void RecordUpdatedTabClosed(base::TimeDelta delta, bool use_long_period);
void RecordUpdatedNavListPruned(base::TimeDelta delta, bool use_long_period);
void RecordUpdatedNavEntryCommit(base::TimeDelta delta, bool use_long_period);
void RecordUpdatedSaveTime(base::TimeDelta delta, bool use_long_period);
void RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,
bool use_long_period);
// Convert back/forward between the Browser and SessionService DB window
// types.
static WindowType WindowTypeForBrowserType(Browser::Type type);
static Browser::Type BrowserTypeForWindowType(WindowType type);
NotificationRegistrar registrar_;
// Maps from session tab id to the range of navigation entries that has
// been written to disk.
//
// This is only used if not all the navigation entries have been
// written.
IdToRange tab_to_available_range_;
// When the user closes the last window, where the last window is the
// last tabbed browser and no more tabbed browsers are open with the same
// profile, the window ID is added here. These IDs are only committed (which
// marks them as closed) if the user creates a new tabbed browser.
typedef std::set<SessionID::id_type> PendingWindowCloseIDs;
PendingWindowCloseIDs pending_window_close_ids_;
// Set of tabs that have been closed by way of the last window or last tab
// closing, but not yet committed.
typedef std::set<SessionID::id_type> PendingTabCloseIDs;
PendingTabCloseIDs pending_tab_close_ids_;
// When a window other than the last window (see description of
// pending_window_close_ids) is closed, the id is added to this set.
typedef std::set<SessionID::id_type> WindowClosingIDs;
WindowClosingIDs window_closing_ids_;
// Set of windows we're tracking changes to. This is only browsers that
// return true from should_track_changes_for_browser_type.
typedef std::set<SessionID::id_type> WindowsTracking;
WindowsTracking windows_tracking_;
// Are there any open trackable browsers?
bool has_open_trackable_browsers_;
// If true and a new tabbed browser is created and there are no opened tabbed
// browser (has_open_trackable_browsers_ is false), then the current session
// is made the last session. See description above class for details on
// current/last session.
bool move_on_new_browser_;
// Used for reporting frequency of session altering operations.
base::TimeTicks last_updated_tab_closed_time_;
base::TimeTicks last_updated_nav_list_pruned_time_;
base::TimeTicks last_updated_nav_entry_commit_time_;
base::TimeTicks last_updated_save_time_;
// Constants used in calculating histogram data.
const base::TimeDelta save_delay_in_millis_;
const base::TimeDelta save_delay_in_mins_;
const base::TimeDelta save_delay_in_hrs_;
DISALLOW_COPY_AND_ASSIGN(SessionService);
};
#endif // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_