Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Side by Side Diff: chrome/browser/net/predictor.cc

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

Powered by Google App Engine
This is Rietveld 408576698