summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sessions
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/sessions')
-rw-r--r--chrome/browser/sessions/session_restore.cc1
-rw-r--r--chrome/browser/sessions/tab_restore_service.cc148
-rw-r--r--chrome/browser/sessions/tab_restore_service.h67
-rw-r--r--chrome/browser/sessions/tab_restore_service_unittest.cc144
4 files changed, 330 insertions, 30 deletions
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index e9e0b1d1..8ffbac9 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -424,6 +424,7 @@ static void Restore(Profile* profile,
NOTREACHED();
return;
}
+ profile->set_restored_last_session(true);
// SessionRestoreImpl takes care of deleting itself when done.
SessionRestoreImpl* restorer =
new SessionRestoreImpl(profile, browser, synchronous,
diff --git a/chrome/browser/sessions/tab_restore_service.cc b/chrome/browser/sessions/tab_restore_service.cc
index f12e6d9..13da68f 100644
--- a/chrome/browser/sessions/tab_restore_service.cc
+++ b/chrome/browser/sessions/tab_restore_service.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/browser/sessions/session_service.h"
#include "chrome/common/scoped_vector.h"
#include "chrome/common/stl_util-inl.h"
@@ -28,8 +29,8 @@ TabRestoreService::Entry::Entry(Type type) : id(next_entry_id++), type(type) {}
// TabRestoreService ----------------------------------------------------------
-// Max number of entries we'll keep around.
-static const size_t kMaxEntries = 10;
+// static
+const size_t TabRestoreService::kMaxEntries = 10;
// Identifier for commands written to file.
// The ordering in the file is as follows:
@@ -64,7 +65,7 @@ struct WindowPayload {
int32 num_tabs;
};
-// Paylowed used for the start of a tab close.
+// Payload used for the start of a tab close.
struct SelectedNavigationInTabPayload {
SessionID::id_type id;
int32 index;
@@ -92,7 +93,7 @@ void RemoveEntryByID(SessionID::id_type id,
TabRestoreService::TabRestoreService(Profile* profile)
: BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
std::wstring()),
- loaded_last_session_(false),
+ load_state_(NOT_LOADED),
restoring_(false),
reached_max_(false),
entries_to_write_(0),
@@ -105,6 +106,7 @@ TabRestoreService::~TabRestoreService() {
FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
STLDeleteElements(&entries_);
+ STLDeleteElements(&staging_entries_);
}
void TabRestoreService::AddObserver(Observer* observer) {
@@ -242,15 +244,30 @@ void TabRestoreService::RestoreEntryById(Browser* browser,
}
void TabRestoreService::LoadTabsFromLastSession() {
- if (loaded_last_session_ || reached_max_)
+ if (load_state_ != NOT_LOADED || reached_max_)
return;
- loaded_last_session_ = true;
+ load_state_ = LOADING;
+ if (!profile()->restored_last_session() &&
+ !profile()->DidLastSessionExitCleanly() &&
+ profile()->GetSessionService()) {
+ // The previous session crashed and wasn't restored. Load the tabs/windows
+ // that were open at the point of crash from the session service.
+ profile()->GetSessionService()->GetLastSession(
+ &load_consumer_,
+ NewCallback(this, &TabRestoreService::OnGotPreviousSession));
+ } else {
+ load_state_ |= LOADED_LAST_SESSION;
+ }
+
+ // Request the tabs closed in the last session. If the last session crashed,
+ // this won't contain the tabs/window that were open at the point of the
+ // crash (the call to GetLastSession above requests those).
ScheduleGetLastSessionCommands(
new InternalGetCommandsRequest(
NewCallback(this, &TabRestoreService::OnGotLastSessionCommands)),
- &load_tabs_consumer_);
+ &load_consumer_);
}
void TabRestoreService::Save() {
@@ -466,6 +483,18 @@ int TabRestoreService::GetSelectedNavigationIndexToPersist(const Tab& tab) {
void TabRestoreService::OnGotLastSessionCommands(
Handle handle,
scoped_refptr<InternalGetCommandsRequest> request) {
+ std::vector<Entry*> entries;
+ CreateEntriesFromCommands(request, &entries);
+ // Closed tabs always go to the end.
+ staging_entries_.insert(staging_entries_.end(), entries.begin(),
+ entries.end());
+ load_state_ |= LOADED_LAST_TABS;
+ LoadStateChanged();
+}
+
+void TabRestoreService::CreateEntriesFromCommands(
+ scoped_refptr<InternalGetCommandsRequest> request,
+ std::vector<Entry*>* loaded_entries) {
if (request->canceled() || entries_.size() == kMaxEntries)
return;
@@ -575,23 +604,7 @@ void TabRestoreService::OnGotLastSessionCommands(
// entries with no navigations.
ValidateAndDeleteEmptyEntries(&(entries.get()));
- if (entries->empty())
- return;
-
- // And add them.
- for (size_t i = 0; i < entries->size(); ++i)
- AddEntry(entries[i], false, false);
-
- // AddEntry takes ownership of the entry, need to clear out entries so that
- // it doesn't delete them.
- entries->clear();
-
- // Make it so we rewrite all the tabs. We need to do this otherwise we won't
- // correctly write out the entries when Save is invoked (Save starts from
- // the front, not the end and we just added the entries to the end).
- entries_to_write_ = entries_.size();
-
- PruneAndNotify();
+ loaded_entries->swap(entries.get());
}
bool TabRestoreService::ValidateTab(Tab* tab) {
@@ -647,3 +660,90 @@ void TabRestoreService::ValidateAndDeleteEmptyEntries(
// Delete the remaining entries.
STLDeleteElements(&invalid_entries);
}
+
+void TabRestoreService::OnGotPreviousSession(
+ Handle handle,
+ std::vector<SessionWindow*>* windows) {
+ std::vector<Entry*> entries;
+ CreateEntriesFromWindows(windows, &entries);
+ // Previous session tabs go first.
+ staging_entries_.insert(staging_entries_.begin(), entries.begin(),
+ entries.end());
+ load_state_ |= LOADED_LAST_SESSION;
+ LoadStateChanged();
+}
+
+void TabRestoreService::CreateEntriesFromWindows(
+ std::vector<SessionWindow*>* windows,
+ std::vector<Entry*>* entries) {
+ for (size_t i = 0; i < windows->size(); ++i) {
+ scoped_ptr<Window> window(new Window());
+ if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
+ entries->push_back(window.release());
+ }
+}
+
+bool TabRestoreService::ConvertSessionWindowToWindow(
+ SessionWindow* session_window,
+ Window* window) {
+ for (size_t i = 0; i < session_window->tabs.size(); ++i) {
+ if (!session_window->tabs[i]->navigations.empty()) {
+ window->tabs.resize(window->tabs.size() + 1);
+ Tab& tab = window->tabs.back();
+ tab.navigations.swap(session_window->tabs[i]->navigations);
+ tab.current_navigation_index =
+ session_window->tabs[i]->current_navigation_index;
+ }
+ }
+ if (window->tabs.empty())
+ return false;
+
+ window->selected_tab_index =
+ std::min(session_window->selected_tab_index,
+ static_cast<int>(window->tabs.size() - 1));
+ return true;
+}
+
+void TabRestoreService::LoadStateChanged() {
+ if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
+ (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
+ // Still waiting on previous session or previous tabs.
+ return;
+ }
+
+ // We're done loading.
+ load_state_ ^= LOADING;
+
+ if (staging_entries_.empty() || reached_max_) {
+ STLDeleteElements(&staging_entries_);
+ return;
+ }
+
+ if (staging_entries_.size() + entries_.size() > kMaxEntries) {
+ // If we add all the staged entries we'll end up with more than
+ // kMaxEntries. Delete entries such that we only end up with
+ // at most kMaxEntries.
+ DCHECK(entries_.size() < kMaxEntries);
+ STLDeleteContainerPointers(
+ staging_entries_.begin() + (kMaxEntries - entries_.size()),
+ staging_entries_.end());
+ staging_entries_.erase(
+ staging_entries_.begin() + (kMaxEntries - entries_.size()),
+ staging_entries_.end());
+ }
+
+ // And add them.
+ for (size_t i = 0; i < staging_entries_.size(); ++i)
+ AddEntry(staging_entries_[i], false, false);
+
+ // AddEntry takes ownership of the entry, need to clear out entries so that
+ // it doesn't delete them.
+ staging_entries_.clear();
+
+ // Make it so we rewrite all the tabs. We need to do this otherwise we won't
+ // correctly write out the entries when Save is invoked (Save starts from
+ // the front, not the end and we just added the entries to the end).
+ entries_to_write_ = staging_entries_.size();
+
+ PruneAndNotify();
+}
diff --git a/chrome/browser/sessions/tab_restore_service.h b/chrome/browser/sessions/tab_restore_service.h
index adf5df5..2962cf5 100644
--- a/chrome/browser/sessions/tab_restore_service.h
+++ b/chrome/browser/sessions/tab_restore_service.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
#include <list>
+#include <vector>
#include "base/observer_list.h"
#include "base/time.h"
@@ -126,14 +127,35 @@ class TabRestoreService : public BaseSessionService {
SessionID::id_type id,
bool replace_existing_tab);
- // Loads the tabs from the previous session. This does nothing if the tabs
+ // 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;
+
protected:
virtual void Save();
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 tabs->navigations from the NavigationController.
void PopulateTabFromController(NavigationController* controller,
Tab* tab);
@@ -179,12 +201,18 @@ class TabRestoreService : public BaseSessionService {
// no valid navigation to persist.
int GetSelectedNavigationIndexToPersist(const Tab& tab);
- // Invoked when we've loaded the session commands from the previous run.
- // This creates entries and adds them to entries_, notifying the observer.
+ // 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);
+
// 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.
@@ -195,11 +223,32 @@ class TabRestoreService : public BaseSessionService {
// hold.
void ValidateAndDeleteEmptyEntries(std::vector<Entry*>* entries);
+ // 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);
+
+ // Creates and add entries to |entries| for each of the windows in |windows|.
+ void CreateEntriesFromWindows(
+ std::vector<SessionWindow*>* windows,
+ std::vector<Entry*>* entries);
+
+ // Converts a SessionWindow into a Window, returning true on success.
+ 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();
+
// Set of entries.
Entries entries_;
// Whether we've loaded the last session.
- bool loaded_last_session_;
+ int load_state_;
// Are we restoring a tab? If this is true we ignore requests to create a
// historical tab.
@@ -221,8 +270,14 @@ class TabRestoreService : public BaseSessionService {
// avoid creating historical tabs for them.
std::set<Browser*> closing_browsers_;
- // Used when loading commands from the previous session.
- CancelableRequestConsumer load_tabs_consumer_;
+ // 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_;
DISALLOW_COPY_AND_ASSIGN(TabRestoreService);
};
diff --git a/chrome/browser/sessions/tab_restore_service_unittest.cc b/chrome/browser/sessions/tab_restore_service_unittest.cc
index 99e65c3..2f7d4ba 100644
--- a/chrome/browser/sessions/tab_restore_service_unittest.cc
+++ b/chrome/browser/sessions/tab_restore_service_unittest.cc
@@ -53,6 +53,34 @@ class TabRestoreServiceTest : public testing::Test {
service_->LoadTabsFromLastSession();
}
+ // Adds a window with one tab and url to the profile's session service.
+ void AddWindowWithOneTabToSessionService() {
+ SessionService* session_service = profile_->GetSessionService();
+ SessionID tab_id;
+ SessionID window_id;
+ session_service->SetWindowType(window_id, Browser::TYPE_NORMAL);
+ session_service->SetTabWindow(window_id, tab_id);
+ session_service->SetTabIndexInWindow(window_id, tab_id, 0);
+ session_service->SetSelectedTabInWindow(window_id, 0);
+ NavigationEntry entry(tab_contents_factory_->type());
+ entry.set_url(url1_);
+ session_service->UpdateTabNavigation(window_id, tab_id, 0, entry);
+ }
+
+ // Creates a SessionService and assigns it to the Profile. The SessionService
+ // is configured with a single window with a single tab pointing at url1_ by
+ // way of AddWindowWithOneTabToSessionService.
+ void CreateSessionServiceWithOneWindow() {
+ // The profile takes ownership of this.
+ SessionService* session_service = new SessionService(profile_.get());
+ profile_->set_session_service(session_service);
+
+ AddWindowWithOneTabToSessionService();
+
+ // Set this, otherwise previous session won't be loaded.
+ profile_->set_last_session_exited_cleanly(false);
+ }
+
GURL url1_;
GURL url2_;
GURL url3_;
@@ -244,3 +272,119 @@ TEST_F(TabRestoreServiceTest, DontLoadTwice) {
// There should only be one entry.
ASSERT_EQ(1, service_->entries().size());
}
+
+// Makes sure we load the previous session as necessary.
+TEST_F(TabRestoreServiceTest, LoadPreviousSession) {
+ CreateSessionServiceWithOneWindow();
+
+ profile_->GetSessionService()->MoveCurrentSessionToLastSession();
+
+ service_->LoadTabsFromLastSession();
+
+ // Make sure we get back one entry with one tab whose url is url1.
+ ASSERT_EQ(1, service_->entries().size());
+ TabRestoreService::Entry* entry2 = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::WINDOW, entry2->type);
+ TabRestoreService::Window* window =
+ static_cast<TabRestoreService::Window*>(entry2);
+ ASSERT_EQ(1, window->tabs.size());
+ EXPECT_EQ(0, window->selected_tab_index);
+ ASSERT_EQ(1, window->tabs[0].navigations.size());
+ EXPECT_EQ(0, window->tabs[0].current_navigation_index);
+ EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].url());
+}
+
+// Makes sure we don't attempt to load previous sessions after a restore.
+TEST_F(TabRestoreServiceTest, DontLoadAfterRestore) {
+ CreateSessionServiceWithOneWindow();
+
+ profile_->GetSessionService()->MoveCurrentSessionToLastSession();
+
+ profile_->set_restored_last_session(true);
+
+ service_->LoadTabsFromLastSession();
+
+ // Because we restored a session TabRestoreService shouldn't load the tabs.
+ ASSERT_EQ(0, service_->entries().size());
+}
+
+// Makes sure we don't attempt to load previous sessions after a clean exit.
+TEST_F(TabRestoreServiceTest, DontLoadAfterCleanExit) {
+ CreateSessionServiceWithOneWindow();
+
+ profile_->GetSessionService()->MoveCurrentSessionToLastSession();
+
+ profile_->set_last_session_exited_cleanly(true);
+
+ service_->LoadTabsFromLastSession();
+
+ ASSERT_EQ(0, service_->entries().size());
+}
+
+TEST_F(TabRestoreServiceTest, LoadPreviousSessionAndTabs) {
+ CreateSessionServiceWithOneWindow();
+
+ profile_->GetSessionService()->MoveCurrentSessionToLastSession();
+
+ AddThreeNavigations();
+
+ service_->CreateHistoricalTab(controller_);
+
+ RecreateService();
+
+ // We should get back two entries, one from the previous session and one from
+ // the tab restore service. The previous session entry should be first.
+ ASSERT_EQ(2, service_->entries().size());
+ // The first entry should come from the session service.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
+ TabRestoreService::Window* window =
+ static_cast<TabRestoreService::Window*>(entry);
+ ASSERT_EQ(1, window->tabs.size());
+ EXPECT_EQ(0, window->selected_tab_index);
+ ASSERT_EQ(1, window->tabs[0].navigations.size());
+ EXPECT_EQ(0, window->tabs[0].current_navigation_index);
+ EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].url());
+
+ // Then the closed tab.
+ entry = *(++service_->entries().begin());
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(3, tab->navigations.size());
+ EXPECT_EQ(2, tab->current_navigation_index);
+ EXPECT_TRUE(url1_ == tab->navigations[0].url());
+ EXPECT_TRUE(url2_ == tab->navigations[1].url());
+ EXPECT_TRUE(url3_ == tab->navigations[2].url());
+}
+
+// Creates TabRestoreService::kMaxEntries + 1 windows in the session service
+// and makes sure we only get back TabRestoreService::kMaxEntries on restore.
+TEST_F(TabRestoreServiceTest, ManyWindowsInSessionService) {
+ CreateSessionServiceWithOneWindow();
+
+ for (size_t i = 0; i < TabRestoreService::kMaxEntries; ++i)
+ AddWindowWithOneTabToSessionService();
+
+ profile_->GetSessionService()->MoveCurrentSessionToLastSession();
+
+ AddThreeNavigations();
+
+ service_->CreateHistoricalTab(controller_);
+
+ RecreateService();
+
+ // We should get back kMaxEntries entries. We added more, but
+ // TabRestoreService only allows up to kMaxEntries.
+ ASSERT_EQ(TabRestoreService::kMaxEntries, service_->entries().size());
+
+ // The first entry should come from the session service.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
+ TabRestoreService::Window* window =
+ static_cast<TabRestoreService::Window*>(entry);
+ ASSERT_EQ(1, window->tabs.size());
+ EXPECT_EQ(0, window->selected_tab_index);
+ ASSERT_EQ(1, window->tabs[0].navigations.size());
+ EXPECT_EQ(0, window->tabs[0].current_navigation_index);
+ EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].url());
+}