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