// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef CHROME_BROWSER_SESSION_SERVICE_H__ #define CHROME_BROWSER_SESSION_SERVICE_H__ #include #include "base/basictypes.h" #include "base/gfx/rect.h" #include "base/task.h" #include "base/time.h" #include "chrome/browser/browser_type.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/session_id.h" #include "chrome/common/notification_service.h" #include "chrome/common/page_transition_types.h" #include "chrome/common/stl_util-inl.h" #include "googleurl/src/gurl.h" class Browser; class NavigationController; class NavigationEntry; class Profile; class TabContents; class Thread; class Timer; class SessionBackend; class SessionCommand; // TabNavigation ------------------------------------------------------------ // TabNavigation corresponds to a NavigationEntry. struct TabNavigation { enum TypeMask { HAS_POST_DATA = 1 }; TabNavigation() : index(-1), transition(PageTransition::TYPED), type_mask(0) { } TabNavigation(int index, const GURL& url, const std::wstring& title, const std::string& state, PageTransition::Type transition) : index(index), url(url), title(title), state(state), transition(transition), type_mask(0) {} // The index in the NavigationController. If this is -1, it means this // TabNavigation is bogus. int index; GURL url; // The title of the page. std::wstring title; std::string state; PageTransition::Type transition; // A mask used for arbitrary boolean values needed to represent a // NavigationEntry. Currently only contains HAS_POST_DATA or 0. int type_mask; }; // SessionTab ---------------------------------------------------------------- // SessionTab corresponds to a NavigationController. struct SessionTab { SessionTab() : tab_visual_index(-1), current_navigation_index(-1) { } // Unique id of the window. SessionID window_id; // Unique if of the tab. SessionID tab_id; // Visual index of the tab within its window. There may be gaps in these // values. // // NOTE: this is really only useful for the SessionService during // restore, others can likely ignore this and use the order of the // tabs in SessionWindow.tabs. int tab_visual_index; // Identifies the index of the current navigation in navigations. For // example, if this is 2 it means the current navigation is navigations[2]. // // NOTE: when the service is creating SessionTabs, initially this // corresponds to TabNavigation.index, not the index in navigations. When done // creating though, this is set to the index in navigations. int current_navigation_index; std::vector navigations; private: DISALLOW_EVIL_CONSTRUCTORS(SessionTab); }; // SessionWindow ------------------------------------------------------------- // Describes a saved window. struct SessionWindow { SessionWindow() : selected_tab_index(-1), type(BrowserType::TABBED_BROWSER), is_constrained(true), is_maximized(false) {} ~SessionWindow() { STLDeleteElements(&tabs); } // Identifier of the window. SessionID window_id; // Bounds of the window. gfx::Rect bounds; // Index of the selected tab in tabs; -1 if no tab is selected. After restore // this value is guaranteed to be a valid index into tabs. // // NOTE: when the service is creating SessionWindows, initially this // corresponds to SessionTab.tab_visual_index, not the index in // tabs. When done creating though, this is set to the index in // tabs. int selected_tab_index; // Type of the browser. Currently we only store browsers of type // TABBED_BROWSER and BROWSER. BrowserType::Type type; // If true, the window is constrained. // // Currently SessionService prunes all constrained windows so that session // restore does not attempt to restore them. bool is_constrained; // The tabs, ordered by visual order. std::vector tabs; // Is the window maximized? bool is_maximized; private: DISALLOW_EVIL_CONSTRUCTORS(SessionWindow); }; // SessionService ------------------------------------------------------------ // SessionService is responsible for maintaining the state of open windows // and tabs so that they can be restored at a later date. // // SessionService supports restoring from two distinct points: // . The last run of the browser. // . A user defined point. That is, any time CreateSavedSession is invoked // the save session is reset from the current state of the browser. // // Additionally the current session can be made the 'last' session at any point // by way of MoveCurrentSessionToLastSession. // // 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 CancelableRequestProvider, public NotificationObserver, public base::RefCountedThreadSafe { friend class SessionServiceTestHelper; public: // Creates a SessionService for the specified profile. explicit SessionService(Profile* profile); // For testing. explicit SessionService(const std::wstring& save_path); ~SessionService(); // Returns true if there are any open tabbed browser windows. bool has_open_tabbed_browsers() const { return has_open_tabbed_browsers_; } // Returns true if a tabbed browser has ever been created. bool tabbed_browser_created() const { return tabbed_browser_created_; } // 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); // Notification that a tab has been closed. // // 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); // 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, BrowserType::Type type); // Removes the navigation entries for tab_id whose indices are >= index. void TabNavigationPathPruned(const SessionID& window_id, const SessionID& tab_id, int index); // 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); // 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*>::Type SavedSessionCallback; // Fetches the contents of the save session, notifying the callback when // done. If the callback is supplied an empty vector of SessionWindows // it means the session could not be restored. Handle GetSavedSession(CancelableRequestConsumerBase* consumer, SavedSessionCallback* callback); // 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. Handle GetLastSession(CancelableRequestConsumerBase* consumer, SavedSessionCallback* callback); // Creates a save session from the current state of the browser. void CreateSavedSession(); // Deletes the saved session if saved session is true, or the last session // if saved_session is false. void DeleteSession(bool saved_session); // Creates a saved session from the contents of the last session. void CopyLastSessionToSavedSession(); // The callback from Get*Session is internally routed to SessionService // first. This is done so that the SessionWindows can be recreated from // the SessionCommands. The following types are used for this. class InternalSavedSessionRequest; typedef Callback2 >::Type InternalSavedSessionCallback; // Request class used from Get*Session. class InternalSavedSessionRequest : public CancelableRequest { public: InternalSavedSessionRequest(CallbackType* callback, SavedSessionCallback* real_callback, bool is_saved_session) : CancelableRequest(callback), real_callback(real_callback), is_saved_session(is_saved_session) { } virtual ~InternalSavedSessionRequest(); // The callback supplied to Get*Session. scoped_ptr real_callback; // Whether the request is for a saved session, or the last session. bool is_saved_session; // The commands. The backend fills this in for us. std::vector commands; private: DISALLOW_EVIL_CONSTRUCTORS(InternalSavedSessionRequest); }; private: typedef std::map > IdToRange; typedef std::map IdToSessionTab; typedef std::map IdToSessionWindow; // Various initialization; called from the constructor. void Init(const std::wstring& path); virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); // Get*Session call into this to schedule the request. The 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 GetSessionImpl(CancelableRequestConsumerBase* consumer, SavedSessionCallback* callback, bool is_saved_session); // 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* CreateUpdateTabNavigationCommand( const SessionID& tab_id, int index, const NavigationEntry& entry); SessionCommand* CreateSetSelectedNavigationIndexCommand( const SessionID& tab_id, int index); SessionCommand* CreateSetWindowTypeCommand(const SessionID& window_id, BrowserType::Type type); // Callback form the backend for getting the commands from the previous // or save file. Converts the commands in SessionWindows and notifies // the real callback. void OnGotSessionCommands( Handle handle, scoped_refptr 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& commands, std::vector* 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* 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::iterator FindClosestNavigationWithIndex( std::vector* 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* windows, std::vector* 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* tabs, std::map* 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& data, std::map* tabs, std::map* 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, std::vector* 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* commands, IdToRange* tab_to_available_range, std::set* 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* commands, IdToRange* tab_to_available_range, std::set* 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. void ScheduleCommand(SessionCommand* command); // Converts all pending tab/window closes to commands and schedules them. void CommitPendingCloses(); // Saves pending commands to the backend. void Save(); // Starts the save timer (if it isn't running already). void StartSaveTimer(); // Returns true if there is only one window open with a single tab that shares // our profile. bool IsOnlyOneTabLeft(); // Returns true if there are no open tabbed browser windows with our profile, // or the only tabbed browser open has a session id of window_id. bool HasOpenTabbedBrowsers(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(BrowserType::Type type) { return type == BrowserType::TABBED_BROWSER; } // The profile used to determine where to save, as well as what tabs // to persist. Profile* profile_; // The number of commands sent to the backend before doing a reset. int commands_since_reset_; // 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_; // Commands we need to send over to the backend. std::vector pending_commands_; // Whether the backend file should be recreated the next time we send // over the commands. bool pending_reset_; // Used to invoke Save. ScopedRunnableMethodFactory save_factory_; // When the user closes the last window, where the last window is the 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 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 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 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 WindowsTracking; WindowsTracking windows_tracking_; // The backend. scoped_refptr backend_; // Thread backend tasks are run on. This comes from the profile, and is // null during testing. Thread* backend_thread_; // Are there any open open tabbed browsers? bool has_open_tabbed_browsers_; // Was a tabbed browser ever created? bool tabbed_browser_created_; }; #endif // CHROME_BROWSER_SESSION_SERVICE_H__