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
3// found in the LICENSE file.
4
5#include "chrome/browser/net/predictor_api.h"
6
7#include <map>
8#include <string>
9
10#include "base/lazy_instance.h"
11#include "base/metrics/field_trial.h"
12#include "base/stl_util-inl.h"
13#include "base/string_number_conversions.h"
14#include "base/synchronization/waitable_event.h"
15#include "base/threading/thread.h"
16#include "base/values.h"
17#include "chrome/browser/browser_process.h"
18#include "chrome/browser/io_thread.h"
19#include "chrome/browser/net/preconnect.h"
20#include "chrome/browser/net/referrer.h"
21#include "chrome/browser/net/url_info.h"
22#include "chrome/browser/prefs/browser_prefs.h"
23#include "chrome/browser/prefs/pref_service.h"
24#include "chrome/browser/prefs/scoped_user_pref_update.h"
25#include "chrome/browser/prefs/session_startup_pref.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/ui/browser.h"
28#include "chrome/common/pref_names.h"
29#include "content/browser/browser_thread.h"
30#include "content/common/notification_registrar.h"
31#include "content/common/notification_service.h"
32#include "net/base/host_resolver.h"
33#include "net/base/host_resolver_impl.h"
34
35using base::Time;
36using base::TimeDelta;
37
38namespace chrome_browser_net {
39
40static void DnsPrefetchMotivatedList(const UrlList& urls,
41                                     UrlInfo::ResolutionMotivation motivation);
42
43static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs,
44                                            PrefService* local_state);
45
46// Given that the underlying Chromium resolver defaults to a total maximum of
47// 8 paralell resolutions, we will avoid any chance of starving navigational
48// resolutions by limiting the number of paralell speculative resolutions.
49// TODO(jar): Move this limitation into the resolver.
50// static
51const size_t PredictorInit::kMaxSpeculativeParallelResolves = 3;
52
53// To control our congestion avoidance system, which discards a queue when
54// resolutions are "taking too long," we need an expected resolution time.
55// Common average is in the range of 300-500ms.
56static const int kExpectedResolutionTimeMs = 500;
57
58// To control the congestion avoidance system, we need an estimate of how many
59// speculative requests may arrive at once.  Since we currently only keep 8
60// subresource names for each frame, we'll use that as our basis.  Note that
61// when scanning search results lists, we might actually get 10 at a time, and
62// wikipedia can often supply (during a page scan) upwards of 50.  In those odd
63// cases, we may discard some of the later speculative requests mistakenly
64// assuming that the resolutions took too long.
65static const int kTypicalSpeculativeGroupSize = 8;
66
67// The next constant specifies an amount of queueing delay that is "too large,"
68// and indicative of problems with resolutions (perhaps due to an overloaded
69// router, or such).  When we exceed this delay, congestion avoidance will kick
70// in and all speculations in the queue will be discarded.
71// static
72const int PredictorInit::kMaxSpeculativeResolveQueueDelayMs =
73    (kExpectedResolutionTimeMs * kTypicalSpeculativeGroupSize) /
74    kMaxSpeculativeParallelResolves;
75
76// A version number for prefs that are saved. This should be incremented when
77// we change the format so that we discard old data.
78static const int kPredictorStartupFormatVersion = 1;
79
80// There will only be one instance ever created of the following Observer class.
81// The InitialObserver lives on the IO thread, and monitors navigations made by
82// the network stack.  This is only used to identify startup time resolutions
83// (for re-resolution during our next process startup).
84// TODO(jar): Consider preconnecting at startup, which may be faster than
85// waiting for render process to start and request a connection.
86class InitialObserver {
87 public:
88  // Recording of when we observed each navigation.
89  typedef std::map<GURL, base::TimeTicks> FirstNavigations;
90
91  // Potentially add a new URL to our startup list.
92  void Append(const GURL& url);
93
94  // Get an HTML version of our current planned first_navigations_.
95  void GetFirstResolutionsHtml(std::string* output);
96
97  // Persist the current first_navigations_ for storage in a list.
98  void GetInitialDnsResolutionList(ListValue* startup_list);
99
100  // Discards all initial loading history.
101  void DiscardInitialNavigationHistory() { first_navigations_.clear(); }
102
103 private:
104  // List of the first N URL resolutions observed in this run.
105  FirstNavigations first_navigations_;
106
107  // The number of URLs we'll save for pre-resolving at next startup.
108  static const size_t kStartupResolutionCount = 10;
109};
110
111// TODO(willchan): Look at killing this global.
112static InitialObserver* g_initial_observer = NULL;
113
114//------------------------------------------------------------------------------
115// This section contains all the globally accessable API entry points for the
116// DNS Prefetching feature.
117//------------------------------------------------------------------------------
118
119// Status of speculative DNS resolution and speculative TCP/IP connection
120// feature.
121static bool predictor_enabled = true;
122
123// Cached inverted copy of the off_the_record pref.
124static bool on_the_record_switch = true;
125
126// Enable/disable Dns prefetch activity (either via command line, or via pref).
127void EnablePredictor(bool enable) {
128  // NOTE: this is invoked on the UI thread.
129  predictor_enabled = enable;
130}
131
132void OnTheRecord(bool enable) {
133  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134  if (on_the_record_switch == enable)
135    return;
136  on_the_record_switch = enable;
137  if (on_the_record_switch)
138    g_browser_process->io_thread()->ChangedToOnTheRecord();
139}
140
141void DiscardInitialNavigationHistory() {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143  if (g_initial_observer)
144    g_initial_observer->DiscardInitialNavigationHistory();
145}
146
147void RegisterUserPrefs(PrefService* user_prefs) {
148  user_prefs->RegisterListPref(prefs::kDnsPrefetchingStartupList);
149  user_prefs->RegisterListPref(prefs::kDnsPrefetchingHostReferralList);
150}
151
152// When enabled, we use the following instance to service all requests in the
153// browser process.
154// TODO(willchan): Look at killing this.
155static Predictor* g_predictor = NULL;
156
157// This API is only used in the browser process.
158// It is called from an IPC message originating in the renderer.  It currently
159// includes both Page-Scan, and Link-Hover prefetching.
160// TODO(jar): Separate out link-hover prefetching, and page-scan results.
161void DnsPrefetchList(const NameList& hostnames) {
162  // TODO(jar): Push GURL transport further back into renderer, but this will
163  // require a Webkit change in the observer :-/.
164  UrlList urls;
165  for (NameList::const_iterator it = hostnames.begin();
166       it < hostnames.end();
167       ++it) {
168    urls.push_back(GURL("http://" + *it + ":80"));
169  }
170
171  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
172  DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED);
173}
174
175static void DnsPrefetchMotivatedList(
176    const UrlList& urls,
177    UrlInfo::ResolutionMotivation motivation) {
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
179         BrowserThread::CurrentlyOn(BrowserThread::IO));
180  if (!predictor_enabled || NULL == g_predictor)
181    return;
182
183  if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
184    g_predictor->ResolveList(urls, motivation);
185  } else {
186    BrowserThread::PostTask(
187        BrowserThread::IO,
188        FROM_HERE,
189        NewRunnableMethod(g_predictor,
190                          &Predictor::ResolveList, urls, motivation));
191  }
192}
193
194// This API is used by the autocomplete popup box (where URLs are typed).
195void AnticipateOmniboxUrl(const GURL& url, bool preconnectable) {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
197  if (!predictor_enabled || NULL == g_predictor)
198    return;
199  if (!url.is_valid() || !url.has_host())
200    return;
201
202  g_predictor->AnticipateOmniboxUrl(url, preconnectable);
203}
204
205void PreconnectUrlAndSubresources(const GURL& url) {
206  if (!predictor_enabled || NULL == g_predictor)
207    return;
208  if (!url.is_valid() || !url.has_host())
209    return;
210
211  g_predictor->PreconnectUrlAndSubresources(url);
212}
213
214
215//------------------------------------------------------------------------------
216// This section intermingles prefetch results with actual browser HTTP
217// network activity.  It supports calculating of the benefit of a prefetch, as
218// well as recording what prefetched hostname resolutions might be potentially
219// helpful during the next chrome-startup.
220//------------------------------------------------------------------------------
221
222void PredictFrameSubresources(const GURL& url) {
223  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224  if (!predictor_enabled || NULL == g_predictor)
225    return;
226  g_predictor->PredictFrameSubresources(url);
227}
228
229void LearnAboutInitialNavigation(const GURL& url) {
230  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
231  if (!predictor_enabled || NULL == g_initial_observer )
232    return;
233  g_initial_observer->Append(url);
234}
235
236void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) {
237  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
238  if (!predictor_enabled || NULL == g_predictor)
239    return;
240  g_predictor->LearnFromNavigation(referring_url, target_url);
241}
242
243// The observer class needs to connect starts and finishes of HTTP network
244// resolutions.  We use the following type for that map.
245typedef std::map<int, UrlInfo> ObservedResolutionMap;
246
247//------------------------------------------------------------------------------
248// Member definitions for InitialObserver class.
249
250void InitialObserver::Append(const GURL& url) {
251  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
252
253  if (!on_the_record_switch || NULL == g_predictor)
254    return;
255  if (kStartupResolutionCount <= first_navigations_.size())
256    return;
257
258  if (url.SchemeIs("http") || url.SchemeIs("https")) {
259    const GURL url_without_path(Predictor::CanonicalizeUrl(url));
260    if (first_navigations_.find(url_without_path) == first_navigations_.end())
261      first_navigations_[url_without_path] = base::TimeTicks::Now();
262  }
263}
264
265void InitialObserver::GetInitialDnsResolutionList(ListValue* startup_list) {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
267  DCHECK(startup_list);
268  startup_list->Clear();
269  DCHECK_EQ(0u, startup_list->GetSize());
270  startup_list->Append(new FundamentalValue(kPredictorStartupFormatVersion));
271  for (FirstNavigations::iterator it = first_navigations_.begin();
272       it != first_navigations_.end();
273       ++it) {
274    DCHECK(it->first == Predictor::CanonicalizeUrl(it->first));
275    startup_list->Append(new StringValue(it->first.spec()));
276  }
277}
278
279void InitialObserver::GetFirstResolutionsHtml(std::string* output) {
280  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
281
282  UrlInfo::UrlInfoTable resolution_list;
283  {
284    for (FirstNavigations::iterator it(first_navigations_.begin());
285         it != first_navigations_.end();
286         it++) {
287      UrlInfo info;
288      info.SetUrl(it->first);
289      info.set_time(it->second);
290      resolution_list.push_back(info);
291    }
292  }
293  UrlInfo::GetHtmlTable(resolution_list,
294      "Future startups will prefetch DNS records for ", false, output);
295}
296
297//------------------------------------------------------------------------------
298// Support observer to detect opening and closing of OffTheRecord windows.
299// This object lives on the UI thread.
300
301class OffTheRecordObserver : public NotificationObserver {
302 public:
303  void Register() {
304    // TODO(pkasting): This test should not be necessary.  See crbug.com/12475.
305    if (registrar_.IsEmpty()) {
306      registrar_.Add(this, NotificationType::BROWSER_CLOSED,
307                     NotificationService::AllSources());
308      registrar_.Add(this, NotificationType::BROWSER_OPENED,
309                     NotificationService::AllSources());
310    }
311  }
312
313  void Observe(NotificationType type, const NotificationSource& source,
314               const NotificationDetails& details) {
315    switch (type.value) {
316      case NotificationType::BROWSER_OPENED:
317        if (!Source<Browser>(source)->profile()->IsOffTheRecord())
318          break;
319        ++count_off_the_record_windows_;
320        OnTheRecord(false);
321        break;
322
323      case NotificationType::BROWSER_CLOSED:
324        if (!Source<Browser>(source)->profile()->IsOffTheRecord())
325        break;  // Ignore ordinary windows.
326        DCHECK_LT(0, count_off_the_record_windows_);
327        if (0 >= count_off_the_record_windows_)  // Defensive coding.
328          break;
329        if (--count_off_the_record_windows_)
330          break;  // Still some windows are incognito.
331        OnTheRecord(true);
332        break;
333
334      default:
335        break;
336    }
337  }
338
339 private:
340  friend struct base::DefaultLazyInstanceTraits<OffTheRecordObserver>;
341
342  OffTheRecordObserver() : count_off_the_record_windows_(0) {}
343  ~OffTheRecordObserver() {}
344
345  NotificationRegistrar registrar_;
346  int count_off_the_record_windows_;
347
348  DISALLOW_COPY_AND_ASSIGN(OffTheRecordObserver);
349};
350
351static base::LazyInstance<OffTheRecordObserver> g_off_the_record_observer(
352    base::LINKER_INITIALIZED);
353
354//------------------------------------------------------------------------------
355// This section supports the about:dns page.
356//------------------------------------------------------------------------------
357
358// Provide global support for the about:dns page.
359void PredictorGetHtmlInfo(std::string* output) {
360  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
361
362  output->append("<html><head><title>About DNS</title>"
363                 // We'd like the following no-cache... but it doesn't work.
364                 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
365                 "</head><body>");
366  if (!predictor_enabled  || NULL == g_predictor) {
367    output->append("DNS pre-resolution and TCP pre-connection is disabled.");
368  } else {
369    if (!on_the_record_switch) {
370      output->append("Incognito mode is active in a window.");
371    } else {
372      // List items fetched at startup.
373      if (g_initial_observer)
374        g_initial_observer->GetFirstResolutionsHtml(output);
375      // Show list of subresource predictions and stats.
376      g_predictor->GetHtmlReferrerLists(output);
377      // Show list of prediction results.
378      g_predictor->GetHtmlInfo(output);
379    }
380  }
381  output->append("</body></html>");
382}
383
384void ClearPredictorCache() {
385  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
386  if (!predictor_enabled || NULL == g_predictor)
387    return;
388  g_predictor->DiscardAllResults();
389}
390
391//------------------------------------------------------------------------------
392// This section intializes global DNS prefetch services.
393//------------------------------------------------------------------------------
394
395static void InitNetworkPredictor(TimeDelta max_dns_queue_delay,
396                                 size_t max_parallel_resolves,
397                                 PrefService* user_prefs,
398                                 PrefService* local_state,
399                                 bool preconnect_enabled) {
400  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
401
402  bool prefetching_enabled =
403      user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled);
404
405  // Gather the list of hostnames to prefetch on startup.
406  UrlList urls =
407      GetPredictedUrlListAtStartup(user_prefs, local_state);
408
409  ListValue* referral_list =
410      static_cast<ListValue*>(user_prefs->GetList(
411          prefs::kDnsPrefetchingHostReferralList)->DeepCopy());
412
413  // Remove obsolete preferences from local state if necessary.
414  int current_version =
415      local_state->GetInteger(prefs::kMultipleProfilePrefMigration);
416  if ((current_version & browser::DNS_PREFS) == 0) {
417    local_state->RegisterListPref(prefs::kDnsStartupPrefetchList);
418    local_state->RegisterListPref(prefs::kDnsHostReferralList);
419    local_state->ClearPref(prefs::kDnsStartupPrefetchList);
420    local_state->ClearPref(prefs::kDnsHostReferralList);
421    local_state->SetInteger(prefs::kMultipleProfilePrefMigration,
422        current_version | browser::DNS_PREFS);
423  }
424
425  g_browser_process->io_thread()->InitNetworkPredictor(
426      prefetching_enabled, max_dns_queue_delay, max_parallel_resolves, urls,
427      referral_list, preconnect_enabled);
428}
429
430void FinalizePredictorInitialization(
431    Predictor* global_predictor,
432    const UrlList& startup_urls,
433    ListValue* referral_list) {
434  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
435  g_predictor = global_predictor;
436  g_initial_observer = new InitialObserver();
437
438  // Prefetch these hostnames on startup.
439  DnsPrefetchMotivatedList(startup_urls,
440                           UrlInfo::STARTUP_LIST_MOTIVATED);
441  g_predictor->DeserializeReferrersThenDelete(referral_list);
442}
443
444void FreePredictorResources() {
445  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
446  g_predictor = NULL;  // Owned and released by io_thread.cc.
447  delete g_initial_observer;
448  g_initial_observer = NULL;
449}
450
451//------------------------------------------------------------------------------
452// Functions to handle saving of hostnames from one session to the next, to
453// expedite startup times.
454
455static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread(
456    ListValue* startup_list,
457    ListValue* referral_list,
458    base::WaitableEvent* completion) {
459  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
460
461  if (NULL == g_predictor) {
462    completion->Signal();
463    return;
464  }
465
466  if (g_initial_observer)
467    g_initial_observer->GetInitialDnsResolutionList(startup_list);
468
469  // Do at least one trim at shutdown, in case the user wasn't running long
470  // enough to do any regular trimming of referrers.
471  g_predictor->TrimReferrersNow();
472  g_predictor->SerializeReferrers(referral_list);
473
474  completion->Signal();
475}
476
477void SavePredictorStateForNextStartupAndTrim(PrefService* prefs) {
478  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
479
480  if (!predictor_enabled || g_predictor == NULL)
481    return;
482
483  base::WaitableEvent completion(true, false);
484
485  ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList);
486  ListPrefUpdate update_referral_list(prefs,
487                                      prefs::kDnsPrefetchingHostReferralList);
488  bool posted = BrowserThread::PostTask(
489      BrowserThread::IO,
490      FROM_HERE,
491      NewRunnableFunction(SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread,
492          update_startup_list.Get(),
493          update_referral_list.Get(),
494          &completion));
495
496  // TODO(jar): Synchronous waiting for the IO thread is a potential source
497  // to deadlocks and should be investigated. See http://crbug.com/78451.
498  DCHECK(posted);
499  if (posted)
500    completion.Wait();
501}
502
503static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs,
504                                            PrefService* local_state) {
505  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
506  UrlList urls;
507  // Recall list of URLs we learned about during last session.
508  // This may catch secondary hostnames, pulled in by the homepages.  It will
509  // also catch more of the "primary" home pages, since that was (presumably)
510  // rendered first (and will be rendered first this time too).
511  const ListValue* startup_list =
512      user_prefs->GetList(prefs::kDnsPrefetchingStartupList);
513
514  if (startup_list) {
515    ListValue::const_iterator it = startup_list->begin();
516    int format_version = -1;
517    if (it != startup_list->end() &&
518        (*it)->GetAsInteger(&format_version) &&
519        format_version == kPredictorStartupFormatVersion) {
520      ++it;
521      for (; it != startup_list->end(); ++it) {
522        std::string url_spec;
523        if (!(*it)->GetAsString(&url_spec)) {
524          LOG(DFATAL);
525          break;  // Format incompatibility.
526        }
527        GURL url(url_spec);
528        if (!url.has_host() || !url.has_scheme()) {
529          LOG(DFATAL);
530          break;  // Format incompatibility.
531        }
532
533        urls.push_back(url);
534      }
535    }
536  }
537
538  // Prepare for any static home page(s) the user has in prefs.  The user may
539  // have a LOT of tab's specified, so we may as well try to warm them all.
540  SessionStartupPref tab_start_pref =
541      SessionStartupPref::GetStartupPref(user_prefs);
542  if (SessionStartupPref::URLS == tab_start_pref.type) {
543    for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
544      GURL gurl = tab_start_pref.urls[i];
545      if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty())
546        continue;
547      if (gurl.SchemeIs("http") || gurl.SchemeIs("https"))
548        urls.push_back(gurl.GetWithEmptyPath());
549    }
550  }
551
552  if (urls.empty())
553    urls.push_back(GURL("http://www.google.com:80"));
554
555  return urls;
556}
557
558//------------------------------------------------------------------------------
559// Methods for the helper class that is used to startup and teardown the whole
560// g_predictor system (both DNS pre-resolution and TCP/IP pre-connection).
561
562PredictorInit::PredictorInit(PrefService* user_prefs,
563                             PrefService* local_state,
564                             bool preconnect_enabled) {
565  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566  // Set up a field trial to see what disabling DNS pre-resolution does to
567  // latency of page loads.
568  base::FieldTrial::Probability kDivisor = 1000;
569  // For each option (i.e., non-default), we have a fixed probability.
570  base::FieldTrial::Probability kProbabilityPerGroup = 100;  // 10% probability.
571
572  // After June 30, 2011 builds, it will always be in default group
573  // (default_enabled_prefetch).
574  trial_ = new base::FieldTrial("DnsImpact", kDivisor,
575                                "default_enabled_prefetch", 2011, 6, 30);
576
577  // First option is to disable prefetching completely.
578  int disabled_prefetch = trial_->AppendGroup("disabled_prefetch",
579                                              kProbabilityPerGroup);
580
581  // We're running two experiments at the same time.  The first set of trials
582  // modulates the delay-time until we declare a congestion event (and purge
583  // our queue).  The second modulates the number of concurrent resolutions
584  // we do at any time.  Users are in exactly one trial (or the default) during
585  // any one run, and hence only one experiment at a time.
586  // Experiment 1:
587  // Set congestion detection at 250, 500, or 750ms, rather than the 1 second
588  // default.
589  int max_250ms_prefetch = trial_->AppendGroup("max_250ms_queue_prefetch",
590                                               kProbabilityPerGroup);
591  int max_500ms_prefetch = trial_->AppendGroup("max_500ms_queue_prefetch",
592                                               kProbabilityPerGroup);
593  int max_750ms_prefetch = trial_->AppendGroup("max_750ms_queue_prefetch",
594                                               kProbabilityPerGroup);
595  // Set congestion detection at 2 seconds instead of the 1 second default.
596  int max_2s_prefetch = trial_->AppendGroup("max_2s_queue_prefetch",
597                                            kProbabilityPerGroup);
598  // Experiment 2:
599  // Set max simultaneous resoultions to 2, 4, or 6, and scale the congestion
600  // limit proportionally (so we don't impact average probability of asserting
601  // congesion very much).
602  int max_2_concurrent_prefetch = trial_->AppendGroup(
603      "max_2 concurrent_prefetch", kProbabilityPerGroup);
604  int max_4_concurrent_prefetch = trial_->AppendGroup(
605      "max_4 concurrent_prefetch", kProbabilityPerGroup);
606  int max_6_concurrent_prefetch = trial_->AppendGroup(
607      "max_6 concurrent_prefetch", kProbabilityPerGroup);
608
609  // We will register the incognito observer regardless of whether prefetching
610  // is enabled, as it is also used to clear the host cache.
611  g_off_the_record_observer.Get().Register();
612
613  if (trial_->group() != disabled_prefetch) {
614    // Initialize the DNS prefetch system.
615    size_t max_parallel_resolves = kMaxSpeculativeParallelResolves;
616    int max_queueing_delay_ms = kMaxSpeculativeResolveQueueDelayMs;
617
618    if (trial_->group() == max_2_concurrent_prefetch)
619      max_parallel_resolves = 2;
620    else if (trial_->group() == max_4_concurrent_prefetch)
621      max_parallel_resolves = 4;
622    else if (trial_->group() == max_6_concurrent_prefetch)
623      max_parallel_resolves = 6;
624
625    if (trial_->group() == max_250ms_prefetch) {
626      max_queueing_delay_ms =
627         (250 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
628    } else if (trial_->group() == max_500ms_prefetch) {
629      max_queueing_delay_ms =
630          (500 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
631    } else if (trial_->group() == max_750ms_prefetch) {
632      max_queueing_delay_ms =
633          (750 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
634    } else if (trial_->group() == max_2s_prefetch) {
635      max_queueing_delay_ms =
636          (2000 * kTypicalSpeculativeGroupSize) /  max_parallel_resolves;
637    }
638
639    TimeDelta max_queueing_delay(
640        TimeDelta::FromMilliseconds(max_queueing_delay_ms));
641
642    DCHECK(!g_predictor);
643    InitNetworkPredictor(max_queueing_delay, max_parallel_resolves, user_prefs,
644                         local_state, preconnect_enabled);
645  }
646}
647
648PredictorInit::~PredictorInit() {
649}
650
651}  // namespace chrome_browser_net
652