#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/containers/mru_cache.h"
+#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "chrome/browser/net/preconnect.h"
#include "chrome/browser/net/spdyproxy/proxy_advisor.h"
#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
DISALLOW_COPY_AND_ASSIGN(LookupRequest);
};
-// This records UMAs for preconnect usage based on navigation URLs to
-// gather precision/recall for user-event based preconnect triggers.
-// Stats are gathered via a LRU cache that remembers all preconnect within the
-// last N seconds.
-// A preconnect trigger is considered as used iff a navigation including
-// access to the preconnected host occurs within a time period specified by
-// kMaxUnusedSocketLifetimeSecondsWithoutAGet.
-class Predictor::PreconnectUsage {
- public:
- PreconnectUsage();
- ~PreconnectUsage();
-
- // Record a preconnect trigger to |url|.
- void ObservePreconnect(const GURL& url);
-
- // Record a user navigation with its redirect history, |url_chain|.
- // We are uncertain if this is actually a link navigation.
- void ObserveNavigationChain(const std::vector<GURL>& url_chain,
- bool is_subresource);
-
- // Record a user link navigation to |final_url|.
- // We are certain that this is a user-triggered link navigation.
- void ObserveLinkNavigation(const GURL& final_url);
-
- private:
- // This tracks whether a preconnect was used in some navigation or not
- class PreconnectPrecisionStat {
- public:
- PreconnectPrecisionStat()
- : timestamp_(base::TimeTicks::Now()),
- was_used_(false) {
- }
-
- const base::TimeTicks& timestamp() { return timestamp_; }
-
- void set_was_used() { was_used_ = true; }
- bool was_used() const { return was_used_; }
-
- private:
- base::TimeTicks timestamp_;
- bool was_used_;
- };
-
- typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects;
- MRUPreconnects mru_preconnects_;
-
- // The longest time an entry can persist in mru_preconnect_
- const base::TimeDelta max_duration_;
-
- std::vector<GURL> recent_navigation_chain_;
-
- DISALLOW_COPY_AND_ASSIGN(PreconnectUsage);
-};
-
-Predictor::PreconnectUsage::PreconnectUsage()
- : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT),
- max_duration_(base::TimeDelta::FromSeconds(
- Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) {
-}
-
-Predictor::PreconnectUsage::~PreconnectUsage() {}
-
-void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) {
- // Evict any overly old entries and record stats.
- base::TimeTicks now = base::TimeTicks::Now();
-
- MRUPreconnects::reverse_iterator eldest_preconnect =
- mru_preconnects_.rbegin();
- while (!mru_preconnects_.empty()) {
- DCHECK(eldest_preconnect == mru_preconnects_.rbegin());
- if (now - eldest_preconnect->second.timestamp() < max_duration_)
- break;
-
- UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed",
- eldest_preconnect->second.was_used());
- eldest_preconnect = mru_preconnects_.Erase(eldest_preconnect);
- }
-
- // Add new entry.
- GURL canonical_url(Predictor::CanonicalizeUrl(url));
- mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat());
-}
-
-void Predictor::PreconnectUsage::ObserveNavigationChain(
- const std::vector<GURL>& url_chain,
- bool is_subresource) {
- if (url_chain.empty())
- return;
-
- if (!is_subresource)
- recent_navigation_chain_ = url_chain;
-
- GURL canonical_url(Predictor::CanonicalizeUrl(url_chain.back()));
-
- MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url);
- bool was_preconnected = (itPreconnect != mru_preconnects_.end());
-
- // This is an UMA which was named incorrectly. This actually measures the
- // ratio of URLRequests which have used a preconnected session.
- UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", was_preconnected);
-}
-
-void Predictor::PreconnectUsage::ObserveLinkNavigation(const GURL& url) {
- if (recent_navigation_chain_.empty() ||
- url != recent_navigation_chain_.back()) {
- // The navigation chain is not available for this navigation.
- recent_navigation_chain_.clear();
- recent_navigation_chain_.push_back(url);
- }
-
- // See if the link navigation involved preconnected session.
- bool did_use_preconnect = false;
- for (std::vector<GURL>::const_iterator it = recent_navigation_chain_.begin();
- it != recent_navigation_chain_.end();
- ++it) {
- GURL canonical_url(Predictor::CanonicalizeUrl(*it));
-
- // Record the preconnect trigger for the url as used if exist
- MRUPreconnects::iterator itPreconnect =
- mru_preconnects_.Peek(canonical_url);
- bool was_preconnected = (itPreconnect != mru_preconnects_.end());
- if (was_preconnected) {
- itPreconnect->second.set_was_used();
- did_use_preconnect = true;
- }
- }
-
- UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedLinkNavigations", did_use_preconnect);
-}
-
-Predictor::Predictor(bool preconnect_enabled)
+Predictor::Predictor(bool preconnect_enabled, bool predictor_enabled)
: url_request_context_getter_(NULL),
- predictor_enabled_(true),
+ predictor_enabled_(predictor_enabled),
+ user_prefs_(NULL),
+ profile_io_data_(NULL),
peak_pending_lookups_(0),
shutdown_(false),
max_concurrent_dns_lookups_(g_max_parallel_resolves),
// static
Predictor* Predictor::CreatePredictor(bool preconnect_enabled,
+ bool predictor_enabled,
bool simple_shutdown) {
if (simple_shutdown)
- return new SimplePredictor(preconnect_enabled);
- return new Predictor(preconnect_enabled);
+ return new SimplePredictor(preconnect_enabled, predictor_enabled);
+ return new Predictor(preconnect_enabled, predictor_enabled);
}
void Predictor::RegisterProfilePrefs(
void Predictor::InitNetworkPredictor(PrefService* user_prefs,
PrefService* local_state,
IOThread* io_thread,
- net::URLRequestContextGetter* getter) {
+ net::URLRequestContextGetter* getter,
+ ProfileIOData* profile_io_data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- bool predictor_enabled =
- user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled);
-
+ user_prefs_ = user_prefs;
url_request_context_getter_ = getter;
// Gather the list of hostnames to prefetch on startup.
&Predictor::FinalizeInitializationOnIOThread,
base::Unretained(this),
urls, referral_list,
- io_thread, predictor_enabled));
+ io_thread, profile_io_data));
}
void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
return;
if (!url.is_valid() || !url.has_host())
return;
+ if (!CanPreresolveAndPreconnect())
+ return;
+
std::string host = url.HostNoBrackets();
bool is_new_host_request = (host != last_omnibox_host_);
last_omnibox_host_ = host;
UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED);
base::TimeTicks now = base::TimeTicks::Now();
- if (preconnect_enabled()) {
+ if (preconnect_enabled_) {
if (preconnectable && !is_new_host_request) {
++consecutive_omnibox_preconnect_count_;
// The omnibox suggests a search URL (for which we can preconnect) after
// get with a fake request (/gen_204 might be the good path on Google).
const int kMaxSearchKeepaliveSeconds(10);
if ((now - last_omnibox_preconnect_).InSeconds() <
- kMaxSearchKeepaliveSeconds)
+ kMaxSearchKeepaliveSeconds)
return; // We've done a preconnect recently.
last_omnibox_preconnect_ = now;
const int kConnectionsNeeded = 1;
- PreconnectUrl(CanonicalizeUrl(url), GURL(), motivation,
- kConnectionsNeeded);
+ PreconnectUrl(
+ CanonicalizeUrl(url), GURL(), motivation, kConnectionsNeeded);
return; // Skip pre-resolution, since we'll open a connection.
}
} else {
const GURL& first_party_for_cookies) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!predictor_enabled_ || !preconnect_enabled() ||
+ if (!predictor_enabled_ || !preconnect_enabled_ ||
!url.is_valid() || !url.has_host())
return;
+ if (!CanPreresolveAndPreconnect())
+ return;
UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED);
const int kConnectionsNeeded = 1;
void Predictor::LearnFromNavigation(const GURL& referring_url,
const GURL& target_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!predictor_enabled_)
+ if (!predictor_enabled_ || !CanPrefetchAndPrerender())
return;
DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
DCHECK_NE(referring_url, GURL::EmptyGURL());
// We'd like the following no-cache... but it doesn't work.
// "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
"</head><body>");
- if (predictor && predictor->predictor_enabled()) {
+ if (predictor && predictor->predictor_enabled() &&
+ predictor->CanPrefetchAndPrerender()) {
predictor->GetHtmlInfo(output);
} else {
output->append("DNS pre-resolution and TCP pre-connection is disabled.");
const UrlList& startup_urls,
base::ListValue* referral_list,
IOThread* io_thread,
- bool predictor_enabled) {
+ ProfileIOData* profile_io_data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- predictor_enabled_ = predictor_enabled;
+ profile_io_data_ = profile_io_data;
initial_observer_.reset(new InitialObserver());
host_resolver_ = io_thread->globals()->host_resolver.get();
- preconnect_usage_.reset(new PreconnectUsage());
net::URLRequestContext* context =
url_request_context_getter_->GetURLRequestContext();
void Predictor::LearnAboutInitialNavigation(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!predictor_enabled_ || NULL == initial_observer_.get() )
+ if (!predictor_enabled_ || NULL == initial_observer_.get() ||
+ !CanPrefetchAndPrerender()) {
return;
+ }
initial_observer_->Append(url, this);
}
BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!predictor_enabled_)
return;
+ if (!CanPrefetchAndPrerender())
+ return;
if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
ResolveList(urls, motivation);
startup_list, referral_list, completion);
}
-void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs) {
+void Predictor::SaveStateForNextStartupAndTrim() {
if (!predictor_enabled_)
return;
+ if (!CanPrefetchAndPrerender())
+ return;
base::WaitableEvent completion(true, false);
- ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList);
- ListPrefUpdate update_referral_list(prefs,
+ ListPrefUpdate update_startup_list(user_prefs_,
+ prefs::kDnsPrefetchingStartupList);
+ ListPrefUpdate update_referral_list(user_prefs_,
prefs::kDnsPrefetchingHostReferralList);
if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
completion->Signal();
}
-void Predictor::EnablePredictor(bool enable) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
- BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
- EnablePredictorOnIOThread(enable);
- } else {
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&Predictor::EnablePredictorOnIOThread,
- base::Unretained(this), enable));
- }
-}
-
-void Predictor::EnablePredictorOnIOThread(bool enable) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- predictor_enabled_ = enable;
-}
-
void Predictor::PreconnectUrl(const GURL& url,
const GURL& first_party_for_cookies,
UrlInfo::ResolutionMotivation motivation,
// Skip the HSTS redirect.
GURL url = GetHSTSRedirectOnIOThread(original_url);
- if (motivation == UrlInfo::MOUSE_OVER_MOTIVATED)
- RecordPreconnectTrigger(url);
-
AdviseProxy(url, motivation, true /* is_preconnect */);
if (observer_) {
url_request_context_getter_.get());
}
-void Predictor::RecordPreconnectTrigger(const GURL& url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (preconnect_usage_)
- preconnect_usage_->ObservePreconnect(url);
-}
-
-void Predictor::RecordPreconnectNavigationStat(
- const std::vector<GURL>& url_chain,
- bool is_subresource) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
- if (preconnect_usage_)
- preconnect_usage_->ObserveNavigationChain(url_chain, is_subresource);
-}
-
-void Predictor::RecordLinkNavigation(const GURL& url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (preconnect_usage_)
- preconnect_usage_->ObserveLinkNavigation(url);
-}
-
void Predictor::PredictFrameSubresources(const GURL& url,
const GURL& first_party_for_cookies) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!predictor_enabled_)
return;
+ if (!CanPrefetchAndPrerender())
+ return;
DCHECK_EQ(url.GetWithEmptyPath(), url);
// Add one pass through the message loop to allow current navigation to
// proceed.
}
}
+bool Predictor::CanPrefetchAndPrerender() const {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ return chrome_browser_net::CanPrefetchAndPrerenderUI(user_prefs_);
+ } else {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return chrome_browser_net::CanPrefetchAndPrerenderIO(profile_io_data_);
+ }
+}
+
+bool Predictor::CanPreresolveAndPreconnect() const {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ return chrome_browser_net::CanPreresolveAndPreconnectUI(user_prefs_);
+ } else {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return chrome_browser_net::CanPreresolveAndPreconnectIO(profile_io_data_);
+ }
+}
+
enum SubresourceValue {
PRECONNECTION,
PRERESOLUTION,
PrefService* user_prefs,
PrefService* local_state,
IOThread* io_thread,
- net::URLRequestContextGetter* getter) {
+ net::URLRequestContextGetter* getter,
+ ProfileIOData* profile_io_data) {
// Empty function for unittests.
}
SetShutdown(true);
}
+bool SimplePredictor::CanPrefetchAndPrerender() const { return true; }
+bool SimplePredictor::CanPreresolveAndPreconnect() const { return true; }
+
} // namespace chrome_browser_net