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