summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorgavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-14 15:11:02 +0000
committergavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-14 15:11:02 +0000
commit5463c700e47a3beac464d95b251335bd90145a4d (patch)
tree31278200af813860369b5210e7d798edc850e6ad /chrome/browser
parente88ff89bb0979a4d3321f9affef7d88f93fcc231 (diff)
downloadchromium_src-5463c700e47a3beac464d95b251335bd90145a4d.zip
chromium_src-5463c700e47a3beac464d95b251335bd90145a4d.tar.gz
chromium_src-5463c700e47a3beac464d95b251335bd90145a4d.tar.bz2
Handle interface to prerenders.
The prerender_manager now returns a PrerenderHandle* when creating a prerender; this is a useful object for canceling the prerender, as well as signaling navigation, etc... BUG=None Review URL: https://chromiumcodereview.appspot.com/10553029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146735 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/predictors/autocomplete_action_predictor.cc19
-rw-r--r--chrome/browser/predictors/autocomplete_action_predictor.h24
-rw-r--r--chrome/browser/prerender/prerender_browsertest.cc140
-rw-r--r--chrome/browser/prerender/prerender_contents.cc135
-rw-r--r--chrome/browser/prerender/prerender_contents.h73
-rw-r--r--chrome/browser/prerender/prerender_final_status.h4
-rw-r--r--chrome/browser/prerender/prerender_handle.cc71
-rw-r--r--chrome/browser/prerender/prerender_handle.h79
-rw-r--r--chrome/browser/prerender/prerender_link_manager.cc68
-rw-r--r--chrome/browser/prerender/prerender_link_manager.h14
-rw-r--r--chrome/browser/prerender/prerender_manager.cc578
-rw-r--r--chrome/browser/prerender/prerender_manager.h212
-rw-r--r--chrome/browser/prerender/prerender_unittest.cc365
-rw-r--r--chrome/browser/ui/omnibox/omnibox_edit_model.cc15
14 files changed, 1137 insertions, 660 deletions
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.cc b/chrome/browser/predictors/autocomplete_action_predictor.cc
index 2ac67fa..642bc7c 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor.cc
@@ -26,6 +26,7 @@
#include "chrome/browser/predictors/predictor_database.h"
#include "chrome/browser/predictors/predictor_database_factory.h"
#include "chrome/browser/prerender/prerender_field_trial.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -119,6 +120,8 @@ AutocompleteActionPredictor::~AutocompleteActionPredictor() {
main_profile_predictor_->incognito_predictor_ = NULL;
else if (incognito_predictor_)
incognito_predictor_->main_profile_predictor_ = NULL;
+ if (prerender_handle_.get())
+ prerender_handle_->OnCancel();
}
void AutocompleteActionPredictor::RegisterTransitionalMatches(
@@ -153,6 +156,22 @@ void AutocompleteActionPredictor::ClearTransitionalMatches() {
transitional_matches_.clear();
}
+void AutocompleteActionPredictor::StartPrerendering(
+ const GURL& url,
+ content::SessionStorageNamespace* session_storage_namespace,
+ const gfx::Size& size) {
+ if (prerender_handle_.get())
+ prerender_handle_->OnNavigateAway();
+ if (prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile_)) {
+ prerender_handle_.reset(
+ prerender_manager->AddPrerenderFromOmnibox(
+ url, session_storage_namespace, size));
+ } else {
+ prerender_handle_.reset();
+ }
+}
+
// Given a match, return a recommended action.
AutocompleteActionPredictor::Action
AutocompleteActionPredictor::RecommendAction(
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.h b/chrome/browser/predictors/autocomplete_action_predictor.h
index d78ced96..34a725a 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.h
+++ b/chrome/browser/predictors/autocomplete_action_predictor.h
@@ -9,6 +9,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/string16.h"
#include "chrome/browser/history/history_types.h"
@@ -25,10 +26,22 @@ class HistoryService;
class PredictorsHandler;
class Profile;
+namespace content {
+class SessionStorageNamespace;
+}
+
+namespace gfx {
+class Size;
+}
+
namespace history {
class URLDatabase;
}
+namespace prerender {
+class PrerenderHandle;
+}
+
namespace predictors {
// This class is responsible for determining the correct predictive network
@@ -78,6 +91,15 @@ class AutocompleteActionPredictor
Action RecommendAction(const string16& user_text,
const AutocompleteMatch& match) const;
+ // Begin prerendering |url| with |session_storage_namespace|. The |size| gives
+ // the initial size for the target prerender. The predictor will run at most
+ // one prerender at a time, so launching a prerender will cancel our previous
+ // prerenders (if any).
+ void StartPrerendering(
+ const GURL& url,
+ content::SessionStorageNamespace* session_storage_namespace,
+ const gfx::Size& size);
+
// Return true if the suggestion type warrants a TCP/IP preconnection.
// i.e., it is now quite likely that the user will select the related domain.
static bool IsPreconnectable(const AutocompleteMatch& match);
@@ -198,6 +220,8 @@ class AutocompleteActionPredictor
// This is cleared after every Omnibox navigation.
std::vector<TransitionalMatch> transitional_matches_;
+ scoped_ptr<prerender::PrerenderHandle> prerender_handle_;
+
// This allows us to predict the effect of confidence threshold changes on
// accuracy. This is cleared after every omnibox navigation.
mutable std::vector<std::pair<GURL, double> > tracked_urls_;
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 104dffc..89f278e 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -19,6 +19,7 @@
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_link_manager.h"
#include "chrome/browser/prerender/prerender_link_manager_factory.h"
#include "chrome/browser/prerender/prerender_manager.h"
@@ -32,6 +33,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h"
@@ -46,6 +48,7 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
+#include "content/public/test/test_navigation_observer.h"
#include "grit/generated_resources.h"
#include "net/base/mock_host_resolver.h"
#include "net/url_request/url_request_context.h"
@@ -113,13 +116,11 @@ bool ShouldRenderPrerenderedPageCorrectly(FinalStatus status) {
case FINAL_STATUS_USED:
case FINAL_STATUS_WINDOW_OPENER:
case FINAL_STATUS_APP_TERMINATING:
- case FINAL_STATUS_FRAGMENT_MISMATCH:
case FINAL_STATUS_CACHE_OR_HISTORY_CLEARED:
// We'll crash the renderer after it's loaded.
case FINAL_STATUS_RENDERER_CRASHED:
case FINAL_STATUS_CANCELLED:
case FINAL_STATUS_DEVTOOLS_ATTACHED:
- case FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH:
return true;
default:
return false;
@@ -229,12 +230,15 @@ class TestPrerenderContents : public PrerenderContents {
}
}
- virtual void AddPendingPrerender(const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size) OVERRIDE {
- PrerenderContents::AddPendingPrerender(url, referrer, size);
+ virtual void AddPendingPrerender(
+ base::WeakPtr<PrerenderHandle> weak_prerender_handle,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size) OVERRIDE {
+ PrerenderContents::AddPendingPrerender(
+ weak_prerender_handle, url, referrer, size);
if (expected_pending_prerenders_ > 0 &&
- pending_prerender_list()->size() == expected_pending_prerenders_) {
+ pending_prerenders().size() == expected_pending_prerenders_) {
MessageLoop::current()->Quit();
}
}
@@ -260,13 +264,28 @@ class TestPrerenderContents : public PrerenderContents {
// Waits until the prerender has |expected_pending_prerenders| pending
// prerenders.
void WaitForPendingPrerenders(size_t expected_pending_prerenders) {
- if (pending_prerender_list()->size() < expected_pending_prerenders) {
+ if (pending_prerenders().size() < expected_pending_prerenders) {
expected_pending_prerenders_ = expected_pending_prerenders;
ui_test_utils::RunMessageLoop();
expected_pending_prerenders_ = 0;
}
- EXPECT_EQ(expected_pending_prerenders, pending_prerender_list()->size());
+ EXPECT_EQ(expected_pending_prerenders, pending_prerenders().size());
+ }
+
+ bool UrlIsPending(const GURL& url) const {
+ for (std::vector<PendingPrerenderInfo>::const_iterator
+ it = pending_prerenders().begin(),
+ end = pending_prerenders().end();
+ it != end;
+ ++it) {
+ if (it->url == url && it->weak_prerender_handle) {
+ EXPECT_TRUE(IsPendingEntry(*it->weak_prerender_handle));
+ EXPECT_TRUE(it->weak_prerender_handle->IsPending());
+ return true;
+ }
+ }
+ return false;
}
// For tests that open the prerender in a new background tab, the RenderView
@@ -276,6 +295,12 @@ class TestPrerenderContents : public PrerenderContents {
int number_of_loads() const { return number_of_loads_; }
+ FinalStatus expected_final_status() const { return expected_final_status_; }
+
+ bool quit_message_loop_on_destruction() const {
+ return quit_message_loop_on_destruction_;
+ }
+
private:
virtual void OnRenderViewHostCreated(
RenderViewHost* new_render_view_host) OVERRIDE {
@@ -492,6 +517,15 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
virtual ~PrerenderBrowserTest() {}
+ content::SessionStorageNamespace* GetSessionStorageNamespace() const {
+ TabContents* tab_contents =
+ current_browser()->tab_strip_model()->GetActiveTabContents();
+ if (!tab_contents)
+ return NULL;
+ return tab_contents->web_contents()->GetRenderViewHost()->
+ GetSessionStorageNamespace();
+ }
+
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
#if defined(ENABLE_SAFE_BROWSING)
SafeBrowsingService::RegisterFactory(safe_browsing_factory_.get());
@@ -705,17 +739,21 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
}
bool UrlIsInPrerenderManager(const std::string& html_file) const {
- GURL dest_url = test_server()->GetURL(html_file);
- return (GetPrerenderManager()->FindEntry(dest_url) != NULL);
+ return UrlIsInPrerenderManager(test_server()->GetURL(html_file));
}
- bool UrlIsInPrerenderManager(const GURL& url) {
- return (GetPrerenderManager()->FindEntry(url) != NULL);
+ bool UrlIsInPrerenderManager(const GURL& url) const {
+ return GetPrerenderManager()->FindPrerenderData(
+ url, GetSessionStorageNamespace()) != NULL;
}
- bool UrlIsPendingInPrerenderManager(const std::string& html_file) const {
+ // This only checks to see if the URL is pending in our TestPrerenderContents.
+ bool UrlIsPending(const std::string& html_file) const {
+ TestPrerenderContents* test_prerender_contents = GetPrerenderContents();
+ if (!test_prerender_contents)
+ return false;
GURL dest_url = test_server()->GetURL(html_file);
- return GetPrerenderManager()->IsPendingEntry(dest_url);
+ return test_prerender_contents->UrlIsPending(dest_url);
}
void set_use_https_src(bool use_https_src_server) {
@@ -766,8 +804,11 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
#endif
TestPrerenderContents* GetPrerenderContents() const {
+ PrerenderManager::PrerenderData* prerender_data =
+ GetPrerenderManager()->FindPrerenderData(
+ dest_url_, GetSessionStorageNamespace());
return static_cast<TestPrerenderContents*>(
- GetPrerenderManager()->FindEntry(dest_url_));
+ prerender_data ? prerender_data->contents() : NULL);
}
void set_loader_path(const std::string& path) {
@@ -852,6 +893,15 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
prerender_contents_factory_);
FinalStatus expected_final_status = expected_final_status_queue.front();
+ // We construct launch_nav_observer so that we can be certain our loader
+ // page has finished loading before continuing. This prevents ambiguous
+ // NOTIFICATION_LOAD_STOP events from making tests flaky.
+ WebContents* web_contents = chrome::GetActiveWebContents(current_browser());
+ ui_test_utils::WindowedNotificationObserver loader_nav_observer(
+ content::NOTIFICATION_LOAD_STOP,
+ content::Source<NavigationController>(
+ &web_contents->GetController()));
+
// ui_test_utils::NavigateToURL uses its own observer and message loop.
// Since the test needs to wait until the prerendered page has stopped
// loading, rather than the page directly navigated to, need to
@@ -861,6 +911,9 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
false));
ui_test_utils::RunMessageLoop();
+ // Now that we've run the prerender until it stopped loading, we can now
+ // also make sure the launcher has finished loading.
+ loader_nav_observer.Wait();
TestPrerenderContents* prerender_contents = GetPrerenderContents();
@@ -884,7 +937,8 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
} else {
// In the failure case, we should have removed |dest_url_| from the
// prerender_manager. We ignore dummy PrerenderContents (as indicated
- // by not having started).
+ // by not having started), and PrerenderContents that are expected to
+ // be left in the manager until the test finishes.
EXPECT_TRUE(prerender_contents == NULL ||
!prerender_contents->prerendering_has_started());
}
@@ -951,12 +1005,24 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
RenderViewHost* render_view_host =
chrome::GetActiveWebContents(current_browser())->GetRenderViewHost();
+
render_view_host->ExecuteJavascriptInWebFrame(
- string16(),
- ASCIIToUTF16(javascript_function_name));
+ string16(), ASCIIToUTF16(javascript_function_name));
- // Run message loop until the prerender contents is destroyed.
- ui_test_utils::RunMessageLoop();
+ if (prerender_contents->quit_message_loop_on_destruction()) {
+ // Run message loop until the prerender contents is destroyed.
+ ui_test_utils::RunMessageLoop();
+ } else {
+ // We don't expect to pick up a running prerender, so instead
+ // observe one navigation.
+ content::TestNavigationObserver observer(
+ content::NotificationService::AllSources(), NULL, 1);
+ base::RunLoop run_loop;
+ observer.WaitForObservation(
+ base::Bind(&ui_test_utils::RunThisRunLoop,
+ base::Unretained(&run_loop)),
+ ui_test_utils::GetQuitTaskForRunLoop(&run_loop));
+ }
}
WaitForLoadPrerenderContentsFactory* prerender_contents_factory_;
@@ -1439,14 +1505,14 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderInfiniteLoop) {
// Next url should be in pending list but not an active entry.
EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileB));
- EXPECT_TRUE(UrlIsPendingInPrerenderManager(kHtmlFileB));
+ EXPECT_TRUE(UrlIsPending(kHtmlFileB));
NavigateToDestURL();
// Make sure the PrerenderContents for the next url is now in the manager
// and not pending.
EXPECT_TRUE(UrlIsInPrerenderManager(kHtmlFileB));
- EXPECT_FALSE(UrlIsPendingInPrerenderManager(kHtmlFileB));
+ EXPECT_FALSE(UrlIsPending(kHtmlFileB));
}
// Checks that we don't prerender in an infinite loop and multiple links are
@@ -1474,8 +1540,8 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderInfiniteLoopMultiple) {
// Next url should be in pending list but not an active entry.
EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileB));
EXPECT_FALSE(UrlIsInPrerenderManager(kHtmlFileC));
- EXPECT_TRUE(UrlIsPendingInPrerenderManager(kHtmlFileB));
- EXPECT_TRUE(UrlIsPendingInPrerenderManager(kHtmlFileC));
+ EXPECT_TRUE(UrlIsPending(kHtmlFileB));
+ EXPECT_TRUE(UrlIsPending(kHtmlFileC));
NavigateToDestURL();
@@ -1486,8 +1552,8 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderInfiniteLoopMultiple) {
bool url_c_is_active_prerender = UrlIsInPrerenderManager(kHtmlFileC);
EXPECT_TRUE((url_b_is_active_prerender || url_c_is_active_prerender) &&
!(url_b_is_active_prerender && url_c_is_active_prerender));
- EXPECT_FALSE(UrlIsPendingInPrerenderManager(kHtmlFileB));
- EXPECT_FALSE(UrlIsPendingInPrerenderManager(kHtmlFileC));
+ EXPECT_FALSE(UrlIsPending(kHtmlFileB));
+ EXPECT_FALSE(UrlIsPending(kHtmlFileC));
}
// See crbug.com/131836.
@@ -1640,7 +1706,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderRendererCrash) {
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
DISABLED_PrerenderPageNavigateFragment) {
PrerenderTestURL("files/prerender/prerender_fragment.html",
- FINAL_STATUS_FRAGMENT_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
NavigateToURL("files/prerender/prerender_fragment.html#fragment");
}
@@ -1651,7 +1717,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
DISABLED_PrerenderFragmentNavigatePage) {
PrerenderTestURL("files/prerender/prerender_fragment.html#fragment",
- FINAL_STATUS_FRAGMENT_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
NavigateToURL("files/prerender/prerender_fragment.html");
}
@@ -1662,7 +1728,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
DISABLED_PrerenderFragmentNavigateFragment) {
PrerenderTestURL("files/prerender/prerender_fragment.html#other_fragment",
- FINAL_STATUS_FRAGMENT_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
NavigateToURL("files/prerender/prerender_fragment.html#fragment");
}
@@ -1674,7 +1740,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
DISABLED_PrerenderClientRedirectFromFragment) {
PrerenderTestURL(
CreateClientRedirect("files/prerender/prerender_fragment.html#fragment"),
- FINAL_STATUS_FRAGMENT_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
2);
NavigateToURL("files/prerender/prerender_fragment.html");
}
@@ -1686,7 +1752,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
DISABLED_PrerenderClientRedirectToFragment) {
PrerenderTestURL(
CreateClientRedirect("files/prerender/prerender_fragment.html"),
- FINAL_STATUS_FRAGMENT_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
2);
NavigateToURL("files/prerender/prerender_fragment.html#fragment");
}
@@ -1848,7 +1914,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPrint) {
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
PrerenderSameDomainWindowOpenerWindowOpen) {
PrerenderTestURL("files/prerender/prerender_page.html",
- FINAL_STATUS_WINDOW_OPENER,
+ FINAL_STATUS_APP_TERMINATING,
1);
OpenDestURLViaWindowOpen();
}
@@ -1858,7 +1924,7 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
PrerenderSameDomainWindowOpenerClickTarget) {
PrerenderTestURL("files/prerender/prerender_page.html",
- FINAL_STATUS_WINDOW_OPENER,
+ FINAL_STATUS_APP_TERMINATING,
1);
OpenDestURLViaClickTarget();
}
@@ -2141,21 +2207,21 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderClickNewWindow) {
PrerenderTestURL("files/prerender/prerender_page_with_link.html",
- FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
OpenDestURLViaClickNewWindow();
}
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderClickNewForegroundTab) {
PrerenderTestURL("files/prerender/prerender_page_with_link.html",
- FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
OpenDestURLViaClickNewForegroundTab();
}
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderClickNewBackgroundTab) {
PrerenderTestURL("files/prerender/prerender_page_with_link.html",
- FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH,
+ FINAL_STATUS_APP_TERMINATING,
1);
OpenDestURLViaClickNewBackgroundTab();
}
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 6267ec2..0b3e1b9 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/prerender/prerender_contents.h"
#include <algorithm>
+#include <functional>
#include <utility>
#include "base/process_util.h"
@@ -12,6 +13,7 @@
#include "chrome/browser/history/history_tab_helper.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/prerender/prerender_final_status.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_render_view_host_observer.h"
#include "chrome/browser/prerender/prerender_tracker.h"
@@ -26,6 +28,7 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/session_storage_namespace.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_view.h"
@@ -35,29 +38,13 @@ using content::DownloadItem;
using content::OpenURLParams;
using content::RenderViewHost;
using content::ResourceRedirectDetails;
+using content::SessionStorageNamespace;
using content::WebContents;
namespace prerender {
namespace {
-// Compares URLs ignoring any ref for the purposes of matching URLs when
-// prerendering.
-struct PrerenderURLPredicate {
- explicit PrerenderURLPredicate(const GURL& url)
- : url_(url) {
- }
-
- bool operator()(const GURL& url) const {
- return url.scheme() == url_.scheme() &&
- url.host() == url_.host() &&
- url.port() == url_.port() &&
- url.path() == url_.path() &&
- url.query() == url_.query();
- }
- GURL url_;
-};
-
// Tells the render process at |child_id| whether |url| is a new prerendered
// page, or whether |url| is being removed as a prerendered page. Currently
// this will only inform the render process that created the prerendered page
@@ -94,24 +81,6 @@ class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
}
};
-struct PrerenderContents::PendingPrerenderInfo {
- PendingPrerenderInfo(const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size);
- const GURL url;
- const content::Referrer referrer;
- const gfx::Size size;
-};
-
-PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
- const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size)
- : url(url),
- referrer(referrer),
- size(size) {
-}
-
// TabContentsDelegateImpl -----------------------------------------------------
class PrerenderContents::TabContentsDelegateImpl
@@ -123,7 +92,7 @@ class PrerenderContents::TabContentsDelegateImpl
// content::WebContentsDelegate implementation:
virtual WebContents* OpenURLFromTab(WebContents* source,
- const OpenURLParams& params) OVERRIDE {
+ const OpenURLParams& params) OVERRIDE {
// |OpenURLFromTab| is typically called when a frame performs a navigation
// that requires the browser to perform the transition instead of WebKit.
// Examples include prerendering a site that redirects to an app URL,
@@ -219,34 +188,61 @@ class PrerenderContents::TabContentsDelegateImpl
PrerenderContents* prerender_contents_;
};
-void PrerenderContents::AddPendingPrerender(const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size) {
- pending_prerender_list_.push_back(PendingPrerenderInfo(url, referrer, size));
+void PrerenderContents::AddPendingPrerender(
+ const base::WeakPtr<PrerenderHandle> weak_prerender_handle,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size) {
+ pending_prerenders_.push_back(
+ PendingPrerenderInfo(weak_prerender_handle, url, referrer, size));
}
-bool PrerenderContents::IsPendingEntry(const GURL& url) const {
- for (PendingPrerenderList::const_iterator it =
- pending_prerender_list_.begin();
- it != pending_prerender_list_.end();
+bool PrerenderContents::IsPendingEntry(
+ const PrerenderHandle& prerender_handle) const {
+ for (std::vector<PendingPrerenderInfo>::const_iterator it =
+ pending_prerenders_.begin();
+ it != pending_prerenders_.end();
++it) {
- if (it->url == url)
+ if (it->weak_prerender_handle.get() == &prerender_handle)
return true;
}
return false;
}
void PrerenderContents::StartPendingPrerenders() {
- PendingPrerenderList pending_prerender_list;
- pending_prerender_list.swap(pending_prerender_list_);
- for (PendingPrerenderList::iterator it = pending_prerender_list.begin();
+ SessionStorageNamespace* session_storage_namespace = NULL;
+ if (RenderViewHost* render_view_host = GetRenderViewHostMutable())
+ session_storage_namespace = render_view_host->GetSessionStorageNamespace();
+ DCHECK(child_id_ == -1 || session_storage_namespace);
+
+ std::vector<PendingPrerenderInfo> pending_prerender_list;
+ pending_prerender_list.swap(pending_prerenders_);
+ for (std::vector<PendingPrerenderInfo>::iterator it =
+ pending_prerender_list.begin();
it != pending_prerender_list.end();
++it) {
- prerender_manager_->AddPrerenderFromLinkRelPrerender(
- child_id_, route_id_, it->url, it->referrer, it->size);
+ if (it->weak_prerender_handle && it->weak_prerender_handle->IsValid()) {
+ prerender_manager_->StartPendingPrerender(
+ it->weak_prerender_handle.get(), ORIGIN_LINK_REL_PRERENDER, child_id_,
+ it->url, it->referrer, it->size, session_storage_namespace);
+ }
}
}
+PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
+ const base::WeakPtr<PrerenderHandle> weak_prerender_handle,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size)
+ : weak_prerender_handle(weak_prerender_handle),
+ url(url),
+ referrer(referrer),
+ size(size) {
+}
+
+PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
+}
+
PrerenderContents::PrerenderContents(
PrerenderManager* prerender_manager,
PrerenderTracker* prerender_tracker,
@@ -262,6 +258,7 @@ PrerenderContents::PrerenderContents(
referrer_(referrer),
profile_(profile),
page_id_(0),
+ session_storage_namespace_id_(-1),
has_stopped_loading_(false),
has_finished_loading_(false),
final_status_(FINAL_STATUS_MAX),
@@ -275,6 +272,13 @@ PrerenderContents::PrerenderContents(
DCHECK(prerender_manager != NULL);
}
+void PrerenderContents::MakeIntoDummyReplacementOf(
+ const PrerenderContents* original_prerender_contents) {
+ load_start_time_ = original_prerender_contents->load_start_time_;
+ session_storage_namespace_id_ =
+ original_prerender_contents->session_storage_namespace_id_;
+}
+
bool PrerenderContents::Init() {
return AddAliasURL(prerender_url_);
}
@@ -287,7 +291,7 @@ PrerenderContents::Factory* PrerenderContents::CreateFactory() {
void PrerenderContents::StartPrerendering(
int creator_child_id,
const gfx::Size& size,
- content::SessionStorageNamespace* session_storage_namespace,
+ SessionStorageNamespace* session_storage_namespace,
bool is_control_group) {
DCHECK(profile_ != NULL);
DCHECK(!size.IsEmpty());
@@ -298,11 +302,15 @@ void PrerenderContents::StartPrerendering(
DCHECK_EQ(1U, alias_urls_.size());
creator_child_id_ = creator_child_id;
+ session_storage_namespace_id_ = session_storage_namespace->id();
size_ = size;
InformRenderProcessAboutPrerender(prerender_url_, true,
creator_child_id_);
+ DCHECK(load_start_time_.is_null());
+ load_start_time_ = base::TimeTicks::Now();
+
// Everything after this point sets up the WebContents object and associated
// RenderView for the prerender page. Don't do this for members of the
// control group.
@@ -358,9 +366,6 @@ void PrerenderContents::StartPrerendering(
this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
content::Source<WebContents>(GetWebContents()));
- DCHECK(load_start_time_.is_null());
- load_start_time_ = base::TimeTicks::Now();
-
// Transfer over the user agent override.
new_contents->SetUserAgentOverride(
prerender_manager_->config().user_agent_override);
@@ -494,7 +499,7 @@ void PrerenderContents::OnRenderViewHostCreated(
}
WebContents* PrerenderContents::CreateWebContents(
- content::SessionStorageNamespace* session_storage_namespace) {
+ SessionStorageNamespace* session_storage_namespace) {
return WebContents::Create(profile_, NULL, MSG_ROUTING_NONE, NULL,
session_storage_namespace);
}
@@ -546,17 +551,15 @@ void PrerenderContents::AddAliasURLsFromOtherPrerenderContents(
}
}
-bool PrerenderContents::MatchesURL(const GURL& url, GURL* matching_url) const {
- std::vector<GURL>::const_iterator matching_url_iterator =
- std::find_if(alias_urls_.begin(),
- alias_urls_.end(),
- PrerenderURLPredicate(url));
- if (matching_url_iterator != alias_urls_.end()) {
- if (matching_url)
- *matching_url = *matching_url_iterator;
- return true;
- }
- return false;
+bool PrerenderContents::Matches(
+ const GURL& url,
+ const SessionStorageNamespace* session_storage_namespace) const {
+ DCHECK(child_id_ == -1 || session_storage_namespace);
+ if (session_storage_namespace &&
+ session_storage_namespace_id_ != session_storage_namespace->id())
+ return false;
+ return std::count_if(alias_urls_.begin(), alias_urls_.end(),
+ std::bind2nd(std::equal_to<GURL>(), url)) != 0;
}
void PrerenderContents::RenderViewGone(base::TerminationStatus status) {
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index 50bc771..7b0a914 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -5,12 +5,12 @@
#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_CONTENTS_H_
#define CHROME_BROWSER_PRERENDER_PRERENDER_CONTENTS_H_
-#include <list>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/prerender/prerender_final_status.h"
@@ -36,6 +36,7 @@ class WebContents;
namespace prerender {
+class PrerenderHandle;
class PrerenderManager;
class PrerenderRenderViewHostObserver;
class PrerenderTracker;
@@ -65,10 +66,6 @@ class PrerenderContents : public content::NotificationObserver,
DISALLOW_COPY_AND_ASSIGN(Factory);
};
- // Information on pages that the prerendered page has tried to prerender.
- struct PendingPrerenderInfo;
- typedef std::list<PendingPrerenderInfo> PendingPrerenderList;
-
// Indicates how this PrerenderContents relates to MatchComplete. This is to
// figure out which histograms to use to record the FinalStatus, Match (record
// all prerenders and control group prerenders) or MatchComplete (record
@@ -79,7 +76,7 @@ class PrerenderContents : public content::NotificationObserver,
// MatchComplete.
MATCH_COMPLETE_DEFAULT,
// A prerender that used to be a regular prerender, but has since been
- // replaced by a MatchComplete dummy. Therefore, we will record this only
+ // replaced by a MatchComplete dummy. Therefore, we will record this only
// for Match, but not for MatchComplete.
MATCH_COMPLETE_REPLACED,
// A prerender that is a MatchComplete dummy replacing a regular prerender.
@@ -94,6 +91,12 @@ class PrerenderContents : public content::NotificationObserver,
virtual ~PrerenderContents();
+ // For MatchComplete correctness, create a dummy replacement prerender
+ // contents to stand in for this prerender contents that (which we are about
+ // to destroy).
+ void MakeIntoDummyReplacementOf(
+ const PrerenderContents* original_prerender_contents);
+
bool Init();
static Factory* CreateFactory();
@@ -155,11 +158,10 @@ class PrerenderContents : public content::NotificationObserver,
base::TimeTicks load_start_time() const { return load_start_time_; }
// Indicates whether this prerendered page can be used for the provided
- // URL, i.e. whether there is a match. |matching_url| is optional and will be
- // set to the URL that is found as a match if it is provided.
- // TODO(gavinp,mmenke): Rework matching to be based on both the URL
- // and the session WebStorage.
- bool MatchesURL(const GURL& url, GURL* matching_url) const;
+ // |url| and |session_storage_namespace|.
+ bool Matches(
+ const GURL& url,
+ const content::SessionStorageNamespace* session_storage_namespace) const;
// content::WebContentsObserver implementation.
virtual void DidStopLoading() OVERRIDE;
@@ -211,19 +213,37 @@ class PrerenderContents : public content::NotificationObserver,
// MouseEvent being dispatched by a link to a website installed as an app.
bool IsCrossSiteNavigationPending() const;
- // Adds a pending prerender to the list.
- virtual void AddPendingPrerender(const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size);
+ // Adds a pending prerender to the list. If |weak_prerender_handle| still
+ // exists when this page is made visible, it will be launched.
+ virtual void AddPendingPrerender(
+ base::WeakPtr<PrerenderHandle> weak_prerender_handle,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size);
// Returns true if |url| corresponds to a pending prerender.
- bool IsPendingEntry(const GURL& url) const;
+ bool IsPendingEntry(const PrerenderHandle& prerender_handle) const;
// Reissues any pending prerender requests from the prerendered page. Also
// clears the list of pending requests.
void StartPendingPrerenders();
protected:
+ // Information on pages that the prerendered page has tried to prerender.
+ struct PendingPrerenderInfo {
+ PendingPrerenderInfo(
+ base::WeakPtr<PrerenderHandle> weak_prerender_handle,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size);
+ ~PendingPrerenderInfo();
+
+ base::WeakPtr<PrerenderHandle> weak_prerender_handle;
+ GURL url;
+ content::Referrer referrer;
+ gfx::Size size;
+ };
+
PrerenderContents(PrerenderManager* prerender_manager,
PrerenderTracker* prerender_tracker,
Profile* profile,
@@ -241,8 +261,8 @@ class PrerenderContents : public content::NotificationObserver,
return notification_registrar_;
}
- const PendingPrerenderList* pending_prerender_list() const {
- return &pending_prerender_list_;
+ const std::vector<PendingPrerenderInfo>& pending_prerenders() const {
+ return pending_prerenders_;
}
bool prerendering_has_been_cancelled() const {
@@ -254,6 +274,11 @@ class PrerenderContents : public content::NotificationObserver,
bool prerendering_has_started_;
+ // Time at which we started to load the URL. This is used to compute
+ // the time elapsed from initiating a prerender until the time the
+ // (potentially only partially) prerendered page is shown to the user.
+ base::TimeTicks load_start_time_;
+
private:
class TabContentsDelegateImpl;
@@ -302,6 +327,11 @@ class PrerenderContents : public content::NotificationObserver,
// such as HTTP redirects or javascript redirects.
std::vector<GURL> alias_urls_;
+ // The session storage namespace id for use in matching. We must save it
+ // rather than get it from the RenderViewHost since in the control group
+ // we won't have a RenderViewHost.
+ int64 session_storage_namespace_id_;
+
bool has_stopped_loading_;
// True when the main frame has finished loading.
@@ -320,11 +350,6 @@ class PrerenderContents : public content::NotificationObserver,
// Used solely to prevent double deletion.
bool prerendering_has_been_cancelled_;
- // Time at which we started to load the URL. This is used to compute
- // the time elapsed from initiating a prerender until the time the
- // (potentially only partially) prerendered page is shown to the user.
- base::TimeTicks load_start_time_;
-
// Process Metrics of the render process associated with the
// RenderViewHost for this object.
scoped_ptr<base::ProcessMetrics> process_metrics_;
@@ -347,7 +372,7 @@ class PrerenderContents : public content::NotificationObserver,
uint8 experiment_id_;
// List of all pages the prerendered page has tried to prerender.
- PendingPrerenderList pending_prerender_list_;
+ std::vector<PendingPrerenderInfo> pending_prerenders_;
// The process that created the child id.
int creator_child_id_;
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h
index 9bad336..1ad3508 100644
--- a/chrome/browser/prerender/prerender_final_status.h
+++ b/chrome/browser/prerender/prerender_final_status.h
@@ -41,14 +41,14 @@ enum FinalStatus {
FINAL_STATUS_WINDOW_OPENER = 26,
// Obsolete: FINAL_STATUS_PAGE_ID_CONFLICT = 27,
FINAL_STATUS_SAFE_BROWSING = 28,
- FINAL_STATUS_FRAGMENT_MISMATCH = 29,
+ // Obsolete: FINAL_STATUS_FRAGMENT_MISMATCH = 29,
FINAL_STATUS_SSL_CLIENT_CERTIFICATE_REQUESTED = 30,
FINAL_STATUS_CACHE_OR_HISTORY_CLEARED = 31,
FINAL_STATUS_CANCELLED = 32,
FINAL_STATUS_SSL_ERROR = 33,
FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING = 34,
FINAL_STATUS_DEVTOOLS_ATTACHED = 35,
- FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH = 36,
+ // Obsolete: FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH = 36,
// Obsolete: FINAL_STATUS_NO_USE_GROUP = 37,
// Obsolete: FINAL_STATUS_MATCH_COMPLETE_DUMMY = 38,
FINAL_STATUS_DUPLICATE = 39,
diff --git a/chrome/browser/prerender/prerender_handle.cc b/chrome/browser/prerender/prerender_handle.cc
new file mode 100644
index 0000000..24de791
--- /dev/null
+++ b/chrome/browser/prerender/prerender_handle.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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/prerender/prerender_handle.h"
+
+#include <algorithm>
+
+#include "chrome/browser/prerender/prerender_contents.h"
+
+namespace prerender {
+
+PrerenderHandle::~PrerenderHandle() {
+ DCHECK(!IsValid());
+ // This shouldn't occur, but we also shouldn't leak if it does.
+ if (IsValid())
+ OnCancel();
+}
+
+void PrerenderHandle::OnNavigateAway() {
+ DCHECK(CalledOnValidThread());
+ if (!IsValid())
+ return;
+ prerender_data_->OnNavigateAwayByHandle();
+ prerender_data_.reset();
+}
+
+void PrerenderHandle::OnCancel() {
+ DCHECK(CalledOnValidThread());
+ if (!IsValid())
+ return;
+ prerender_data_->OnCancelByHandle();
+ prerender_data_.reset();
+}
+
+bool PrerenderHandle::IsValid() const {
+ return prerender_data_ != NULL;
+}
+
+bool PrerenderHandle::IsPending() const {
+ DCHECK(CalledOnValidThread());
+ return prerender_data_ && !prerender_data_->contents();
+}
+
+bool PrerenderHandle::IsPrerendering() const {
+ DCHECK(CalledOnValidThread());
+ return prerender_data_ && prerender_data_->contents();
+}
+
+bool PrerenderHandle::IsFinishedLoading() const {
+ DCHECK(CalledOnValidThread());
+ if (!prerender_data_ || IsPending())
+ return false;
+ return prerender_data_->contents()->has_finished_loading();
+}
+
+PrerenderHandle::PrerenderHandle(
+ PrerenderManager::PrerenderData* prerender_data)
+ : prerender_data_(prerender_data->AsWeakPtr()),
+ weak_ptr_factory_(this) {
+ prerender_data->OnNewHandle();
+}
+
+void PrerenderHandle::SwapPrerenderDataWith(
+ PrerenderHandle* other_prerender_handle) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(other_prerender_handle);
+ std::swap(prerender_data_, other_prerender_handle->prerender_data_);
+}
+
+} // namespace prerender
diff --git a/chrome/browser/prerender/prerender_handle.h b/chrome/browser/prerender/prerender_handle.h
new file mode 100644
index 0000000..90a9dcc
--- /dev/null
+++ b/chrome/browser/prerender/prerender_handle.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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_PRERENDER_PRERENDER_HANDLE_H_
+#define CHROME_BROWSER_PRERENDER_PRERENDER_HANDLE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+
+class GURL;
+
+namespace content {
+class SessionStorageNamespace;
+}
+
+namespace prerender {
+
+class PrerenderContents;
+
+// A class representing a running prerender to a client of the PrerenderManager.
+// Methods on PrerenderManager which start prerenders return a caller-owned
+// PrerenderHandle* to the client (or NULL if they are unable to start a
+// prerender). Because the PrerenderManager can stop running prerenders at any
+// time, callers may wish to check PrerenderHandle::IsValid() before operating
+// on their prerenders.
+class PrerenderHandle : public base::NonThreadSafe {
+ public:
+ // Before calling the destructor, the caller must invalidate the handle by
+ // calling either OnNavigateAway or OnCancel.
+ ~PrerenderHandle();
+
+ // The launcher is navigating away from the context that launched this
+ // prerender. The prerender will likely stay alive briefly though, in case we
+ // are going through a redirect chain that will target it. This call
+ // invalidates the handle. If the prerender handle is already invalid, this
+ // call does nothing.
+ void OnNavigateAway();
+
+ // The launcher has taken explicit action to remove this prerender (for
+ // instance, removing a link element from a document). This call invalidates
+ // the handle. If the prerender handle is already invalid, this call does
+ // nothing.
+ void OnCancel();
+
+ // True if the prerender handle is still connected to a (pending or running)
+ // prerender. Handles can become invalid through explicit requests by the
+ // client, such as calling OnCancel() or OnNavigateAway(), and handles
+ // also become invalid when the PrerenderManager cancels prerenders.
+ bool IsValid() const;
+
+ // True if this prerender was launched by a page that was itself being
+ // prerendered, and so has not yet been started.
+ bool IsPending() const;
+
+ // True if this prerender is currently active.
+ bool IsPrerendering() const;
+
+ // True if we started a prerender, and it has finished loading.
+ bool IsFinishedLoading() const;
+
+ private:
+ friend class PrerenderManager;
+
+ explicit PrerenderHandle(PrerenderManager::PrerenderData* prerender_data);
+
+ void SwapPrerenderDataWith(PrerenderHandle* other_prerender_handle);
+
+ base::WeakPtr<PrerenderManager::PrerenderData> prerender_data_;
+ base::WeakPtrFactory<PrerenderHandle> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrerenderHandle);
+};
+
+} // namespace prerender
+
+#endif // CHROME_BROWSER_PRERENDER_PRERENDER_HANDLE_H_
diff --git a/chrome/browser/prerender/prerender_link_manager.cc b/chrome/browser/prerender/prerender_link_manager.cc
index c12e6cc..27793ee 100644
--- a/chrome/browser/prerender/prerender_link_manager.cc
+++ b/chrome/browser/prerender/prerender_link_manager.cc
@@ -9,6 +9,7 @@
#include <utility>
#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -29,6 +30,13 @@ PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
}
PrerenderLinkManager::~PrerenderLinkManager() {
+ for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin();
+ it != ids_to_handle_map_.end();
+ ++it) {
+ PrerenderHandle* prerender_handle = it->second;
+ prerender_handle->OnCancel();
+ delete prerender_handle;
+ }
}
bool PrerenderLinkManager::OnAddPrerender(int child_id,
@@ -44,20 +52,28 @@ bool PrerenderLinkManager::OnAddPrerender(int child_id,
<< ", size = (" << size.width() << ", " << size.height() << ")"
<< ", render_view_route_id = " << render_view_route_id;
+ const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
+ DCHECK_EQ(0U, ids_to_handle_map_.count(child_and_prerender_id));
+
// TODO(gavinp): Add tests to ensure fragments work, then remove this fragment
// clearing code.
url_canon::Replacements<char> replacements;
replacements.ClearRef();
const GURL url = orig_url.ReplaceComponents(replacements);
- if (!manager_->AddPrerenderFromLinkRelPrerender(
- child_id, render_view_route_id, url, referrer, size)) {
- return false;
+ scoped_ptr<PrerenderHandle> prerender_handle(
+ manager_->AddPrerenderFromLinkRelPrerender(
+ child_id, render_view_route_id, url, referrer, size));
+ if (prerender_handle.get()) {
+ std::pair<IdPairToPrerenderHandleMap::iterator, bool> insert_result =
+ ids_to_handle_map_.insert(IdPairToPrerenderHandleMap::value_type(
+ child_and_prerender_id, NULL));
+ DCHECK(insert_result.second);
+ delete insert_result.first->second;
+ insert_result.first->second = prerender_handle.release();
+ return true;
}
- const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
- DCHECK_EQ(0U, ids_to_url_map_.count(child_and_prerender_id));
- ids_to_url_map_.insert(std::make_pair(child_and_prerender_id, url));
- return true;
+ return false;
}
// TODO(gavinp): Once an observer interface is provided down to the WebKit
@@ -69,24 +85,28 @@ void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
DVLOG(2) << "OnCancelPrerender, child_id = " << child_id
<< ", prerender_id = " << prerender_id;
const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
- IdPairToUrlMap::iterator id_url_iter =
- ids_to_url_map_.find(child_and_prerender_id);
- if (id_url_iter == ids_to_url_map_.end()) {
+ IdPairToPrerenderHandleMap::iterator id_to_handle_iter =
+ ids_to_handle_map_.find(child_and_prerender_id);
+ if (id_to_handle_iter == ids_to_handle_map_.end()) {
DVLOG(5) << "... canceling a prerender that doesn't exist.";
return;
}
- const GURL url = id_url_iter->second;
- ids_to_url_map_.erase(id_url_iter);
- manager_->MaybeCancelPrerender(url);
+ PrerenderHandle* prerender_handle = id_to_handle_iter->second;
+ prerender_handle->OnCancel();
+ RemovePrerender(id_to_handle_iter);
}
void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
DVLOG(2) << "OnAbandonPrerender, child_id = " << child_id
<< ", prerender_id = " << prerender_id;
- // TODO(gavinp,cbentzel): Implement reasonable behaviour for
- // navigation away from launcher.
const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
- ids_to_url_map_.erase(child_and_prerender_id);
+ IdPairToPrerenderHandleMap::iterator id_to_handle_iter =
+ ids_to_handle_map_.find(child_and_prerender_id);
+ if (id_to_handle_iter == ids_to_handle_map_.end())
+ return;
+ PrerenderHandle* prerender_handle = id_to_handle_iter->second;
+ prerender_handle->OnNavigateAway();
+ RemovePrerender(id_to_handle_iter);
}
void PrerenderLinkManager::OnChannelClosing(int child_id) {
@@ -96,9 +116,9 @@ void PrerenderLinkManager::OnChannelClosing(int child_id) {
const ChildAndPrerenderIdPair child_and_maximum_prerender_id(
child_id, std::numeric_limits<int>::max());
std::queue<int> prerender_ids_to_abandon;
- for (IdPairToUrlMap::iterator
- i = ids_to_url_map_.lower_bound(child_and_minimum_prerender_id),
- e = ids_to_url_map_.upper_bound(child_and_maximum_prerender_id);
+ for (IdPairToPrerenderHandleMap::iterator
+ i = ids_to_handle_map_.lower_bound(child_and_minimum_prerender_id),
+ e = ids_to_handle_map_.upper_bound(child_and_maximum_prerender_id);
i != e; ++i) {
prerender_ids_to_abandon.push(i->first.second);
}
@@ -111,8 +131,14 @@ void PrerenderLinkManager::OnChannelClosing(int child_id) {
}
bool PrerenderLinkManager::IsEmpty() const {
- return ids_to_url_map_.empty();
+ return ids_to_handle_map_.empty();
}
-} // namespace prerender
+void PrerenderLinkManager::RemovePrerender(
+ const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter) {
+ PrerenderHandle* prerender_handle = id_to_handle_iter->second;
+ delete prerender_handle;
+ ids_to_handle_map_.erase(id_to_handle_iter);
+}
+} // namespace prerender
diff --git a/chrome/browser/prerender/prerender_link_manager.h b/chrome/browser/prerender/prerender_link_manager.h
index 5f091c2..d9e9c00 100644
--- a/chrome/browser/prerender/prerender_link_manager.h
+++ b/chrome/browser/prerender/prerender_link_manager.h
@@ -9,7 +9,6 @@
#include <utility>
#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
#include "chrome/browser/profiles/profile_keyed_service.h"
#include "googleurl/src/gurl.h"
@@ -25,6 +24,7 @@ class Size;
namespace prerender {
+class PrerenderHandle;
class PrerenderManager;
// PrerenderLinkManager implements the API on Link elements for all documents
@@ -69,14 +69,20 @@ class PrerenderLinkManager : public ProfileKeyedService {
friend class PrerenderTest;
typedef std::pair<int, int> ChildAndPrerenderIdPair;
- typedef std::map<ChildAndPrerenderIdPair, GURL> IdPairToUrlMap;
+ typedef std::map<ChildAndPrerenderIdPair, PrerenderHandle*>
+ IdPairToPrerenderHandleMap;
- void RemovePrerender(const IdPairToUrlMap::iterator& id_url_iter);
+ void RemovePrerender(
+ const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter);
bool IsEmpty() const;
PrerenderManager* manager_;
- IdPairToUrlMap ids_to_url_map_;
+
+ // A map from child process id and prerender id to PrerenderHandles. We map
+ // from this pair because the prerender ids are only unique within their
+ // renderer process.
+ IdPairToPrerenderHandleMap ids_to_handle_map_;
DISALLOW_COPY_AND_ASSIGN(PrerenderLinkManager);
};
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 5d04091..6eee18a 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -4,7 +4,8 @@
#include "chrome/browser/prerender/prerender_manager.h"
-#include <set>
+#include <algorithm>
+#include <functional>
#include <string>
#include <vector>
@@ -24,6 +25,7 @@
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/prerender/prerender_field_trial.h"
#include "chrome/browser/prerender/prerender_final_status.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_histograms.h"
#include "chrome/browser/prerender/prerender_history.h"
#include "chrome/browser/prerender/prerender_local_predictor.h"
@@ -106,10 +108,8 @@ bool NeedMatchCompleteDummyForFinalStatus(FinalStatus final_status) {
final_status != FINAL_STATUS_MANAGER_SHUTDOWN &&
final_status != FINAL_STATUS_APP_TERMINATING &&
final_status != FINAL_STATUS_WINDOW_OPENER &&
- final_status != FINAL_STATUS_FRAGMENT_MISMATCH &&
final_status != FINAL_STATUS_CACHE_OR_HISTORY_CLEARED &&
final_status != FINAL_STATUS_CANCELLED &&
- final_status != FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH &&
final_status != FINAL_STATUS_DEVTOOLS_ATTACHED &&
final_status != FINAL_STATUS_CROSS_SITE_NAVIGATION_PENDING;
}
@@ -171,18 +171,6 @@ int PrerenderManager::prerenders_per_session_count_ = 0;
PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
PRERENDER_MODE_ENABLED;
-struct PrerenderManager::PrerenderContentsData {
- PrerenderContents* contents_;
- base::Time start_time_;
- int active_count_;
- PrerenderContentsData(PrerenderContents* contents, base::Time start_time)
- : contents_(contents),
- start_time_(start_time),
- active_count_(1) {
- CHECK(contents);
- }
-};
-
struct PrerenderManager::NavigationRecord {
GURL url_;
base::TimeTicks time_;
@@ -217,25 +205,32 @@ void PrerenderManager::Shutdown() {
DoShutdown();
}
-bool PrerenderManager::AddPrerenderFromLinkRelPrerender(
+PrerenderHandle* PrerenderManager::AddPrerenderFromLinkRelPrerender(
int process_id,
int route_id,
const GURL& url,
const content::Referrer& referrer,
- gfx::Size size) {
+ const gfx::Size& size) {
#if defined(OS_ANDROID)
// TODO(jcivelli): http://crbug.com/113322 We should have an option to disable
// link-prerender and enable omnibox-prerender only.
return false;
#else
- std::pair<int, int> child_route_id_pair(process_id, route_id);
- PrerenderContentsDataList::iterator it =
- FindPrerenderContentsForChildRouteIdPair(child_route_id_pair);
- if (it != prerender_list_.end()) {
+ DCHECK(!size.IsEmpty());
+ if (PrerenderData* parent_prerender_data =
+ FindPrerenderDataForChildAndRoute(process_id, route_id)) {
// Instead of prerendering from inside of a running prerender, we will defer
// this request until its launcher is made visible.
- it->contents_->AddPendingPrerender(url, referrer, size);
- return true;
+ if (PrerenderContents* contents = parent_prerender_data->contents_) {
+ pending_prerender_list_.push_back(
+ linked_ptr<PrerenderData>(new PrerenderData(this)));
+ PrerenderHandle* prerender_handle =
+ new PrerenderHandle(pending_prerender_list_.back().get());
+ contents->AddPendingPrerender(
+ prerender_handle->weak_ptr_factory_.GetWeakPtr(),
+ url, referrer, size);
+ return prerender_handle;
+ }
}
// Unit tests pass in a process_id == -1.
@@ -243,21 +238,10 @@ bool PrerenderManager::AddPrerenderFromLinkRelPrerender(
if (process_id != -1) {
RenderViewHost* source_render_view_host =
RenderViewHost::FromID(process_id, route_id);
- if (!source_render_view_host || !source_render_view_host->GetView())
- return false;
+ if (!source_render_view_host)
+ return NULL;
session_storage_namespace =
source_render_view_host->GetSessionStorageNamespace();
-
- if (size.IsEmpty()) {
- // Use the size of the tab requesting the prerendering.
- WebContents* web_contents =
- WebContents::FromRenderViewHost(source_render_view_host);
- if (web_contents && web_contents->GetView()) {
- gfx::Rect container_bounds;
- web_contents->GetView()->GetContainerBounds(&container_bounds);
- size = container_bounds.size();
- }
- }
}
return AddPrerender(ORIGIN_LINK_REL_PRERENDER,
@@ -266,44 +250,31 @@ bool PrerenderManager::AddPrerenderFromLinkRelPrerender(
#endif
}
-bool PrerenderManager::AddPrerenderFromOmnibox(
+PrerenderHandle* PrerenderManager::AddPrerenderFromOmnibox(
const GURL& url,
SessionStorageNamespace* session_storage_namespace,
- gfx::Size size) {
+ const gfx::Size& size) {
if (!IsOmniboxEnabled(profile_))
- return false;
- return AddPrerender(ORIGIN_OMNIBOX, -1, url,
- content::Referrer(), size,
+ return NULL;
+ return AddPrerender(ORIGIN_OMNIBOX, -1, url, content::Referrer(), size,
session_storage_namespace);
}
-void PrerenderManager::MaybeCancelPrerender(const GURL& url) {
- PrerenderContentsDataList::iterator it = FindPrerenderContentsForURL(url);
- if (it == prerender_list_.end())
- return;
- PrerenderContentsData& prerender_contents_data = *it;
- if (--prerender_contents_data.active_count_ == 0)
- prerender_contents_data.contents_->Destroy(FINAL_STATUS_CANCELLED);
-}
-
void PrerenderManager::DestroyPrerenderForRenderView(
int process_id, int view_id, FinalStatus final_status) {
DCHECK(CalledOnValidThread());
- PrerenderContentsDataList::iterator it =
- FindPrerenderContentsForChildRouteIdPair(
- std::make_pair(process_id, view_id));
- if (it != prerender_list_.end()) {
- PrerenderContents* prerender_contents = it->contents_;
- prerender_contents->Destroy(final_status);
+ if (PrerenderData* prerender_data =
+ FindPrerenderDataForChildAndRoute(process_id, view_id)) {
+ prerender_data->contents_->Destroy(final_status);
}
}
void PrerenderManager::CancelAllPrerenders() {
DCHECK(CalledOnValidThread());
- while (!prerender_list_.empty()) {
- PrerenderContentsData data = prerender_list_.front();
- DCHECK(data.contents_);
- data.contents_->Destroy(FINAL_STATUS_CANCELLED);
+ while (!active_prerender_list_.empty()) {
+ PrerenderContents* prerender_contents =
+ active_prerender_list_.front()->contents();
+ prerender_contents->Destroy(FINAL_STATUS_CANCELLED);
}
}
@@ -312,27 +283,37 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
DCHECK(CalledOnValidThread());
DCHECK(!IsWebContentsPrerendering(web_contents));
- scoped_ptr<PrerenderContents> prerender_contents(
- GetEntryButNotSpecifiedWC(url, web_contents));
- if (prerender_contents.get() == NULL)
+ RenderViewHost* old_render_view_host = web_contents->GetRenderViewHost();
+
+ DeleteOldEntries();
+ DeletePendingDeleteEntries();
+ PrerenderData* prerender_data = FindPrerenderData(
+ url, old_render_view_host->GetSessionStorageNamespace());
+ if (!prerender_data)
+ return false;
+ DCHECK(prerender_data->contents_);
+ if (IsNoSwapInExperiment(prerender_data->contents_->experiment_id()))
return false;
+ if (TabContents* new_tab_contents =
+ prerender_data->contents_->prerender_contents()) {
+ if (web_contents == new_tab_contents->web_contents())
+ return false; // Do not swap in to ourself.
+ }
+
+ scoped_ptr<PrerenderContents> prerender_contents(prerender_data->contents_);
+ std::list<linked_ptr<PrerenderData> >::iterator to_erase =
+ FindIteratorForPrerenderContents(prerender_contents.get());
+ DCHECK(active_prerender_list_.end() != to_erase);
+ DCHECK_EQ(prerender_data, to_erase->get());
+ active_prerender_list_.erase(to_erase);
+
// Do not use the prerendered version if there is an opener object.
if (web_contents->HasOpener()) {
prerender_contents.release()->Destroy(FINAL_STATUS_WINDOW_OPENER);
return false;
}
- // Even if we match, the location.hash might be different. Record this as a
- // separate final status.
- GURL matching_url;
- bool url_matches = prerender_contents->MatchesURL(url, &matching_url);
- DCHECK(url_matches);
- if (url_matches && url.ref() != matching_url.ref()) {
- prerender_contents.release()->Destroy(FINAL_STATUS_FRAGMENT_MISMATCH);
- return false;
- }
-
// If we are just in the control group (which can be detected by noticing
// that prerendering hasn't even started yet), record that |web_contents| now
// would be showing a prerendered contents, but otherwise, don't do anything.
@@ -359,22 +340,6 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
return false;
}
- // If the session storage namespaces don't match, cancel the prerender.
- RenderViewHost* old_render_view_host = web_contents->GetRenderViewHost();
- RenderViewHost* new_render_view_host =
- prerender_contents->prerender_contents()->web_contents()->
- GetRenderViewHost();
- DCHECK(old_render_view_host);
- DCHECK(new_render_view_host);
- if (old_render_view_host->GetSessionStorageNamespace() !=
- new_render_view_host->GetSessionStorageNamespace()) {
- DestroyAndMarkMatchCompleteAsUsed(
- prerender_contents.release(),
- FINAL_STATUS_SESSION_STORAGE_NAMESPACE_MISMATCH);
- return false;
- }
-
- // If we don't want to use prerenders at all, we are done.
// For bookkeeping purposes, we need to mark this WebContents to
// reflect that it would have been prerendered.
if (GetMode() == PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP) {
@@ -393,6 +358,8 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
if (!prerender_tracker_->TryUse(child_id, route_id))
return false;
+ // At this point, we've determined that we will use the prerender.
+
if (!prerender_contents->load_start_time().is_null()) {
histograms_->RecordTimeUntilUsed(GetCurrentTimeTicks() -
prerender_contents->load_start_time(),
@@ -403,10 +370,17 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
histograms_->RecordUsedPrerender(prerender_contents->origin());
prerender_contents->set_final_status(FINAL_STATUS_USED);
+ RenderViewHost* new_render_view_host =
+ prerender_contents->prerender_contents()->web_contents()->
+ GetRenderViewHost();
new_render_view_host->Send(
new PrerenderMsg_SetIsPrerendering(new_render_view_host->GetRoutingID(),
false));
+ // Start pending prerender requests from the PrerenderContents, if there are
+ // any.
+ prerender_contents->StartPendingPrerenders();
+
TabContents* new_tab_contents =
prerender_contents->ReleasePrerenderContents();
TabContents* old_tab_contents = TabContents::FromWebContents(web_contents);
@@ -439,10 +413,6 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents,
DCHECK(prerender_tab_helper != NULL);
prerender_tab_helper->PrerenderSwappedIn();
- // Start pending prerender requests from the PrerenderContents, if there are
- // any.
- prerender_contents->StartPendingPrerenders();
-
if (old_tab_contents->web_contents()->NeedToFireBeforeUnload()) {
// Schedule the delete to occur after the tab has run its unload handlers.
on_close_tab_contents_deleters_.push_back(
@@ -469,50 +439,46 @@ void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry,
DCHECK_EQ(0, std::count(pending_delete_list_.begin(),
pending_delete_list_.end(), entry));
- for (PrerenderContentsDataList::iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- if (it->contents_ == entry) {
- bool swapped_in_dummy_replacement = false;
-
- // If this PrerenderContents is being deleted due to a cancellation,
- // we need to create a dummy replacement for PPLT accounting purposes
- // for the Match Complete group.
- // This is the case if the cancellation is for any reason that would not
- // occur in the control group case.
- if (entry->match_complete_status() ==
- PrerenderContents::MATCH_COMPLETE_DEFAULT &&
- NeedMatchCompleteDummyForFinalStatus(final_status) &&
- ActuallyPrerendering()) {
- // TODO(tburkard): I'd like to DCHECK that we are actually prerendering.
- // However, what if new conditions are added and
- // NeedMatchCompleteDummyForFinalStatus, is not being updated. Not sure
- // what's the best thing to do here. For now, I will just check whether
- // we are actually prerendering.
- entry->set_match_complete_status(
- PrerenderContents::MATCH_COMPLETE_REPLACED);
- if (PrerenderContents* dummy_replacement_prerender_contents =
- CreatePrerenderContents(entry->prerender_url(),
- entry->referrer(),
- entry->origin(),
- entry->experiment_id())) {
- dummy_replacement_prerender_contents->set_match_complete_status(
- PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
- if (!dummy_replacement_prerender_contents->Init())
- break;
- dummy_replacement_prerender_contents->
- AddAliasURLsFromOtherPrerenderContents(entry);
- dummy_replacement_prerender_contents->set_match_complete_status(
- PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
- it->contents_ = dummy_replacement_prerender_contents;
- swapped_in_dummy_replacement = true;
- }
- }
- if (!swapped_in_dummy_replacement)
- prerender_list_.erase(it);
- break;
+ std::list<linked_ptr<PrerenderData> >::iterator it =
+ FindIteratorForPrerenderContents(entry);
+
+ // If this PrerenderContents is being deleted due to a cancellation,
+ // we need to create a dummy replacement for PPLT accounting purposes
+ // for the Match Complete group.
+ // This is the case if the cancellation is for any reason that would not
+ // occur in the control group case.
+ if (it != active_prerender_list_.end()) {
+ if (entry->match_complete_status() ==
+ PrerenderContents::MATCH_COMPLETE_DEFAULT &&
+ NeedMatchCompleteDummyForFinalStatus(final_status) &&
+ ActuallyPrerendering()) {
+ // TODO(tburkard): I'd like to DCHECK that we are actually prerendering.
+ // However, what if new conditions are added and
+ // NeedMatchCompleteDummyForFinalStatus, is not being updated. Not sure
+ // what's the best thing to do here. For now, I will just check whether
+ // we are actually prerendering.
+ entry->set_match_complete_status(
+ PrerenderContents::MATCH_COMPLETE_REPLACED);
+ PrerenderContents* dummy_replacement_prerender_contents =
+ CreatePrerenderContents(entry->prerender_url(), entry->referrer(),
+ entry->origin(), entry->experiment_id());
+ dummy_replacement_prerender_contents->MakeIntoDummyReplacementOf(entry);
+ DCHECK(dummy_replacement_prerender_contents);
+
+ dummy_replacement_prerender_contents->set_match_complete_status(
+ PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
+ DCHECK(dummy_replacement_prerender_contents->Init());
+ dummy_replacement_prerender_contents->
+ AddAliasURLsFromOtherPrerenderContents(entry);
+ dummy_replacement_prerender_contents->set_match_complete_status(
+ PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
+
+ it->get()->contents_ = dummy_replacement_prerender_contents;
+ } else {
+ active_prerender_list_.erase(it);
}
}
+
AddToHistory(entry);
pending_delete_list_.push_back(entry);
@@ -623,10 +589,12 @@ bool PrerenderManager::IsNoUseGroup() {
bool PrerenderManager::IsWebContentsPrerendering(
WebContents* web_contents) const {
DCHECK(CalledOnValidThread());
- for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin();
- it != prerender_list_.end();
+ for (std::list<linked_ptr<PrerenderData> >::const_iterator it =
+ active_prerender_list_.begin();
+ it != active_prerender_list_.end();
++it) {
- TabContents* prerender_tab_contents = it->contents_->prerender_contents();
+ TabContents* prerender_tab_contents =
+ it->get()->contents_->prerender_contents();
if (prerender_tab_contents &&
prerender_tab_contents->web_contents() == web_contents) {
return true;
@@ -647,12 +615,6 @@ bool PrerenderManager::IsWebContentsPrerendering(
return false;
}
-bool PrerenderManager::DidPrerenderFinishLoading(const GURL& url) const {
- DCHECK(CalledOnValidThread());
- PrerenderContents* contents = FindEntry(url);
- return contents ? contents->has_finished_loading() : false;
-}
-
void PrerenderManager::MarkWebContentsAsPrerendered(WebContents* web_contents) {
DCHECK(CalledOnValidThread());
prerendered_tab_contents_set_.insert(web_contents);
@@ -768,22 +730,6 @@ void PrerenderManager::AddCondition(const PrerenderCondition* condition) {
prerender_conditions_.push_back(condition);
}
-bool PrerenderManager::IsPendingEntry(const GURL& url) const {
- DCHECK(CalledOnValidThread());
- for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- if (it->contents_->IsPendingEntry(url))
- return true;
- }
- return false;
-}
-
-bool PrerenderManager::IsPrerendering(const GURL& url) const {
- DCHECK(CalledOnValidThread());
- return (FindEntry(url) != NULL);
-}
-
void PrerenderManager::RecordNavigation(const GURL& url) {
DCHECK(CalledOnValidThread());
@@ -792,31 +738,134 @@ void PrerenderManager::RecordNavigation(const GURL& url) {
}
// protected
+PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager)
+ : manager_(manager), contents_(NULL), handle_count_(0) {
+}
+
+PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager,
+ PrerenderContents* contents)
+ : manager_(manager), contents_(contents), handle_count_(0) {
+}
+
+void PrerenderManager::PrerenderData::OnNewHandle() {
+ DCHECK(contents_ || handle_count_ == 0) <<
+ "Cannot create multiple handles to a pending prerender.";
+ ++handle_count_;
+}
+
+void PrerenderManager::PrerenderData::OnNavigateAwayByHandle() {
+ // TODO(gavinp): Implement reasonable behaviour for navigation away from
+ // launcher. We can't just call OnCancel, because many cases have redirect
+ // chains that will eventually lead to the correct prerendered page, and we
+ // don't want to delete our prerender just as it is going to be used.
+
+ if (!contents_) {
+ DCHECK_EQ(1, handle_count_);
+ // Pending prerenders are not maintained in the active_prerender_list_, so
+ // they will not get normal expiry. Since this prerender hasn't even been
+ // launched yet, and it's held by a page that is being prerendered, we will
+ // just delete it.
+ manager_->DestroyPendingPrerenderData(this);
+ }
+}
+
+void PrerenderManager::PrerenderData::OnCancelByHandle() {
+ DCHECK_LE(1, handle_count_);
+ DCHECK(contents_ || handle_count_ == 1);
+
+ if (--handle_count_ == 0) {
+ if (contents_) {
+ // This will eventually remove this object from active_prerender_list_,
+ // triggering the linked_ptr auto deletion.
+ contents_->Destroy(FINAL_STATUS_CANCELLED);
+ } else {
+ manager_->DestroyPendingPrerenderData(this);
+ }
+ }
+}
+
+PrerenderManager::PrerenderData::~PrerenderData() {
+}
+
void PrerenderManager::SetPrerenderContentsFactory(
PrerenderContents::Factory* prerender_contents_factory) {
DCHECK(CalledOnValidThread());
prerender_contents_factory_.reset(prerender_contents_factory);
}
+void PrerenderManager::StartPendingPrerender(
+ PrerenderHandle* existing_prerender_handle,
+ Origin origin,
+ int process_id,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size,
+ content::SessionStorageNamespace* session_storage_namespace) {
+ DCHECK(existing_prerender_handle);
+ DCHECK(existing_prerender_handle->IsValid());
+ DCHECK(existing_prerender_handle->IsPending());
+
+ DVLOG(6) << "StartPendingPrerender";
+ DVLOG(6) << "existing_prerender_handle->handle_count_ = " <<
+ existing_prerender_handle->prerender_data_->handle_count_;
+
+ DCHECK(process_id == -1 || session_storage_namespace);
+
+ scoped_ptr<PrerenderHandle> swap_prerender_handle(AddPrerender(
+ origin, process_id, url, referrer, size, session_storage_namespace));
+ if (swap_prerender_handle.get()) {
+ // AddPrerender has returned a new prerender handle to us. We want to make
+ // |existing_prerender_handle| active, so swap the underlying PrerenderData
+ // between the two handles, and delete our old handle (which will release
+ // our entry in the pending_prerender_list_).
+ existing_prerender_handle->SwapPrerenderDataWith(
+ swap_prerender_handle.get());
+ swap_prerender_handle->OnCancel();
+ return;
+ }
+
+ // We could not start our Prerender. Canceling the existing handle will make
+ // it return false for PrerenderHandle::IsPending(), and will release the
+ // PrerenderData from pending_prerender_list_.
+ existing_prerender_handle->OnCancel();
+}
+
+void PrerenderManager::DestroyPendingPrerenderData(
+ PrerenderData* pending_prerender_data) {
+ for (std::list<linked_ptr<PrerenderData> >::iterator
+ it = pending_prerender_list_.begin();
+ it != pending_prerender_list_.end();
+ ++it) {
+ if (it->get() == pending_prerender_data) {
+ DCHECK_GE(1, it->get()->handle_count_);
+ pending_prerender_list_.erase(it);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
void PrerenderManager::DoShutdown() {
DestroyAllContents(FINAL_STATUS_MANAGER_SHUTDOWN);
STLDeleteElements(&prerender_conditions_);
on_close_tab_contents_deleters_.reset();
profile_ = NULL;
+
+ DCHECK(active_prerender_list_.empty());
}
// private
-bool PrerenderManager::AddPrerender(
+PrerenderHandle* PrerenderManager::AddPrerender(
Origin origin,
int process_id,
const GURL& url_arg,
const content::Referrer& referrer,
- gfx::Size size,
+ const gfx::Size& size,
SessionStorageNamespace* session_storage_namespace) {
DCHECK(CalledOnValidThread());
if (!IsEnabled())
- return false;
+ return NULL;
if (origin == ORIGIN_LINK_REL_PRERENDER &&
IsGoogleSearchResultURL(referrer.url)) {
@@ -840,10 +889,10 @@ bool PrerenderManager::AddPrerender(
// histogram tracking.
histograms_->RecordPrerender(origin, url_arg);
- if (PrerenderContentsData* prerender_contents_data = FindEntryData(url)) {
- ++prerender_contents_data->active_count_;
+ if (PrerenderData* preexisting_prerender_data =
+ FindPrerenderData(url, session_storage_namespace)) {
RecordFinalStatus(origin, experiment, FINAL_STATUS_DUPLICATE);
- return true;
+ return new PrerenderHandle(preexisting_prerender_data);
}
// Do not prerender if there are too many render processes, and we would
@@ -861,7 +910,7 @@ bool PrerenderManager::AddPrerender(
profile_, url) &&
!content::RenderProcessHost::run_renderer_in_process()) {
RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES);
- return false;
+ return NULL;
}
#endif
@@ -871,64 +920,39 @@ bool PrerenderManager::AddPrerender(
// this doesn't make sense as the next prerender request will be triggered
// by a navigation and is unlikely to be the same site.
RecordFinalStatus(origin, experiment, FINAL_STATUS_RATE_LIMIT_EXCEEDED);
- return false;
+ return NULL;
}
PrerenderContents* prerender_contents = CreatePrerenderContents(
url, referrer, origin, experiment);
if (!prerender_contents || !prerender_contents->Init())
- return false;
+ return NULL;
histograms_->RecordPrerenderStarted(origin);
// TODO(cbentzel): Move invalid checks here instead of PrerenderContents?
- PrerenderContentsData data(prerender_contents, GetCurrentTime());
-
- prerender_list_.push_back(data);
+ active_prerender_list_.push_back(
+ linked_ptr<PrerenderData>(new PrerenderData(this, prerender_contents)));
+ PrerenderHandle* prerender_handle =
+ new PrerenderHandle(active_prerender_list_.back().get());
last_prerender_start_time_ = GetCurrentTimeTicks();
- if (size.IsEmpty())
- size = config_.default_tab_bounds.size();
+ gfx::Size contents_size =
+ size.IsEmpty() ? config_.default_tab_bounds.size() : size;
- data.contents_->StartPrerendering(process_id, size, session_storage_namespace,
- control_group_behavior);
+ prerender_contents->StartPrerendering(process_id, contents_size,
+ session_storage_namespace,
+ control_group_behavior);
- while (prerender_list_.size() > config_.max_elements) {
- data = prerender_list_.front();
- prerender_list_.pop_front();
- data.contents_->Destroy(FINAL_STATUS_EVICTED);
+ while (active_prerender_list_.size() > config_.max_elements) {
+ prerender_contents = active_prerender_list_.front()->contents_;
+ DCHECK(prerender_contents);
+ prerender_contents->Destroy(FINAL_STATUS_EVICTED);
}
- StartSchedulingPeriodicCleanups();
- return true;
-}
-
-PrerenderContents* PrerenderManager::GetEntry(const GURL& url) {
- return GetEntryButNotSpecifiedWC(url, NULL);
-}
-PrerenderContents* PrerenderManager::GetEntryButNotSpecifiedWC(
- const GURL& url,
- WebContents* wc) {
- DCHECK(CalledOnValidThread());
- DeleteOldEntries();
- DeletePendingDeleteEntries();
- for (PrerenderContentsDataList::iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- PrerenderContents* prerender_contents = it->contents_;
- if (prerender_contents->MatchesURL(url, NULL) &&
- !IsNoSwapInExperiment(prerender_contents->experiment_id())) {
- if (!prerender_contents->prerender_contents() ||
- !wc ||
- prerender_contents->prerender_contents()->web_contents() != wc) {
- prerender_list_.erase(it);
- return prerender_contents;
- }
- }
- }
- // Entry not found.
- return NULL;
+ StartSchedulingPeriodicCleanups();
+ return prerender_handle;
}
void PrerenderManager::StartSchedulingPeriodicCleanups() {
@@ -941,10 +965,7 @@ void PrerenderManager::StartSchedulingPeriodicCleanups() {
&PrerenderManager::PeriodicCleanup);
}
-void PrerenderManager::MaybeStopSchedulingPeriodicCleanups() {
- if (!prerender_list_.empty())
- return;
-
+void PrerenderManager::StopSchedulingPeriodicCleanups() {
DCHECK(CalledOnValidThread());
repeating_timer_.Stop();
}
@@ -953,22 +974,22 @@ void PrerenderManager::PeriodicCleanup() {
DCHECK(CalledOnValidThread());
DeleteOldTabContents();
DeleteOldEntries();
+ if (active_prerender_list_.empty())
+ StopSchedulingPeriodicCleanups();
// Grab a copy of the current PrerenderContents pointers, so that we
// will not interfere with potential deletions of the list.
std::vector<PrerenderContents*> prerender_contents;
- for (PrerenderContentsDataList::iterator it = prerender_list_.begin();
- it != prerender_list_.end();
- ++it) {
- DCHECK(it->contents_);
- prerender_contents.push_back(it->contents_);
- }
- for (std::vector<PrerenderContents*>::iterator it =
- prerender_contents.begin();
- it != prerender_contents.end();
- ++it) {
- (*it)->DestroyWhenUsingTooManyResources();
+ prerender_contents.reserve(active_prerender_list_.size());
+ for (std::list<linked_ptr<PrerenderData> >::iterator
+ it = active_prerender_list_.begin();
+ it != active_prerender_list_.end();
+ ++it) {
+ prerender_contents.push_back(it->get()->contents_);
}
+ std::for_each(prerender_contents.begin(), prerender_contents.end(),
+ std::mem_fun(
+ &PrerenderContents::DestroyWhenUsingTooManyResources));
DeletePendingDeleteEntries();
}
@@ -986,21 +1007,20 @@ base::TimeDelta PrerenderManager::GetMaxAge() const {
base::TimeDelta::FromSeconds(300) : config_.max_age);
}
-bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const {
+bool PrerenderManager::IsPrerenderFresh(const base::TimeTicks start) const {
DCHECK(CalledOnValidThread());
- base::Time now = GetCurrentTime();
- return (now - start < GetMaxAge());
+ return GetCurrentTimeTicks() - start < GetMaxAge();
}
void PrerenderManager::DeleteOldEntries() {
DCHECK(CalledOnValidThread());
- while (!prerender_list_.empty()) {
- PrerenderContentsData data = prerender_list_.front();
- if (IsPrerenderElementFresh(data.start_time_))
+ while (!active_prerender_list_.empty()) {
+ PrerenderContents* contents = active_prerender_list_.front()->contents_;
+ DCHECK(contents);
+ if (IsPrerenderFresh(contents->load_start_time()))
return;
- data.contents_->Destroy(FINAL_STATUS_TIMED_OUT);
+ contents->Destroy(FINAL_STATUS_TIMED_OUT);
}
- MaybeStopSchedulingPeriodicCleanups();
}
base::Time PrerenderManager::GetCurrentTime() const {
@@ -1030,58 +1050,53 @@ void PrerenderManager::DeletePendingDeleteEntries() {
}
}
-PrerenderManager::PrerenderContentsData* PrerenderManager::FindEntryData(
- const GURL& url) {
- DCHECK(CalledOnValidThread());
- PrerenderContentsDataList::iterator it = FindPrerenderContentsForURL(url);
- if (it == prerender_list_.end())
- return NULL;
- PrerenderContentsData& prerender_contents_data = *it;
- return &prerender_contents_data;
-}
-
-PrerenderContents* PrerenderManager::FindEntry(const GURL& url) const {
- DCHECK(CalledOnValidThread());
- for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin();
- it != prerender_list_.end();
+PrerenderManager::PrerenderData* PrerenderManager::FindPrerenderData(
+ const GURL& url,
+ const SessionStorageNamespace* session_storage_namespace) {
+ for (std::list<linked_ptr<PrerenderData> >::iterator
+ it = active_prerender_list_.begin();
+ it != active_prerender_list_.end();
++it) {
- if (it->contents_->MatchesURL(url, NULL))
- return it->contents_;
+ PrerenderContents* prerender_contents = it->get()->contents_;
+ if (prerender_contents->Matches(url, session_storage_namespace))
+ return it->get();
}
- // Entry not found.
return NULL;
}
-PrerenderManager::PrerenderContentsDataList::iterator
- PrerenderManager::FindPrerenderContentsForChildRouteIdPair(
- const std::pair<int, int>& child_route_id_pair) {
- PrerenderContentsDataList::iterator it = prerender_list_.begin();
- for (; it != prerender_list_.end(); ++it) {
- PrerenderContents* prerender_contents = it->contents_;
+PrerenderManager::PrerenderData*
+PrerenderManager::FindPrerenderDataForChildAndRoute(
+ const int child_id, const int route_id) {
+ for (std::list<linked_ptr<PrerenderData> >::iterator
+ it = active_prerender_list_.begin();
+ it != active_prerender_list_.end();
+ ++it) {
+ PrerenderContents* prerender_contents = it->get()->contents_;
- int child_id;
- int route_id;
- bool has_child_id = prerender_contents->GetChildId(&child_id);
- bool has_route_id = has_child_id &&
- prerender_contents->GetRouteId(&route_id);
+ int contents_child_id;
+ if (!prerender_contents->GetChildId(&contents_child_id))
+ continue;
+ int contents_route_id;
+ if (!prerender_contents->GetRouteId(&contents_route_id))
+ continue;
- if (has_child_id && has_route_id &&
- child_id == child_route_id_pair.first &&
- route_id == child_route_id_pair.second) {
- break;
- }
+ if (contents_child_id == child_id && contents_route_id == route_id)
+ return it->get();
}
- return it;
+ return NULL;
}
-PrerenderManager::PrerenderContentsDataList::iterator
- PrerenderManager::FindPrerenderContentsForURL(const GURL& url) {
- for (PrerenderContentsDataList::iterator it = prerender_list_.begin();
- it != prerender_list_.end(); ++it) {
- if (it->contents_->MatchesURL(url, NULL))
+std::list<linked_ptr<PrerenderManager::PrerenderData> >::iterator
+PrerenderManager::FindIteratorForPrerenderContents(
+ PrerenderContents* prerender_contents) {
+ for (std::list<linked_ptr<PrerenderData> >::iterator
+ it = active_prerender_list_.begin();
+ it != active_prerender_list_.end();
+ ++it) {
+ if (prerender_contents == it->get()->contents_)
return it;
}
- return prerender_list_.end();
+ return active_prerender_list_.end();
}
bool PrerenderManager::DoesRateLimitAllowPrerender() const {
@@ -1143,23 +1158,21 @@ void PrerenderManager::AddToHistory(PrerenderContents* contents) {
Value* PrerenderManager::GetActivePrerendersAsValue() const {
ListValue* list_value = new ListValue();
- for (PrerenderContentsDataList::const_iterator it = prerender_list_.begin();
- it != prerender_list_.end();
+ for (std::list<linked_ptr<PrerenderData> >::const_iterator it =
+ active_prerender_list_.begin();
+ it != active_prerender_list_.end();
++it) {
- Value* prerender_value = it->contents_->GetAsValue();
- if (!prerender_value)
- continue;
- list_value->Append(prerender_value);
+ if (Value* prerender_value = it->get()->contents_->GetAsValue())
+ list_value->Append(prerender_value);
}
return list_value;
}
void PrerenderManager::DestroyAllContents(FinalStatus final_status) {
DeleteOldTabContents();
- while (!prerender_list_.empty()) {
- PrerenderContentsData data = prerender_list_.front();
- prerender_list_.pop_front();
- data.contents_->Destroy(final_status);
+ while (!active_prerender_list_.empty()) {
+ PrerenderContents* contents = active_prerender_list_.front()->contents_;
+ contents->Destroy(final_status);
}
DeletePendingDeleteEntries();
}
@@ -1218,3 +1231,4 @@ PrerenderManager* FindPrerenderManagerUsingRenderProcessId(
}
} // namespace prerender
+
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 1a667cc..3341572 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_
#include <list>
+#include <map>
#include <string>
#include <utility>
@@ -13,6 +14,7 @@
#include "base/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/time.h"
@@ -54,6 +56,7 @@ struct hash<content::WebContents*> {
namespace prerender {
class PrerenderCondition;
+class PrerenderHandle;
class PrerenderHistograms;
class PrerenderHistory;
class PrerenderLocalPredictor;
@@ -100,36 +103,31 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// Entry points for adding prerenders.
// Adds a prerender for |url| if valid. |process_id| and |route_id| identify
- // the RenderView that the prerender request came from. The |size| may be
- // empty, and the current tab size will be used if it is. If the current
- // active tab size cannot be found, we use a default from PrerenderConfig.
- // Returns true if the URL was added, false if it was not.
- // If the launching RenderView is itself prerendering, the prerender is added
- // as a pending prerender.
- bool AddPrerenderFromLinkRelPrerender(
+ // the RenderView that the prerender request came from. If |size| is empty, a
+ // default from the PrerenderConfig is used. Returns a caller-owned
+ // PrerenderHandle* if the URL was added, NULL if it was not. If the launching
+ // RenderView is itself prerendering, the prerender is added as a pending
+ // prerender.
+ PrerenderHandle* AddPrerenderFromLinkRelPrerender(
int process_id,
int route_id,
const GURL& url,
const content::Referrer& referrer,
- gfx::Size size);
+ const gfx::Size& size);
// Adds a prerender for |url| if valid. As the prerender request is coming
// from a source without a RenderViewHost (i.e., the omnibox) we don't have a
// child or route id, or a referrer. This method uses sensible values for
// those. The |session_storage_namespace| matches the namespace of the active
- // tab at the time the prerender is generated from the omnibox.
- bool AddPrerenderFromOmnibox(
+ // tab at the time the prerender is generated from the omnibox. Returns a
+ // caller-owned PrerenderHandle*, or NULL.
+ PrerenderHandle* AddPrerenderFromOmnibox(
const GURL& url,
content::SessionStorageNamespace* session_storage_namespace,
- gfx::Size size);
+ const gfx::Size& size);
- // Request cancelation of a previously added prerender. If the |active_count_|
- // of the prerender is one, it will be canceled. Otherwise, |active_count_|
- // will be decremented by one.
- void MaybeCancelPrerender(const GURL& url);
-
- // Destroy all prerenders for the given child route id pair and assign a final
- // status to them.
+ // If |process_id| and |view_id| refer to a running prerender, destroy
+ // it with |final_status|.
virtual void DestroyPrerenderForRenderView(int process_id,
int view_id,
FinalStatus final_status);
@@ -183,10 +181,6 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// is prerendering a page.
bool IsWebContentsPrerendering(content::WebContents* web_contents) const;
- // Returns true if there is a prerendered page for the given URL and it has
- // finished loading. Only valid if called before MaybeUsePrerenderedPage.
- bool DidPrerenderFinishLoading(const GURL& url) const;
-
// Maintaining and querying the set of WebContents belonging to this
// PrerenderManager that are currently showing prerendered pages.
void MarkWebContentsAsPrerendered(content::WebContents* web_contents);
@@ -236,11 +230,6 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// Adds a condition. This is owned by the PrerenderManager.
void AddCondition(const PrerenderCondition* condition);
- bool IsPendingEntry(const GURL& url) const;
-
- // Returns true if |url| matches any URLs being prerendered.
- bool IsPrerendering(const GURL& url) const;
-
// Records that some visible tab navigated (or was redirected) to the
// provided URL.
void RecordNavigation(const GURL& url);
@@ -250,94 +239,102 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
PrerenderHistograms* histograms() const { return histograms_.get(); }
protected:
+ class PrerenderData : public base::SupportsWeakPtr<PrerenderData> {
+ public:
+ // Constructor for a pending prerender, which will get its contents later.
+ explicit PrerenderData(PrerenderManager* manager);
+
+ // Constructor for an active prerender.
+ PrerenderData(PrerenderManager* manager, PrerenderContents* contents);
+
+ ~PrerenderData();
+
+ // A new PrerenderHandle has been created for this PrerenderData.
+ void OnNewHandle();
+
+ // The launcher associated with a handle is navigating away from the context
+ // that launched this prerender. If the prerender is active, it may stay
+ // alive briefly though, in case we we going through a redirect chain that
+ // will eventually land at it.
+ void OnNavigateAwayByHandle();
+
+ // The launcher associated with a handle has taken explicit action to cancel
+ // this prerender. We may well destroy the prerender in this case if no
+ // other handles continue to track it.
+ void OnCancelByHandle();
+
+ PrerenderContents* contents() { return contents_; }
+
+ private:
+ friend class PrerenderManager;
+
+ PrerenderManager* manager_;
+ PrerenderContents* contents_;
+
+ // The number of distinct PrerenderHandles created for |this|, including
+ // ones that have called PrerenderData::OnNavigateAwayByHandle(), but not
+ // counting the ones that have called PrerenderData::OnCancelByHandle(). For
+ // pending prerenders, this will always be 1, since the PrerenderManager
+ // only merges handles of running prerenders.
+ int handle_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrerenderData);
+ };
+
void SetPrerenderContentsFactory(
PrerenderContents::Factory* prerender_contents_factory);
+ // Adds a prerender from a pending Prerender, called by
+ // PrerenderContents::StartPendingPrerenders.
+ void StartPendingPrerender(
+ PrerenderHandle* existing_prerender_handle,
+ Origin origin,
+ int process_id,
+ const GURL& url,
+ const content::Referrer& referrer,
+ const gfx::Size& size,
+ content::SessionStorageNamespace* session_storage_namespace);
+
+ void DestroyPendingPrerenderData(PrerenderData* pending_prerender_data);
+
// Utility method that is called from the virtual Shutdown method on this
// class but is called directly from the TestPrerenderManager in the unit
// tests.
void DoShutdown();
private:
- // Test that needs needs access to internal functions.
friend class PrerenderBrowserTest;
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, AliasURLTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, CancelAllTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest,
- CancelOmniboxRemovesOmniboxTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest,
- CancelOmniboxDoesNotRemoveLinkTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, ClearTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, ControlGroup);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, DropOldestRequestTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, DropSecondRequestTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, ExpireTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, FoundTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, FragmentMatchesFragmentTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, FragmentMatchesPageTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerAbandon);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerAddTwiceAbandonTwice);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerAddTwiceCancelTwice);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest,
- LinkManagerAddTwiceCancelTwiceThenAbandonTwice);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerCancel);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerCancelThenAbandon);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerCancelThenAddAgain);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerCancelTwice);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerExpireThenAddAgain);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, LinkManagerExpireThenCancel);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, NotSoRecentlyVisited);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, PageMatchesFragmentTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, PendingPrerenderTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, PPLTDummy);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, RateLimitInWindowTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, RateLimitOutsideWindowTest);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, RecentlyVisitedPPLTDummy);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, SourceRenderViewClosed);
- FRIEND_TEST_ALL_PREFIXES(PrerenderTest, TwoElementPrerenderTest);
-
- struct PrerenderContentsData;
- struct NavigationRecord;
+ friend class PrerenderContents;
+ friend class PrerenderHandle;
+ friend class UnitTestPrerenderManager;
class OnCloseTabContentsDeleter;
+ struct NavigationRecord;
- typedef std::list<PrerenderContentsData> PrerenderContentsDataList;
typedef base::hash_map<content::WebContents*, bool> WouldBePrerenderedMap;
// Time window for which we record old navigations, in milliseconds.
static const int kNavigationRecordWindowMs = 5000;
- // Adds a prerender for |url| from referrer |referrer| initiated from the
- // child process specified by |child_id|. The |origin| specifies how the
- // prerender was added. If the |size| is empty, then
- // PrerenderContents::StartPrerendering will instead use the size of the
- // currently active tab. If the current active tab size cannot be found, it
- // then uses a default from PrerenderConfig.
- bool AddPrerender(
+ void OnCancelPrerenderHandle(PrerenderData* prerender_data);
+
+ // Adds a prerender for |url| from |referrer| initiated from the process
+ // |child_id|. The |origin| specifies how the prerender was added. If |size|
+ // is empty, then PrerenderContents::StartPrerendering will instead use a
+ // default from PrerenderConfig. Returns a PrerenderHandle*, owned by the
+ // caller, or NULL.
+ PrerenderHandle* AddPrerender(
Origin origin,
int child_id,
const GURL& url,
const content::Referrer& referrer,
- gfx::Size size,
+ const gfx::Size& size,
content::SessionStorageNamespace* session_storage_namespace);
- // Retrieves the PrerenderContents object for the specified URL, if it
- // has been prerendered. The caller will then have ownership of the
- // PrerenderContents object and is responsible for freeing it.
- // Returns NULL if the specified URL has not been prerendered.
- PrerenderContents* GetEntry(const GURL& url);
-
- // Identical to GetEntry, with one exception:
- // The WebContents specified indicates the WC in which to swap the
- // prerendering into. If the WebContents specified is the one
- // to doing the prerendered itself, will return NULL.
- PrerenderContents* GetEntryButNotSpecifiedWC(const GURL& url,
- content::WebContents* wc);
-
- // Starts scheduling periodic cleanups.
void StartSchedulingPeriodicCleanups();
- // Stops scheduling periodic cleanups if they're no longer needed.
- void MaybeStopSchedulingPeriodicCleanups();
+ void StopSchedulingPeriodicCleanups();
+
+ void EvictOldestPrerendersIfNecessary();
// Deletes stale and cancelled prerendered PrerenderContents, as well as
// WebContents that have been replaced by prerendered WebContents.
@@ -351,7 +348,7 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
void PostCleanupTask();
base::TimeDelta GetMaxAge() const;
- bool IsPrerenderElementFresh(const base::Time start) const;
+ bool IsPrerenderFresh(base::TimeTicks start) const;
void DeleteOldEntries();
virtual base::Time GetCurrentTime() const;
virtual base::TimeTicks GetCurrentTimeTicks() const;
@@ -365,20 +362,21 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// list.
void DeletePendingDeleteEntries();
- // Finds the specified PrerenderContentsData/PrerenderContents and returns it,
- // if it exists. Returns NULL otherwise. Unlike GetEntry, the
- // PrerenderManager maintains ownership of the PrerenderContents.
- PrerenderContentsData* FindEntryData(const GURL& url);
- PrerenderContents* FindEntry(const GURL& url) const;
+ // Finds the active PrerenderData object for a running prerender matching
+ // |url| and |session_storage_namespace|.
+ PrerenderData* FindPrerenderData(
+ const GURL& url,
+ const content::SessionStorageNamespace* session_storage_namespace);
- // Returns the iterator to the PrerenderContentsData entry that is being
- // prerendered from the given child route id pair.
- PrerenderContentsDataList::iterator
- FindPrerenderContentsForChildRouteIdPair(
- const std::pair<int, int>& child_route_id_pair);
+ // If |child_id| and |route_id| correspond to a RenderView that is an active
+ // prerender, returns the PrerenderData object for that prerender. Otherwise,
+ // returns NULL.
+ PrerenderData* FindPrerenderDataForChildAndRoute(int child_id, int route_id);
- PrerenderContentsDataList::iterator
- FindPrerenderContentsForURL(const GURL& url);
+ // Given the |prerender_contents|, find the iterator in active_prerender_list_
+ // correponding to the given prerender.
+ std::list<linked_ptr<PrerenderData> >::iterator
+ FindIteratorForPrerenderContents(PrerenderContents* prerender_contents);
bool DoesRateLimitAllowPrerender() const;
@@ -439,8 +437,12 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
PrerenderTracker* prerender_tracker_;
- // List of prerendered elements.
- PrerenderContentsDataList prerender_list_;
+ // List of all running prerenders. It is kept sorted, in increasing order by
+ // expiry time. This list owns the PrerenderData objects contained in it.
+ std::list<linked_ptr<PrerenderData> > active_prerender_list_;
+
+ // List of all pending prerenders.
+ std::list<linked_ptr<PrerenderData> > pending_prerender_list_;
// List of recent navigations in this profile, sorted by ascending
// navigate_time_.
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index b6403d9..5a17ee0 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -7,6 +7,7 @@
#include "base/message_loop.h"
#include "base/time.h"
#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_link_manager.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_origin.h"
@@ -24,20 +25,17 @@ using content::Referrer;
namespace prerender {
+class UnitTestPrerenderManager;
+
namespace {
class DummyPrerenderContents : public PrerenderContents {
public:
- DummyPrerenderContents(PrerenderManager* prerender_manager,
+ DummyPrerenderContents(UnitTestPrerenderManager* test_prerender_manager,
PrerenderTracker* prerender_tracker,
const GURL& url,
Origin origin,
- FinalStatus expected_final_status)
- : PrerenderContents(prerender_manager, prerender_tracker,
- NULL, url, Referrer(), origin,
- PrerenderManager::kNoExperiment),
- expected_final_status_(expected_final_status) {
- }
+ FinalStatus expected_final_status);
virtual ~DummyPrerenderContents() {
EXPECT_EQ(expected_final_status_, final_status());
@@ -47,14 +45,7 @@ class DummyPrerenderContents : public PrerenderContents {
int ALLOW_UNUSED creator_child_id,
const gfx::Size& ALLOW_UNUSED size,
content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace,
- bool is_control_group) OVERRIDE {
- // In the base PrerenderContents implementation, StartPrerendering will
- // be called even when the PrerenderManager is part of the control group,
- // but it will early exit before actually creating a new RenderView if
- // |is_control_group| is true;
- if (!is_control_group)
- prerendering_has_started_ = true;
- }
+ bool is_control_group) OVERRIDE;
virtual bool GetChildId(int* child_id) const OVERRIDE {
*child_id = 0;
@@ -73,12 +64,20 @@ class DummyPrerenderContents : public PrerenderContents {
}
private:
+ UnitTestPrerenderManager* test_prerender_manager_;
FinalStatus expected_final_status_;
};
-class TestPrerenderManager : public PrerenderManager {
+const gfx::Size kSize(640, 480);
+
+} // namespace
+
+class UnitTestPrerenderManager : public PrerenderManager {
public:
- explicit TestPrerenderManager(PrerenderTracker* prerender_tracker)
+ using PrerenderManager::kNavigationRecordWindowMs;
+ using PrerenderManager::GetMaxAge;
+
+ explicit UnitTestPrerenderManager(PrerenderTracker* prerender_tracker)
: PrerenderManager(&profile_, prerender_tracker),
time_(base::Time::Now()),
time_ticks_(base::TimeTicks::Now()),
@@ -87,23 +86,36 @@ class TestPrerenderManager : public PrerenderManager {
set_rate_limit_enabled(false);
}
- virtual ~TestPrerenderManager() {
+ virtual ~UnitTestPrerenderManager() {
if (next_prerender_contents()) {
next_prerender_contents_.release()->Destroy(
FINAL_STATUS_MANAGER_SHUTDOWN);
}
- // Set the final status for all PrerenderContents with an expected final
- // status of FINAL_STATUS_USED. These values are normally set when the
- // prerendered RVH is swapped into a tab, which doesn't happen in these
- // unit tests.
- for (ScopedVector<PrerenderContents>::iterator it =
- used_prerender_contents_.begin();
- it != used_prerender_contents_.end(); ++it) {
- (*it)->set_final_status(FINAL_STATUS_USED);
- }
DoShutdown();
}
+ PrerenderContents* FindEntry(const GURL& url) {
+ DeleteOldEntries();
+ DeletePendingDeleteEntries();
+ if (PrerenderData* data = FindPrerenderData(url, NULL))
+ return data->contents();
+ return NULL;
+ }
+
+ PrerenderContents* FindAndUseEntry(const GURL& url) {
+ PrerenderData* prerender_data = FindPrerenderData(url, NULL);
+ if (!prerender_data)
+ return NULL;
+ PrerenderContents* prerender_contents = prerender_data->contents();
+ prerender_contents->set_final_status(FINAL_STATUS_USED);
+ std::list<linked_ptr<PrerenderData> >::iterator to_erase =
+ FindIteratorForPrerenderContents(prerender_contents);
+ DCHECK(to_erase != active_prerender_list_.end());
+ active_prerender_list_.erase(to_erase);
+ prerender_contents->StartPendingPrerenders();
+ return prerender_contents;
+ }
+
void AdvanceTime(base::TimeDelta delta) {
time_ += delta;
}
@@ -159,6 +171,15 @@ class TestPrerenderManager : public PrerenderManager {
return next_prerender_contents_.get();
}
+ // from PrerenderManager
+ virtual base::Time GetCurrentTime() const OVERRIDE {
+ return time_;
+ }
+
+ virtual base::TimeTicks GetCurrentTimeTicks() const OVERRIDE {
+ return time_ticks_;
+ }
+
private:
void SetNextPrerenderContents(DummyPrerenderContents* prerender_contents) {
DCHECK(!next_prerender_contents_.get());
@@ -167,13 +188,6 @@ class TestPrerenderManager : public PrerenderManager {
used_prerender_contents_.push_back(prerender_contents);
}
- virtual base::Time GetCurrentTime() const OVERRIDE {
- return time_;
- }
-
- virtual base::TimeTicks GetCurrentTimeTicks() const OVERRIDE {
- return time_ticks_;
- }
virtual PrerenderContents* CreatePrerenderContents(
const GURL& url,
@@ -181,8 +195,8 @@ class TestPrerenderManager : public PrerenderManager {
Origin origin,
uint8 experiment_id) OVERRIDE {
DCHECK(next_prerender_contents_.get());
- DCHECK_EQ(next_prerender_contents_->prerender_url(), url);
- DCHECK_EQ(next_prerender_contents_->origin(), origin);
+ EXPECT_EQ(url, next_prerender_contents_->prerender_url());
+ EXPECT_EQ(origin, next_prerender_contents_->origin());
return next_prerender_contents_.release();
}
@@ -208,7 +222,32 @@ class RestorePrerenderMode {
PrerenderManager::PrerenderManagerMode prev_mode_;
};
-} // namespace
+DummyPrerenderContents::DummyPrerenderContents(
+ UnitTestPrerenderManager* test_prerender_manager,
+ PrerenderTracker* prerender_tracker,
+ const GURL& url,
+ Origin origin,
+ FinalStatus expected_final_status)
+ : PrerenderContents(test_prerender_manager, prerender_tracker,
+ NULL, url, Referrer(), origin,
+ PrerenderManager::kNoExperiment),
+ test_prerender_manager_(test_prerender_manager),
+ expected_final_status_(expected_final_status) {
+}
+
+void DummyPrerenderContents::StartPrerendering(
+ int ALLOW_UNUSED creator_child_id,
+ const gfx::Size& ALLOW_UNUSED size,
+ content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace,
+ bool is_control_group) {
+ // In the base PrerenderContents implementation, StartPrerendering will
+ // be called even when the PrerenderManager is part of the control group,
+ // but it will early exit before actually creating a new RenderView if
+ // |is_control_group| is true;
+ if (!is_control_group)
+ prerendering_has_started_ = true;
+ load_start_time_ = test_prerender_manager_->GetCurrentTimeTicks();
+}
class PrerenderTest : public testing::Test {
public:
@@ -217,7 +256,7 @@ class PrerenderTest : public testing::Test {
PrerenderTest() : ui_thread_(BrowserThread::UI, &message_loop_),
prerender_manager_(
- new TestPrerenderManager(prerender_tracker())),
+ new UnitTestPrerenderManager(prerender_tracker())),
prerender_link_manager_(
new PrerenderLinkManager(prerender_manager_.get())),
last_prerender_id_(0) {
@@ -227,7 +266,11 @@ class PrerenderTest : public testing::Test {
switches::kPrerenderFromOmniboxSwitchValueEnabled);
}
- TestPrerenderManager* prerender_manager() {
+ ~PrerenderTest() {
+ prerender_link_manager_->OnChannelClosing(kDefaultChildId);
+ }
+
+ UnitTestPrerenderManager* prerender_manager() {
return prerender_manager_.get();
}
@@ -252,7 +295,7 @@ class PrerenderTest : public testing::Test {
return prerender_link_manager()->OnAddPrerender(
kDefaultChildId, GetNextPrerenderID(),
url, content::Referrer(),
- gfx::Size(), kDefaultRenderViewRouteId);
+ kSize, kDefaultRenderViewRouteId);
}
private:
@@ -263,18 +306,23 @@ class PrerenderTest : public testing::Test {
// Needed to pass PrerenderManager's DCHECKs.
MessageLoop message_loop_;
content::TestBrowserThread ui_thread_;
- scoped_ptr<TestPrerenderManager> prerender_manager_;
+ scoped_ptr<UnitTestPrerenderManager> prerender_manager_;
scoped_ptr<PrerenderLinkManager> prerender_link_manager_;
int last_prerender_id_;
};
-TEST_F(PrerenderTest, EmptyTest) {
- EXPECT_FALSE(prerender_manager()->MaybeUsePrerenderedPage(
- NULL,
- GURL("http://www.google.com/")));
+TEST_F(PrerenderTest, FoundTest) {
+ GURL url("http://www.google.com/");
+ DummyPrerenderContents* prerender_contents =
+ prerender_manager()->CreateNextPrerenderContents(
+ url,
+ FINAL_STATUS_USED);
+ EXPECT_TRUE(AddSimplePrerender(url));
+ EXPECT_TRUE(prerender_contents->prerendering_has_started());
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
-TEST_F(PrerenderTest, FoundTest) {
+TEST_F(PrerenderTest, DuplicateTest) {
GURL url("http://www.google.com/");
DummyPrerenderContents* prerender_contents =
prerender_manager()->CreateNextPrerenderContents(
@@ -282,7 +330,19 @@ TEST_F(PrerenderTest, FoundTest) {
FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+
+ scoped_ptr<PrerenderHandle> duplicate_prerender_handle(
+ prerender_manager()->AddPrerenderFromLinkRelPrerender(
+ kDefaultChildId, kDefaultRenderViewRouteId, url,
+ Referrer(url, WebKit::WebReferrerPolicyDefault), kSize));
+
+ EXPECT_TRUE(duplicate_prerender_handle->IsValid());
+ EXPECT_TRUE(duplicate_prerender_handle->IsPrerendering());
+
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
+
+ EXPECT_FALSE(duplicate_prerender_handle->IsValid());
+ EXPECT_FALSE(duplicate_prerender_handle->IsPrerendering());
}
// Make sure that if queue a request, and a second prerender request for the
@@ -307,7 +367,7 @@ TEST_F(PrerenderTest, DropSecondRequestTest) {
prerender_manager()->next_prerender_contents());
EXPECT_FALSE(prerender_contents1->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
// Ensure that we expire a prerendered page after the max. permitted time.
@@ -321,9 +381,9 @@ TEST_F(PrerenderTest, ExpireTest) {
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_EQ(null, prerender_manager()->next_prerender_contents());
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- prerender_manager()->AdvanceTime(prerender_manager()->GetMaxAge() +
- base::TimeDelta::FromSeconds(1));
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ prerender_manager()->AdvanceTimeTicks(prerender_manager()->GetMaxAge() +
+ base::TimeDelta::FromSeconds(1));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
}
// LRU Test. Make sure that if we prerender more than one request, that
@@ -348,8 +408,8 @@ TEST_F(PrerenderTest, DropOldestRequestTest) {
EXPECT_EQ(null, prerender_manager()->next_prerender_contents());
EXPECT_TRUE(prerender_contents1->prerendering_has_started());
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
- ASSERT_EQ(prerender_contents1, prerender_manager()->GetEntry(url1));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
+ ASSERT_EQ(prerender_contents1, prerender_manager()->FindAndUseEntry(url1));
}
// Two element prerender test. Ensure that the LRU operates correctly if we
@@ -384,9 +444,9 @@ TEST_F(PrerenderTest, TwoElementPrerenderTest) {
EXPECT_EQ(null, prerender_manager()->next_prerender_contents());
EXPECT_TRUE(prerender_contents2->prerendering_has_started());
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
- ASSERT_EQ(prerender_contents1, prerender_manager()->GetEntry(url1));
- ASSERT_EQ(prerender_contents2, prerender_manager()->GetEntry(url2));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
+ ASSERT_EQ(prerender_contents1, prerender_manager()->FindAndUseEntry(url1));
+ ASSERT_EQ(prerender_contents2, prerender_manager()->FindAndUseEntry(url2));
}
TEST_F(PrerenderTest, AliasURLTest) {
@@ -403,16 +463,18 @@ TEST_F(PrerenderTest, AliasURLTest) {
prerender_manager()->CreateNextPrerenderContents(
url, alias_urls, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
- ASSERT_EQ(NULL, prerender_manager()->GetEntry(not_an_alias_url));
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(alias_url1));
+ ASSERT_EQ(NULL, prerender_manager()->FindEntry(not_an_alias_url));
+ ASSERT_EQ(prerender_contents,
+ prerender_manager()->FindAndUseEntry(alias_url1));
prerender_contents = prerender_manager()->CreateNextPrerenderContents(
url, alias_urls, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(alias_url2));
+ ASSERT_EQ(prerender_contents,
+ prerender_manager()->FindAndUseEntry(alias_url2));
prerender_contents = prerender_manager()->CreateNextPrerenderContents(
url, alias_urls, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
// Test that alias URLs can not be added.
prerender_contents = prerender_manager()->CreateNextPrerenderContents(
@@ -421,7 +483,7 @@ TEST_F(PrerenderTest, AliasURLTest) {
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(AddSimplePrerender(alias_url1));
EXPECT_TRUE(AddSimplePrerender(alias_url2));
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
// Ensure that we ignore prerender requests within the rate limit.
@@ -489,14 +551,92 @@ TEST_F(PrerenderTest, PendingPrerenderTest) {
GURL pending_url("http://news.google.com/");
- EXPECT_TRUE(prerender_manager()->AddPrerenderFromLinkRelPrerender(
- child_id, route_id,
- pending_url, Referrer(url, WebKit::WebReferrerPolicyDefault),
- gfx::Size()));
+ DummyPrerenderContents* pending_prerender_contents =
+ prerender_manager()->CreateNextPrerenderContents(
+ pending_url,
+ ORIGIN_GWS_PRERENDER,
+ FINAL_STATUS_USED);
+ scoped_ptr<PrerenderHandle> pending_prerender_handle(
+ prerender_manager()->AddPrerenderFromLinkRelPrerender(
+ child_id, route_id, pending_url,
+ Referrer(url, WebKit::WebReferrerPolicyDefault), kSize));
+ DCHECK(pending_prerender_handle.get());
+ EXPECT_TRUE(pending_prerender_handle->IsValid());
+ EXPECT_TRUE(pending_prerender_handle->IsPending());
- EXPECT_TRUE(prerender_manager()->IsPendingEntry(pending_url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
+
+ EXPECT_FALSE(pending_prerender_handle->IsPending());
+ ASSERT_EQ(pending_prerender_contents,
+ prerender_manager()->FindAndUseEntry(pending_url));
+}
+
+TEST_F(PrerenderTest, InvalidPendingPrerenderTest) {
+ GURL url("http://www.google.com/");
+ DummyPrerenderContents* prerender_contents =
+ prerender_manager()->CreateNextPrerenderContents(
+ url,
+ FINAL_STATUS_USED);
+ EXPECT_TRUE(AddSimplePrerender(url));
+
+ int child_id;
+ int route_id;
+ ASSERT_TRUE(prerender_contents->GetChildId(&child_id));
+ ASSERT_TRUE(prerender_contents->GetRouteId(&route_id));
+
+ // This pending URL has an unsupported scheme, and won't be able
+ // to start.
+ GURL pending_url("ftp://news.google.com/");
+
+ prerender_manager()->CreateNextPrerenderContents(
+ pending_url,
+ ORIGIN_GWS_PRERENDER,
+ FINAL_STATUS_UNSUPPORTED_SCHEME);
+ scoped_ptr<PrerenderHandle> pending_prerender_handle(
+ prerender_manager()->AddPrerenderFromLinkRelPrerender(
+ child_id, route_id, pending_url,
+ Referrer(url, WebKit::WebReferrerPolicyDefault), kSize));
+ DCHECK(pending_prerender_handle.get());
+ EXPECT_TRUE(pending_prerender_handle->IsValid());
+ EXPECT_TRUE(pending_prerender_handle->IsPending());
+
+ EXPECT_TRUE(prerender_contents->prerendering_has_started());
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
+
+ EXPECT_FALSE(pending_prerender_handle->IsValid());
+ EXPECT_FALSE(pending_prerender_handle->IsPending());
+}
+
+TEST_F(PrerenderTest, CancelPendingPrerenderTest) {
+ GURL url("http://www.google.com/");
+ DummyPrerenderContents* prerender_contents =
+ prerender_manager()->CreateNextPrerenderContents(
+ url,
+ FINAL_STATUS_USED);
+ EXPECT_TRUE(AddSimplePrerender(url));
+
+ int child_id;
+ int route_id;
+ ASSERT_TRUE(prerender_contents->GetChildId(&child_id));
+ ASSERT_TRUE(prerender_contents->GetRouteId(&route_id));
+
+ GURL pending_url("http://news.google.com/");
+
+ scoped_ptr<PrerenderHandle> pending_prerender_handle(
+ prerender_manager()->AddPrerenderFromLinkRelPrerender(
+ child_id, route_id, pending_url,
+ Referrer(url, WebKit::WebReferrerPolicyDefault), kSize));
+ DCHECK(pending_prerender_handle.get());
+ EXPECT_TRUE(pending_prerender_handle->IsValid());
+ EXPECT_TRUE(pending_prerender_handle->IsPending());
+
+ EXPECT_TRUE(prerender_contents->prerendering_has_started());
+
+ pending_prerender_handle->OnCancel();
+ EXPECT_FALSE(pending_prerender_handle->IsValid());
+
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
// Tests that a PrerenderManager created for a browser session in the control
@@ -524,7 +664,7 @@ TEST_F(PrerenderTest, SourceRenderViewClosed) {
FINAL_STATUS_MANAGER_SHUTDOWN);
EXPECT_FALSE(prerender_link_manager()->OnAddPrerender(
100, GetNextPrerenderID(), url,
- Referrer(), gfx::Size(), 200));
+ Referrer(), kSize, 200));
}
// Tests that prerendering is cancelled when we launch a second prerender of
@@ -547,14 +687,14 @@ TEST_F(PrerenderTest, NotSoRecentlyVisited) {
prerender_manager()->RecordNavigation(url);
prerender_manager()->AdvanceTimeTicks(
base::TimeDelta::FromMilliseconds(
- PrerenderManager::kNavigationRecordWindowMs + 500));
+ UnitTestPrerenderManager::kNavigationRecordWindowMs + 500));
DummyPrerenderContents* prerender_contents =
prerender_manager()->CreateNextPrerenderContents(
url, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
// Tests that our PPLT dummy prerender gets created properly.
@@ -573,7 +713,7 @@ TEST_F(PrerenderTest, PPLTDummy) {
// Adding this ftp URL will force the expected unsupported scheme error.
prerender_contents->AddAliasURL(ftp_url);
- ASSERT_EQ(pplt_dummy_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(pplt_dummy_contents, prerender_manager()->FindAndUseEntry(url));
}
// Tests that our PPLT dummy prerender gets created properly, even
@@ -593,29 +733,29 @@ TEST_F(PrerenderTest, RecentlyVisitedPPLTDummy) {
GURL ftp_url("ftp://ftp.google.com/");
prerender_contents->AddAliasURL(ftp_url);
- ASSERT_EQ(pplt_dummy_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(pplt_dummy_contents, prerender_manager()->FindAndUseEntry(url));
}
-// Tests that the prerender manager ignores fragment references when matching
-// prerender URLs in the case the fragment is not in the prerender URL.
-TEST_F(PrerenderTest, PageMatchesFragmentTest) {
- GURL url("http://www.google.com/");
+// Tests that the prerender link manager discards fragments when launching,
+// and matches only occur on URLs without fragments.
+// TODO(gavinp): Drop this test once we are fragment clean.
+TEST_F(PrerenderTest, FragmentDroppedOnLaunchTest) {
GURL fragment_url("http://www.google.com/#test");
+ GURL url("http://www.google.com/");
DummyPrerenderContents* prerender_contents =
prerender_manager()->CreateNextPrerenderContents(url,
FINAL_STATUS_USED);
- EXPECT_TRUE(AddSimplePrerender(url));
+ EXPECT_TRUE(AddSimplePrerender(fragment_url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(fragment_url));
+ EXPECT_FALSE(prerender_manager()->FindEntry(fragment_url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
-// Tests that the prerender manager ignores fragment references when matching
-// prerender URLs in the case the fragment is in the prerender URL.
-// TODO(gavinp): Re-enable this in the Prerender API once we're officially
+// Tests that the prerender manager matches include the fragment.
+// TODO(gavinp): Re-enable this once we are fragment clean.
// fragment clean.
-TEST_F(PrerenderTest, DISABLED_FragmentMatchesPageTest) {
- GURL url("http://www.google.com/");
+TEST_F(PrerenderTest, DISABLED_FragmentMatchesTest) {
GURL fragment_url("http://www.google.com/#test");
DummyPrerenderContents* prerender_contents =
@@ -623,14 +763,14 @@ TEST_F(PrerenderTest, DISABLED_FragmentMatchesPageTest) {
FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(fragment_url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents,
+ prerender_manager()->FindAndUseEntry(fragment_url));
}
-// Tests that the prerender manager ignores fragment references when matching
-// prerender URLs in the case the fragment is in both URLs.
-// TODO(gavinp): Re-enable this in the Prerender API once we're officially
-// fragment clean.
-TEST_F(PrerenderTest, DISABLED_FragmentMatchesFragmentTest) {
+// Tests that the prerender manager uses fragment references when matching
+// prerender URLs in the case a different fragment is in both URLs.
+// TODO(gavinp): Re-enable this once we are fragment clean.
+TEST_F(PrerenderTest, DISABLED_FragmentsDifferTest) {
GURL fragment_url("http://www.google.com/#test");
GURL other_fragment_url("http://www.google.com/#other_test");
@@ -639,8 +779,12 @@ TEST_F(PrerenderTest, DISABLED_FragmentMatchesFragmentTest) {
FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(fragment_url));
EXPECT_TRUE(prerender_contents->prerendering_has_started());
+
+ DummyPrerenderContents* null = NULL;
+ ASSERT_EQ(null, prerender_manager()->FindEntry(other_fragment_url));
+
ASSERT_EQ(prerender_contents,
- prerender_manager()->GetEntry(other_fragment_url));
+ prerender_manager()->FindAndUseEntry(fragment_url));
}
// Make sure that clearing works as expected.
@@ -700,7 +844,7 @@ TEST_F(PrerenderTest, LinkManagerCancel) {
EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
}
@@ -728,10 +872,10 @@ TEST_F(PrerenderTest, LinkManagerCancelThenAbandon) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
}
-// TODO(gavinp): Update this test after abandon has an effect on Prerenders,
+// TODO(gavinp): Re-enabmed this test after abandon has an effect on Prerenders,
// like shortening the timeouts.
TEST_F(PrerenderTest, LinkManagerAbandon) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
@@ -750,7 +894,7 @@ TEST_F(PrerenderTest, LinkManagerAbandon) {
last_prerender_id());
EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
}
@@ -771,7 +915,7 @@ TEST_F(PrerenderTest, LinkManagerCancelTwice) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
prerender_link_manager()->OnCancelPrerender(kDefaultChildId,
last_prerender_id());
}
@@ -806,7 +950,7 @@ TEST_F(PrerenderTest, LinkManagerAddTwiceCancelTwice) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
}
TEST_F(PrerenderTest, LinkManagerAddTwiceCancelTwiceThenAbandonTwice) {
@@ -849,7 +993,7 @@ TEST_F(PrerenderTest, LinkManagerAddTwiceCancelTwiceThenAbandonTwice) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
}
// TODO(gavinp): Update this test after abandon has an effect on Prerenders,
@@ -883,7 +1027,7 @@ TEST_F(PrerenderTest, LinkManagerAddTwiceAbandonTwice) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled());
- ASSERT_EQ(prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url));
}
// TODO(gavinp): After abandon shortens the expire time on a Prerender,
@@ -902,17 +1046,17 @@ TEST_F(PrerenderTest, LinkManagerExpireThenCancel) {
EXPECT_TRUE(prerender_contents->prerendering_has_started());
EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled());
ASSERT_EQ(prerender_contents, prerender_manager()->FindEntry(url));
- prerender_manager()->AdvanceTime(prerender_manager()->GetMaxAge() +
- base::TimeDelta::FromSeconds(1));
+ prerender_manager()->AdvanceTimeTicks(prerender_manager()->GetMaxAge() +
+ base::TimeDelta::FromSeconds(1));
EXPECT_FALSE(IsEmptyPrerenderLinkManager());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
prerender_link_manager()->OnCancelPrerender(kDefaultChildId,
last_prerender_id());
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
}
TEST_F(PrerenderTest, LinkManagerExpireThenAddAgain) {
@@ -924,17 +1068,19 @@ TEST_F(PrerenderTest, LinkManagerExpireThenAddAgain) {
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(first_prerender_contents->prerendering_has_started());
EXPECT_FALSE(first_prerender_contents->prerendering_has_been_cancelled());
- ASSERT_EQ(first_prerender_contents, prerender_manager()->FindEntry(url));
- prerender_manager()->AdvanceTime(prerender_manager()->GetMaxAge() +
- base::TimeDelta::FromSeconds(1));
+ ASSERT_EQ(first_prerender_contents,
+ prerender_manager()->FindAndUseEntry(url));
+ prerender_manager()->AdvanceTimeTicks(prerender_manager()->GetMaxAge() +
+ base::TimeDelta::FromSeconds(1));
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
DummyPrerenderContents* second_prerender_contents =
prerender_manager()->CreateNextPrerenderContents(
url, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(second_prerender_contents->prerendering_has_started());
- ASSERT_EQ(second_prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(second_prerender_contents,
+ prerender_manager()->FindAndUseEntry(url));
// The PrerenderLinkManager is not empty since we never removed the first
// prerender.
EXPECT_FALSE(IsEmptyPrerenderLinkManager());
@@ -955,13 +1101,14 @@ TEST_F(PrerenderTest, LinkManagerCancelThenAddAgain) {
EXPECT_TRUE(IsEmptyPrerenderLinkManager());
EXPECT_TRUE(first_prerender_contents->prerendering_has_been_cancelled());
DummyPrerenderContents* null = NULL;
- ASSERT_EQ(null, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(null, prerender_manager()->FindEntry(url));
DummyPrerenderContents* second_prerender_contents =
prerender_manager()->CreateNextPrerenderContents(
url, FINAL_STATUS_USED);
EXPECT_TRUE(AddSimplePrerender(url));
EXPECT_TRUE(second_prerender_contents->prerendering_has_started());
- ASSERT_EQ(second_prerender_contents, prerender_manager()->GetEntry(url));
+ ASSERT_EQ(second_prerender_contents,
+ prerender_manager()->FindAndUseEntry(url));
EXPECT_FALSE(IsEmptyPrerenderLinkManager());
}
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
index 5661042..51282ac 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -1122,18 +1122,13 @@ void OmniboxEditModel::DoPrerender(const AutocompleteMatch& match) {
TabContents* tab = controller_->GetTabContents();
if (!tab)
return;
- prerender::PrerenderManager* prerender_manager =
- prerender::PrerenderManagerFactory::GetForProfile(tab->profile());
- if (!prerender_manager)
- return;
-
- content::RenderViewHost* current_host =
- tab->web_contents()->GetRenderViewHost();
gfx::Rect container_bounds;
tab->web_contents()->GetView()->GetContainerBounds(&container_bounds);
- prerender_manager->AddPrerenderFromOmnibox(
- match.destination_url, current_host->GetSessionStorageNamespace(),
- container_bounds.size());
+ AutocompleteActionPredictorFactory::GetForProfile(profile_)->
+ StartPrerendering(match.destination_url,
+ tab->web_contents()->GetRenderViewHost()->
+ GetSessionStorageNamespace(),
+ container_bounds.size());
}
void OmniboxEditModel::DoPreconnect(const AutocompleteMatch& match) {