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