summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-06 19:30:19 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-12-06 19:30:19 +0000
commit169627b81ce036a7014476c366b060e050b5ff70 (patch)
tree6231d8cfaa065513b4742d99204b25da4178037a /chrome
parentee824a436efbdeed4ca78efc4dd2aa4976ba43a9 (diff)
downloadchromium_src-169627b81ce036a7014476c366b060e050b5ff70.zip
chromium_src-169627b81ce036a7014476c366b060e050b5ff70.tar.gz
chromium_src-169627b81ce036a7014476c366b060e050b5ff70.tar.bz2
Makes the tab restore service persist closed tabs/windows to disk and
reload them when asked. Sorry for largish looking change. It's made big by refactoring common code between TabRestoreService and SessionService into a common superclass. At the same time I removed some dead code and shuffled the session related classes into a single directory for easier perusal. BUG=384 TEST=close the browser, start the browser and make sure the new tab page shows closed windows/tabs from the previous session. Review URL: http://codereview.chromium.org/13152 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6490 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/browser.cc6
-rw-r--r--chrome/browser/browser.h6
-rw-r--r--chrome/browser/browser.scons12
-rw-r--r--chrome/browser/browser.vcproj58
-rw-r--r--chrome/browser/browser_init.cc7
-rw-r--r--chrome/browser/browsing_data_remover.cc10
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.cc13
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.h2
-rw-r--r--chrome/browser/navigation_controller.cc29
-rw-r--r--chrome/browser/navigation_controller.h6
-rw-r--r--chrome/browser/navigation_controller_unittest.cc11
-rw-r--r--chrome/browser/profile.cc9
-rw-r--r--chrome/browser/profile.h2
-rw-r--r--chrome/browser/session_backend.h181
-rw-r--r--chrome/browser/sessions/base_session_service.cc237
-rw-r--r--chrome/browser/sessions/base_session_service.h168
-rw-r--r--chrome/browser/sessions/session_backend.cc (renamed from chrome/browser/session_backend.cc)131
-rw-r--r--chrome/browser/sessions/session_backend.h122
-rw-r--r--chrome/browser/sessions/session_backend_unittest.cc (renamed from chrome/browser/session_backend_unittest.cc)69
-rw-r--r--chrome/browser/sessions/session_command.cc32
-rw-r--r--chrome/browser/sessions/session_command.h67
-rw-r--r--chrome/browser/sessions/session_id.cc11
-rw-r--r--chrome/browser/sessions/session_id.h (renamed from chrome/browser/session_id.h)18
-rw-r--r--chrome/browser/sessions/session_restore.cc (renamed from chrome/browser/session_restore.cc)32
-rw-r--r--chrome/browser/sessions/session_restore.h (renamed from chrome/browser/session_restore.h)11
-rw-r--r--chrome/browser/sessions/session_restore_uitest.cc (renamed from chrome/browser/session_restore_uitest.cc)0
-rw-r--r--chrome/browser/sessions/session_service.cc (renamed from chrome/browser/session_service.cc)348
-rw-r--r--chrome/browser/sessions/session_service.h (renamed from chrome/browser/session_service.h)296
-rw-r--r--chrome/browser/sessions/session_service_test_helper.cc (renamed from chrome/browser/session_service_test_helper.cc)24
-rw-r--r--chrome/browser/sessions/session_service_test_helper.h (renamed from chrome/browser/session_service_test_helper.h)11
-rw-r--r--chrome/browser/sessions/session_service_unittest.cc (renamed from chrome/browser/session_service_unittest.cc)35
-rw-r--r--chrome/browser/sessions/session_types.cc41
-rw-r--r--chrome/browser/sessions/session_types.h183
-rw-r--r--chrome/browser/sessions/tab_restore_service.cc649
-rw-r--r--chrome/browser/sessions/tab_restore_service.h (renamed from chrome/browser/tab_restore_service.h)97
-rw-r--r--chrome/browser/sessions/tab_restore_service_unittest.cc246
-rw-r--r--chrome/browser/tab_restore_service.cc196
-rw-r--r--chrome/browser/tabs/tab_strip_model.cc2
-rw-r--r--chrome/test/ui/ui_tests.scons2
-rw-r--r--chrome/test/ui/ui_tests.vcproj2
-rw-r--r--chrome/test/unit/unit_tests.scons7
-rw-r--r--chrome/test/unit/unittests.vcproj16
42 files changed, 2192 insertions, 1213 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index ad297be..4d96433 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -33,6 +33,7 @@
#include "chrome/browser/plugin_process_host.h"
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/ssl_error_info.h"
#include "chrome/browser/site_instance.h"
#include "chrome/browser/task_manager.h"
@@ -2234,11 +2235,6 @@ NavigationController* Browser::BuildRestoredNavigationController(
if (!navigations.empty()) {
DCHECK(selected_navigation >= 0 &&
selected_navigation < static_cast<int>(navigations.size()));
- // We should have a valid URL, if we don't fall back to the default.
- GURL url = navigations[selected_navigation].url;
- if (url.is_empty())
- url = GetHomePage();
-
// Create a NavigationController. This constructor creates the appropriate
// set of TabContents.
return new NavigationController(profile_, navigations, selected_navigation);
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 9e8de5e..a704199 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -11,7 +11,7 @@
#include "chrome/browser/controller.h"
#include "chrome/browser/shell_dialogs.h"
#include "chrome/browser/browser_window.h"
-#include "chrome/browser/session_id.h"
+#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_contents_delegate.h"
#include "chrome/browser/tabs/tab_strip_model.h"
@@ -27,7 +27,7 @@ class LocationBarView;
class PrefService;
class Profile;
class StatusBubble;
-struct TabNavigation;
+class TabNavigation;
class WebApp;
class Browser : public TabStripModelDelegate,
@@ -539,7 +539,7 @@ class Browser : public TabStripModelDelegate,
// Unique identifier of this browser for session restore. This id is only
// unique within the current session, and is not guaranteed to be unique
// across sessions.
- SessionID session_id_;
+ const SessionID session_id_;
// TODO(beng): should be combined with ToolbarModel now that this is the only
// implementation.
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons
index 564b09c..3ac082e 100644
--- a/chrome/browser/browser.scons
+++ b/chrome/browser/browser.scons
@@ -251,9 +251,14 @@ if env['PLATFORM'] == 'win32':
'safe_browsing/protocol_manager.cc',
'safe_browsing/safe_browsing_blocking_page.cc',
'sandbox_policy.cc',
- 'session_backend.cc',
- 'session_restore.cc',
- 'session_service.cc',
+ 'sessions/base_session_service.cc',
+ 'sessions/session_backend.cc',
+ 'sessions/session_command.cc',
+ 'sessions/session_id.cc',
+ 'sessions/session_restore.cc',
+ 'sessions/session_service.cc',
+ 'sessions/session_types.cc',
+ 'sessions/tab_restore_service.cc',
'shell_integration.cc',
'site_instance.cc',
'spellcheck_worditerator.cc',
@@ -266,7 +271,6 @@ if env['PLATFORM'] == 'win32':
'suspend_controller.cc',
'tab_contents.cc',
'tab_contents_factory.cc',
- 'tab_restore_service.cc',
'tab_util.cc',
'tabs/tab_strip_model.cc',
'tabs/tab_strip_model_order_controller.cc',
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index fdfcfc7..9383b95 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -562,14 +562,6 @@
>
</File>
<File
- RelativePath=".\session_restore.cc"
- >
- </File>
- <File
- RelativePath=".\session_restore.h"
- >
- </File>
- <File
RelativePath=".\session_startup_pref.cc"
>
</File>
@@ -1478,31 +1470,67 @@
Name="Sessions and Tab Restore"
>
<File
- RelativePath=".\session_backend.cc"
+ RelativePath=".\sessions\base_session_service.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\base_session_service.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_backend.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_backend.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_id.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_id.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_command.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_command.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_restore.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sessions\session_restore.h"
>
</File>
<File
- RelativePath=".\session_backend.h"
+ RelativePath=".\sessions\session_service.cc"
>
</File>
<File
- RelativePath=".\session_id.h"
+ RelativePath=".\sessions\session_service.h"
>
</File>
<File
- RelativePath=".\session_service.cc"
+ RelativePath=".\sessions\session_types.cc"
>
</File>
<File
- RelativePath=".\session_service.h"
+ RelativePath=".\sessions\session_types.h"
>
</File>
<File
- RelativePath=".\tab_restore_service.cc"
+ RelativePath=".\sessions\tab_restore_service.cc"
>
</File>
<File
- RelativePath=".\tab_restore_service.h"
+ RelativePath=".\sessions\tab_restore_service.h"
>
</File>
</Filter>
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index b56c0b38..6084a53 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -27,8 +27,8 @@
#include "chrome/browser/infobar_delegate.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/net/dns_global.h"
-#include "chrome/browser/session_restore.h"
#include "chrome/browser/session_startup_pref.h"
+#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/url_fixer_upper.h"
#include "chrome/browser/web_app_launcher.h"
@@ -76,7 +76,7 @@ class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate {
}
virtual bool Accept() {
// Restore the session.
- SessionRestore::RestoreSession(profile_, NULL, false, true, false,
+ SessionRestore::RestoreSession(profile_, NULL, true, false,
std::vector<GURL>());
return true;
}
@@ -507,8 +507,7 @@ bool BrowserInit::LaunchWithProfile::OpenStartupURLs(
// infobar.
return false;
}
- SessionRestore::RestoreSessionSynchronously(profile_, false,
- urls_to_open);
+ SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open);
return true;
case SessionStartupPref::URLS:
diff --git a/chrome/browser/browsing_data_remover.cc b/chrome/browser/browsing_data_remover.cc
index e0cdc68..86f8e98 100644
--- a/chrome/browser/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data_remover.cc
@@ -8,8 +8,8 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/profile.h"
-#include "chrome/browser/session_service.h"
-#include "chrome/browser/tab_restore_service.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
#include "chrome/browser/template_url_model.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/webdata/web_data_service.h"
@@ -77,13 +77,15 @@ void BrowsingDataRemover::Remove(int remove_mask) {
// We also delete the list of recently closed tabs. Since these expire,
// they can't be more than a day old, so we can simply clear them all.
TabRestoreService* tab_service = profile_->GetTabRestoreService();
- if (tab_service)
+ if (tab_service) {
tab_service->ClearEntries();
+ tab_service->DeleteLastSession();
+ }
// We also delete the last session when we delete the history.
SessionService* session_service = profile_->GetSessionService();
if (session_service)
- session_service->DeleteSession(false); // Last session.
+ session_service->DeleteLastSession();
}
if (remove_mask & REMOVE_DOWNLOADS) {
diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc
index cb1fe1b..1775e1e 100644
--- a/chrome/browser/dom_ui/new_tab_ui.cc
+++ b/chrome/browser/dom_ui/new_tab_ui.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/template_url.h"
#include "chrome/browser/user_data_manager.h"
#include "chrome/browser/user_metrics.h"
@@ -677,8 +678,13 @@ void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs(
// GetTabRestoreService() can return NULL (i.e., when in Off the
// Record mode)
- if (tab_restore_service_)
+ if (tab_restore_service_) {
+ // This does nothing if the tabs have already been loaded or they
+ // shouldn't be loaded.
+ tab_restore_service_->LoadTabsFromLastSession();
+
tab_restore_service_->AddObserver(this);
+ }
}
if (tab_restore_service_)
@@ -726,10 +732,11 @@ bool RecentlyClosedTabsHandler::TabToValue(
const TabNavigation& current_navigation =
tab.navigations.at(tab.current_navigation_index);
- if (current_navigation.url == NewTabUIURL())
+ if (current_navigation.url() == NewTabUIURL())
return false;
- SetURLAndTitle(dictionary, current_navigation.title, current_navigation.url);
+ SetURLAndTitle(dictionary, current_navigation.title(),
+ current_navigation.url());
dictionary->SetString(L"type", L"tab");
return true;
}
diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h
index 814e73a..4995b4d 100644
--- a/chrome/browser/dom_ui/new_tab_ui.h
+++ b/chrome/browser/dom_ui/new_tab_ui.h
@@ -9,7 +9,7 @@
#include "chrome/browser/dom_ui/dom_ui_host.h"
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
#include "chrome/browser/history/history.h"
-#include "chrome/browser/tab_restore_service.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
#include "chrome/browser/template_url_model.h"
class GURL;
diff --git a/chrome/browser/navigation_controller.cc b/chrome/browser/navigation_controller.cc
index 18e270a..2b3f96e 100644
--- a/chrome/browser/navigation_controller.cc
+++ b/chrome/browser/navigation_controller.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/repost_form_warning_dialog.h"
-#include "chrome/browser/session_service.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/site_instance.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_contents_delegate.h"
@@ -137,30 +137,11 @@ static void CreateNavigationEntriesFromTabNavigations(
const std::vector<TabNavigation>& navigations,
std::vector<linked_ptr<NavigationEntry> >* entries) {
// Create a NavigationEntry for each of the navigations.
+ int page_id = 0;
for (std::vector<TabNavigation>::const_iterator i =
- navigations.begin(); i != navigations.end(); ++i) {
- const TabNavigation& navigation = *i;
-
- GURL real_url = navigation.url;
- TabContentsType type = TabContents::TypeForURL(&real_url);
- DCHECK(type != TAB_CONTENTS_UNKNOWN_TYPE);
-
- NavigationEntry* entry = new NavigationEntry(
- type,
- NULL, // The site instance for restored tabs is sent on navigation
- // (WebContents::GetSiteInstanceForEntry).
- static_cast<int>(i - navigations.begin()),
- real_url,
- navigation.referrer,
- navigation.title,
- // Use a transition type of reload so that we don't incorrectly
- // increase the typed count.
- PageTransition::RELOAD);
- entry->set_display_url(navigation.url);
- entry->set_content_state(navigation.state);
- entry->set_has_post_data(
- navigation.type_mask & TabNavigation::HAS_POST_DATA);
- entries->push_back(linked_ptr<NavigationEntry>(entry));
+ navigations.begin(); i != navigations.end(); ++i, ++page_id) {
+ entries->push_back(
+ linked_ptr<NavigationEntry>(i->ToNavigationEntry(page_id)));
}
}
diff --git a/chrome/browser/navigation_controller.h b/chrome/browser/navigation_controller.h
index 74e3049..bd1d2aa 100644
--- a/chrome/browser/navigation_controller.h
+++ b/chrome/browser/navigation_controller.h
@@ -9,7 +9,7 @@
#include "base/linked_ptr.h"
#include "base/ref_counted.h"
-#include "chrome/browser/session_id.h"
+#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/site_instance.h"
#include "chrome/browser/ssl_manager.h"
#include "chrome/browser/tab_contents_type.h"
@@ -20,7 +20,7 @@ class Profile;
class TabContents;
class WebContents;
class TabContentsCollector;
-struct TabNavigation;
+class TabNavigation;
struct ViewHostMsg_FrameNavigate_Params;
// A NavigationController maintains the back-forward list for a single tab and
@@ -538,7 +538,7 @@ class NavigationController {
// Unique identifier of this controller for session restore. This id is only
// unique within the current session, and is not guaranteed to be unique
// across sessions.
- SessionID session_id_;
+ const SessionID session_id_;
// Unique identifier of the window we're in. Used by session restore.
SessionID window_id_;
diff --git a/chrome/browser/navigation_controller_unittest.cc b/chrome/browser/navigation_controller_unittest.cc
index 98e9ad7..08b43a0 100644
--- a/chrome/browser/navigation_controller_unittest.cc
+++ b/chrome/browser/navigation_controller_unittest.cc
@@ -9,8 +9,9 @@
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/history/history.h"
-#include "chrome/browser/session_service.h"
-#include "chrome/browser/session_service_test_helper.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_service_test_helper.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_contents_delegate.h"
#include "chrome/browser/tab_contents_factory.h"
@@ -1529,9 +1530,9 @@ TEST_F(NavigationControllerHistoryTest, NavigationThenBack) {
TabNavigation nav(0, url0, GURL(), std::wstring(), std::string(),
PageTransition::LINK);
helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[0]);
- nav.url = url1;
+ nav.set_url(url1);
helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[1]);
- nav.url = url2;
+ nav.set_url(url2);
helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[2]);
}
@@ -1559,6 +1560,6 @@ TEST_F(NavigationControllerHistoryTest, NavigationPruning) {
TabNavigation nav(0, url0, GURL(), std::wstring(), std::string(),
PageTransition::LINK);
helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[0]);
- nav.url = url2;
+ nav.set_url(url2);
helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[1]);
}
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 76f2f16..3430a3d 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -23,8 +23,9 @@
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/render_process_host.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
#include "chrome/browser/spellchecker.h"
-#include "chrome/browser/tab_restore_service.h"
#include "chrome/browser/template_url_fetcher.h"
#include "chrome/browser/template_url_model.h"
#include "chrome/browser/visitedlink_master.h"
@@ -574,7 +575,7 @@ ProfileImpl::ProfileImpl(const std::wstring& path)
}
ProfileImpl::~ProfileImpl() {
- tab_restore_service_.reset();
+ tab_restore_service_ = NULL;
StopCreateSessionServiceTimer();
// TemplateURLModel schedules a task on the WebDataService from its
@@ -878,12 +879,12 @@ Time ProfileImpl::GetStartTime() const {
TabRestoreService* ProfileImpl::GetTabRestoreService() {
if (!tab_restore_service_.get())
- tab_restore_service_.reset(new TabRestoreService(this));
+ tab_restore_service_ = new TabRestoreService(this);
return tab_restore_service_.get();
}
void ProfileImpl::ResetTabRestoreService() {
- tab_restore_service_.reset(NULL);
+ tab_restore_service_ = NULL;
}
// To be run in the IO thread to notify all resource message filters that the
diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h
index 51a5c32..44cdf7f 100644
--- a/chrome/browser/profile.h
+++ b/chrome/browser/profile.h
@@ -339,7 +339,7 @@ class ProfileImpl : public Profile,
// See GetStartTime for details.
base::Time start_time_;
- scoped_ptr<TabRestoreService> tab_restore_service_;
+ scoped_refptr<TabRestoreService> tab_restore_service_;
// This can not be a scoped_refptr because we must release it on the I/O
// thread.
diff --git a/chrome/browser/session_backend.h b/chrome/browser/session_backend.h
deleted file mode 100644
index 011ea95..0000000
--- a/chrome/browser/session_backend.h
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2006-2008 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_SESSION_BACKEND_H__
-#define CHROME_BROWSER_SESSION_BACKEND_H__
-
-#include <vector>
-
-#include "base/ref_counted.h"
-#include "base/scoped_handle.h"
-#include "chrome/browser/session_service.h"
-
-class Pickle;
-
-// SessionCommand -------------------------------------------------------------
-
-// SessionCommand contains a command id and arbitrary amount chunk of memory.
-//
-// SessionBackend reads and writes SessionCommands.
-//
-// A SessionCommand may be created directly from a Pickle, which is useful
-// for types of arbitrary length.
-class SessionCommand {
- public:
- // These get written to disk, so we define types for them.
- // Type for the identifier.
- typedef uint8 id_type;
- // Type for writing the size.
- typedef uint16 size_type;
-
- // Creates a session command with the specified id. This allocates a buffer
- // of size |size| that must be filled via contents().
- SessionCommand(id_type id, size_type size);
-
- // Convenience constructor that creates a session command with the specified
- // id whose contents is populated from the contents of pickle.
- SessionCommand(id_type id, const Pickle& pickle);
-
- // The contents of the command.
- char* contents() { return &(contents_[0]); }
- const char* contents() const { return &(contents_[0]); }
-
- // Identifier for the command.
- id_type id() const { return id_; }
-
- // Size of data.
- size_type size() const { return static_cast<size_type>(contents_.size()); }
-
- // Convenience for extracting the data to a target. Returns false if
- // count is not equal to the size of data this command contains.
- bool GetPayload(void* dest, size_t count) const;
-
- // Returns the contents as a pickle. It is up to the caller to delete the
- // returned Pickle. The returned Pickle references the underlying data of
- // this SessionCommand. If you need it to outlive the command, copy the
- // pickle.
- Pickle* PayloadAsPickle() const;
-
- private:
- const id_type id_;
- std::string contents_;
-
- DISALLOW_EVIL_CONSTRUCTORS(SessionCommand);
-};
-
-// SessionBackend -------------------------------------------------------------
-
-// SessionBackend is the backend used by SessionService. It is responsible
-// for maintaining up to 3 files:
-// . The current file, which is the file commands passed to AppendCommands
-// get written to.
-// . The last file. When created the current file is moved to the last
-// file.
-// . A save file, which is created with arbitrary commands.
-//
-// Each file contains an arbitrary set of commands supplied from
-// SessionService.
-
-class SessionBackend : public base::RefCountedThreadSafe<SessionBackend> {
- public:
- typedef SessionCommand::id_type id_type;
- typedef SessionCommand::size_type size_type;
-
- // Initial size of the buffer used in reading the file. This is exposed
- // for testing.
- static const int kFileReadBufferSize;
-
- // Creates a SessionBackend. This method is invoked on the MAIN thread,
- // and does NO IO. The real work is done from Init, which is invoked on
- // the file thread.
- //
- // The supplied path is the directory the files are writen to.
- explicit SessionBackend(const std::wstring& path_to_dir);
-
- // Moves the current file to the last file, and recreates the current file.
- //
- // NOTE: this is invoked before every command, and does nothing if we've
- // already Init'ed.
- void Init();
-
- // Recreates the save file with the specified commands.
- //
- // This deletes the SessionCommands passed to it.
- void SaveSession(const std::vector<SessionCommand*>& commands);
-
- // Appends the specified commands to the current file. If reset_first is
- // true the the current file is recreated.
- //
- // NOTE: this deletes SessionCommands in commands as well as the supplied
- // vector.
- void AppendCommands(std::vector<SessionCommand*>* commands,
- bool reset_first);
-
- // Invoked from the service, invokes ReadSessionImpl to do the work.
- void ReadSession(
- scoped_refptr<SessionService::InternalSavedSessionRequest> request);
-
- // Reads the commands from the last file, or save file if
- // use_save_file is true.
- //
- // On success, the read commands are added to commands. It is up to the
- // caller to delete the commands.
- bool ReadSessionImpl(bool use_save_file,
- std::vector<SessionCommand*>* commands);
-
- // If saved_session is true, deletes the saved session, otherwise deletes
- // the last file.
- void DeleteSession(bool saved_session);
-
- // Copies the contents of the last session file to the saved session file.
- void CopyLastSessionToSavedSession();
-
- // Moves the current session to the last and resets the current. This is
- // called during startup and if the user launchs the app and no tabbed
- // browsers are running.
- void MoveCurrentSessionToLastSession();
-
- private:
- // Recreates the current file such that it only contains the header and
- // NO commands.
- void ResetFile();
-
- // Opens the current file and writes the header. On success a handle to
- // the file is returned.
- HANDLE OpenAndWriteHeader(const std::wstring& path);
-
- // Appends the specified commands to the specified file.
- bool AppendCommandsToFile(HANDLE handle,
- const std::vector<SessionCommand*>& commands);
-
- // Returns the path to the last file.
- std::wstring GetLastSessionPath();
-
- // Returns the path to the save file.
- std::wstring GetSavedSessionPath();
-
- // Returns the path to the current file.
- std::wstring GetCurrentSessionPath();
-
- // Directory files are relative to.
- const std::wstring path_to_dir_;
-
- // Whether the previous target file is valid.
- bool last_session_valid_;
-
- // Handle to the target file.
- ScopedHandle current_session_handle_;
-
- // Whether we've inited. Remember, the constructor is run on the
- // Main thread, all others on the IO thread, hence lazy initialization.
- bool inited_;
-
- // If true, the file is empty (no commands have been added to it).
- bool empty_file_;
-
- DISALLOW_EVIL_CONSTRUCTORS(SessionBackend);
-};
-
-#endif // #define CHROME_BROWSER_SESSION_BACKEND_H__
-
diff --git a/chrome/browser/sessions/base_session_service.cc b/chrome/browser/sessions/base_session_service.cc
new file mode 100644
index 0000000..b26f7e0
--- /dev/null
+++ b/chrome/browser/sessions/base_session_service.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2006-2008 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.
+
+#include "chrome/browser/sessions/base_session_service.h"
+
+#include "base/pickle.h"
+#include "base/thread.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/common/stl_util-inl.h"
+
+// InternalGetCommandsRequest -------------------------------------------------
+
+BaseSessionService::InternalGetCommandsRequest::~InternalGetCommandsRequest() {
+ STLDeleteElements(&commands);
+}
+
+// BaseSessionService ---------------------------------------------------------
+
+namespace {
+
+// Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to
+// |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|).
+// |bytes_written| is incremented to reflect the data written.
+void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
+ const std::string& str) {
+ int num_bytes = str.size() * sizeof(char);
+ if (*bytes_written + num_bytes < max_bytes) {
+ *bytes_written += num_bytes;
+ pickle.WriteString(str);
+ } else {
+ pickle.WriteString(std::string());
+ }
+}
+
+// Wide version of WriteStringToPickle.
+void WriteWStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
+ const std::wstring& str) {
+ int num_bytes = str.size() * sizeof(wchar_t);
+ if (*bytes_written + num_bytes < max_bytes) {
+ *bytes_written += num_bytes;
+ pickle.WriteWString(str);
+ } else {
+ pickle.WriteWString(std::wstring());
+ }
+}
+
+} // namespace
+
+// Delay between when a command is received, and when we save it to the
+// backend.
+static const int kSaveDelayMS = 2500;
+
+// static
+const int BaseSessionService::max_persist_navigation_count = 6;
+
+BaseSessionService::BaseSessionService(SessionType type,
+ Profile* profile,
+ const std::wstring& path)
+ : profile_(profile),
+ path_(path),
+ backend_thread_(NULL),
+#pragma warning(suppress: 4355) // Okay to pass "this" here.
+ save_factory_(this),
+ pending_reset_(false),
+ commands_since_reset_(0) {
+ if (profile) {
+ // We should never be created when off the record.
+ DCHECK(!profile->IsOffTheRecord());
+ }
+ backend_ = new SessionBackend(type, profile_ ? profile_->GetPath() : path_);
+ DCHECK(backend_.get());
+ backend_thread_ = g_browser_process->file_thread();
+ if (!backend_thread_)
+ backend_->Init();
+ // If backend_thread is non-null, backend will init itself as appropriate.
+}
+
+BaseSessionService::~BaseSessionService() {
+}
+
+void BaseSessionService::DeleteLastSession() {
+ if (!backend_thread()) {
+ backend()->DeleteLastSession();
+ } else {
+ backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ backend(), &SessionBackend::DeleteLastSession));
+ }
+}
+
+void BaseSessionService::ScheduleCommand(SessionCommand* command) {
+ DCHECK(command);
+ commands_since_reset_++;
+ pending_commands_.push_back(command);
+ StartSaveTimer();
+}
+
+void BaseSessionService::StartSaveTimer() {
+ // Don't start a timer when testing (profile == NULL or
+ // MessageLoop::current() is NULL).
+ if (MessageLoop::current() && profile() && save_factory_.empty()) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ save_factory_.NewRunnableMethod(&BaseSessionService::Save),
+ kSaveDelayMS);
+ }
+}
+
+void BaseSessionService::Save() {
+ DCHECK(backend());
+
+ if (pending_commands_.empty())
+ return;
+
+ if (!backend_thread()) {
+ backend()->AppendCommands(
+ new std::vector<SessionCommand*>(pending_commands_), pending_reset_);
+ } else {
+ backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ backend(), &SessionBackend::AppendCommands,
+ new std::vector<SessionCommand*>(pending_commands_),
+ pending_reset_));
+ }
+ // Backend took ownership of commands.
+ pending_commands_.clear();
+
+ if (pending_reset_) {
+ commands_since_reset_ = 0;
+ pending_reset_ = false;
+ }
+}
+
+SessionCommand* BaseSessionService::CreateUpdateTabNavigationCommand(
+ SessionID::id_type command_id,
+ SessionID::id_type tab_id,
+ int index,
+ const NavigationEntry& entry) {
+ // Use pickle to handle marshalling.
+ Pickle pickle;
+ pickle.WriteInt(tab_id);
+ pickle.WriteInt(index);
+
+ // We only allow navigations up to 63k (which should be completely
+ // reasonable). On the off chance we get one that is too big, try to
+ // keep the url.
+
+ // Bound the string data (which is variable length) to
+ // |max_state_size bytes| bytes.
+ static const SessionCommand::size_type max_state_size =
+ std::numeric_limits<SessionCommand::size_type>::max() - 1024;
+
+ int bytes_written = 0;
+
+ WriteStringToPickle(pickle, &bytes_written, max_state_size,
+ entry.display_url().spec());
+
+ WriteWStringToPickle(pickle, &bytes_written, max_state_size,
+ entry.title());
+
+ WriteStringToPickle(pickle, &bytes_written, max_state_size,
+ entry.content_state());
+
+ pickle.WriteInt(entry.transition_type());
+ int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0;
+ pickle.WriteInt(type_mask);
+
+ WriteStringToPickle(pickle, &bytes_written, max_state_size,
+ entry.referrer().is_valid() ? entry.referrer().spec() : std::string());
+
+ // Adding more data? Be sure and update TabRestoreService too.
+ return new SessionCommand(command_id, pickle);
+}
+
+bool BaseSessionService::RestoreUpdateTabNavigationCommand(
+ const SessionCommand& command,
+ TabNavigation* navigation,
+ SessionID::id_type* tab_id) {
+ scoped_ptr<Pickle> pickle(command.PayloadAsPickle());
+ if (!pickle.get())
+ return false;
+ void* iterator = NULL;
+ std::string url_spec;
+ if (!pickle->ReadInt(&iterator, tab_id) ||
+ !pickle->ReadInt(&iterator, &(navigation->index_)) ||
+ !pickle->ReadString(&iterator, &url_spec) ||
+ !pickle->ReadWString(&iterator, &(navigation->title_)) ||
+ !pickle->ReadString(&iterator, &(navigation->state_)) ||
+ !pickle->ReadInt(&iterator,
+ reinterpret_cast<int*>(&(navigation->transition_))))
+ return false;
+ // type_mask did not always exist in the written stream. As such, we
+ // don't fail if it can't be read.
+ bool has_type_mask = pickle->ReadInt(&iterator, &(navigation->type_mask_));
+
+ if (has_type_mask) {
+ // the "referrer" property was added after type_mask to the written
+ // stream. As such, we don't fail if it can't be read.
+ std::string referrer_spec;
+ pickle->ReadString(&iterator, &referrer_spec);
+ if (!referrer_spec.empty())
+ navigation->referrer_ = GURL(referrer_spec);
+ }
+
+ navigation->url_ = GURL(url_spec);
+ return true;
+}
+
+bool BaseSessionService::ShouldTrackEntry(const NavigationEntry& entry) {
+ // Don't track entries that have post data. Post data may contain passwords
+ // and other sensitive data users don't want stored to disk.
+ return entry.display_url().is_valid() && !entry.has_post_data();
+}
+
+bool BaseSessionService::ShouldTrackEntry(const TabNavigation& navigation) {
+ // Don't track entries that have post data. Post data may contain passwords
+ // and other sensitive data users don't want stored to disk.
+ return navigation.url().is_valid() &&
+ (navigation.type_mask() & TabNavigation::HAS_POST_DATA) == 0;
+}
+
+BaseSessionService::Handle BaseSessionService::ScheduleGetLastSessionCommands(
+ InternalGetCommandsRequest* request,
+ CancelableRequestConsumerBase* consumer) {
+ scoped_refptr<InternalGetCommandsRequest> request_wrapper(request);
+ AddRequest(request, consumer);
+ if (backend_thread()) {
+ backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ backend(), &SessionBackend::ReadLastSessionCommands, request));
+ } else {
+ backend()->ReadLastSessionCommands(request);
+ }
+ return request->handle();
+}
diff --git a/chrome/browser/sessions/base_session_service.h b/chrome/browser/sessions/base_session_service.h
new file mode 100644
index 0000000..49c70c4
--- /dev/null
+++ b/chrome/browser/sessions/base_session_service.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2006-2008 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_BASE_SESSION_SERVICE_H_
+#define CHROME_BROWSER_SESSIONS_BASE_SESSION_SERVICE_H_
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/sessions/session_id.h"
+
+class NavigationEntry;
+class Profile;
+class SessionBackend;
+class SessionCommand;
+class SessionService;
+class TabNavigation;
+
+namespace base {
+class Thread;
+}
+
+// BaseSessionService is the super class of both tab restore service and
+// session service. It contains commonality needed by both, in particular
+// it manages a set of SessionCommands that are periodically sent to a
+// SessionBackend.
+class BaseSessionService : public CancelableRequestProvider,
+ public base::RefCountedThreadSafe<BaseSessionService> {
+ public:
+ // Identifies the type of session service this is. This is used by the
+ // backend to determine the name of the files.
+ enum SessionType {
+ SESSION_RESTORE,
+ TAB_RESTORE
+ };
+
+ // Creates a new BaseSessionService. After creation you need to invoke
+ // Init.
+ // |type| gives the type of session service, |profile| the profile and
+ // |path| the path to save files to. If |profile| is non-NULL, |path| is
+ // ignored and instead the path comes from the profile.
+ BaseSessionService(SessionType type,
+ Profile* profile,
+ const std::wstring& path);
+
+ virtual ~BaseSessionService();
+
+ Profile* profile() const { return profile_; }
+
+ // Deletes the last session.
+ void DeleteLastSession();
+
+ class InternalGetCommandsRequest;
+
+ typedef Callback2<Handle, scoped_refptr<InternalGetCommandsRequest> >::Type
+ InternalGetCommandsCallback;
+
+ // Callback used when fetching the last session. The last session consists
+ // of a vector of SessionCommands.
+ class InternalGetCommandsRequest :
+ public CancelableRequest<InternalGetCommandsCallback> {
+ public:
+ explicit InternalGetCommandsRequest(CallbackType* callback)
+ : CancelableRequest(callback) {
+ }
+ virtual ~InternalGetCommandsRequest();
+
+ // The commands. The backend fills this in for us.
+ std::vector<SessionCommand*> commands;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InternalGetCommandsRequest);
+ };
+
+ protected:
+ // Returns the backend.
+ SessionBackend* backend() const { return backend_; }
+
+ // Returns the thread the backend runs on. This returns NULL during testing.
+ base::Thread* backend_thread() const { return backend_thread_; }
+
+ // Returns the set of commands that needed to be scheduled. The commands
+ // in the vector are owned by BaseSessionService, until they are scheduled
+ // on the backend at which point the backend owns the commands.
+ std::vector<SessionCommand*>& pending_commands() {
+ return pending_commands_;
+ }
+
+ // Whether the next save resets the file before writing to it.
+ void set_pending_reset(bool value) { pending_reset_ = value; }
+ bool pending_reset() const { return pending_reset_; }
+
+ // Returns the number of commands sent down since the last reset.
+ int commands_since_reset() const { return commands_since_reset_; }
+
+ // Schedules a command. This adds |command| to pending_commands_ and
+ // invokes StartSaveTimer to start a timer that invokes Save at a later
+ // time.
+ virtual void ScheduleCommand(SessionCommand* command);
+
+ // Starts the timer that invokes Save (if timer isn't already running).
+ void StartSaveTimer();
+
+ // Saves pending commands to the backend. This is invoked from the timer
+ // scheduled by StartSaveTimer.
+ virtual void Save();
+
+ // Creates a SessionCommand that represents a navigation.
+ SessionCommand* CreateUpdateTabNavigationCommand(
+ SessionID::id_type command_id,
+ SessionID::id_type tab_id,
+ int index,
+ const NavigationEntry& entry);
+
+ // Converts a SessionCommand previously created by
+ // CreateUpdateTabNavigationCommand into a TabNavigation. Returns true
+ // on success. If successful |tab_id| is set to the id of the restored tab.
+ bool RestoreUpdateTabNavigationCommand(const SessionCommand& command,
+ TabNavigation* navigation,
+ SessionID::id_type* tab_id);
+
+ // Returns true if the NavigationEntry should be written to disk.
+ bool ShouldTrackEntry(const NavigationEntry& entry);
+
+ // Returns true if the TabNavigationshould be written to disk.
+ bool ShouldTrackEntry(const TabNavigation& navigation);
+
+ // Invokes ReadLastSessionCommands with request on the backend thread.
+ // If testing, ReadLastSessionCommands is invoked directly.
+ Handle ScheduleGetLastSessionCommands(
+ InternalGetCommandsRequest* request,
+ CancelableRequestConsumerBase* consumer);
+
+ // Max number of navigation entries in each direction we'll persist.
+ static const int max_persist_navigation_count;
+
+ private:
+ // The profile. This may be null during testing.
+ Profile* profile_;
+
+ // Path to read from. This is only used if profile_ is NULL.
+ const std::wstring& path_;
+
+ // The backend.
+ scoped_refptr<SessionBackend> backend_;
+
+ // Thread backend tasks are run on, is NULL during testing.
+ base::Thread* backend_thread_;
+
+ // Used to invoke Save.
+ ScopedRunnableMethodFactory<BaseSessionService> save_factory_;
+
+ // Commands we need to send over to the backend.
+ std::vector<SessionCommand*> pending_commands_;
+
+ // Whether the backend file should be recreated the next time we send
+ // over the commands.
+ bool pending_reset_;
+
+ // The number of commands sent to the backend before doing a reset.
+ int commands_since_reset_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseSessionService);
+};
+
+#endif // CHROME_BROWSER_SESSIONS_BASE_SESSION_SERVICE_H_
diff --git a/chrome/browser/session_backend.cc b/chrome/browser/sessions/session_backend.cc
index 0f1f863..dc259d1 100644
--- a/chrome/browser/session_backend.cc
+++ b/chrome/browser/sessions/session_backend.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/session_backend.h"
+#include "chrome/browser/sessions/session_backend.h"
#include <limits>
@@ -43,13 +43,14 @@ class SessionFileReader {
// Reads the contents of the file specified in the constructor, returning
// true on success. It is up to the caller to free all SessionCommands
// added to commands.
- bool Read(std::vector<SessionCommand*>* commands);
+ bool Read(BaseSessionService::SessionType type,
+ std::vector<SessionCommand*>* commands);
private:
// Reads a single command, returning it. A return value of NULL indicates
// either there are no commands, or there was an error. Use errored_ to
// distinguish the two. If NULL is returned, and there is no error, it means
- // the end of file was sucessfully reached.
+ // the end of file was successfully reached.
SessionCommand* ReadCommand();
// Shifts the unused portion of buffer_ to the beginning and fills the
@@ -76,7 +77,8 @@ class SessionFileReader {
DISALLOW_EVIL_CONSTRUCTORS(SessionFileReader);
};
-bool SessionFileReader::Read(std::vector<SessionCommand*>* commands) {
+bool SessionFileReader::Read(BaseSessionService::SessionType type,
+ std::vector<SessionCommand*>* commands) {
if (!handle_.IsValid())
return false;
int32 header[2];
@@ -93,8 +95,13 @@ bool SessionFileReader::Read(std::vector<SessionCommand*>* commands) {
read_commands->push_back(command);
if (!errored_)
read_commands->swap(*commands);
- UMA_HISTOGRAM_TIMES(L"SessionRestore.read_session_file_time",
- TimeTicks::Now() - start_time);
+ if (type == BaseSessionService::TAB_RESTORE) {
+ UMA_HISTOGRAM_TIMES(L"TabRestore.read_session_file_time",
+ TimeTicks::Now() - start_time);
+ } else {
+ UMA_HISTOGRAM_TIMES(L"SessionRestore.read_session_file_time",
+ TimeTicks::Now() - start_time);
+ }
return !errored_;
}
@@ -166,47 +173,23 @@ bool SessionFileReader::FillBuffer() {
} // namespace
-// SessionCommand -------------------------------------------------------------
-
-SessionCommand::SessionCommand(id_type id, size_type size)
- : id_(id),
- contents_(size, 0) {
-}
-
-SessionCommand::SessionCommand(id_type id, const Pickle& pickle)
- : id_(id),
- contents_(pickle.size(), 0) {
- DCHECK(pickle.size() < std::numeric_limits<size_type>::max());
- memcpy(contents(), pickle.data(), pickle.size());
-}
-
-bool SessionCommand::GetPayload(void* dest, size_t count) const {
- if (size() != count)
- return false;
- memcpy(dest, &(contents_[0]), count);
- return true;
-}
-
-Pickle* SessionCommand::PayloadAsPickle() const {
- return new Pickle(contents(), static_cast<int>(size()));
-}
-
// SessionBackend -------------------------------------------------------------
-// Target file name.
-static const wchar_t* const kCurrentSessionFileName = L"Current Session";
+// File names (current and previous) for a type of TAB.
+static const wchar_t* const kCurrentTabSessionFileName = L"Current Tabs";
+static const wchar_t* const kLastTabSessionFileName = L"Last Tabs";
-// Previous target file.
+// File names (current and previous) for a type of SESSION.
+static const wchar_t* const kCurrentSessionFileName = L"Current Session";
static const wchar_t* const kLastSessionFileName = L"Last Session";
-// Saved session file name.
-static const wchar_t* const kSavedSessionFileName = L"Saved Session";
-
// static
const int SessionBackend::kFileReadBufferSize = 1024;
-SessionBackend::SessionBackend(const std::wstring& path_to_dir)
- : path_to_dir_(path_to_dir),
+SessionBackend::SessionBackend(BaseSessionService::SessionType type,
+ const std::wstring& path_to_dir)
+ : type_(type),
+ path_to_dir_(path_to_dir),
last_session_valid_(false),
inited_(false),
empty_file_(true) {
@@ -225,15 +208,6 @@ void SessionBackend::Init() {
MoveCurrentSessionToLastSession();
}
-void SessionBackend::SaveSession(
- const std::vector<SessionCommand*>& commands) {
- Init();
- ScopedHandle handle(OpenAndWriteHeader(GetSavedSessionPath()));
- if (handle.IsValid())
- AppendCommandsToFile(handle, commands);
- STLDeleteContainerPointers(commands.begin(), commands.end());
-}
-
void SessionBackend::AppendCommands(
std::vector<SessionCommand*>* commands,
bool reset_first) {
@@ -249,36 +223,27 @@ void SessionBackend::AppendCommands(
delete commands;
}
-void SessionBackend::ReadSession(
- scoped_refptr<SessionService::InternalSavedSessionRequest> request) {
+void SessionBackend::ReadLastSessionCommands(
+ scoped_refptr<BaseSessionService::InternalGetCommandsRequest> request) {
if (request->canceled())
return;
Init();
- ReadSessionImpl(request->is_saved_session, &(request->commands));
+ ReadLastSessionCommandsImpl(&(request->commands));
request->ForwardResult(
- SessionService::InternalSavedSessionRequest::TupleType(request->handle(),
- request));
+ BaseSessionService::InternalGetCommandsRequest::TupleType(
+ request->handle(), request));
}
-bool SessionBackend::ReadSessionImpl(bool use_save_file,
- std::vector<SessionCommand*>* commands) {
+bool SessionBackend::ReadLastSessionCommandsImpl(
+ std::vector<SessionCommand*>* commands) {
Init();
- const std::wstring path =
- use_save_file ? GetSavedSessionPath() : GetLastSessionPath();
- SessionFileReader file_reader(path);
- return file_reader.Read(commands);
+ SessionFileReader file_reader(GetLastSessionPath());
+ return file_reader.Read(type_, commands);
}
-void SessionBackend::DeleteSession(bool saved_session) {
+void SessionBackend::DeleteLastSession() {
Init();
- const std::wstring path =
- saved_session ? GetSavedSessionPath() : GetLastSessionPath();
- file_util::Delete(path, false);
-}
-
-void SessionBackend::CopyLastSessionToSavedSession() {
- Init();
- file_util::CopyFile(GetLastSessionPath(), GetSavedSessionPath());
+ file_util::Delete(GetLastSessionPath(), false);
}
void SessionBackend::MoveCurrentSessionToLastSession() {
@@ -292,8 +257,13 @@ void SessionBackend::MoveCurrentSessionToLastSession() {
if (file_util::PathExists(current_session_path)) {
int64 file_size;
if (file_util::GetFileSize(current_session_path, &file_size)) {
- UMA_HISTOGRAM_COUNTS(L"SessionRestore.last_session_file_size",
- static_cast<int>(file_size / 1024));
+ if (type_ == BaseSessionService::TAB_RESTORE) {
+ UMA_HISTOGRAM_COUNTS(L"TabRestore.last_session_file_size",
+ static_cast<int>(file_size / 1024));
+ } else {
+ UMA_HISTOGRAM_COUNTS(L"SessionRestore.last_session_file_size",
+ static_cast<int>(file_size / 1024));
+ }
}
last_session_valid_ = file_util::Move(current_session_path,
last_session_path);
@@ -314,7 +284,10 @@ bool SessionBackend::AppendCommandsToFile(
DWORD wrote;
const size_type content_size = static_cast<size_type>((*i)->size());
const size_type total_size = content_size + sizeof(id_type);
- UMA_HISTOGRAM_COUNTS(L"SessionRestore.command_size", total_size);
+ if (type_ == BaseSessionService::TAB_RESTORE)
+ UMA_HISTOGRAM_COUNTS(L"TabRestore.command_size", total_size);
+ else
+ UMA_HISTOGRAM_COUNTS(L"SessionRestore.command_size", total_size);
if (!WriteFile(handle, &total_size, sizeof(total_size), &wrote, NULL) ||
wrote != sizeof(total_size)) {
NOTREACHED() << "error writing";
@@ -362,18 +335,18 @@ HANDLE SessionBackend::OpenAndWriteHeader(const std::wstring& path) {
std::wstring SessionBackend::GetLastSessionPath() {
std::wstring path = path_to_dir_;
- file_util::AppendToPath(&path, kLastSessionFileName);
- return path;
-}
-
-std::wstring SessionBackend::GetSavedSessionPath() {
- std::wstring path = path_to_dir_;
- file_util::AppendToPath(&path, kSavedSessionFileName);
+ if (type_ == BaseSessionService::TAB_RESTORE)
+ file_util::AppendToPath(&path, kLastTabSessionFileName);
+ else
+ file_util::AppendToPath(&path, kLastSessionFileName);
return path;
}
std::wstring SessionBackend::GetCurrentSessionPath() {
std::wstring path = path_to_dir_;
- file_util::AppendToPath(&path, kCurrentSessionFileName);
+ if (type_ == BaseSessionService::TAB_RESTORE)
+ file_util::AppendToPath(&path, kCurrentTabSessionFileName);
+ else
+ file_util::AppendToPath(&path, kCurrentSessionFileName);
return path;
}
diff --git a/chrome/browser/sessions/session_backend.h b/chrome/browser/sessions/session_backend.h
new file mode 100644
index 0000000..1519d3f
--- /dev/null
+++ b/chrome/browser/sessions/session_backend.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2006-2008 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_SESSION_BACKEND_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_BACKEND_H_
+
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
+#include "chrome/browser/sessions/base_session_service.h"
+#include "chrome/browser/sessions/session_command.h"
+
+class Pickle;
+
+// SessionBackend -------------------------------------------------------------
+
+// SessionBackend is the backend used by BaseSessionService. It is responsible
+// for maintaining two files:
+// . The current file, which is the file commands passed to AppendCommands
+// get written to.
+// . The last file. When created the current file is moved to the last
+// file.
+//
+// Each file contains an arbitrary set of commands supplied from
+// BaseSessionService. A command consists of a unique id and a stream of bytes.
+// SessionBackend does not use the id in anyway, that is used by
+// BaseSessionService.
+class SessionBackend : public base::RefCountedThreadSafe<SessionBackend> {
+ public:
+ typedef SessionCommand::id_type id_type;
+ typedef SessionCommand::size_type size_type;
+
+ // Initial size of the buffer used in reading the file. This is exposed
+ // for testing.
+ static const int kFileReadBufferSize;
+
+ // Creates a SessionBackend. This method is invoked on the MAIN thread,
+ // and does no IO. The real work is done from Init, which is invoked on
+ // the file thread.
+ //
+ // |path_to_dir| gives the path the files are written two, and |type|
+ // indicates which service is using this backend. |type| is used to determine
+ // the name of the files to use as well as for logging.
+ SessionBackend(BaseSessionService::SessionType type,
+ const std::wstring& path_to_dir);
+
+ // Moves the current file to the last file, and recreates the current file.
+ //
+ // NOTE: this is invoked before every command, and does nothing if we've
+ // already Init'ed.
+ void Init();
+
+ // Appends the specified commands to the current file. If reset_first is
+ // true the the current file is recreated.
+ //
+ // NOTE: this deletes SessionCommands in commands as well as the supplied
+ // vector.
+ void AppendCommands(std::vector<SessionCommand*>* commands,
+ bool reset_first);
+
+ // Invoked from the service to read the commands that make up the last
+ // session, invokes ReadSessionImpl to do the work.
+ void ReadLastSessionCommands(
+ scoped_refptr<BaseSessionService::InternalGetCommandsRequest> request);
+
+ // Reads the commands from the last file.
+ //
+ // On success, the read commands are added to commands. It is up to the
+ // caller to delete the commands.
+ bool ReadLastSessionCommandsImpl(std::vector<SessionCommand*>* commands);
+
+ // Deletes the file containing the commands for the last session.
+ void DeleteLastSession();
+
+ // Moves the current session to the last and resets the current. This is
+ // called during startup and if the user launchs the app and no tabbed
+ // browsers are running.
+ void MoveCurrentSessionToLastSession();
+
+ private:
+ // Recreates the current file such that it only contains the header and
+ // NO commands.
+ void ResetFile();
+
+ // Opens the current file and writes the header. On success a handle to
+ // the file is returned.
+ HANDLE OpenAndWriteHeader(const std::wstring& path);
+
+ // Appends the specified commands to the specified file.
+ bool AppendCommandsToFile(HANDLE handle,
+ const std::vector<SessionCommand*>& commands);
+
+ const BaseSessionService::SessionType type_;
+
+ // Returns the path to the last file.
+ std::wstring GetLastSessionPath();
+
+ // Returns the path to the current file.
+ std::wstring GetCurrentSessionPath();
+
+ // Directory files are relative to.
+ const std::wstring path_to_dir_;
+
+ // Whether the previous target file is valid.
+ bool last_session_valid_;
+
+ // Handle to the target file.
+ ScopedHandle current_session_handle_;
+
+ // Whether we've inited. Remember, the constructor is run on the
+ // Main thread, all others on the IO thread, hence lazy initialization.
+ bool inited_;
+
+ // If true, the file is empty (no commands have been added to it).
+ bool empty_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionBackend);
+};
+
+#endif // #define CHROME_BROWSER_SESSIONS_SESSION_BACKEND_H_
diff --git a/chrome/browser/session_backend_unittest.cc b/chrome/browser/sessions/session_backend_unittest.cc
index 8023648..b7bfd92 100644
--- a/chrome/browser/session_backend_unittest.cc
+++ b/chrome/browser/sessions/session_backend_unittest.cc
@@ -5,7 +5,8 @@
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/time.h"
-#include "chrome/browser/session_backend.h"
+#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/common/stl_util-inl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -57,7 +58,8 @@ class SessionBackendTest : public testing::Test {
};
TEST_F(SessionBackendTest, SimpleReadWrite) {
- scoped_refptr<SessionBackend> backend(new SessionBackend(path_));
+ scoped_refptr<SessionBackend> backend(
+ new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
struct TestData data = { 1, "a" };
std::vector<SessionCommand*> commands;
commands.push_back(CreateCommandFromData(data));
@@ -66,8 +68,8 @@ TEST_F(SessionBackendTest, SimpleReadWrite) {
// Read it back in.
backend = NULL;
- backend = new SessionBackend(path_);
- backend->ReadSessionImpl(false, &commands);
+ backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(1, commands.size());
AssertCommandEqualsData(data, commands[0]);
@@ -75,14 +77,14 @@ TEST_F(SessionBackendTest, SimpleReadWrite) {
STLDeleteElements(&commands);
backend = NULL;
- backend = new SessionBackend(path_);
- backend->ReadSessionImpl(false, &commands);
+ backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(0, commands.size());
// Make sure we can delete.
- backend->DeleteSession(false);
- backend->ReadSessionImpl(false, &commands);
+ backend->DeleteLastSession();
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(0, commands.size());
}
@@ -104,11 +106,12 @@ TEST_F(SessionBackendTest, RandomData) {
};
for (int i = 0; i < arraysize(data); ++i) {
- scoped_refptr<SessionBackend> backend(new SessionBackend(path_));
+ scoped_refptr<SessionBackend> backend(
+ new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
std::vector<SessionCommand*> commands;
if (i != 0) {
// Read previous data.
- backend->ReadSessionImpl(false, &commands);
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(i, commands.size());
for (std::vector<SessionCommand*>::iterator j = commands.begin();
j != commands.end(); ++j) {
@@ -128,7 +131,8 @@ TEST_F(SessionBackendTest, BigData) {
{ 2, "ab" },
};
- scoped_refptr<SessionBackend> backend(new SessionBackend(path_));
+ scoped_refptr<SessionBackend> backend(
+ new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
std::vector<SessionCommand*> commands;
commands.push_back(CreateCommandFromData(data[0]));
const SessionCommand::size_type big_size = SessionBackend::kFileReadBufferSize + 100;
@@ -142,9 +146,9 @@ TEST_F(SessionBackendTest, BigData) {
commands.clear();
backend = NULL;
- backend = new SessionBackend(path_);
+ backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
commands.clear();
- backend->ReadSessionImpl(false, &commands);
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(3, commands.size());
AssertCommandEqualsData(data[0], commands[0]);
AssertCommandEqualsData(data[1], commands[2]);
@@ -157,41 +161,20 @@ TEST_F(SessionBackendTest, BigData) {
STLDeleteElements(&commands);
}
-TEST_F(SessionBackendTest, SaveSession) {
- struct TestData data[] = {
- { 1, "a" },
- { 2, "ab" },
- };
-
- scoped_refptr<SessionBackend> backend(new SessionBackend(path_));
- std::vector<SessionCommand*> commands;
- for (int i = 0; i < arraysize(data); ++i) {
- commands.push_back(CreateCommandFromData(data[i]));
- }
- backend->SaveSession(commands);
-
- commands.clear();
-
- backend->ReadSessionImpl(true, &commands);
- ASSERT_EQ(arraysize(data), commands.size());
- for (int i = 0; i < arraysize(data); ++i)
- AssertCommandEqualsData(data[i], commands[i]);
- STLDeleteElements(&commands);
-}
-
TEST_F(SessionBackendTest, EmptyCommand) {
TestData empty_command;
empty_command.command_id = 1;
- scoped_refptr<SessionBackend> backend(new SessionBackend(path_));
- std::vector<SessionCommand*> commands;
- commands.push_back(CreateCommandFromData(empty_command));
- backend->SaveSession(commands);
-
- commands.clear();
+ scoped_refptr<SessionBackend> backend(
+ new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
+ std::vector<SessionCommand*>* empty_commands =
+ new std::vector<SessionCommand*>();
+ empty_commands->push_back(CreateCommandFromData(empty_command));
+ backend->AppendCommands(empty_commands, true);
+ backend->MoveCurrentSessionToLastSession();
- backend->ReadSessionImpl(true, &commands);
+ std::vector<SessionCommand*> commands;
+ backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(1, commands.size());
AssertCommandEqualsData(empty_command, commands[0]);
STLDeleteElements(&commands);
}
-
diff --git a/chrome/browser/sessions/session_command.cc b/chrome/browser/sessions/session_command.cc
new file mode 100644
index 0000000..e5ca2f5
--- /dev/null
+++ b/chrome/browser/sessions/session_command.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2006-2008 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.
+
+#include <limits>
+
+#include "chrome/browser/sessions/session_command.h"
+
+#include "base/pickle.h"
+
+SessionCommand::SessionCommand(id_type id, size_type size)
+ : id_(id),
+ contents_(size, 0) {
+}
+
+SessionCommand::SessionCommand(id_type id, const Pickle& pickle)
+ : id_(id),
+ contents_(pickle.size(), 0) {
+ DCHECK(pickle.size() < std::numeric_limits<size_type>::max());
+ memcpy(contents(), pickle.data(), pickle.size());
+}
+
+bool SessionCommand::GetPayload(void* dest, size_t count) const {
+ if (size() != count)
+ return false;
+ memcpy(dest, &(contents_[0]), count);
+ return true;
+}
+
+Pickle* SessionCommand::PayloadAsPickle() const {
+ return new Pickle(contents(), static_cast<int>(size()));
+}
diff --git a/chrome/browser/sessions/session_command.h b/chrome/browser/sessions/session_command.h
new file mode 100644
index 0000000..beb7d85
--- /dev/null
+++ b/chrome/browser/sessions/session_command.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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_SESSION_COMMAND_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_COMMAND_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class Pickle;
+
+// SessionCommand contains a command id and arbitrary chunk of data. The id
+// and chunk of data are specific to the service creating them.
+//
+// Both TabRestoreService and SessionService use SessionCommands to represent
+// state on disk.
+//
+// There are two ways to create a SessionCommand:
+// . Specificy the size of the data block to create. This is useful for
+// commands that have a fixed size.
+// . From a pickle, this is useful for commands whose length varies.
+class SessionCommand {
+ public:
+ // These get written to disk, so we define types for them.
+ // Type for the identifier.
+ typedef uint8 id_type;
+ // Type for writing the size.
+ typedef uint16 size_type;
+
+ // Creates a session command with the specified id. This allocates a buffer
+ // of size |size| that must be filled via contents().
+ SessionCommand(id_type id, size_type size);
+
+ // Convenience constructor that creates a session command with the specified
+ // id whose contents is populated from the contents of pickle.
+ SessionCommand(id_type id, const Pickle& pickle);
+
+ // The contents of the command.
+ char* contents() { return &(contents_[0]); }
+ const char* contents() const { return &(contents_[0]); }
+
+ // Identifier for the command.
+ id_type id() const { return id_; }
+
+ // Size of data.
+ size_type size() const { return static_cast<size_type>(contents_.size()); }
+
+ // Convenience for extracting the data to a target. Returns false if
+ // count is not equal to the size of data this command contains.
+ bool GetPayload(void* dest, size_t count) const;
+
+ // Returns the contents as a pickle. It is up to the caller to delete the
+ // returned Pickle. The returned Pickle references the underlying data of
+ // this SessionCommand. If you need it to outlive the command, copy the
+ // pickle.
+ Pickle* PayloadAsPickle() const;
+
+ private:
+ const id_type id_;
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionCommand);
+};
+
+#endif // CHROME_BROWSER_SESSIONS_SESSION_COMMAND_H_
diff --git a/chrome/browser/sessions/session_id.cc b/chrome/browser/sessions/session_id.cc
new file mode 100644
index 0000000..fc7e249
--- /dev/null
+++ b/chrome/browser/sessions/session_id.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2006-2008 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.
+
+#include "chrome/browser/sessions/session_id.h"
+
+static SessionID::id_type next_id = 1;
+
+SessionID::SessionID() {
+ id_ = next_id++;
+}
diff --git a/chrome/browser/session_id.h b/chrome/browser/sessions/session_id.h
index 8ecf84e..bab33c85 100644
--- a/chrome/browser/session_id.h
+++ b/chrome/browser/sessions/session_id.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_SESSION_ID_H_
-#define CHROME_BROWSER_SESSION_ID_H_
+#ifndef CHROME_BROWSER_SESSIONS_SESSION_ID_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_ID_H_
-// SessionID ------------------------------------------------------------------
+#include "base/basictypes.h"
-// Uniquely identifies a session, tab or window.
+class SessionService;
+// Uniquely identifies a tab or window for the duration of a session.
class SessionID {
- friend class SessionService;
public:
typedef int32 id_type;
@@ -20,10 +20,9 @@ class SessionID {
// Returns the underlying id.
id_type id() const { return id_; }
- // Returns true if the two commands are equal.
- bool Equals(const SessionID& other) const;
-
private:
+ friend class SessionService;
+
explicit SessionID(id_type id) : id_(id) {}
// Resets the id. This is used when restoring a session
@@ -32,5 +31,4 @@ class SessionID {
id_type id_;
};
-#endif // CHROME_BROWSER_SESSION_ID_H_
-
+#endif // CHROME_BROWSER_SESSIONS_SESSION_ID_H_
diff --git a/chrome/browser/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 258ed7c..74038ce 100644
--- a/chrome/browser/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/session_restore.h"
+#include "chrome/browser/sessions/session_restore.h"
#include <vector>
@@ -12,7 +12,8 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/profile.h"
-#include "chrome/browser/session_service.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
@@ -182,14 +183,12 @@ class SessionRestoreImpl : public NotificationObserver {
public:
SessionRestoreImpl(Profile* profile,
Browser* browser,
- bool use_saved_session,
bool synchronous,
bool clobber_existing_window,
bool always_create_tabbed_browser,
const std::vector<GURL>& urls_to_open)
: profile_(profile),
browser_(browser),
- use_saved_session_(use_saved_session),
synchronous_(synchronous),
clobber_existing_window_(clobber_existing_window),
always_create_tabbed_browser_(always_create_tabbed_browser),
@@ -199,12 +198,9 @@ class SessionRestoreImpl : public NotificationObserver {
void SessionRestoreImpl::Restore() {
SessionService* session_service = profile_->GetSessionService();
DCHECK(session_service);
- SessionService::SavedSessionCallback* callback =
+ SessionService::LastSessionCallback* callback =
NewCallback(this, &SessionRestoreImpl::OnGotSession);
- if (use_saved_session_)
- session_service->GetSavedSession(&request_consumer_, callback);
- else
- session_service->GetLastSession(&request_consumer_, callback);
+ session_service->GetLastSession(&request_consumer_, callback);
if (synchronous_) {
MessageLoop::current()->Run();
@@ -384,10 +380,6 @@ class SessionRestoreImpl : public NotificationObserver {
// The first browser to restore to, may be null.
Browser* browser_;
- // Whether we're restoring the saved session (true) or the last session
- // (false).
- const bool use_saved_session_;
-
// Whether or not restore is synchronous.
const bool synchronous_;
@@ -420,7 +412,6 @@ size_t SessionRestore::num_tabs_to_load_ = 0;
static void Restore(Profile* profile,
Browser* browser,
- bool use_saved_session,
bool synchronous,
bool clobber_existing_window,
bool always_create_tabbed_browser,
@@ -430,28 +421,25 @@ static void Restore(Profile* profile,
return;
// SessionRestoreImpl takes care of deleting itself when done.
SessionRestoreImpl* restorer =
- new SessionRestoreImpl(profile, browser, use_saved_session,
- synchronous, clobber_existing_window,
- always_create_tabbed_browser,
- urls_to_open);
+ new SessionRestoreImpl(profile, browser, synchronous,
+ clobber_existing_window,
+ always_create_tabbed_browser, urls_to_open);
restorer->Restore();
}
// static
void SessionRestore::RestoreSession(Profile* profile,
Browser* browser,
- bool use_saved_session,
bool clobber_existing_window,
bool always_create_tabbed_browser,
const std::vector<GURL>& urls_to_open) {
- Restore(profile, browser, use_saved_session, false, clobber_existing_window,
+ Restore(profile, browser, false, clobber_existing_window,
always_create_tabbed_browser, urls_to_open);
}
// static
void SessionRestore::RestoreSessionSynchronously(
Profile* profile,
- bool use_saved_session,
const std::vector<GURL>& urls_to_open) {
- Restore(profile, NULL, use_saved_session, true, false, true, urls_to_open);
+ Restore(profile, NULL, true, false, true, urls_to_open);
}
diff --git a/chrome/browser/session_restore.h b/chrome/browser/sessions/session_restore.h
index 9ebe7d9..eb44d9a 100644
--- a/chrome/browser/session_restore.h
+++ b/chrome/browser/sessions/session_restore.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_SESSION_RESTORE_H__
-#define CHROME_BROWSER_SESSION_RESTORE_H__
+#ifndef CHROME_BROWSER_SESSIONS_SESSION_RESTORE_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_RESTORE_H_
#include <vector>
@@ -30,7 +30,6 @@ class SessionRestore {
// If urls_to_open is non-empty, a tab is added for each of the URLs.
static void RestoreSession(Profile* profile,
Browser* browser,
- bool use_saved_session,
bool clobber_existing_window,
bool always_create_tabbed_browser,
const std::vector<GURL>& urls_to_open);
@@ -41,7 +40,6 @@ class SessionRestore {
// If urls_to_open is non-empty, a tab is added for each of the URLs.
static void RestoreSessionSynchronously(
Profile* profile,
- bool use_saved_session,
const std::vector<GURL>& urls_to_open);
// The max number of non-selected tabs SessionRestore loads when restoring
@@ -51,8 +49,7 @@ class SessionRestore {
private:
SessionRestore();
- DISALLOW_EVIL_CONSTRUCTORS(SessionRestore);
+ DISALLOW_COPY_AND_ASSIGN(SessionRestore);
};
-#endif // CHROME_BROWSER_SESSION_RESTORE_H__
-
+#endif // CHROME_BROWSER_SESSIONS_SESSION_RESTORE_H_
diff --git a/chrome/browser/session_restore_uitest.cc b/chrome/browser/sessions/session_restore_uitest.cc
index f93098a..f93098a 100644
--- a/chrome/browser/session_restore_uitest.cc
+++ b/chrome/browser/sessions/session_restore_uitest.cc
diff --git a/chrome/browser/session_service.cc b/chrome/browser/sessions/session_service.cc
index fa11168..6016651 100644
--- a/chrome/browser/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/session_service.h"
+#include "chrome/browser/sessions/session_service.h"
#include <limits>
@@ -17,9 +17,10 @@
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile.h"
-#include "chrome/browser/session_backend.h"
-#include "chrome/browser/session_restore.h"
#include "chrome/browser/session_startup_pref.h"
+#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
@@ -47,18 +48,32 @@ static const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
static const SessionCommand::id_type
kCommandTabNavigationPathPrunedFromFront = 11;
-// Max number of navigation entries in each direction we'll persist.
-static const int kMaxNavigationCountToPersist = 6;
-
-// Delay between when a command is received, and when we save it to the
-// backend.
-static const int kSaveDelayMS = 2500;
-
// Every kWritesPerReset commands triggers recreating the file.
static const int kWritesPerReset = 250;
namespace {
+// The callback from GetLastSession is internally routed to SessionService
+// first and then the caller. This is done so that the SessionWindows can be
+// recreated from the SessionCommands and the SessionWindows passed to the
+// caller. The following class is used for this.
+class InternalLastSessionRequest :
+ public BaseSessionService::InternalGetCommandsRequest {
+ public:
+ InternalLastSessionRequest(
+ CallbackType* callback,
+ SessionService::LastSessionCallback* real_callback)
+ : BaseSessionService::InternalGetCommandsRequest(callback),
+ real_callback(real_callback) {
+ }
+
+ // The callback supplied to GetLastSession.
+ scoped_ptr<SessionService::LastSessionCallback> real_callback;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InternalLastSessionRequest);
+};
+
// Various payload structures.
struct ClosedPayload {
SessionID::id_type id;
@@ -91,75 +106,26 @@ typedef IDAndIndexPayload WindowTypePayload;
typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
-// Helper used by CreateUpdateTabNavigationCommand(). It writes |str| to
-// |pickle|, if and only if |str| fits within (|max_bytes| - |*bytes_written|).
-// |bytes_written| is incremented to reflect the data written.
-void WriteStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
- const std::string& str) {
- int num_bytes = str.size() * sizeof(char);
- if (*bytes_written + num_bytes < max_bytes) {
- *bytes_written += num_bytes;
- pickle.WriteString(str);
- } else {
- pickle.WriteString(std::string());
- }
-}
-
-// Wide version of WriteStringToPickle.
-void WriteWStringToPickle(Pickle& pickle, int* bytes_written, int max_bytes,
- const std::wstring& str) {
- int num_bytes = str.size() * sizeof(wchar_t);
- if (*bytes_written + num_bytes < max_bytes) {
- *bytes_written += num_bytes;
- pickle.WriteWString(str);
- } else {
- pickle.WriteWString(std::wstring());
- }
-}
-
} // namespace
-// SessionID ------------------------------------------------------------------
-
-static SessionID::id_type next_id = 1;
-
-SessionID::SessionID() {
- id_ = next_id++;
-}
-
// SessionService -------------------------------------------------------------
SessionService::SessionService(Profile* profile)
- : profile_(profile),
-#pragma warning(suppress: 4355) // Okay to pass "this" here.
- save_factory_(this),
- pending_reset_(false),
+ : BaseSessionService(SESSION_RESTORE, profile, std::wstring()),
has_open_tabbed_browsers_(false),
move_on_new_browser_(false) {
- DCHECK(profile);
- // We should never be created when off the record.
- DCHECK(!profile->IsOffTheRecord());
- Init(profile->GetPath());
+ Init();
}
SessionService::SessionService(const std::wstring& save_path)
- : profile_(NULL),
-#pragma warning(suppress: 4355) // Okay to pass "this" here.
- save_factory_(this),
- pending_reset_(false),
+ : BaseSessionService(SESSION_RESTORE, NULL, save_path),
has_open_tabbed_browsers_(false),
move_on_new_browser_(false) {
- Init(save_path);
+ Init();
}
SessionService::~SessionService() {
- if (!backend_.get())
- return;
Save();
- // If no pending requests, then the backend closes immediately and is
- // deleted. Otherwise the backend is deleted after all pending requests on
- // the file thread complete, which is done before the process exits.
- backend_ = NULL;
// Unregister our notifications.
NotificationService::current()->RemoveObserver(
@@ -187,11 +153,11 @@ void SessionService::MoveCurrentSessionToLastSession() {
Save();
- if (!backend_thread_) {
- backend_->MoveCurrentSessionToLastSession();
+ if (!backend_thread()) {
+ backend()->MoveCurrentSessionToLastSession();
} else {
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::MoveCurrentSessionToLastSession));
+ backend_thread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ backend(), &SessionBackend::MoveCurrentSessionToLastSession));
}
}
@@ -363,7 +329,8 @@ void SessionService::UpdateTabNavigation(const SessionID& window_id,
range.first = std::min(index, range.first);
range.second = std::max(index, range.second);
}
- ScheduleCommand(CreateUpdateTabNavigationCommand(tab_id, index, entry));
+ ScheduleCommand(CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
+ tab_id.id(), index, entry));
}
void SessionService::TabRestored(NavigationController* controller) {
@@ -371,7 +338,7 @@ void SessionService::TabRestored(NavigationController* controller) {
return;
BuildCommandsForTab(controller->window_id(), controller, -1,
- &pending_commands_, NULL);
+ &pending_commands(), NULL);
StartSaveTimer();
}
@@ -402,49 +369,16 @@ void SessionService::SetSelectedTabInWindow(const SessionID& window_id,
ScheduleCommand(CreateSetSelectedTabInWindow(window_id, index));
}
-SessionService::Handle SessionService::GetSavedSession(
- CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback) {
- return GetSessionImpl(consumer, callback, true);
-}
-
SessionService::Handle SessionService::GetLastSession(
CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback) {
- return GetSessionImpl(consumer, callback, false);
-}
-
-void SessionService::CreateSavedSession() {
- std::vector<SessionCommand*> commands;
- // Commands are freed by backend.
- BuildCommandsFromBrowsers(&commands, NULL, NULL);
- if (!backend_thread_) {
- backend_->SaveSession(commands);
- } else {
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::SaveSession, commands));
- }
-}
-
-void SessionService::DeleteSession(bool saved_session) {
- if (!backend_thread_) {
- backend_->DeleteSession(saved_session);
- } else {
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::DeleteSession, saved_session));
- }
+ LastSessionCallback* callback) {
+ return ScheduleGetLastSessionCommands(
+ new InternalLastSessionRequest(
+ NewCallback(this, &SessionService::OnGotLastSessionCommands),
+ callback), consumer);
}
-void SessionService::CopyLastSessionToSavedSession() {
- if (!backend_thread_) {
- backend_->CopyLastSessionToSavedSession();
- } else {
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::CopyLastSessionToSavedSession));
- }
-}
-
-void SessionService::Init(const std::wstring& path) {
+void SessionService::Init() {
// Register for the notifications we're interested in.
NotificationService::current()->AddObserver(
this, NOTIFY_TAB_PARENTED, NotificationService::AllSources());
@@ -458,14 +392,6 @@ void SessionService::Init(const std::wstring& path) {
this, NOTIFY_NAV_ENTRY_COMMITTED, NotificationService::AllSources());
NotificationService::current()->AddObserver(
this, NOTIFY_BROWSER_OPENED, NotificationService::AllSources());
-
- DCHECK(!path.empty());
- commands_since_reset_ = 0;
- backend_ = new SessionBackend(path);
- backend_thread_ = g_browser_process->file_thread();
- if (!backend_thread_)
- backend_->Init();
- // If backend_thread, backend will init itself as appropriate.
}
void SessionService::Observe(NotificationType type,
@@ -475,7 +401,7 @@ void SessionService::Observe(NotificationType type,
switch (type) {
case NOTIFY_BROWSER_OPENED: {
Browser* browser = Source<Browser>(source).ptr();
- if (browser->profile() != profile_ ||
+ if (browser->profile() != profile() ||
!should_track_changes_for_browser_type(browser->type())) {
return;
}
@@ -488,10 +414,10 @@ void SessionService::Observe(NotificationType type,
MoveCurrentSessionToLastSession();
move_on_new_browser_ = false;
}
- SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile_);
+ SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile());
if (pref.type == SessionStartupPref::LAST) {
SessionRestore::RestoreSession(
- profile_, browser, false, false, false, std::vector<GURL>());
+ profile(), browser, false, false, std::vector<GURL>());
}
}
SetWindowType(browser->session_id(), browser->type());
@@ -555,21 +481,6 @@ void SessionService::Observe(NotificationType type,
}
}
-SessionService::Handle SessionService::GetSessionImpl(
- CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback,
- bool is_saved_session) {
- scoped_refptr<InternalSavedSessionRequest> request(
- new InternalSavedSessionRequest(
- NewCallback(this, &SessionService::OnGotSessionCommands),
- callback,
- is_saved_session));
- AddRequest(request.get(), consumer);
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::ReadSession, request));
- return request->handle();
-}
-
SessionCommand* SessionService::CreateSetSelectedTabInWindow(
const SessionID& window_id,
int index) {
@@ -649,46 +560,6 @@ SessionCommand* SessionService::CreateWindowClosedCommand(
return command;
}
-SessionCommand* SessionService::CreateUpdateTabNavigationCommand(
- const SessionID& tab_id,
- int index,
- const NavigationEntry& entry) {
- // Use pickle to handle marshalling.
- Pickle pickle;
- pickle.WriteInt(tab_id.id());
- pickle.WriteInt(index);
-
- // We only allow navigations up to 63k (which should be completely
- // reasonable). On the off chance we get one that is too big, try to
- // keep the url.
-
- // Bound the string data (which is variable length) to
- // |max_state_size bytes| bytes.
- static const SessionCommand::size_type max_state_size =
- std::numeric_limits<SessionCommand::size_type>::max() - 1024;
-
- int bytes_written = 0;
-
- WriteStringToPickle(pickle, &bytes_written, max_state_size,
- entry.display_url().spec());
-
- WriteWStringToPickle(pickle, &bytes_written, max_state_size,
- entry.title());
-
- WriteStringToPickle(pickle, &bytes_written, max_state_size,
- entry.content_state());
-
- pickle.WriteInt(entry.transition_type());
- int type_mask = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0;
- pickle.WriteInt(type_mask);
-
- WriteStringToPickle(pickle, &bytes_written, max_state_size,
- entry.referrer().is_valid() ? entry.referrer().spec() : std::string());
-
- // Adding more data? Be sure and update TabRestoreService too.
- return new SessionCommand(kCommandUpdateTabNavigation, pickle);
-}
-
SessionCommand* SessionService::CreateSetSelectedNavigationIndexCommand(
const SessionID& tab_id,
int index) {
@@ -713,17 +584,18 @@ SessionCommand* SessionService::CreateSetWindowTypeCommand(
return command;
}
-void SessionService::OnGotSessionCommands(
+void SessionService::OnGotLastSessionCommands(
Handle handle,
- scoped_refptr<InternalSavedSessionRequest> request) {
+ scoped_refptr<InternalGetCommandsRequest> request) {
if (request->canceled())
return;
ScopedVector<SessionWindow> valid_windows;
RestoreSessionFromCommands(
request->commands, &(valid_windows.get()));
- request->real_callback->RunWithParams(
- SavedSessionCallback::TupleType(request->handle(),
- &(valid_windows.get())));
+ static_cast<InternalLastSessionRequest*>(request.get())->
+ real_callback->RunWithParams(
+ LastSessionCallback::TupleType(request->handle(),
+ &(valid_windows.get())));
}
void SessionService::RestoreSessionFromCommands(
@@ -793,7 +665,7 @@ std::vector<TabNavigation>::iterator
DCHECK(navigations);
for (std::vector<TabNavigation>::iterator i = navigations->begin();
i != navigations->end(); ++i) {
- if (i->index >= index)
+ if (i->index() >= index)
return i;
}
return navigations->end();
@@ -952,8 +824,8 @@ bool SessionService::CreateTabsAndWindows(
// And update the index of existing navigations.
for (std::vector<TabNavigation>::iterator i = tab->navigations.begin();
i != tab->navigations.end();) {
- i->index -= payload.index;
- if (i->index < 0)
+ i->set_index(i->index() - payload.index);
+ if (i->index() < 0)
i = tab->navigations.erase(i);
else
++i;
@@ -962,42 +834,16 @@ bool SessionService::CreateTabsAndWindows(
}
case kCommandUpdateTabNavigation: {
- scoped_ptr<Pickle> pickle(command->PayloadAsPickle());
- if (!pickle.get())
- return true;
TabNavigation navigation;
SessionID::id_type tab_id;
- void* iterator = NULL;
- std::string url_spec;
- if (!pickle->ReadInt(&iterator, &tab_id) ||
- !pickle->ReadInt(&iterator, &(navigation.index)) ||
- !pickle->ReadString(&iterator, &url_spec) ||
- !pickle->ReadWString(&iterator, &(navigation.title)) ||
- !pickle->ReadString(&iterator, &(navigation.state)) ||
- !pickle->ReadInt(&iterator,
- reinterpret_cast<int*>(&(navigation.transition))))
+ if (!RestoreUpdateTabNavigationCommand(*command, &navigation, &tab_id))
return true;
- // type_mask did not always exist in the written stream. As such, we
- // don't fail if it can't be read.
- bool has_type_mask =
- pickle->ReadInt(&iterator, &(navigation.type_mask));
-
- if (has_type_mask) {
- // the "referrer" property was added after type_mask to the written
- // stream. As such, we don't fail if it can't be read.
- std::string referrer_spec;
- pickle->ReadString(&iterator, &referrer_spec);
- if (!referrer_spec.empty()) {
- navigation.referrer = GURL(referrer_spec);
- }
- }
- navigation.url = GURL(url_spec);
SessionTab* tab = GetTab(tab_id, tabs);
std::vector<TabNavigation>::iterator i =
FindClosestNavigationWithIndex(&(tab->navigations),
- navigation.index);
- if (i != tab->navigations.end() && i->index == navigation.index)
+ navigation.index());
+ if (i != tab->navigations.end() && i->index() == navigation.index())
*i = navigation;
else
tab->navigations.insert(i, navigation);
@@ -1048,8 +894,8 @@ void SessionService::BuildCommandsForTab(
CreateSetTabWindowCommand(window_id, controller->session_id()));
const int current_index = controller->GetCurrentEntryIndex();
const int min_index = std::max(0,
- current_index - kMaxNavigationCountToPersist);
- const int max_index = std::min(current_index + kMaxNavigationCountToPersist,
+ current_index - max_persist_navigation_count);
+ const int max_index = std::min(current_index + max_persist_navigation_count,
controller->GetEntryCount());
const int pending_index = controller->GetPendingEntryIndex();
if (tab_to_available_range) {
@@ -1062,7 +908,8 @@ void SessionService::BuildCommandsForTab(
DCHECK(entry);
if (ShouldTrackEntry(*entry)) {
commands->push_back(
- CreateUpdateTabNavigationCommand(controller->session_id(),
+ CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
+ controller->session_id().id(),
i,
*entry));
}
@@ -1098,7 +945,7 @@ void SessionService::BuildCommandsForBrowser(
for (int i = 0; i < browser->tab_count(); ++i) {
TabContents* tab = browser->GetTabContentsAt(i);
DCHECK(tab);
- if (tab->profile() == profile_) {
+ if (tab->profile() == profile()) {
BuildCommandsForTab(browser->session_id(), tab->controller(), i,
commands, tab_to_available_range);
if (windows_to_track && !added_to_windows_to_track) {
@@ -1134,11 +981,11 @@ void SessionService::BuildCommandsFromBrowsers(
}
void SessionService::ScheduleReset() {
- pending_reset_ = true;
- STLDeleteElements(&pending_commands_);
+ set_pending_reset(true);
+ STLDeleteElements(&pending_commands());
tab_to_available_range_.clear();
windows_tracking_.clear();
- BuildCommandsFromBrowsers(&pending_commands_, &tab_to_available_range_,
+ BuildCommandsFromBrowsers(&pending_commands(), &tab_to_available_range_,
&windows_tracking_);
if (!windows_tracking_.empty()) {
// We're lazily created on startup and won't get an initial batch of
@@ -1164,7 +1011,7 @@ bool SessionService::ReplacePendingCommand(SessionCommand* command) {
return false;
}
for (std::vector<SessionCommand*>::reverse_iterator i =
- pending_commands_.rbegin(); i != pending_commands_.rend(); ++i) {
+ pending_commands().rbegin(); i != pending_commands().rend(); ++i) {
SessionCommand* existing_command = *i;
if (existing_command->id() == kCommandUpdateTabNavigation) {
SessionID::id_type existing_tab_id;
@@ -1181,8 +1028,8 @@ bool SessionService::ReplacePendingCommand(SessionCommand* command) {
// it with the new one. We need to add to the end of the list just in
// case there is a prune command after the update command.
delete existing_command;
- pending_commands_.erase(i.base() - 1);
- pending_commands_.push_back(command);
+ pending_commands().erase(i.base() - 1);
+ pending_commands().push_back(command);
return true;
}
return false;
@@ -1195,17 +1042,15 @@ void SessionService::ScheduleCommand(SessionCommand* command) {
DCHECK(command);
if (ReplacePendingCommand(command))
return;
- commands_since_reset_++;
- pending_commands_.push_back(command);
+ BaseSessionService::ScheduleCommand(command);
// Don't schedule a reset on tab closed/window closed. Otherwise we may
// lose tabs/windows we want to restore from if we exit right after this.
- if (!pending_reset_ && pending_window_close_ids_.empty() &&
- commands_since_reset_ >= kWritesPerReset &&
+ if (!pending_reset() && pending_window_close_ids_.empty() &&
+ commands_since_reset() >= kWritesPerReset &&
(command->id() != kCommandTabClosed &&
command->id() != kCommandWindowClosed)) {
ScheduleReset();
}
- StartSaveTimer();
}
void SessionService::CommitPendingCloses() {
@@ -1222,39 +1067,8 @@ void SessionService::CommitPendingCloses() {
pending_window_close_ids_.clear();
}
-void SessionService::Save() {
- DCHECK(backend_.get());
-
- if (pending_commands_.empty())
- return;
-
- if (!backend_thread_) {
- backend_->AppendCommands(
- new std::vector<SessionCommand*>(pending_commands_), pending_reset_);
- } else {
- backend_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- backend_.get(), &SessionBackend::AppendCommands,
- new std::vector<SessionCommand*>(pending_commands_),
- pending_reset_));
- }
- if (pending_reset_) {
- commands_since_reset_ = 0;
- pending_reset_ = false;
- }
- // Backend took ownership of commands.
- pending_commands_.clear();
-}
-
-void SessionService::StartSaveTimer() {
- // Don't start a timer when testing (profile == NULL).
- if (profile_ && save_factory_.empty()) {
- MessageLoop::current()->PostDelayedTask(FROM_HERE,
- save_factory_.NewRunnableMethod(&SessionService::Save), kSaveDelayMS);
- }
-}
-
bool SessionService::IsOnlyOneTabLeft() {
- if (!profile_) {
+ if (!profile()) {
// We're testing, always return false.
return false;
}
@@ -1266,7 +1080,7 @@ bool SessionService::IsOnlyOneTabLeft() {
i != BrowserList::end(); ++i) {
const SessionID::id_type window_id = (*i)->session_id().id();
if (should_track_changes_for_browser_type((*i)->type()) &&
- (*i)->profile()->GetOriginalProfile() == profile_ &&
+ (*i)->profile()->GetOriginalProfile() == profile() &&
window_closing_ids_.find(window_id) == window_closing_ids_.end()) {
if (++window_count > 1)
return false;
@@ -1280,7 +1094,7 @@ bool SessionService::IsOnlyOneTabLeft() {
}
bool SessionService::HasOpenTabbedBrowsers(const SessionID& window_id) {
- if (!profile_) {
+ if (!profile()) {
// We're testing, always return false.
return true;
}
@@ -1294,7 +1108,7 @@ bool SessionService::HasOpenTabbedBrowsers(const SessionID& window_id) {
if (browser_id != window_id.id() &&
window_closing_ids_.find(browser_id) == window_closing_ids_.end() &&
should_track_changes_for_browser_type(browser->type()) &&
- browser->profile()->GetOriginalProfile() == profile_) {
+ browser->profile()->GetOriginalProfile() == profile()) {
return true;
}
}
@@ -1304,15 +1118,3 @@ bool SessionService::HasOpenTabbedBrowsers(const SessionID& window_id) {
bool SessionService::ShouldTrackChangesToWindow(const SessionID& window_id) {
return windows_tracking_.find(window_id.id()) != windows_tracking_.end();
}
-
-bool SessionService::ShouldTrackEntry(const NavigationEntry& entry) {
- // Don't track entries that have post data. Post data may contain passwords
- // and other sensitive data users don't want stored to disk.
- return entry.display_url().is_valid() && !entry.has_post_data();
-}
-
-// InternalSavedSessionRequest ------------------------------------------------
-
-SessionService::InternalSavedSessionRequest::~InternalSavedSessionRequest() {
- STLDeleteElements(&commands);
-}
diff --git a/chrome/browser/session_service.h b/chrome/browser/sessions/session_service.h
index 2ff8608..1c3402d3 100644
--- a/chrome/browser/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -2,165 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_SESSION_SERVICE_H__
-#define CHROME_BROWSER_SESSION_SERVICE_H__
+#ifndef CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
#include <map>
#include "base/basictypes.h"
-#include "base/gfx/rect.h"
-#include "base/task.h"
-#include "base/time.h"
#include "chrome/browser/browser.h"
-#include "chrome/browser/cancelable_request.h"
-#include "chrome/browser/session_id.h"
+#include "chrome/browser/sessions/base_session_service.h"
+#include "chrome/browser/sessions/session_id.h"
#include "chrome/common/notification_service.h"
-#include "chrome/common/page_transition_types.h"
-#include "chrome/common/stl_util-inl.h"
-#include "googleurl/src/gurl.h"
class Browser;
class NavigationController;
class NavigationEntry;
class Profile;
-class TabContents;
-class SessionBackend;
class SessionCommand;
-
-namespace base {
-class Thread;
-}
-
-// TabNavigation ------------------------------------------------------------
-
-// TabNavigation corresponds to a NavigationEntry.
-
-struct TabNavigation {
- friend class SessionService;
-
- enum TypeMask {
- HAS_POST_DATA = 1
- };
-
- TabNavigation() : transition(PageTransition::TYPED), type_mask(0), index(-1) {
- }
- TabNavigation(int index,
- const GURL& url,
- const GURL& referrer,
- const std::wstring& title,
- const std::string& state,
- PageTransition::Type transition)
- : url(url),
- referrer(referrer),
- title(title),
- state(state),
- transition(transition),
- type_mask(0),
- index(index) {}
-
-
- GURL url;
- GURL referrer;
-
- // The title of the page.
- std::wstring title;
- std::string state;
- PageTransition::Type transition;
-
- // A mask used for arbitrary boolean values needed to represent a
- // NavigationEntry. Currently only contains HAS_POST_DATA or 0.
- int type_mask;
-
- private:
- // The index in the NavigationController. If this is -1, it means this
- // TabNavigation is bogus.
- //
- // This is used when determining the selected TabNavigation and only useful
- // by SessionService.
- int index;
-};
-
-// SessionTab ----------------------------------------------------------------
-
-// SessionTab corresponds to a NavigationController.
-
-struct SessionTab {
- SessionTab() : tab_visual_index(-1), current_navigation_index(-1) { }
-
- // Unique id of the window.
- SessionID window_id;
-
- // Unique if of the tab.
- SessionID tab_id;
-
- // Visual index of the tab within its window. There may be gaps in these
- // values.
- //
- // NOTE: this is really only useful for the SessionService during
- // restore, others can likely ignore this and use the order of the
- // tabs in SessionWindow.tabs.
- int tab_visual_index;
-
- // Identifies the index of the current navigation in navigations. For
- // example, if this is 2 it means the current navigation is navigations[2].
- //
- // NOTE: when the service is creating SessionTabs, initially this
- // corresponds to TabNavigation.index, not the index in navigations. When done
- // creating though, this is set to the index in navigations.
- int current_navigation_index;
-
- std::vector<TabNavigation> navigations;
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(SessionTab);
-};
-
-// SessionWindow -------------------------------------------------------------
-
-// Describes a saved window.
-
-struct SessionWindow {
- SessionWindow()
- : selected_tab_index(-1),
- type(Browser::TYPE_NORMAL),
- is_constrained(true),
- is_maximized(false) {}
- ~SessionWindow() { STLDeleteElements(&tabs); }
-
- // Identifier of the window.
- SessionID window_id;
-
- // Bounds of the window.
- gfx::Rect bounds;
-
- // Index of the selected tab in tabs; -1 if no tab is selected. After restore
- // this value is guaranteed to be a valid index into tabs.
- //
- // NOTE: when the service is creating SessionWindows, initially this
- // corresponds to SessionTab.tab_visual_index, not the index in
- // tabs. When done creating though, this is set to the index in
- // tabs.
- int selected_tab_index;
-
- // Type of the browser. Currently we only store browsers of type
- // TYPE_NORMAL and TYPE_POPUP.
- Browser::Type type;
-
- // If true, the window is constrained.
- //
- // Currently SessionService prunes all constrained windows so that session
- // restore does not attempt to restore them.
- bool is_constrained;
-
- // The tabs, ordered by visual order.
- std::vector<SessionTab*> tabs;
-
- // Is the window maximized?
- bool is_maximized;
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(SessionWindow);
-};
+struct SessionTab;
+struct SessionWindow;
// SessionService ------------------------------------------------------------
@@ -168,19 +27,12 @@ struct SessionWindow {
// and tabs so that they can be restored at a later date. The state of the
// currently open browsers is referred to as the current session.
//
-// SessionService supports restoring from two distinct points (or sessions):
-// . The previous or last session. The previous session typically corresponds
-// to the last run of the browser, but not always. For example, if the user
-// has a tabbed browser and app window running, closes the tabbed browser,
-// then creates a new tabbed browser the current session is made the last
-// session and the current session reset. This is done to provide the
-// illusion that app windows run in separate processes.
-// . A user defined point. That is, any time CreateSavedSession is invoked
-// the save session is reset from the current state of the browser.
-//
-// Additionally the current session can be made the 'last' session at any point
-// by way of MoveCurrentSessionToLastSession. This may be done at certain points
-// during the browser that are viewed as changing the
+// SessionService supports restoring from the previous or last session. The
+// previous session typically corresponds to the last run of the browser, but
+// not always. For example, if the user has a tabbed browser and app window
+// running, closes the tabbed browser, then creates a new tabbed browser the
+// current session is made the last session and the current session reset. This
+// is done to provide the illusion that app windows run in separate processes.
//
// SessionService itself maintains a set of SessionCommands that allow
// SessionService to rebuild the open state of the browser (as
@@ -188,10 +40,8 @@ struct SessionWindow {
// flushed to SessionBackend and written to a file. Every so often
// SessionService rebuilds the contents of the file from the open state
// of the browser.
-
-class SessionService : public CancelableRequestProvider,
- public NotificationObserver,
- public base::RefCountedThreadSafe<SessionService> {
+class SessionService : public BaseSessionService,
+ public NotificationObserver {
friend class SessionServiceTestHelper;
public:
// Creates a SessionService for the specified profile.
@@ -199,7 +49,7 @@ class SessionService : public CancelableRequestProvider,
// For testing.
explicit SessionService(const std::wstring& save_path);
- ~SessionService();
+ virtual ~SessionService();
// Resets the contents of the file from the current state of all open
// browsers whose profile matches our profile.
@@ -279,84 +129,29 @@ class SessionService : public CancelableRequestProvider,
//
// The time gives the time the session was closed.
typedef Callback2<Handle, std::vector<SessionWindow*>*>::Type
- SavedSessionCallback;
-
- // Fetches the contents of the save session, notifying the callback when
- // done. If the callback is supplied an empty vector of SessionWindows
- // it means the session could not be restored.
- Handle GetSavedSession(CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback);
+ LastSessionCallback;
// Fetches the contents of the last session, notifying the callback when
// done. If the callback is supplied an empty vector of SessionWindows
// it means the session could not be restored.
+ //
+ // The created request does NOT directly invoke the callback, rather the
+ // callback invokes OnGotSessionCommands from which we map the
+ // SessionCommands to browser state, then notify the callback.
Handle GetLastSession(CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback);
-
- // Creates a save session from the current state of the browser.
- void CreateSavedSession();
-
- // Deletes the saved session if saved session is true, or the last session
- // if saved_session is false.
- void DeleteSession(bool saved_session);
-
- // Creates a saved session from the contents of the last session.
- void CopyLastSessionToSavedSession();
-
- // The callback from Get*Session is internally routed to SessionService
- // first. This is done so that the SessionWindows can be recreated from
- // the SessionCommands. The following types are used for this.
- class InternalSavedSessionRequest;
-
- typedef Callback2<Handle, scoped_refptr<InternalSavedSessionRequest> >::Type
- InternalSavedSessionCallback;
-
- // Request class used from Get*Session.
- class InternalSavedSessionRequest :
- public CancelableRequest<InternalSavedSessionCallback> {
- public:
- InternalSavedSessionRequest(CallbackType* callback,
- SavedSessionCallback* real_callback,
- bool is_saved_session)
- : CancelableRequest(callback),
- real_callback(real_callback),
- is_saved_session(is_saved_session) {
- }
- virtual ~InternalSavedSessionRequest();
-
- // The callback supplied to Get*Session.
- scoped_ptr<SavedSessionCallback> real_callback;
-
- // Whether the request is for a saved session, or the last session.
- bool is_saved_session;
-
- // The commands. The backend fills this in for us.
- std::vector<SessionCommand*> commands;
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(InternalSavedSessionRequest);
- };
+ LastSessionCallback* callback);
private:
typedef std::map<SessionID::id_type,std::pair<int,int> > IdToRange;
typedef std::map<SessionID::id_type,SessionTab*> IdToSessionTab;
typedef std::map<SessionID::id_type,SessionWindow*> IdToSessionWindow;
- // Various initialization; called from the constructor.
- void Init(const std::wstring& path);
+ void Init();
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
- // Get*Session call into this to schedule the request. The request
- // does NOT directly invoke the callback, rather the callback invokes
- // OnGotSessionCommands from which we map the SessionCommands to browser
- // state, then notify the callback.
- Handle GetSessionImpl(CancelableRequestConsumerBase* consumer,
- SavedSessionCallback* callback,
- bool is_saved_session);
-
// Methods to create the various commands. It is up to the caller to delete
// the returned the SessionCommand* object.
SessionCommand* CreateSetSelectedTabInWindow(const SessionID& window_id,
@@ -376,11 +171,6 @@ class SessionService : public CancelableRequestProvider,
SessionCommand* CreateWindowClosedCommand(SessionID::id_type tab_id);
- SessionCommand* CreateUpdateTabNavigationCommand(
- const SessionID& tab_id,
- int index,
- const NavigationEntry& entry);
-
SessionCommand* CreateSetSelectedNavigationIndexCommand(
const SessionID& tab_id,
int index);
@@ -391,9 +181,9 @@ class SessionService : public CancelableRequestProvider,
// Callback form the backend for getting the commands from the previous
// or save file. Converts the commands in SessionWindows and notifies
// the real callback.
- void OnGotSessionCommands(
+ void OnGotLastSessionCommands(
Handle handle,
- scoped_refptr<InternalSavedSessionRequest> request);
+ scoped_refptr<InternalGetCommandsRequest> request);
// Converts the commands into SessionWindows. On return any valid
// windows are added to valid_windows. It is up to the caller to delete
@@ -499,12 +289,6 @@ class SessionService : public CancelableRequestProvider,
// Converts all pending tab/window closes to commands and schedules them.
void CommitPendingCloses();
- // Saves pending commands to the backend.
- void Save();
-
- // Starts the save timer (if it isn't running already).
- void StartSaveTimer();
-
// Returns true if there is only one window open with a single tab that shares
// our profile.
bool IsOnlyOneTabLeft();
@@ -516,21 +300,11 @@ class SessionService : public CancelableRequestProvider,
// Returns true if changes to tabs in the specified window should be tracked.
bool ShouldTrackChangesToWindow(const SessionID& window_id);
- // Should we track the specified entry?
- bool SessionService::ShouldTrackEntry(const NavigationEntry& entry);
-
// Returns true if we track changes to the specified browser type.
static bool should_track_changes_for_browser_type(Browser::Type type) {
return type == Browser::TYPE_NORMAL;
}
- // The profile used to determine where to save, as well as what tabs
- // to persist.
- Profile* profile_;
-
- // The number of commands sent to the backend before doing a reset.
- int commands_since_reset_;
-
// Maps from session tab id to the range of navigation entries that has
// been written to disk.
//
@@ -538,16 +312,6 @@ class SessionService : public CancelableRequestProvider,
// written.
IdToRange tab_to_available_range_;
- // Commands we need to send over to the backend.
- std::vector<SessionCommand*> pending_commands_;
-
- // Whether the backend file should be recreated the next time we send
- // over the commands.
- bool pending_reset_;
-
- // Used to invoke Save.
- ScopedRunnableMethodFactory<SessionService> save_factory_;
-
// When the user closes the last window, where the last window is the
// last tabbed browser and no more tabbed browsers are open with the same
// profile, the window ID is added here. These IDs are only committed (which
@@ -570,13 +334,6 @@ class SessionService : public CancelableRequestProvider,
typedef std::set<SessionID::id_type> WindowsTracking;
WindowsTracking windows_tracking_;
- // The backend.
- scoped_refptr<SessionBackend> backend_;
-
- // Thread backend tasks are run on. This comes from the profile, and is
- // null during testing.
- base::Thread* backend_thread_;
-
// Are there any open open tabbed browsers?
bool has_open_tabbed_browsers_;
@@ -585,7 +342,8 @@ class SessionService : public CancelableRequestProvider,
// is made the previous session. See description above class for details on
// current/previou session.
bool move_on_new_browser_;
-};
-#endif // CHROME_BROWSER_SESSION_SERVICE_H__
+ DISALLOW_COPY_AND_ASSIGN(SessionService);
+};
+#endif // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
diff --git a/chrome/browser/session_service_test_helper.cc b/chrome/browser/sessions/session_service_test_helper.cc
index 4155d71..401c59c 100644
--- a/chrome/browser/session_service_test_helper.cc
+++ b/chrome/browser/sessions/session_service_test_helper.cc
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/session_service_test_helper.h"
+#include "chrome/browser/sessions/session_service_test_helper.h"
-#include "chrome/browser/session_backend.h"
-#include "chrome/browser/session_service.h"
+#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/common/scoped_vector.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,7 +34,7 @@ void SessionServiceTestHelper::ReadWindows(
std::vector<SessionWindow*>* windows) {
Time last_time;
ScopedVector<SessionCommand> read_commands;
- backend()->ReadSessionImpl(false, &(read_commands.get()));
+ backend()->ReadLastSessionCommandsImpl(&(read_commands.get()));
RestoreSessionFromCommands(read_commands.get(), windows);
}
@@ -60,12 +62,12 @@ void SessionServiceTestHelper::AssertTabEquals(
void SessionServiceTestHelper::AssertNavigationEquals(
const TabNavigation& expected,
const TabNavigation& actual) {
- EXPECT_TRUE(expected.url == actual.url);
- EXPECT_EQ(expected.referrer, actual.referrer);
- EXPECT_EQ(expected.title, actual.title);
- EXPECT_EQ(expected.state, actual.state);
- EXPECT_EQ(expected.transition, actual.transition);
- EXPECT_EQ(expected.type_mask, actual.type_mask);
+ EXPECT_TRUE(expected.url() == actual.url());
+ EXPECT_EQ(expected.referrer(), actual.referrer());
+ EXPECT_EQ(expected.title(), actual.title());
+ EXPECT_EQ(expected.state(), actual.state());
+ EXPECT_EQ(expected.transition(), actual.transition());
+ EXPECT_EQ(expected.type_mask(), actual.type_mask());
}
void SessionServiceTestHelper::AssertSingleWindowWithSingleTab(
@@ -77,6 +79,6 @@ void SessionServiceTestHelper::AssertSingleWindowWithSingleTab(
}
SessionBackend* SessionServiceTestHelper::backend() {
- return service_->backend_.get();
+ return service_->backend();
}
diff --git a/chrome/browser/session_service_test_helper.h b/chrome/browser/sessions/session_service_test_helper.h
index 80b9bb4..92a6db6 100644
--- a/chrome/browser/session_service_test_helper.h
+++ b/chrome/browser/sessions/session_service_test_helper.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_SESSION_SERVICE_TEST_HELPER_H__
-#define CHROME_BROWSER_SESSION_SERVICE_TEST_HELPER_H__
+#ifndef CHROME_BROWSER_SESSIONS_SERVICE_TEST_HELPER_H_
+#define CHROME_BROWSER_SESSIONS_SERVICE_TEST_HELPER_H_
#include <vector>
@@ -15,7 +15,7 @@ class SessionID;
class SessionService;
struct SessionTab;
struct SessionWindow;
-struct TabNavigation;
+class TabNavigation;
// A simple class that makes writing SessionService related tests easier.
@@ -64,8 +64,7 @@ class SessionServiceTestHelper {
private:
scoped_refptr<SessionService> service_;
- DISALLOW_EVIL_CONSTRUCTORS(SessionServiceTestHelper);
+ DISALLOW_COPY_AND_ASSIGN(SessionServiceTestHelper);
};
-#endif // CHROME_BROWSER_SESSION_SERVICE_TEST_HELPER_H__
-
+#endif // CHROME_BROWSER_SESSIONS_SERVICE_TEST_HELPER_H_
diff --git a/chrome/browser/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index fc2a2d7..1939c7e 100644
--- a/chrome/browser/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -7,9 +7,10 @@
#include "base/file_util.h"
#include "base/path_service.h"
#include "chrome/browser/navigation_entry.h"
-#include "chrome/browser/session_backend.h"
-#include "chrome/browser/session_service.h"
-#include "chrome/browser/session_service_test_helper.h"
+#include "chrome/browser/sessions/session_backend.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_service_test_helper.h"
+#include "chrome/browser/sessions/session_types.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/scoped_vector.h"
#include "chrome/common/stl_util-inl.h"
@@ -29,7 +30,8 @@ class SessionServiceTest : public testing::Test {
file_util::CreateDirectory(path_);
file_util::AppendToPath(&path_, b);
- helper_.set_service(new SessionService(path_));
+ SessionService* session_service = new SessionService(path_);
+ helper_.set_service(session_service);
service()->SetWindowType(window_id, Browser::TYPE_NORMAL);
service()->SetWindowBounds(window_id, window_bounds, false);
@@ -46,13 +48,13 @@ class SessionServiceTest : public testing::Test {
int index,
bool select) {
NavigationEntry entry(TAB_CONTENTS_UNKNOWN_TYPE);
- entry.set_url(navigation.url);
- entry.set_referrer(navigation.referrer);
- entry.set_title(navigation.title);
- entry.set_content_state(navigation.state);
- entry.set_transition_type(navigation.transition);
+ entry.set_url(navigation.url());
+ entry.set_referrer(navigation.referrer());
+ entry.set_title(navigation.title());
+ entry.set_content_state(navigation.state());
+ entry.set_transition_type(navigation.transition());
entry.set_has_post_data(
- navigation.type_mask & TabNavigation::HAS_POST_DATA);
+ navigation.type_mask() & TabNavigation::HAS_POST_DATA);
service()->UpdateTabNavigation(window_id, tab_id, index, entry);
if (select)
service()->SetSelectedNavigationIndex(window_id, tab_id, index);
@@ -62,7 +64,8 @@ class SessionServiceTest : public testing::Test {
// Forces closing the file.
helper_.set_service(NULL);
- helper_.set_service(new SessionService(path_));
+ SessionService* session_service = new SessionService(path_);
+ helper_.set_service(session_service);
helper_.ReadWindows(windows);
}
@@ -115,7 +118,7 @@ TEST_F(SessionServiceTest, PrunePostData1) {
TabNavigation nav1(0, GURL("http://google.com"), GURL(), L"abc", "def",
PageTransition::QUALIFIER_MASK);
- nav1.type_mask = TabNavigation::HAS_POST_DATA;
+ nav1.set_type_mask(TabNavigation::HAS_POST_DATA);
helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
UpdateNavigation(window_id, tab_id, nav1, 0, true);
@@ -135,7 +138,7 @@ TEST_F(SessionServiceTest, PrunePostData2) {
TabNavigation nav1(0, GURL("http://google.com"),
GURL("http://www.referrer.com"), L"abc", "def",
PageTransition::QUALIFIER_MASK);
- nav1.type_mask = TabNavigation::HAS_POST_DATA;
+ nav1.set_type_mask(TabNavigation::HAS_POST_DATA);
TabNavigation nav2(0, GURL("http://google2.com"), GURL(), L"abc", "def",
PageTransition::QUALIFIER_MASK);
@@ -436,9 +439,9 @@ TEST_F(SessionServiceTest, PruneFromFront) {
SessionTab* tab = windows[0]->tabs[0];
ASSERT_EQ(1, tab->current_navigation_index);
EXPECT_EQ(3U, tab->navigations.size());
- EXPECT_TRUE(GURL(base_url + IntToString(2)) == tab->navigations[0].url);
- EXPECT_TRUE(GURL(base_url + IntToString(3)) == tab->navigations[1].url);
- EXPECT_TRUE(GURL(base_url + IntToString(4)) == tab->navigations[2].url);
+ EXPECT_TRUE(GURL(base_url + IntToString(2)) == tab->navigations[0].url());
+ EXPECT_TRUE(GURL(base_url + IntToString(3)) == tab->navigations[1].url());
+ EXPECT_TRUE(GURL(base_url + IntToString(4)) == tab->navigations[2].url());
}
// Prunes from front so that we have no entries.
diff --git a/chrome/browser/sessions/session_types.cc b/chrome/browser/sessions/session_types.cc
new file mode 100644
index 0000000..5c17454
--- /dev/null
+++ b/chrome/browser/sessions/session_types.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 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.
+
+#include "chrome/browser/sessions/session_types.h"
+
+#include "chrome/browser/navigation_entry.h"
+
+// TabNavigation --------------------------------------------------------------
+
+// static
+NavigationEntry* TabNavigation::ToNavigationEntry(int page_id) const {
+ GURL real_url = url_;
+ TabContentsType type = TabContents::TypeForURL(&real_url);
+ DCHECK(type != TAB_CONTENTS_UNKNOWN_TYPE);
+
+ NavigationEntry* entry = new NavigationEntry(
+ type,
+ NULL, // The site instance for restored tabs is sent on navigation
+ // (WebContents::GetSiteInstanceForEntry).
+ page_id,
+ real_url,
+ referrer_,
+ title_,
+ // Use a transition type of reload so that we don't incorrectly
+ // increase the typed count.
+ PageTransition::RELOAD);
+ entry->set_display_url(url_);
+ entry->set_content_state(state_);
+ entry->set_has_post_data(type_mask_ & TabNavigation::HAS_POST_DATA);
+ return entry;
+}
+
+void TabNavigation::SetFromNavigationEntry(const NavigationEntry& entry) {
+ url_ = entry.display_url();
+ referrer_ = entry.referrer();
+ title_ = entry.title();
+ state_ = entry.content_state();
+ transition_ = entry.transition_type();
+ type_mask_ = entry.has_post_data() ? TabNavigation::HAS_POST_DATA : 0;
+}
diff --git a/chrome/browser/sessions/session_types.h b/chrome/browser/sessions/session_types.h
new file mode 100644
index 0000000..deabead
--- /dev/null
+++ b/chrome/browser/sessions/session_types.h
@@ -0,0 +1,183 @@
+// Copyright (c) 2006-2008 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_SESSION_TYPES_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_TYPES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/gfx/rect.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/common/page_transition_types.h"
+#include "googleurl/src/gurl.h"
+
+class NavigationEntry;
+
+// TabNavigation -------------------------------------------------------------
+
+// TabNavigation corresponds to the parts of NavigationEntry needed to restore
+// the NavigationEntry during session restore and tab restore.
+//
+// TabNavigation is cheap and supports copy semantics.
+class TabNavigation {
+ public:
+ enum TypeMask {
+ HAS_POST_DATA = 1
+ };
+
+ TabNavigation()
+ : transition_(PageTransition::TYPED),
+ type_mask_(0),
+ index_(-1) {
+ }
+
+ TabNavigation(int index,
+ const GURL& url,
+ const GURL& referrer,
+ const std::wstring& title,
+ const std::string& state,
+ PageTransition::Type transition)
+ : url_(url),
+ referrer_(referrer),
+ title_(title),
+ state_(state),
+ transition_(transition),
+ type_mask_(0),
+ index_(index) {}
+
+ // Converts this TabNavigation into a NavigationEntry with a page id of
+ // |page_id|. The caller owns the returned NavigationEntry.
+ NavigationEntry* ToNavigationEntry(int page_id) const;
+
+ // Resets this TabNavigation from |entry|.
+ void SetFromNavigationEntry(const NavigationEntry& entry);
+
+ // URL of the page.
+ void set_url(const GURL& url) { url_ = url; }
+ const GURL& url() const { return url_; }
+
+ // The referrer.
+ const GURL& referrer() const { return referrer_; }
+
+ // The title of the page.
+ const std::wstring& title() const { return title_; }
+
+ // State bits.
+ const std::string& state() const { return state_; }
+
+ // Transition type.
+ void set_transition(PageTransition::Type transition) {
+ transition_ = transition;
+ }
+ PageTransition::Type transition() const { return transition_; }
+
+ // A mask used for arbitrary boolean values needed to represent a
+ // NavigationEntry. Currently only contains HAS_POST_DATA or 0.
+ void set_type_mask(int type_mask) { type_mask_ = type_mask; }
+ int type_mask() const { return type_mask_; }
+
+ // The index in the NavigationController. If this is -1, it means this
+ // TabNavigation is bogus.
+ //
+ // This is used when determining the selected TabNavigation and only useful
+ // by BaseSessionService and SessionService.
+ void set_index(int index) { index_ = index; }
+ int index() { return index_; }
+
+ private:
+ friend class BaseSessionService;
+
+ GURL url_;
+ GURL referrer_;
+ std::wstring title_;
+ std::string state_;
+ PageTransition::Type transition_;
+ int type_mask_;
+
+ int index_;
+};
+
+// SessionTab ----------------------------------------------------------------
+
+// SessionTab corresponds to a NavigationController.
+struct SessionTab {
+ SessionTab() : tab_visual_index(-1), current_navigation_index(-1) { }
+
+ // Unique id of the window.
+ SessionID window_id;
+
+ // Unique if of the tab.
+ SessionID tab_id;
+
+ // Visual index of the tab within its window. There may be gaps in these
+ // values.
+ //
+ // NOTE: this is really only useful for the SessionService during
+ // restore, others can likely ignore this and use the order of the
+ // tabs in SessionWindow.tabs.
+ int tab_visual_index;
+
+ // Identifies the index of the current navigation in navigations. For
+ // example, if this is 2 it means the current navigation is navigations[2].
+ //
+ // NOTE: when the service is creating SessionTabs, initially this
+ // corresponds to TabNavigation.index, not the index in navigations. When done
+ // creating though, this is set to the index in navigations.
+ int current_navigation_index;
+
+ std::vector<TabNavigation> navigations;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SessionTab);
+};
+
+// SessionWindow -------------------------------------------------------------
+
+// Describes a saved window.
+struct SessionWindow {
+ SessionWindow()
+ : selected_tab_index(-1),
+ type(Browser::TYPE_NORMAL),
+ is_constrained(true),
+ is_maximized(false) {}
+ ~SessionWindow() { STLDeleteElements(&tabs); }
+
+ // Identifier of the window.
+ SessionID window_id;
+
+ // Bounds of the window.
+ gfx::Rect bounds;
+
+ // Index of the selected tab in tabs; -1 if no tab is selected. After restore
+ // this value is guaranteed to be a valid index into tabs.
+ //
+ // NOTE: when the service is creating SessionWindows, initially this
+ // corresponds to SessionTab.tab_visual_index, not the index in
+ // tabs. When done creating though, this is set to the index in
+ // tabs.
+ int selected_tab_index;
+
+ // Type of the browser. Currently we only store browsers of type
+ // TYPE_NORMAL and TYPE_POPUP.
+ Browser::Type type;
+
+ // If true, the window is constrained.
+ //
+ // Currently SessionService prunes all constrained windows so that session
+ // restore does not attempt to restore them.
+ bool is_constrained;
+
+ // The tabs, ordered by visual order.
+ std::vector<SessionTab*> tabs;
+
+ // Is the window maximized?
+ bool is_maximized;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SessionWindow);
+};
+
+#endif // CHROME_BROWSER_SESSIONS_SESSION_TYPES_H_
diff --git a/chrome/browser/sessions/tab_restore_service.cc b/chrome/browser/sessions/tab_restore_service.cc
new file mode 100644
index 0000000..f12e6d9
--- /dev/null
+++ b/chrome/browser/sessions/tab_restore_service.cc
@@ -0,0 +1,649 @@
+// Copyright (c) 2006-2008 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.
+
+#include <algorithm>
+#include <iterator>
+
+#include "chrome/browser/sessions/tab_restore_service.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/browser/sessions/session_backend.h"
+#include "chrome/common/scoped_vector.h"
+#include "chrome/common/stl_util-inl.h"
+
+using base::Time;
+
+// Entry ----------------------------------------------------------------------
+
+// ID of the next Entry.
+static SessionID::id_type next_entry_id = 1;
+
+TabRestoreService::Entry::Entry() : id(next_entry_id++), type(TAB) {}
+
+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;
+
+// Identifier for commands written to file.
+// The ordering in the file is as follows:
+// . When the user closes a tab a command of type
+// kCommandSelectedNavigationInTab is written identifying the tab and
+// the selected index. This is followed by any number of
+// kCommandUpdateTabNavigation commands (1 per navigation entry).
+// . When the user closes a window a kCommandSelectedNavigationInTab command
+// is written out and followed by n tab closed sequences (as previoulsy
+// described).
+// . When the user restores an entry a command of type kCommandRestoredEntry
+// is written.
+static const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
+static const SessionCommand::id_type kCommandRestoredEntry = 2;
+static const SessionCommand::id_type kCommandWindow = 3;
+static const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
+
+// Number of entries (not commands) before we clobber the file and write
+// everything.
+static const int kEntriesPerReset = 40;
+
+namespace {
+
+// Payload structures.
+
+typedef int32 RestoredEntryPayload;
+
+// Payload used for the start of a window close.
+struct WindowPayload {
+ SessionID::id_type window_id;
+ int32 selected_tab_index;
+ int32 num_tabs;
+};
+
+// Paylowed used for the start of a tab close.
+struct SelectedNavigationInTabPayload {
+ SessionID::id_type id;
+ int32 index;
+};
+
+typedef std::map<SessionID::id_type, TabRestoreService::Entry*> IDToEntry;
+
+// If |id_to_entry| contains an entry for |id| the corresponding entry is
+// deleted and removed from both |id_to_entry| and |entries|. This is used
+// when creating entries from the backend file.
+void RemoveEntryByID(SessionID::id_type id,
+ IDToEntry* id_to_entry,
+ std::vector<TabRestoreService::Entry*>* entries) {
+ IDToEntry::iterator i = id_to_entry->find(id);
+ if (i == id_to_entry->end())
+ return;
+
+ entries->erase(std::find(entries->begin(), entries->end(), i->second));
+ delete i->second;
+ id_to_entry->erase(i);
+}
+
+} // namespace
+
+TabRestoreService::TabRestoreService(Profile* profile)
+ : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
+ std::wstring()),
+ loaded_last_session_(false),
+ restoring_(false),
+ reached_max_(false),
+ entries_to_write_(0),
+ entries_written_(0) {
+}
+
+TabRestoreService::~TabRestoreService() {
+ if (backend())
+ Save();
+
+ FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
+ STLDeleteElements(&entries_);
+}
+
+void TabRestoreService::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void TabRestoreService::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
+ if (restoring_)
+ return;
+
+ Browser* browser = Browser::GetBrowserForController(tab, NULL);
+ if (closing_browsers_.find(browser) != closing_browsers_.end())
+ return;
+
+ scoped_ptr<Tab> local_tab(new Tab());
+ PopulateTabFromController(tab, local_tab.get());
+ if (local_tab->navigations.empty())
+ return;
+
+ AddEntry(local_tab.release(), true, true);
+}
+
+void TabRestoreService::BrowserClosing(Browser* browser) {
+ if (browser->type() != Browser::TYPE_NORMAL ||
+ browser->tab_count() == 0)
+ return;
+
+ 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 {
+ AddEntry(window, true, true);
+ }
+}
+
+void TabRestoreService::BrowserClosed(Browser* browser) {
+ closing_browsers_.erase(browser);
+}
+
+void TabRestoreService::ClearEntries() {
+ // Mark all the tabs as closed so that we don't attempt to restore them.
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i)
+ ScheduleCommand(CreateRestoredEntryCommand((*i)->id));
+
+ entries_to_write_ = 0;
+
+ // Schedule a pending reset so that we nuke the file on next write.
+ set_pending_reset(true);
+
+ // Schedule a command, otherwise if there are no pending commands Save does
+ // nothing.
+ ScheduleCommand(CreateRestoredEntryCommand(1));
+
+ STLDeleteElements(&entries_);
+ NotifyTabsChanged();
+}
+
+void TabRestoreService::RestoreMostRecentEntry(Browser* browser) {
+ if (entries_.empty())
+ return;
+
+ RestoreEntryById(browser, entries_.front()->id, false);
+}
+
+void TabRestoreService::RestoreEntryById(Browser* browser,
+ SessionID::id_type id,
+ bool replace_existing_tab) {
+ Entries::iterator i = GetEntryIteratorById(id);
+ if (i == entries_.end()) {
+ // Don't hoark here, we allow an invalid id.
+ return;
+ }
+
+ size_t index = 0;
+ for (Entries::iterator j = entries_.begin(); j != i && j != entries_.end();
+ ++j, ++index);
+ if (static_cast<int>(index) < entries_to_write_)
+ entries_to_write_--;
+
+ ScheduleCommand(CreateRestoredEntryCommand(id));
+
+ restoring_ = true;
+ Entry* entry = *i;
+ entries_.erase(i);
+ i = entries_.end();
+ if (browser) { // Browser is null during testing.
+ 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();
+ }
+ }
+ delete entry;
+ restoring_ = false;
+ NotifyTabsChanged();
+}
+
+void TabRestoreService::LoadTabsFromLastSession() {
+ if (loaded_last_session_ || reached_max_)
+ return;
+
+ loaded_last_session_ = true;
+
+ ScheduleGetLastSessionCommands(
+ new InternalGetCommandsRequest(
+ NewCallback(this, &TabRestoreService::OnGotLastSessionCommands)),
+ &load_tabs_consumer_);
+}
+
+void TabRestoreService::Save() {
+ int to_write_count = std::min(entries_to_write_,
+ static_cast<int>(entries_.size()));
+ entries_to_write_ = 0;
+ if (entries_written_ + to_write_count > kEntriesPerReset) {
+ to_write_count = entries_.size();
+ set_pending_reset(true);
+ }
+ if (to_write_count) {
+ // Write the to_write_count most recently added entries out. The most
+ // recently added entry is at the front, so we use a reverse iterator to
+ // write in the order the entries were added.
+ Entries::reverse_iterator i = entries_.rbegin();
+ DCHECK(static_cast<size_t>(to_write_count) <= entries_.size());
+ std::advance(i, entries_.size() - static_cast<int>(to_write_count));
+ for (; i != entries_.rend(); ++i) {
+ Entry* entry = *i;
+ if (entry->type == TAB) {
+ Tab* tab = static_cast<Tab*>(entry);
+ int selected_index = GetSelectedNavigationIndexToPersist(*tab);
+ if (selected_index != -1)
+ ScheduleCommandsForTab(*tab, selected_index);
+ } else {
+ ScheduleCommandsForWindow(*static_cast<Window*>(entry));
+ }
+ entries_written_++;
+ }
+ }
+ if (pending_reset())
+ entries_written_ = 0;
+ BaseSessionService::Save();
+}
+
+void TabRestoreService::PopulateTabFromController(
+ NavigationController* controller,
+ Tab* tab) {
+ const int pending_index = controller->GetPendingEntryIndex();
+ int entry_count = controller->GetEntryCount();
+ if (entry_count == 0 && pending_index == 0)
+ entry_count++;
+ tab->navigations.resize(static_cast<int>(entry_count));
+ for (int i = 0; i < entry_count; ++i) {
+ NavigationEntry* entry = (i == pending_index) ?
+ controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
+ tab->navigations[i].SetFromNavigationEntry(*entry);
+ }
+ tab->current_navigation_index = controller->GetCurrentEntryIndex();
+ if (tab->current_navigation_index == -1 && entry_count > 0)
+ tab->current_navigation_index = 0;
+}
+
+void TabRestoreService::NotifyTabsChanged() {
+ FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceChanged(this));
+}
+
+void TabRestoreService::AddEntry(Entry* entry, bool notify, bool to_front) {
+ if (to_front)
+ entries_.push_front(entry);
+ else
+ entries_.push_back(entry);
+ if (notify)
+ PruneAndNotify();
+ // Start the save timer, when it fires we'll generate the commands.
+ StartSaveTimer();
+ entries_to_write_++;
+}
+
+void TabRestoreService::PruneAndNotify() {
+ while (entries_.size() > kMaxEntries) {
+ delete entries_.back();
+ entries_.pop_back();
+ reached_max_ = true;
+ }
+
+ NotifyTabsChanged();
+}
+
+TabRestoreService::Entries::iterator TabRestoreService::GetEntryIteratorById(
+ SessionID::id_type id) {
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
+ if ((*i)->id == id)
+ return i;
+ }
+ return entries_.end();
+}
+
+void TabRestoreService::ScheduleCommandsForWindow(const Window& window) {
+ DCHECK(!window.tabs.empty());
+ int selected_tab = window.selected_tab_index;
+ int valid_tab_count = 0;
+ int real_selected_tab = selected_tab;
+ for (size_t i = 0; i < window.tabs.size(); ++i) {
+ if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
+ valid_tab_count++;
+ } else if (static_cast<int>(i) < selected_tab) {
+ real_selected_tab--;
+ }
+ }
+ if (valid_tab_count == 0)
+ return; // No tabs to persist.
+
+ ScheduleCommand(
+ CreateWindowCommand(window.id,
+ std::min(real_selected_tab, valid_tab_count - 1),
+ valid_tab_count));
+
+ for (size_t i = 0; i < window.tabs.size(); ++i) {
+ int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
+ if (selected_index != -1)
+ ScheduleCommandsForTab(window.tabs[i], selected_index);
+ }
+}
+
+void TabRestoreService::ScheduleCommandsForTab(const Tab& tab,
+ int selected_index) {
+ const std::vector<TabNavigation>& navigations = tab.navigations;
+ int max_index = static_cast<int>(navigations.size());
+
+ // Determine the first navigation we'll persist.
+ int valid_count_before_selected = 0;
+ int first_index_to_persist = selected_index;
+ for (int i = selected_index - 1; i >= 0 &&
+ valid_count_before_selected < max_persist_navigation_count; --i) {
+ if (ShouldTrackEntry(navigations[i])) {
+ first_index_to_persist = i;
+ valid_count_before_selected++;
+ }
+ }
+
+ // Write the command that identifies the selected tab.
+ ScheduleCommand(
+ CreateSelectedNavigationInTabCommand(tab.id,
+ valid_count_before_selected));
+
+ // Then write the navigations.
+ for (int i = first_index_to_persist, wrote_count = 0;
+ i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) {
+ if (ShouldTrackEntry(navigations[i])) {
+ // Creating a NavigationEntry isn't the most efficient way to go about
+ // this, but it simplifies the code and makes it less error prone as we
+ // add new data to NavigationEntry.
+ scoped_ptr<NavigationEntry> entry(
+ navigations[i].ToNavigationEntry(wrote_count));
+ ScheduleCommand(
+ CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
+ wrote_count++, *entry));
+ }
+ }
+}
+
+SessionCommand* TabRestoreService::CreateWindowCommand(SessionID::id_type id,
+ int selected_tab_index,
+ int num_tabs) {
+ WindowPayload payload = { 0 };
+ payload.window_id = id;
+ payload.selected_tab_index = selected_tab_index;
+ payload.num_tabs = num_tabs;
+
+ SessionCommand* command =
+ new SessionCommand(kCommandWindow, sizeof(payload));
+ memcpy(command->contents(), &payload, sizeof(payload));
+ return command;
+}
+
+SessionCommand* TabRestoreService::CreateSelectedNavigationInTabCommand(
+ SessionID::id_type tab_id,
+ int32 index) {
+ SelectedNavigationInTabPayload payload = { 0 };
+ payload.id = tab_id;
+ payload.index = index;
+ SessionCommand* command =
+ new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload));
+ memcpy(command->contents(), &payload, sizeof(payload));
+ return command;
+}
+
+SessionCommand* TabRestoreService::CreateRestoredEntryCommand(
+ SessionID::id_type entry_id) {
+ RestoredEntryPayload payload = entry_id;
+ SessionCommand* command =
+ new SessionCommand(kCommandRestoredEntry, sizeof(payload));
+ memcpy(command->contents(), &payload, sizeof(payload));
+ return command;
+}
+
+int TabRestoreService::GetSelectedNavigationIndexToPersist(const Tab& tab) {
+ const std::vector<TabNavigation>& navigations = tab.navigations;
+ int selected_index = tab.current_navigation_index;
+ int max_index = static_cast<int>(navigations.size());
+
+ // Find the first navigation to persist. We won't persist the selected
+ // navigation if ShouldTrackEntry returns false.
+ while (selected_index >= 0 &&
+ !ShouldTrackEntry(navigations[selected_index])) {
+ selected_index--;
+ }
+
+ if (selected_index != -1)
+ return selected_index;
+
+ // Couldn't find a navigation to persist going back, go forward.
+ selected_index = tab.current_navigation_index + 1;
+ while (selected_index < max_index &&
+ !ShouldTrackEntry(navigations[selected_index])) {
+ selected_index++;
+ }
+
+ return (selected_index == max_index) ? -1 : selected_index;
+}
+
+void TabRestoreService::OnGotLastSessionCommands(
+ Handle handle,
+ scoped_refptr<InternalGetCommandsRequest> request) {
+ if (request->canceled() || entries_.size() == kMaxEntries)
+ return;
+
+ std::vector<SessionCommand*>& commands = request->commands;
+ // Iterate through the commands populating entries and id_to_entry.
+ ScopedVector<Entry> entries;
+ IDToEntry id_to_entry;
+ // If non-null we're processing the navigations of this tab.
+ Tab* current_tab = NULL;
+ // If non-null we're processing the tabs of this window.
+ Window* current_window = NULL;
+ // If > 0, we've gotten a window command but not all the tabs yet.
+ int pending_window_tabs = 0;
+ for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
+ i != commands.end(); ++i) {
+ const SessionCommand& command = *(*i);
+ switch (command.id()) {
+ case kCommandRestoredEntry: {
+ if (pending_window_tabs > 0) {
+ // Should never receive a restored command while waiting for all the
+ // tabs in a window.
+ return;
+ }
+
+ current_tab = NULL;
+ current_window = NULL;
+
+ RestoredEntryPayload payload;
+ if (!command.GetPayload(&payload, sizeof(payload)))
+ return;
+ RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
+ break;
+ }
+
+ case kCommandWindow: {
+ WindowPayload payload;
+ if (pending_window_tabs > 0) {
+ // Should never receive a window command while waiting for all the
+ // tabs in a window.
+ return;
+ }
+
+ if (!command.GetPayload(&payload, sizeof(payload)))
+ return;
+
+ pending_window_tabs = payload.num_tabs;
+ if (pending_window_tabs <= 0) {
+ // Should always have at least 1 tab. Likely indicates corruption.
+ return;
+ }
+
+ RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
+
+ current_window = new Window();
+ current_window->selected_tab_index = payload.selected_tab_index;
+ entries->push_back(current_window);
+ id_to_entry[payload.window_id] = current_window;
+ break;
+ }
+
+ case kCommandSelectedNavigationInTab: {
+ SelectedNavigationInTabPayload payload;
+ if (!command.GetPayload(&payload, sizeof(payload)))
+ return;
+
+ if (pending_window_tabs > 0) {
+ if (!current_window) {
+ // We should have created a window already.
+ NOTREACHED();
+ return;
+ }
+ current_window->tabs.resize(current_window->tabs.size() + 1);
+ current_tab = &(current_window->tabs.back());
+ if (--pending_window_tabs == 0)
+ current_window = NULL;
+ } else {
+ RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
+ current_tab = new Tab();
+ id_to_entry[payload.id] = current_tab;
+ entries->push_back(current_tab);
+ }
+ current_tab->current_navigation_index = payload.index;
+ break;
+ }
+
+ case kCommandUpdateTabNavigation: {
+ if (!current_tab) {
+ // Should be in a tab when we get this.
+ return;
+ }
+ current_tab->navigations.resize(current_tab->navigations.size() + 1);
+ SessionID::id_type tab_id;
+ if (!RestoreUpdateTabNavigationCommand(
+ command, &current_tab->navigations.back(), &tab_id)) {
+ return;
+ }
+ break;
+ }
+
+ default:
+ // Unknown type, usually indicates corruption of file. Ignore it.
+ return;
+ }
+ }
+
+ // If there was corruption some of the entries won't be valid. Prune any
+ // 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();
+}
+
+bool TabRestoreService::ValidateTab(Tab* tab) {
+ if (tab->navigations.empty())
+ return false;
+
+ tab->current_navigation_index =
+ std::max(0, std::min(tab->current_navigation_index,
+ static_cast<int>(tab->navigations.size()) - 1));
+ return true;
+}
+
+void TabRestoreService::ValidateAndDeleteEmptyEntries(
+ std::vector<Entry*>* entries) {
+ std::vector<Entry*> valid_entries;
+ std::vector<Entry*> invalid_entries;
+
+ size_t max_valid = kMaxEntries - entries_.size();
+ // Iterate from the back so that we keep the most recently closed entries.
+ for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
+ i != entries->rend(); ++i) {
+ bool valid_entry = false;
+ if (valid_entries.size() != max_valid) {
+ if ((*i)->type == TAB) {
+ Tab* tab = static_cast<Tab*>(*i);
+ if (ValidateTab(tab))
+ valid_entry = true;
+ } else {
+ Window* window = static_cast<Window*>(*i);
+ for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
+ tab_i != window->tabs.end();) {
+ if (!ValidateTab(&(*tab_i)))
+ tab_i = window->tabs.erase(tab_i);
+ else
+ ++tab_i;
+ }
+ if (!window->tabs.empty()) {
+ window->selected_tab_index =
+ std::max(0, std::min(window->selected_tab_index,
+ static_cast<int>(window->tabs.size() - 1)));
+ valid_entry = true;
+ }
+ }
+ }
+ if (valid_entry)
+ valid_entries.push_back(*i);
+ else
+ invalid_entries.push_back(*i);
+ }
+ // NOTE: at this point the entries are ordered with newest at the front.
+ entries->swap(valid_entries);
+
+ // Delete the remaining entries.
+ STLDeleteElements(&invalid_entries);
+}
diff --git a/chrome/browser/tab_restore_service.h b/chrome/browser/sessions/tab_restore_service.h
index 5d1460e..adf5df5 100644
--- a/chrome/browser/tab_restore_service.h
+++ b/chrome/browser/sessions/tab_restore_service.h
@@ -2,15 +2,18 @@
// 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_SESSIONS_TAB_RESTORE_SERVICE_H_
+#define CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
#include <list>
#include "base/observer_list.h"
#include "base/time.h"
-#include "chrome/browser/session_service.h"
+#include "chrome/browser/sessions/base_session_service.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/sessions/session_types.h"
+class Browser;
class NavigationController;
class Profile;
@@ -25,7 +28,7 @@ class Profile;
//
// To listen for changes to the set of entries managed by the TabRestoreService
// add an observer.
-class TabRestoreService {
+class TabRestoreService : public BaseSessionService {
public:
// Observer is notified when the set of entries managed by TabRestoreService
// changes in some way.
@@ -52,7 +55,7 @@ class TabRestoreService {
// Unique id for this entry. The id is guaranteed to be unique for a
// session.
- int id;
+ SessionID::id_type id;
// The type of the entry.
Type type;
@@ -63,7 +66,6 @@ class TabRestoreService {
Tab() : Entry(TAB), current_navigation_index(-1) {}
// The navigations.
- // WARNING: navigations may be empty.
std::vector<TabNavigation> navigations;
// Index of the selected navigation in navigations.
@@ -85,7 +87,7 @@ class TabRestoreService {
// Creates a new TabRestoreService.
explicit TabRestoreService(Profile* profile);
- ~TabRestoreService();
+ virtual ~TabRestoreService();
// Adds/removes an observer. TabRestoreService does not take ownership of
// the observer.
@@ -120,7 +122,16 @@ class TabRestoreService {
// 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);
+ void RestoreEntryById(Browser* browser,
+ SessionID::id_type id,
+ bool replace_existing_tab);
+
+ // Loads the tabs from the previous session. This does nothing if the tabs
+ // from the previous session have already been loaded.
+ void LoadTabsFromLastSession();
+
+ protected:
+ virtual void Save();
private:
// Populates tabs->navigations from the NavigationController.
@@ -130,24 +141,79 @@ class TabRestoreService {
// Notifies observers the tabs have changed.
void NotifyTabsChanged();
+ // Adds |entry| to the list of entries. 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|.
- Entries::iterator GetEntryIteratorById(int id);
+ 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);
+
+ // Creates a tab close command.
+ SessionCommand* CreateSelectedNavigationInTabCommand(
+ SessionID::id_type tab_id,
+ int32 index);
+
+ // 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 from the previous run.
+ // This creates entries and adds them to entries_, notifying the observer.
+ void OnGotLastSessionCommands(
+ Handle handle,
+ scoped_refptr<InternalGetCommandsRequest> request);
+
+ // 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);
- Profile* profile_;
+ // Set of entries.
+ Entries entries_;
// Whether we've loaded the last session.
bool loaded_last_session_;
- // Set of entries.
- Entries entries_;
-
// 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<Observer> observer_list_;
// Set of tabs that we've received a BrowserClosing method for but no
@@ -155,7 +221,10 @@ class TabRestoreService {
// avoid creating historical tabs for them.
std::set<Browser*> closing_browsers_;
+ // Used when loading commands from the previous session.
+ CancelableRequestConsumer load_tabs_consumer_;
+
DISALLOW_COPY_AND_ASSIGN(TabRestoreService);
};
-#endif // CHROME_BROWSER_TAB_RESTORE_SERVICE_H_
+#endif // CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_H_
diff --git a/chrome/browser/sessions/tab_restore_service_unittest.cc b/chrome/browser/sessions/tab_restore_service_unittest.cc
new file mode 100644
index 0000000..99e65c3
--- /dev/null
+++ b/chrome/browser/sessions/tab_restore_service_unittest.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2006-2008 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.
+
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
+#include "chrome/test/test_tab_contents.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TabRestoreServiceTest : public testing::Test {
+ public:
+ TabRestoreServiceTest()
+ : tab_contents_factory_(
+ TestTabContentsFactory::CreateAndRegisterFactory()),
+ profile_(new TestingProfile()),
+ service_(new TabRestoreService(profile_.get())) {
+ test_contents_ = tab_contents_factory_->CreateInstanceImpl();
+ test_contents_->set_commit_on_navigate(true);
+ controller_ = new NavigationController(test_contents_, profile_.get());
+ url1_ = GURL(tab_contents_factory_->scheme() + "://1");
+ url2_ = GURL(tab_contents_factory_->scheme() + "://2");
+ url3_ = GURL(tab_contents_factory_->scheme() + "://3");
+ }
+
+ ~TabRestoreServiceTest() {
+ controller_->Destroy();
+ }
+
+ protected:
+ void AddThreeNavigations() {
+ // Navigate to three URLs.
+ controller_->LoadURL(url1_, GURL(), PageTransition::RELOAD);
+ controller_->LoadURL(url2_, GURL(), PageTransition::RELOAD);
+ controller_->LoadURL(url3_, GURL(), PageTransition::RELOAD);
+ }
+
+ void NavigateToIndex(int index) {
+ // Navigate back. We have to do this song and dance as NavigationController
+ // isn't happy if you navigate immediately while going back.
+ test_contents_->set_commit_on_navigate(false);
+ controller_->GoToIndex(index);
+ test_contents_->CompleteNavigationAsRenderer(
+ controller_->GetPendingEntry()->page_id(),
+ controller_->GetPendingEntry()->url());
+ }
+
+ void RecreateService() {
+ // Must set service to null first so that it is destroyed.
+ service_ = NULL;
+ service_ = new TabRestoreService(profile_.get());
+ service_->LoadTabsFromLastSession();
+ }
+
+ GURL url1_;
+ GURL url2_;
+ GURL url3_;
+ scoped_ptr<TestTabContentsFactory> tab_contents_factory_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_refptr<TabRestoreService> service_;
+ NavigationController* controller_;
+ TestTabContents* test_contents_;
+};
+
+TEST_F(TabRestoreServiceTest, Basic) {
+ AddThreeNavigations();
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+
+ // Make sure an entry was created.
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Make sure the entry matches.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(3, tab->navigations.size());
+ EXPECT_TRUE(url1_ == tab->navigations[0].url());
+ EXPECT_TRUE(url2_ == tab->navigations[1].url());
+ EXPECT_TRUE(url3_ == tab->navigations[2].url());
+ EXPECT_EQ(2, tab->current_navigation_index);
+
+ NavigateToIndex(1);
+
+ // And check again.
+ service_->CreateHistoricalTab(controller_);
+
+ // There should be two entries now.
+ ASSERT_EQ(2, service_->entries().size());
+
+ // Make sure the entry matches
+ entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(3, tab->navigations.size());
+ EXPECT_TRUE(url1_ == tab->navigations[0].url());
+ EXPECT_TRUE(url2_ == tab->navigations[1].url());
+ EXPECT_TRUE(url3_ == tab->navigations[2].url());
+ EXPECT_EQ(1, tab->current_navigation_index);
+}
+
+// Make sure TabRestoreService doesn't create an entry for a tab with no
+// navigations.
+TEST_F(TabRestoreServiceTest, DontCreateEmptyTab) {
+ service_->CreateHistoricalTab(controller_);
+ EXPECT_TRUE(service_->entries().empty());
+}
+
+// Tests restoring a single tab.
+TEST_F(TabRestoreServiceTest, Restore) {
+ AddThreeNavigations();
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ // One entry should be created.
+ ASSERT_EQ(1, service_->entries().size());
+
+ // And verify the entry.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(3, tab->navigations.size());
+ EXPECT_TRUE(url1_ == tab->navigations[0].url());
+ EXPECT_TRUE(url2_ == tab->navigations[1].url());
+ EXPECT_TRUE(url3_ == tab->navigations[2].url());
+ EXPECT_EQ(2, tab->current_navigation_index);
+}
+
+// Make sure a tab that is restored doesn't come back.
+TEST_F(TabRestoreServiceTest, DontLoadRestoredTab) {
+ AddThreeNavigations();
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Restore the tab.
+ service_->RestoreEntryById(NULL, service_->entries().front()->id, true);
+ ASSERT_TRUE(service_->entries().empty());
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ // There should be no entries.
+ ASSERT_EQ(0, service_->entries().size());
+}
+
+// Make sure we don't persist entries to disk that have post data.
+TEST_F(TabRestoreServiceTest, DontPersistPostData1) {
+ AddThreeNavigations();
+ controller_->GetEntryAtIndex(2)->set_has_post_data(true);
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ // One entry should be created.
+ ASSERT_EQ(1, service_->entries().size());
+
+ // And verify the entry, the last navigation (url3_) should not have
+ // been written to disk as it contained post data.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(2, tab->navigations.size());
+ EXPECT_TRUE(url1_ == tab->navigations[0].url());
+ EXPECT_TRUE(url2_ == tab->navigations[1].url());
+ EXPECT_EQ(1, tab->current_navigation_index);
+}
+
+// Make sure we don't persist entries to disk that have post data. This
+// differs from DontPersistPostData1 in that all navigations before the
+// current index have post data.
+TEST_F(TabRestoreServiceTest, DontPersistPostData2) {
+ AddThreeNavigations();
+ NavigateToIndex(1);
+ controller_->GetEntryAtIndex(0)->set_has_post_data(true);
+ controller_->GetEntryAtIndex(1)->set_has_post_data(true);
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ // One entry should be created.
+ ASSERT_EQ(1, service_->entries().size());
+
+ // And verify the entry, the last navigation (url3_) should not have
+ // been written to disk as it contained post data.
+ TabRestoreService::Entry* entry = service_->entries().front();
+ ASSERT_EQ(TabRestoreService::TAB, entry->type);
+ TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
+ ASSERT_EQ(1, tab->navigations.size());
+ EXPECT_TRUE(url3_ == tab->navigations[0].url());
+ EXPECT_EQ(0, tab->current_navigation_index);
+}
+
+// Make sure we don't persist entries to disk that have post data. This
+// differs from DontPersistPostData1 in that all the navigations have post
+// data, so that nothing should be persisted.
+TEST_F(TabRestoreServiceTest, DontPersistPostData3) {
+ AddThreeNavigations();
+ controller_->GetEntryAtIndex(0)->set_has_post_data(true);
+ controller_->GetEntryAtIndex(1)->set_has_post_data(true);
+ controller_->GetEntryAtIndex(2)->set_has_post_data(true);
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ // One entry should be created.
+ ASSERT_TRUE(service_->entries().empty());
+}
+
+// Make sure we don't persist entries to disk that have post data. This
+// differs from DontPersistPostData1 in that all the navigations have post
+// data, so that nothing should be persisted.
+TEST_F(TabRestoreServiceTest, DontLoadTwice) {
+ AddThreeNavigations();
+
+ // Have the service record the tab.
+ service_->CreateHistoricalTab(controller_);
+ ASSERT_EQ(1, service_->entries().size());
+
+ // Recreate the service and have it load the tabs.
+ RecreateService();
+
+ service_->LoadTabsFromLastSession();
+
+ // There should only be one entry.
+ ASSERT_EQ(1, service_->entries().size());
+}
diff --git a/chrome/browser/tab_restore_service.cc b/chrome/browser/tab_restore_service.cc
deleted file mode 100644
index bc8b9c7..0000000
--- a/chrome/browser/tab_restore_service.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright (c) 2006-2008 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.
-
-#include "chrome/browser/tab_restore_service.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;
-
-// Entry ----------------------------------------------------------------------
-
-// ID of the next Entry.
-static int next_entry_id = 1;
-
-TabRestoreService::Entry::Entry() : id(next_entry_id++), type(TAB) {}
-
-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;
-
-TabRestoreService::TabRestoreService(Profile* profile)
- : profile_(profile),
- loaded_last_session_(false),
- restoring_(false) {
-}
-
-TabRestoreService::~TabRestoreService() {
- FOR_EACH_OBSERVER(Observer, observer_list_, TabRestoreServiceDestroyed(this));
- STLDeleteElements(&entries_);
-}
-
-void TabRestoreService::AddObserver(Observer* observer) {
- observer_list_.AddObserver(observer);
-}
-
-void TabRestoreService::RemoveObserver(Observer* observer) {
- observer_list_.RemoveObserver(observer);
-}
-
-void TabRestoreService::CreateHistoricalTab(NavigationController* tab) {
- if (restoring_)
- return;
-
- Browser* browser = Browser::GetBrowserForController(tab, NULL);
- if (closing_browsers_.find(browser) != closing_browsers_.end())
- return;
-
- Tab* local_tab = new Tab();
- PopulateTabFromController(tab, local_tab);
- entries_.push_front(local_tab);
-
- PruneAndNotify();
-}
-
-void TabRestoreService::BrowserClosing(Browser* browser) {
- if (browser->type() != Browser::TYPE_NORMAL ||
- browser->tab_count() == 0)
- return;
-
- 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::BrowserClosed(Browser* browser) {
- closing_browsers_.erase(browser);
-}
-
-void TabRestoreService::ClearEntries() {
- STLDeleteElements(&entries_);
- NotifyTabsChanged();
-}
-
-void TabRestoreService::RestoreMostRecentEntry(Browser* browser) {
- if (entries_.empty())
- return;
-
- RestoreEntryById(browser, entries_.front()->id, false);
-}
-
-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;
- }
-
- 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();
- }
- delete entry;
- restoring_ = false;
- NotifyTabsChanged();
-}
-
-void TabRestoreService::PopulateTabFromController(
- NavigationController* controller,
- Tab* tab) {
- const int pending_index = controller->GetPendingEntryIndex();
- int entry_count = controller->GetEntryCount();
- if (entry_count == 0 && pending_index == 0)
- entry_count++;
- tab->navigations.resize(static_cast<int>(entry_count));
- for (int i = 0; i < entry_count; ++i) {
- NavigationEntry* entry = (i == pending_index) ?
- controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
- TabNavigation& tab_nav = tab->navigations[i];
- tab_nav.url = entry->display_url();
- tab_nav.referrer = entry->referrer();
- tab_nav.title = entry->title();
- tab_nav.state = entry->content_state();
- tab_nav.transition = entry->transition_type();
- tab_nav.type_mask = entry->has_post_data() ?
- TabNavigation::HAS_POST_DATA : 0;
- }
- tab->current_navigation_index = controller->GetCurrentEntryIndex();
- if (tab->current_navigation_index == -1 && entry_count > 0)
- tab->current_navigation_index = 0;
-}
-
-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/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc
index 21ba9b5..3658ef5 100644
--- a/chrome/browser/tabs/tab_strip_model.cc
+++ b/chrome/browser/tabs/tab_strip_model.cc
@@ -13,8 +13,8 @@
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
#include "chrome/browser/tab_contents_factory.h"
-#include "chrome/browser/tab_restore_service.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/tabs/tab_strip_model_order_controller.h"
#include "chrome/browser/user_metrics.h"
diff --git a/chrome/test/ui/ui_tests.scons b/chrome/test/ui/ui_tests.scons
index 597e438..202ce96 100644
--- a/chrome/test/ui/ui_tests.scons
+++ b/chrome/test/ui/ui_tests.scons
@@ -104,7 +104,7 @@ ui_test_files = [
'$CHROME_DIR/browser/resource_dispatcher_host_uitest.cc',
'$CHROME_DIR/browser/sanity_uitest.cc',
'$CHROME_DIR/browser/session_history_uitest.cc',
- '$CHROME_DIR/browser/session_restore_uitest.cc',
+ '$CHROME_DIR/browser/sessions/session_restore_uitest.cc',
'$CHROME_DIR/browser/ssl_uitest.cc',
'$CHROME_DIR/browser/tab_restore_uitest.cc',
'$CHROME_DIR/browser/view_source_uitest.cc',
diff --git a/chrome/test/ui/ui_tests.vcproj b/chrome/test/ui/ui_tests.vcproj
index 7173351..fbfe834 100644
--- a/chrome/test/ui/ui_tests.vcproj
+++ b/chrome/test/ui/ui_tests.vcproj
@@ -306,7 +306,7 @@
Name="TestSessionRestore"
>
<File
- RelativePath="..\..\browser\session_restore_uitest.cc"
+ RelativePath="..\..\browser\sessions\session_restore_uitest.cc"
>
</File>
</Filter>
diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons
index df884b1..e870062 100644
--- a/chrome/test/unit/unit_tests.scons
+++ b/chrome/test/unit/unit_tests.scons
@@ -184,9 +184,10 @@ if env['PLATFORM'] == 'win32':
'$CHROME_DIR/browser/resource_dispatcher_host_unittest.cc',
'$CHROME_DIR/browser/rlz/rlz_unittest.cc',
'$CHROME_DIR/browser/safe_browsing/protocol_manager_unittest.cc',
- '$CHROME_DIR/browser/session_backend_unittest.cc',
- '$CHROME_DIR/browser/session_service_test_helper.cc',
- '$CHROME_DIR/browser/session_service_unittest.cc',
+ '$CHROME_DIR/browser/sessions/session_backend_unittest.cc',
+ '$CHROME_DIR/browser/sessions/session_service_test_helper.cc',
+ '$CHROME_DIR/browser/sessions/session_service_unittest.cc',
+ '$CHROME_DIR/browser/sessions/tab_restore_service_unittest.cc',
'$CHROME_DIR/browser/site_instance_unittest.cc',
'$CHROME_DIR/browser/spellcheck_unittest.cc',
'$CHROME_DIR/browser/tabs/tab_strip_model_unittest.cc',
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index ab3eda5..11bf464 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -474,19 +474,27 @@
Name="TestSessionService"
>
<File
- RelativePath="..\..\browser\session_backend_unittest.cc"
+ RelativePath="..\..\browser\sessions\session_backend_unittest.cc"
>
</File>
<File
- RelativePath="..\..\browser\session_service_test_helper.cc"
+ RelativePath="..\..\browser\sessions\session_service_test_helper.cc"
>
</File>
<File
- RelativePath="..\..\browser\session_service_test_helper.h"
+ RelativePath="..\..\browser\sessions\session_service_test_helper.h"
>
</File>
<File
- RelativePath="..\..\browser\session_service_unittest.cc"
+ RelativePath="..\..\browser\sessions\session_service_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="TestTabRestoreService"
+ >
+ <File
+ RelativePath="..\..\browser\sessions\tab_restore_service_unittest.cc"
>
</File>
</Filter>