| Index: chrome/browser/net/predictor.cc
|
| ===================================================================
|
| --- chrome/browser/net/predictor.cc (revision 89645)
|
| +++ chrome/browser/net/predictor.cc (working copy)
|
| @@ -12,9 +12,15 @@
|
| #include "base/compiler_specific.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/stringprintf.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| #include "base/time.h"
|
| #include "base/values.h"
|
| #include "chrome/browser/net/preconnect.h"
|
| +#include "chrome/browser/prefs/pref_service.h"
|
| +#include "chrome/browser/prefs/scoped_user_pref_update.h"
|
| +#include "chrome/browser/prefs/session_startup_pref.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/common/pref_names.h"
|
| #include "content/browser/browser_thread.h"
|
| #include "net/base/address_list.h"
|
| #include "net/base/completion_callback.h"
|
| @@ -28,6 +34,8 @@
|
|
|
| namespace chrome_browser_net {
|
|
|
| +static void DnsPrefetchMotivatedList(const UrlList& urls,
|
| + UrlInfo::ResolutionMotivation motivation);
|
| // static
|
| const double Predictor::kPreconnectWorthyExpectedValue = 0.8;
|
| // static
|
| @@ -54,6 +62,10 @@
|
| // static
|
| const size_t Predictor::kUrlsTrimmedPerIncrement = 5u;
|
|
|
| +// A version number for prefs that are saved. This should be incremented when
|
| +// we change the format so that we discard old data.
|
| +static const int kPredictorStartupFormatVersion = 1;
|
| +
|
| class Predictor::LookupRequest {
|
| public:
|
| LookupRequest(Predictor* predictor,
|
| @@ -102,8 +114,12 @@
|
| Predictor::Predictor(net::HostResolver* host_resolver,
|
| TimeDelta max_dns_queue_delay,
|
| size_t max_concurrent,
|
| - bool preconnect_enabled)
|
| - : peak_pending_lookups_(0),
|
| + bool preconnect_enabled,
|
| + bool predictor_enabled)
|
| + : predictor_enabled_(true),
|
| + off_the_record_windows_count_(0),
|
| + on_the_record_(true),
|
| + peak_pending_lookups_(0),
|
| shutdown_(false),
|
| max_concurrent_dns_lookups_(max_concurrent),
|
| max_dns_queue_delay_(max_dns_queue_delay),
|
| @@ -112,6 +128,7 @@
|
| consecutive_omnibox_preconnect_count_(0),
|
| next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings),
|
| ALLOW_THIS_IN_INITIALIZER_LIST(trim_task_factory_(this)) {
|
| + initial_observer_.reset(new InitialObserver());
|
| }
|
|
|
| Predictor::~Predictor() {
|
| @@ -151,6 +168,8 @@
|
| void Predictor::LearnFromNavigation(const GURL& referring_url,
|
| const GURL& target_url) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (!predictor_enabled_)
|
| + return;
|
| DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
|
| DCHECK_NE(referring_url, GURL::EmptyGURL());
|
| DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url));
|
| @@ -169,6 +188,11 @@
|
| };
|
|
|
| void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + if (!predictor_enabled_)
|
| + return;
|
| + if (!url.is_valid() || !url.has_host())
|
| + return;
|
| std::string host = url.HostNoBrackets();
|
| bool is_new_host_request = (host != last_omnibox_host_);
|
| last_omnibox_host_ = host;
|
| @@ -237,6 +261,10 @@
|
| }
|
|
|
| void Predictor::PreconnectUrlAndSubresources(const GURL& url) {
|
| + if (!predictor_enabled_)
|
| + return;
|
| + if (!url.is_valid() || !url.has_host())
|
| + return;
|
| if (preconnect_enabled()) {
|
| std::string host = url.HostNoBrackets();
|
| UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED);
|
| @@ -248,6 +276,8 @@
|
| }
|
|
|
| void Predictor::PredictFrameSubresources(const GURL& url) {
|
| + if (!predictor_enabled_)
|
| + return;
|
| DCHECK_EQ(url.GetWithEmptyPath(), url);
|
| // Add one pass through the message loop to allow current navigation to
|
| // proceed.
|
| @@ -423,6 +453,11 @@
|
|
|
| void Predictor::GetHtmlInfo(std::string* output) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (initial_observer_.get())
|
| + initial_observer_->GetFirstResolutionsHtml(output);
|
| + // Show list of subresource predictions and stats.
|
| + GetHtmlReferrerLists(output);
|
| +
|
| // Local lists for calling UrlInfo
|
| UrlInfo::UrlInfoTable name_not_found;
|
| UrlInfo::UrlInfoTable name_preresolved;
|
| @@ -703,8 +738,212 @@
|
| PostIncrementalTrimTask();
|
| }
|
|
|
| +void Predictor::DiscardInitialNavigationHistory() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (initial_observer_.get())
|
| + initial_observer_->DiscardInitialNavigationHistory();
|
| +}
|
| +
|
| +void Predictor::FinalizeInitialization(
|
| + const UrlList& startup_urls,
|
| + ListValue* referral_list) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + // Prefetch these hostnames on startup.
|
| + DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED);
|
| + DeserializeReferrersThenDelete(referral_list);
|
| +}
|
| +
|
| //------------------------------------------------------------------------------
|
| +// This section intermingles prefetch results with actual browser HTTP
|
| +// network activity. It supports calculating of the benefit of a prefetch, as
|
| +// well as recording what prefetched hostname resolutions might be potentially
|
| +// helpful during the next chrome-startup.
|
| +//------------------------------------------------------------------------------
|
|
|
| +void Predictor::LearnAboutInitialNavigation(const GURL& url) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (!predictor_enabled_ || NULL == initial_observer_.get() )
|
| + return;
|
| + initial_observer_->Append(url, this);
|
| +}
|
| +
|
| +// This API is only used in the browser process.
|
| +// It is called from an IPC message originating in the renderer. It currently
|
| +// includes both Page-Scan, and Link-Hover prefetching.
|
| +// TODO(jar): Separate out link-hover prefetching, and page-scan results.
|
| +void Predictor::DnsPrefetchList(const NameList& hostnames) {
|
| + // TODO(jar): Push GURL transport further back into renderer, but this will
|
| + // require a Webkit change in the observer :-/.
|
| + UrlList urls;
|
| + for (NameList::const_iterator it = hostnames.begin();
|
| + it < hostnames.end();
|
| + ++it) {
|
| + urls.push_back(GURL("http://" + *it + ":80"));
|
| + }
|
| +
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED);
|
| +}
|
| +
|
| +void Predictor::DnsPrefetchMotivatedList(
|
| + const UrlList& urls,
|
| + UrlInfo::ResolutionMotivation motivation) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
|
| + BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (!predictor_enabled_)
|
| + return;
|
| +
|
| + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
| + ResolveList(urls, motivation);
|
| + } else {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + NewRunnableMethod(this, &Predictor::ResolveList, urls, motivation));
|
| + }
|
| +}
|
| +
|
| +//------------------------------------------------------------------------------
|
| +// Functions to handle saving of hostnames from one session to the next, to
|
| +// expedite startup times.
|
| +
|
| +static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
|
| + ListValue* startup_list,
|
| + ListValue* referral_list,
|
| + base::WaitableEvent* completion,
|
| + Profile* profile) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + if (NULL == profile->GetPredictor()) {
|
| + completion->Signal();
|
| + return;
|
| + }
|
| + profile->GetPredictor()->SaveDnsPrefetchStateForNextStartupAndTrim(
|
| + startup_list, referral_list, completion);
|
| +}
|
| +
|
| +void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim(
|
| + ListValue* startup_list,
|
| + ListValue* referral_list,
|
| + base::WaitableEvent* completion) {
|
| + if (initial_observer_.get())
|
| + initial_observer_->GetInitialDnsResolutionList(startup_list);
|
| +
|
| + // Do at least one trim at shutdown, in case the user wasn't running long
|
| + // enough to do any regular trimming of referrers.
|
| + TrimReferrersNow();
|
| + SerializeReferrers(referral_list);
|
| +
|
| + completion->Signal();
|
| +}
|
| +
|
| +void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs,
|
| + Profile* profile) {
|
| + if (!predictor_enabled_)
|
| + return;
|
| +
|
| + base::WaitableEvent completion(true, false);
|
| +
|
| + ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList);
|
| + ListPrefUpdate update_referral_list(prefs,
|
| + prefs::kDnsPrefetchingHostReferralList);
|
| + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
| + SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
|
| + update_startup_list.Get(),
|
| + update_referral_list.Get(),
|
| + &completion,
|
| + profile);
|
| + } else {
|
| + bool posted = BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + NewRunnableFunction(
|
| + SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread,
|
| + update_startup_list.Get(),
|
| + update_referral_list.Get(),
|
| + &completion,
|
| + profile));
|
| +
|
| + // TODO(jar): Synchronous waiting for the IO thread is a potential source
|
| + // to deadlocks and should be investigated. See http://crbug.com/78451.
|
| + DCHECK(posted);
|
| + if (posted)
|
| + completion.Wait();
|
| + }
|
| +}
|
| +
|
| +UrlList Predictor::GetPredictedUrlListAtStartup(
|
| + PrefService* user_prefs,
|
| + PrefService* local_state) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + UrlList urls;
|
| + // Recall list of URLs we learned about during last session.
|
| + // This may catch secondary hostnames, pulled in by the homepages. It will
|
| + // also catch more of the "primary" home pages, since that was (presumably)
|
| + // rendered first (and will be rendered first this time too).
|
| + const ListValue* startup_list =
|
| + user_prefs->GetList(prefs::kDnsPrefetchingStartupList);
|
| +
|
| + if (startup_list) {
|
| + ListValue::const_iterator it = startup_list->begin();
|
| + int format_version = -1;
|
| + if (it != startup_list->end() &&
|
| + (*it)->GetAsInteger(&format_version) &&
|
| + format_version == kPredictorStartupFormatVersion) {
|
| + ++it;
|
| + for (; it != startup_list->end(); ++it) {
|
| + std::string url_spec;
|
| + if (!(*it)->GetAsString(&url_spec)) {
|
| + LOG(DFATAL);
|
| + break; // Format incompatibility.
|
| + }
|
| + GURL url(url_spec);
|
| + if (!url.has_host() || !url.has_scheme()) {
|
| + LOG(DFATAL);
|
| + break; // Format incompatibility.
|
| + }
|
| +
|
| + urls.push_back(url);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Prepare for any static home page(s) the user has in prefs. The user may
|
| + // have a LOT of tab's specified, so we may as well try to warm them all.
|
| + SessionStartupPref tab_start_pref =
|
| + SessionStartupPref::GetStartupPref(user_prefs);
|
| + if (SessionStartupPref::URLS == tab_start_pref.type) {
|
| + for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
|
| + GURL gurl = tab_start_pref.urls[i];
|
| + if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty())
|
| + continue;
|
| + if (gurl.SchemeIs("http") || gurl.SchemeIs("https"))
|
| + urls.push_back(gurl.GetWithEmptyPath());
|
| + }
|
| + }
|
| +
|
| + if (urls.empty())
|
| + urls.push_back(GURL("http://www.google.com:80"));
|
| +
|
| + return urls;
|
| +}
|
| +
|
| +bool Predictor::HandleIncognitoBrowserClosed() {
|
| + DCHECK_LT(0, off_the_record_windows_count_);
|
| + if (0 >= off_the_record_windows_count_) // Defensive coding.
|
| + return false;
|
| + if (--off_the_record_windows_count_)
|
| + return false; // Still some windows are incognito.
|
| + return true;
|
| +}
|
| +
|
| +void Predictor::HandleIncognitoBrowserOpened() {
|
| + off_the_record_windows_count_++;
|
| +}
|
| +
|
| +//------------------------------------------------------------------------------
|
| +
|
| Predictor::HostNameQueue::HostNameQueue() {
|
| }
|
|
|
| @@ -744,7 +983,59 @@
|
| delete referral_list;
|
| }
|
|
|
| +//------------------------------------------------------------------------------
|
| +// Member definitions for InitialObserver class.
|
|
|
| +void Predictor::InitialObserver::Append(const GURL& url,
|
| + Predictor* predictor) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + // TODO(rlp): Do we really need the predictor check here?
|
| + if (!predictor->on_the_record() || NULL == predictor)
|
| + return;
|
| + if (kStartupResolutionCount <= first_navigations_.size())
|
| + return;
|
| +
|
| + DCHECK(url.SchemeIs("http") || url.SchemeIs("https"));
|
| + DCHECK_EQ(url, Predictor::CanonicalizeUrl(url));
|
| + if (first_navigations_.find(url) == first_navigations_.end())
|
| + first_navigations_[url] = base::TimeTicks::Now();
|
| +}
|
| +
|
| +void Predictor::InitialObserver::GetInitialDnsResolutionList(
|
| + ListValue* startup_list) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + DCHECK(startup_list);
|
| + startup_list->Clear();
|
| + DCHECK_EQ(0u, startup_list->GetSize());
|
| + startup_list->Append(new FundamentalValue(kPredictorStartupFormatVersion));
|
| + for (FirstNavigations::iterator it = first_navigations_.begin();
|
| + it != first_navigations_.end();
|
| + ++it) {
|
| + DCHECK(it->first == Predictor::CanonicalizeUrl(it->first));
|
| + startup_list->Append(new StringValue(it->first.spec()));
|
| + }
|
| +}
|
| +
|
| +void Predictor::InitialObserver::GetFirstResolutionsHtml(
|
| + std::string* output) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + UrlInfo::UrlInfoTable resolution_list;
|
| + {
|
| + for (FirstNavigations::iterator it(first_navigations_.begin());
|
| + it != first_navigations_.end();
|
| + it++) {
|
| + UrlInfo info;
|
| + info.SetUrl(it->first);
|
| + info.set_time(it->second);
|
| + resolution_list.push_back(info);
|
| + }
|
| + }
|
| + UrlInfo::GetHtmlTable(resolution_list,
|
| + "Future startups will prefetch DNS records for ", false, output);
|
| +}
|
| +
|
| //------------------------------------------------------------------------------
|
| // Helper functions
|
| //------------------------------------------------------------------------------
|
|
|