// Copyright (c) 2012 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_ #include <map> #include <string> #include "base/basictypes.h" #include "base/callback.h" #include "base/memory/scoped_vector.h" #include "base/task/cancelable_task_tracker.h" #include "base/time/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_finder.h" #include "chrome/browser/ui/browser_list_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "ui/base/ui_base_types.h" class Profile; class SessionCommand; struct SessionTab; struct SessionWindow; namespace content { class NavigationEntry; class WebContents; } // 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 SerializedNavigationEntry). 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 KeyedService, public content::NotificationObserver, public chrome::BrowserListObserver { friend class SessionServiceTestHelper; public: // Used to distinguish an application window from a normal one. enum AppType { TYPE_APP, TYPE_NORMAL }; // Creates a SessionService for the specified profile. explicit SessionService(Profile* profile); // For testing. explicit SessionService(const base::FilePath& save_path); virtual ~SessionService(); // Returns true if a new window opening should really be treated like the // start of a session (with potential session restore, startup URLs, etc.). // In particular, this is true if there are no tabbed browsers running // currently (eg. because only background or other app pages are running). bool ShouldNewWindowStartSession(); // 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, ui::WindowShowState show_state); // 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 |WebContents::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 a window has opened. void WindowOpened(Browser* browser); // 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); // Called when a tab is inserted. void TabInserted(content::WebContents* contents); // Called when a tab is closing. void TabClosing(content::WebContents* contents); // 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, AppType app_type); // Sets the application name of the specified window. void SetWindowAppName(const SessionID& window_id, const std::string& app_name); // 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, const sessions::SerializedNavigationEntry& navigation); // Notification that a tab has restored its entries or a closed tab is being // reused. void TabRestored(content::WebContents* tab, 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); // Sets the user agent override of the specified tab. void SetTabUserAgentOverride(const SessionID& window_id, const SessionID& tab_id, const std::string& user_agent_override); // Callback from GetLastSession. // The second parameter is the id of the window that was last active. typedef base::Callback<void(ScopedVector<SessionWindow>, SessionID::id_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. base::CancelableTaskTracker::TaskId GetLastSession( const SessionCallback& callback, base::CancelableTaskTracker* tracker); // Overridden from BaseSessionService because we want some UMA reporting on // session update activities. virtual void Save() OVERRIDE; private: // Allow tests to access our innards for testing purposes. FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, RestoreActivation1); FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, RestoreActivation2); FRIEND_TEST_ALL_PREFIXES(NoStartupWindowTest, DontInitSessionServiceForApps); 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; // 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. // Note that we only store browsers of type TYPE_TABBED and TYPE_POPUP. enum WindowType { TYPE_TABBED = 0, TYPE_POPUP = 1 }; void Init(); // Returns true if we have scheduled any commands, or any scheduled commands // have been saved. bool processed_any_commands(); // 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(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; // chrome::BrowserListObserver virtual void OnBrowserAdded(Browser* browser) OVERRIDE {} virtual void OnBrowserRemoved(Browser* browser) OVERRIDE {} virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE; // 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, ui::WindowShowState show_state); 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); SessionCommand* CreateSessionStorageAssociatedCommand( const SessionID& tab_id, const std::string& session_storage_persistent_id); SessionCommand* CreateSetActiveWindowCommand(const SessionID& window_id); // Converts |commands| to SessionWindows and notifies the callback. void OnGotSessionCommands(const SessionCallback& callback, ScopedVector<SessionCommand> commands); // 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. |active_window_id| will be set with the // id of the last active window, but it's only valid when this id corresponds // to the id of one of the windows in valid_windows. void RestoreSessionFromCommands(const std::vector<SessionCommand*>& commands, std::vector<SessionWindow*>* valid_windows, SessionID::id_type* active_window_id); // 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<sessions::SerializedNavigationEntry>::iterator FindClosestNavigationWithIndex( std::vector<sessions::SerializedNavigationEntry>* 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 commands specified in |data|. The created // tabs and windows are added to |tabs| and |windows| respectively, with the // id of the active window set in |active_window_id|. 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, SessionID::id_type* active_window_id); // Adds commands to commands that will recreate the state of the specified // tab. 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, content::WebContents* tab, 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) OVERRIDE; // 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() const; // 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) const; // Returns true if changes to tabs in the specified window should be tracked. bool ShouldTrackChangesToWindow(const SessionID& window_id) const; // Returns true if we track changes to the specified browser. bool ShouldTrackBrowser(Browser* browser) const; // Returns true if we track changes to the specified browser type. static bool should_track_changes_for_browser_type( Browser::Type type, AppType app_type); // 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(int 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); // Deletes session data if no windows are open for the current profile. void MaybeDeleteSessionOnlyData(); // Convert back/forward between the Browser and SessionService DB window // types. static WindowType WindowTypeForBrowserType(Browser::Type type); static Browser::Type BrowserTypeForWindowType(WindowType type); content::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_; // For browser_tests, since we want to simulate the browser shutting down // without quitting. bool force_browser_not_alive_with_no_windows_; DISALLOW_COPY_AND_ASSIGN(SessionService); }; #endif // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_