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