diff options
author | gavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-14 15:11:02 +0000 |
---|---|---|
committer | gavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-14 15:11:02 +0000 |
commit | 5463c700e47a3beac464d95b251335bd90145a4d (patch) | |
tree | 31278200af813860369b5210e7d798edc850e6ad /chrome/browser | |
parent | e88ff89bb0979a4d3321f9affef7d88f93fcc231 (diff) | |
download | chromium_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.cc | 19 | ||||
-rw-r--r-- | chrome/browser/predictors/autocomplete_action_predictor.h | 24 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_browsertest.cc | 140 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.cc | 135 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.h | 73 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_final_status.h | 4 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_handle.cc | 71 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_handle.h | 79 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_link_manager.cc | 68 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_link_manager.h | 14 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 578 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.h | 212 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_unittest.cc | 365 | ||||
-rw-r--r-- | chrome/browser/ui/omnibox/omnibox_edit_model.cc | 15 |
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) { |