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