summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-30 22:09:47 +0000
committertburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-30 22:09:47 +0000
commitaf404f8a05d638a8662c10652f84a9c32564b5f9 (patch)
tree1a1b9f9c264e40f716a057f019305ce64689e7a7
parentf3d1bf08c38ed94a3c528b717212e608e1a38213 (diff)
downloadchromium_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.cc87
-rw-r--r--chrome/browser/prerender/prerender_field_trial.h18
-rw-r--r--chrome/browser/prerender/prerender_local_predictor.cc431
-rw-r--r--chrome/browser/prerender/prerender_local_predictor.h58
-rw-r--r--chrome/browser/prerender/prerender_manager.cc2
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()) {