1// Copyright (c) 2010 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_TAB_RESTORE_SERVICE_H_
6#define CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
7#pragma once
8
9#include <list>
10#include <set>
11#include <vector>
12
13#include "base/observer_list.h"
14#include "base/time.h"
15#include "chrome/browser/sessions/base_session_service.h"
16#include "chrome/browser/sessions/session_id.h"
17#include "chrome/browser/sessions/session_types.h"
18#include "content/browser/in_process_webkit/session_storage_namespace.h"
19
20class NavigationController;
21class Profile;
22class TabRestoreServiceDelegate;
23class TabRestoreServiceObserver;
24struct SessionWindow;
25
26// TabRestoreService is responsible for maintaining the most recently closed
27// tabs and windows. When a tab is closed
28// TabRestoreService::CreateHistoricalTab is invoked and a Tab is created to
29// represent the tab. Similarly, when a browser is closed, BrowserClosing is
30// invoked and a Window is created to represent the window.
31//
32// To restore a tab/window from the TabRestoreService invoke RestoreEntryById
33// or RestoreMostRecentEntry.
34//
35// To listen for changes to the set of entries managed by the TabRestoreService
36// add an observer.
37class TabRestoreService : public BaseSessionService {
38 public:
39  // Interface used to allow the test to provide a custom time.
40  class TimeFactory {
41   public:
42    virtual ~TimeFactory();
43    virtual base::Time TimeNow() = 0;
44  };
45
46  // The type of entry.
47  enum Type {
48    TAB,
49    WINDOW
50  };
51
52  struct Entry {
53    Entry();
54    explicit Entry(Type type);
55    virtual ~Entry();
56
57    // Unique id for this entry. The id is guaranteed to be unique for a
58    // session.
59    SessionID::id_type id;
60
61    // The type of the entry.
62    Type type;
63
64    // The time when the window or tab was closed.
65    base::Time timestamp;
66
67    // Is this entry from the last session? This is set to true for entries that
68    // were closed during the last session, and false for entries that were
69    // closed during this session.
70    bool from_last_session;
71  };
72
73  // Represents a previously open tab.
74  struct Tab : public Entry {
75    Tab();
76    virtual ~Tab();
77
78    bool has_browser() const { return browser_id > 0; }
79
80    // The navigations.
81    std::vector<TabNavigation> navigations;
82
83    // Index of the selected navigation in navigations.
84    int current_navigation_index;
85
86    // The ID of the browser to which this tab belonged, so it can be restored
87    // there. May be 0 (an invalid SessionID) when restoring an entire session.
88    SessionID::id_type browser_id;
89
90    // Index within the tab strip. May be -1 for an unknown index.
91    int tabstrip_index;
92
93    // True if the tab was pinned.
94    bool pinned;
95
96    // If non-empty gives the id of the extension for the tab.
97    std::string extension_app_id;
98
99    // The associated session storage namespace (if any).
100    scoped_refptr<SessionStorageNamespace> session_storage_namespace;
101  };
102
103  // Represents a previously open window.
104  struct Window : public Entry {
105    Window();
106    virtual ~Window();
107
108    // The tabs that comprised the window, in order.
109    std::vector<Tab> tabs;
110
111    // Index of the selected tab.
112    int selected_tab_index;
113  };
114
115  typedef std::list<Entry*> Entries;
116
117  // Creates a new TabRestoreService and provides an object that provides the
118  // current time. The TabRestoreService does not take ownership of the
119  // |time_factory_|.
120  explicit TabRestoreService(Profile* profile,
121                             TimeFactory* time_factory_ = NULL);
122
123  // Adds/removes an observer. TabRestoreService does not take ownership of
124  // the observer.
125  void AddObserver(TabRestoreServiceObserver* observer);
126  void RemoveObserver(TabRestoreServiceObserver* observer);
127
128  // Creates a Tab to represent |tab| and notifies observers the list of
129  // entries has changed.
130  void CreateHistoricalTab(NavigationController* tab, int index);
131
132  // Invoked when a browser is closing. If |delegate| is a tabbed browser with
133  // at least one tab, a Window is created, added to entries and observers are
134  // notified.
135  void BrowserClosing(TabRestoreServiceDelegate* delegate);
136
137  // Invoked when the browser is done closing.
138  void BrowserClosed(TabRestoreServiceDelegate* delegate);
139
140  // Removes all entries from the list and notifies observers the list
141  // of tabs has changed.
142  void ClearEntries();
143
144  // Returns the entries, ordered with most recently closed entries at the
145  // front.
146  virtual const Entries& entries() const;
147
148  // Restores the most recently closed entry. Does nothing if there are no
149  // entries to restore. If the most recently restored entry is a tab, it is
150  // added to |delegate|.
151  void RestoreMostRecentEntry(TabRestoreServiceDelegate* delegate);
152
153  // Restores an entry by id. If there is no entry with an id matching |id|,
154  // this does nothing. If |replace_existing_tab| is true and id identifies a
155  // tab, the newly created tab replaces the selected tab in |delegate|. If
156  // |delegate| is NULL, this creates a new window for the entry.
157  void RestoreEntryById(TabRestoreServiceDelegate* delegate,
158                        SessionID::id_type id,
159                        bool replace_existing_tab);
160
161  // Loads the tabs and previous session. This does nothing if the tabs
162  // from the previous session have already been loaded.
163  void LoadTabsFromLastSession();
164
165  // Max number of entries we'll keep around.
166  static const size_t kMaxEntries;
167
168  // Creates and add entries to |entries| for each of the windows in |windows|.
169  void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows,
170                                std::vector<Entry*>* entries);
171
172 protected:
173  virtual void Save();
174
175  virtual ~TabRestoreService();
176
177 private:
178  // Used to indicate what has loaded.
179  enum LoadState {
180    // Indicates we haven't loaded anything.
181    NOT_LOADED           = 1 << 0,
182
183    // Indicates we've asked for the last sessions and tabs but haven't gotten
184    // the result back yet.
185    LOADING              = 1 << 2,
186
187    // Indicates we finished loading the last tabs (but not necessarily the
188    // last session).
189    LOADED_LAST_TABS     = 1 << 3,
190
191    // Indicates we finished loading the last session (but not necessarily the
192    // last tabs).
193    LOADED_LAST_SESSION  = 1 << 4
194  };
195
196  // Populates the tab's navigations from the NavigationController, and its
197  // browser_id and pinned state from the browser.
198  void PopulateTab(Tab* tab,
199                   int index,
200                   TabRestoreServiceDelegate* delegate,
201                   NavigationController* controller);
202
203  // Notifies observers the tabs have changed.
204  void NotifyTabsChanged();
205
206  // Adds |entry| to the list of entries and takes ownership. If |prune| is true
207  // |PruneAndNotify| is invoked. If |to_front| is true the entry is added to
208  // the front, otherwise the back. Normal closes go to the front, but
209  // tab/window closes from the previous session are added to the back.
210  void AddEntry(Entry* entry, bool prune, bool to_front);
211
212  // Prunes entries_ to contain only kMaxEntries and invokes NotifyTabsChanged.
213  void PruneAndNotify();
214
215  // Returns an iterator into entries_ whose id matches |id|. If |id| identifies
216  // a Window, then its iterator position will be returned. If it identifies a
217  // tab, then the iterator position of the Window in which the Tab resides is
218  // returned.
219  Entries::iterator GetEntryIteratorById(SessionID::id_type id);
220
221  // Schedules the commands for a window close.
222  void ScheduleCommandsForWindow(const Window& window);
223
224  // Schedules the commands for a tab close. |selected_index| gives the
225  // index of the selected navigation.
226  void ScheduleCommandsForTab(const Tab& tab, int selected_index);
227
228  // Creates a window close command.
229  SessionCommand* CreateWindowCommand(SessionID::id_type id,
230                                      int selected_tab_index,
231                                      int num_tabs,
232                                      base::Time timestamp);
233
234  // Creates a tab close command.
235  SessionCommand* CreateSelectedNavigationInTabCommand(
236      SessionID::id_type tab_id,
237      int32 index,
238      base::Time timestamp);
239
240  // Creates a restore command.
241  SessionCommand* CreateRestoredEntryCommand(SessionID::id_type entry_id);
242
243  // Returns the index to persist as the selected index. This is the same
244  // as |tab.current_navigation_index| unless the entry at
245  // |tab.current_navigation_index| shouldn't be persisted. Returns -1 if
246  // no valid navigation to persist.
247  int GetSelectedNavigationIndexToPersist(const Tab& tab);
248
249  // Invoked when we've loaded the session commands that identify the
250  // previously closed tabs. This creates entries, adds them to
251  // staging_entries_, and invokes LoadState.
252  void OnGotLastSessionCommands(
253      Handle handle,
254      scoped_refptr<InternalGetCommandsRequest> request);
255
256  // Populates |loaded_entries| with Entries from |request|.
257  void CreateEntriesFromCommands(
258      scoped_refptr<InternalGetCommandsRequest> request,
259      std::vector<Entry*>* loaded_entries);
260
261  // This is a helper function for RestoreEntryById() for restoring a single
262  // tab. If |replace_existing_tab| is true, the newly created tab replaces the
263  // selected tab in |delegate|. If |delegate| is NULL, this creates a new
264  // window for the entry. This returns the TabRestoreServiceDelegate into which
265  // the tab was restored.
266  TabRestoreServiceDelegate* RestoreTab(const Tab& tab,
267                                        TabRestoreServiceDelegate* delegate,
268                                        bool replace_existing_tab);
269
270  // Returns true if |tab| has more than one navigation. If |tab| has more
271  // than one navigation |tab->current_navigation_index| is constrained based
272  // on the number of navigations.
273  bool ValidateTab(Tab* tab);
274
275  // Validates all entries in |entries|, deleting any with no navigations.
276  // This also deletes any entries beyond the max number of entries we can
277  // hold.
278  void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
279
280  // Finds tab entries with the old browser_id and sets it to the new one.
281  void UpdateTabBrowserIDs(SessionID::id_type old_id,
282                           SessionID::id_type new_id);
283
284  // Callback from SessionService when we've received the windows from the
285  // previous session. This creates and add entries to |staging_entries_|
286  // and invokes LoadStateChanged.
287  void OnGotPreviousSession(Handle handle,
288                            std::vector<SessionWindow*>* windows);
289
290  // Converts a SessionWindow into a Window, returning true on success. We use 0
291  // as the timestamp here since we do not know when the window/tab was closed.
292  bool ConvertSessionWindowToWindow(
293      SessionWindow* session_window,
294      Window* window);
295
296  // Invoked when previous tabs or session is loaded. If both have finished
297  // loading the entries in staging_entries_ are added to entries_ and
298  // observers are notified.
299  void LoadStateChanged();
300
301  // Gets the current time. This uses the time_factory_ if there is one.
302  base::Time TimeNow() const;
303
304  // Set of entries.
305  Entries entries_;
306
307  // Whether we've loaded the last session.
308  int load_state_;
309
310  // Are we restoring a tab? If this is true we ignore requests to create a
311  // historical tab.
312  bool restoring_;
313
314  // Have the max number of entries ever been created?
315  bool reached_max_;
316
317  // The number of entries to write.
318  int entries_to_write_;
319
320  // Number of entries we've written.
321  int entries_written_;
322
323  ObserverList<TabRestoreServiceObserver> observer_list_;
324
325  // Set of delegates that we've received a BrowserClosing method for but no
326  // corresponding BrowserClosed. We cache the set of delegates closing to
327  // avoid creating historical tabs for them.
328  std::set<TabRestoreServiceDelegate*> closing_delegates_;
329
330  // Used when loading previous tabs/session.
331  CancelableRequestConsumer load_consumer_;
332
333  // Results from previously closed tabs/sessions is first added here. When
334  // the results from both us and the session restore service have finished
335  // loading LoadStateChanged is invoked, which adds these entries to
336  // entries_.
337  std::vector<Entry*> staging_entries_;
338
339  TimeFactory* time_factory_;
340
341  DISALLOW_COPY_AND_ASSIGN(TabRestoreService);
342};
343
344#endif  // CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
345