OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/net/predictor.h" | 5 #include "chrome/browser/net/predictor.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <set> | 9 #include <set> |
10 #include <sstream> | 10 #include <sstream> |
11 | 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/command_line.h" |
12 #include "base/compiler_specific.h" | 14 #include "base/compiler_specific.h" |
13 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 16 #include "base/stl_util.h" |
14 #include "base/stringprintf.h" | 17 #include "base/stringprintf.h" |
| 18 #include "base/synchronization/waitable_event.h" |
15 #include "base/time.h" | 19 #include "base/time.h" |
16 #include "base/values.h" | 20 #include "base/values.h" |
| 21 #include "chrome/browser/io_thread.h" |
17 #include "chrome/browser/net/preconnect.h" | 22 #include "chrome/browser/net/preconnect.h" |
| 23 #include "chrome/browser/prefs/browser_prefs.h" |
| 24 #include "chrome/browser/prefs/pref_service.h" |
| 25 #include "chrome/browser/prefs/scoped_user_pref_update.h" |
| 26 #include "chrome/browser/prefs/session_startup_pref.h" |
| 27 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/pref_names.h" |
18 #include "content/browser/browser_thread.h" | 29 #include "content/browser/browser_thread.h" |
19 #include "net/base/address_list.h" | 30 #include "net/base/address_list.h" |
20 #include "net/base/completion_callback.h" | 31 #include "net/base/completion_callback.h" |
21 #include "net/base/host_port_pair.h" | 32 #include "net/base/host_port_pair.h" |
22 #include "net/base/host_resolver.h" | 33 #include "net/base/host_resolver.h" |
23 #include "net/base/net_errors.h" | 34 #include "net/base/net_errors.h" |
24 #include "net/base/net_log.h" | 35 #include "net/base/net_log.h" |
25 #include "net/base/single_request_host_resolver.h" | 36 #include "net/base/single_request_host_resolver.h" |
26 | 37 |
27 using base::TimeDelta; | 38 using base::TimeDelta; |
28 | 39 |
29 namespace chrome_browser_net { | 40 namespace chrome_browser_net { |
30 | 41 |
| 42 static void DnsPrefetchMotivatedList(const UrlList& urls, |
| 43 UrlInfo::ResolutionMotivation motivation); |
31 // static | 44 // static |
32 const double Predictor::kPreconnectWorthyExpectedValue = 0.8; | 45 const double Predictor::kPreconnectWorthyExpectedValue = 0.8; |
33 // static | 46 // static |
34 const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.1; | 47 const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.1; |
35 // static | 48 // static |
36 const double Predictor::kDiscardableExpectedValue = 0.05; | 49 const double Predictor::kDiscardableExpectedValue = 0.05; |
37 // The goal is of trimming is to to reduce the importance (number of expected | 50 // The goal is of trimming is to to reduce the importance (number of expected |
38 // subresources needed) by a factor of 2 after about 24 hours of uptime. We will | 51 // subresources needed) by a factor of 2 after about 24 hours of uptime. We will |
39 // trim roughly once-an-hour of uptime. The ratio to use in each trim operation | 52 // trim roughly once-an-hour of uptime. The ratio to use in each trim operation |
40 // is then the 24th root of 0.5. If a user only surfs for 4 hours a day, then | 53 // is then the 24th root of 0.5. If a user only surfs for 4 hours a day, then |
41 // after about 6 days they will have halved all their estimates of subresource | 54 // after about 6 days they will have halved all their estimates of subresource |
42 // connections. Once this falls below kDiscardableExpectedValue the referrer | 55 // connections. Once this falls below kDiscardableExpectedValue the referrer |
43 // will be discarded. | 56 // will be discarded. |
44 // TODO(jar): Measure size of referrer lists in the field. Consider an adaptive | 57 // TODO(jar): Measure size of referrer lists in the field. Consider an adaptive |
45 // system that uses a higher trim ratio when the list is large. | 58 // system that uses a higher trim ratio when the list is large. |
46 // static | 59 // static |
47 const double Predictor::kReferrerTrimRatio = 0.97153; | 60 const double Predictor::kReferrerTrimRatio = 0.97153; |
48 | 61 |
49 // static | 62 // static |
50 const TimeDelta Predictor::kDurationBetweenTrimmings = TimeDelta::FromHours(1); | 63 const TimeDelta Predictor::kDurationBetweenTrimmings = TimeDelta::FromHours(1); |
51 // static | 64 // static |
52 const TimeDelta Predictor::kDurationBetweenTrimmingIncrements = | 65 const TimeDelta Predictor::kDurationBetweenTrimmingIncrements = |
53 TimeDelta::FromSeconds(15); | 66 TimeDelta::FromSeconds(15); |
54 // static | 67 // static |
55 const size_t Predictor::kUrlsTrimmedPerIncrement = 5u; | 68 const size_t Predictor::kUrlsTrimmedPerIncrement = 5u; |
| 69 // static |
| 70 const size_t Predictor::kMaxSpeculativeParallelResolves = 3; |
| 71 // To control our congestion avoidance system, which discards a queue when |
| 72 // resolutions are "taking too long," we need an expected resolution time. |
| 73 // Common average is in the range of 300-500ms. |
| 74 const int kExpectedResolutionTimeMs = 500; |
| 75 // static |
| 76 const int Predictor::kTypicalSpeculativeGroupSize = 8; |
| 77 // static |
| 78 const int Predictor::kMaxSpeculativeResolveQueueDelayMs = |
| 79 (kExpectedResolutionTimeMs * Predictor::kTypicalSpeculativeGroupSize) / |
| 80 Predictor::kMaxSpeculativeParallelResolves; |
| 81 |
| 82 static int g_max_queueing_delay_ms = 0; |
| 83 static size_t g_max_parallel_resolves = 0u; |
| 84 |
| 85 // A version number for prefs that are saved. This should be incremented when |
| 86 // we change the format so that we discard old data. |
| 87 static const int kPredictorStartupFormatVersion = 1; |
56 | 88 |
57 class Predictor::LookupRequest { | 89 class Predictor::LookupRequest { |
58 public: | 90 public: |
59 LookupRequest(Predictor* predictor, | 91 LookupRequest(Predictor* predictor, |
60 net::HostResolver* host_resolver, | 92 net::HostResolver* host_resolver, |
61 const GURL& url) | 93 const GURL& url) |
62 : ALLOW_THIS_IN_INITIALIZER_LIST( | 94 : ALLOW_THIS_IN_INITIALIZER_LIST( |
63 net_callback_(this, &LookupRequest::OnLookupFinished)), | 95 net_callback_(this, &LookupRequest::OnLookupFinished)), |
64 predictor_(predictor), | 96 predictor_(predictor), |
65 url_(url), | 97 url_(url), |
(...skipping 26 matching lines...) Expand all Loading... |
92 | 124 |
93 Predictor* predictor_; // The predictor which started us. | 125 Predictor* predictor_; // The predictor which started us. |
94 | 126 |
95 const GURL url_; // Hostname to resolve. | 127 const GURL url_; // Hostname to resolve. |
96 net::SingleRequestHostResolver resolver_; | 128 net::SingleRequestHostResolver resolver_; |
97 net::AddressList addresses_; | 129 net::AddressList addresses_; |
98 | 130 |
99 DISALLOW_COPY_AND_ASSIGN(LookupRequest); | 131 DISALLOW_COPY_AND_ASSIGN(LookupRequest); |
100 }; | 132 }; |
101 | 133 |
102 Predictor::Predictor(net::HostResolver* host_resolver, | 134 Predictor::Predictor(bool preconnect_enabled) |
103 TimeDelta max_dns_queue_delay, | 135 : initial_observer_(NULL), |
104 size_t max_concurrent, | 136 predictor_enabled_(true), |
105 bool preconnect_enabled) | 137 peak_pending_lookups_(0), |
106 : peak_pending_lookups_(0), | |
107 shutdown_(false), | 138 shutdown_(false), |
108 max_concurrent_dns_lookups_(max_concurrent), | 139 max_concurrent_dns_lookups_(g_max_parallel_resolves), |
109 max_dns_queue_delay_(max_dns_queue_delay), | 140 max_dns_queue_delay_( |
110 host_resolver_(host_resolver), | 141 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), |
| 142 host_resolver_(NULL), |
111 preconnect_enabled_(preconnect_enabled), | 143 preconnect_enabled_(preconnect_enabled), |
112 consecutive_omnibox_preconnect_count_(0), | 144 consecutive_omnibox_preconnect_count_(0), |
113 next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings), | 145 next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings) { |
114 ALLOW_THIS_IN_INITIALIZER_LIST(trim_task_factory_(this)) { | 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
115 } | 147 } |
116 | 148 |
117 Predictor::~Predictor() { | 149 Predictor::~Predictor() { |
| 150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
118 DCHECK(shutdown_); | 151 DCHECK(shutdown_); |
119 } | 152 } |
120 | 153 |
121 void Predictor::Shutdown() { | 154 void Predictor::RegisterUserPrefs(PrefService* user_prefs) { |
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 155 user_prefs->RegisterListPref(prefs::kDnsPrefetchingStartupList, |
123 DCHECK(!shutdown_); | 156 PrefService::UNSYNCABLE_PREF); |
124 shutdown_ = true; | 157 user_prefs->RegisterListPref(prefs::kDnsPrefetchingHostReferralList, |
125 | 158 PrefService::UNSYNCABLE_PREF); |
126 std::set<LookupRequest*>::iterator it; | |
127 for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it) | |
128 delete *it; | |
129 } | 159 } |
130 | 160 |
131 // Overloaded Resolve() to take a vector of names. | 161 // --------------------- Start UI methods. ------------------------------------ |
132 void Predictor::ResolveList(const UrlList& urls, | |
133 UrlInfo::ResolutionMotivation motivation) { | |
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
135 | 162 |
136 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) { | 163 void Predictor::InitNetworkPredictor(PrefService* user_prefs, |
137 AppendToResolutionQueue(*it, motivation); | 164 PrefService* local_state, |
| 165 IOThread* io_thread) { |
| 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 167 |
| 168 predictor_enabled_ = user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled); |
| 169 |
| 170 // Gather the list of hostnames to prefetch on startup. |
| 171 UrlList urls = GetPredictedUrlListAtStartup(user_prefs, local_state); |
| 172 |
| 173 base::ListValue* referral_list = |
| 174 static_cast<base::ListValue*>(user_prefs->GetList( |
| 175 prefs::kDnsPrefetchingHostReferralList)->DeepCopy()); |
| 176 |
| 177 // Remove obsolete preferences from local state if necessary. |
| 178 int current_version = |
| 179 local_state->GetInteger(prefs::kMultipleProfilePrefMigration); |
| 180 if ((current_version & browser::DNS_PREFS) == 0) { |
| 181 local_state->RegisterListPref(prefs::kDnsStartupPrefetchList, |
| 182 PrefService::UNSYNCABLE_PREF); |
| 183 local_state->RegisterListPref(prefs::kDnsHostReferralList, |
| 184 PrefService::UNSYNCABLE_PREF); |
| 185 local_state->ClearPref(prefs::kDnsStartupPrefetchList); |
| 186 local_state->ClearPref(prefs::kDnsHostReferralList); |
| 187 local_state->SetInteger(prefs::kMultipleProfilePrefMigration, |
| 188 current_version | browser::DNS_PREFS); |
138 } | 189 } |
| 190 |
| 191 BrowserThread::PostTask( |
| 192 BrowserThread::IO, |
| 193 FROM_HERE, |
| 194 base::Bind( |
| 195 &Predictor::FinalizeInitializationOnIOThread, |
| 196 base::Unretained(this), |
| 197 urls, referral_list, |
| 198 io_thread)); |
139 } | 199 } |
140 | 200 |
141 // Basic Resolve() takes an invidual name, and adds it | 201 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { |
142 // to the queue. | 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
143 void Predictor::Resolve(const GURL& url, | 203 if (!predictor_enabled_) |
144 UrlInfo::ResolutionMotivation motivation) { | |
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
146 if (!url.has_host()) | |
147 return; | 204 return; |
148 AppendToResolutionQueue(url, motivation); | 205 if (!url.is_valid() || !url.has_host()) |
149 } | 206 return; |
150 | |
151 void Predictor::LearnFromNavigation(const GURL& referring_url, | |
152 const GURL& target_url) { | |
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
154 DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url)); | |
155 DCHECK_NE(referring_url, GURL::EmptyGURL()); | |
156 DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url)); | |
157 DCHECK_NE(target_url, GURL::EmptyGURL()); | |
158 | |
159 referrers_[referring_url].SuggestHost(target_url); | |
160 // Possibly do some referrer trimming. | |
161 TrimReferrers(); | |
162 } | |
163 | |
164 enum SubresourceValue { | |
165 PRECONNECTION, | |
166 PRERESOLUTION, | |
167 TOO_NEW, | |
168 SUBRESOURCE_VALUE_MAX | |
169 }; | |
170 | |
171 void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { | |
172 std::string host = url.HostNoBrackets(); | 207 std::string host = url.HostNoBrackets(); |
173 bool is_new_host_request = (host != last_omnibox_host_); | 208 bool is_new_host_request = (host != last_omnibox_host_); |
174 last_omnibox_host_ = host; | 209 last_omnibox_host_ = host; |
175 | 210 |
176 UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED); | 211 UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED); |
177 base::TimeTicks now = base::TimeTicks::Now(); | 212 base::TimeTicks now = base::TimeTicks::Now(); |
178 | 213 |
179 if (preconnect_enabled()) { | 214 if (preconnect_enabled()) { |
180 if (preconnectable && !is_new_host_request) { | 215 if (preconnectable && !is_new_host_request) { |
181 ++consecutive_omnibox_preconnect_count_; | 216 ++consecutive_omnibox_preconnect_count_; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 const int kMinPreresolveSeconds(10); | 260 const int kMinPreresolveSeconds(10); |
226 if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds()) | 261 if (kMinPreresolveSeconds > (now - last_omnibox_preresolve_).InSeconds()) |
227 return; | 262 return; |
228 } | 263 } |
229 last_omnibox_preresolve_ = now; | 264 last_omnibox_preresolve_ = now; |
230 | 265 |
231 // Perform at least DNS pre-resolution. | 266 // Perform at least DNS pre-resolution. |
232 BrowserThread::PostTask( | 267 BrowserThread::PostTask( |
233 BrowserThread::IO, | 268 BrowserThread::IO, |
234 FROM_HERE, | 269 FROM_HERE, |
235 NewRunnableMethod(this, &Predictor::Resolve, CanonicalizeUrl(url), | 270 base::Bind(&Predictor::Resolve, base::Unretained(this), |
236 motivation)); | 271 CanonicalizeUrl(url), motivation)); |
237 } | 272 } |
238 | 273 |
239 void Predictor::PreconnectUrlAndSubresources(const GURL& url) { | 274 void Predictor::PreconnectUrlAndSubresources(const GURL& url) { |
| 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 276 if (!predictor_enabled_) |
| 277 return; |
| 278 if (!url.is_valid() || !url.has_host()) |
| 279 return; |
240 if (preconnect_enabled()) { | 280 if (preconnect_enabled()) { |
241 std::string host = url.HostNoBrackets(); | 281 std::string host = url.HostNoBrackets(); |
242 UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED); | 282 UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED); |
243 const int kConnectionsNeeded = 1; | 283 const int kConnectionsNeeded = 1; |
244 PreconnectOnUIThread(CanonicalizeUrl(url), motivation, | 284 PreconnectOnUIThread(CanonicalizeUrl(url), motivation, |
245 kConnectionsNeeded); | 285 kConnectionsNeeded); |
246 PredictFrameSubresources(url.GetWithEmptyPath()); | 286 PredictFrameSubresources(url.GetWithEmptyPath()); |
247 } | 287 } |
248 } | 288 } |
249 | 289 |
250 void Predictor::PredictFrameSubresources(const GURL& url) { | 290 UrlList Predictor::GetPredictedUrlListAtStartup( |
251 DCHECK_EQ(url.GetWithEmptyPath(), url); | 291 PrefService* user_prefs, |
252 // Add one pass through the message loop to allow current navigation to | 292 PrefService* local_state) { |
253 // proceed. | 293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 294 UrlList urls; |
| 295 // Recall list of URLs we learned about during last session. |
| 296 // This may catch secondary hostnames, pulled in by the homepages. It will |
| 297 // also catch more of the "primary" home pages, since that was (presumably) |
| 298 // rendered first (and will be rendered first this time too). |
| 299 const ListValue* startup_list = |
| 300 user_prefs->GetList(prefs::kDnsPrefetchingStartupList); |
| 301 |
| 302 if (startup_list) { |
| 303 base::ListValue::const_iterator it = startup_list->begin(); |
| 304 int format_version = -1; |
| 305 if (it != startup_list->end() && |
| 306 (*it)->GetAsInteger(&format_version) && |
| 307 format_version == kPredictorStartupFormatVersion) { |
| 308 ++it; |
| 309 for (; it != startup_list->end(); ++it) { |
| 310 std::string url_spec; |
| 311 if (!(*it)->GetAsString(&url_spec)) { |
| 312 LOG(DFATAL); |
| 313 break; // Format incompatibility. |
| 314 } |
| 315 GURL url(url_spec); |
| 316 if (!url.has_host() || !url.has_scheme()) { |
| 317 LOG(DFATAL); |
| 318 break; // Format incompatibility. |
| 319 } |
| 320 |
| 321 urls.push_back(url); |
| 322 } |
| 323 } |
| 324 } |
| 325 |
| 326 // Prepare for any static home page(s) the user has in prefs. The user may |
| 327 // have a LOT of tab's specified, so we may as well try to warm them all. |
| 328 SessionStartupPref tab_start_pref = |
| 329 SessionStartupPref::GetStartupPref(user_prefs); |
| 330 if (SessionStartupPref::URLS == tab_start_pref.type) { |
| 331 for (size_t i = 0; i < tab_start_pref.urls.size(); i++) { |
| 332 GURL gurl = tab_start_pref.urls[i]; |
| 333 if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty()) |
| 334 continue; |
| 335 if (gurl.SchemeIs("http") || gurl.SchemeIs("https")) |
| 336 urls.push_back(gurl.GetWithEmptyPath()); |
| 337 } |
| 338 } |
| 339 |
| 340 if (urls.empty()) |
| 341 urls.push_back(GURL("http://www.google.com:80")); |
| 342 |
| 343 return urls; |
| 344 } |
| 345 |
| 346 void Predictor::set_max_queueing_delay(int max_queueing_delay_ms) { |
| 347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 348 g_max_queueing_delay_ms = max_queueing_delay_ms; |
| 349 } |
| 350 |
| 351 void Predictor::set_max_parallel_resolves(size_t max_parallel_resolves) { |
| 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 353 g_max_parallel_resolves = max_parallel_resolves; |
| 354 } |
| 355 |
| 356 void Predictor::ShutdownOnUIThread(PrefService* user_prefs) { |
| 357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 358 |
| 359 SaveStateForNextStartupAndTrim(user_prefs); |
| 360 |
254 BrowserThread::PostTask( | 361 BrowserThread::PostTask( |
255 BrowserThread::IO, | 362 BrowserThread::IO, |
256 FROM_HERE, | 363 FROM_HERE, |
257 NewRunnableMethod(this, &Predictor::PrepareFrameSubresources, url)); | 364 base::Bind(&Predictor::Shutdown, base::Unretained(this))); |
258 } | 365 } |
259 | 366 |
260 void Predictor::PrepareFrameSubresources(const GURL& url) { | 367 // ---------------------- End UI methods. ------------------------------------- |
| 368 |
| 369 // --------------------- Start IO methods. ------------------------------------ |
| 370 |
| 371 void Predictor::Shutdown() { |
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
262 DCHECK_EQ(url.GetWithEmptyPath(), url); | 373 DCHECK(!shutdown_); |
263 Referrers::iterator it = referrers_.find(url); | 374 shutdown_ = true; |
264 if (referrers_.end() == it) { | 375 |
265 // Only when we don't know anything about this url, make 2 connections | 376 STLDeleteElements(&pending_lookups_); |
266 // available. We could do this completely via learning (by prepopulating | 377 } |
267 // the referrer_ list with this expected value), but it would swell the | 378 |
268 // size of the list with all the "Leaf" nodes in the tree (nodes that don't | 379 void Predictor::DiscardAllResults() { |
269 // load any subresources). If we learn about this resource, we will instead | 380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
270 // provide a more carefully estimated preconnection count. | 381 // Delete anything listed so far in this session that shows in about:dns. |
271 if (preconnect_enabled_) | 382 referrers_.clear(); |
272 PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2); | 383 |
| 384 |
| 385 // Try to delete anything in our work queue. |
| 386 while (!work_queue_.IsEmpty()) { |
| 387 // Emulate processing cycle as though host was not found. |
| 388 GURL url = work_queue_.Pop(); |
| 389 UrlInfo* info = &results_[url]; |
| 390 DCHECK(info->HasUrl(url)); |
| 391 info->SetAssignedState(); |
| 392 info->SetNoSuchNameState(); |
| 393 } |
| 394 // Now every result_ is either resolved, or is being resolved |
| 395 // (see LookupRequest). |
| 396 |
| 397 // Step through result_, recording names of all hosts that can't be erased. |
| 398 // We can't erase anything being worked on. |
| 399 Results assignees; |
| 400 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { |
| 401 GURL url(it->first); |
| 402 UrlInfo* info = &it->second; |
| 403 DCHECK(info->HasUrl(url)); |
| 404 if (info->is_assigned()) { |
| 405 info->SetPendingDeleteState(); |
| 406 assignees[url] = *info; |
| 407 } |
| 408 } |
| 409 DCHECK_LE(assignees.size(), max_concurrent_dns_lookups_); |
| 410 results_.clear(); |
| 411 // Put back in the names being worked on. |
| 412 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { |
| 413 DCHECK(it->second.is_marked_to_delete()); |
| 414 results_[it->first] = it->second; |
| 415 } |
| 416 } |
| 417 |
| 418 // Overloaded Resolve() to take a vector of names. |
| 419 void Predictor::ResolveList(const UrlList& urls, |
| 420 UrlInfo::ResolutionMotivation motivation) { |
| 421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 422 |
| 423 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) { |
| 424 AppendToResolutionQueue(*it, motivation); |
| 425 } |
| 426 } |
| 427 |
| 428 // Basic Resolve() takes an invidual name, and adds it |
| 429 // to the queue. |
| 430 void Predictor::Resolve(const GURL& url, |
| 431 UrlInfo::ResolutionMotivation motivation) { |
| 432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 433 if (!url.has_host()) |
273 return; | 434 return; |
| 435 AppendToResolutionQueue(url, motivation); |
| 436 } |
| 437 |
| 438 void Predictor::LearnFromNavigation(const GURL& referring_url, |
| 439 const GURL& target_url) { |
| 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 441 if (!predictor_enabled_) |
| 442 return; |
| 443 DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url)); |
| 444 DCHECK_NE(referring_url, GURL::EmptyGURL()); |
| 445 DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url)); |
| 446 DCHECK_NE(target_url, GURL::EmptyGURL()); |
| 447 |
| 448 referrers_[referring_url].SuggestHost(target_url); |
| 449 // Possibly do some referrer trimming. |
| 450 TrimReferrers(); |
| 451 } |
| 452 |
| 453 //----------------------------------------------------------------------------- |
| 454 // This section supports the about:dns page. |
| 455 |
| 456 void Predictor::PredictorGetHtmlInfo(Predictor* predictor, |
| 457 std::string* output) { |
| 458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 459 |
| 460 output->append("<html><head><title>About DNS</title>" |
| 461 // We'd like the following no-cache... but it doesn't work. |
| 462 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" |
| 463 "</head><body>"); |
| 464 if (predictor && predictor->predictor_enabled()) { |
| 465 predictor->GetHtmlInfo(output); |
| 466 } else { |
| 467 output->append("DNS pre-resolution and TCP pre-connection is disabled."); |
274 } | 468 } |
275 | 469 output->append("</body></html>"); |
276 Referrer* referrer = &(it->second); | |
277 referrer->IncrementUseCount(); | |
278 const UrlInfo::ResolutionMotivation motivation = | |
279 UrlInfo::LEARNED_REFERAL_MOTIVATED; | |
280 for (Referrer::iterator future_url = referrer->begin(); | |
281 future_url != referrer->end(); ++future_url) { | |
282 SubresourceValue evalution(TOO_NEW); | |
283 double connection_expectation = future_url->second.subresource_use_rate(); | |
284 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", | |
285 static_cast<int>(connection_expectation * 100), | |
286 10, 5000, 50); | |
287 future_url->second.ReferrerWasObserved(); | |
288 if (preconnect_enabled_ && | |
289 connection_expectation > kPreconnectWorthyExpectedValue) { | |
290 evalution = PRECONNECTION; | |
291 future_url->second.IncrementPreconnectionCount(); | |
292 int count = static_cast<int>(std::ceil(connection_expectation)); | |
293 if (url.host() == future_url->first.host()) | |
294 ++count; | |
295 PreconnectOnIOThread(future_url->first, motivation, count); | |
296 } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) { | |
297 evalution = PRERESOLUTION; | |
298 future_url->second.preresolution_increment(); | |
299 UrlInfo* queued_info = AppendToResolutionQueue(future_url->first, | |
300 motivation); | |
301 if (queued_info) | |
302 queued_info->SetReferringHostname(url); | |
303 } | |
304 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution, | |
305 SUBRESOURCE_VALUE_MAX); | |
306 } | |
307 } | 470 } |
308 | 471 |
309 // Provide sort order so all .com's are together, etc. | 472 // Provide sort order so all .com's are together, etc. |
310 struct RightToLeftStringSorter { | 473 struct RightToLeftStringSorter { |
311 bool operator()(const GURL& left, | 474 bool operator()(const GURL& left, |
312 const GURL& right) const { | 475 const GURL& right) const { |
313 return string_compare(left.host(), right.host()); | 476 return string_compare(left.host(), right.host()); |
314 } | 477 } |
315 | 478 |
316 static bool string_compare(const std::string& left_host, | 479 static bool string_compare(const std::string& left_host, |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 static_cast<int>(future_url->second.preresolution_count()), | 574 static_cast<int>(future_url->second.preresolution_count()), |
412 static_cast<double>(future_url->second.subresource_use_rate()), | 575 static_cast<double>(future_url->second.subresource_use_rate()), |
413 future_url->first.spec().c_str()); | 576 future_url->first.spec().c_str()); |
414 } | 577 } |
415 } | 578 } |
416 output->append("</table>"); | 579 output->append("</table>"); |
417 } | 580 } |
418 | 581 |
419 void Predictor::GetHtmlInfo(std::string* output) { | 582 void Predictor::GetHtmlInfo(std::string* output) { |
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 583 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 584 if (initial_observer_.get()) |
| 585 initial_observer_->GetFirstResolutionsHtml(output); |
| 586 // Show list of subresource predictions and stats. |
| 587 GetHtmlReferrerLists(output); |
| 588 |
421 // Local lists for calling UrlInfo | 589 // Local lists for calling UrlInfo |
422 UrlInfo::UrlInfoTable name_not_found; | 590 UrlInfo::UrlInfoTable name_not_found; |
423 UrlInfo::UrlInfoTable name_preresolved; | 591 UrlInfo::UrlInfoTable name_preresolved; |
424 | 592 |
425 // Get copies of all useful data. | 593 // Get copies of all useful data. |
426 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo; | 594 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo; |
427 SortedUrlInfo snapshot; | 595 SortedUrlInfo snapshot; |
428 // UrlInfo supports value semantics, so we can do a shallow copy. | 596 // UrlInfo supports value semantics, so we can do a shallow copy. |
429 for (Results::iterator it(results_.begin()); it != results_.end(); it++) | 597 for (Results::iterator it(results_.begin()); it != results_.end(); it++) |
430 snapshot[it->first] = it->second; | 598 snapshot[it->first] = it->second; |
(...skipping 15 matching lines...) Expand all Loading... |
446 brief = true; | 614 brief = true; |
447 #endif // NDEBUG | 615 #endif // NDEBUG |
448 | 616 |
449 // Call for display of each table, along with title. | 617 // Call for display of each table, along with title. |
450 UrlInfo::GetHtmlTable(name_preresolved, | 618 UrlInfo::GetHtmlTable(name_preresolved, |
451 "Preresolution DNS records performed for ", brief, output); | 619 "Preresolution DNS records performed for ", brief, output); |
452 UrlInfo::GetHtmlTable(name_not_found, | 620 UrlInfo::GetHtmlTable(name_not_found, |
453 "Preresolving DNS records revealed non-existence for ", brief, output); | 621 "Preresolving DNS records revealed non-existence for ", brief, output); |
454 } | 622 } |
455 | 623 |
456 UrlInfo* Predictor::AppendToResolutionQueue( | 624 void Predictor::TrimReferrersNow() { |
457 const GURL& url, | 625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 626 // Just finish up work if an incremental trim is in progress. |
| 627 if (urls_being_trimmed_.empty()) |
| 628 LoadUrlsForTrimming(); |
| 629 IncrementalTrimReferrers(true); // Do everything now. |
| 630 } |
| 631 |
| 632 void Predictor::SerializeReferrers(base::ListValue* referral_list) { |
| 633 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 634 referral_list->Clear(); |
| 635 referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION)); |
| 636 for (Referrers::const_iterator it = referrers_.begin(); |
| 637 it != referrers_.end(); ++it) { |
| 638 // Serialize the list of subresource names. |
| 639 Value* subresource_list(it->second.Serialize()); |
| 640 |
| 641 // Create a list for each referer. |
| 642 ListValue* motivator(new ListValue); |
| 643 motivator->Append(new StringValue(it->first.spec())); |
| 644 motivator->Append(subresource_list); |
| 645 |
| 646 referral_list->Append(motivator); |
| 647 } |
| 648 } |
| 649 |
| 650 void Predictor::DeserializeReferrers(const base::ListValue& referral_list) { |
| 651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 652 int format_version = -1; |
| 653 if (referral_list.GetSize() > 0 && |
| 654 referral_list.GetInteger(0, &format_version) && |
| 655 format_version == PREDICTOR_REFERRER_VERSION) { |
| 656 for (size_t i = 1; i < referral_list.GetSize(); ++i) { |
| 657 base::ListValue* motivator; |
| 658 if (!referral_list.GetList(i, &motivator)) { |
| 659 NOTREACHED(); |
| 660 return; |
| 661 } |
| 662 std::string motivating_url_spec; |
| 663 if (!motivator->GetString(0, &motivating_url_spec)) { |
| 664 NOTREACHED(); |
| 665 return; |
| 666 } |
| 667 |
| 668 Value* subresource_list; |
| 669 if (!motivator->Get(1, &subresource_list)) { |
| 670 NOTREACHED(); |
| 671 return; |
| 672 } |
| 673 |
| 674 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list); |
| 675 } |
| 676 } |
| 677 } |
| 678 |
| 679 void Predictor::DeserializeReferrersThenDelete( |
| 680 base::ListValue* referral_list) { |
| 681 DeserializeReferrers(*referral_list); |
| 682 delete referral_list; |
| 683 } |
| 684 |
| 685 void Predictor::DiscardInitialNavigationHistory() { |
| 686 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 687 if (initial_observer_.get()) |
| 688 initial_observer_->DiscardInitialNavigationHistory(); |
| 689 } |
| 690 |
| 691 void Predictor::FinalizeInitializationOnIOThread( |
| 692 const UrlList& startup_urls, |
| 693 base::ListValue* referral_list, |
| 694 IOThread* io_thread) { |
| 695 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 696 |
| 697 initial_observer_.reset(new InitialObserver()); |
| 698 host_resolver_ = io_thread->globals()->host_resolver.get(); |
| 699 |
| 700 // ScopedRunnableMethodFactory instances need to be created and destroyed |
| 701 // on the same thread. The predictor lives on the IO thread and will die |
| 702 // from there so now that we're on the IO thread we need to properly |
| 703 // initialize the ScopedrunnableMethodFactory. |
| 704 trim_task_factory_.reset(new ScopedRunnableMethodFactory<Predictor>(this)); |
| 705 |
| 706 // Prefetch these hostnames on startup. |
| 707 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); |
| 708 DeserializeReferrersThenDelete(referral_list); |
| 709 } |
| 710 |
| 711 //----------------------------------------------------------------------------- |
| 712 // This section intermingles prefetch results with actual browser HTTP |
| 713 // network activity. It supports calculating of the benefit of a prefetch, as |
| 714 // well as recording what prefetched hostname resolutions might be potentially |
| 715 // helpful during the next chrome-startup. |
| 716 //----------------------------------------------------------------------------- |
| 717 |
| 718 void Predictor::LearnAboutInitialNavigation(const GURL& url) { |
| 719 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 720 if (!predictor_enabled_ || NULL == initial_observer_.get() ) |
| 721 return; |
| 722 initial_observer_->Append(url, this); |
| 723 } |
| 724 |
| 725 // This API is only used in the browser process. |
| 726 // It is called from an IPC message originating in the renderer. It currently |
| 727 // includes both Page-Scan, and Link-Hover prefetching. |
| 728 // TODO(jar): Separate out link-hover prefetching, and page-scan results. |
| 729 void Predictor::DnsPrefetchList(const NameList& hostnames) { |
| 730 // TODO(jar): Push GURL transport further back into renderer, but this will |
| 731 // require a Webkit change in the observer :-/. |
| 732 UrlList urls; |
| 733 for (NameList::const_iterator it = hostnames.begin(); |
| 734 it < hostnames.end(); |
| 735 ++it) { |
| 736 urls.push_back(GURL("http://" + *it + ":80")); |
| 737 } |
| 738 |
| 739 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 740 DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED); |
| 741 } |
| 742 |
| 743 void Predictor::DnsPrefetchMotivatedList( |
| 744 const UrlList& urls, |
458 UrlInfo::ResolutionMotivation motivation) { | 745 UrlInfo::ResolutionMotivation motivation) { |
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
460 DCHECK(url.has_host()); | 747 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
461 | 748 if (!predictor_enabled_) |
462 if (shutdown_) | 749 return; |
463 return NULL; | 750 |
464 | 751 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
465 UrlInfo* info = &results_[url]; | 752 ResolveList(urls, motivation); |
466 info->SetUrl(url); // Initialize or DCHECK. | 753 } else { |
467 // TODO(jar): I need to discard names that have long since expired. | 754 BrowserThread::PostTask( |
468 // Currently we only add to the domain map :-/ | 755 BrowserThread::IO, |
469 | 756 FROM_HERE, |
470 DCHECK(info->HasUrl(url)); | 757 base::Bind(&Predictor::ResolveList, base::Unretained(this), |
471 | 758 urls, motivation)); |
472 if (!info->NeedsDnsUpdate()) { | 759 } |
473 info->DLogResultsStats("DNS PrefetchNotUpdated"); | 760 } |
474 return NULL; | 761 |
475 } | 762 //----------------------------------------------------------------------------- |
476 | 763 // Functions to handle saving of hostnames from one session to the next, to |
477 info->SetQueuedState(motivation); | 764 // expedite startup times. |
478 work_queue_.Push(url, motivation); | 765 |
479 StartSomeQueuedResolutions(); | 766 static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread( |
480 return info; | 767 base::ListValue* startup_list, |
481 } | 768 base::ListValue* referral_list, |
482 | 769 base::WaitableEvent* completion, |
483 void Predictor::StartSomeQueuedResolutions() { | 770 Predictor* predictor) { |
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 771 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
485 | 772 |
486 while (!work_queue_.IsEmpty() && | 773 if (NULL == predictor) { |
487 pending_lookups_.size() < max_concurrent_dns_lookups_) { | 774 completion->Signal(); |
488 const GURL url(work_queue_.Pop()); | 775 return; |
489 UrlInfo* info = &results_[url]; | 776 } |
490 DCHECK(info->HasUrl(url)); | 777 predictor->SaveDnsPrefetchStateForNextStartupAndTrim( |
491 info->SetAssignedState(); | 778 startup_list, referral_list, completion); |
492 | 779 } |
493 if (CongestionControlPerformed(info)) { | 780 |
494 DCHECK(work_queue_.IsEmpty()); | 781 void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs) { |
495 return; | 782 if (!predictor_enabled_) |
| 783 return; |
| 784 |
| 785 base::WaitableEvent completion(true, false); |
| 786 |
| 787 ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList); |
| 788 ListPrefUpdate update_referral_list(prefs, |
| 789 prefs::kDnsPrefetchingHostReferralList); |
| 790 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 791 SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread( |
| 792 update_startup_list.Get(), |
| 793 update_referral_list.Get(), |
| 794 &completion, |
| 795 this); |
| 796 } else { |
| 797 bool posted = BrowserThread::PostTask( |
| 798 BrowserThread::IO, |
| 799 FROM_HERE, |
| 800 base::Bind( |
| 801 SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread, |
| 802 update_startup_list.Get(), |
| 803 update_referral_list.Get(), |
| 804 &completion, |
| 805 this)); |
| 806 |
| 807 // TODO(jar): Synchronous waiting for the IO thread is a potential source |
| 808 // to deadlocks and should be investigated. See http://crbug.com/78451. |
| 809 DCHECK(posted); |
| 810 if (posted) |
| 811 completion.Wait(); |
| 812 } |
| 813 } |
| 814 |
| 815 void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim( |
| 816 base::ListValue* startup_list, |
| 817 base::ListValue* referral_list, |
| 818 base::WaitableEvent* completion) { |
| 819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 820 if (initial_observer_.get()) |
| 821 initial_observer_->GetInitialDnsResolutionList(startup_list); |
| 822 |
| 823 // Do at least one trim at shutdown, in case the user wasn't running long |
| 824 // enough to do any regular trimming of referrers. |
| 825 TrimReferrersNow(); |
| 826 SerializeReferrers(referral_list); |
| 827 |
| 828 completion->Signal(); |
| 829 } |
| 830 |
| 831 void Predictor::EnablePredictor(bool enable) { |
| 832 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| 833 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 834 |
| 835 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 836 EnablePredictorOnIOThread(enable); |
| 837 } else { |
| 838 BrowserThread::PostTask( |
| 839 BrowserThread::IO, |
| 840 FROM_HERE, |
| 841 base::Bind(&Predictor::EnablePredictorOnIOThread, |
| 842 base::Unretained(this), enable)); |
| 843 } |
| 844 } |
| 845 |
| 846 void Predictor::EnablePredictorOnIOThread(bool enable) { |
| 847 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 848 predictor_enabled_ = enable; |
| 849 } |
| 850 |
| 851 void Predictor::PredictFrameSubresources(const GURL& url) { |
| 852 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| 853 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 854 if (!predictor_enabled_) |
| 855 return; |
| 856 DCHECK_EQ(url.GetWithEmptyPath(), url); |
| 857 // Add one pass through the message loop to allow current navigation to |
| 858 // proceed. |
| 859 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 860 PrepareFrameSubresources(url); |
| 861 } else { |
| 862 BrowserThread::PostTask( |
| 863 BrowserThread::IO, |
| 864 FROM_HERE, |
| 865 base::Bind(&Predictor::PrepareFrameSubresources, |
| 866 base::Unretained(this), url)); |
| 867 } |
| 868 } |
| 869 |
| 870 enum SubresourceValue { |
| 871 PRECONNECTION, |
| 872 PRERESOLUTION, |
| 873 TOO_NEW, |
| 874 SUBRESOURCE_VALUE_MAX |
| 875 }; |
| 876 |
| 877 void Predictor::PrepareFrameSubresources(const GURL& url) { |
| 878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 879 DCHECK_EQ(url.GetWithEmptyPath(), url); |
| 880 Referrers::iterator it = referrers_.find(url); |
| 881 if (referrers_.end() == it) { |
| 882 // Only when we don't know anything about this url, make 2 connections |
| 883 // available. We could do this completely via learning (by prepopulating |
| 884 // the referrer_ list with this expected value), but it would swell the |
| 885 // size of the list with all the "Leaf" nodes in the tree (nodes that don't |
| 886 // load any subresources). If we learn about this resource, we will instead |
| 887 // provide a more carefully estimated preconnection count. |
| 888 if (preconnect_enabled_) |
| 889 PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2); |
| 890 return; |
| 891 } |
| 892 |
| 893 Referrer* referrer = &(it->second); |
| 894 referrer->IncrementUseCount(); |
| 895 const UrlInfo::ResolutionMotivation motivation = |
| 896 UrlInfo::LEARNED_REFERAL_MOTIVATED; |
| 897 for (Referrer::iterator future_url = referrer->begin(); |
| 898 future_url != referrer->end(); ++future_url) { |
| 899 SubresourceValue evalution(TOO_NEW); |
| 900 double connection_expectation = future_url->second.subresource_use_rate(); |
| 901 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", |
| 902 static_cast<int>(connection_expectation * 100), |
| 903 10, 5000, 50); |
| 904 future_url->second.ReferrerWasObserved(); |
| 905 if (preconnect_enabled_ && |
| 906 connection_expectation > kPreconnectWorthyExpectedValue) { |
| 907 evalution = PRECONNECTION; |
| 908 future_url->second.IncrementPreconnectionCount(); |
| 909 int count = static_cast<int>(std::ceil(connection_expectation)); |
| 910 if (url.host() == future_url->first.host()) |
| 911 ++count; |
| 912 PreconnectOnIOThread(future_url->first, motivation, count); |
| 913 } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) { |
| 914 evalution = PRERESOLUTION; |
| 915 future_url->second.preresolution_increment(); |
| 916 UrlInfo* queued_info = AppendToResolutionQueue(future_url->first, |
| 917 motivation); |
| 918 if (queued_info) |
| 919 queued_info->SetReferringHostname(url); |
496 } | 920 } |
497 | 921 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution, |
498 LookupRequest* request = new LookupRequest(this, host_resolver_, url); | 922 SUBRESOURCE_VALUE_MAX); |
499 int status = request->Start(); | 923 } |
500 if (status == net::ERR_IO_PENDING) { | |
501 // Will complete asynchronously. | |
502 pending_lookups_.insert(request); | |
503 peak_pending_lookups_ = std::max(peak_pending_lookups_, | |
504 pending_lookups_.size()); | |
505 } else { | |
506 // Completed synchronously (was already cached by HostResolver), or else | |
507 // there was (equivalently) some network error that prevents us from | |
508 // finding the name. Status net::OK means it was "found." | |
509 LookupFinished(request, url, status == net::OK); | |
510 delete request; | |
511 } | |
512 } | |
513 } | |
514 | |
515 bool Predictor::CongestionControlPerformed(UrlInfo* info) { | |
516 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
517 // Note: queue_duration is ONLY valid after we go to assigned state. | |
518 if (info->queue_duration() < max_dns_queue_delay_) | |
519 return false; | |
520 // We need to discard all entries in our queue, as we're keeping them waiting | |
521 // too long. By doing this, we'll have a chance to quickly service urgent | |
522 // resolutions, and not have a bogged down system. | |
523 while (true) { | |
524 info->RemoveFromQueue(); | |
525 if (work_queue_.IsEmpty()) | |
526 break; | |
527 info = &results_[work_queue_.Pop()]; | |
528 info->SetAssignedState(); | |
529 } | |
530 return true; | |
531 } | 924 } |
532 | 925 |
533 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url, | 926 void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url, |
534 bool found) { | 927 bool found) { |
535 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 928 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
536 | 929 |
537 LookupFinished(request, url, found); | 930 LookupFinished(request, url, found); |
538 pending_lookups_.erase(request); | 931 pending_lookups_.erase(request); |
539 delete request; | 932 delete request; |
540 | 933 |
541 StartSomeQueuedResolutions(); | 934 StartSomeQueuedResolutions(); |
542 } | 935 } |
543 | 936 |
544 void Predictor::LookupFinished(LookupRequest* request, const GURL& url, | 937 void Predictor::LookupFinished(LookupRequest* request, const GURL& url, |
545 bool found) { | 938 bool found) { |
546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 939 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
547 UrlInfo* info = &results_[url]; | 940 UrlInfo* info = &results_[url]; |
548 DCHECK(info->HasUrl(url)); | 941 DCHECK(info->HasUrl(url)); |
549 if (info->is_marked_to_delete()) { | 942 if (info->is_marked_to_delete()) { |
550 results_.erase(url); | 943 results_.erase(url); |
551 } else { | 944 } else { |
552 if (found) | 945 if (found) |
553 info->SetFoundState(); | 946 info->SetFoundState(); |
554 else | 947 else |
555 info->SetNoSuchNameState(); | 948 info->SetNoSuchNameState(); |
556 } | 949 } |
557 } | 950 } |
558 | 951 |
559 void Predictor::DiscardAllResults() { | 952 UrlInfo* Predictor::AppendToResolutionQueue( |
| 953 const GURL& url, |
| 954 UrlInfo::ResolutionMotivation motivation) { |
560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 955 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
561 // Delete anything listed so far in this session that shows in about:dns. | 956 DCHECK(url.has_host()); |
562 referrers_.clear(); | |
563 | 957 |
| 958 if (shutdown_) |
| 959 return NULL; |
564 | 960 |
565 // Try to delete anything in our work queue. | 961 UrlInfo* info = &results_[url]; |
566 while (!work_queue_.IsEmpty()) { | 962 info->SetUrl(url); // Initialize or DCHECK. |
567 // Emulate processing cycle as though host was not found. | 963 // TODO(jar): I need to discard names that have long since expired. |
568 GURL url = work_queue_.Pop(); | 964 // Currently we only add to the domain map :-/ |
| 965 |
| 966 DCHECK(info->HasUrl(url)); |
| 967 |
| 968 if (!info->NeedsDnsUpdate()) { |
| 969 info->DLogResultsStats("DNS PrefetchNotUpdated"); |
| 970 return NULL; |
| 971 } |
| 972 |
| 973 info->SetQueuedState(motivation); |
| 974 work_queue_.Push(url, motivation); |
| 975 StartSomeQueuedResolutions(); |
| 976 return info; |
| 977 } |
| 978 |
| 979 bool Predictor::CongestionControlPerformed(UrlInfo* info) { |
| 980 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 981 // Note: queue_duration is ONLY valid after we go to assigned state. |
| 982 if (info->queue_duration() < max_dns_queue_delay_) |
| 983 return false; |
| 984 // We need to discard all entries in our queue, as we're keeping them waiting |
| 985 // too long. By doing this, we'll have a chance to quickly service urgent |
| 986 // resolutions, and not have a bogged down system. |
| 987 while (true) { |
| 988 info->RemoveFromQueue(); |
| 989 if (work_queue_.IsEmpty()) |
| 990 break; |
| 991 info = &results_[work_queue_.Pop()]; |
| 992 info->SetAssignedState(); |
| 993 } |
| 994 return true; |
| 995 } |
| 996 |
| 997 void Predictor::StartSomeQueuedResolutions() { |
| 998 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 999 |
| 1000 while (!work_queue_.IsEmpty() && |
| 1001 pending_lookups_.size() < max_concurrent_dns_lookups_) { |
| 1002 const GURL url(work_queue_.Pop()); |
569 UrlInfo* info = &results_[url]; | 1003 UrlInfo* info = &results_[url]; |
570 DCHECK(info->HasUrl(url)); | 1004 DCHECK(info->HasUrl(url)); |
571 info->SetAssignedState(); | 1005 info->SetAssignedState(); |
572 info->SetNoSuchNameState(); | |
573 } | |
574 // Now every result_ is either resolved, or is being resolved | |
575 // (see LookupRequest). | |
576 | 1006 |
577 // Step through result_, recording names of all hosts that can't be erased. | 1007 if (CongestionControlPerformed(info)) { |
578 // We can't erase anything being worked on. | 1008 DCHECK(work_queue_.IsEmpty()); |
579 Results assignees; | 1009 return; |
580 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { | |
581 GURL url(it->first); | |
582 UrlInfo* info = &it->second; | |
583 DCHECK(info->HasUrl(url)); | |
584 if (info->is_assigned()) { | |
585 info->SetPendingDeleteState(); | |
586 assignees[url] = *info; | |
587 } | 1010 } |
588 } | |
589 DCHECK(assignees.size() <= max_concurrent_dns_lookups_); | |
590 results_.clear(); | |
591 // Put back in the names being worked on. | |
592 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { | |
593 DCHECK(it->second.is_marked_to_delete()); | |
594 results_[it->first] = it->second; | |
595 } | |
596 } | |
597 | 1011 |
598 void Predictor::TrimReferrersNow() { | 1012 LookupRequest* request = new LookupRequest(this, host_resolver_, url); |
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1013 int status = request->Start(); |
600 // Just finish up work if an incremental trim is in progress. | 1014 if (status == net::ERR_IO_PENDING) { |
601 if (urls_being_trimmed_.empty()) | 1015 // Will complete asynchronously. |
602 LoadUrlsForTrimming(); | 1016 pending_lookups_.insert(request); |
603 IncrementalTrimReferrers(true); // Do everything now. | 1017 peak_pending_lookups_ = std::max(peak_pending_lookups_, |
604 } | 1018 pending_lookups_.size()); |
605 | 1019 } else { |
606 void Predictor::SerializeReferrers(ListValue* referral_list) { | 1020 // Completed synchronously (was already cached by HostResolver), or else |
607 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1021 // there was (equivalently) some network error that prevents us from |
608 referral_list->Clear(); | 1022 // finding the name. Status net::OK means it was "found." |
609 referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION)); | 1023 LookupFinished(request, url, status == net::OK); |
610 for (Referrers::const_iterator it = referrers_.begin(); | 1024 delete request; |
611 it != referrers_.end(); ++it) { | |
612 // Serialize the list of subresource names. | |
613 Value* subresource_list(it->second.Serialize()); | |
614 | |
615 // Create a list for each referer. | |
616 ListValue* motivator(new ListValue); | |
617 motivator->Append(new StringValue(it->first.spec())); | |
618 motivator->Append(subresource_list); | |
619 | |
620 referral_list->Append(motivator); | |
621 } | |
622 } | |
623 | |
624 void Predictor::DeserializeReferrers(const ListValue& referral_list) { | |
625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
626 int format_version = -1; | |
627 if (referral_list.GetSize() > 0 && | |
628 referral_list.GetInteger(0, &format_version) && | |
629 format_version == PREDICTOR_REFERRER_VERSION) { | |
630 for (size_t i = 1; i < referral_list.GetSize(); ++i) { | |
631 ListValue* motivator; | |
632 if (!referral_list.GetList(i, &motivator)) { | |
633 NOTREACHED(); | |
634 return; | |
635 } | |
636 std::string motivating_url_spec; | |
637 if (!motivator->GetString(0, &motivating_url_spec)) { | |
638 NOTREACHED(); | |
639 return; | |
640 } | |
641 | |
642 Value* subresource_list; | |
643 if (!motivator->Get(1, &subresource_list)) { | |
644 NOTREACHED(); | |
645 return; | |
646 } | |
647 | |
648 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list); | |
649 } | 1025 } |
650 } | 1026 } |
651 } | 1027 } |
652 | 1028 |
653 void Predictor::TrimReferrers() { | 1029 void Predictor::TrimReferrers() { |
654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1030 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
655 if (!urls_being_trimmed_.empty()) | 1031 if (!urls_being_trimmed_.empty()) |
656 return; // There is incremental trimming in progress already. | 1032 return; // There is incremental trimming in progress already. |
657 | 1033 |
658 // Check to see if it is time to trim yet. | 1034 // Check to see if it is time to trim yet. |
(...skipping 12 matching lines...) Expand all Loading... |
671 it != referrers_.end(); ++it) | 1047 it != referrers_.end(); ++it) |
672 urls_being_trimmed_.push_back(it->first); | 1048 urls_being_trimmed_.push_back(it->first); |
673 UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size()); | 1049 UMA_HISTOGRAM_COUNTS("Net.PredictionTrimSize", urls_being_trimmed_.size()); |
674 } | 1050 } |
675 | 1051 |
676 void Predictor::PostIncrementalTrimTask() { | 1052 void Predictor::PostIncrementalTrimTask() { |
677 if (urls_being_trimmed_.empty()) | 1053 if (urls_being_trimmed_.empty()) |
678 return; | 1054 return; |
679 MessageLoop::current()->PostDelayedTask( | 1055 MessageLoop::current()->PostDelayedTask( |
680 FROM_HERE, | 1056 FROM_HERE, |
681 trim_task_factory_.NewRunnableMethod(&Predictor::IncrementalTrimReferrers, | 1057 trim_task_factory_->NewRunnableMethod( |
682 false), | 1058 &Predictor::IncrementalTrimReferrers, false), |
683 kDurationBetweenTrimmingIncrements.InMilliseconds()); | 1059 kDurationBetweenTrimmingIncrements.InMilliseconds()); |
684 } | 1060 } |
685 | 1061 |
686 void Predictor::IncrementalTrimReferrers(bool trim_all_now) { | 1062 void Predictor::IncrementalTrimReferrers(bool trim_all_now) { |
687 size_t trim_count = urls_being_trimmed_.size(); | 1063 size_t trim_count = urls_being_trimmed_.size(); |
688 if (!trim_all_now) | 1064 if (!trim_all_now) |
689 trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement); | 1065 trim_count = std::min(trim_count, kUrlsTrimmedPerIncrement); |
690 while (trim_count-- != 0) { | 1066 while (trim_count-- != 0) { |
691 Referrers::iterator it = referrers_.find(urls_being_trimmed_.back()); | 1067 Referrers::iterator it = referrers_.find(urls_being_trimmed_.back()); |
692 urls_being_trimmed_.pop_back(); | 1068 urls_being_trimmed_.pop_back(); |
693 if (it == referrers_.end()) | 1069 if (it == referrers_.end()) |
694 continue; // Defensive code: It got trimmed away already. | 1070 continue; // Defensive code: It got trimmed away already. |
695 if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue)) | 1071 if (!it->second.Trim(kReferrerTrimRatio, kDiscardableExpectedValue)) |
696 referrers_.erase(it); | 1072 referrers_.erase(it); |
697 } | 1073 } |
698 PostIncrementalTrimTask(); | 1074 PostIncrementalTrimTask(); |
699 } | 1075 } |
700 | 1076 |
701 //------------------------------------------------------------------------------ | 1077 // ---------------------- End IO methods. ------------------------------------- |
| 1078 |
| 1079 //----------------------------------------------------------------------------- |
702 | 1080 |
703 Predictor::HostNameQueue::HostNameQueue() { | 1081 Predictor::HostNameQueue::HostNameQueue() { |
704 } | 1082 } |
705 | 1083 |
706 Predictor::HostNameQueue::~HostNameQueue() { | 1084 Predictor::HostNameQueue::~HostNameQueue() { |
707 } | 1085 } |
708 | 1086 |
709 void Predictor::HostNameQueue::Push(const GURL& url, | 1087 void Predictor::HostNameQueue::Push(const GURL& url, |
710 UrlInfo::ResolutionMotivation motivation) { | 1088 UrlInfo::ResolutionMotivation motivation) { |
711 switch (motivation) { | 1089 switch (motivation) { |
(...skipping 15 matching lines...) Expand all Loading... |
727 | 1105 |
728 GURL Predictor::HostNameQueue::Pop() { | 1106 GURL Predictor::HostNameQueue::Pop() { |
729 DCHECK(!IsEmpty()); | 1107 DCHECK(!IsEmpty()); |
730 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_ | 1108 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_ |
731 : &rush_queue_); | 1109 : &rush_queue_); |
732 GURL url(queue->front()); | 1110 GURL url(queue->front()); |
733 queue->pop(); | 1111 queue->pop(); |
734 return url; | 1112 return url; |
735 } | 1113 } |
736 | 1114 |
737 void Predictor::DeserializeReferrersThenDelete(ListValue* referral_list) { | 1115 //----------------------------------------------------------------------------- |
738 DeserializeReferrers(*referral_list); | 1116 // Member definitions for InitialObserver class. |
739 delete referral_list; | 1117 |
| 1118 void Predictor::InitialObserver::Append(const GURL& url, |
| 1119 Predictor* predictor) { |
| 1120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1121 |
| 1122 // TODO(rlp): Do we really need the predictor check here? |
| 1123 if (NULL == predictor) |
| 1124 return; |
| 1125 if (kStartupResolutionCount <= first_navigations_.size()) |
| 1126 return; |
| 1127 |
| 1128 DCHECK(url.SchemeIs("http") || url.SchemeIs("https")); |
| 1129 DCHECK_EQ(url, Predictor::CanonicalizeUrl(url)); |
| 1130 if (first_navigations_.find(url) == first_navigations_.end()) |
| 1131 first_navigations_[url] = base::TimeTicks::Now(); |
740 } | 1132 } |
741 | 1133 |
| 1134 void Predictor::InitialObserver::GetInitialDnsResolutionList( |
| 1135 base::ListValue* startup_list) { |
| 1136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1137 DCHECK(startup_list); |
| 1138 startup_list->Clear(); |
| 1139 DCHECK_EQ(0u, startup_list->GetSize()); |
| 1140 startup_list->Append( |
| 1141 new base::FundamentalValue(kPredictorStartupFormatVersion)); |
| 1142 for (FirstNavigations::iterator it = first_navigations_.begin(); |
| 1143 it != first_navigations_.end(); |
| 1144 ++it) { |
| 1145 DCHECK(it->first == Predictor::CanonicalizeUrl(it->first)); |
| 1146 startup_list->Append(new StringValue(it->first.spec())); |
| 1147 } |
| 1148 } |
742 | 1149 |
743 //------------------------------------------------------------------------------ | 1150 void Predictor::InitialObserver::GetFirstResolutionsHtml( |
| 1151 std::string* output) { |
| 1152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1153 |
| 1154 UrlInfo::UrlInfoTable resolution_list; |
| 1155 { |
| 1156 for (FirstNavigations::iterator it(first_navigations_.begin()); |
| 1157 it != first_navigations_.end(); |
| 1158 it++) { |
| 1159 UrlInfo info; |
| 1160 info.SetUrl(it->first); |
| 1161 info.set_time(it->second); |
| 1162 resolution_list.push_back(info); |
| 1163 } |
| 1164 } |
| 1165 UrlInfo::GetHtmlTable(resolution_list, |
| 1166 "Future startups will prefetch DNS records for ", false, output); |
| 1167 } |
| 1168 |
| 1169 //----------------------------------------------------------------------------- |
744 // Helper functions | 1170 // Helper functions |
745 //------------------------------------------------------------------------------ | 1171 //----------------------------------------------------------------------------- |
746 | 1172 |
747 // static | 1173 // static |
748 GURL Predictor::CanonicalizeUrl(const GURL& url) { | 1174 GURL Predictor::CanonicalizeUrl(const GURL& url) { |
749 if (!url.has_host()) | 1175 if (!url.has_host()) |
750 return GURL::EmptyGURL(); | 1176 return GURL::EmptyGURL(); |
751 | 1177 |
752 std::string scheme; | 1178 std::string scheme; |
753 if (url.has_scheme()) { | 1179 if (url.has_scheme()) { |
754 scheme = url.scheme(); | 1180 scheme = url.scheme(); |
755 if (scheme != "http" && scheme != "https") | 1181 if (scheme != "http" && scheme != "https") |
756 return GURL::EmptyGURL(); | 1182 return GURL::EmptyGURL(); |
757 if (url.has_port()) | 1183 if (url.has_port()) |
758 return url.GetWithEmptyPath(); | 1184 return url.GetWithEmptyPath(); |
759 } else { | 1185 } else { |
760 scheme = "http"; | 1186 scheme = "http"; |
761 } | 1187 } |
762 | 1188 |
763 // If we omit a port, it will default to 80 or 443 as appropriate. | 1189 // If we omit a port, it will default to 80 or 443 as appropriate. |
764 std::string colon_plus_port; | 1190 std::string colon_plus_port; |
765 if (url.has_port()) | 1191 if (url.has_port()) |
766 colon_plus_port = ":" + url.port(); | 1192 colon_plus_port = ":" + url.port(); |
767 | 1193 |
768 return GURL(scheme + "://" + url.host() + colon_plus_port); | 1194 return GURL(scheme + "://" + url.host() + colon_plus_port); |
769 } | 1195 } |
770 | 1196 |
771 | |
772 } // namespace chrome_browser_net | 1197 } // namespace chrome_browser_net |
OLD | NEW |