Home | History | Annotate | Download | only in net
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/net/predictor.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 #include <set>
     10 #include <sstream>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/bind.h"
     14 #include "base/compiler_specific.h"
     15 #include "base/containers/mru_cache.h"
     16 #include "base/metrics/histogram.h"
     17 #include "base/prefs/pref_service.h"
     18 #include "base/prefs/scoped_user_pref_update.h"
     19 #include "base/stl_util.h"
     20 #include "base/strings/string_split.h"
     21 #include "base/strings/string_util.h"
     22 #include "base/strings/stringprintf.h"
     23 #include "base/synchronization/waitable_event.h"
     24 #include "base/threading/thread_restrictions.h"
     25 #include "base/time/time.h"
     26 #include "base/values.h"
     27 #include "chrome/browser/io_thread.h"
     28 #include "chrome/browser/net/preconnect.h"
     29 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h"
     30 #include "chrome/browser/net/spdyproxy/proxy_advisor.h"
     31 #include "chrome/browser/prefs/session_startup_pref.h"
     32 #include "chrome/common/chrome_switches.h"
     33 #include "chrome/common/pref_names.h"
     34 #include "components/user_prefs/pref_registry_syncable.h"
     35 #include "content/public/browser/browser_thread.h"
     36 #include "net/base/address_list.h"
     37 #include "net/base/completion_callback.h"
     38 #include "net/base/host_port_pair.h"
     39 #include "net/base/net_errors.h"
     40 #include "net/base/net_log.h"
     41 #include "net/dns/host_resolver.h"
     42 #include "net/dns/single_request_host_resolver.h"
     43 #include "net/url_request/url_request_context_getter.h"
     44 
     45 using base::TimeDelta;
     46 using content::BrowserThread;
     47 
     48 namespace chrome_browser_net {
     49 
     50 // static
     51 const int Predictor::kPredictorReferrerVersion = 2;
     52 const double Predictor::kPreconnectWorthyExpectedValue = 0.8;
     53 const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.1;
     54 const double Predictor::kDiscardableExpectedValue = 0.05;
     55 // The goal is of trimming is to to reduce the importance (number of expected
     56 // subresources needed) by a factor of 2 after about 24 hours of uptime. We will
     57 // trim roughly once-an-hour of uptime.  The ratio to use in each trim operation
     58 // is then the 24th root of 0.5.  If a user only surfs for 4 hours a day, then
     59 // after about 6 days they will have halved all their estimates of subresource
     60 // connections.  Once this falls below kDiscardableExpectedValue the referrer
     61 // will be discarded.
     62 // TODO(jar): Measure size of referrer lists in the field.  Consider an adaptive
     63 // system that uses a higher trim ratio when the list is large.
     64 // static
     65 const double Predictor::kReferrerTrimRatio = 0.97153;
     66 const int64 Predictor::kDurationBetweenTrimmingsHours = 1;
     67 const int64 Predictor::kDurationBetweenTrimmingIncrementsSeconds = 15;
     68 const size_t Predictor::kUrlsTrimmedPerIncrement = 5u;
     69 const size_t Predictor::kMaxSpeculativeParallelResolves = 3;
     70 const int Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet = 10;
     71 // To control our congestion avoidance system, which discards a queue when
     72 // resolutions are "taking too long," we need an expected resolution time.
     73 // Common average is in the range of 300-500ms.
     74 const int kExpectedResolutionTimeMs = 500;
     75 const int Predictor::kTypicalSpeculativeGroupSize = 8;
     76 const int Predictor::kMaxSpeculativeResolveQueueDelayMs =
     77     (kExpectedResolutionTimeMs * Predictor::kTypicalSpeculativeGroupSize) /
     78     Predictor::kMaxSpeculativeParallelResolves;
     79 
     80 static int g_max_queueing_delay_ms =
     81     Predictor::kMaxSpeculativeResolveQueueDelayMs;
     82 static size_t g_max_parallel_resolves =
     83     Predictor::kMaxSpeculativeParallelResolves;
     84 
     85 // A version number for prefs that are saved. This should be incremented when
     86 // we change the format so that we discard old data.
     87 static const int kPredictorStartupFormatVersion = 1;
     88 
     89 class Predictor::LookupRequest {
     90  public:
     91   LookupRequest(Predictor* predictor,
     92                 net::HostResolver* host_resolver,
     93                 const GURL& url)
     94       : predictor_(predictor),
     95         url_(url),
     96         resolver_(host_resolver) {
     97   }
     98 
     99   // Return underlying network resolver status.
    100   // net::OK ==> Host was found synchronously.
    101   // net:ERR_IO_PENDING ==> Network will callback later with result.
    102   // anything else ==> Host was not found synchronously.
    103   int Start() {
    104     net::HostResolver::RequestInfo resolve_info(
    105         net::HostPortPair::FromURL(url_));
    106 
    107     // Make a note that this is a speculative resolve request. This allows us
    108     // to separate it from real navigations in the observer's callback, and
    109     // lets the HostResolver know it can de-prioritize it.
    110     resolve_info.set_is_speculative(true);
    111     return resolver_.Resolve(
    112         resolve_info,
    113         net::DEFAULT_PRIORITY,
    114         &addresses_,
    115         base::Bind(&LookupRequest::OnLookupFinished, base::Unretained(this)),
    116         net::BoundNetLog());
    117   }
    118 
    119  private:
    120   void OnLookupFinished(int result) {
    121     predictor_->OnLookupFinished(this, url_, result == net::OK);
    122   }
    123 
    124   Predictor* predictor_;  // The predictor which started us.
    125 
    126   const GURL url_;  // Hostname to resolve.
    127   net::SingleRequestHostResolver resolver_;
    128   net::AddressList addresses_;
    129 
    130   DISALLOW_COPY_AND_ASSIGN(LookupRequest);
    131 };
    132 
    133 // This records UMAs for preconnect usage based on navigation URLs to
    134 // gather precision/recall for user-event based preconnect triggers.
    135 // Stats are gathered via a LRU cache that remembers all preconnect within the
    136 // last N seconds.
    137 // A preconnect trigger is considered as used iff a navigation including
    138 // access to the preconnected host occurs within a time period specified by
    139 // kMaxUnusedSocketLifetimeSecondsWithoutAGet.
    140 class Predictor::PreconnectUsage {
    141  public:
    142   PreconnectUsage();
    143   ~PreconnectUsage();
    144 
    145   // Record a preconnect trigger to |url|.
    146   void ObservePreconnect(const GURL& url);
    147 
    148   // Record a user navigation with its redirect history, |url_chain|.
    149   // We are uncertain if this is actually a link navigation.
    150   void ObserveNavigationChain(const std::vector<GURL>& url_chain,
    151                               bool is_subresource);
    152 
    153   // Record a user link navigation to |final_url|.
    154   // We are certain that this is a user-triggered link navigation.
    155   void ObserveLinkNavigation(const GURL& final_url);
    156 
    157  private:
    158   // This tracks whether a preconnect was used in some navigation or not
    159   class PreconnectPrecisionStat {
    160    public:
    161     PreconnectPrecisionStat()
    162         : timestamp_(base::TimeTicks::Now()),
    163           was_used_(false) {
    164     }
    165 
    166     const base::TimeTicks& timestamp() { return timestamp_; }
    167 
    168     void set_was_used() { was_used_ = true; }
    169     bool was_used() const { return was_used_; }
    170 
    171    private:
    172     base::TimeTicks timestamp_;
    173     bool was_used_;
    174   };
    175 
    176   typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects;
    177   MRUPreconnects mru_preconnects_;
    178 
    179   // The longest time an entry can persist in mru_preconnect_
    180   const base::TimeDelta max_duration_;
    181 
    182   std::vector<GURL> recent_navigation_chain_;
    183 
    184   DISALLOW_COPY_AND_ASSIGN(PreconnectUsage);
    185 };
    186 
    187 Predictor::PreconnectUsage::PreconnectUsage()
    188     : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT),
    189       max_duration_(base::TimeDelta::FromSeconds(
    190           Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) {
    191 }
    192 
    193 Predictor::PreconnectUsage::~PreconnectUsage() {}
    194 
    195 void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) {
    196   // Evict any overly old entries and record stats.
    197   base::TimeTicks now = base::TimeTicks::Now();
    198 
    199   MRUPreconnects::reverse_iterator eldest_preconnect =
    200       mru_preconnects_.rbegin();
    201   while (!mru_preconnects_.empty()) {
    202     DCHECK(eldest_preconnect == mru_preconnects_.rbegin());
    203     if (now - eldest_preconnect->second.timestamp() < max_duration_)
    204       break;
    205 
    206     UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed",
    207                           eldest_preconnect->second.was_used());
    208     eldest_preconnect = mru_preconnects_.Erase(eldest_preconnect);
    209   }
    210 
    211   // Add new entry.
    212   GURL canonical_url(Predictor::CanonicalizeUrl(url));
    213   mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat());
    214 }
    215 
    216 void Predictor::PreconnectUsage::ObserveNavigationChain(
    217     const std::vector<GURL>& url_chain,
    218     bool is_subresource) {
    219   if (url_chain.empty())
    220     return;
    221 
    222   if (!is_subresource)
    223     recent_navigation_chain_ = url_chain;
    224 
    225   GURL canonical_url(Predictor::CanonicalizeUrl(url_chain.back()));
    226 
    227   MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url);
    228   bool was_preconnected = (itPreconnect != mru_preconnects_.end());
    229 
    230   // This is an UMA which was named incorrectly. This actually measures the
    231   // ratio of URLRequests which have used a preconnected session.
    232   UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", was_preconnected);
    233 }
    234 
    235 void Predictor::PreconnectUsage::ObserveLinkNavigation(const GURL& url) {
    236   if (recent_navigation_chain_.empty() ||
    237       url != recent_navigation_chain_.back()) {
    238     // The navigation chain is not available for this navigation.
    239     recent_navigation_chain_.clear();
    240     recent_navigation_chain_.push_back(url);
    241   }
    242 
    243   // See if the link navigation involved preconnected session.
    244   bool did_use_preconnect = false;
    245   for (std::vector<GURL>::const_iterator it = recent_navigation_chain_.begin();
    246        it != recent_navigation_chain_.end();
    247        ++it) {
    248     GURL canonical_url(Predictor::CanonicalizeUrl(*it));
    249 
    250     // Record the preconnect trigger for the url as used if exist
    251     MRUPreconnects::iterator itPreconnect =
    252         mru_preconnects_.Peek(canonical_url);
    253     bool was_preconnected = (itPreconnect != mru_preconnects_.end());
    254     if (was_preconnected) {
    255       itPreconnect->second.set_was_used();
    256       did_use_preconnect = true;
    257     }
    258   }
    259 
    260   UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedLinkNavigations", did_use_preconnect);
    261 }
    262 
    263 Predictor::Predictor(bool preconnect_enabled)
    264     : url_request_context_getter_(NULL),
    265       predictor_enabled_(true),
    266       peak_pending_lookups_(0),
    267       shutdown_(false),
    268       max_concurrent_dns_lookups_(g_max_parallel_resolves),
    269       max_dns_queue_delay_(
    270           TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)),
    271       host_resolver_(NULL),
    272       preconnect_enabled_(preconnect_enabled),
    273       consecutive_omnibox_preconnect_count_(0),
    274       next_trim_time_(base::TimeTicks::Now() +
    275                       TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) {
    276   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    277 }
    278 
    279 Predictor::~Predictor() {
    280   // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the
    281   // ProfileManagerTest has been updated with a mock profile.
    282   DCHECK(shutdown_);
    283 }
    284 
    285 // static
    286 Predictor* Predictor::CreatePredictor(bool preconnect_enabled,
    287                                       bool simple_shutdown) {
    288   if (simple_shutdown)
    289     return new SimplePredictor(preconnect_enabled);
    290   return new Predictor(preconnect_enabled);
    291 }
    292 
    293 void Predictor::RegisterProfilePrefs(
    294     user_prefs::PrefRegistrySyncable* registry) {
    295   registry->RegisterListPref(prefs::kDnsPrefetchingStartupList,
    296                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    297   registry->RegisterListPref(prefs::kDnsPrefetchingHostReferralList,
    298                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    299 }
    300 
    301 // --------------------- Start UI methods. ------------------------------------
    302 
    303 void Predictor::InitNetworkPredictor(PrefService* user_prefs,
    304                                      PrefService* local_state,
    305                                      IOThread* io_thread,
    306                                      net::URLRequestContextGetter* getter) {
    307   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    308 
    309   bool predictor_enabled =
    310       user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled);
    311 
    312   url_request_context_getter_ = getter;
    313 
    314   // Gather the list of hostnames to prefetch on startup.
    315   UrlList urls = GetPredictedUrlListAtStartup(user_prefs, local_state);
    316 
    317   base::ListValue* referral_list =
    318       static_cast<base::ListValue*>(user_prefs->GetList(
    319           prefs::kDnsPrefetchingHostReferralList)->DeepCopy());
    320 
    321   // Now that we have the statistics in memory, wipe them from the Preferences
    322   // file. They will be serialized back on a clean shutdown. This way we only
    323   // have to worry about clearing our in-memory state when Clearing Browsing
    324   // Data.
    325   user_prefs->ClearPref(prefs::kDnsPrefetchingStartupList);
    326   user_prefs->ClearPref(prefs::kDnsPrefetchingHostReferralList);
    327 
    328 #if defined(OS_ANDROID) || defined(OS_IOS)
    329   // TODO(marq): Once https://codereview.chromium.org/30883003/ lands, also
    330   // condition this on DataReductionProxySettings::IsDataReductionProxyAllowed()
    331   // Until then, we may create a proxy advisor when the proxy feature itself
    332   // isn't available, and the advisor instance will never send advisory
    333   // requests, which is slightly wasteful but not harmful.
    334   if (DataReductionProxySettings::IsPreconnectHintingAllowed()) {
    335     proxy_advisor_.reset(new ProxyAdvisor(user_prefs, getter));
    336   }
    337 #endif
    338 
    339   BrowserThread::PostTask(
    340       BrowserThread::IO,
    341       FROM_HERE,
    342       base::Bind(
    343           &Predictor::FinalizeInitializationOnIOThread,
    344           base::Unretained(this),
    345           urls, referral_list,
    346           io_thread, predictor_enabled));
    347 }
    348 
    349 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
    350   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    351   if (!predictor_enabled_)
    352     return;
    353   if (!url.is_valid() || !url.has_host())
    354     return;
    355   std::string host = url.HostNoBrackets();
    356   bool is_new_host_request = (host != last_omnibox_host_);
    357   last_omnibox_host_ = host;
    358 
    359   UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED);
    360   base::TimeTicks now = base::TimeTicks::Now();
    361 
    362   if (preconnect_enabled()) {
    363     if (preconnectable && !is_new_host_request) {
    364       ++consecutive_omnibox_preconnect_count_;
    365       // The omnibox suggests a search URL (for which we can preconnect) after
    366       // one or two characters are typed, even though such typing often (1 in
    367       // 3?) becomes a real URL.  This code waits till is has more evidence of a
    368       // preconnectable URL (search URL) before forming a preconnection, so as
    369       // to reduce the useless preconnect rate.
    370       // Perchance this logic should be pushed back into the omnibox, where the
    371       // actual characters typed, such as a space, can better forcast whether
    372       // we need to search/preconnect or not.  By waiting for at least 4
    373       // characters in a row that have lead to a search proposal, we avoid
    374       // preconnections for a prefix like "www." and we also wait until we have
    375       // at least a 4 letter word to search for.
    376       // Each character typed appears to induce 2 calls to
    377       // AnticipateOmniboxUrl(), so we double 4 characters and limit at 8
    378       // requests.
    379       // TODO(jar): Use an A/B test to optimize this.
    380       const int kMinConsecutiveRequests = 8;
    381       if (consecutive_omnibox_preconnect_count_ >= kMinConsecutiveRequests) {
    382         // TODO(jar): Perhaps we should do a GET to leave the socket open in the
    383         // pool.  Currently, we just do a connect, which MAY be reset if we
    384         // don't use it in 10 secondes!!!  As a result, we may do more
    385         // connections, and actually cost the server more than if we did a real
    386         // get with a fake request (/gen_204 might be the good path on Google).
    387         const int kMaxSearchKeepaliveSeconds(10);
    388         if ((now - last_omnibox_preconnect_).InSeconds() <
    389              kMaxSearchKeepaliveSeconds)
    390           return;  // We've done a preconnect recently.
    391         last_omnibox_preconnect_ = now;
    392         const int kConnectionsNeeded = 1;
    393         PreconnectUrl(CanonicalizeUrl(url), GURL(), motivation,
    394                       kConnectionsNeeded);
    395         return;  // Skip pre-resolution, since we'll open a connection.
    396       }
    397     } else {
    398       consecutive_omnibox_preconnect_count_ = 0;
    399     }
    400   }
    401 
    402   // Fall through and consider pre-resolution.
    403 
    404   // Omnibox tends to call in pairs (just a few milliseconds apart), and we
    405   // really don't need to keep resolving a name that often.
    406   // TODO(jar): A/B tests could check for perf impact of the early returns.
    407   if (!is_new_host_request) {
    408     const int kMinPreresolveSeconds(10);
    409     if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds())
    410       return;
    411   }
    412   last_omnibox_preresolve_ = now;
    413 
    414   BrowserThread::PostTask(
    415       BrowserThread::IO,
    416       FROM_HERE,
    417       base::Bind(&Predictor::Resolve, base::Unretained(this),
    418                  CanonicalizeUrl(url), motivation));
    419 }
    420 
    421 void Predictor::PreconnectUrlAndSubresources(const GURL& url,
    422     const GURL& first_party_for_cookies) {
    423   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    424          BrowserThread::CurrentlyOn(BrowserThread::IO));
    425   if (!predictor_enabled_ || !preconnect_enabled() ||
    426       !url.is_valid() || !url.has_host())
    427     return;
    428 
    429   UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED);
    430   const int kConnectionsNeeded = 1;
    431   PreconnectUrl(CanonicalizeUrl(url), first_party_for_cookies,
    432                 motivation, kConnectionsNeeded);
    433   PredictFrameSubresources(url.GetWithEmptyPath(), first_party_for_cookies);
    434 }
    435 
    436 UrlList Predictor::GetPredictedUrlListAtStartup(
    437     PrefService* user_prefs,
    438     PrefService* local_state) {
    439   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    440   UrlList urls;
    441   // Recall list of URLs we learned about during last session.
    442   // This may catch secondary hostnames, pulled in by the homepages.  It will
    443   // also catch more of the "primary" home pages, since that was (presumably)
    444   // rendered first (and will be rendered first this time too).
    445   const ListValue* startup_list =
    446       user_prefs->GetList(prefs::kDnsPrefetchingStartupList);
    447 
    448   if (startup_list) {
    449     base::ListValue::const_iterator it = startup_list->begin();
    450     int format_version = -1;
    451     if (it != startup_list->end() &&
    452         (*it)->GetAsInteger(&format_version) &&
    453         format_version == kPredictorStartupFormatVersion) {
    454       ++it;
    455       for (; it != startup_list->end(); ++it) {
    456         std::string url_spec;
    457         if (!(*it)->GetAsString(&url_spec)) {
    458           LOG(DFATAL);
    459           break;  // Format incompatibility.
    460         }
    461         GURL url(url_spec);
    462         if (!url.has_host() || !url.has_scheme()) {
    463           LOG(DFATAL);
    464           break;  // Format incompatibility.
    465         }
    466 
    467         urls.push_back(url);
    468       }
    469     }
    470   }
    471 
    472   // Prepare for any static home page(s) the user has in prefs.  The user may
    473   // have a LOT of tab's specified, so we may as well try to warm them all.
    474   SessionStartupPref tab_start_pref =
    475       SessionStartupPref::GetStartupPref(user_prefs);
    476   if (SessionStartupPref::URLS == tab_start_pref.type) {
    477     for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
    478       GURL gurl = tab_start_pref.urls[i];
    479       if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty())
    480         continue;
    481       if (gurl.SchemeIsHTTPOrHTTPS())
    482         urls.push_back(gurl.GetWithEmptyPath());
    483     }
    484   }
    485 
    486   if (urls.empty())
    487     urls.push_back(GURL("http://www.google.com:80"));
    488 
    489   return urls;
    490 }
    491 
    492 void Predictor::set_max_queueing_delay(int max_queueing_delay_ms) {
    493   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    494   g_max_queueing_delay_ms = max_queueing_delay_ms;
    495 }
    496 
    497 void Predictor::set_max_parallel_resolves(size_t max_parallel_resolves) {
    498   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    499   g_max_parallel_resolves = max_parallel_resolves;
    500 }
    501 
    502 void Predictor::ShutdownOnUIThread() {
    503   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    504   BrowserThread::PostTask(
    505       BrowserThread::IO,
    506       FROM_HERE,
    507       base::Bind(&Predictor::Shutdown, base::Unretained(this)));
    508 }
    509 
    510 // ---------------------- End UI methods. -------------------------------------
    511 
    512 // --------------------- Start IO methods. ------------------------------------
    513 
    514 void Predictor::Shutdown() {
    515   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    516   DCHECK(!shutdown_);
    517   shutdown_ = true;
    518 
    519   STLDeleteElements(&pending_lookups_);
    520 }
    521 
    522 void Predictor::DiscardAllResults() {
    523   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    524   // Delete anything listed so far in this session that shows in about:dns.
    525   referrers_.clear();
    526 
    527 
    528   // Try to delete anything in our work queue.
    529   while (!work_queue_.IsEmpty()) {
    530     // Emulate processing cycle as though host was not found.
    531     GURL url = work_queue_.Pop();
    532     UrlInfo* info = &results_[url];
    533     DCHECK(info->HasUrl(url));
    534     info->SetAssignedState();
    535     info->SetNoSuchNameState();
    536   }
    537   // Now every result_ is either resolved, or is being resolved
    538   // (see LookupRequest).
    539 
    540   // Step through result_, recording names of all hosts that can't be erased.
    541   // We can't erase anything being worked on.
    542   Results assignees;
    543   for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
    544     GURL url(it->first);
    545     UrlInfo* info = &it->second;
    546     DCHECK(info->HasUrl(url));
    547     if (info->is_assigned()) {
    548       info->SetPendingDeleteState();
    549       assignees[url] = *info;
    550     }
    551   }
    552   DCHECK_LE(assignees.size(), max_concurrent_dns_lookups_);
    553   results_.clear();
    554   // Put back in the names being worked on.
    555   for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
    556     DCHECK(it->second.is_marked_to_delete());
    557     results_[it->first] = it->second;
    558   }
    559 }
    560 
    561 // Overloaded Resolve() to take a vector of names.
    562 void Predictor::ResolveList(const UrlList& urls,
    563                             UrlInfo::ResolutionMotivation motivation) {
    564   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    565 
    566   for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) {
    567     AppendToResolutionQueue(*it, motivation);
    568   }
    569 }
    570 
    571 // Basic Resolve() takes an invidual name, and adds it
    572 // to the queue.
    573 void Predictor::Resolve(const GURL& url,
    574                         UrlInfo::ResolutionMotivation motivation) {
    575   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    576   if (!url.has_host())
    577     return;
    578   AppendToResolutionQueue(url, motivation);
    579 }
    580 
    581 void Predictor::LearnFromNavigation(const GURL& referring_url,
    582                                     const GURL& target_url) {
    583   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    584   if (!predictor_enabled_)
    585     return;
    586   DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
    587   DCHECK_NE(referring_url, GURL::EmptyGURL());
    588   DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url));
    589   DCHECK_NE(target_url, GURL::EmptyGURL());
    590 
    591   referrers_[referring_url].SuggestHost(target_url);
    592   // Possibly do some referrer trimming.
    593   TrimReferrers();
    594 }
    595 
    596 //-----------------------------------------------------------------------------
    597 // This section supports the about:dns page.
    598 
    599 void Predictor::PredictorGetHtmlInfo(Predictor* predictor,
    600                                      std::string* output) {
    601   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    602 
    603   output->append("<html><head><title>About DNS</title>"
    604                  // We'd like the following no-cache... but it doesn't work.
    605                  // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
    606                  "</head><body>");
    607   if (predictor && predictor->predictor_enabled()) {
    608     predictor->GetHtmlInfo(output);
    609   } else {
    610     output->append("DNS pre-resolution and TCP pre-connection is disabled.");
    611   }
    612   output->append("</body></html>");
    613 }
    614 
    615 // Provide sort order so all .com's are together, etc.
    616 struct RightToLeftStringSorter {
    617   bool operator()(const GURL& left, const GURL& right) const {
    618     return ReverseComponents(left) < ReverseComponents(right);
    619   }
    620 
    621  private:
    622   // Transforms something like "http://www.google.com/xyz" to
    623   // "http://com.google.www/xyz".
    624   static std::string ReverseComponents(const GURL& url) {
    625     // Reverse the components in the hostname.
    626     std::vector<std::string> parts;
    627     base::SplitString(url.host(), '.', &parts);
    628     std::reverse(parts.begin(), parts.end());
    629     std::string reversed_host = JoinString(parts, '.');
    630 
    631     // Return the new URL.
    632     GURL::Replacements url_components;
    633     url_components.SetHostStr(reversed_host);
    634     return url.ReplaceComponents(url_components).spec();
    635   }
    636 };
    637 
    638 void Predictor::GetHtmlReferrerLists(std::string* output) {
    639   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    640   if (referrers_.empty())
    641     return;
    642 
    643   // TODO(jar): Remove any plausible JavaScript from names before displaying.
    644 
    645   typedef std::set<GURL, struct RightToLeftStringSorter>
    646       SortedNames;
    647   SortedNames sorted_names;
    648 
    649   for (Referrers::iterator it = referrers_.begin();
    650        referrers_.end() != it; ++it)
    651     sorted_names.insert(it->first);
    652 
    653   output->append("<br><table border>");
    654   output->append(
    655       "<tr><th>Host for Page</th>"
    656       "<th>Page Load<br>Count</th>"
    657       "<th>Subresource<br>Navigations</th>"
    658       "<th>Subresource<br>PreConnects</th>"
    659       "<th>Subresource<br>PreResolves</th>"
    660       "<th>Expected<br>Connects</th>"
    661       "<th>Subresource Spec</th></tr>");
    662 
    663   for (SortedNames::iterator it = sorted_names.begin();
    664        sorted_names.end() != it; ++it) {
    665     Referrer* referrer = &(referrers_[*it]);
    666     bool first_set_of_futures = true;
    667     for (Referrer::iterator future_url = referrer->begin();
    668          future_url != referrer->end(); ++future_url) {
    669       output->append("<tr align=right>");
    670       if (first_set_of_futures) {
    671         base::StringAppendF(output,
    672                             "<td rowspan=%d>%s</td><td rowspan=%d>%d</td>",
    673                             static_cast<int>(referrer->size()),
    674                             it->spec().c_str(),
    675                             static_cast<int>(referrer->size()),
    676                             static_cast<int>(referrer->use_count()));
    677       }
    678       first_set_of_futures = false;
    679       base::StringAppendF(output,
    680           "<td>%d</td><td>%d</td><td>%d</td><td>%2.3f</td><td>%s</td></tr>",
    681           static_cast<int>(future_url->second.navigation_count()),
    682           static_cast<int>(future_url->second.preconnection_count()),
    683           static_cast<int>(future_url->second.preresolution_count()),
    684           static_cast<double>(future_url->second.subresource_use_rate()),
    685           future_url->first.spec().c_str());
    686     }
    687   }
    688   output->append("</table>");
    689 }
    690 
    691 void Predictor::GetHtmlInfo(std::string* output) {
    692   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    693   if (initial_observer_.get())
    694     initial_observer_->GetFirstResolutionsHtml(output);
    695   // Show list of subresource predictions and stats.
    696   GetHtmlReferrerLists(output);
    697 
    698   // Local lists for calling UrlInfo
    699   UrlInfo::UrlInfoTable name_not_found;
    700   UrlInfo::UrlInfoTable name_preresolved;
    701 
    702   // Get copies of all useful data.
    703   typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo;
    704   SortedUrlInfo snapshot;
    705   // UrlInfo supports value semantics, so we can do a shallow copy.
    706   for (Results::iterator it(results_.begin()); it != results_.end(); it++)
    707     snapshot[it->first] = it->second;
    708 
    709   // Partition the UrlInfo's into categories.
    710   for (SortedUrlInfo::iterator it(snapshot.begin());
    711        it != snapshot.end(); it++) {
    712     if (it->second.was_nonexistent()) {
    713       name_not_found.push_back(it->second);
    714       continue;
    715     }
    716     if (!it->second.was_found())
    717       continue;  // Still being processed.
    718     name_preresolved.push_back(it->second);
    719   }
    720 
    721   bool brief = false;
    722 #ifdef NDEBUG
    723   brief = true;
    724 #endif  // NDEBUG
    725 
    726   // Call for display of each table, along with title.
    727   UrlInfo::GetHtmlTable(name_preresolved,
    728       "Preresolution DNS records performed for ", brief, output);
    729   UrlInfo::GetHtmlTable(name_not_found,
    730       "Preresolving DNS records revealed non-existence for ", brief, output);
    731 }
    732 
    733 void Predictor::TrimReferrersNow() {
    734   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    735   // Just finish up work if an incremental trim is in progress.
    736   if (urls_being_trimmed_.empty())
    737     LoadUrlsForTrimming();
    738   IncrementalTrimReferrers(true);  // Do everything now.
    739 }
    740 
    741 void Predictor::SerializeReferrers(base::ListValue* referral_list) {
    742   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    743   referral_list->Clear();
    744   referral_list->Append(new base::FundamentalValue(kPredictorReferrerVersion));
    745   for (Referrers::const_iterator it = referrers_.begin();
    746        it != referrers_.end(); ++it) {
    747     // Serialize the list of subresource names.
    748     Value* subresource_list(it->second.Serialize());
    749 
    750     // Create a list for each referer.
    751     ListValue* motivator(new ListValue);
    752     motivator->Append(new StringValue(it->first.spec()));
    753     motivator->Append(subresource_list);
    754 
    755     referral_list->Append(motivator);
    756   }
    757 }
    758 
    759 void Predictor::DeserializeReferrers(const base::ListValue& referral_list) {
    760   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    761   int format_version = -1;
    762   if (referral_list.GetSize() > 0 &&
    763       referral_list.GetInteger(0, &format_version) &&
    764       format_version == kPredictorReferrerVersion) {
    765     for (size_t i = 1; i < referral_list.GetSize(); ++i) {
    766       const base::ListValue* motivator;
    767       if (!referral_list.GetList(i, &motivator)) {
    768         NOTREACHED();
    769         return;
    770       }
    771       std::string motivating_url_spec;
    772       if (!motivator->GetString(0, &motivating_url_spec)) {
    773         NOTREACHED();
    774         return;
    775       }
    776 
    777       const Value* subresource_list;
    778       if (!motivator->Get(1, &subresource_list)) {
    779         NOTREACHED();
    780         return;
    781       }
    782 
    783       referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list);
    784     }
    785   }
    786 }
    787 
    788 void Predictor::DeserializeReferrersThenDelete(
    789     base::ListValue* referral_list) {
    790   DeserializeReferrers(*referral_list);
    791   delete referral_list;
    792 }
    793 
    794 void Predictor::DiscardInitialNavigationHistory() {
    795   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    796   if (initial_observer_.get())
    797     initial_observer_->DiscardInitialNavigationHistory();
    798 }
    799 
    800 void Predictor::FinalizeInitializationOnIOThread(
    801     const UrlList& startup_urls,
    802     base::ListValue* referral_list,
    803     IOThread* io_thread,
    804     bool predictor_enabled) {
    805   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    806 
    807   predictor_enabled_ = predictor_enabled;
    808   initial_observer_.reset(new InitialObserver());
    809   host_resolver_ = io_thread->globals()->host_resolver.get();
    810   preconnect_usage_.reset(new PreconnectUsage());
    811 
    812   // base::WeakPtrFactory instances need to be created and destroyed
    813   // on the same thread. The predictor lives on the IO thread and will die
    814   // from there so now that we're on the IO thread we need to properly
    815   // initialize the base::WeakPtrFactory.
    816   // TODO(groby): Check if WeakPtrFactory has the same constraint.
    817   weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this));
    818 
    819   // Prefetch these hostnames on startup.
    820   DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED);
    821   DeserializeReferrersThenDelete(referral_list);
    822 }
    823 
    824 //-----------------------------------------------------------------------------
    825 // This section intermingles prefetch results with actual browser HTTP
    826 // network activity.  It supports calculating of the benefit of a prefetch, as
    827 // well as recording what prefetched hostname resolutions might be potentially
    828 // helpful during the next chrome-startup.
    829 //-----------------------------------------------------------------------------
    830 
    831 void Predictor::LearnAboutInitialNavigation(const GURL& url) {
    832   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    833   if (!predictor_enabled_ || NULL == initial_observer_.get() )
    834     return;
    835   initial_observer_->Append(url, this);
    836 }
    837 
    838 // This API is only used in the browser process.
    839 // It is called from an IPC message originating in the renderer.  It currently
    840 // includes both Page-Scan, and Link-Hover prefetching.
    841 // TODO(jar): Separate out link-hover prefetching, and page-scan results.
    842 void Predictor::DnsPrefetchList(const NameList& hostnames) {
    843   // TODO(jar): Push GURL transport further back into renderer, but this will
    844   // require a Webkit change in the observer :-/.
    845   UrlList urls;
    846   for (NameList::const_iterator it = hostnames.begin();
    847        it < hostnames.end();
    848        ++it) {
    849     urls.push_back(GURL("http://" + *it + ":80"));
    850   }
    851 
    852   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    853   DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED);
    854 }
    855 
    856 void Predictor::DnsPrefetchMotivatedList(
    857     const UrlList& urls,
    858     UrlInfo::ResolutionMotivation motivation) {
    859   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    860          BrowserThread::CurrentlyOn(BrowserThread::IO));
    861   if (!predictor_enabled_)
    862     return;
    863 
    864   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    865     ResolveList(urls, motivation);
    866   } else {
    867     BrowserThread::PostTask(
    868         BrowserThread::IO,
    869         FROM_HERE,
    870         base::Bind(&Predictor::ResolveList, base::Unretained(this),
    871                    urls, motivation));
    872   }
    873 }
    874 
    875 //-----------------------------------------------------------------------------
    876 // Functions to handle saving of hostnames from one session to the next, to
    877 // expedite startup times.
    878 
    879 static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
    880     base::ListValue* startup_list,
    881     base::ListValue* referral_list,
    882     base::WaitableEvent* completion,
    883     Predictor* predictor) {
    884   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    885 
    886   if (NULL == predictor) {
    887     completion->Signal();
    888     return;
    889   }
    890   predictor->SaveDnsPrefetchStateForNextStartupAndTrim(
    891       startup_list, referral_list, completion);
    892 }
    893 
    894 void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs) {
    895   if (!predictor_enabled_)
    896     return;
    897 
    898   base::WaitableEvent completion(true, false);
    899 
    900   ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList);
    901   ListPrefUpdate update_referral_list(prefs,
    902                                       prefs::kDnsPrefetchingHostReferralList);
    903   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    904     SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
    905         update_startup_list.Get(),
    906         update_referral_list.Get(),
    907         &completion,
    908         this);
    909   } else {
    910     bool posted = BrowserThread::PostTask(
    911         BrowserThread::IO,
    912         FROM_HERE,
    913         base::Bind(
    914             &SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread,
    915             update_startup_list.Get(),
    916             update_referral_list.Get(),
    917             &completion,
    918             this));
    919 
    920     // TODO(jar): Synchronous waiting for the IO thread is a potential source
    921     // to deadlocks and should be investigated. See http://crbug.com/78451.
    922     DCHECK(posted);
    923     if (posted) {
    924       // http://crbug.com/124954
    925       base::ThreadRestrictions::ScopedAllowWait allow_wait;
    926       completion.Wait();
    927     }
    928   }
    929 }
    930 
    931 void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim(
    932     base::ListValue* startup_list,
    933     base::ListValue* referral_list,
    934     base::WaitableEvent* completion) {
    935   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    936   if (initial_observer_.get())
    937     initial_observer_->GetInitialDnsResolutionList(startup_list);
    938 
    939   // Do at least one trim at shutdown, in case the user wasn't running long
    940   // enough to do any regular trimming of referrers.
    941   TrimReferrersNow();
    942   SerializeReferrers(referral_list);
    943 
    944   completion->Signal();
    945 }
    946 
    947 void Predictor::EnablePredictor(bool enable) {
    948   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    949          BrowserThread::CurrentlyOn(BrowserThread::IO));
    950 
    951   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    952     EnablePredictorOnIOThread(enable);
    953   } else {
    954     BrowserThread::PostTask(
    955         BrowserThread::IO,
    956         FROM_HERE,
    957         base::Bind(&Predictor::EnablePredictorOnIOThread,
    958                    base::Unretained(this), enable));
    959   }
    960 }
    961 
    962 void Predictor::EnablePredictorOnIOThread(bool enable) {
    963   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    964   predictor_enabled_ = enable;
    965 }
    966 
    967 void Predictor::PreconnectUrl(const GURL& url,
    968                               const GURL& first_party_for_cookies,
    969                               UrlInfo::ResolutionMotivation motivation,
    970                               int count) {
    971   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    972          BrowserThread::CurrentlyOn(BrowserThread::IO));
    973 
    974   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    975     PreconnectUrlOnIOThread(url, first_party_for_cookies, motivation, count);
    976   } else {
    977     BrowserThread::PostTask(
    978         BrowserThread::IO,
    979         FROM_HERE,
    980         base::Bind(&Predictor::PreconnectUrlOnIOThread,
    981                    base::Unretained(this), url, first_party_for_cookies,
    982                    motivation, count));
    983   }
    984 }
    985 
    986 void Predictor::PreconnectUrlOnIOThread(
    987     const GURL& url,
    988     const GURL& first_party_for_cookies,
    989     UrlInfo::ResolutionMotivation motivation,
    990     int count) {
    991   if (motivation == UrlInfo::MOUSE_OVER_MOTIVATED)
    992     RecordPreconnectTrigger(url);
    993 
    994   AdviseProxy(url, motivation, true /* is_preconnect */);
    995 
    996   PreconnectOnIOThread(url,
    997                        first_party_for_cookies,
    998                        motivation,
    999                        count,
   1000                        url_request_context_getter_.get());
   1001 }
   1002 
   1003 void Predictor::RecordPreconnectTrigger(const GURL& url) {
   1004   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1005   if (preconnect_usage_)
   1006     preconnect_usage_->ObservePreconnect(url);
   1007 }
   1008 
   1009 void Predictor::RecordPreconnectNavigationStat(
   1010     const std::vector<GURL>& url_chain,
   1011     bool is_subresource) {
   1012   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1013 
   1014   if (preconnect_usage_)
   1015     preconnect_usage_->ObserveNavigationChain(url_chain, is_subresource);
   1016 }
   1017 
   1018 void Predictor::RecordLinkNavigation(const GURL& url) {
   1019   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1020   if (preconnect_usage_)
   1021     preconnect_usage_->ObserveLinkNavigation(url);
   1022 }
   1023 
   1024 void Predictor::PredictFrameSubresources(const GURL& url,
   1025                                          const GURL& first_party_for_cookies) {
   1026   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
   1027          BrowserThread::CurrentlyOn(BrowserThread::IO));
   1028   if (!predictor_enabled_)
   1029     return;
   1030   DCHECK_EQ(url.GetWithEmptyPath(), url);
   1031   // Add one pass through the message loop to allow current navigation to
   1032   // proceed.
   1033   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
   1034     PrepareFrameSubresources(url, first_party_for_cookies);
   1035   } else {
   1036     BrowserThread::PostTask(
   1037         BrowserThread::IO,
   1038         FROM_HERE,
   1039         base::Bind(&Predictor::PrepareFrameSubresources,
   1040                    base::Unretained(this), url, first_party_for_cookies));
   1041   }
   1042 }
   1043 
   1044 void Predictor::AdviseProxy(const GURL& url,
   1045                             UrlInfo::ResolutionMotivation motivation,
   1046                             bool is_preconnect) {
   1047   if (!proxy_advisor_)
   1048     return;
   1049 
   1050   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
   1051          BrowserThread::CurrentlyOn(BrowserThread::IO));
   1052 
   1053   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
   1054     AdviseProxyOnIOThread(url, motivation, is_preconnect);
   1055   } else {
   1056     BrowserThread::PostTask(
   1057         BrowserThread::IO,
   1058         FROM_HERE,
   1059         base::Bind(&Predictor::AdviseProxyOnIOThread,
   1060                    base::Unretained(this), url, motivation, is_preconnect));
   1061   }
   1062 }
   1063 
   1064 enum SubresourceValue {
   1065   PRECONNECTION,
   1066   PRERESOLUTION,
   1067   TOO_NEW,
   1068   SUBRESOURCE_VALUE_MAX
   1069 };
   1070 
   1071 void Predictor::PrepareFrameSubresources(const GURL& url,
   1072                                          const GURL& first_party_for_cookies) {
   1073   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1074   DCHECK_EQ(url.GetWithEmptyPath(), url);
   1075   Referrers::iterator it = referrers_.find(url);
   1076   if (referrers_.end() == it) {
   1077     // Only when we don't know anything about this url, make 2 connections
   1078     // available.  We could do this completely via learning (by prepopulating
   1079     // the referrer_ list with this expected value), but it would swell the
   1080     // size of the list with all the "Leaf" nodes in the tree (nodes that don't
   1081     // load any subresources).  If we learn about this resource, we will instead
   1082     // provide a more carefully estimated preconnection count.
   1083     if (preconnect_enabled_) {
   1084       PreconnectUrlOnIOThread(url, first_party_for_cookies,
   1085                               UrlInfo::SELF_REFERAL_MOTIVATED, 2);
   1086     }
   1087     return;
   1088   }
   1089 
   1090   Referrer* referrer = &(it->second);
   1091   referrer->IncrementUseCount();
   1092   const UrlInfo::ResolutionMotivation motivation =
   1093       UrlInfo::LEARNED_REFERAL_MOTIVATED;
   1094   for (Referrer::iterator future_url = referrer->begin();
   1095        future_url != referrer->end(); ++future_url) {
   1096     SubresourceValue evalution(TOO_NEW);
   1097     double connection_expectation = future_url->second.subresource_use_rate();
   1098     UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation",
   1099                                 static_cast<int>(connection_expectation * 100),
   1100                                 10, 5000, 50);
   1101     future_url->second.ReferrerWasObserved();
   1102     if (preconnect_enabled_ &&
   1103         connection_expectation > kPreconnectWorthyExpectedValue) {
   1104       evalution = PRECONNECTION;
   1105       future_url->second.IncrementPreconnectionCount();
   1106       int count = static_cast<int>(std::ceil(connection_expectation));
   1107       if (url.host() == future_url->first.host())
   1108         ++count;
   1109       PreconnectUrlOnIOThread(future_url->first, first_party_for_cookies,
   1110                               motivation, count);
   1111     } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) {
   1112       evalution = PRERESOLUTION;
   1113       future_url->second.preresolution_increment();
   1114       UrlInfo* queued_info = AppendToResolutionQueue(future_url->first,
   1115                                                      motivation);
   1116       if (queued_info)
   1117         queued_info->SetReferringHostname(url);
   1118     }
   1119     UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution,
   1120                               SUBRESOURCE_VALUE_MAX);
   1121   }
   1122 }
   1123 
   1124 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url,
   1125                                  bool found) {
   1126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1127 
   1128   LookupFinished(request, url, found);
   1129   pending_lookups_.erase(request);
   1130   delete request;
   1131 
   1132   StartSomeQueuedResolutions();
   1133 }
   1134 
   1135 void Predictor::LookupFinished(LookupRequest* request, const GURL& url,
   1136                                bool found) {
   1137   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1138   UrlInfo* info = &results_[url];
   1139   DCHECK(info->HasUrl(url));
   1140   if (info->is_marked_to_delete()) {
   1141     results_.erase(url);
   1142   } else {
   1143     if (found)
   1144       info->SetFoundState();
   1145     else
   1146       info->SetNoSuchNameState();
   1147   }
   1148 }
   1149 
   1150 UrlInfo* Predictor::AppendToResolutionQueue(
   1151     const GURL& url,
   1152     UrlInfo::ResolutionMotivation motivation) {
   1153   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1154   DCHECK(url.has_host());
   1155 
   1156   if (shutdown_)
   1157     return NULL;
   1158 
   1159   UrlInfo* info = &results_[url];
   1160   info->SetUrl(url);  // Initialize or DCHECK.
   1161   // TODO(jar):  I need to discard names that have long since expired.
   1162   // Currently we only add to the domain map :-/
   1163 
   1164   DCHECK(info->HasUrl(url));
   1165 
   1166   if (!info->NeedsDnsUpdate()) {
   1167     info->DLogResultsStats("DNS PrefetchNotUpdated");
   1168     return NULL;
   1169   }
   1170 
   1171   AdviseProxy(url, motivation, false /* is_preconnect */);
   1172   if (proxy_advisor_ && proxy_advisor_->WouldProxyURL(url)) {
   1173     info->DLogResultsStats("DNS PrefetchForProxiedRequest");
   1174     return NULL;
   1175   }
   1176 
   1177   info->SetQueuedState(motivation);
   1178   work_queue_.Push(url, motivation);
   1179   StartSomeQueuedResolutions();
   1180   return info;
   1181 }
   1182 
   1183 bool Predictor::CongestionControlPerformed(UrlInfo* info) {
   1184   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1185   // Note: queue_duration is ONLY valid after we go to assigned state.
   1186   if (info->queue_duration() < max_dns_queue_delay_)
   1187     return false;
   1188   // We need to discard all entries in our queue, as we're keeping them waiting
   1189   // too long.  By doing this, we'll have a chance to quickly service urgent
   1190   // resolutions, and not have a bogged down system.
   1191   while (true) {
   1192     info->RemoveFromQueue();
   1193     if (work_queue_.IsEmpty())
   1194       break;
   1195     info = &results_[work_queue_.Pop()];
   1196     info->SetAssignedState();
   1197   }
   1198   return true;
   1199 }
   1200 
   1201 void Predictor::StartSomeQueuedResolutions() {
   1202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1203 
   1204   while (!work_queue_.IsEmpty() &&
   1205          pending_lookups_.size() < max_concurrent_dns_lookups_) {
   1206     const GURL url(work_queue_.Pop());
   1207     UrlInfo* info = &results_[url];
   1208     DCHECK(info->HasUrl(url));
   1209     info->SetAssignedState();
   1210 
   1211     if (CongestionControlPerformed(info)) {
   1212       DCHECK(work_queue_.IsEmpty());
   1213       return;
   1214     }
   1215 
   1216     LookupRequest* request = new LookupRequest(this, host_resolver_, url);
   1217     int status = request->Start();
   1218     if (status == net::ERR_IO_PENDING) {
   1219       // Will complete asynchronously.
   1220       pending_lookups_.insert(request);
   1221       peak_pending_lookups_ = std::max(peak_pending_lookups_,
   1222                                        pending_lookups_.size());
   1223     } else {
   1224       // Completed synchronously (was already cached by HostResolver), or else
   1225       // there was (equivalently) some network error that prevents us from
   1226       // finding the name.  Status net::OK means it was "found."
   1227       LookupFinished(request, url, status == net::OK);
   1228       delete request;
   1229     }
   1230   }
   1231 }
   1232 
   1233 void Predictor::TrimReferrers() {
   1234   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1235   if (!urls_being_trimmed_.empty())
   1236     return;   // There is incremental trimming in progress already.
   1237 
   1238   // Check to see if it is time to trim yet.
   1239   base::TimeTicks now = base::TimeTicks::Now();
   1240   if (now < next_trim_time_)
   1241     return;
   1242   next_trim_time_ = now + TimeDelta::FromHours(kDurationBetweenTrimmingsHours);
   1243 
   1244   LoadUrlsForTrimming();
   1245   PostIncrementalTrimTask();
   1246 }
   1247 
   1248 void Predictor::LoadUrlsForTrimming() {
   1249   DCHECK(urls_being_trimmed_.empty());
   1250   for (Referrers::const_iterator it = referrers_.begin();
   1251        it != referrers_.end(); ++it)
   1252     urls_being_trimmed_.push_back(it->first);
   1253   UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size());
   1254 }
   1255 
   1256 void Predictor::PostIncrementalTrimTask() {
   1257   if (urls_being_trimmed_.empty())
   1258     return;
   1259   const TimeDelta kDurationBetweenTrimmingIncrements =
   1260       TimeDelta::FromSeconds(kDurationBetweenTrimmingIncrementsSeconds);
   1261   base::MessageLoop::current()->PostDelayedTask(
   1262       FROM_HERE,
   1263       base::Bind(&Predictor::IncrementalTrimReferrers,
   1264                  weak_factory_->GetWeakPtr(), false),
   1265       kDurationBetweenTrimmingIncrements);
   1266 }
   1267 
   1268 void Predictor::IncrementalTrimReferrers(bool trim_all_now) {
   1269   size_t trim_count = urls_being_trimmed_.size();
   1270   if (!trim_all_now)
   1271     trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement);
   1272   while (trim_count-- != 0) {
   1273     Referrers::iterator it = referrers_.find(urls_being_trimmed_.back());
   1274     urls_being_trimmed_.pop_back();
   1275     if (it == referrers_.end())
   1276       continue;  // Defensive code: It got trimmed away already.
   1277     if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue))
   1278       referrers_.erase(it);
   1279   }
   1280   PostIncrementalTrimTask();
   1281 }
   1282 
   1283 void Predictor::AdviseProxyOnIOThread(const GURL& url,
   1284                                       UrlInfo::ResolutionMotivation motivation,
   1285                                       bool is_preconnect) {
   1286   if (!proxy_advisor_)
   1287     return;
   1288   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1289   proxy_advisor_->Advise(url, motivation, is_preconnect);
   1290 }
   1291 
   1292 // ---------------------- End IO methods. -------------------------------------
   1293 
   1294 //-----------------------------------------------------------------------------
   1295 
   1296 Predictor::HostNameQueue::HostNameQueue() {
   1297 }
   1298 
   1299 Predictor::HostNameQueue::~HostNameQueue() {
   1300 }
   1301 
   1302 void Predictor::HostNameQueue::Push(const GURL& url,
   1303     UrlInfo::ResolutionMotivation motivation) {
   1304   switch (motivation) {
   1305     case UrlInfo::STATIC_REFERAL_MOTIVATED:
   1306     case UrlInfo::LEARNED_REFERAL_MOTIVATED:
   1307     case UrlInfo::MOUSE_OVER_MOTIVATED:
   1308       rush_queue_.push(url);
   1309       break;
   1310 
   1311     default:
   1312       background_queue_.push(url);
   1313       break;
   1314   }
   1315 }
   1316 
   1317 bool Predictor::HostNameQueue::IsEmpty() const {
   1318   return rush_queue_.empty() && background_queue_.empty();
   1319 }
   1320 
   1321 GURL Predictor::HostNameQueue::Pop() {
   1322   DCHECK(!IsEmpty());
   1323   std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_
   1324                                               : &rush_queue_);
   1325   GURL url(queue->front());
   1326   queue->pop();
   1327   return url;
   1328 }
   1329 
   1330 //-----------------------------------------------------------------------------
   1331 // Member definitions for InitialObserver class.
   1332 
   1333 Predictor::InitialObserver::InitialObserver() {
   1334 }
   1335 
   1336 Predictor::InitialObserver::~InitialObserver() {
   1337 }
   1338 
   1339 void Predictor::InitialObserver::Append(const GURL& url,
   1340                                         Predictor* predictor) {
   1341   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1342 
   1343   // TODO(rlp): Do we really need the predictor check here?
   1344   if (NULL == predictor)
   1345     return;
   1346   if (kStartupResolutionCount <= first_navigations_.size())
   1347     return;
   1348 
   1349   DCHECK(url.SchemeIsHTTPOrHTTPS());
   1350   DCHECK_EQ(url, Predictor::CanonicalizeUrl(url));
   1351   if (first_navigations_.find(url) == first_navigations_.end())
   1352     first_navigations_[url] = base::TimeTicks::Now();
   1353 }
   1354 
   1355 void Predictor::InitialObserver::GetInitialDnsResolutionList(
   1356     base::ListValue* startup_list) {
   1357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1358   DCHECK(startup_list);
   1359   startup_list->Clear();
   1360   DCHECK_EQ(0u, startup_list->GetSize());
   1361   startup_list->Append(
   1362       new base::FundamentalValue(kPredictorStartupFormatVersion));
   1363   for (FirstNavigations::iterator it = first_navigations_.begin();
   1364        it != first_navigations_.end();
   1365        ++it) {
   1366     DCHECK(it->first == Predictor::CanonicalizeUrl(it->first));
   1367     startup_list->Append(new StringValue(it->first.spec()));
   1368   }
   1369 }
   1370 
   1371 void Predictor::InitialObserver::GetFirstResolutionsHtml(
   1372     std::string* output) {
   1373   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1374 
   1375   UrlInfo::UrlInfoTable resolution_list;
   1376   {
   1377     for (FirstNavigations::iterator it(first_navigations_.begin());
   1378          it != first_navigations_.end();
   1379          it++) {
   1380       UrlInfo info;
   1381       info.SetUrl(it->first);
   1382       info.set_time(it->second);
   1383       resolution_list.push_back(info);
   1384     }
   1385   }
   1386   UrlInfo::GetHtmlTable(resolution_list,
   1387       "Future startups will prefetch DNS records for ", false, output);
   1388 }
   1389 
   1390 //-----------------------------------------------------------------------------
   1391 // Helper functions
   1392 //-----------------------------------------------------------------------------
   1393 
   1394 // static
   1395 GURL Predictor::CanonicalizeUrl(const GURL& url) {
   1396   if (!url.has_host())
   1397      return GURL::EmptyGURL();
   1398 
   1399   std::string scheme;
   1400   if (url.has_scheme()) {
   1401     scheme = url.scheme();
   1402     if (scheme != "http" && scheme != "https")
   1403       return GURL::EmptyGURL();
   1404     if (url.has_port())
   1405       return url.GetWithEmptyPath();
   1406   } else {
   1407     scheme = "http";
   1408   }
   1409 
   1410   // If we omit a port, it will default to 80 or 443 as appropriate.
   1411   std::string colon_plus_port;
   1412   if (url.has_port())
   1413     colon_plus_port = ":" + url.port();
   1414 
   1415   return GURL(scheme + "://" + url.host() + colon_plus_port);
   1416 }
   1417 
   1418 void SimplePredictor::InitNetworkPredictor(
   1419     PrefService* user_prefs,
   1420     PrefService* local_state,
   1421     IOThread* io_thread,
   1422     net::URLRequestContextGetter* getter) {
   1423   // Empty function for unittests.
   1424 }
   1425 
   1426 void SimplePredictor::ShutdownOnUIThread() {
   1427   SetShutdown(true);
   1428 }
   1429 
   1430 }  // namespace chrome_browser_net
   1431