// Copyright (c) 2010 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_TAB_RESTORE_SERVICE_H_
#define CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
#pragma once
#include <list>
#include <set>
#include <vector>
#include "base/observer_list.h"
#include "base/time.h"
#include "chrome/browser/sessions/base_session_service.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/sessions/session_types.h"
#include "content/browser/in_process_webkit/session_storage_namespace.h"
class NavigationController;
class Profile;
class TabRestoreServiceDelegate;
class TabRestoreServiceObserver;
struct SessionWindow;
// TabRestoreService is responsible for maintaining the most recently closed
// tabs and windows. When a tab is closed
// TabRestoreService::CreateHistoricalTab is invoked and a Tab is created to
// represent the tab. Similarly, when a browser is closed, BrowserClosing is
// invoked and a Window is created to represent the window.
//
// To restore a tab/window from the TabRestoreService invoke RestoreEntryById
// or RestoreMostRecentEntry.
//
// To listen for changes to the set of entries managed by the TabRestoreService
// add an observer.
class TabRestoreService : public BaseSessionService {
public:
// Interface used to allow the test to provide a custom time.
class TimeFactory {
public:
virtual ~TimeFactory();
virtual base::Time TimeNow() = 0;
};
// The type of entry.
enum Type {
TAB,
WINDOW
};
struct Entry {
Entry();
explicit Entry(Type type);
virtual ~Entry();
// Unique id for this entry. The id is guaranteed to be unique for a
// session.
SessionID::id_type id;
// The type of the entry.
Type type;
// The time when the window or tab was closed.
base::Time timestamp;
// Is this entry from the last session? This is set to true for entries that
// were closed during the last session, and false for entries that were
// closed during this session.
bool from_last_session;
};
// Represents a previously open tab.
struct Tab : public Entry {
Tab();
virtual ~Tab();
bool has_browser() const { return browser_id > 0; }
// The navigations.
std::vector<TabNavigation> navigations;
// Index of the selected navigation in navigations.
int current_navigation_index;
// The ID of the browser to which this tab belonged, so it can be restored
// there. May be 0 (an invalid SessionID) when restoring an entire session.
SessionID::id_type browser_id;
// Index within the tab strip. May be -1 for an unknown index.
int tabstrip_index;
// True if the tab was pinned.
bool pinned;
// If non-empty gives the id of the extension for the tab.
std::string extension_app_id;
// The associated session storage namespace (if any).
scoped_refptr<SessionStorageNamespace> session_storage_namespace;
};
// Represents a previously open window.
struct Window : public Entry {
Window();
virtual ~Window();
// The tabs that comprised the window, in order.
std::vector<Tab> tabs;
// Index of the selected tab.
int selected_tab_index;
};
typedef std::list<Entry*> Entries;
// Creates a new TabRestoreService and provides an object that provides the
// current time. The TabRestoreService does not take ownership of the
// |time_factory_|.
explicit TabRestoreService(Profile* profile,
TimeFactory* time_factory_ = NULL);
// Adds/removes an observer. TabRestoreService does not take ownership of
// the observer.
void AddObserver(TabRestoreServiceObserver* observer);
void RemoveObserver(TabRestoreServiceObserver* observer);
// Creates a Tab to represent |tab| and notifies observers the list of
// entries has changed.
void CreateHistoricalTab(NavigationController* tab, int index);
// Invoked when a browser is closing. If |delegate| is a tabbed browser with
// at least one tab, a Window is created, added to entries and observers are
// notified.
void BrowserClosing(TabRestoreServiceDelegate* delegate);
// Invoked when the browser is done closing.
void BrowserClosed(TabRestoreServiceDelegate* delegate);
// Removes all entries from the list and notifies observers the list
// of tabs has changed.
void ClearEntries();
// Returns the entries, ordered with most recently closed entries at the
// front.
virtual const Entries& entries() const;
// Restores the most recently closed entry. Does nothing if there are no
// entries to restore. If the most recently restored entry is a tab, it is
// added to |delegate|.
void RestoreMostRecentEntry(TabRestoreServiceDelegate* delegate);
// Restores an entry by id. If there is no entry with an id matching |id|,
// this does nothing. If |replace_existing_tab| is true and id identifies a
// tab, the newly created tab replaces the selected tab in |delegate|. If
// |delegate| is NULL, this creates a new window for the entry.
void RestoreEntryById(TabRestoreServiceDelegate* delegate,
SessionID::id_type id,
bool replace_existing_tab);
// Loads the tabs and previous session. This does nothing if the tabs
// from the previous session have already been loaded.
void LoadTabsFromLastSession();
// Max number of entries we'll keep around.
static const size_t kMaxEntries;
// Creates and add entries to |entries| for each of the windows in |windows|.
void CreateEntriesFromWindows(std::vector<SessionWindow*>* windows,
std::vector<Entry*>* entries);
protected:
virtual void Save();
virtual ~TabRestoreService();
private:
// Used to indicate what has loaded.
enum LoadState {
// Indicates we haven't loaded anything.
NOT_LOADED = 1 << 0,
// Indicates we've asked for the last sessions and tabs but haven't gotten
// the result back yet.
LOADING = 1 << 2,
// Indicates we finished loading the last tabs (but not necessarily the
// last session).
LOADED_LAST_TABS = 1 << 3,
// Indicates we finished loading the last session (but not necessarily the
// last tabs).
LOADED_LAST_SESSION = 1 << 4
};
// Populates the tab's navigations from the NavigationController, and its
// browser_id and pinned state from the browser.
void PopulateTab(Tab* tab,
int index,
TabRestoreServiceDelegate* delegate,
NavigationController* controller);
// Notifies observers the tabs have changed.
void NotifyTabsChanged();
// Adds |entry| to the list of entries and takes ownership. If |prune| is true
// |PruneAndNotify| is invoked. If |to_front| is true the entry is added to
// the front, otherwise the back. Normal closes go to the front, but
// tab/window closes from the previous session are added to the back.
void AddEntry(Entry* entry, bool prune, bool to_front);
// Prunes entries_ to contain only kMaxEntries and invokes NotifyTabsChanged.
void PruneAndNotify();
// Returns an iterator into entries_ whose id matches |id|. If |id| identifies
// a Window, then its iterator position will be returned. If it identifies a
// tab, then the iterator position of the Window in which the Tab resides is
// returned.
Entries::iterator GetEntryIteratorById(SessionID::id_type id);
// Schedules the commands for a window close.
void ScheduleCommandsForWindow(const Window& window);
// Schedules the commands for a tab close. |selected_index| gives the
// index of the selected navigation.
void ScheduleCommandsForTab(const Tab& tab, int selected_index);
// Creates a window close command.
SessionCommand* CreateWindowCommand(SessionID::id_type id,
int selected_tab_index,
int num_tabs,
base::Time timestamp);
// Creates a tab close command.
SessionCommand* CreateSelectedNavigationInTabCommand(
SessionID::id_type tab_id,
int32 index,
base::Time timestamp);
// Creates a restore command.
SessionCommand* CreateRestoredEntryCommand(SessionID::id_type entry_id);
// Returns the index to persist as the selected index. This is the same
// as |tab.current_navigation_index| unless the entry at
// |tab.current_navigation_index| shouldn't be persisted. Returns -1 if
// no valid navigation to persist.
int GetSelectedNavigationIndexToPersist(const Tab& tab);
// Invoked when we've loaded the session commands that identify the
// previously closed tabs. This creates entries, adds them to
// staging_entries_, and invokes LoadState.
void OnGotLastSessionCommands(
Handle handle,
scoped_refptr<InternalGetCommandsRequest> request);
// Populates |loaded_entries| with Entries from |request|.
void CreateEntriesFromCommands(
scoped_refptr<InternalGetCommandsRequest> request,
std::vector<Entry*>* loaded_entries);
// This is a helper function for RestoreEntryById() for restoring a single
// tab. If |replace_existing_tab| is true, the newly created tab replaces the
// selected tab in |delegate|. If |delegate| is NULL, this creates a new
// window for the entry. This returns the TabRestoreServiceDelegate into which
// the tab was restored.
TabRestoreServiceDelegate* RestoreTab(const Tab& tab,
TabRestoreServiceDelegate* delegate,
bool replace_existing_tab);
// Returns true if |tab| has more than one navigation. If |tab| has more
// than one navigation |tab->current_navigation_index| is constrained based
// on the number of navigations.
bool ValidateTab(Tab* tab);
// Validates all entries in |entries|, deleting any with no navigations.
// This also deletes any entries beyond the max number of entries we can
// hold.
void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
// Finds tab entries with the old browser_id and sets it to the new one.
void UpdateTabBrowserIDs(SessionID::id_type old_id,
SessionID::id_type new_id);
// Callback from SessionService when we've received the windows from the
// previous session. This creates and add entries to |staging_entries_|
// and invokes LoadStateChanged.
void OnGotPreviousSession(Handle handle,
std::vector<SessionWindow*>* windows);
// Converts a SessionWindow into a Window, returning true on success. We use 0
// as the timestamp here since we do not know when the window/tab was closed.
bool ConvertSessionWindowToWindow(
SessionWindow* session_window,
Window* window);
// Invoked when previous tabs or session is loaded. If both have finished
// loading the entries in staging_entries_ are added to entries_ and
// observers are notified.
void LoadStateChanged();
// Gets the current time. This uses the time_factory_ if there is one.
base::Time TimeNow() const;
// Set of entries.
Entries entries_;
// Whether we've loaded the last session.
int load_state_;
// Are we restoring a tab? If this is true we ignore requests to create a
// historical tab.
bool restoring_;
// Have the max number of entries ever been created?
bool reached_max_;
// The number of entries to write.
int entries_to_write_;
// Number of entries we've written.
int entries_written_;
ObserverList<TabRestoreServiceObserver> observer_list_;
// Set of delegates that we've received a BrowserClosing method for but no
// corresponding BrowserClosed. We cache the set of delegates closing to
// avoid creating historical tabs for them.
std::set<TabRestoreServiceDelegate*> closing_delegates_;
// Used when loading previous tabs/session.
CancelableRequestConsumer load_consumer_;
// Results from previously closed tabs/sessions is first added here. When
// the results from both us and the session restore service have finished
// loading LoadStateChanged is invoked, which adds these entries to
// entries_.
std::vector<Entry*> staging_entries_;
TimeFactory* time_factory_;
DISALLOW_COPY_AND_ASSIGN(TabRestoreService);
};
#endif // CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_