summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-25 22:45:39 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-25 22:45:39 +0000
commitd8375fdbe8d32ad3562152ecd53378383e393971 (patch)
tree8c8f3e524fb5e600095368e982e5730916259453
parentec9f22cd4e7151ab3c3eb6ba47885bd75d342ae3 (diff)
downloadchromium_src-d8375fdbe8d32ad3562152ecd53378383e393971.zip
chromium_src-d8375fdbe8d32ad3562152ecd53378383e393971.tar.gz
chromium_src-d8375fdbe8d32ad3562152ecd53378383e393971.tar.bz2
Changes tab restore service to handle restoring closed windows as a
single unit. Sadly I've written another ui test. Lets hope it isn't flakey. Glen is going to change the NTP to deal with this appropriately. BUG=4686 TEST=Try closing a window (with more than one window open), hitting control-shift-t, and make sure the window and all it's tabs comes back. Review URL: http://codereview.chromium.org/11377 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6003 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser.cc22
-rw-r--r--chrome/browser/browsing_data_remover.cc2
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.cc98
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.h10
-rw-r--r--chrome/browser/tab_restore_service.cc212
-rw-r--r--chrome/browser/tab_restore_service.h155
-rw-r--r--chrome/browser/tab_restore_uitest.cc54
-rw-r--r--chrome/test/automation/browser_proxy.cc18
-rw-r--r--chrome/test/automation/browser_proxy.h3
9 files changed, 361 insertions, 213 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index efacd90..a8cc944 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -189,6 +189,10 @@ Browser::~Browser() {
if (session_service)
session_service->WindowClosed(session_id_);
+ TabRestoreService* tab_restore_service = profile()->GetTabRestoreService();
+ if (tab_restore_service)
+ tab_restore_service->BrowserClosed(this);
+
NotificationService::current()->RemoveObserver(
this, NOTIFY_SSL_STATE_CHANGED, NotificationService::AllSources());
@@ -439,6 +443,10 @@ void Browser::OnWindowClosing() {
if (session_service)
session_service->WindowClosing(session_id());
+ TabRestoreService* tab_restore_service = profile()->GetTabRestoreService();
+ if (tab_restore_service)
+ tab_restore_service->BrowserClosing(this);
+
CloseAllTabs();
}
@@ -689,14 +697,7 @@ void Browser::RestoreTab() {
if (!service)
return;
- const TabRestoreService::Tabs& tabs = service->tabs();
- if (tabs.empty() || tabs.front().from_last_session)
- return;
-
- const TabRestoreService::HistoricalTab& tab = tabs.front();
- AddRestoredTab(tab.navigations, tab_count(), tab.current_navigation_index,
- true);
- service->RemoveHistoricalTabById(tab.id);
+ service->RestoreMostRecentEntry(this);
}
void Browser::ConvertPopupToTabbedBrowser() {
@@ -1950,6 +1951,11 @@ void Browser::InitCommandState() {
void Browser::UpdateNavigationCommands() {
TabContents* current_tab = GetSelectedTabContents();
+ if (!current_tab) {
+ // It's possible for this to be null during tab restore.
+ return;
+ }
+
NavigationController* nc = current_tab->controller();
controller_.UpdateCommandEnabled(IDC_BACK, nc->CanGoBack());
controller_.UpdateCommandEnabled(IDC_FORWARD, nc->CanGoForward());
diff --git a/chrome/browser/browsing_data_remover.cc b/chrome/browser/browsing_data_remover.cc
index 41aa9ae..e0cdc68 100644
--- a/chrome/browser/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data_remover.cc
@@ -78,7 +78,7 @@ void BrowsingDataRemover::Remove(int remove_mask) {
// they can't be more than a day old, so we can simply clear them all.
TabRestoreService* tab_service = profile_->GetTabRestoreService();
if (tab_service)
- tab_service->ClearHistoricalTabs();
+ tab_service->ClearEntries();
// We also delete the last session when we delete the history.
SessionService* session_service = profile_->GetSessionService();
diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc
index 729c9dc..5a87b2d 100644
--- a/chrome/browser/dom_ui/new_tab_ui.cc
+++ b/chrome/browser/dom_ui/new_tab_ui.cc
@@ -644,20 +644,10 @@ void RecentlyClosedTabsHandler::HandleReopenTab(const Value* content) {
std::wstring wstring_value;
if (string_value->GetAsString(&wstring_value)) {
int session_to_restore = _wtoi(wstring_value.c_str());
-
- const TabRestoreService::Tabs& tabs = tab_restore_service_->tabs();
- for (TabRestoreService::Tabs::const_iterator it = tabs.begin();
- it != tabs.end(); ++it) {
- if (it->id == session_to_restore) {
- TabRestoreService* tab_restore_service = tab_restore_service_;
- browser->ReplaceRestoredTab(
- it->navigations, it->current_navigation_index);
- tab_restore_service->RemoveHistoricalTabById(session_to_restore);
- // The current tab has been nuked at this point;
- // don't touch any member variables.
- break;
- }
- }
+ tab_restore_service_->RestoreEntryById(browser, session_to_restore,
+ true);
+ // The current tab has been nuked at this point; don't touch any member
+ // variables.
}
}
}
@@ -680,28 +670,28 @@ void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs(
void RecentlyClosedTabsHandler::TabRestoreServiceChanged(
TabRestoreService* service) {
- const TabRestoreService::Tabs& tabs = service->tabs();
+ const TabRestoreService::Entries& entries = service->entries();
ListValue list_value;
int added_count = 0;
- // We filter the list of recently closed to only show 'interesting' tabs,
- // where an interesting tab navigation is not the new tab ui.
- for (TabRestoreService::Tabs::const_iterator it = tabs.begin();
- it != tabs.end() && added_count < 3; ++it) {
- if (it->navigations.empty())
- continue;
-
- const TabNavigation& navigator =
- it->navigations.at(it->current_navigation_index);
- if (navigator.url == NewTabUIURL())
- continue;
-
- DictionaryValue* dictionary = new DictionaryValue;
- SetURLAndTitle(dictionary, navigator.title, navigator.url);
- dictionary->SetInteger(L"sessionId", it->id);
-
- list_value.Append(dictionary);
- added_count++;
+ // We filter the list of recently closed to only show 'interesting' entries,
+ // where an interesting entry is either a closed window or a closed tab
+ // whose selected navigation is not the new tab ui.
+ for (TabRestoreService::Entries::const_iterator it = entries.begin();
+ it != entries.end() && added_count < 3; ++it) {
+ TabRestoreService::Entry* entry = *it;
+ DictionaryValue* value = new DictionaryValue();
+ if ((entry->type == TabRestoreService::TAB &&
+ TabToValue(*static_cast<TabRestoreService::Tab*>(entry), value)) ||
+ (entry->type == TabRestoreService::WINDOW &&
+ WindowToValue(*static_cast<TabRestoreService::Window*>(entry),
+ value))) {
+ value->SetInteger(L"sessionId", entry->id);
+ list_value.Append(value);
+ added_count++;
+ } else {
+ delete value;
+ }
}
dom_ui_host_->CallJavascriptFunction(L"recentlyClosedTabs", list_value);
}
@@ -711,6 +701,48 @@ void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed(
tab_restore_service_ = NULL;
}
+bool RecentlyClosedTabsHandler::TabToValue(
+ const TabRestoreService::Tab& tab,
+ DictionaryValue* dictionary) {
+ if (tab.navigations.empty())
+ return false;
+
+ const TabNavigation& current_navigation =
+ tab.navigations.at(tab.current_navigation_index);
+ if (current_navigation.url == NewTabUIURL())
+ return false;
+
+ SetURLAndTitle(dictionary, current_navigation.title, current_navigation.url);
+ dictionary->SetString(L"type", L"tab");
+ return true;
+}
+
+bool RecentlyClosedTabsHandler::WindowToValue(
+ const TabRestoreService::Window& window,
+ DictionaryValue* dictionary) {
+ if (window.tabs.empty()) {
+ NOTREACHED();
+ return false;
+ }
+
+ ListValue* tab_values = new ListValue();
+ for (size_t i = 0; i < window.tabs.size(); ++i) {
+ DictionaryValue* tab_value = new DictionaryValue();
+ if (TabToValue(window.tabs[i], tab_value))
+ tab_values->Append(tab_value);
+ else
+ delete tab_value;
+ }
+ if (tab_values->GetSize() == 0) {
+ delete tab_values;
+ return false;
+ }
+
+ dictionary->SetString(L"type", L"window");
+ dictionary->Set(L"tabs", tab_values);
+ return true;
+}
+
///////////////////////////////////////////////////////////////////////////////
// HistoryHandler
diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h
index ed31cde..814e73a 100644
--- a/chrome/browser/dom_ui/new_tab_ui.h
+++ b/chrome/browser/dom_ui/new_tab_ui.h
@@ -263,6 +263,16 @@ class RecentlyClosedTabsHandler : public DOMMessageHandler,
virtual void TabRestoreServiceDestroyed(TabRestoreService* service);
private:
+ // Converts a closed tab to the value sent down to the NTP. Returns true on
+ // success, false if the value shouldn't be sent down.
+ bool TabToValue(const TabRestoreService::Tab& tab,
+ DictionaryValue* dictionary);
+
+ // Converts a closed window to the value sent down to the NTP. Returns true
+ // on success, false if the value shouldn't be sent down.
+ bool WindowToValue(const TabRestoreService::Window& window,
+ DictionaryValue* dictionary);
+
DOMUIHost* dom_ui_host_;
/// TabRestoreService that we are observing.
diff --git a/chrome/browser/tab_restore_service.cc b/chrome/browser/tab_restore_service.cc
index 4fcbe8c..bc8b9c7 100644
--- a/chrome/browser/tab_restore_service.cc
+++ b/chrome/browser/tab_restore_service.cc
@@ -4,40 +4,37 @@
#include "chrome/browser/tab_restore_service.h"
-#include "chrome/browser/profile.h"
+#include "chrome/browser/browser_list.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/stl_util-inl.h"
using base::Time;
-// HistoricalTab --------------------------------------------------------------
+// Entry ----------------------------------------------------------------------
-// ID of the next HistoricalTab.
-static int next_historical_tab_id = 1;
+// ID of the next Entry.
+static int next_entry_id = 1;
-TabRestoreService::HistoricalTab::HistoricalTab()
- : close_time(Time::Now()),
- from_last_session(false),
- current_navigation_index(-1),
- id(next_historical_tab_id++) {
-}
+TabRestoreService::Entry::Entry() : id(next_entry_id++), type(TAB) {}
-// TabRestoreService ----------------------------------------------------------
+TabRestoreService::Entry::Entry(Type type) : id(next_entry_id++), type(type) {}
-// Max number of tabs we'll keep around.
-static const size_t kMaxTabs = 10;
+// TabRestoreService ----------------------------------------------------------
-// Amount of time from when the session starts and when we'll allow loading of
-// the last sessions tabs.
-static const int kLoadFromLastSessionMS = 600000;
+// Max number of entries we'll keep around.
+static const size_t kMaxEntries = 10;
TabRestoreService::TabRestoreService(Profile* profile)
: profile_(profile),
- loaded_last_session_(false) {
+ loaded_last_session_(false),
+ restoring_(false) {
}
TabRestoreService::~TabRestoreService() {
FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
+ STLDeleteElements(&entries_);
}
void TabRestoreService::AddObserver(Observer* observer) {
@@ -48,107 +45,112 @@ void TabRestoreService::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
-void TabRestoreService::LoadPreviousSessionTabs() {
- if (!WillLoadPreviousSessionTabs() || IsLoadingPreviousSessionTabs())
+void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
+ if (restoring_)
return;
- profile_->GetSessionService()->GetLastSession(
- &cancelable_consumer_,
- NewCallback(this, &TabRestoreService::OnGotLastSession));
-}
+ Browser* browser = Browser::GetBrowserForController(tab, NULL);
+ if (closing_browsers_.find(browser) != closing_browsers_.end())
+ return;
-bool TabRestoreService::IsLoadingPreviousSessionTabs() {
- return cancelable_consumer_.HasPendingRequests();
-}
+ Tab* local_tab = new Tab();
+ PopulateTabFromController(tab, local_tab);
+ entries_.push_front(local_tab);
-bool TabRestoreService::WillLoadPreviousSessionTabs() {
- return (!loaded_last_session_ && tabs_.size() < kMaxTabs &&
- (Time::Now() - profile_->GetStartTime()).InMilliseconds() <
- kLoadFromLastSessionMS);
+ PruneAndNotify();
}
-void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
- tabs_.push_front(HistoricalTab());
-
- PopulateTabFromController(tab, &(tabs_.front()));
-
- while (tabs_.size() > kMaxTabs)
- tabs_.pop_back();
+void TabRestoreService::BrowserClosing(Browser* browser) {
+ if (browser->type() != Browser::TYPE_NORMAL ||
+ browser->tab_count() == 0)
+ return;
- NotifyTabsChanged();
+ closing_browsers_.insert(browser);
+
+ Window* window = new Window();
+ window->selected_tab_index = browser->selected_index();
+ window->tabs.resize(browser->tab_count());
+ size_t entry_index = 0;
+ for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
+ PopulateTabFromController(
+ browser->GetTabContentsAt(tab_index)->controller(),
+ &(window->tabs[entry_index]));
+ if (window->tabs[entry_index].navigations.empty())
+ window->tabs.erase(window->tabs.begin() + entry_index);
+ else
+ entry_index++;
+ }
+ if (window->tabs.empty()) {
+ delete window;
+ window = NULL;
+ } else {
+ entries_.push_front(window);
+ PruneAndNotify();
+ }
}
-void TabRestoreService::RemoveHistoricalTabById(int id) {
- for (Tabs::iterator i = tabs_.begin(); i != tabs_.end(); ++i) {
- if (i->id == id) {
- tabs_.erase(i);
- NotifyTabsChanged();
- return;
- }
- }
- // Don't hoark here, we allow an invalid id.
+void TabRestoreService::BrowserClosed(Browser* browser) {
+ closing_browsers_.erase(browser);
}
-void TabRestoreService::ClearHistoricalTabs() {
- tabs_.clear();
+void TabRestoreService::ClearEntries() {
+ STLDeleteElements(&entries_);
NotifyTabsChanged();
}
-void TabRestoreService::OnGotLastSession(SessionService::Handle handle,
- std::vector<SessionWindow*>* windows) {
- DCHECK(!loaded_last_session_);
- loaded_last_session_ = true;
-
- if (tabs_.size() == kMaxTabs)
+void TabRestoreService::RestoreMostRecentEntry(Browser* browser) {
+ if (entries_.empty())
return;
- AddHistoricalTabs(windows);
-
- NotifyTabsChanged();
+ RestoreEntryById(browser, entries_.front()->id, false);
}
-void TabRestoreService::AddHistoricalTabs(
- std::vector<SessionWindow*>* windows) {
- // First pass, extract the selected tabs in each window.
- for (size_t i = 0; i < windows->size(); ++i) {
- SessionWindow* window = (*windows)[i];
- if (window->type == Browser::TYPE_NORMAL) {
- DCHECK(window->selected_tab_index >= 0 &&
- window->selected_tab_index <
- static_cast<int>(window->tabs.size()));
- AppendHistoricalTabFromSessionTab(
- window->tabs[window->selected_tab_index]);
- if (tabs_.size() == kMaxTabs)
- return;
- }
+void TabRestoreService::RestoreEntryById(Browser* browser,
+ int id,
+ bool replace_existing_tab) {
+ Entries::iterator i = GetEntryIteratorById(id);
+ if (i == entries_.end()) {
+ // Don't hoark here, we allow an invalid id.
+ return;
}
- // Second pass, extract the non-selected tabs.
- for (size_t window_index = 0; window_index < windows->size();
- ++window_index) {
- SessionWindow* window = (*windows)[window_index];
- if (window->type != Browser::TYPE_NORMAL)
- continue; // Ignore popups.
-
- for (size_t tab_index = 0; tab_index < window->tabs.size(); ++tab_index) {
- if (tab_index == window->selected_tab_index)
- continue; // Pass one took care of this tab.
- AppendHistoricalTabFromSessionTab(window->tabs[tab_index]);
- if (tabs_.size() == kMaxTabs)
- return;
+ restoring_ = true;
+ Entry* entry = *i;
+ entries_.erase(i);
+ i = entries_.end();
+ if (entry->type == TAB) {
+ Tab* tab = static_cast<Tab*>(entry);
+ if (replace_existing_tab) {
+ browser->ReplaceRestoredTab(tab->navigations,
+ tab->current_navigation_index);
+ } else {
+ browser->AddRestoredTab(tab->navigations, browser->tab_count(),
+ tab->current_navigation_index, true);
+ }
+ } else if (entry->type == WINDOW) {
+ const Window* window = static_cast<Window*>(entry);
+ Browser* browser = Browser::Create(profile_);
+ for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
+ const Tab& tab = window->tabs[tab_i];
+ NavigationController* restored_controller =
+ browser->AddRestoredTab(tab.navigations, browser->tab_count(),
+ tab.current_navigation_index,
+ (tab_i == window->selected_tab_index));
+ if (restored_controller)
+ restored_controller->LoadIfNecessary();
}
+ browser->window()->Show();
+ } else {
+ NOTREACHED();
}
-}
-
-void TabRestoreService::AppendHistoricalTabFromSessionTab(
- SessionTab* tab) {
- tabs_.push_back(HistoricalTab());
- PopulateTabFromSessionTab(tab, &(tabs_.back()));
+ delete entry;
+ restoring_ = false;
+ NotifyTabsChanged();
}
void TabRestoreService::PopulateTabFromController(
NavigationController* controller,
- HistoricalTab* tab) {
+ Tab* tab) {
const int pending_index = controller->GetPendingEntryIndex();
int entry_count = controller->GetEntryCount();
if (entry_count == 0 && pending_index == 0)
@@ -171,14 +173,24 @@ void TabRestoreService::PopulateTabFromController(
tab->current_navigation_index = 0;
}
-void TabRestoreService::PopulateTabFromSessionTab(
- SessionTab* session_tab,
- HistoricalTab* tab) {
- tab->navigations.swap(session_tab->navigations);
- tab->from_last_session = true;
- tab->current_navigation_index = session_tab->current_navigation_index;
-}
-
void TabRestoreService::NotifyTabsChanged() {
FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceChanged(this));
}
+
+void TabRestoreService::PruneAndNotify() {
+ while (entries_.size() > kMaxEntries) {
+ delete entries_.back();
+ entries_.pop_back();
+ }
+
+ NotifyTabsChanged();
+}
+
+TabRestoreService::Entries::iterator TabRestoreService::GetEntryIteratorById(
+ int id) {
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
+ if ((*i)->id == id)
+ return i;
+ }
+ return entries_.end();
+}
diff --git a/chrome/browser/tab_restore_service.h b/chrome/browser/tab_restore_service.h
index 66157f4..5d1460e 100644
--- a/chrome/browser/tab_restore_service.h
+++ b/chrome/browser/tab_restore_service.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_TAB_RESTORE_SERVICE_H__
-#define CHROME_BROWSER_TAB_RESTORE_SERVICE_H__
+#ifndef CHROME_BROWSER_TAB_RESTORE_SERVICE_H_
+#define CHROME_BROWSER_TAB_RESTORE_SERVICE_H_
#include <list>
@@ -15,27 +15,23 @@ class NavigationController;
class Profile;
// TabRestoreService is responsible for maintaining the most recently closed
-// tabs. When the user closes a tab TabRestoreService::CreateHistoricalTab is
-// invoked and a HistoricalTab is created to represent the tab.
+// 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.
//
-// TabRestoreService can recreate tabs from the previous session as well.
-// LoadPreviousSessionTabs loads (asynchronously) the tabs from the previous
-// session. When done, the observer is notified.
+// To restore a tab/window from the TabRestoreService invoke RestoreEntryById
+// or RestoreMostRecentEntry.
//
-// To restore a tab from the TabRestoreService invoke AddRestoredTab on the
-// Browser you want to restore the tab to, followed by RemoveHistoricalTabById
-// to remove the tab from the restore service.
-//
-// To listen for changes to the set of tabs managed by the TabRestoreService
+// To listen for changes to the set of entries managed by the TabRestoreService
// add an observer.
-
class TabRestoreService {
public:
- // Observer is notified when the set of tabs managed by TabRestoreService
+ // Observer is notified when the set of entries managed by TabRestoreService
// changes in some way.
class Observer {
public:
- // Sent when the internal tab state changed
+ // Sent when the set of entries changes in some way.
virtual void TabRestoreServiceChanged(TabRestoreService* service) = 0;
// Sent to all remaining Observers when TabRestoreService's
@@ -43,15 +39,28 @@ class TabRestoreService {
virtual void TabRestoreServiceDestroyed(TabRestoreService* service) = 0;
};
- // Represents a previously open tab.
- struct HistoricalTab {
- HistoricalTab();
+ // 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.
+ int id;
- // Time the tab was closed.
- base::Time close_time;
+ // The type of the entry.
+ Type type;
+ };
- // If true, this is a historical session and not a closed tab.
- bool from_last_session;
+ // Represents a previously open tab.
+ struct Tab : public Entry {
+ Tab() : Entry(TAB), current_navigation_index(-1) {}
// The navigations.
// WARNING: navigations may be empty.
@@ -59,16 +68,22 @@ class TabRestoreService {
// Index of the selected navigation in navigations.
int current_navigation_index;
+ };
- // Unique id for the closed tab. This is guaranteed to be unique for
- // a session.
- const int id;
+ // Represents a previously open window.
+ struct Window : public Entry {
+ Window() : Entry(WINDOW), selected_tab_index(-1) {}
+
+ // The tabs that comprised the window, in order.
+ std::vector<Tab> tabs;
+
+ // Index of the selected tab.
+ int selected_tab_index;
};
- typedef std::list<HistoricalTab> Tabs;
+ typedef std::list<Entry*> Entries;
- // Creates a new TabRestoreService. This does not load tabs from the last
- // session, you must explicitly invoke LoadPreviousSessionTabs to do that.
+ // Creates a new TabRestoreService.
explicit TabRestoreService(Profile* profile);
~TabRestoreService();
@@ -77,70 +92,70 @@ class TabRestoreService {
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
- // If the previous session has not been loaded, it is loaded and the tabs
- // from it are placed at the end of the queue.
- void LoadPreviousSessionTabs();
-
- // Returns true if loading the previous sessions tabs.
- bool IsLoadingPreviousSessionTabs();
-
- // Returns true if LoadPreviousSessionTabs will attempt to load any tabs.
- bool WillLoadPreviousSessionTabs();
-
- // Creates a HistoricalTab to represent the tab and notifies observers the
- // list of tabs has changed.
+ // Creates a Tab to represent |tab| and notifies observers the list of
+ // entries has changed.
void CreateHistoricalTab(NavigationController* tab);
- // Removes the HistoricalTab with the specified id and notifies observers.
- // Does nothing if id does not identify a valid historical tab id.
- void RemoveHistoricalTabById(int id);
+ // Invoked when a browser is closing. If |browser| is a tabbed browser with
+ // at least one tab, a Window is created, added to entries and observers are
+ // notified.
+ void BrowserClosing(Browser* browser);
- // Removes all HistoricalTabs from the list and notifies observers the list
- // of tabs has changed.
- void ClearHistoricalTabs();
+ // Invoked when the browser is done closing.
+ void BrowserClosed(Browser* browser);
- // Returns the tabs, ordered with most recently closed tabs at the front.
- const Tabs& tabs() const { return tabs_; }
+ // Removes all entries from the list and notifies observers the list
+ // of tabs has changed.
+ void ClearEntries();
- private:
- // Callback from loading the last session. As necessary adds the tabs to
- // tabs_.
- void OnGotLastSession(SessionService::Handle handle,
- std::vector<SessionWindow*>* windows);
+ // Returns the entries, ordered with most recently closed entries at the
+ // front.
+ const Entries& entries() const { return entries_; }
- // Invoked from OnGotLastSession to add the necessary tabs from windows
- // to tabs_.
- void AddHistoricalTabs(std::vector<SessionWindow*>* windows);
+ // 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 |browser|.
+ void RestoreMostRecentEntry(Browser* browser);
- // Creates a HistoricalTab from the tab.
- void AppendHistoricalTabFromSessionTab(SessionTab* tab);
+ // 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 |browser|.
+ void RestoreEntryById(Browser* browser, int id, bool replace_existing_tab);
+ private:
// Populates tabs->navigations from the NavigationController.
void PopulateTabFromController(NavigationController* controller,
- HistoricalTab* tab);
-
- // Populates tab->navigations from a previous sessions navigations.
- void PopulateTabFromSessionTab(SessionTab* session_tab,
- HistoricalTab* tab);
+ Tab* tab);
// Notifies observers the tabs have changed.
void NotifyTabsChanged();
+ // Prunes entries_ to contain only kMaxEntries and invokes NotifyTabsChanged.
+ void PruneAndNotify();
+
+ // Returns an iterator into entries_ whose id matches |id|.
+ Entries::iterator GetEntryIteratorById(int id);
+
Profile* profile_;
// Whether we've loaded the last session.
bool loaded_last_session_;
- // Set of tabs. We own the NavigationControllers in this list.
- Tabs tabs_;
+ // Set of entries.
+ Entries entries_;
- // Used in requesting the last session.
- CancelableRequestConsumer cancelable_consumer_;
+ // Are we restoring a tab? If this is true we ignore requests to create a
+ // historical tab.
+ bool restoring_;
ObserverList<Observer> observer_list_;
- DISALLOW_EVIL_CONSTRUCTORS(TabRestoreService);
-};
+ // Set of tabs that we've received a BrowserClosing method for but no
+ // corresponding BrowserClosed. We cache the set of browsers closing to
+ // avoid creating historical tabs for them.
+ std::set<Browser*> closing_browsers_;
-#endif // CHROME_BROWSER_TAB_RESTORE_SERVICE_H__
+ DISALLOW_COPY_AND_ASSIGN(TabRestoreService);
+};
+#endif // CHROME_BROWSER_TAB_RESTORE_SERVICE_H_
diff --git a/chrome/browser/tab_restore_uitest.cc b/chrome/browser/tab_restore_uitest.cc
index 7a55257..d108d0f 100644
--- a/chrome/browser/tab_restore_uitest.cc
+++ b/chrome/browser/tab_restore_uitest.cc
@@ -170,7 +170,6 @@ TEST_F(TabRestoreUITest, RestoreWithExistingSiteInstance) {
ASSERT_EQ(http_url1, GetActiveTabURL());
}
-
// Tests that the SiteInstances used for entries in a restored tab's history
// are given appropriate max page IDs, even if the renderer for the entry
// already exists. (Bug 1204135)
@@ -222,3 +221,56 @@ TEST_F(TabRestoreUITest, RestoreCrossSiteWithExistingSiteInstance) {
ASSERT_EQ(http_url2, GetActiveTabURL());
}
+TEST_F(TabRestoreUITest, RestoreWindow) {
+ // Create a new window.
+ int window_count;
+ ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
+ ASSERT_TRUE(automation()->OpenNewBrowserWindow(SW_HIDE));
+ ASSERT_TRUE(automation()->WaitForWindowCountToBecome(++window_count,
+ kWaitForActionMaxMsec));
+
+ // Create two more tabs, one with url1, the other url2.
+ scoped_ptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ int initial_tab_count;
+ ASSERT_TRUE(browser_proxy->GetTabCount(&initial_tab_count));
+ browser_proxy->AppendTab(url1_);
+ ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 1,
+ kWaitForActionMaxMsec));
+ scoped_ptr<TabProxy> new_tab(browser_proxy->GetTab(initial_tab_count));
+ new_tab->NavigateToURL(url1_);
+ browser_proxy->AppendTab(url2_);
+ ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
+ kWaitForActionMaxMsec));
+ new_tab.reset(browser_proxy->GetTab(initial_tab_count + 1));
+ new_tab->NavigateToURL(url2_);
+
+ // Close the window.
+ ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_CLOSEWINDOW));
+ browser_proxy.reset();
+ new_tab.reset();
+ ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count - 1,
+ kWaitForActionMaxMsec));
+
+ // Restore the window.
+ browser_proxy.reset(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy->ApplyAccelerator(IDC_RESTORE_TAB));
+ ASSERT_TRUE(automation()->WaitForWindowCountToBecome(window_count,
+ kWaitForActionMaxMsec));
+
+ browser_proxy.reset(automation()->GetBrowserWindow(1));
+ ASSERT_TRUE(browser_proxy->WaitForTabCountToBecome(initial_tab_count + 2,
+ kWaitForActionMaxMsec));
+
+ scoped_ptr<TabProxy> restored_tab_proxy(
+ browser_proxy->GetTab(initial_tab_count));
+ ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
+ GURL url;
+ ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
+ ASSERT_TRUE(url == url1_);
+
+ restored_tab_proxy.reset(
+ browser_proxy->GetTab(initial_tab_count + 1));
+ ASSERT_TRUE(restored_tab_proxy->WaitForTabToBeRestored(kWaitForActionMsec));
+ ASSERT_TRUE(restored_tab_proxy->GetCurrentURL(&url));
+ ASSERT_TRUE(url == url2_);
+}
diff --git a/chrome/test/automation/browser_proxy.cc b/chrome/test/automation/browser_proxy.cc
index ce47335..faead8f 100644
--- a/chrome/test/automation/browser_proxy.cc
+++ b/chrome/test/automation/browser_proxy.cc
@@ -286,6 +286,24 @@ bool BrowserProxy::WaitForTabCountToChange(int count, int* new_count,
return false;
}
+bool BrowserProxy::WaitForTabCountToBecome(int count, int wait_timeout) {
+ const TimeTicks start = TimeTicks::Now();
+ const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout);
+ while (TimeTicks::Now() - start < timeout) {
+ Sleep(automation::kSleepTime);
+ bool is_timeout;
+ int new_count;
+ bool succeeded = GetTabCountWithTimeout(&new_count, wait_timeout,
+ &is_timeout);
+ if (!succeeded)
+ return false;
+ if (count == new_count)
+ return true;
+ }
+ // If we get here, the tab count doesn't match.
+ return false;
+}
+
bool BrowserProxy::WaitForTabToBecomeActive(int tab,
int wait_timeout) {
const TimeTicks start = TimeTicks::Now();
diff --git a/chrome/test/automation/browser_proxy.h b/chrome/test/automation/browser_proxy.h
index b2c92ca..3cc681e 100644
--- a/chrome/test/automation/browser_proxy.h
+++ b/chrome/test/automation/browser_proxy.h
@@ -122,6 +122,9 @@ class BrowserProxy : public AutomationResourceProxy {
// Returns false if the tab count does not change.
bool WaitForTabCountToChange(int count, int* new_count, int wait_timeout);
+ // Block the thread until the tab count is |count|.
+ bool WaitForTabCountToBecome(int count, int wait_timeout);
+
// Block the thread until the specified tab is the active tab.
// |wait_timeout| is the timeout, in milliseconds, for waiting.
// Returns false if the tab does not become active.