summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/sessions/session_restore.cc36
-rw-r--r--chrome/browser/sessions/session_restore.h4
-rw-r--r--chrome/browser/sessions/session_restore_browsertest.cc122
-rw-r--r--chrome/browser/sessions/session_restore_delegate.cc51
-rw-r--r--chrome/browser/sessions/session_restore_delegate.h24
-rw-r--r--chrome/browser/sessions/session_restore_stats_collector.cc6
-rw-r--r--chrome/browser/sessions/tab_loader.cc8
7 files changed, 227 insertions, 24 deletions
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 8402192..afafc0a 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -179,7 +179,7 @@ class SessionRestoreImpl : public content::NotificationObserver {
}
// Always create in a new window.
- FinishedTabCreation(true, true, created_contents);
+ FinishedTabCreation(true, true, &created_contents);
on_session_restored_callbacks_->Notify(
static_cast<int>(created_contents.size()));
@@ -280,10 +280,9 @@ class SessionRestoreImpl : public content::NotificationObserver {
// have been loaded.
//
// Returns the Browser that was created, if any.
- Browser* FinishedTabCreation(
- bool succeeded,
- bool created_tabbed_browser,
- const std::vector<RestoredTab>& contents_created) {
+ Browser* FinishedTabCreation(bool succeeded,
+ bool created_tabbed_browser,
+ std::vector<RestoredTab>* contents_created) {
Browser* browser = nullptr;
if (!created_tabbed_browser && always_create_tabbed_browser_) {
browser =
@@ -299,7 +298,9 @@ class SessionRestoreImpl : public content::NotificationObserver {
if (succeeded) {
// Start Loading tabs.
- SessionRestoreDelegate::RestoreTabs(contents_created, restore_started_);
+ if (SessionRestore::SmartLoadingEnabled())
+ std::stable_sort(contents_created->begin(), contents_created->end());
+ SessionRestoreDelegate::RestoreTabs(*contents_created, restore_started_);
}
if (!synchronous_) {
@@ -362,7 +363,7 @@ class SessionRestoreImpl : public content::NotificationObserver {
content::BrowserContext::GetDefaultStoragePartition(profile_)
->GetDOMStorageContext()
->StartScavengingUnusedSessionStorage();
- return FinishedTabCreation(false, false, *created_contents);
+ return FinishedTabCreation(false, false, created_contents);
}
#if defined(OS_CHROMEOS)
@@ -462,7 +463,7 @@ class SessionRestoreImpl : public content::NotificationObserver {
// FinishedTabCreation will create a new TabbedBrowser and add the urls to
// it.
Browser* finished_browser =
- FinishedTabCreation(true, has_tabbed_browser, *created_contents);
+ FinishedTabCreation(true, has_tabbed_browser, created_contents);
if (finished_browser)
last_browser = finished_browser;
@@ -518,9 +519,8 @@ class SessionRestoreImpl : public content::NotificationObserver {
if (!contents)
continue;
- RestoredTab restored_tab;
- restored_tab.contents = contents;
- restored_tab.is_active = is_selected_tab;
+ RestoredTab restored_tab(contents, is_selected_tab,
+ tab.extension_app_id.empty(), tab.pinned);
created_contents->push_back(restored_tab);
// If this isn't the selected tab, there's nothing else to do.
@@ -543,9 +543,8 @@ class SessionRestoreImpl : public content::NotificationObserver {
WebContents* contents =
RestoreTab(tab, tab_index_offset + i, browser, false);
if (contents) {
- RestoredTab restored_tab;
- restored_tab.contents = contents;
- restored_tab.is_active = false;
+ RestoredTab restored_tab(contents, false,
+ tab.extension_app_id.empty(), tab.pinned);
created_contents->push_back(restored_tab);
}
}
@@ -819,5 +818,14 @@ SessionRestore::CallbackSubscription
}
// static
+bool SessionRestore::SmartLoadingEnabled() {
+ base::FieldTrial* trial =
+ base::FieldTrialList::Find("SessionRestoreBackgroundLoading");
+ if (!trial || trial->group_name() != "Smart")
+ return false;
+ return true;
+}
+
+// static
base::CallbackList<void(int)>*
SessionRestore::on_session_restored_callbacks_ = nullptr;
diff --git a/chrome/browser/sessions/session_restore.h b/chrome/browser/sessions/session_restore.h
index e6cb781..9117abe 100644
--- a/chrome/browser/sessions/session_restore.h
+++ b/chrome/browser/sessions/session_restore.h
@@ -98,6 +98,10 @@ class SessionRestore {
static CallbackSubscription RegisterOnSessionRestoredCallback(
const base::Callback<void(int)>& callback);
+ // Returns true if smart session restore is enabled (ie. background tabs are
+ // sorted before being loaded).
+ static bool SmartLoadingEnabled();
+
private:
SessionRestore();
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 5d39701..cea36b2 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -212,6 +212,55 @@ class SessionRestoreTest : public InProcessBrowserTest {
const BrowserList* active_browser_list_;
};
+// Activates the smart restore behaviour and can track the loading of tabs.
+class SmartSessionRestoreTest : public SessionRestoreTest,
+ public content::NotificationObserver {
+ public:
+ SmartSessionRestoreTest() {}
+ void StartObserving(int num_tabs) {
+ num_tabs_ = num_tabs;
+ registrar_.Add(this, content::NOTIFICATION_LOAD_START,
+ content::NotificationService::AllSources());
+ }
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override {
+ switch (type) {
+ case content::NOTIFICATION_LOAD_START: {
+ content::NavigationController* controller =
+ content::Source<content::NavigationController>(source).ptr();
+ web_contents_.push_back(controller->GetWebContents());
+ if (web_contents_.size() == static_cast<size_t>(num_tabs_))
+ message_loop_runner_->Quit();
+ break;
+ }
+ }
+ }
+ const std::vector<content::WebContents*>& web_contents() const {
+ return web_contents_;
+ }
+
+ void WaitForAllTabsToStartLoading() {
+ message_loop_runner_ = new content::MessageLoopRunner;
+ message_loop_runner_->Run();
+ }
+
+ protected:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kForceFieldTrials, "SessionRestoreBackgroundLoading/Smart/");
+ }
+
+ private:
+ content::NotificationRegistrar registrar_;
+ // Ordered by load start order.
+ std::vector<content::WebContents*> web_contents_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+ int num_tabs_;
+
+ DISALLOW_COPY_AND_ASSIGN(SmartSessionRestoreTest);
+};
+
// Verifies that restored tabs have a root window. This is important
// otherwise the wrong information is communicated to the renderer.
// (http://crbug.com/342672).
@@ -1280,3 +1329,76 @@ IN_PROC_BROWSER_TEST_F(SessionRestoreTest, SessionStorageAfterTabReplace) {
ASSERT_EQ(1u, active_browser_list_->size());
EXPECT_EQ(1, new_browser->tab_strip_model()->count());
}
+
+IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
+ ASSERT_TRUE(SessionRestore::SmartLoadingEnabled());
+
+ const int NumTabs = 6;
+
+ // Start observing the loading of tabs, to make sure the order is correct.
+ StartObserving(NumTabs);
+
+ struct TabInfo {
+ GURL url;
+ bool pinned;
+ int expected_load_order;
+ };
+
+ TabInfo tab_info[NumTabs] = {
+ // This will be the foreground tab and will always load first.
+ {GURL("http://google.com/1"), false, 1},
+ {GURL("http://google.com/2"), false, 3},
+ // Internal page, should load last.
+ {GURL(chrome::kChromeUINewTabURL), false, 6},
+ {GURL("http://google.com/4"), false, 4},
+ {GURL("http://google.com/5"), true, 2}, // Pinned, should load second.
+ {GURL("http://google.com/6"), false, 5},
+ };
+
+ // Set up the restore data.
+ std::vector<const sessions::SessionWindow*> session;
+ sessions::SessionWindow window;
+ sessions::SessionTab tab[NumTabs];
+
+ for (int i = 0; i < NumTabs; i++) {
+ SerializedNavigationEntry nav =
+ SerializedNavigationEntryTestHelper::CreateNavigation(
+ tab_info[i].url.spec(), tab_info[i].url.spec().c_str());
+ sync_pb::SessionTab sync_data;
+ sync_data.set_tab_visual_index(0);
+ sync_data.set_current_navigation_index(0);
+ sync_data.add_navigation()->CopyFrom(nav.ToSyncData());
+ sync_data.set_pinned(tab_info[i].pinned);
+ tab[i].SetFromSyncData(sync_data, base::Time::Now());
+ window.tabs.push_back(tab + i);
+ }
+
+ session.push_back(&window);
+ Profile* profile = browser()->profile();
+ ui_test_utils::BrowserAddedObserver window_observer;
+ std::vector<Browser*> browsers = SessionRestore::RestoreForeignSessionWindows(
+ profile, browser()->host_desktop_type(), session.begin(), session.end());
+
+ ASSERT_EQ(1u, browsers.size());
+ ASSERT_TRUE(browsers[0]);
+ ASSERT_EQ(NumTabs, browsers[0]->tab_strip_model()->count());
+
+ WaitForAllTabsToStartLoading();
+
+ ASSERT_EQ(static_cast<size_t>(NumTabs), web_contents().size());
+
+ // Make sure that contents are loaded in the correct order, ie. each tab rank
+ // is higher that its preceding one.
+ std::map<GURL, int> ranks;
+ for (auto t : tab_info)
+ ranks[t.url] = t.expected_load_order;
+ for (size_t i = 1; i < web_contents().size(); i++) {
+ int current_rank = ranks[web_contents()[i]->GetLastCommittedURL()];
+ int previous_rank = ranks[web_contents()[i - 1]->GetLastCommittedURL()];
+ ASSERT_LT(previous_rank, current_rank);
+ }
+
+ // The SessionWindow destructor deletes the tabs, so we have to clear them
+ // here to avoid a crash.
+ window.tabs.clear();
+}
diff --git a/chrome/browser/sessions/session_restore_delegate.cc b/chrome/browser/sessions/session_restore_delegate.cc
index 2c458ec..5f016e0 100644
--- a/chrome/browser/sessions/session_restore_delegate.cc
+++ b/chrome/browser/sessions/session_restore_delegate.cc
@@ -7,8 +7,59 @@
#include "base/metrics/field_trial.h"
#include "chrome/browser/sessions/session_restore_stats_collector.h"
#include "chrome/browser/sessions/tab_loader.h"
+#include "chrome/common/url_constants.h"
#include "components/favicon/content/content_favicon_driver.h"
+namespace {
+
+bool IsInternalPage(const GURL& url) {
+ // There are many chrome:// UI URLs, but only look for the ones that users
+ // are likely to have open. Most of the benefit is from the NTP URL.
+ const char* const kReloadableUrlPrefixes[] = {
+ chrome::kChromeUIDownloadsURL,
+ chrome::kChromeUIHistoryURL,
+ chrome::kChromeUINewTabURL,
+ chrome::kChromeUISettingsURL,
+ };
+ // Prefix-match against the table above. Use strncmp to avoid allocating
+ // memory to convert the URL prefix constants into std::strings.
+ for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) {
+ if (!strncmp(url.spec().c_str(), kReloadableUrlPrefixes[i],
+ strlen(kReloadableUrlPrefixes[i])))
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+SessionRestoreDelegate::RestoredTab::RestoredTab(content::WebContents* contents,
+ bool is_active,
+ bool is_app,
+ bool is_pinned)
+ : contents_(contents),
+ is_active_(is_active),
+ is_app_(is_app),
+ is_internal_page_(IsInternalPage(contents->GetLastCommittedURL())),
+ is_pinned_(is_pinned) {
+}
+
+bool SessionRestoreDelegate::RestoredTab::operator<(
+ const RestoredTab& right) const {
+ // Tab with internal web UI like NTP or Settings are good choices to
+ // defer loading.
+ if (is_internal_page_ != right.is_internal_page_)
+ return !is_internal_page_;
+ // Pinned tabs should be loaded first.
+ if (is_pinned_ != right.is_pinned_)
+ return is_pinned_;
+ // Apps should be loaded before normal tabs.
+ if (is_app_ != right.is_app_)
+ return is_app_;
+ // TODO(georgesak): Add criterion based on recency.
+ return false;
+}
+
// static
void SessionRestoreDelegate::RestoreTabs(
const std::vector<RestoredTab>& tabs,
diff --git a/chrome/browser/sessions/session_restore_delegate.h b/chrome/browser/sessions/session_restore_delegate.h
index 019ddac..63e40ee 100644
--- a/chrome/browser/sessions/session_restore_delegate.h
+++ b/chrome/browser/sessions/session_restore_delegate.h
@@ -17,9 +17,27 @@ class WebContents;
// stats collector.
class SessionRestoreDelegate {
public:
- struct RestoredTab {
- content::WebContents* contents;
- bool is_active;
+ class RestoredTab {
+ public:
+ RestoredTab(content::WebContents* contents,
+ bool is_active,
+ bool is_app,
+ bool is_pinned);
+
+ bool operator<(const RestoredTab& right) const;
+
+ content::WebContents* contents() const { return contents_; }
+ bool is_active() const { return is_active_; }
+ bool is_app() const { return is_app_; }
+ bool is_internal_page() const { return is_internal_page_; }
+ bool is_pinned() const { return is_pinned_; }
+
+ private:
+ content::WebContents* contents_;
+ bool is_active_;
+ bool is_app_; // Browser window is an app.
+ bool is_internal_page_; // Internal web UI page, like NTP or Settings.
+ bool is_pinned_;
};
static void RestoreTabs(const std::vector<RestoredTab>& tabs,
diff --git a/chrome/browser/sessions/session_restore_stats_collector.cc b/chrome/browser/sessions/session_restore_stats_collector.cc
index 30159f9..f593b6e 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector.cc
@@ -165,10 +165,10 @@ void SessionRestoreStatsCollector::AddTabs(
const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) {
tab_count_ += tabs.size();
for (auto& tab : tabs) {
- RegisterForNotifications(&tab.contents->GetController());
- if (tab.is_active) {
+ RegisterForNotifications(&tab.contents()->GetController());
+ if (tab.is_active()) {
RenderWidgetHost* render_widget_host =
- GetRenderWidgetHost(&tab.contents->GetController());
+ GetRenderWidgetHost(&tab.contents()->GetController());
render_widget_hosts_loading_.insert(render_widget_host);
}
}
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index df77a8c..28b80ef 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -85,11 +85,11 @@ void TabLoader::StartLoading(const std::vector<RestoredTab>& tabs) {
// Add the tabs to the list of tabs loading/to load and register them for
// notifications.
for (auto& restored_tab : tabs) {
- if (!restored_tab.is_active)
- tabs_to_load_.push_back(&restored_tab.contents->GetController());
+ if (!restored_tab.is_active())
+ tabs_to_load_.push_back(&restored_tab.contents()->GetController());
else
- tabs_loading_.insert(&restored_tab.contents->GetController());
- RegisterForNotifications(&restored_tab.contents->GetController());
+ tabs_loading_.insert(&restored_tab.contents()->GetController());
+ RegisterForNotifications(&restored_tab.contents()->GetController());
}
// When multiple profiles are using the same TabLoader, another profile might
// already have started loading. In that case, the tabs scheduled for loading