diff options
6 files changed, 109 insertions, 22 deletions
diff --git a/chrome/browser/prefetch/prefetch_field_trial.cc b/chrome/browser/prefetch/prefetch_field_trial.cc index d88e0c7..0db983b 100644 --- a/chrome/browser/prefetch/prefetch_field_trial.cc +++ b/chrome/browser/prefetch/prefetch_field_trial.cc @@ -8,6 +8,7 @@ #include "base/metrics/field_trial.h" #include "base/strings/string_util.h" +#include "chrome/browser/prerender/prerender_field_trial.h" namespace prefetch { @@ -15,6 +16,10 @@ bool IsPrefetchFieldTrialEnabled() { std::string experiment = base::FieldTrialList::FindFullName("Prefetch"); if (StartsWithASCII(experiment, "ExperimentYes", false)) return true; + // If this client needs to prefetch for the Prerender Local Predictor, + // enable prefetching. + if (prerender::IsLocalPredictorPrerenderPrefetchEnabled()) + return true; return false; } diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc index 759b269..9a750be2c 100644 --- a/chrome/browser/prerender/prerender_field_trial.cc +++ b/chrome/browser/prerender/prerender_field_trial.cc @@ -44,6 +44,7 @@ const char kLocalPredictorUnencryptedSyncOnlyKeyName[] = const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist"; const char kPrerenderLaunchKeyName[] = "PrerenderLaunch"; const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl"; +const char kPrerenderPrefetchKeyName[] = "PrerenderPrefetch"; const char kPrerenderQueryPrerenderServiceKeyName[] = "PrerenderQueryPrerenderService"; const char kPrerenderQueryPrerenderServiceCurrentURLKeyName[] = @@ -57,6 +58,7 @@ const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds"; const char kPrerenderPriorityHalfLifeTimeKeyName[] = "PrerenderPriorityHalfLifeTimeSeconds"; const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders"; +const char kMaxLaunchPrerenderKeyName[] = "MaxLaunchPrerenders"; const char kSkipFragment[] = "SkipFragment"; const char kSkipHTTPS[] = "SkipHTTPS"; const char kSkipWhitelist[] = "SkipWhitelist"; @@ -328,7 +330,14 @@ bool IsLocalPredictorPrerenderLaunchEnabled() { } bool IsLocalPredictorPrerenderAlwaysControlEnabled() { - return GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) == + // If we prefetch rather than prerender, we automatically also prerender + // as a control group only. + return (GetLocalPredictorSpecValue(kPrerenderAlwaysControlKeyName) == + kEnabledGroup) || IsLocalPredictorPrerenderPrefetchEnabled(); +} + +bool IsLocalPredictorPrerenderPrefetchEnabled() { + return GetLocalPredictorSpecValue(kPrerenderPrefetchKeyName) == kEnabledGroup; } @@ -396,6 +405,14 @@ int GetLocalPredictorMaxConcurrentPrerenders() { return std::min(std::max(num_prerenders, 1), 10); } +int GetLocalPredictorMaxLaunchPrerenders() { + int num_prerenders; + StringToInt(GetLocalPredictorSpecValue(kMaxLaunchPrerenderKeyName), + &num_prerenders); + // Sanity check: Ensure the number of prerenders is between 1 and 10. + return std::min(std::max(num_prerenders, 1), 10); +} + bool SkipLocalPredictorFragment() { return GetLocalPredictorSpecValue(kSkipFragment) == kEnabledGroup; } diff --git a/chrome/browser/prerender/prerender_field_trial.h b/chrome/browser/prerender/prerender_field_trial.h index 038f734..ef7c6e3 100644 --- a/chrome/browser/prerender/prerender_field_trial.h +++ b/chrome/browser/prerender/prerender_field_trial.h @@ -48,6 +48,9 @@ bool IsLocalPredictorPrerenderLaunchEnabled(); // is irrelevant. bool IsLocalPredictorPrerenderAlwaysControlEnabled(); +// Returns true if the local predictor should prefetch rather than prerender. +bool IsLocalPredictorPrerenderPrefetchEnabled(); + // Returns true if we should query the prerender service for the profile // provided. bool ShouldQueryPrerenderService(Profile* profile); @@ -79,6 +82,10 @@ int GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds(); // may maintain. int GetLocalPredictorMaxConcurrentPrerenders(); +// Returns the maximum number of concurrent prerenders the local predictor +// may launch concurrently. +int GetLocalPredictorMaxLaunchPrerenders(); + // The following functions return whether certain LocalPredictor checks should // be skipped, as indicated by the name. bool SkipLocalPredictorFragment(); diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc index cc3b1f5..d3f7578 100644 --- a/chrome/browser/prerender/prerender_local_predictor.cc +++ b/chrome/browser/prerender/prerender_local_predictor.cc @@ -32,9 +32,12 @@ #include "chrome/browser/safe_browsing/database_manager.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" +#include "chrome/common/prefetch_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/page_transition_types.h" #include "crypto/secure_hash.h" @@ -50,6 +53,7 @@ using base::ListValue; using base::Value; using content::BrowserThread; using content::PageTransition; +using content::RenderFrameHost; using content::SessionStorageNamespace; using content::WebContents; using history::URLID; @@ -64,6 +68,8 @@ namespace { static const size_t kURLHashSize = 5; static const int kNumPrerenderCandidates = 5; +static const int kInvalidProcessId = -1; +static const int kInvalidFrameId = -1; } // namespace @@ -90,9 +96,16 @@ struct PrerenderLocalPredictor::CandidatePrerenderInfo { LocalPredictorURLInfo source_url_; vector<LocalPredictorURLInfo> candidate_urls_; scoped_refptr<SessionStorageNamespace> session_storage_namespace_; + // Render Process ID and Route ID of the page causing the prerender to be + // issued. Needed so that we can cause its renderer to issue prefetches within + // its context. + int render_process_id_; + int render_frame_id_; scoped_ptr<gfx::Size> size_; base::Time start_time_; // used for various time measurements - explicit CandidatePrerenderInfo(URLID source_id) { + explicit CandidatePrerenderInfo(URLID source_id) + : render_process_id_(kInvalidProcessId), + render_frame_id_(kInvalidFrameId) { source_url_.id = source_id; } void MaybeAddCandidateURLFromLocalData(URLID id, double priority) { @@ -598,6 +611,9 @@ void PrerenderLocalPredictor::OnLookupURL( info->session_storage_namespace_ = source_web_contents->GetController().GetDefaultSessionStorageNamespace(); + RenderFrameHost* rfh = source_web_contents->GetMainFrame(); + info->render_process_id_ = rfh->GetProcess()->GetID(); + info->render_frame_id_ = rfh->GetRoutingID(); gfx::Rect container_bounds = source_web_contents->GetContainerBounds(); info->size_.reset(new gfx::Size(container_bounds.size())); @@ -1053,10 +1069,25 @@ bool PrerenderLocalPredictor::DoesPrerenderMatchPLTRecord( } PrerenderLocalPredictor::PrerenderProperties* -PrerenderLocalPredictor::GetIssuedPrerenderSlotForPriority(double priority) { +PrerenderLocalPredictor::GetIssuedPrerenderSlotForPriority(const GURL& url, + double priority) { int num_prerenders = GetLocalPredictorMaxConcurrentPrerenders(); while (static_cast<int>(issued_prerenders_.size()) < num_prerenders) issued_prerenders_.push_back(new PrerenderProperties()); + // First, check if we already have a prerender for the same URL issued. + // If yes, we don't want to prerender this URL again, so we return NULL + // (on matching slot found). + for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { + PrerenderProperties* p = issued_prerenders_[i]; + DCHECK(p != NULL); + if (p->prerender_handle && p->prerender_handle->IsPrerendering() && + p->prerender_handle->Matches(url, NULL)) { + return NULL; + } + } + // Otherwise, let's see if there are any empty slots. If yes, return the first + // one we find. Otherwise, if the lowest priority prerender has a lower + // priority than the page we want to prerender, use its slot. PrerenderProperties* lowest_priority_prerender = NULL; for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) { PrerenderProperties* p = issued_prerenders_[i]; @@ -1091,8 +1122,10 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( g_browser_process->safe_browsing_service()->database_manager(); #endif PrerenderProperties* prerender_properties = NULL; - + int num_issued = 0; for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { + if (num_issued > GetLocalPredictorMaxLaunchPrerenders()) + return; RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL); url_info.reset(new LocalPredictorURLInfo(info->candidate_urls_[i])); if (url_info->local_history_based) { @@ -1126,7 +1159,7 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( continue; } prerender_properties = - GetIssuedPrerenderSlotForPriority(url_info->priority); + GetIssuedPrerenderSlotForPriority(url_info->url, url_info->priority); if (!prerender_properties) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW); url_info.reset(NULL); @@ -1148,7 +1181,9 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( // For root pages, we assume that they are reasonably safe, and we // will just prerender them without any additional checks. RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ROOT_PAGE); - break; + IssuePrerender(info.get(), url_info.get(), prerender_properties); + num_issued++; + continue; } if (IsLogOutURL(url_info->url)) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_LOGOUT_URL); @@ -1166,40 +1201,48 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( // If a page is on the side-effect free whitelist, we will just prerender // it without any additional checks. RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST); - break; + IssuePrerender(info.get(), url_info.get(), prerender_properties); + num_issued++; + continue; } #endif if (!SkipLocalPredictorServiceWhitelist() && url_info->service_whitelist && url_info->service_whitelist_lookup_ok) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST); - break; + IssuePrerender(info.get(), url_info.get(), prerender_properties); + num_issued++; + continue; } if (!SkipLocalPredictorLoggedIn() && !url_info->logged_in && url_info->logged_in_lookup_ok) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN); - break; + IssuePrerender(info.get(), url_info.get(), prerender_properties); + num_issued++; + continue; } if (!SkipLocalPredictorDefaultNoPrerender()) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_NOT_PRERENDERING); url_info.reset(NULL); } else { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING); + IssuePrerender(info.get(), url_info.get(), prerender_properties); + num_issued++; + continue; } } - if (!url_info.get()) - return; - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER); - DCHECK(prerender_properties != NULL); - if (IsLocalPredictorPrerenderLaunchEnabled()) { - IssuePrerender(info.Pass(), url_info.Pass(), prerender_properties); - } } void PrerenderLocalPredictor::IssuePrerender( - scoped_ptr<CandidatePrerenderInfo> info, - scoped_ptr<LocalPredictorURLInfo> url_info, + CandidatePrerenderInfo* info, + LocalPredictorURLInfo* url_info, PrerenderProperties* prerender_properties) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER); + DCHECK(prerender_properties != NULL); + DCHECK(info != NULL); + DCHECK(url_info != NULL); + if (!IsLocalPredictorPrerenderLaunchEnabled()) + return; URLID url_id = url_info->id; const GURL& url = url_info->url; double priority = url_info->priority; @@ -1243,6 +1286,16 @@ void PrerenderLocalPredictor::IssuePrerender( new_prerender_handle->OnCancel(); RecordEvent(EVENT_ISSUE_PRERENDER_CANCELLED_OLD_PRERENDER); } + // If we are prefetching rather than prerendering, now is the time to launch + // the prefetch. + if (IsLocalPredictorPrerenderPrefetchEnabled()) { + // Obtain the render frame host that caused this prefetch. + RenderFrameHost* rfh = RenderFrameHost::FromID(info->render_process_id_, + info->render_frame_id_); + // If it is still alive, launch the prefresh. + if (rfh) + rfh->Send(new PrefetchMsg_Prefetch(rfh->GetRoutingID(), url)); + } } RecordEvent(EVENT_ADD_VISIT_PRERENDERING); diff --git a/chrome/browser/prerender/prerender_local_predictor.h b/chrome/browser/prerender/prerender_local_predictor.h index 3081d4f..64d47b6 100644 --- a/chrome/browser/prerender/prerender_local_predictor.h +++ b/chrome/browser/prerender/prerender_local_predictor.h @@ -181,13 +181,14 @@ class PrerenderLocalPredictor : public history::VisitDatabaseObserver, // Returns an element of issued_prerenders_, which should be replaced // by a new prerender of the priority indicated, or NULL, if the priority - // is too low. - PrerenderProperties* GetIssuedPrerenderSlotForPriority(double priority); + // is too low (or if the URL requested is already prerendering). + PrerenderProperties* GetIssuedPrerenderSlotForPriority(const GURL& url, + double priority); void ContinuePrerenderCheck(scoped_ptr<CandidatePrerenderInfo> info); void LogCandidateURLStats(const GURL& url) const; - void IssuePrerender(scoped_ptr<CandidatePrerenderInfo> info, - scoped_ptr<LocalPredictorURLInfo> url_info, + void IssuePrerender(CandidatePrerenderInfo* info, + LocalPredictorURLInfo* url_info, PrerenderProperties* prerender_properties); void MaybeCancelURLFetcher(net::URLFetcher* fetcher); // Returns true if the parsed response is semantically correct and could diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index 62ed58d..04f1008 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -1533,6 +1533,10 @@ bool PrerenderManager::DoesRateLimitAllowPrerender(Origin origin) const { histograms_->RecordTimeBetweenPrerenderRequests(origin, elapsed_time); if (!config_.rate_limit_enabled) return true; + // The LocalPredictor may issue multiple prerenders simultaneously (if so + // configured), so no throttling. + if (origin == ORIGIN_LOCAL_PREDICTOR) + return true; return elapsed_time >= base::TimeDelta::FromMilliseconds(kMinTimeBetweenPrerendersMs); } |