diff options
author | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-30 22:09:47 +0000 |
---|---|---|
committer | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-30 22:09:47 +0000 |
commit | af404f8a05d638a8662c10652f84a9c32564b5f9 (patch) | |
tree | 1a1b9f9c264e40f716a057f019305ce64689e7a7 | |
parent | f3d1bf08c38ed94a3c528b717212e608e1a38213 (diff) | |
download | chromium_src-af404f8a05d638a8662c10652f84a9c32564b5f9.zip chromium_src-af404f8a05d638a8662c10652f84a9c32564b5f9.tar.gz chromium_src-af404f8a05d638a8662c10652f84a9c32564b5f9.tar.bz2 |
Integrate the LocalPredictor with the Prerender Service.
R=jam@chromium.org
Review URL: https://codereview.chromium.org/23622012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220674 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/prerender/prerender_field_trial.cc | 87 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_field_trial.h | 18 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_local_predictor.cc | 431 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_local_predictor.h | 58 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 2 |
5 files changed, 534 insertions, 62 deletions
diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc index e7b91ad..d696836 100644 --- a/chrome/browser/prerender/prerender_field_trial.cc +++ b/chrome/browser/prerender/prerender_field_trial.cc @@ -15,6 +15,8 @@ #include "chrome/browser/predictors/autocomplete_action_predictor.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" @@ -37,9 +39,16 @@ const char kEnabledGroup[] = "Enabled"; const char kLocalPredictorSpecTrialName[] = "PrerenderLocalPredictorSpec"; const char kLocalPredictorKeyName[] = "LocalPredictor"; +const char kLocalPredictorUnencryptedSyncOnlyKeyName[] = + "LocalPredictorUnencryptedSyncOnly"; const char kSideEffectFreeWhitelistKeyName[] = "SideEffectFreeWhitelist"; const char kPrerenderLaunchKeyName[] = "PrerenderLaunch"; const char kPrerenderAlwaysControlKeyName[] = "PrerenderAlwaysControl"; +const char kPrerenderQueryPrerenderServiceKeyName[] = + "PrerenderQueryPrerenderService"; +const char kPrerenderServiceBehaviorIDKeyName[] = "PrerenderServiceBehaviorID"; +const char kPrerenderServiceFetchTimeoutKeyName[] = + "PrerenderServiceFetchTimeoutMs"; const char kPrerenderTTLKeyName[] = "PrerenderTTLSeconds"; const char kPrerenderPriorityHalfLifeTimeKeyName[] = "PrerenderPriorityHalfLifeTimeSeconds"; @@ -47,8 +56,16 @@ const char kMaxConcurrentPrerenderKeyName[] = "MaxConcurrentPrerenders"; const char kSkipFragment[] = "SkipFragment"; const char kSkipHTTPS[] = "SkipHTTPS"; const char kSkipWhitelist[] = "SkipWhitelist"; +const char kSkipServiceWhitelist[] = "SkipServiceWhitelist"; const char kSkipLoggedIn[] = "SkipLoggedIn"; const char kSkipDefaultNoPrerender[] = "SkipDefaultNoPrerender"; +const char kLocalPredictorServiceURLPrefixTrialName[] = + "PrerenderLocalPredictorServiceURLPrefix"; +const char kDefaultPrerenderServiceURLPrefix[] = + "https://clients4.google.com/prerenderservice/?q="; +const int kMinPrerenderServiceTimeoutMs = 1; +const int kMaxPrerenderServiceTimeoutMs = 10000; +const int kDefaultPrerenderServiceTimeoutMs = 1000; void SetupPrefetchFieldTrial() { chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); @@ -291,7 +308,16 @@ string GetLocalPredictorSpecValue(string spec_key) { return string(); } -bool IsLocalPredictorEnabled() { +bool IsUnencryptedSyncEnabled(Profile* profile) { + ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()-> + GetForProfile(profile); + return service && service->GetSessionModelAssociator() && + !service->EncryptEverythingEnabled(); +} + +// Indicates whether the Local Predictor is enabled based on field trial +// selection. +bool IsLocalPredictorEnabledBasedOnSelection() { #if defined(OS_ANDROID) || defined(OS_IOS) return false; #endif @@ -302,12 +328,27 @@ bool IsLocalPredictorEnabled() { return GetLocalPredictorSpecValue(kLocalPredictorKeyName) == kEnabledGroup; } +// Usually, we enable the Local Predictor based on field trial selection +// (see above), so we can just return that setting. +// However, via Finch, we can specify to not create a LocalPredictor if +// UnencryptedSync is not enabled. Therefore, we have to perform this additional +// check to determine whether or not we actually want to enable the +// LocalPredictor. +bool IsLocalPredictorEnabled(Profile* profile) { + if (GetLocalPredictorSpecValue(kLocalPredictorUnencryptedSyncOnlyKeyName) == + kEnabledGroup && + !IsUnencryptedSyncEnabled(profile)) { + return false; + } + return IsLocalPredictorEnabledBasedOnSelection(); +} + bool IsLoggedInPredictorEnabled() { - return IsLocalPredictorEnabled(); + return IsLocalPredictorEnabledBasedOnSelection(); } bool IsSideEffectFreeWhitelistEnabled() { - return IsLocalPredictorEnabled() && + return IsLocalPredictorEnabledBasedOnSelection() && GetLocalPredictorSpecValue(kSideEffectFreeWhitelistKeyName) != kDisabledGroup; } @@ -321,6 +362,42 @@ bool IsLocalPredictorPrerenderAlwaysControlEnabled() { kEnabledGroup; } +bool ShouldQueryPrerenderService(Profile* profile) { + return IsUnencryptedSyncEnabled(profile) && + GetLocalPredictorSpecValue(kPrerenderQueryPrerenderServiceKeyName) == + kEnabledGroup; +} + +string GetPrerenderServiceURLPrefix() { + string prefix = + FieldTrialList::FindFullName(kLocalPredictorServiceURLPrefixTrialName); + if (prefix.empty()) + prefix = kDefaultPrerenderServiceURLPrefix; + return prefix; +} + +int GetPrerenderServiceBehaviorID() { + int id; + StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceBehaviorIDKeyName), + &id); + // The behavior ID must be non-negative. + if (id < 0) + id = 0; + return id; +} + +int GetPrerenderServiceFetchTimeoutMs() { + int result; + StringToInt(GetLocalPredictorSpecValue(kPrerenderServiceFetchTimeoutKeyName), + &result); + // The behavior ID must be non-negative. + if (result < kMinPrerenderServiceTimeoutMs || + result > kMaxPrerenderServiceTimeoutMs) { + result = kDefaultPrerenderServiceTimeoutMs; + } + return result; +} + int GetLocalPredictorTTLSeconds() { int ttl; StringToInt(GetLocalPredictorSpecValue(kPrerenderTTLKeyName), &ttl); @@ -365,6 +442,10 @@ bool SkipLocalPredictorWhitelist() { return GetLocalPredictorSpecValue(kSkipWhitelist) == kEnabledGroup; } +bool SkipLocalPredictorServiceWhitelist() { + return GetLocalPredictorSpecValue(kSkipServiceWhitelist) == kEnabledGroup; +} + bool SkipLocalPredictorLoggedIn() { return GetLocalPredictorSpecValue(kSkipLoggedIn) == kEnabledGroup; } diff --git a/chrome/browser/prerender/prerender_field_trial.h b/chrome/browser/prerender/prerender_field_trial.h index 13ebaf1..931e991 100644 --- a/chrome/browser/prerender/prerender_field_trial.h +++ b/chrome/browser/prerender/prerender_field_trial.h @@ -22,7 +22,7 @@ void ConfigurePrefetchAndPrerender(const CommandLine& command_line); bool IsOmniboxEnabled(Profile* profile); // Returns true iff the Prerender Local Predictor is enabled. -bool IsLocalPredictorEnabled(); +bool IsLocalPredictorEnabled(Profile* profile); // Returns true iff the LoggedIn Predictor is enabled. bool IsLoggedInPredictorEnabled(); @@ -38,6 +38,21 @@ bool IsLocalPredictorPrerenderLaunchEnabled(); // is irrelevant. bool IsLocalPredictorPrerenderAlwaysControlEnabled(); +// Returns true if we should query the prerender service for the profile +// provided. +bool ShouldQueryPrerenderService(Profile* profile); + +// Returns the URL prefix to be used for the prerender service. The only thing +// that will be appended is the urlencoded query json. +std::string GetPrerenderServiceURLPrefix(); + +// Returns the prerender service behavior ID that should be passed to the +// to the prerender service in requests. +int GetPrerenderServiceBehaviorID(); + +// Returns the fetch timeout to be used for the prerender service, in ms. +int GetPrerenderServiceFetchTimeoutMs(); + // Returns the TTL to be used for the local predictor. int GetLocalPredictorTTLSeconds(); @@ -54,6 +69,7 @@ int GetLocalPredictorMaxConcurrentPrerenders(); bool SkipLocalPredictorFragment(); bool SkipLocalPredictorHTTPS(); bool SkipLocalPredictorWhitelist(); +bool SkipLocalPredictorServiceWhitelist(); bool SkipLocalPredictorLoggedIn(); bool SkipLocalPredictorDefaultNoPrerender(); diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc index 70a9f1d..e28954b 100644 --- a/chrome/browser/prerender/prerender_local_predictor.cc +++ b/chrome/browser/prerender/prerender_local_predictor.cc @@ -12,8 +12,11 @@ #include <string> #include <utility> +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" +#include "base/stl_util.h" #include "base/timer/timer.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/history/history_database.h" @@ -36,14 +39,20 @@ #include "content/public/common/page_transition_types.h" #include "crypto/secure_hash.h" #include "grit/browser_resources.h" +#include "net/base/escape.h" +#include "net/url_request/url_fetcher.h" #include "ui/base/resource/resource_bundle.h" #include "url/url_canon.h" +using base::DictionaryValue; +using base::ListValue; +using base::Value; using content::BrowserThread; using content::PageTransition; using content::SessionStorageNamespace; using content::WebContents; using history::URLID; +using net::URLFetcher; using predictors::LoggedInPredictorTable; using std::string; using std::vector; @@ -66,23 +75,51 @@ struct PrerenderLocalPredictor::LocalPredictorURLInfo { bool url_lookup_success; bool logged_in; bool logged_in_lookup_ok; + bool local_history_based; + bool service_whitelist; + bool service_whitelist_lookup_ok; + bool service_whitelist_reported; double priority; }; // A struct consisting of everything needed for launching a potential prerender // on a navigation: The navigation URL (source) triggering potential prerenders, // and a set of candidate URLs. -struct PrerenderLocalPredictor::LocalPredictorURLLookupInfo { +struct PrerenderLocalPredictor::CandidatePrerenderInfo { LocalPredictorURLInfo source_url_; vector<LocalPredictorURLInfo> candidate_urls_; - explicit LocalPredictorURLLookupInfo(URLID source_id) { + scoped_refptr<SessionStorageNamespace> session_storage_namespace_; + scoped_ptr<gfx::Size> size_; + base::Time start_time_; // used for various time measurements + explicit CandidatePrerenderInfo(URLID source_id) { source_url_.id = source_id; } - void MaybeAddCandidateURL(URLID id, double priority) { - // TODO(tburkard): clean up this code, potentially using a list or a heap + void MaybeAddCandidateURLFromLocalData(URLID id, double priority) { LocalPredictorURLInfo info; info.id = id; + info.local_history_based = true; + info.service_whitelist = false; + info.service_whitelist_lookup_ok = false; + info.service_whitelist_reported = false; info.priority = priority; + MaybeAddCandidateURLInternal(info); + } + void MaybeAddCandidateURLFromService(GURL url, double priority, + bool whitelist, + bool whitelist_lookup_ok) { + LocalPredictorURLInfo info; + info.id = kint64max; + info.url = url; + info.url_lookup_success = true; + info.local_history_based = false; + info.service_whitelist = whitelist; + info.service_whitelist_lookup_ok = whitelist_lookup_ok; + info.service_whitelist_reported = true; + info.priority = priority; + MaybeAddCandidateURLInternal(info); + } + void MaybeAddCandidateURLInternal(const LocalPredictorURLInfo& info) { + // TODO(tburkard): clean up this code, potentially using a list or a heap int insert_pos = candidate_urls_.size(); if (insert_pos < kNumPrerenderCandidates) candidate_urls_.push_back(info); @@ -99,11 +136,17 @@ struct PrerenderLocalPredictor::LocalPredictorURLLookupInfo { namespace { +#define TIMING_HISTOGRAM(name, value) \ + UMA_HISTOGRAM_CUSTOM_TIMES(name, value, \ + base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromSeconds(10), \ + 50); + // Task to lookup the URL for a given URLID. class GetURLForURLIDTask : public history::HistoryDBTask { public: GetURLForURLIDTask( - PrerenderLocalPredictor::LocalPredictorURLLookupInfo* request, + PrerenderLocalPredictor::CandidatePrerenderInfo* request, const base::Closure& callback) : request_(request), callback_(callback), @@ -120,11 +163,8 @@ class GetURLForURLIDTask : public history::HistoryDBTask { virtual void DoneRunOnMainThread() OVERRIDE { callback_.Run(); - UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.LocalPredictorURLLookupTime", - base::Time::Now() - start_time_, - base::TimeDelta::FromMilliseconds(10), - base::TimeDelta::FromSeconds(10), - 50); + TIMING_HISTOGRAM("Prerender.LocalPredictorURLLookupTime", + base::Time::Now() - start_time_); } private: @@ -138,7 +178,7 @@ class GetURLForURLIDTask : public history::HistoryDBTask { request->url = url_row.url(); } - PrerenderLocalPredictor::LocalPredictorURLLookupInfo* request_; + PrerenderLocalPredictor::CandidatePrerenderInfo* request_; base::Closure callback_; base::Time start_time_; DISALLOW_COPY_AND_ASSIGN(GetURLForURLIDTask); @@ -199,7 +239,8 @@ bool IsIntermediateRedirect(PageTransition transition) { } bool IsFormSubmit(PageTransition transition) { - return (transition & content::PAGE_TRANSITION_FORM_SUBMIT) != 0; + return PageTransitionCoreTypeIs(transition, + content::PAGE_TRANSITION_FORM_SUBMIT); } bool ShouldExcludeTransitionForPrediction(PageTransition transition) { @@ -271,11 +312,11 @@ bool URLsIdenticalIgnoringFragments(const GURL& url1, const GURL& url2) { void LookupLoggedInStatesOnDBThread( scoped_refptr<LoggedInPredictorTable> logged_in_predictor_table, - PrerenderLocalPredictor::LocalPredictorURLLookupInfo* request_) { + PrerenderLocalPredictor::CandidatePrerenderInfo* request) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - for (int i = 0; i < static_cast<int>(request_->candidate_urls_.size()); i++) { + for (int i = 0; i < static_cast<int>(request->candidate_urls_.size()); i++) { PrerenderLocalPredictor::LocalPredictorURLInfo* info = - &request_->candidate_urls_[i]; + &request->candidate_urls_[i]; if (info->url_lookup_success) { logged_in_predictor_table->HasUserLoggedIn( info->url, &info->logged_in, &info->logged_in_lookup_ok); @@ -386,6 +427,9 @@ PrerenderLocalPredictor::~PrerenderLocalPredictor() { if (p->prerender_handle) p->prerender_handle->OnCancel(); } + STLDeleteContainerPairPointers( + outstanding_prerender_service_requests_.begin(), + outstanding_prerender_service_requests_.end()); } void PrerenderLocalPredictor::Shutdown() { @@ -432,8 +476,8 @@ void PrerenderLocalPredictor::OnAddVisit(const history::BriefVisitInfo& info) { std::map<URLID, int> next_urls_num_found; int num_occurrences_of_current_visit = 0; base::Time last_visited; - scoped_ptr<LocalPredictorURLLookupInfo> lookup_info( - new LocalPredictorURLLookupInfo(info.url_id)); + scoped_ptr<CandidatePrerenderInfo> lookup_info( + new CandidatePrerenderInfo(info.url_id)); const vector<history::BriefVisitInfo>& visits = *(visit_history_.get()); for (int i = 0; i < static_cast<int>(visits.size()); i++) { if (!ShouldExcludeTransitionForPrediction(visits[i].transition)) { @@ -474,20 +518,15 @@ void PrerenderLocalPredictor::OnAddVisit(const history::BriefVisitInfo& info) { RecordEvent(EVENT_ADD_VISIT_IDENTIFIED_PRERENDER_CANDIDATE); double priority = static_cast<double>(it->second) / static_cast<double>(num_occurrences_of_current_visit); - lookup_info->MaybeAddCandidateURL(it->first, priority); + lookup_info->MaybeAddCandidateURLFromLocalData(it->first, priority); } } - if (lookup_info->candidate_urls_.size() == 0) { - RecordEvent(EVENT_NO_PRERENDER_CANDIDATES); - return; - } - RecordEvent(EVENT_START_URL_LOOKUP); HistoryService* history = GetHistoryIfExists(); if (history) { RecordEvent(EVENT_GOT_HISTORY_ISSUING_LOOKUP); - LocalPredictorURLLookupInfo* lookup_info_ptr = lookup_info.get(); + CandidatePrerenderInfo* lookup_info_ptr = lookup_info.get(); history->ScheduleDBTask( new GetURLForURLIDTask( lookup_info_ptr, @@ -499,17 +538,20 @@ void PrerenderLocalPredictor::OnAddVisit(const history::BriefVisitInfo& info) { } void PrerenderLocalPredictor::OnLookupURL( - scoped_ptr<LocalPredictorURLLookupInfo> info) { - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT); + scoped_ptr<CandidatePrerenderInfo> info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_GE(static_cast<int>(info->candidate_urls_.size()), 1); + RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT); if (!info->source_url_.url_lookup_success) { RecordEvent(EVENT_PRERENDER_URL_LOOKUP_FAILED); return; } - LogCandidateURLStats(info->candidate_urls_[0].url); + if (info->candidate_urls_.size() > 0 && + info->candidate_urls_[0].url_lookup_success) { + LogCandidateURLStats(info->candidate_urls_[0].url); + } WebContents* source_web_contents = NULL; bool multiple_source_web_contents_candidates = false; @@ -540,14 +582,302 @@ void PrerenderLocalPredictor::OnLookupURL( if (multiple_source_web_contents_candidates) RecordEvent(EVENT_PRERENDER_URL_LOOKUP_MULTIPLE_SOURCE_WEBCONTENTS_FOUND); - - scoped_refptr<SessionStorageNamespace> session_storage_namespace = + info->session_storage_namespace_ = source_web_contents->GetController().GetDefaultSessionStorageNamespace(); gfx::Rect container_bounds; source_web_contents->GetView()->GetContainerBounds(&container_bounds); - scoped_ptr<gfx::Size> size(new gfx::Size(container_bounds.size())); + info->size_.reset(new gfx::Size(container_bounds.size())); + + RecordEvent(EVENT_PRERENDER_URL_LOOKUP_SUCCESS); + + DoPrerenderServiceCheck(info.Pass()); +} +void PrerenderLocalPredictor::DoPrerenderServiceCheck( + scoped_ptr<CandidatePrerenderInfo> info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!ShouldQueryPrerenderService(prerender_manager_->profile())) { + RecordEvent(EVENT_PRERENDER_SERVICE_DISABLED); + DoLoggedInLookup(info.Pass()); + return; + } + /* + Create a JSON request. + Here is a sample request: + { "prerender_request": { + "version": 1, + "behavior_id": 6, + "hint_request": { + "browse_history": [ + { "url": "http://www.cnn.com/" + } + ] + }, + "candidate_check_request": { + "candidates": [ + { "url": "http://www.cnn.com/sports/" + }, + { "url": "http://www.cnn.com/politics/" + } + ] + } + } + } + */ + DictionaryValue json_data; + DictionaryValue* req = new DictionaryValue(); + req->SetInteger("version", 1); + req->SetInteger("behavior_id", GetPrerenderServiceBehaviorID()); + if (info->source_url_.url_lookup_success) { + ListValue* browse_history = new ListValue(); + DictionaryValue* browse_item = new DictionaryValue(); + browse_item->SetString("url", info->source_url_.url.spec()); + browse_history->Append(browse_item); + DictionaryValue* hint_request = new DictionaryValue(); + hint_request->Set("browse_history", browse_history); + req->Set("hint_request", hint_request); + } + int num_candidate_urls = 0; + for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { + if (info->candidate_urls_[i].url_lookup_success) + num_candidate_urls++; + } + if (num_candidate_urls > 0) { + ListValue* candidates = new ListValue(); + DictionaryValue* candidate; + for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) { + if (info->candidate_urls_[i].url_lookup_success) { + candidate = new DictionaryValue(); + candidate->SetString("url", info->candidate_urls_[i].url.spec()); + candidates->Append(candidate); + } + } + DictionaryValue* candidate_check_request = new DictionaryValue(); + candidate_check_request->Set("candidates", candidates); + req->Set("candidate_check_request", candidate_check_request); + } + json_data.Set("prerender_request", req); + string request_string; + base::JSONWriter::Write(&json_data, &request_string); + GURL fetch_url(GetPrerenderServiceURLPrefix() + + net::EscapeQueryParamValue(request_string, false)); + net::URLFetcher* fetcher = net::URLFetcher::Create( + 0, + fetch_url, + URLFetcher::GET, this); + fetcher->SetRequestContext( + prerender_manager_->profile()->GetRequestContext()); + fetcher->AddExtraRequestHeader("Pragma: no-cache"); + info->start_time_ = base::Time::Now(); + outstanding_prerender_service_requests_.insert( + std::make_pair(fetcher, info.release())); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PrerenderLocalPredictor::MaybeCancelURLFetcher, + weak_factory_.GetWeakPtr(), fetcher), + base::TimeDelta::FromMilliseconds(GetPrerenderServiceFetchTimeoutMs())); + RecordEvent(EVENT_PRERENDER_SERVICE_ISSUED_LOOKUP); + fetcher->Start(); +} + +void PrerenderLocalPredictor::MaybeCancelURLFetcher(net::URLFetcher* fetcher) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + OutstandingFetchers::iterator it = + outstanding_prerender_service_requests_.find(fetcher); + if (it == outstanding_prerender_service_requests_.end()) + return; + delete it->first; + scoped_ptr<CandidatePrerenderInfo> info(it->second); + outstanding_prerender_service_requests_.erase(it); + RecordEvent(EVENT_PRERENDER_SERVICE_LOOKUP_TIMED_OUT); + DoLoggedInLookup(info.Pass()); +} + +bool PrerenderLocalPredictor::ApplyParsedPrerenderServiceResponse( + DictionaryValue* dict, + CandidatePrerenderInfo* info, + bool* hinting_timed_out, + bool* hinting_url_lookup_timed_out, + bool* candidate_url_lookup_timed_out) { + /* + Process the response to the request. + Here is a sample response to illustrate the format. + { + "prerender_response": { + "behavior_id": 6, + "hint_response": { + "hinting_timed_out": 0, + "candidates": [ + { "url": "http://www.cnn.com/story-1", + "in_index": 1, + "likelihood": 0.60, + "in_index_timed_out": 0 + }, + { "url": "http://www.cnn.com/story-2", + "in_index": 1, + "likelihood": 0.30, + "in_index_timed_out": 0 + } + ] + }, + "candidate_check_response": { + "candidates": [ + { "url": "http://www.cnn.com/sports/", + "in_index": 1, + "in_index_timed_out": 0 + }, + { "url": "http://www.cnn.com/politics/", + "in_index": 0, + "in_index_timed_out": "1" + } + ] + } + } + } + */ + ListValue* list = NULL; + int int_value; + if (!dict->GetInteger("prerender_response.behavior_id", &int_value) || + int_value != GetPrerenderServiceBehaviorID()) { + return false; + } + if (!dict->GetList("prerender_response.candidate_check_response.candidates", + &list)) { + if (info->candidate_urls_.size() > 0) + return false; + } else { + for (size_t i = 0; i < list->GetSize(); i++) { + DictionaryValue* d; + if (!list->GetDictionary(i, &d)) + return false; + string url_string; + if (!d->GetString("url", &url_string) || !GURL(url_string).is_valid()) + return false; + GURL url(url_string); + int in_index_timed_out = 0; + int in_index = 0; + if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) || + in_index_timed_out != 1) && + !d->GetInteger("in_index", &in_index)) { + return false; + } + if (in_index < 0 || in_index > 1 || + in_index_timed_out < 0 || in_index_timed_out > 1) { + return false; + } + if (in_index_timed_out == 1) + *candidate_url_lookup_timed_out = true; + for (size_t j = 0; j < info->candidate_urls_.size(); j++) { + if (info->candidate_urls_[j].url == url) { + info->candidate_urls_[j].service_whitelist_reported = true; + info->candidate_urls_[j].service_whitelist = (in_index == 1); + info->candidate_urls_[j].service_whitelist_lookup_ok = + ((1 - in_index_timed_out) == 1); + } + } + } + for (size_t i = 0; i < info->candidate_urls_.size(); i++) { + if (!info->candidate_urls_[i].service_whitelist_reported) + return false; + } + } + + list = NULL; + if (dict->GetInteger("prerender_response.hint_response.hinting_timed_out", + &int_value) && + int_value == 1) { + *hinting_timed_out = true; + } else if (!dict->GetList("prerender_response.hint_response.candidates", + &list)) { + return false; + } else { + for (int i = 0; i < static_cast<int>(list->GetSize()); i++) { + DictionaryValue* d; + if (!list->GetDictionary(i, &d)) + return false; + string url; + double priority; + if (!d->GetString("url", &url) || !d->GetDouble("likelihood", &priority) + || !GURL(url).is_valid()) { + return false; + } + int in_index_timed_out = 0; + int in_index = 0; + if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) || + in_index_timed_out != 1) && + !d->GetInteger("in_index", &in_index)) { + return false; + } + if (priority < 0.0 || priority > 1.0 || in_index < 0 || in_index > 1 || + in_index_timed_out < 0 || in_index_timed_out > 1) { + return false; + } + if (in_index_timed_out == 1) + *hinting_url_lookup_timed_out = true; + info->MaybeAddCandidateURLFromService(GURL(url), + priority, + in_index == 1, + (1 - in_index_timed_out) == 1); + } + } + + return true; +} + +void PrerenderLocalPredictor::OnURLFetchComplete( + const net::URLFetcher* source) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + RecordEvent(EVENT_PRERENDER_SERVICE_RECEIVED_RESULT); + net::URLFetcher* fetcher = const_cast<net::URLFetcher*>(source); + OutstandingFetchers::iterator it = + outstanding_prerender_service_requests_.find(fetcher); + if (it == outstanding_prerender_service_requests_.end()) { + RecordEvent(EVENT_PRERENDER_SERVICE_NO_RECORD_FOR_RESULT); + return; + } + scoped_ptr<CandidatePrerenderInfo> info(it->second); + outstanding_prerender_service_requests_.erase(it); + TIMING_HISTOGRAM("Prerender.LocalPredictorServiceLookupTime", + base::Time::Now() - info->start_time_); + string result; + fetcher->GetResponseAsString(&result); + scoped_ptr<Value> root; + root.reset(base::JSONReader::Read(result)); + bool hinting_timed_out = false; + bool hinting_url_lookup_timed_out = false; + bool candidate_url_lookup_timed_out = false; + if (!root.get() || !root->IsType(Value::TYPE_DICTIONARY)) { + RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR_INCORRECT_JSON); + } else { + if (ApplyParsedPrerenderServiceResponse( + static_cast<DictionaryValue*>(root.get()), + info.get(), + &hinting_timed_out, + &hinting_url_lookup_timed_out, + &candidate_url_lookup_timed_out)) { + // We finished parsing the result, and found no errors. + RecordEvent(EVENT_PRERENDER_SERVICE_PARSED_CORRECTLY); + if (hinting_timed_out) + RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_TIMED_OUT); + if (hinting_url_lookup_timed_out) + RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_URL_LOOKUP_TIMED_OUT); + if (candidate_url_lookup_timed_out) + RecordEvent(EVENT_PRERENDER_SERVICE_CANDIDATE_URL_LOOKUP_TIMED_OUT); + DoLoggedInLookup(info.Pass()); + return; + } + } + + // If we did not return earlier, an error happened during parsing. + // Record this, and proceed. + RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR); + DoLoggedInLookup(info.Pass()); +} + +void PrerenderLocalPredictor:: DoLoggedInLookup( + scoped_ptr<CandidatePrerenderInfo> info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); scoped_refptr<LoggedInPredictorTable> logged_in_table = prerender_manager_->logged_in_predictor_table(); @@ -558,7 +888,9 @@ void PrerenderLocalPredictor::OnLookupURL( RecordEvent(EVENT_PRERENDER_URL_LOOKUP_ISSUING_LOGGED_IN_LOOKUP); - LocalPredictorURLLookupInfo* info_ptr = info.get(); + info->start_time_ = base::Time::Now(); + + CandidatePrerenderInfo* info_ptr = info.get(); BrowserThread::PostTaskAndReply( BrowserThread::DB, FROM_HERE, base::Bind(&LookupLoggedInStatesOnDBThread, @@ -566,8 +898,6 @@ void PrerenderLocalPredictor::OnLookupURL( info_ptr), base::Bind(&PrerenderLocalPredictor::ContinuePrerenderCheck, weak_factory_.GetWeakPtr(), - session_storage_namespace, - base::Passed(&size), base::Passed(&info))); } @@ -711,10 +1041,15 @@ PrerenderLocalPredictor::GetIssuedPrerenderSlotForPriority(double priority) { } void PrerenderLocalPredictor::ContinuePrerenderCheck( - scoped_refptr<SessionStorageNamespace> session_storage_namespace, - scoped_ptr<gfx::Size> size, - scoped_ptr<LocalPredictorURLLookupInfo> info) { + scoped_ptr<CandidatePrerenderInfo> info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + TIMING_HISTOGRAM("Prerender.LocalPredictorLoggedInLookupTime", + base::Time::Now() - info->start_time_); RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_STARTED); + if (info->candidate_urls_.size() == 0) { + RecordEvent(EVENT_NO_PRERENDER_CANDIDATES); + return; + } scoped_ptr<LocalPredictorURLInfo> url_info; scoped_refptr<SafeBrowsingDatabaseManager> sb_db_manager = g_browser_process->safe_browsing_service()->database_manager(); @@ -779,6 +1114,11 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST); break; } + if (!SkipLocalPredictorServiceWhitelist() && + url_info->service_whitelist && url_info->service_whitelist_lookup_ok) { + RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST); + break; + } if (!SkipLocalPredictorLoggedIn() && !url_info->logged_in && url_info->logged_in_lookup_ok) { RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN); @@ -796,26 +1136,25 @@ void PrerenderLocalPredictor::ContinuePrerenderCheck( RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER); DCHECK(prerender_properties != NULL); if (IsLocalPredictorPrerenderLaunchEnabled()) { - IssuePrerender(session_storage_namespace, size.Pass(), - url_info.Pass(), prerender_properties); + IssuePrerender(info.Pass(), url_info.Pass(), prerender_properties); } } void PrerenderLocalPredictor::IssuePrerender( - scoped_refptr<SessionStorageNamespace> session_storage_namespace, - scoped_ptr<gfx::Size> size, - scoped_ptr<LocalPredictorURLInfo> info, + scoped_ptr<CandidatePrerenderInfo> info, + scoped_ptr<LocalPredictorURLInfo> url_info, PrerenderProperties* prerender_properties) { - URLID url_id = info->id; - const GURL& url = info->url; - double priority = info->priority; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + URLID url_id = url_info->id; + const GURL& url = url_info->url; + double priority = url_info->priority; base::Time current_time = GetCurrentTime(); RecordEvent(EVENT_ISSUING_PRERENDER); // Issue the prerender and obtain a new handle. scoped_ptr<prerender::PrerenderHandle> new_prerender_handle( prerender_manager_->AddPrerenderFromLocalPredictor( - url, session_storage_namespace.get(), *size)); + url, info->session_storage_namespace_.get(), *(info->size_))); // Check if this is a duplicate of an existing prerender. If yes, clean up // the new handle. diff --git a/chrome/browser/prerender/prerender_local_predictor.h b/chrome/browser/prerender/prerender_local_predictor.h index db16be40..e94f866 100644 --- a/chrome/browser/prerender/prerender_local_predictor.h +++ b/chrome/browser/prerender/prerender_local_predictor.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_PRERENDER_PRERENDER_LOCAL_PREDICTOR_H_ #define CHROME_BROWSER_PRERENDER_PRERENDER_LOCAL_PREDICTOR_H_ +#include <map> #include <vector> #include "base/containers/hash_tables.h" @@ -13,10 +14,15 @@ #include "base/timer/timer.h" #include "chrome/browser/common/cancelable_request.h" #include "chrome/browser/history/visit_database.h" +#include "net/url_request/url_fetcher_delegate.h" #include "url/gurl.h" class HistoryService; +namespace base { +class DictionaryValue; +} + namespace content { class SessionStorageNamespace; class WebContents; @@ -35,10 +41,11 @@ class PrerenderManager; // predictions. // At this point, the class is not actually creating prerenders, but just // recording timing stats about the effect prerendering would have. -class PrerenderLocalPredictor : public history::VisitDatabaseObserver { +class PrerenderLocalPredictor : public history::VisitDatabaseObserver, + public net::URLFetcherDelegate { public: struct LocalPredictorURLInfo; - struct LocalPredictorURLLookupInfo; + struct CandidatePrerenderInfo; enum Event { EVENT_CONSTRUCTED = 0, EVENT_INIT_SCHEDULED = 1, @@ -96,6 +103,19 @@ class PrerenderLocalPredictor : public history::VisitDatabaseObserver { EVENT_ISSUE_PRERENDER_NEW_PRERENDER = 53, EVENT_ISSUE_PRERENDER_CANCELLED_OLD_PRERENDER = 54, EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING = 55, + EVENT_PRERENDER_URL_LOOKUP_SUCCESS = 56, + EVENT_PRERENDER_SERVICE_DISABLED = 57, + EVENT_PRERENDER_SERVICE_ISSUED_LOOKUP = 58, + EVENT_PRERENDER_SERVICE_LOOKUP_TIMED_OUT = 59, + EVENT_PRERENDER_SERVICE_RECEIVED_RESULT = 60, + EVENT_PRERENDER_SERVICE_NO_RECORD_FOR_RESULT = 61, + EVENT_PRERENDER_SERVICE_PARSED_CORRECTLY = 62, + EVENT_PRERENDER_SERVICE_PARSE_ERROR = 63, + EVENT_PRERENDER_SERVICE_PARSE_ERROR_INCORRECT_JSON = 64, + EVENT_PRERENDER_SERVICE_HINTING_TIMED_OUT = 65, + EVENT_PRERENDER_SERVICE_HINTING_URL_LOOKUP_TIMED_OUT = 66, + EVENT_PRERENDER_SERVICE_CANDIDATE_URL_LOOKUP_TIMED_OUT = 67, + EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST = 68, EVENT_MAX_VALUE }; @@ -117,6 +137,9 @@ class PrerenderLocalPredictor : public history::VisitDatabaseObserver { void OnTabHelperURLSeen(const GURL& url, content::WebContents* web_contents); + // net::URLFetcherDelegate implementation: + void virtual OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + private: struct PrerenderProperties; HistoryService* GetHistoryIfExists() const; @@ -127,23 +150,36 @@ class PrerenderLocalPredictor : public history::VisitDatabaseObserver { base::TimeDelta plt) const; void RecordEvent(Event event) const; - void OnLookupURL(scoped_ptr<LocalPredictorURLLookupInfo> info); + void OnLookupURL(scoped_ptr<CandidatePrerenderInfo> info); + + // Lookup the prerender candidate in the Prerender Service (if applicable). + void DoPrerenderServiceCheck(scoped_ptr<CandidatePrerenderInfo> info); + + // Lookup the prerender candidate in the LoggedIn Predictor. + void DoLoggedInLookup(scoped_ptr<CandidatePrerenderInfo> info); // 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); - void ContinuePrerenderCheck( - scoped_refptr<content::SessionStorageNamespace> session_storage_namespace, - scoped_ptr<gfx::Size> size, - scoped_ptr<LocalPredictorURLLookupInfo> info); + void ContinuePrerenderCheck(scoped_ptr<CandidatePrerenderInfo> info); void LogCandidateURLStats(const GURL& url) const; - void IssuePrerender(scoped_refptr<content::SessionStorageNamespace> - session_storage_namespace, - scoped_ptr<gfx::Size> size, - scoped_ptr<LocalPredictorURLInfo> info, + void IssuePrerender(scoped_ptr<CandidatePrerenderInfo> info, + scoped_ptr<LocalPredictorURLInfo> url_info, PrerenderProperties* prerender_properties); + void MaybeCancelURLFetcher(net::URLFetcher* fetcher); + // Returns true if the parsed response is semantically correct and could + // be fully applied. + bool ApplyParsedPrerenderServiceResponse( + base::DictionaryValue* dict, + CandidatePrerenderInfo* info, + bool* hinting_timed_out, + bool* hinting_url_lookup_timed_out, + bool* candidate_url_lookup_timed_out); + typedef std::map<net::URLFetcher*, CandidatePrerenderInfo*> + OutstandingFetchers; + OutstandingFetchers outstanding_prerender_service_requests_; PrerenderManager* prerender_manager_; base::OneShotTimer<PrerenderLocalPredictor> timer_; diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index f704d407..c0f8a08 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -241,7 +241,7 @@ PrerenderManager::PrerenderManager(Profile* profile, // the same thread that it was created on. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (IsLocalPredictorEnabled()) + if (IsLocalPredictorEnabled(profile)) local_predictor_.reset(new PrerenderLocalPredictor(this)); if (IsLoggedInPredictorEnabled() && !profile_->IsOffTheRecord()) { |