Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / net / predictor.cc
blobc435a8c42e1552334f025c5c16a57ecfcfa8f769
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 // The default value of the credentials flag when preconnecting.
91 static bool kAllowCredentialsOnPreconnectByDefault = true;
93 static int g_max_queueing_delay_ms =
94 Predictor::kMaxSpeculativeResolveQueueDelayMs;
95 static size_t g_max_parallel_resolves =
96 Predictor::kMaxSpeculativeParallelResolves;
98 // A version number for prefs that are saved. This should be incremented when
99 // we change the format so that we discard old data.
100 static const int kPredictorStartupFormatVersion = 1;
102 class Predictor::LookupRequest {
103 public:
104 LookupRequest(Predictor* predictor,
105 net::HostResolver* host_resolver,
106 const GURL& url)
107 : predictor_(predictor),
108 url_(url),
109 resolver_(host_resolver) {
112 // Return underlying network resolver status.
113 // net::OK ==> Host was found synchronously.
114 // net:ERR_IO_PENDING ==> Network will callback later with result.
115 // anything else ==> Host was not found synchronously.
116 int Start() {
117 net::HostResolver::RequestInfo resolve_info(
118 net::HostPortPair::FromURL(url_));
120 // Make a note that this is a speculative resolve request. This allows us
121 // to separate it from real navigations in the observer's callback, and
122 // lets the HostResolver know it can de-prioritize it.
123 resolve_info.set_is_speculative(true);
124 return resolver_.Resolve(
125 resolve_info,
126 net::DEFAULT_PRIORITY,
127 &addresses_,
128 base::Bind(&LookupRequest::OnLookupFinished, base::Unretained(this)),
129 net::BoundNetLog());
132 private:
133 void OnLookupFinished(int result) {
134 predictor_->OnLookupFinished(this, url_, result == net::OK);
137 Predictor* predictor_; // The predictor which started us.
139 const GURL url_; // Hostname to resolve.
140 net::SingleRequestHostResolver resolver_;
141 net::AddressList addresses_;
143 DISALLOW_COPY_AND_ASSIGN(LookupRequest);
146 Predictor::Predictor(bool preconnect_enabled, bool predictor_enabled)
147 : url_request_context_getter_(NULL),
148 predictor_enabled_(predictor_enabled),
149 user_prefs_(NULL),
150 profile_io_data_(NULL),
151 peak_pending_lookups_(0),
152 shutdown_(false),
153 max_concurrent_dns_lookups_(g_max_parallel_resolves),
154 max_dns_queue_delay_(
155 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)),
156 host_resolver_(NULL),
157 transport_security_state_(NULL),
158 ssl_config_service_(NULL),
159 proxy_service_(NULL),
160 preconnect_enabled_(preconnect_enabled),
161 consecutive_omnibox_preconnect_count_(0),
162 next_trim_time_(base::TimeTicks::Now() +
163 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)),
164 observer_(NULL) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
168 Predictor::~Predictor() {
169 DCHECK_CURRENTLY_ON(BrowserThread::IO);
170 DCHECK(shutdown_);
173 // static
174 Predictor* Predictor::CreatePredictor(bool preconnect_enabled,
175 bool predictor_enabled,
176 bool simple_shutdown) {
177 if (simple_shutdown)
178 return new SimplePredictor(preconnect_enabled, predictor_enabled);
179 return new Predictor(preconnect_enabled, predictor_enabled);
182 void Predictor::RegisterProfilePrefs(
183 user_prefs::PrefRegistrySyncable* registry) {
184 registry->RegisterListPref(prefs::kDnsPrefetchingStartupList);
185 registry->RegisterListPref(prefs::kDnsPrefetchingHostReferralList);
188 // --------------------- Start UI methods. ------------------------------------
190 void Predictor::InitNetworkPredictor(PrefService* user_prefs,
191 PrefService* local_state,
192 IOThread* io_thread,
193 net::URLRequestContextGetter* getter,
194 ProfileIOData* profile_io_data) {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI);
197 user_prefs_ = user_prefs;
198 url_request_context_getter_ = getter;
200 // Gather the list of hostnames to prefetch on startup.
201 UrlList urls = GetPredictedUrlListAtStartup(user_prefs, local_state);
203 base::ListValue* referral_list =
204 static_cast<base::ListValue*>(user_prefs->GetList(
205 prefs::kDnsPrefetchingHostReferralList)->DeepCopy());
207 // Now that we have the statistics in memory, wipe them from the Preferences
208 // file. They will be serialized back on a clean shutdown. This way we only
209 // have to worry about clearing our in-memory state when Clearing Browsing
210 // Data.
211 user_prefs->ClearPref(prefs::kDnsPrefetchingStartupList);
212 user_prefs->ClearPref(prefs::kDnsPrefetchingHostReferralList);
214 BrowserThread::PostTask(
215 BrowserThread::IO,
216 FROM_HERE,
217 base::Bind(
218 &Predictor::FinalizeInitializationOnIOThread,
219 base::Unretained(this),
220 urls, referral_list,
221 io_thread, profile_io_data));
224 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI);
226 if (!predictor_enabled_)
227 return;
228 if (!url.is_valid() || !url.has_host())
229 return;
230 if (!CanPreresolveAndPreconnect())
231 return;
233 std::string host = url.HostNoBrackets();
234 bool is_new_host_request = (host != last_omnibox_host_);
235 last_omnibox_host_ = host;
237 UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED);
238 base::TimeTicks now = base::TimeTicks::Now();
240 if (preconnect_enabled_) {
241 if (preconnectable && !is_new_host_request) {
242 ++consecutive_omnibox_preconnect_count_;
243 // The omnibox suggests a search URL (for which we can preconnect) after
244 // one or two characters are typed, even though such typing often (1 in
245 // 3?) becomes a real URL. This code waits till is has more evidence of a
246 // preconnectable URL (search URL) before forming a preconnection, so as
247 // to reduce the useless preconnect rate.
248 // Perchance this logic should be pushed back into the omnibox, where the
249 // actual characters typed, such as a space, can better forcast whether
250 // we need to search/preconnect or not. By waiting for at least 4
251 // characters in a row that have lead to a search proposal, we avoid
252 // preconnections for a prefix like "www." and we also wait until we have
253 // at least a 4 letter word to search for.
254 // Each character typed appears to induce 2 calls to
255 // AnticipateOmniboxUrl(), so we double 4 characters and limit at 8
256 // requests.
257 // TODO(jar): Use an A/B test to optimize this.
258 const int kMinConsecutiveRequests = 8;
259 if (consecutive_omnibox_preconnect_count_ >= kMinConsecutiveRequests) {
260 // TODO(jar): Perhaps we should do a GET to leave the socket open in the
261 // pool. Currently, we just do a connect, which MAY be reset if we
262 // don't use it in 10 secondes!!! As a result, we may do more
263 // connections, and actually cost the server more than if we did a real
264 // get with a fake request (/gen_204 might be the good path on Google).
265 const int kMaxSearchKeepaliveSeconds(10);
266 if ((now - last_omnibox_preconnect_).InSeconds() <
267 kMaxSearchKeepaliveSeconds)
268 return; // We've done a preconnect recently.
269 last_omnibox_preconnect_ = now;
270 const int kConnectionsNeeded = 1;
271 PreconnectUrl(CanonicalizeUrl(url), GURL(), motivation,
272 kAllowCredentialsOnPreconnectByDefault,
273 kConnectionsNeeded);
274 return; // Skip pre-resolution, since we'll open a connection.
276 } else {
277 consecutive_omnibox_preconnect_count_ = 0;
281 // Fall through and consider pre-resolution.
283 // Omnibox tends to call in pairs (just a few milliseconds apart), and we
284 // really don't need to keep resolving a name that often.
285 // TODO(jar): A/B tests could check for perf impact of the early returns.
286 if (!is_new_host_request) {
287 const int kMinPreresolveSeconds(10);
288 if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds())
289 return;
291 last_omnibox_preresolve_ = now;
293 BrowserThread::PostTask(
294 BrowserThread::IO,
295 FROM_HERE,
296 base::Bind(&Predictor::Resolve, base::Unretained(this),
297 CanonicalizeUrl(url), motivation));
300 void Predictor::PreconnectUrlAndSubresources(const GURL& url,
301 const GURL& first_party_for_cookies) {
302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
303 BrowserThread::CurrentlyOn(BrowserThread::IO));
304 if (!predictor_enabled_ || !preconnect_enabled_ ||
305 !url.is_valid() || !url.has_host())
306 return;
307 if (!CanPreresolveAndPreconnect())
308 return;
310 UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED);
311 const int kConnectionsNeeded = 1;
312 PreconnectUrl(CanonicalizeUrl(url), first_party_for_cookies, motivation,
313 kConnectionsNeeded, kAllowCredentialsOnPreconnectByDefault);
314 PredictFrameSubresources(url.GetWithEmptyPath(), first_party_for_cookies);
317 UrlList Predictor::GetPredictedUrlListAtStartup(
318 PrefService* user_prefs,
319 PrefService* local_state) {
320 DCHECK_CURRENTLY_ON(BrowserThread::UI);
321 UrlList urls;
322 // Recall list of URLs we learned about during last session.
323 // This may catch secondary hostnames, pulled in by the homepages. It will
324 // also catch more of the "primary" home pages, since that was (presumably)
325 // rendered first (and will be rendered first this time too).
326 const base::ListValue* startup_list =
327 user_prefs->GetList(prefs::kDnsPrefetchingStartupList);
329 if (startup_list) {
330 base::ListValue::const_iterator it = startup_list->begin();
331 int format_version = -1;
332 if (it != startup_list->end() &&
333 (*it)->GetAsInteger(&format_version) &&
334 format_version == kPredictorStartupFormatVersion) {
335 ++it;
336 for (; it != startup_list->end(); ++it) {
337 std::string url_spec;
338 if (!(*it)->GetAsString(&url_spec)) {
339 LOG(DFATAL);
340 break; // Format incompatibility.
342 GURL url(url_spec);
343 if (!url.has_host() || !url.has_scheme()) {
344 LOG(DFATAL);
345 break; // Format incompatibility.
348 urls.push_back(url);
353 // Prepare for any static home page(s) the user has in prefs. The user may
354 // have a LOT of tab's specified, so we may as well try to warm them all.
355 SessionStartupPref tab_start_pref =
356 SessionStartupPref::GetStartupPref(user_prefs);
357 if (SessionStartupPref::URLS == tab_start_pref.type) {
358 for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
359 GURL gurl = tab_start_pref.urls[i];
360 if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty())
361 continue;
362 if (gurl.SchemeIsHTTPOrHTTPS())
363 urls.push_back(gurl.GetWithEmptyPath());
367 if (urls.empty())
368 urls.push_back(GURL("http://www.google.com:80"));
370 return urls;
373 void Predictor::set_max_queueing_delay(int max_queueing_delay_ms) {
374 DCHECK_CURRENTLY_ON(BrowserThread::UI);
375 g_max_queueing_delay_ms = max_queueing_delay_ms;
378 void Predictor::set_max_parallel_resolves(size_t max_parallel_resolves) {
379 DCHECK_CURRENTLY_ON(BrowserThread::UI);
380 g_max_parallel_resolves = max_parallel_resolves;
383 void Predictor::ShutdownOnUIThread() {
384 DCHECK_CURRENTLY_ON(BrowserThread::UI);
385 BrowserThread::PostTask(
386 BrowserThread::IO,
387 FROM_HERE,
388 base::Bind(&Predictor::Shutdown, base::Unretained(this)));
391 // ---------------------- End UI methods. -------------------------------------
393 // --------------------- Start IO methods. ------------------------------------
395 void Predictor::Shutdown() {
396 DCHECK_CURRENTLY_ON(BrowserThread::IO);
397 DCHECK(!shutdown_);
398 shutdown_ = true;
400 STLDeleteElements(&pending_lookups_);
403 void Predictor::DiscardAllResults() {
404 DCHECK_CURRENTLY_ON(BrowserThread::IO);
405 // Delete anything listed so far in this session that shows in about:dns.
406 referrers_.clear();
409 // Try to delete anything in our work queue.
410 while (!work_queue_.IsEmpty()) {
411 // Emulate processing cycle as though host was not found.
412 GURL url = work_queue_.Pop();
413 UrlInfo* info = &results_[url];
414 DCHECK(info->HasUrl(url));
415 info->SetAssignedState();
416 info->SetNoSuchNameState();
418 // Now every result_ is either resolved, or is being resolved
419 // (see LookupRequest).
421 // Step through result_, recording names of all hosts that can't be erased.
422 // We can't erase anything being worked on.
423 Results assignees;
424 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
425 GURL url(it->first);
426 UrlInfo* info = &it->second;
427 DCHECK(info->HasUrl(url));
428 if (info->is_assigned()) {
429 info->SetPendingDeleteState();
430 assignees[url] = *info;
433 DCHECK_LE(assignees.size(), max_concurrent_dns_lookups_);
434 results_.clear();
435 // Put back in the names being worked on.
436 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
437 DCHECK(it->second.is_marked_to_delete());
438 results_[it->first] = it->second;
442 // Overloaded Resolve() to take a vector of names.
443 void Predictor::ResolveList(const UrlList& urls,
444 UrlInfo::ResolutionMotivation motivation) {
445 DCHECK_CURRENTLY_ON(BrowserThread::IO);
447 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) {
448 AppendToResolutionQueue(*it, motivation);
452 // Basic Resolve() takes an invidual name, and adds it
453 // to the queue.
454 void Predictor::Resolve(const GURL& url,
455 UrlInfo::ResolutionMotivation motivation) {
456 DCHECK_CURRENTLY_ON(BrowserThread::IO);
457 if (!url.has_host())
458 return;
459 AppendToResolutionQueue(url, motivation);
462 void Predictor::LearnFromNavigation(const GURL& referring_url,
463 const GURL& target_url) {
464 DCHECK_CURRENTLY_ON(BrowserThread::IO);
465 if (!predictor_enabled_ || !CanPreresolveAndPreconnect())
466 return;
467 DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url));
468 DCHECK_NE(referring_url, GURL::EmptyGURL());
469 DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url));
470 DCHECK_NE(target_url, GURL::EmptyGURL());
472 referrers_[referring_url].SuggestHost(target_url);
473 // Possibly do some referrer trimming.
474 TrimReferrers();
477 //-----------------------------------------------------------------------------
478 // This section supports the about:dns page.
480 void Predictor::PredictorGetHtmlInfo(Predictor* predictor,
481 std::string* output) {
482 DCHECK_CURRENTLY_ON(BrowserThread::IO);
484 output->append("<html><head><title>About DNS</title>"
485 // We'd like the following no-cache... but it doesn't work.
486 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
487 "</head><body>");
488 if (predictor && predictor->predictor_enabled() &&
489 predictor->CanPreresolveAndPreconnect()) {
490 predictor->GetHtmlInfo(output);
491 } else {
492 output->append("DNS pre-resolution and TCP pre-connection is disabled.");
494 output->append("</body></html>");
497 // Provide sort order so all .com's are together, etc.
498 struct RightToLeftStringSorter {
499 bool operator()(const GURL& left, const GURL& right) const {
500 return ReverseComponents(left) < ReverseComponents(right);
503 private:
504 // Transforms something like "http://www.google.com/xyz" to
505 // "http://com.google.www/xyz".
506 static std::string ReverseComponents(const GURL& url) {
507 // Reverse the components in the hostname.
508 std::vector<std::string> parts = base::SplitString(
509 url.host(), ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
510 std::reverse(parts.begin(), parts.end());
511 std::string reversed_host = base::JoinString(parts, ".");
513 // Return the new URL.
514 GURL::Replacements url_components;
515 url_components.SetHostStr(reversed_host);
516 return url.ReplaceComponents(url_components).spec();
520 void Predictor::GetHtmlReferrerLists(std::string* output) {
521 DCHECK_CURRENTLY_ON(BrowserThread::IO);
522 if (referrers_.empty())
523 return;
525 // TODO(jar): Remove any plausible JavaScript from names before displaying.
527 typedef std::set<GURL, struct RightToLeftStringSorter>
528 SortedNames;
529 SortedNames sorted_names;
531 for (Referrers::iterator it = referrers_.begin();
532 referrers_.end() != it; ++it)
533 sorted_names.insert(it->first);
535 output->append("<br><table border>");
536 output->append(
537 "<tr><th>Host for Page</th>"
538 "<th>Page Load<br>Count</th>"
539 "<th>Subresource<br>Navigations</th>"
540 "<th>Subresource<br>PreConnects</th>"
541 "<th>Subresource<br>PreResolves</th>"
542 "<th>Expected<br>Connects</th>"
543 "<th>Subresource Spec</th></tr>");
545 for (SortedNames::iterator it = sorted_names.begin();
546 sorted_names.end() != it; ++it) {
547 Referrer* referrer = &(referrers_[*it]);
548 bool first_set_of_futures = true;
549 for (Referrer::iterator future_url = referrer->begin();
550 future_url != referrer->end(); ++future_url) {
551 output->append("<tr align=right>");
552 if (first_set_of_futures) {
553 base::StringAppendF(output,
554 "<td rowspan=%d>%s</td><td rowspan=%d>%d</td>",
555 static_cast<int>(referrer->size()),
556 it->spec().c_str(),
557 static_cast<int>(referrer->size()),
558 static_cast<int>(referrer->use_count()));
560 first_set_of_futures = false;
561 base::StringAppendF(output,
562 "<td>%d</td><td>%d</td><td>%d</td><td>%2.3f</td><td>%s</td></tr>",
563 static_cast<int>(future_url->second.navigation_count()),
564 static_cast<int>(future_url->second.preconnection_count()),
565 static_cast<int>(future_url->second.preresolution_count()),
566 static_cast<double>(future_url->second.subresource_use_rate()),
567 future_url->first.spec().c_str());
570 output->append("</table>");
573 void Predictor::GetHtmlInfo(std::string* output) {
574 DCHECK_CURRENTLY_ON(BrowserThread::IO);
575 if (initial_observer_.get())
576 initial_observer_->GetFirstResolutionsHtml(output);
577 // Show list of subresource predictions and stats.
578 GetHtmlReferrerLists(output);
580 // Local lists for calling UrlInfo
581 UrlInfo::UrlInfoTable name_not_found;
582 UrlInfo::UrlInfoTable name_preresolved;
584 // Get copies of all useful data.
585 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo;
586 SortedUrlInfo snapshot;
587 // UrlInfo supports value semantics, so we can do a shallow copy.
588 for (Results::iterator it(results_.begin()); it != results_.end(); it++)
589 snapshot[it->first] = it->second;
591 // Partition the UrlInfo's into categories.
592 for (SortedUrlInfo::iterator it(snapshot.begin());
593 it != snapshot.end(); it++) {
594 if (it->second.was_nonexistent()) {
595 name_not_found.push_back(it->second);
596 continue;
598 if (!it->second.was_found())
599 continue; // Still being processed.
600 name_preresolved.push_back(it->second);
603 bool brief = false;
604 #ifdef NDEBUG
605 brief = true;
606 #endif // NDEBUG
608 // Call for display of each table, along with title.
609 UrlInfo::GetHtmlTable(name_preresolved,
610 "Preresolution DNS records performed for ", brief, output);
611 UrlInfo::GetHtmlTable(name_not_found,
612 "Preresolving DNS records revealed non-existence for ", brief, output);
615 void Predictor::TrimReferrersNow() {
616 DCHECK_CURRENTLY_ON(BrowserThread::IO);
617 // Just finish up work if an incremental trim is in progress.
618 if (urls_being_trimmed_.empty())
619 LoadUrlsForTrimming();
620 IncrementalTrimReferrers(true); // Do everything now.
623 void Predictor::SerializeReferrers(base::ListValue* referral_list) {
624 DCHECK_CURRENTLY_ON(BrowserThread::IO);
625 referral_list->Clear();
626 referral_list->Append(new base::FundamentalValue(kPredictorReferrerVersion));
627 for (Referrers::const_iterator it = referrers_.begin();
628 it != referrers_.end(); ++it) {
629 // Serialize the list of subresource names.
630 base::Value* subresource_list(it->second.Serialize());
632 // Create a list for each referer.
633 base::ListValue* motivator(new base::ListValue);
634 motivator->Append(new base::StringValue(it->first.spec()));
635 motivator->Append(subresource_list);
637 referral_list->Append(motivator);
641 void Predictor::DeserializeReferrers(const base::ListValue& referral_list) {
642 DCHECK_CURRENTLY_ON(BrowserThread::IO);
643 int format_version = -1;
644 if (referral_list.GetSize() > 0 &&
645 referral_list.GetInteger(0, &format_version) &&
646 format_version == kPredictorReferrerVersion) {
647 for (size_t i = 1; i < referral_list.GetSize(); ++i) {
648 const base::ListValue* motivator;
649 if (!referral_list.GetList(i, &motivator)) {
650 NOTREACHED();
651 return;
653 std::string motivating_url_spec;
654 if (!motivator->GetString(0, &motivating_url_spec)) {
655 NOTREACHED();
656 return;
659 const base::Value* subresource_list;
660 if (!motivator->Get(1, &subresource_list)) {
661 NOTREACHED();
662 return;
665 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list);
670 void Predictor::DeserializeReferrersThenDelete(
671 base::ListValue* referral_list) {
672 DeserializeReferrers(*referral_list);
673 delete referral_list;
676 void Predictor::DiscardInitialNavigationHistory() {
677 DCHECK_CURRENTLY_ON(BrowserThread::IO);
678 if (initial_observer_.get())
679 initial_observer_->DiscardInitialNavigationHistory();
682 void Predictor::FinalizeInitializationOnIOThread(
683 const UrlList& startup_urls,
684 base::ListValue* referral_list,
685 IOThread* io_thread,
686 ProfileIOData* profile_io_data) {
687 DCHECK_CURRENTLY_ON(BrowserThread::IO);
689 profile_io_data_ = profile_io_data;
690 initial_observer_.reset(new InitialObserver());
691 host_resolver_ = io_thread->globals()->host_resolver.get();
693 net::URLRequestContext* context =
694 url_request_context_getter_->GetURLRequestContext();
695 transport_security_state_ = context->transport_security_state();
696 ssl_config_service_ = context->ssl_config_service();
697 proxy_service_ = context->proxy_service();
699 // base::WeakPtrFactory instances need to be created and destroyed
700 // on the same thread. The predictor lives on the IO thread and will die
701 // from there so now that we're on the IO thread we need to properly
702 // initialize the base::WeakPtrFactory.
703 // TODO(groby): Check if WeakPtrFactory has the same constraint.
704 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this));
706 // Prefetch these hostnames on startup.
707 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED);
709 DeserializeReferrersThenDelete(referral_list);
712 //-----------------------------------------------------------------------------
713 // This section intermingles prefetch results with actual browser HTTP
714 // network activity. It supports calculating of the benefit of a prefetch, as
715 // well as recording what prefetched hostname resolutions might be potentially
716 // helpful during the next chrome-startup.
717 //-----------------------------------------------------------------------------
719 void Predictor::LearnAboutInitialNavigation(const GURL& url) {
720 DCHECK_CURRENTLY_ON(BrowserThread::IO);
721 if (!predictor_enabled_ || NULL == initial_observer_.get() ||
722 !CanPreresolveAndPreconnect()) {
723 return;
725 initial_observer_->Append(url, this);
728 // This API is only used in the browser process.
729 // It is called from an IPC message originating in the renderer. It currently
730 // includes both Page-Scan, and Link-Hover prefetching.
731 // TODO(jar): Separate out link-hover prefetching, and page-scan results.
732 void Predictor::DnsPrefetchList(const NameList& hostnames) {
733 // TODO(jar): Push GURL transport further back into renderer, but this will
734 // require a Webkit change in the observer :-/.
735 UrlList urls;
736 for (NameList::const_iterator it = hostnames.begin();
737 it < hostnames.end();
738 ++it) {
739 urls.push_back(GURL("http://" + *it + ":80"));
742 DCHECK_CURRENTLY_ON(BrowserThread::IO);
743 DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED);
746 void Predictor::DnsPrefetchMotivatedList(
747 const UrlList& urls,
748 UrlInfo::ResolutionMotivation motivation) {
749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
750 BrowserThread::CurrentlyOn(BrowserThread::IO));
751 if (!predictor_enabled_)
752 return;
753 if (!CanPreresolveAndPreconnect())
754 return;
756 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
757 ResolveList(urls, motivation);
758 } else {
759 BrowserThread::PostTask(
760 BrowserThread::IO,
761 FROM_HERE,
762 base::Bind(&Predictor::ResolveList, base::Unretained(this),
763 urls, motivation));
767 //-----------------------------------------------------------------------------
768 // Functions to handle saving of hostnames from one session to the next, to
769 // expedite startup times.
771 static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
772 base::ListValue* startup_list,
773 base::ListValue* referral_list,
774 base::WaitableEvent* completion,
775 Predictor* predictor) {
776 DCHECK_CURRENTLY_ON(BrowserThread::IO);
778 if (NULL == predictor) {
779 completion->Signal();
780 return;
782 predictor->SaveDnsPrefetchStateForNextStartupAndTrim(
783 startup_list, referral_list, completion);
786 void Predictor::SaveStateForNextStartupAndTrim() {
787 if (!predictor_enabled_)
788 return;
789 if (!CanPreresolveAndPreconnect())
790 return;
792 base::WaitableEvent completion(true, false);
794 ListPrefUpdate update_startup_list(user_prefs_,
795 prefs::kDnsPrefetchingStartupList);
796 ListPrefUpdate update_referral_list(user_prefs_,
797 prefs::kDnsPrefetchingHostReferralList);
798 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
799 SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
800 update_startup_list.Get(),
801 update_referral_list.Get(),
802 &completion,
803 this);
804 } else {
805 bool posted = BrowserThread::PostTask(
806 BrowserThread::IO,
807 FROM_HERE,
808 base::Bind(
809 &SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread,
810 update_startup_list.Get(),
811 update_referral_list.Get(),
812 &completion,
813 this));
815 // TODO(jar): Synchronous waiting for the IO thread is a potential source
816 // to deadlocks and should be investigated. See http://crbug.com/78451.
817 DCHECK(posted);
818 if (posted) {
819 // http://crbug.com/124954
820 base::ThreadRestrictions::ScopedAllowWait allow_wait;
821 completion.Wait();
826 void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim(
827 base::ListValue* startup_list,
828 base::ListValue* referral_list,
829 base::WaitableEvent* completion) {
830 DCHECK_CURRENTLY_ON(BrowserThread::IO);
831 if (initial_observer_.get())
832 initial_observer_->GetInitialDnsResolutionList(startup_list);
834 // Do at least one trim at shutdown, in case the user wasn't running long
835 // enough to do any regular trimming of referrers.
836 // TODO(lizeb): Should trimming really be done at each shutdown? This could be
837 // a frequent occurrence on Android.
838 TrimReferrersNow();
839 SerializeReferrers(referral_list);
841 completion->Signal();
844 void Predictor::PreconnectUrl(const GURL& url,
845 const GURL& first_party_for_cookies,
846 UrlInfo::ResolutionMotivation motivation,
847 bool allow_credentials,
848 int count) {
849 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
850 BrowserThread::CurrentlyOn(BrowserThread::IO));
852 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
853 PreconnectUrlOnIOThread(url, first_party_for_cookies, motivation,
854 allow_credentials, count);
855 } else {
856 BrowserThread::PostTask(
857 BrowserThread::IO, FROM_HERE,
858 base::Bind(&Predictor::PreconnectUrlOnIOThread, base::Unretained(this),
859 url, first_party_for_cookies, motivation, allow_credentials,
860 count));
864 void Predictor::PreconnectUrlOnIOThread(
865 const GURL& original_url,
866 const GURL& first_party_for_cookies,
867 UrlInfo::ResolutionMotivation motivation,
868 bool allow_credentials,
869 int count) {
870 // Skip the HSTS redirect.
871 GURL url = GetHSTSRedirectOnIOThread(original_url);
873 if (observer_) {
874 observer_->OnPreconnectUrl(
875 url, first_party_for_cookies, motivation, count);
878 PreconnectOnIOThread(url, first_party_for_cookies, motivation, count,
879 url_request_context_getter_.get(), allow_credentials);
882 void Predictor::PredictFrameSubresources(const GURL& url,
883 const GURL& first_party_for_cookies) {
884 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
885 BrowserThread::CurrentlyOn(BrowserThread::IO));
886 if (!predictor_enabled_)
887 return;
888 if (!CanPreresolveAndPreconnect())
889 return;
890 DCHECK_EQ(url.GetWithEmptyPath(), url);
891 // Add one pass through the message loop to allow current navigation to
892 // proceed.
893 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
894 PrepareFrameSubresources(url, first_party_for_cookies);
895 } else {
896 BrowserThread::PostTask(
897 BrowserThread::IO,
898 FROM_HERE,
899 base::Bind(&Predictor::PrepareFrameSubresources,
900 base::Unretained(this), url, first_party_for_cookies));
904 bool Predictor::CanPrefetchAndPrerender() const {
905 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
906 return chrome_browser_net::CanPrefetchAndPrerenderUI(user_prefs_);
907 } else {
908 DCHECK_CURRENTLY_ON(BrowserThread::IO);
909 return chrome_browser_net::CanPrefetchAndPrerenderIO(profile_io_data_);
913 bool Predictor::CanPreresolveAndPreconnect() const {
914 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
915 return chrome_browser_net::CanPreresolveAndPreconnectUI(user_prefs_);
916 } else {
917 DCHECK_CURRENTLY_ON(BrowserThread::IO);
918 return chrome_browser_net::CanPreresolveAndPreconnectIO(profile_io_data_);
922 enum SubresourceValue {
923 PRECONNECTION,
924 PRERESOLUTION,
925 TOO_NEW,
926 SUBRESOURCE_VALUE_MAX
929 void Predictor::PrepareFrameSubresources(const GURL& original_url,
930 const GURL& first_party_for_cookies) {
931 // Apply HSTS redirect early so it is taken into account when looking up
932 // subresources.
933 GURL url = GetHSTSRedirectOnIOThread(original_url);
935 DCHECK_CURRENTLY_ON(BrowserThread::IO);
936 DCHECK_EQ(url.GetWithEmptyPath(), url);
937 Referrers::iterator it = referrers_.find(url);
938 if (referrers_.end() == it) {
939 // Only when we don't know anything about this url, make 2 connections
940 // available. We could do this completely via learning (by prepopulating
941 // the referrer_ list with this expected value), but it would swell the
942 // size of the list with all the "Leaf" nodes in the tree (nodes that don't
943 // load any subresources). If we learn about this resource, we will instead
944 // provide a more carefully estimated preconnection count.
945 if (preconnect_enabled_) {
946 PreconnectUrlOnIOThread(url, first_party_for_cookies,
947 UrlInfo::SELF_REFERAL_MOTIVATED,
948 kAllowCredentialsOnPreconnectByDefault, 2);
950 return;
953 Referrer* referrer = &(it->second);
954 referrer->IncrementUseCount();
955 const UrlInfo::ResolutionMotivation motivation =
956 UrlInfo::LEARNED_REFERAL_MOTIVATED;
957 for (Referrer::iterator future_url = referrer->begin();
958 future_url != referrer->end(); ++future_url) {
959 SubresourceValue evalution(TOO_NEW);
960 double connection_expectation = future_url->second.subresource_use_rate();
961 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation",
962 static_cast<int>(connection_expectation * 100),
963 10, 5000, 50);
964 future_url->second.ReferrerWasObserved();
965 if (preconnect_enabled_ &&
966 connection_expectation > kPreconnectWorthyExpectedValue) {
967 evalution = PRECONNECTION;
968 future_url->second.IncrementPreconnectionCount();
969 int count = static_cast<int>(std::ceil(connection_expectation));
970 if (url.host() == future_url->first.host())
971 ++count;
972 PreconnectUrlOnIOThread(future_url->first, first_party_for_cookies,
973 motivation,
974 kAllowCredentialsOnPreconnectByDefault, count);
975 } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) {
976 evalution = PRERESOLUTION;
977 future_url->second.preresolution_increment();
978 UrlInfo* queued_info = AppendToResolutionQueue(future_url->first,
979 motivation);
980 if (queued_info)
981 queued_info->SetReferringHostname(url);
983 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution,
984 SUBRESOURCE_VALUE_MAX);
988 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url,
989 bool found) {
990 DCHECK_CURRENTLY_ON(BrowserThread::IO);
992 LookupFinished(request, url, found);
993 pending_lookups_.erase(request);
994 delete request;
996 StartSomeQueuedResolutions();
999 void Predictor::LookupFinished(LookupRequest* request, const GURL& url,
1000 bool found) {
1001 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1002 UrlInfo* info = &results_[url];
1003 DCHECK(info->HasUrl(url));
1004 if (info->is_marked_to_delete()) {
1005 results_.erase(url);
1006 } else {
1007 if (found)
1008 info->SetFoundState();
1009 else
1010 info->SetNoSuchNameState();
1014 bool Predictor::WouldLikelyProxyURL(const GURL& url) {
1015 if (!proxy_service_)
1016 return false;
1018 net::ProxyInfo info;
1019 bool synchronous_success = proxy_service_->TryResolveProxySynchronously(
1020 url, net::LOAD_NORMAL, &info, NULL, net::BoundNetLog());
1022 return synchronous_success && !info.is_direct();
1025 UrlInfo* Predictor::AppendToResolutionQueue(
1026 const GURL& url,
1027 UrlInfo::ResolutionMotivation motivation) {
1028 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1029 DCHECK(url.has_host());
1031 if (shutdown_)
1032 return NULL;
1034 UrlInfo* info = &results_[url];
1035 info->SetUrl(url); // Initialize or DCHECK.
1036 // TODO(jar): I need to discard names that have long since expired.
1037 // Currently we only add to the domain map :-/
1039 DCHECK(info->HasUrl(url));
1041 if (!info->NeedsDnsUpdate()) {
1042 info->DLogResultsStats("DNS PrefetchNotUpdated");
1043 return NULL;
1046 bool would_likely_proxy;
1048 // TODO(ttuttle): Remove ScopedTracker below once crbug.com/436671 is fixed.
1049 tracked_objects::ScopedTracker tracking_profile(
1050 FROM_HERE_WITH_EXPLICIT_FUNCTION("436671 WouldLikelyProxyURL()"));
1051 would_likely_proxy = WouldLikelyProxyURL(url);
1054 if (would_likely_proxy) {
1055 info->DLogResultsStats("DNS PrefetchForProxiedRequest");
1056 return NULL;
1059 info->SetQueuedState(motivation);
1060 work_queue_.Push(url, motivation);
1062 StartSomeQueuedResolutions();
1063 return info;
1066 bool Predictor::CongestionControlPerformed(UrlInfo* info) {
1067 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1068 // Note: queue_duration is ONLY valid after we go to assigned state.
1069 if (info->queue_duration() < max_dns_queue_delay_)
1070 return false;
1071 // We need to discard all entries in our queue, as we're keeping them waiting
1072 // too long. By doing this, we'll have a chance to quickly service urgent
1073 // resolutions, and not have a bogged down system.
1074 while (true) {
1075 info->RemoveFromQueue();
1076 if (work_queue_.IsEmpty())
1077 break;
1078 info = &results_[work_queue_.Pop()];
1079 info->SetAssignedState();
1081 return true;
1084 void Predictor::StartSomeQueuedResolutions() {
1085 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1087 while (!work_queue_.IsEmpty() &&
1088 pending_lookups_.size() < max_concurrent_dns_lookups_) {
1089 const GURL url(work_queue_.Pop());
1090 UrlInfo* info = &results_[url];
1091 DCHECK(info->HasUrl(url));
1092 info->SetAssignedState();
1094 if (CongestionControlPerformed(info)) {
1095 DCHECK(work_queue_.IsEmpty());
1096 return;
1099 LookupRequest* request = new LookupRequest(this, host_resolver_, url);
1101 int status;
1103 // TODO(ttuttle): Remove ScopedTracker below once crbug.com/436671 is
1104 // fixed.
1105 tracked_objects::ScopedTracker tracking_profile(
1106 FROM_HERE_WITH_EXPLICIT_FUNCTION("436671 LookupRequest::Start()"));
1107 status = request->Start();
1110 if (status == net::ERR_IO_PENDING) {
1111 // Will complete asynchronously.
1112 pending_lookups_.insert(request);
1113 peak_pending_lookups_ = std::max(peak_pending_lookups_,
1114 pending_lookups_.size());
1115 } else {
1116 // Completed synchronously (was already cached by HostResolver), or else
1117 // there was (equivalently) some network error that prevents us from
1118 // finding the name. Status net::OK means it was "found."
1119 LookupFinished(request, url, status == net::OK);
1120 delete request;
1125 void Predictor::TrimReferrers() {
1126 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1127 if (!urls_being_trimmed_.empty())
1128 return; // There is incremental trimming in progress already.
1130 // Check to see if it is time to trim yet.
1131 base::TimeTicks now = base::TimeTicks::Now();
1132 if (now < next_trim_time_)
1133 return;
1134 next_trim_time_ = now + TimeDelta::FromHours(kDurationBetweenTrimmingsHours);
1136 LoadUrlsForTrimming();
1137 PostIncrementalTrimTask();
1140 void Predictor::LoadUrlsForTrimming() {
1141 DCHECK(urls_being_trimmed_.empty());
1142 for (Referrers::const_iterator it = referrers_.begin();
1143 it != referrers_.end(); ++it)
1144 urls_being_trimmed_.push_back(it->first);
1145 UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size());
1148 void Predictor::PostIncrementalTrimTask() {
1149 if (urls_being_trimmed_.empty())
1150 return;
1151 const TimeDelta kDurationBetweenTrimmingIncrements =
1152 TimeDelta::FromSeconds(kDurationBetweenTrimmingIncrementsSeconds);
1153 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1154 FROM_HERE, base::Bind(&Predictor::IncrementalTrimReferrers,
1155 weak_factory_->GetWeakPtr(), false),
1156 kDurationBetweenTrimmingIncrements);
1159 void Predictor::IncrementalTrimReferrers(bool trim_all_now) {
1160 size_t trim_count = urls_being_trimmed_.size();
1161 if (!trim_all_now)
1162 trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement);
1163 while (trim_count-- != 0) {
1164 Referrers::iterator it = referrers_.find(urls_being_trimmed_.back());
1165 urls_being_trimmed_.pop_back();
1166 if (it == referrers_.end())
1167 continue; // Defensive code: It got trimmed away already.
1168 if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue))
1169 referrers_.erase(it);
1171 PostIncrementalTrimTask();
1174 GURL Predictor::GetHSTSRedirectOnIOThread(const GURL& url) {
1175 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1177 if (!transport_security_state_)
1178 return url;
1179 if (!url.SchemeIs("http"))
1180 return url;
1181 if (!transport_security_state_->ShouldUpgradeToSSL(url.host()))
1182 return url;
1184 url::Replacements<char> replacements;
1185 const char kNewScheme[] = "https";
1186 replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme)));
1187 return url.ReplaceComponents(replacements);
1190 // ---------------------- End IO methods. -------------------------------------
1192 //-----------------------------------------------------------------------------
1194 Predictor::HostNameQueue::HostNameQueue() {
1197 Predictor::HostNameQueue::~HostNameQueue() {
1200 void Predictor::HostNameQueue::Push(const GURL& url,
1201 UrlInfo::ResolutionMotivation motivation) {
1202 switch (motivation) {
1203 case UrlInfo::STATIC_REFERAL_MOTIVATED:
1204 case UrlInfo::LEARNED_REFERAL_MOTIVATED:
1205 case UrlInfo::MOUSE_OVER_MOTIVATED:
1206 rush_queue_.push(url);
1207 break;
1209 default:
1210 background_queue_.push(url);
1211 break;
1215 bool Predictor::HostNameQueue::IsEmpty() const {
1216 return rush_queue_.empty() && background_queue_.empty();
1219 GURL Predictor::HostNameQueue::Pop() {
1220 DCHECK(!IsEmpty());
1221 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_
1222 : &rush_queue_);
1223 GURL url(queue->front());
1224 queue->pop();
1225 return url;
1228 //-----------------------------------------------------------------------------
1229 // Member definitions for InitialObserver class.
1231 Predictor::InitialObserver::InitialObserver() {
1234 Predictor::InitialObserver::~InitialObserver() {
1237 void Predictor::InitialObserver::Append(const GURL& url,
1238 Predictor* predictor) {
1239 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1241 // TODO(rlp): Do we really need the predictor check here?
1242 if (NULL == predictor)
1243 return;
1244 if (kStartupResolutionCount <= first_navigations_.size())
1245 return;
1247 DCHECK(url.SchemeIsHTTPOrHTTPS());
1248 DCHECK_EQ(url, Predictor::CanonicalizeUrl(url));
1249 if (first_navigations_.find(url) == first_navigations_.end())
1250 first_navigations_[url] = base::TimeTicks::Now();
1253 void Predictor::InitialObserver::GetInitialDnsResolutionList(
1254 base::ListValue* startup_list) {
1255 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1256 DCHECK(startup_list);
1257 startup_list->Clear();
1258 DCHECK_EQ(0u, startup_list->GetSize());
1259 startup_list->Append(
1260 new base::FundamentalValue(kPredictorStartupFormatVersion));
1261 for (FirstNavigations::iterator it = first_navigations_.begin();
1262 it != first_navigations_.end();
1263 ++it) {
1264 DCHECK(it->first == Predictor::CanonicalizeUrl(it->first));
1265 startup_list->Append(new base::StringValue(it->first.spec()));
1269 void Predictor::InitialObserver::GetFirstResolutionsHtml(
1270 std::string* output) {
1271 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1273 UrlInfo::UrlInfoTable resolution_list;
1275 for (FirstNavigations::iterator it(first_navigations_.begin());
1276 it != first_navigations_.end();
1277 it++) {
1278 UrlInfo info;
1279 info.SetUrl(it->first);
1280 info.set_time(it->second);
1281 resolution_list.push_back(info);
1284 UrlInfo::GetHtmlTable(resolution_list,
1285 "Future startups will prefetch DNS records for ", false, output);
1288 //-----------------------------------------------------------------------------
1289 // Helper functions
1290 //-----------------------------------------------------------------------------
1292 // static
1293 GURL Predictor::CanonicalizeUrl(const GURL& url) {
1294 if (!url.has_host())
1295 return GURL::EmptyGURL();
1297 std::string scheme;
1298 if (url.has_scheme()) {
1299 scheme = url.scheme();
1300 if (scheme != "http" && scheme != "https")
1301 return GURL::EmptyGURL();
1302 if (url.has_port())
1303 return url.GetWithEmptyPath();
1304 } else {
1305 scheme = "http";
1308 // If we omit a port, it will default to 80 or 443 as appropriate.
1309 std::string colon_plus_port;
1310 if (url.has_port())
1311 colon_plus_port = ":" + url.port();
1313 return GURL(scheme + "://" + url.host() + colon_plus_port);
1316 void SimplePredictor::InitNetworkPredictor(
1317 PrefService* user_prefs,
1318 PrefService* local_state,
1319 IOThread* io_thread,
1320 net::URLRequestContextGetter* getter,
1321 ProfileIOData* profile_io_data) {
1322 // Empty function for unittests.
1325 void SimplePredictor::ShutdownOnUIThread() {
1326 SetShutdown(true);
1329 bool SimplePredictor::CanPrefetchAndPrerender() const { return true; }
1330 bool SimplePredictor::CanPreresolveAndPreconnect() const { return true; }
1332 } // namespace chrome_browser_net