diff options
Diffstat (limited to 'chrome')
30 files changed, 1251 insertions, 628 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc index b4950e8..8c4958e 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.cc +++ b/chrome/browser/autocomplete/autocomplete_edit.cc @@ -22,7 +22,7 @@ #include "chrome/browser/extensions/extension_omnibox_api.h" #include "chrome/browser/google/google_url_tracker.h" #include "chrome/browser/instant/instant_controller.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" @@ -1035,8 +1035,11 @@ void AutocompleteEditModel::DoPreconnect(const AutocompleteMatch& match) { // Warm up DNS Prefetch cache, or preconnect to a search service. UMA_HISTOGRAM_ENUMERATION("Autocomplete.MatchType", match.type, AutocompleteMatch::NUM_TYPES); - chrome_browser_net::AnticipateOmniboxUrl(match.destination_url, - NetworkActionPredictor::IsPreconnectable(match)); + if (profile_->GetNetworkPredictor()) { + profile_->GetNetworkPredictor()->AnticipateOmniboxUrl( + match.destination_url, + NetworkActionPredictor::IsPreconnectable(match)); + } // We could prefetch the alternate nav URL, if any, but because there // can be many of these as a user types an initial series of characters, // the OS DNS cache could suffer eviction problems for minimal gain. diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc index 16bdb70..7d45bb0 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc @@ -13,7 +13,6 @@ #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/infobars/infobar_tab_helper.h" -#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/translate/translate_infobar_delegate.h" #include "chrome/browser/translate/translate_manager.h" diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index aeb75e5..1574cf5 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -30,7 +30,7 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/memory_details.h" #include "chrome/browser/metrics/histogram_synchronizer.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/plugin_prefs.h" #include "chrome/browser/profiles/profile.h" @@ -726,18 +726,20 @@ class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> { // Calls FinishOnUIThread() on completion. void StartOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + chrome_browser_net::Predictor* predictor = + source_->profile()->GetNetworkPredictor(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread)); + NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread, predictor)); } - void StartOnIOThread() { + void StartOnIOThread(chrome_browser_net::Predictor* predictor) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); std::string data; AppendHeader(&data, 0, "About DNS"); AppendBody(&data); - chrome_browser_net::PredictorGetHtmlInfo(&data); + chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data); AppendFooter(&data); BrowserThread::PostTask( diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index a66a818..e2a5544 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -37,7 +37,6 @@ #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/metrics/thread_watcher.h" #include "chrome/browser/net/chrome_net_log.h" -#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" #include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/policy/browser_policy_connector.h" diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc index 92683fe..c18d22f 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -38,7 +38,6 @@ #include "content/browser/renderer_host/render_process_host.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/renderer_host/render_widget_host.h" -#include "net/predictor_api.h" #include "ui/base/resource/resource_bundle.h" #if defined(OS_WIN) @@ -140,10 +139,6 @@ void Shutdown() { // time to get here. If you have something that *must* happen on end session, // consider putting it in BrowserProcessImpl::EndSession. PrefService* prefs = g_browser_process->local_state(); - ProfileManager* profile_manager = g_browser_process->profile_manager(); - PrefService* user_prefs = profile_manager->GetDefaultProfile()->GetPrefs(); - - chrome_browser_net::SavePredictorStateForNextStartupAndTrim(user_prefs); MetricsService* metrics = g_browser_process->metrics_service(); if (metrics) diff --git a/chrome/browser/browsing_data_remover.cc b/chrome/browser/browsing_data_remover.cc index b759d1d..92c4d9d 100644 --- a/chrome/browser/browsing_data_remover.cc +++ b/chrome/browser/browsing_data_remover.cc @@ -20,6 +20,7 @@ #include "chrome/browser/io_thread.h" #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/password_manager/password_store.h" #include "chrome/browser/plugin_data_remover.h" #include "chrome/browser/prefs/pref_member.h" @@ -388,7 +389,13 @@ void BrowsingDataRemover::ClearNetworkingHistory(IOThread* io_thread) { // This function should be called on the IO thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - io_thread->ClearNetworkingHistory(); + io_thread->ClearHostCache(); + + chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor(); + if (predictor) { + predictor->DiscardInitialNavigationHistory(); + predictor->DiscardAllResults(); + } // Notify the UI thread that we are done. BrowserThread::PostTask( diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index e503636..52c860d 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -51,7 +51,7 @@ #include "chrome/browser/net/chrome_dns_cert_provenance_checker.h" #include "chrome/browser/net/chrome_dns_cert_provenance_checker_factory.h" #include "chrome/browser/net/chrome_net_log.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" #include "chrome/browser/plugin_prefs.h" #include "chrome/browser/policy/browser_policy_connector.h" @@ -974,6 +974,88 @@ void ChromeBrowserMainParts::ConnectBackupJobsFieldTrial() { } } +void ChromeBrowserMainParts::PredictorFieldTrial() { + const base::FieldTrial::Probability kDivisor = 1000; + // For each option (i.e., non-default), we have a fixed probability. + // 0.1% probability. + const base::FieldTrial::Probability kProbabilityPerGroup = 1; + + // After June 30, 2011 builds, it will always be in default group + // (default_enabled_prefetch). + scoped_refptr<base::FieldTrial> trial( + new base::FieldTrial("DnsImpact", kDivisor, + "default_enabled_prefetch", 2011, 10, 30)); + + // First option is to disable prefetching completely. + int disabled_prefetch = trial->AppendGroup("disabled_prefetch", + kProbabilityPerGroup); + + // We're running two experiments at the same time. The first set of trials + // modulates the delay-time until we declare a congestion event (and purge + // our queue). The second modulates the number of concurrent resolutions + // we do at any time. Users are in exactly one trial (or the default) during + // any one run, and hence only one experiment at a time. + // Experiment 1: + // Set congestion detection at 250, 500, or 750ms, rather than the 1 second + // default. + int max_250ms_prefetch = trial->AppendGroup("max_250ms_queue_prefetch", + kProbabilityPerGroup); + int max_500ms_prefetch = trial->AppendGroup("max_500ms_queue_prefetch", + kProbabilityPerGroup); + int max_750ms_prefetch = trial->AppendGroup("max_750ms_queue_prefetch", + kProbabilityPerGroup); + // Set congestion detection at 2 seconds instead of the 1 second default. + int max_2s_prefetch = trial->AppendGroup("max_2s_queue_prefetch", + kProbabilityPerGroup); + // Experiment 2: + // Set max simultaneous resoultions to 2, 4, or 6, and scale the congestion + // limit proportionally (so we don't impact average probability of asserting + // congesion very much). + int max_2_concurrent_prefetch = trial->AppendGroup( + "max_2 concurrent_prefetch", kProbabilityPerGroup); + int max_4_concurrent_prefetch = trial->AppendGroup( + "max_4 concurrent_prefetch", kProbabilityPerGroup); + int max_6_concurrent_prefetch = trial->AppendGroup( + "max_6 concurrent_prefetch", kProbabilityPerGroup); + + if (trial->group() != disabled_prefetch) { + // Initialize the DNS prefetch system. + size_t max_parallel_resolves = + chrome_browser_net::Predictor::kMaxSpeculativeParallelResolves; + int max_queueing_delay_ms = + chrome_browser_net::Predictor::kMaxSpeculativeResolveQueueDelayMs; + + if (trial->group() == max_2_concurrent_prefetch) + max_parallel_resolves = 2; + else if (trial->group() == max_4_concurrent_prefetch) + max_parallel_resolves = 4; + else if (trial->group() == max_6_concurrent_prefetch) + max_parallel_resolves = 6; + chrome_browser_net::Predictor::set_max_parallel_resolves( + max_parallel_resolves); + + if (trial->group() == max_250ms_prefetch) { + max_queueing_delay_ms = + (250 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) / + max_parallel_resolves; + } else if (trial->group() == max_500ms_prefetch) { + max_queueing_delay_ms = + (500 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) / + max_parallel_resolves; + } else if (trial->group() == max_750ms_prefetch) { + max_queueing_delay_ms = + (750 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) / + max_parallel_resolves; + } else if (trial->group() == max_2s_prefetch) { + max_queueing_delay_ms = + (2000 * chrome_browser_net::Predictor::kTypicalSpeculativeGroupSize) / + max_parallel_resolves; + } + chrome_browser_net::Predictor::set_max_queueing_delay( + max_queueing_delay_ms); + } +} + // Test the impact on subsequent Google searches of getting suggestions from // www.google.TLD instead of clients1.google.TLD. void ChromeBrowserMainParts::SuggestPrefixFieldTrial() { @@ -1045,6 +1127,7 @@ void ChromeBrowserMainParts::SetupFieldTrials(bool metrics_recording_enabled, ConnectBackupJobsFieldTrial(); SuggestPrefixFieldTrial(); WarmConnectionFieldTrial(); + PredictorFieldTrial(); } // ----------------------------------------------------------------------------- @@ -1559,20 +1642,6 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunInternal() { RegisterApplicationRestart(parsed_command_line()); #endif // OS_WIN - // Initialize and maintain network predictor module, which handles DNS - // pre-resolution, as well as TCP/IP connection pre-warming. - // This also registers an observer to discard data when closing incognito - // mode. - bool preconnect_enabled = true; // Default status (easy to change!). - if (parsed_command_line().HasSwitch(switches::kDisablePreconnect)) - preconnect_enabled = false; - else if (parsed_command_line().HasSwitch(switches::kEnablePreconnect)) - preconnect_enabled = true; - chrome_browser_net::PredictorInit dns_prefetch( - user_prefs, - local_state, - preconnect_enabled); - #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) // Init the RLZ library. This just binds the dll and schedules a task on the // file thread to be run sometime later. If this is the first run we record diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h index 503de23..74ed9ec 100644 --- a/chrome/browser/chrome_browser_main.h +++ b/chrome/browser/chrome_browser_main.h @@ -75,6 +75,10 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { // A/B test for using a different host prefix in Google search suggest. void SuggestPrefixFieldTrial(); + // Field trial to see what disabling DNS pre-resolution does to + // latency of page loads. + void PredictorFieldTrial(); + // Methods for |SetupMetricsAndFieldTrials()| -------------------------------- static MetricsService* InitializeMetrics( diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 41b84cc..ce7a52c 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -153,7 +153,13 @@ TabContentsView* ChromeContentBrowserClient::CreateTabContentsView( void ChromeContentBrowserClient::RenderViewHostCreated( RenderViewHost* render_view_host) { - new ChromeRenderViewHostObserver(render_view_host); + + SiteInstance* site_instance = render_view_host->site_instance(); + Profile* profile = Profile::FromBrowserContext( + site_instance->browsing_instance()->browser_context()); + + new ChromeRenderViewHostObserver(render_view_host, + profile->GetNetworkPredictor()); new ExtensionMessageHandler(render_view_host); } diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index b018554..38474c6 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -24,7 +24,6 @@ #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/connect_interceptor.h" #include "chrome/browser/net/passive_log_collector.h" -#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/proxy_service_factory.h" #include "chrome/browser/prefs/pref_service.h" @@ -344,8 +343,6 @@ IOThread::IOThread( net_log_(net_log), extension_event_router_forwarder_(extension_event_router_forwarder), globals_(NULL), - speculative_interceptor_(NULL), - predictor_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { // We call RegisterPrefs() here (instead of inside browser_prefs.cc) to make // sure that everything is initialized in the right order. @@ -387,33 +384,6 @@ ChromeNetLog* IOThread::net_log() { return net_log_; } -void IOThread::InitNetworkPredictor( - bool prefetching_enabled, - base::TimeDelta max_dns_queue_delay, - size_t max_speculative_parallel_resolves, - const chrome_common_net::UrlList& startup_urls, - ListValue* referral_list, - bool preconnect_enabled) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod( - this, - &IOThread::InitNetworkPredictorOnIOThread, - prefetching_enabled, max_dns_queue_delay, - max_speculative_parallel_resolves, - startup_urls, referral_list, preconnect_enabled)); -} - -void IOThread::ChangedToOnTheRecord() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod( - this, - &IOThread::ChangedToOnTheRecordOnIOThread)); -} - net::URLRequestContextGetter* IOThread::system_url_request_context_getter() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!system_url_request_context_getter_) { @@ -422,15 +392,6 @@ net::URLRequestContextGetter* IOThread::system_url_request_context_getter() { return system_url_request_context_getter_; } -void IOThread::ClearNetworkingHistory() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ClearHostCache(); - // Discard acrued data used to speculate in the future. - chrome_browser_net::DiscardInitialNavigationHistory(); - if (predictor_) - predictor_->DiscardAllResults(); -} - void IOThread::Init() { // Though this thread is called the "IO" thread, it actually just routes // messages around; it shouldn't be allowed to perform any blocking disk I/O. @@ -528,21 +489,6 @@ void IOThread::CleanUp() { // This must be reset before the ChromeNetLog is destroyed. network_change_observer_.reset(); - // Not initialized in Init(). May not be initialized. - if (predictor_) { - predictor_->Shutdown(); - - // TODO(willchan): Stop reference counting Predictor. It's owned by - // IOThread now. - predictor_->Release(); - predictor_ = NULL; - chrome_browser_net::FreePredictorResources(); - } - - // Deletion will unregister this interceptor. - delete speculative_interceptor_; - speculative_interceptor_ = NULL; - system_proxy_config_service_.reset(); delete globals_; @@ -599,51 +545,6 @@ net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( negotiate_enable_port_); } -void IOThread::InitNetworkPredictorOnIOThread( - bool prefetching_enabled, - base::TimeDelta max_dns_queue_delay, - size_t max_speculative_parallel_resolves, - const chrome_common_net::UrlList& startup_urls, - ListValue* referral_list, - bool preconnect_enabled) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - CHECK(!predictor_); - - chrome_browser_net::EnablePredictor(prefetching_enabled); - - predictor_ = new chrome_browser_net::Predictor( - globals_->host_resolver.get(), - max_dns_queue_delay, - max_speculative_parallel_resolves, - preconnect_enabled); - predictor_->AddRef(); - - // Speculative_interceptor_ is used to predict subresource usage. - DCHECK(!speculative_interceptor_); - speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor; - - FinalizePredictorInitialization(predictor_, startup_urls, referral_list); -} - -void IOThread::ChangedToOnTheRecordOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - if (predictor_) { - // Destroy all evidence of our OTR session. - // Note: OTR mode never saves InitialNavigationHistory data. - predictor_->Predictor::DiscardAllResults(); - } - - // Clear the host cache to avoid showing entries from the OTR session - // in about:net-internals. - ClearHostCache(); - - // Clear all of the passively logged data. - // TODO(eroman): this is a bit heavy handed, really all we need to do is - // clear the data pertaining to incognito context. - net_log_->ClearAllPassivelyCapturedEvents(); -} - void IOThread::ClearHostCache() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h index a2b42b5..9340f39 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -14,7 +14,6 @@ #include "chrome/browser/browser_process_sub_thread.h" #include "chrome/browser/net/ssl_config_service_manager.h" #include "chrome/browser/prefs/pref_member.h" -#include "chrome/common/net/predictor_common.h" #include "net/base/network_change_notifier.h" class ChromeNetLog; @@ -29,11 +28,6 @@ namespace base { class ListValue; } -namespace chrome_browser_net { -class ConnectInterceptor; -class Predictor; -} // namespace chrome_browser_net - namespace net { class CertVerifier; class CookieStore; @@ -109,28 +103,13 @@ class IOThread : public BrowserProcessSubThread { ChromeNetLog* net_log(); - // Initializes the network predictor, which induces DNS pre-resolution and/or - // TCP/IP preconnections. |prefetching_enabled| indicates whether or not DNS - // prefetching should be enabled, and |preconnect_enabled| controls whether - // TCP/IP preconnection is enabled. This should be called by the UI thread. - // It will post a task to the IO thread to perform the actual initialization. - void InitNetworkPredictor(bool prefetching_enabled, - base::TimeDelta max_dns_queue_delay, - size_t max_speculative_parallel_resolves, - const chrome_common_net::UrlList& startup_urls, - base::ListValue* referral_list, - bool preconnect_enabled); - - // Handles changing to On The Record mode, discarding confidential data. - void ChangedToOnTheRecord(); - // Returns a getter for the URLRequestContext. Only called on the UI thread. net::URLRequestContextGetter* system_url_request_context_getter(); - // Clear all network stack history, including the host cache, as well as - // speculative data about subresources of visited sites, and startup-time - // navigations. - void ClearNetworkingHistory(); + // Clears the host cache. Intended to be used to prevent exposing recently + // visited sites on about:net-internals/#dns and about:dns pages. Must be + // called on the IO thread. + void ClearHostCache(); protected: virtual void Init(); @@ -152,21 +131,6 @@ class IOThread : public BrowserProcessSubThread { // SystemURLRequestContextGetter. To be called on IO thread. void InitSystemRequestContextOnIOThread(); - void InitNetworkPredictorOnIOThread( - bool prefetching_enabled, - base::TimeDelta max_dns_queue_delay, - size_t max_speculative_parallel_resolves, - const chrome_common_net::UrlList& startup_urls, - base::ListValue* referral_list, - bool preconnect_enabled); - - void ChangedToOnTheRecordOnIOThread(); - - // Clears the host cache. Intended to be used to prevent exposing recently - // visited sites on about:net-internals/#dns and about:dns pages. Must be - // called on the IO thread. - void ClearHostCache(); - // Returns an SSLConfigService instance. net::SSLConfigService* GetSSLConfigService(); @@ -208,14 +172,6 @@ class IOThread : public BrowserProcessSubThread { // These member variables are initialized by a task posted to the IO thread, // which gets posted by calling certain member functions of IOThread. - - // Note: we user explicit pointers rather than smart pointers to be more - // explicit about destruction order, and ensure that there is no chance that - // these observers would be used accidentally after we have begun to tear - // down. - chrome_browser_net::ConnectInterceptor* speculative_interceptor_; - chrome_browser_net::Predictor* predictor_; - scoped_ptr<net::ProxyConfigService> system_proxy_config_service_; scoped_refptr<PrefProxyConfigTracker> pref_proxy_config_tracker_; diff --git a/chrome/browser/net/connect_interceptor.cc b/chrome/browser/net/connect_interceptor.cc index 5938df2..0ea6dbd 100644 --- a/chrome/browser/net/connect_interceptor.cc +++ b/chrome/browser/net/connect_interceptor.cc @@ -4,8 +4,9 @@ #include "chrome/browser/net/connect_interceptor.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "net/base/load_flags.h" +#include "net/url_request/url_request.h" namespace chrome_browser_net { @@ -16,24 +17,24 @@ namespace chrome_browser_net { // TODO(jar): We should do a persistent field trial to validate/optimize this. static const int kMaxUnusedSocketLifetimeSecondsWithoutAGet = 10; -ConnectInterceptor::ConnectInterceptor() +ConnectInterceptor::ConnectInterceptor(Predictor* predictor) : timed_cache_(base::TimeDelta::FromSeconds( - kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { - net::URLRequest::Deprecated::RegisterRequestInterceptor(this); + kMaxUnusedSocketLifetimeSecondsWithoutAGet)), + predictor_(predictor) { + DCHECK(predictor); } ConnectInterceptor::~ConnectInterceptor() { - net::URLRequest::Deprecated::UnregisterRequestInterceptor(this); } net::URLRequestJob* ConnectInterceptor::MaybeIntercept( - net::URLRequest* request) { + net::URLRequest* request) const { GURL request_scheme_host(Predictor::CanonicalizeUrl(request->url())); if (request_scheme_host == GURL::EmptyGURL()) return NULL; // Learn what URLs are likely to be needed during next startup. - LearnAboutInitialNavigation(request_scheme_host); + predictor_->LearnAboutInitialNavigation(request_scheme_host); bool redirected_host = false; if (request->referrer().empty()) { @@ -55,7 +56,8 @@ net::URLRequestJob* ConnectInterceptor::MaybeIntercept( if (request->original_url().path().length() <= 1 && timed_cache_.WasRecentlySeen(original_scheme_host)) { // TODO(jar): These definite redirects could be learned much faster. - LearnFromNavigation(original_scheme_host, request_scheme_host); + predictor_->LearnFromNavigation(original_scheme_host, + request_scheme_host); } } } @@ -64,7 +66,8 @@ net::URLRequestJob* ConnectInterceptor::MaybeIntercept( bool is_subresource = !(request->load_flags() & net::LOAD_MAIN_FRAME); // Learn about our referring URL, for use in the future. if (is_subresource && timed_cache_.WasRecentlySeen(referring_scheme_host)) - LearnFromNavigation(referring_scheme_host, request_scheme_host); + predictor_->LearnFromNavigation(referring_scheme_host, + request_scheme_host); if (referring_scheme_host == request_scheme_host) { // We've already made any/all predictions when we navigated to the // referring host, so we can bail out here. @@ -80,18 +83,18 @@ net::URLRequestJob* ConnectInterceptor::MaybeIntercept( // main frame request - way back in RenderViewHost::Navigate. So only handle // predictions now for subresources or for redirected hosts. if ((request->load_flags() & net::LOAD_SUB_FRAME) || redirected_host) - PredictFrameSubresources(request_scheme_host); + predictor_->PredictFrameSubresources(request_scheme_host); return NULL; } net::URLRequestJob* ConnectInterceptor::MaybeInterceptResponse( - net::URLRequest* request) { + net::URLRequest* request) const { return NULL; } net::URLRequestJob* ConnectInterceptor::MaybeInterceptRedirect( - net::URLRequest* request, - const GURL& location) { + const GURL& location, + net::URLRequest* request) const { return NULL; } @@ -103,7 +106,7 @@ ConnectInterceptor::TimedCache::TimedCache(const base::TimeDelta& max_duration) // Make Clang compilation happy with explicit destructor. ConnectInterceptor::TimedCache::~TimedCache() {} -bool ConnectInterceptor::TimedCache::WasRecentlySeen(const GURL& url) { +bool ConnectInterceptor::TimedCache::WasRecentlySeen(const GURL& url) const { DCHECK_EQ(url.GetWithEmptyPath(), url); // Evict any overly old entries. base::TimeTicks now = base::TimeTicks::Now(); @@ -117,7 +120,7 @@ bool ConnectInterceptor::TimedCache::WasRecentlySeen(const GURL& url) { return mru_cache_.end() != mru_cache_.Peek(url); } -void ConnectInterceptor::TimedCache::SetRecentlySeen(const GURL& url) { +void ConnectInterceptor::TimedCache::SetRecentlySeen(const GURL& url) const { DCHECK_EQ(url.GetWithEmptyPath(), url); mru_cache_.Put(url, base::TimeTicks::Now()); } diff --git a/chrome/browser/net/connect_interceptor.h b/chrome/browser/net/connect_interceptor.h index eca1c17..657700e 100644 --- a/chrome/browser/net/connect_interceptor.h +++ b/chrome/browser/net/connect_interceptor.h @@ -6,30 +6,34 @@ #define CHROME_BROWSER_NET_CONNECT_INTERCEPTOR_H_ #pragma once -#include "net/url_request/url_request.h" - #include "base/gtest_prod_util.h" #include "base/memory/mru_cache.h" +#include "base/time.h" +#include "net/url_request/url_request_job_factory.h" namespace chrome_browser_net { +class Predictor; + //------------------------------------------------------------------------------ // An interceptor to monitor URLRequests so that we can do speculative DNS // resolution and/or speculative TCP preconnections. -class ConnectInterceptor : public net::URLRequest::Interceptor { +class ConnectInterceptor : public net::URLRequestJobFactory::Interceptor { public: // Construction includes registration as an URL. - ConnectInterceptor(); + explicit ConnectInterceptor(Predictor* predictor); // Destruction includes unregistering. virtual ~ConnectInterceptor(); protected: // Overridden from net::URLRequest::Interceptor: // Learn about referrers, and optionally preconnect based on history. - virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request); - virtual net::URLRequestJob* MaybeInterceptResponse(net::URLRequest* request); - virtual net::URLRequestJob* MaybeInterceptRedirect(net::URLRequest* request, - const GURL& location); + virtual net::URLRequestJob* MaybeIntercept( + net::URLRequest* request) const OVERRIDE; + virtual net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request) const OVERRIDE; + virtual net::URLRequestJob* MaybeInterceptRedirect( + const GURL& location, net::URLRequest* request) const OVERRIDE; private: // Provide access to local class TimedCache for testing. @@ -48,17 +52,20 @@ class ConnectInterceptor : public net::URLRequest::Interceptor { // Evicts any entries that have been in the FIFO "too long," and then checks // to see if the given url is (still) in the FIFO cache. - bool WasRecentlySeen(const GURL& url); + bool WasRecentlySeen(const GURL& url) const; // Adds the given url to the cache, where it will remain for max_duration_. - void SetRecentlySeen(const GURL& url); + void SetRecentlySeen(const GURL& url) const; private: // Our cache will be keyed on a URL (actually, just a scheme/host/port). // We will always track the time it was last added to the FIFO cache by // remembering a TimeTicks value. typedef base::MRUCache<GURL, base::TimeTicks> UrlMruTimedCache; - UrlMruTimedCache mru_cache_; + // mru_cache_ has to be mutable in order to be accessed from the overriden + // URLRequestJob functions. It is mutable because it tracks the urls and + // caches them. + mutable UrlMruTimedCache mru_cache_; // The longest time an entry can persist in the cache, and still be found. const base::TimeDelta max_duration_; @@ -66,6 +73,7 @@ class ConnectInterceptor : public net::URLRequest::Interceptor { DISALLOW_COPY_AND_ASSIGN(TimedCache); }; TimedCache timed_cache_; + Predictor* const predictor_; DISALLOW_COPY_AND_ASSIGN(ConnectInterceptor); }; diff --git a/chrome/browser/net/net_pref_observer.cc b/chrome/browser/net/net_pref_observer.cc index 7d57d97..8d59995 100644 --- a/chrome/browser/net/net_pref_observer.cc +++ b/chrome/browser/net/net_pref_observer.cc @@ -5,7 +5,7 @@ #include "chrome/browser/net/net_pref_observer.h" #include "base/task.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" @@ -28,10 +28,14 @@ void SetEnforceThrottlingOnThrottlerManager(bool enforce) { } NetPrefObserver::NetPrefObserver(PrefService* prefs, - prerender::PrerenderManager* prerender_manager) - : prerender_manager_(prerender_manager) { + prerender::PrerenderManager* prerender_manager, + chrome_browser_net::Predictor* predictor) + : prerender_manager_(prerender_manager), + predictor_(predictor) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(prefs); + DCHECK(predictor); + network_prediction_enabled_.Init(prefs::kNetworkPredictionEnabled, prefs, this); spdy_disabled_.Init(prefs::kDisableSpdy, prefs, this); @@ -56,7 +60,7 @@ void NetPrefObserver::Observe(int type, void NetPrefObserver::ApplySettings(const std::string* pref_name) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - chrome_browser_net::EnablePredictor(*network_prediction_enabled_); + predictor_->EnablePredictor(*network_prediction_enabled_); if (prerender_manager_) prerender_manager_->set_enabled(*network_prediction_enabled_); net::HttpStreamFactory::set_spdy_enabled(!*spdy_disabled_); diff --git a/chrome/browser/net/net_pref_observer.h b/chrome/browser/net/net_pref_observer.h index b7b60c0..4d2ca47 100644 --- a/chrome/browser/net/net_pref_observer.h +++ b/chrome/browser/net/net_pref_observer.h @@ -14,6 +14,10 @@ class Profile; +namespace chrome_browser_net { +class Predictor; +} + namespace prerender { class PrerenderManager; } @@ -27,7 +31,8 @@ class NetPrefObserver : public NotificationObserver { // |prerender_manager| may be NULL. If not, |*prerender_manager| must // outlive this. NetPrefObserver(PrefService* prefs, - prerender::PrerenderManager* prerender_manager); + prerender::PrerenderManager* prerender_manager, + chrome_browser_net::Predictor* predictor); virtual ~NetPrefObserver(); // NotificationObserver @@ -45,6 +50,7 @@ class NetPrefObserver : public NotificationObserver { BooleanPrefMember spdy_disabled_; BooleanPrefMember http_throttling_enabled_; prerender::PrerenderManager* prerender_manager_; + chrome_browser_net::Predictor* predictor_; DISALLOW_COPY_AND_ASSIGN(NetPrefObserver); }; diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc index 7a2320ce..ed4aa4b 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -9,12 +9,23 @@ #include <set> #include <sstream> +#include "base/bind.h" +#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/metrics/histogram.h" +#include "base/stl_util.h" #include "base/stringprintf.h" +#include "base/synchronization/waitable_event.h" #include "base/time.h" #include "base/values.h" +#include "chrome/browser/io_thread.h" #include "chrome/browser/net/preconnect.h" +#include "chrome/browser/prefs/browser_prefs.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/browser/prefs/session_startup_pref.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" #include "content/browser/browser_thread.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" @@ -48,6 +59,22 @@ const TimeDelta Predictor::kDurationBetweenTrimmings = TimeDelta::FromHours(1); const TimeDelta Predictor::kDurationBetweenTrimmingIncrements = TimeDelta::FromSeconds(15); const size_t Predictor::kUrlsTrimmedPerIncrement = 5u; +const size_t Predictor::kMaxSpeculativeParallelResolves = 3; +// To control our congestion avoidance system, which discards a queue when +// resolutions are "taking too long," we need an expected resolution time. +// Common average is in the range of 300-500ms. +const int kExpectedResolutionTimeMs = 500; +const int Predictor::kTypicalSpeculativeGroupSize = 8; +const int Predictor::kMaxSpeculativeResolveQueueDelayMs = + (kExpectedResolutionTimeMs * Predictor::kTypicalSpeculativeGroupSize) / + Predictor::kMaxSpeculativeParallelResolves; + +static int g_max_queueing_delay_ms = 0; +static size_t g_max_parallel_resolves = 0u; + +// A version number for prefs that are saved. This should be incremented when +// we change the format so that we discard old data. +static const int kPredictorStartupFormatVersion = 1; class Predictor::LookupRequest { public: @@ -94,76 +121,89 @@ class Predictor::LookupRequest { DISALLOW_COPY_AND_ASSIGN(LookupRequest); }; -Predictor::Predictor(net::HostResolver* host_resolver, - TimeDelta max_dns_queue_delay, - size_t max_concurrent, - bool preconnect_enabled) - : peak_pending_lookups_(0), +Predictor::Predictor(bool preconnect_enabled) + : initial_observer_(NULL), + predictor_enabled_(true), + peak_pending_lookups_(0), shutdown_(false), - max_concurrent_dns_lookups_(max_concurrent), - max_dns_queue_delay_(max_dns_queue_delay), - host_resolver_(host_resolver), + max_concurrent_dns_lookups_(g_max_parallel_resolves), + max_dns_queue_delay_( + TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), + host_resolver_(NULL), preconnect_enabled_(preconnect_enabled), consecutive_omnibox_preconnect_count_(0), - next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings), - ALLOW_THIS_IN_INITIALIZER_LIST(trim_task_factory_(this)) { + next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } Predictor::~Predictor() { + // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the + // ProfileManagerTest has been updated with a mock profile. DCHECK(shutdown_); } -void Predictor::Shutdown() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!shutdown_); - shutdown_ = true; - - std::set<LookupRequest*>::iterator it; - for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it) - delete *it; +// static +Predictor* Predictor::CreatePredictor( + bool preconnect_enabled, bool simple_shutdown) { + if (simple_shutdown) + return new SimplePredictor(preconnect_enabled); + return new Predictor(preconnect_enabled); } -// Overloaded Resolve() to take a vector of names. -void Predictor::ResolveList(const UrlList& urls, - UrlInfo::ResolutionMotivation motivation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) { - AppendToResolutionQueue(*it, motivation); - } +void Predictor::RegisterUserPrefs(PrefService* user_prefs) { + user_prefs->RegisterListPref(prefs::kDnsPrefetchingStartupList, + PrefService::UNSYNCABLE_PREF); + user_prefs->RegisterListPref(prefs::kDnsPrefetchingHostReferralList, + PrefService::UNSYNCABLE_PREF); } -// Basic Resolve() takes an invidual name, and adds it -// to the queue. -void Predictor::Resolve(const GURL& url, - UrlInfo::ResolutionMotivation motivation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!url.has_host()) - return; - AppendToResolutionQueue(url, motivation); -} - -void Predictor::LearnFromNavigation(const GURL& referring_url, - const GURL& target_url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url)); - DCHECK_NE(referring_url, GURL::EmptyGURL()); - DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url)); - DCHECK_NE(target_url, GURL::EmptyGURL()); +// --------------------- Start UI methods. ------------------------------------ + +void Predictor::InitNetworkPredictor(PrefService* user_prefs, + PrefService* local_state, + IOThread* io_thread) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + bool predictor_enabled = + user_prefs->GetBoolean(prefs::kNetworkPredictionEnabled); + + // Gather the list of hostnames to prefetch on startup. + UrlList urls = GetPredictedUrlListAtStartup(user_prefs, local_state); + + base::ListValue* referral_list = + static_cast<base::ListValue*>(user_prefs->GetList( + prefs::kDnsPrefetchingHostReferralList)->DeepCopy()); + + // Remove obsolete preferences from local state if necessary. + int current_version = + local_state->GetInteger(prefs::kMultipleProfilePrefMigration); + if ((current_version & browser::DNS_PREFS) == 0) { + local_state->RegisterListPref(prefs::kDnsStartupPrefetchList, + PrefService::UNSYNCABLE_PREF); + local_state->RegisterListPref(prefs::kDnsHostReferralList, + PrefService::UNSYNCABLE_PREF); + local_state->ClearPref(prefs::kDnsStartupPrefetchList); + local_state->ClearPref(prefs::kDnsHostReferralList); + local_state->SetInteger(prefs::kMultipleProfilePrefMigration, + current_version | browser::DNS_PREFS); + } - referrers_[referring_url].SuggestHost(target_url); - // Possibly do some referrer trimming. - TrimReferrers(); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind( + &Predictor::FinalizeInitializationOnIOThread, + base::Unretained(this), + urls, referral_list, + io_thread, predictor_enabled)); } -enum SubresourceValue { - PRECONNECTION, - PRERESOLUTION, - TOO_NEW, - SUBRESOURCE_VALUE_MAX -}; - void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!predictor_enabled_) + return; + if (!url.is_valid() || !url.has_host()) + return; std::string host = url.HostNoBrackets(); bool is_new_host_request = (host != last_omnibox_host_); last_omnibox_host_ = host; @@ -227,11 +267,16 @@ void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - NewRunnableMethod(this, &Predictor::Resolve, CanonicalizeUrl(url), - motivation)); + base::Bind(&Predictor::Resolve, base::Unretained(this), + CanonicalizeUrl(url), motivation)); } void Predictor::PreconnectUrlAndSubresources(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!predictor_enabled_) + return; + if (!url.is_valid() || !url.has_host()) + return; if (preconnect_enabled()) { std::string host = url.HostNoBrackets(); UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED); @@ -242,65 +287,188 @@ void Predictor::PreconnectUrlAndSubresources(const GURL& url) { } } -void Predictor::PredictFrameSubresources(const GURL& url) { - DCHECK_EQ(url.GetWithEmptyPath(), url); - // Add one pass through the message loop to allow current navigation to - // proceed. +UrlList Predictor::GetPredictedUrlListAtStartup( + PrefService* user_prefs, + PrefService* local_state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + UrlList urls; + // Recall list of URLs we learned about during last session. + // This may catch secondary hostnames, pulled in by the homepages. It will + // also catch more of the "primary" home pages, since that was (presumably) + // rendered first (and will be rendered first this time too). + const ListValue* startup_list = + user_prefs->GetList(prefs::kDnsPrefetchingStartupList); + + if (startup_list) { + base::ListValue::const_iterator it = startup_list->begin(); + int format_version = -1; + if (it != startup_list->end() && + (*it)->GetAsInteger(&format_version) && + format_version == kPredictorStartupFormatVersion) { + ++it; + for (; it != startup_list->end(); ++it) { + std::string url_spec; + if (!(*it)->GetAsString(&url_spec)) { + LOG(DFATAL); + break; // Format incompatibility. + } + GURL url(url_spec); + if (!url.has_host() || !url.has_scheme()) { + LOG(DFATAL); + break; // Format incompatibility. + } + + urls.push_back(url); + } + } + } + + // Prepare for any static home page(s) the user has in prefs. The user may + // have a LOT of tab's specified, so we may as well try to warm them all. + SessionStartupPref tab_start_pref = + SessionStartupPref::GetStartupPref(user_prefs); + if (SessionStartupPref::URLS == tab_start_pref.type) { + for (size_t i = 0; i < tab_start_pref.urls.size(); i++) { + GURL gurl = tab_start_pref.urls[i]; + if (!gurl.is_valid() || gurl.SchemeIsFile() || gurl.host().empty()) + continue; + if (gurl.SchemeIs("http") || gurl.SchemeIs("https")) + urls.push_back(gurl.GetWithEmptyPath()); + } + } + + if (urls.empty()) + urls.push_back(GURL("http://www.google.com:80")); + + return urls; +} + +void Predictor::set_max_queueing_delay(int max_queueing_delay_ms) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + g_max_queueing_delay_ms = max_queueing_delay_ms; +} + +void Predictor::set_max_parallel_resolves(size_t max_parallel_resolves) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + g_max_parallel_resolves = max_parallel_resolves; +} + +void Predictor::ShutdownOnUIThread(PrefService* user_prefs) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + SaveStateForNextStartupAndTrim(user_prefs); + BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - NewRunnableMethod(this, &Predictor::PrepareFrameSubresources, url)); + base::Bind(&Predictor::Shutdown, base::Unretained(this))); } -void Predictor::PrepareFrameSubresources(const GURL& url) { +// ---------------------- End UI methods. ------------------------------------- + +// --------------------- Start IO methods. ------------------------------------ + +void Predictor::Shutdown() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK_EQ(url.GetWithEmptyPath(), url); - Referrers::iterator it = referrers_.find(url); - if (referrers_.end() == it) { - // Only when we don't know anything about this url, make 2 connections - // available. We could do this completely via learning (by prepopulating - // the referrer_ list with this expected value), but it would swell the - // size of the list with all the "Leaf" nodes in the tree (nodes that don't - // load any subresources). If we learn about this resource, we will instead - // provide a more carefully estimated preconnection count. - if (preconnect_enabled_) - PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2); - return; + DCHECK(!shutdown_); + shutdown_ = true; + + STLDeleteElements(&pending_lookups_); +} + +void Predictor::DiscardAllResults() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Delete anything listed so far in this session that shows in about:dns. + referrers_.clear(); + + + // Try to delete anything in our work queue. + while (!work_queue_.IsEmpty()) { + // Emulate processing cycle as though host was not found. + GURL url = work_queue_.Pop(); + UrlInfo* info = &results_[url]; + DCHECK(info->HasUrl(url)); + info->SetAssignedState(); + info->SetNoSuchNameState(); } + // Now every result_ is either resolved, or is being resolved + // (see LookupRequest). - Referrer* referrer = &(it->second); - referrer->IncrementUseCount(); - const UrlInfo::ResolutionMotivation motivation = - UrlInfo::LEARNED_REFERAL_MOTIVATED; - for (Referrer::iterator future_url = referrer->begin(); - future_url != referrer->end(); ++future_url) { - SubresourceValue evalution(TOO_NEW); - double connection_expectation = future_url->second.subresource_use_rate(); - UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", - static_cast<int>(connection_expectation * 100), - 10, 5000, 50); - future_url->second.ReferrerWasObserved(); - if (preconnect_enabled_ && - connection_expectation > kPreconnectWorthyExpectedValue) { - evalution = PRECONNECTION; - future_url->second.IncrementPreconnectionCount(); - int count = static_cast<int>(std::ceil(connection_expectation)); - if (url.host() == future_url->first.host()) - ++count; - PreconnectOnIOThread(future_url->first, motivation, count); - } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) { - evalution = PRERESOLUTION; - future_url->second.preresolution_increment(); - UrlInfo* queued_info = AppendToResolutionQueue(future_url->first, - motivation); - if (queued_info) - queued_info->SetReferringHostname(url); + // Step through result_, recording names of all hosts that can't be erased. + // We can't erase anything being worked on. + Results assignees; + for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { + GURL url(it->first); + UrlInfo* info = &it->second; + DCHECK(info->HasUrl(url)); + if (info->is_assigned()) { + info->SetPendingDeleteState(); + assignees[url] = *info; } - UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution, - SUBRESOURCE_VALUE_MAX); + } + DCHECK_LE(assignees.size(), max_concurrent_dns_lookups_); + results_.clear(); + // Put back in the names being worked on. + for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { + DCHECK(it->second.is_marked_to_delete()); + results_[it->first] = it->second; } } +// Overloaded Resolve() to take a vector of names. +void Predictor::ResolveList(const UrlList& urls, + UrlInfo::ResolutionMotivation motivation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) { + AppendToResolutionQueue(*it, motivation); + } +} + +// Basic Resolve() takes an invidual name, and adds it +// to the queue. +void Predictor::Resolve(const GURL& url, + UrlInfo::ResolutionMotivation motivation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!url.has_host()) + return; + AppendToResolutionQueue(url, motivation); +} + +void Predictor::LearnFromNavigation(const GURL& referring_url, + const GURL& target_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!predictor_enabled_) + return; + DCHECK_EQ(referring_url, Predictor::CanonicalizeUrl(referring_url)); + DCHECK_NE(referring_url, GURL::EmptyGURL()); + DCHECK_EQ(target_url, Predictor::CanonicalizeUrl(target_url)); + DCHECK_NE(target_url, GURL::EmptyGURL()); + + referrers_[referring_url].SuggestHost(target_url); + // Possibly do some referrer trimming. + TrimReferrers(); +} + +//----------------------------------------------------------------------------- +// This section supports the about:dns page. + +void Predictor::PredictorGetHtmlInfo(Predictor* predictor, + std::string* output) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + output->append("<html><head><title>About DNS</title>" + // We'd like the following no-cache... but it doesn't work. + // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" + "</head><body>"); + if (predictor && predictor->predictor_enabled()) { + predictor->GetHtmlInfo(output); + } else { + output->append("DNS pre-resolution and TCP pre-connection is disabled."); + } + output->append("</body></html>"); +} + // Provide sort order so all .com's are together, etc. struct RightToLeftStringSorter { bool operator()(const GURL& left, @@ -413,6 +581,11 @@ void Predictor::GetHtmlReferrerLists(std::string* output) { void Predictor::GetHtmlInfo(std::string* output) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (initial_observer_.get()) + initial_observer_->GetFirstResolutionsHtml(output); + // Show list of subresource predictions and stats. + GetHtmlReferrerLists(output); + // Local lists for calling UrlInfo UrlInfo::UrlInfoTable name_not_found; UrlInfo::UrlInfoTable name_preresolved; @@ -448,81 +621,308 @@ void Predictor::GetHtmlInfo(std::string* output) { "Preresolving DNS records revealed non-existence for ", brief, output); } -UrlInfo* Predictor::AppendToResolutionQueue( - const GURL& url, - UrlInfo::ResolutionMotivation motivation) { +void Predictor::TrimReferrersNow() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(url.has_host()); + // Just finish up work if an incremental trim is in progress. + if (urls_being_trimmed_.empty()) + LoadUrlsForTrimming(); + IncrementalTrimReferrers(true); // Do everything now. +} - if (shutdown_) - return NULL; +void Predictor::SerializeReferrers(base::ListValue* referral_list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + referral_list->Clear(); + referral_list->Append(new base::FundamentalValue(kPredictorReferrerVersion)); + for (Referrers::const_iterator it = referrers_.begin(); + it != referrers_.end(); ++it) { + // Serialize the list of subresource names. + Value* subresource_list(it->second.Serialize()); - UrlInfo* info = &results_[url]; - info->SetUrl(url); // Initialize or DCHECK. - // TODO(jar): I need to discard names that have long since expired. - // Currently we only add to the domain map :-/ + // Create a list for each referer. + ListValue* motivator(new ListValue); + motivator->Append(new StringValue(it->first.spec())); + motivator->Append(subresource_list); - DCHECK(info->HasUrl(url)); + referral_list->Append(motivator); + } +} - if (!info->NeedsDnsUpdate()) { - info->DLogResultsStats("DNS PrefetchNotUpdated"); - return NULL; +void Predictor::DeserializeReferrers(const base::ListValue& referral_list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int format_version = -1; + if (referral_list.GetSize() > 0 && + referral_list.GetInteger(0, &format_version) && + format_version == kPredictorReferrerVersion) { + for (size_t i = 1; i < referral_list.GetSize(); ++i) { + base::ListValue* motivator; + if (!referral_list.GetList(i, &motivator)) { + NOTREACHED(); + return; + } + std::string motivating_url_spec; + if (!motivator->GetString(0, &motivating_url_spec)) { + NOTREACHED(); + return; + } + + Value* subresource_list; + if (!motivator->Get(1, &subresource_list)) { + NOTREACHED(); + return; + } + + referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list); + } } +} - info->SetQueuedState(motivation); - work_queue_.Push(url, motivation); - StartSomeQueuedResolutions(); - return info; +void Predictor::DeserializeReferrersThenDelete( + base::ListValue* referral_list) { + DeserializeReferrers(*referral_list); + delete referral_list; } -void Predictor::StartSomeQueuedResolutions() { +void Predictor::DiscardInitialNavigationHistory() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (initial_observer_.get()) + initial_observer_->DiscardInitialNavigationHistory(); +} - while (!work_queue_.IsEmpty() && - pending_lookups_.size() < max_concurrent_dns_lookups_) { - const GURL url(work_queue_.Pop()); - UrlInfo* info = &results_[url]; - DCHECK(info->HasUrl(url)); - info->SetAssignedState(); +void Predictor::FinalizeInitializationOnIOThread( + const UrlList& startup_urls, + base::ListValue* referral_list, + IOThread* io_thread, + bool predictor_enabled) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (CongestionControlPerformed(info)) { - DCHECK(work_queue_.IsEmpty()); - return; - } + predictor_enabled_ = predictor_enabled; + initial_observer_.reset(new InitialObserver()); + host_resolver_ = io_thread->globals()->host_resolver.get(); - LookupRequest* request = new LookupRequest(this, host_resolver_, url); - int status = request->Start(); - if (status == net::ERR_IO_PENDING) { - // Will complete asynchronously. - pending_lookups_.insert(request); - peak_pending_lookups_ = std::max(peak_pending_lookups_, - pending_lookups_.size()); - } else { - // Completed synchronously (was already cached by HostResolver), or else - // there was (equivalently) some network error that prevents us from - // finding the name. Status net::OK means it was "found." - LookupFinished(request, url, status == net::OK); - delete request; - } + // ScopedRunnableMethodFactory instances need to be created and destroyed + // on the same thread. The predictor lives on the IO thread and will die + // from there so now that we're on the IO thread we need to properly + // initialize the ScopedrunnableMethodFactory. + trim_task_factory_.reset(new ScopedRunnableMethodFactory<Predictor>(this)); + + // Prefetch these hostnames on startup. + DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); + DeserializeReferrersThenDelete(referral_list); +} + +//----------------------------------------------------------------------------- +// This section intermingles prefetch results with actual browser HTTP +// network activity. It supports calculating of the benefit of a prefetch, as +// well as recording what prefetched hostname resolutions might be potentially +// helpful during the next chrome-startup. +//----------------------------------------------------------------------------- + +void Predictor::LearnAboutInitialNavigation(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!predictor_enabled_ || NULL == initial_observer_.get() ) + return; + initial_observer_->Append(url, this); +} + +// This API is only used in the browser process. +// It is called from an IPC message originating in the renderer. It currently +// includes both Page-Scan, and Link-Hover prefetching. +// TODO(jar): Separate out link-hover prefetching, and page-scan results. +void Predictor::DnsPrefetchList(const NameList& hostnames) { + // TODO(jar): Push GURL transport further back into renderer, but this will + // require a Webkit change in the observer :-/. + UrlList urls; + for (NameList::const_iterator it = hostnames.begin(); + it < hostnames.end(); + ++it) { + urls.push_back(GURL("http://" + *it + ":80")); } + + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DnsPrefetchMotivatedList(urls, UrlInfo::PAGE_SCAN_MOTIVATED); } -bool Predictor::CongestionControlPerformed(UrlInfo* info) { +void Predictor::DnsPrefetchMotivatedList( + const UrlList& urls, + UrlInfo::ResolutionMotivation motivation) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || + BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!predictor_enabled_) + return; + + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { + ResolveList(urls, motivation); + } else { + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&Predictor::ResolveList, base::Unretained(this), + urls, motivation)); + } +} + +//----------------------------------------------------------------------------- +// Functions to handle saving of hostnames from one session to the next, to +// expedite startup times. + +static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread( + base::ListValue* startup_list, + base::ListValue* referral_list, + base::WaitableEvent* completion, + Predictor* predictor) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // Note: queue_duration is ONLY valid after we go to assigned state. - if (info->queue_duration() < max_dns_queue_delay_) - return false; - // We need to discard all entries in our queue, as we're keeping them waiting - // too long. By doing this, we'll have a chance to quickly service urgent - // resolutions, and not have a bogged down system. - while (true) { - info->RemoveFromQueue(); - if (work_queue_.IsEmpty()) - break; - info = &results_[work_queue_.Pop()]; - info->SetAssignedState(); + + if (NULL == predictor) { + completion->Signal(); + return; + } + predictor->SaveDnsPrefetchStateForNextStartupAndTrim( + startup_list, referral_list, completion); +} + +void Predictor::SaveStateForNextStartupAndTrim(PrefService* prefs) { + if (!predictor_enabled_) + return; + + base::WaitableEvent completion(true, false); + + ListPrefUpdate update_startup_list(prefs, prefs::kDnsPrefetchingStartupList); + ListPrefUpdate update_referral_list(prefs, + prefs::kDnsPrefetchingHostReferralList); + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { + SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread( + update_startup_list.Get(), + update_referral_list.Get(), + &completion, + this); + } else { + bool posted = BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind( + SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread, + update_startup_list.Get(), + update_referral_list.Get(), + &completion, + this)); + + // TODO(jar): Synchronous waiting for the IO thread is a potential source + // to deadlocks and should be investigated. See http://crbug.com/78451. + DCHECK(posted); + if (posted) + completion.Wait(); + } +} + +void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim( + base::ListValue* startup_list, + base::ListValue* referral_list, + base::WaitableEvent* completion) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (initial_observer_.get()) + initial_observer_->GetInitialDnsResolutionList(startup_list); + + // Do at least one trim at shutdown, in case the user wasn't running long + // enough to do any regular trimming of referrers. + TrimReferrersNow(); + SerializeReferrers(referral_list); + + completion->Signal(); +} + +void Predictor::EnablePredictor(bool enable) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || + BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { + EnablePredictorOnIOThread(enable); + } else { + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&Predictor::EnablePredictorOnIOThread, + base::Unretained(this), enable)); + } +} + +void Predictor::EnablePredictorOnIOThread(bool enable) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + predictor_enabled_ = enable; +} + +void Predictor::PredictFrameSubresources(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || + BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!predictor_enabled_) + return; + DCHECK_EQ(url.GetWithEmptyPath(), url); + // Add one pass through the message loop to allow current navigation to + // proceed. + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { + PrepareFrameSubresources(url); + } else { + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&Predictor::PrepareFrameSubresources, + base::Unretained(this), url)); + } +} + +enum SubresourceValue { + PRECONNECTION, + PRERESOLUTION, + TOO_NEW, + SUBRESOURCE_VALUE_MAX +}; + +void Predictor::PrepareFrameSubresources(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_EQ(url.GetWithEmptyPath(), url); + Referrers::iterator it = referrers_.find(url); + if (referrers_.end() == it) { + // Only when we don't know anything about this url, make 2 connections + // available. We could do this completely via learning (by prepopulating + // the referrer_ list with this expected value), but it would swell the + // size of the list with all the "Leaf" nodes in the tree (nodes that don't + // load any subresources). If we learn about this resource, we will instead + // provide a more carefully estimated preconnection count. + if (preconnect_enabled_) + PreconnectOnIOThread(url, UrlInfo::SELF_REFERAL_MOTIVATED, 2); + return; + } + + Referrer* referrer = &(it->second); + referrer->IncrementUseCount(); + const UrlInfo::ResolutionMotivation motivation = + UrlInfo::LEARNED_REFERAL_MOTIVATED; + for (Referrer::iterator future_url = referrer->begin(); + future_url != referrer->end(); ++future_url) { + SubresourceValue evalution(TOO_NEW); + double connection_expectation = future_url->second.subresource_use_rate(); + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", + static_cast<int>(connection_expectation * 100), + 10, 5000, 50); + future_url->second.ReferrerWasObserved(); + if (preconnect_enabled_ && + connection_expectation > kPreconnectWorthyExpectedValue) { + evalution = PRECONNECTION; + future_url->second.IncrementPreconnectionCount(); + int count = static_cast<int>(std::ceil(connection_expectation)); + if (url.host() == future_url->first.host()) + ++count; + PreconnectOnIOThread(future_url->first, motivation, count); + } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) { + evalution = PRERESOLUTION; + future_url->second.preresolution_increment(); + UrlInfo* queued_info = AppendToResolutionQueue(future_url->first, + motivation); + if (queued_info) + queued_info->SetReferringHostname(url); + } + UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution, + SUBRESOURCE_VALUE_MAX); } - return true; } void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url, @@ -551,96 +951,79 @@ void Predictor::LookupFinished(LookupRequest* request, const GURL& url, } } -void Predictor::DiscardAllResults() { +UrlInfo* Predictor::AppendToResolutionQueue( + const GURL& url, + UrlInfo::ResolutionMotivation motivation) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // Delete anything listed so far in this session that shows in about:dns. - referrers_.clear(); + DCHECK(url.has_host()); + if (shutdown_) + return NULL; - // Try to delete anything in our work queue. - while (!work_queue_.IsEmpty()) { - // Emulate processing cycle as though host was not found. - GURL url = work_queue_.Pop(); - UrlInfo* info = &results_[url]; - DCHECK(info->HasUrl(url)); - info->SetAssignedState(); - info->SetNoSuchNameState(); - } - // Now every result_ is either resolved, or is being resolved - // (see LookupRequest). + UrlInfo* info = &results_[url]; + info->SetUrl(url); // Initialize or DCHECK. + // TODO(jar): I need to discard names that have long since expired. + // Currently we only add to the domain map :-/ - // Step through result_, recording names of all hosts that can't be erased. - // We can't erase anything being worked on. - Results assignees; - for (Results::iterator it = results_.begin(); results_.end() != it; ++it) { - GURL url(it->first); - UrlInfo* info = &it->second; - DCHECK(info->HasUrl(url)); - if (info->is_assigned()) { - info->SetPendingDeleteState(); - assignees[url] = *info; - } - } - DCHECK(assignees.size() <= max_concurrent_dns_lookups_); - results_.clear(); - // Put back in the names being worked on. - for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) { - DCHECK(it->second.is_marked_to_delete()); - results_[it->first] = it->second; + DCHECK(info->HasUrl(url)); + + if (!info->NeedsDnsUpdate()) { + info->DLogResultsStats("DNS PrefetchNotUpdated"); + return NULL; } -} -void Predictor::TrimReferrersNow() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // Just finish up work if an incremental trim is in progress. - if (urls_being_trimmed_.empty()) - LoadUrlsForTrimming(); - IncrementalTrimReferrers(true); // Do everything now. + info->SetQueuedState(motivation); + work_queue_.Push(url, motivation); + StartSomeQueuedResolutions(); + return info; } -void Predictor::SerializeReferrers(ListValue* referral_list) { +bool Predictor::CongestionControlPerformed(UrlInfo* info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - referral_list->Clear(); - referral_list->Append(new base::FundamentalValue(kPredictorReferrerVersion)); - for (Referrers::const_iterator it = referrers_.begin(); - it != referrers_.end(); ++it) { - // Serialize the list of subresource names. - Value* subresource_list(it->second.Serialize()); - - // Create a list for each referer. - ListValue* motivator(new ListValue); - motivator->Append(new StringValue(it->first.spec())); - motivator->Append(subresource_list); - - referral_list->Append(motivator); + // Note: queue_duration is ONLY valid after we go to assigned state. + if (info->queue_duration() < max_dns_queue_delay_) + return false; + // We need to discard all entries in our queue, as we're keeping them waiting + // too long. By doing this, we'll have a chance to quickly service urgent + // resolutions, and not have a bogged down system. + while (true) { + info->RemoveFromQueue(); + if (work_queue_.IsEmpty()) + break; + info = &results_[work_queue_.Pop()]; + info->SetAssignedState(); } + return true; } -void Predictor::DeserializeReferrers(const ListValue& referral_list) { +void Predictor::StartSomeQueuedResolutions() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int format_version = -1; - if (referral_list.GetSize() > 0 && - referral_list.GetInteger(0, &format_version) && - format_version == kPredictorReferrerVersion) { - for (size_t i = 1; i < referral_list.GetSize(); ++i) { - ListValue* motivator; - if (!referral_list.GetList(i, &motivator)) { - NOTREACHED(); - return; - } - std::string motivating_url_spec; - if (!motivator->GetString(0, &motivating_url_spec)) { - NOTREACHED(); - return; - } - Value* subresource_list; - if (!motivator->Get(1, &subresource_list)) { - NOTREACHED(); - return; - } + while (!work_queue_.IsEmpty() && + pending_lookups_.size() < max_concurrent_dns_lookups_) { + const GURL url(work_queue_.Pop()); + UrlInfo* info = &results_[url]; + DCHECK(info->HasUrl(url)); + info->SetAssignedState(); - referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list); + if (CongestionControlPerformed(info)) { + DCHECK(work_queue_.IsEmpty()); + return; + } + + LookupRequest* request = new LookupRequest(this, host_resolver_, url); + int status = request->Start(); + if (status == net::ERR_IO_PENDING) { + // Will complete asynchronously. + pending_lookups_.insert(request); + peak_pending_lookups_ = std::max(peak_pending_lookups_, + pending_lookups_.size()); + } else { + // Completed synchronously (was already cached by HostResolver), or else + // there was (equivalently) some network error that prevents us from + // finding the name. Status net::OK means it was "found." + LookupFinished(request, url, status == net::OK); + delete request; } } } @@ -673,8 +1056,8 @@ void Predictor::PostIncrementalTrimTask() { return; MessageLoop::current()->PostDelayedTask( FROM_HERE, - trim_task_factory_.NewRunnableMethod(&Predictor::IncrementalTrimReferrers, - false), + trim_task_factory_->NewRunnableMethod( + &Predictor::IncrementalTrimReferrers, false), kDurationBetweenTrimmingIncrements.InMilliseconds()); } @@ -693,7 +1076,9 @@ void Predictor::IncrementalTrimReferrers(bool trim_all_now) { PostIncrementalTrimTask(); } -//------------------------------------------------------------------------------ +// ---------------------- End IO methods. ------------------------------------- + +//----------------------------------------------------------------------------- Predictor::HostNameQueue::HostNameQueue() { } @@ -729,15 +1114,69 @@ GURL Predictor::HostNameQueue::Pop() { return url; } -void Predictor::DeserializeReferrersThenDelete(ListValue* referral_list) { - DeserializeReferrers(*referral_list); - delete referral_list; +//----------------------------------------------------------------------------- +// Member definitions for InitialObserver class. + +Predictor::InitialObserver::InitialObserver() { +} + +Predictor::InitialObserver::~InitialObserver() { +} + +void Predictor::InitialObserver::Append(const GURL& url, + Predictor* predictor) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // TODO(rlp): Do we really need the predictor check here? + if (NULL == predictor) + return; + if (kStartupResolutionCount <= first_navigations_.size()) + return; + + DCHECK(url.SchemeIs("http") || url.SchemeIs("https")); + DCHECK_EQ(url, Predictor::CanonicalizeUrl(url)); + if (first_navigations_.find(url) == first_navigations_.end()) + first_navigations_[url] = base::TimeTicks::Now(); } +void Predictor::InitialObserver::GetInitialDnsResolutionList( + base::ListValue* startup_list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(startup_list); + startup_list->Clear(); + DCHECK_EQ(0u, startup_list->GetSize()); + startup_list->Append( + new base::FundamentalValue(kPredictorStartupFormatVersion)); + for (FirstNavigations::iterator it = first_navigations_.begin(); + it != first_navigations_.end(); + ++it) { + DCHECK(it->first == Predictor::CanonicalizeUrl(it->first)); + startup_list->Append(new StringValue(it->first.spec())); + } +} -//------------------------------------------------------------------------------ +void Predictor::InitialObserver::GetFirstResolutionsHtml( + std::string* output) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + UrlInfo::UrlInfoTable resolution_list; + { + for (FirstNavigations::iterator it(first_navigations_.begin()); + it != first_navigations_.end(); + it++) { + UrlInfo info; + info.SetUrl(it->first); + info.set_time(it->second); + resolution_list.push_back(info); + } + } + UrlInfo::GetHtmlTable(resolution_list, + "Future startups will prefetch DNS records for ", false, output); +} + +//----------------------------------------------------------------------------- // Helper functions -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- // static GURL Predictor::CanonicalizeUrl(const GURL& url) { @@ -763,5 +1202,14 @@ GURL Predictor::CanonicalizeUrl(const GURL& url) { return GURL(scheme + "://" + url.host() + colon_plus_port); } +void SimplePredictor::InitNetworkPredictor(PrefService* user_prefs, + PrefService* local_state, + IOThread* io_thread) { + // Empty function for unittests. +} + +void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { + SetShutdown(true); +} } // namespace chrome_browser_net diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index dcd8006..710b8ae 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -27,7 +27,7 @@ #include <vector> #include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "chrome/browser/net/url_info.h" #include "chrome/browser/net/referrer.h" #include "chrome/common/net/predictor_common.h" @@ -37,29 +37,97 @@ namespace base { class ListValue; } +namespace base { +class WaitableEvent; +} + namespace net { class HostResolver; } // namespace net +class IOThread; +class PrefService; +class Profile; + namespace chrome_browser_net { typedef chrome_common_net::UrlList UrlList; typedef chrome_common_net::NameList NameList; typedef std::map<GURL, UrlInfo> Results; -// Note that Predictor is not thread safe, and must only be called from -// the IO thread. Failure to do so will result in a DCHECK at runtime. -class Predictor : public base::RefCountedThreadSafe<Predictor> { +// Predictor is constructed during Profile construction (on the UI thread), +// but it is destroyed on the IO thread when ProfileIOData goes away. All of +// its core state and functionality happens on the IO thread. The only UI +// methods are initialization / shutdown related (including preconnect +// initialization), or convenience methods that internally forward calls to +// the IO thread. +class Predictor { public: // A version number for prefs that are saved. This should be incremented when // we change the format so that we discard old data. static const int kPredictorReferrerVersion; + // Given that the underlying Chromium resolver defaults to a total maximum of + // 8 paralell resolutions, we will avoid any chance of starving navigational + // resolutions by limiting the number of paralell speculative resolutions. + // This is used in the field trials and testing. + // TODO(jar): Move this limitation into the resolver. + static const size_t kMaxSpeculativeParallelResolves; + + // To control the congestion avoidance system, we need an estimate of how + // many speculative requests may arrive at once. Since we currently only + // keep 8 subresource names for each frame, we'll use that as our basis. + // Note that when scanning search results lists, we might actually get 10 at + // a time, and wikipedia can often supply (during a page scan) upwards of 50. + // In those odd cases, we may discard some of the later speculative requests + // mistakenly assuming that the resolutions took too long. + static const int kTypicalSpeculativeGroupSize; + + // The next constant specifies an amount of queueing delay that is + // "too large," and indicative of problems with resolutions (perhaps due to + // an overloaded router, or such). When we exceed this delay, congestion + // avoidance will kick in and all speculations in the queue will be discarded. + static const int kMaxSpeculativeResolveQueueDelayMs; + // |max_concurrent| specifies how many concurrent (parallel) prefetches will // be performed. Host lookups will be issued through |host_resolver|. - Predictor(net::HostResolver* host_resolver, - base::TimeDelta max_queue_delay_ms, size_t max_concurrent, - bool preconnect_enabled); + explicit Predictor(bool preconnect_enabled); + + virtual ~Predictor(); + + // This function is used to create a predictor. For testing, we can create + // a version which does a simpler shutdown. + static Predictor* CreatePredictor(bool preconnect_enabled, + bool simple_shutdown); + + static void RegisterUserPrefs(PrefService* user_prefs); + + // ------------- Start UI thread methods. + + virtual void InitNetworkPredictor(PrefService* user_prefs, + PrefService* local_state, + IOThread* io_thread); + + // The Omnibox has proposed a given url to the user, and if it is a search + // URL, then it also indicates that this is preconnectable (i.e., we could + // preconnect to the search server). + void AnticipateOmniboxUrl(const GURL& url, bool preconnectable); + + // Preconnect a URL and all of its subresource domains. + void PreconnectUrlAndSubresources(const GURL& url); + + static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs, + PrefService* local_state); + + static void set_max_queueing_delay(int max_queueing_delay_ms); + + static void set_max_parallel_resolves(size_t max_parallel_resolves); + + virtual void ShutdownOnUIThread(PrefService* user_prefs); + + // ------------- End UI thread methods. + + // ------------- Start IO thread methods. // Cancel pending requests and prevent new ones from being made. void Shutdown(); @@ -74,23 +142,8 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // Add hostname(s) to the queue for processing. void ResolveList(const UrlList& urls, UrlInfo::ResolutionMotivation motivation); - void Resolve(const GURL& url, - UrlInfo::ResolutionMotivation motivation); - - // Instigate pre-connection to any URLs, or pre-resolution of related host, - // that we predict will be needed after this navigation (typically - // more-embedded resources on a page). This method will actually post a task - // to do the actual work, so as not to jump ahead of the frame navigation that - // instigated this activity. - void PredictFrameSubresources(const GURL& url); - // The Omnibox has proposed a given url to the user, and if it is a search - // URL, then it also indicates that this is preconnectable (i.e., we could - // preconnect to the search server). - void AnticipateOmniboxUrl(const GURL& url, bool preconnectable); - - // Preconnect a URL and all of its subresource domains. - void PreconnectUrlAndSubresources(const GURL& url); + void Resolve(const GURL& url, UrlInfo::ResolutionMotivation motivation); // Record details of a navigation so that we can preresolve the host name // ahead of time the next time the users navigates to the indicated host. @@ -98,11 +151,14 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // canonicalized to not have a path. void LearnFromNavigation(const GURL& referring_url, const GURL& target_url); + // When displaying info in about:dns, the following API is called. + static void PredictorGetHtmlInfo(Predictor* predictor, std::string* output); + // Dump HTML table containing list of referrers for about:dns. void GetHtmlReferrerLists(std::string* output); // Dump the list of currently known referrer domains and related prefetchable - // domains. + // domains for about:dns. void GetHtmlInfo(std::string* output); // Discards any referrer for which all the suggested host names are currently @@ -124,21 +180,83 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { void DeserializeReferrersThenDelete(base::ListValue* referral_list); - // For unit test code only. - size_t max_concurrent_dns_lookups() const { - return max_concurrent_dns_lookups_; - } + void DiscardInitialNavigationHistory(); - // Flag setting to use preconnection instead of just DNS pre-fetching. - bool preconnect_enabled() const { return preconnect_enabled_; } + void FinalizeInitializationOnIOThread( + const std::vector<GURL>& urls_to_prefetch, + base::ListValue* referral_list, + IOThread* io_thread, + bool predictor_enabled); + + // During startup, we learn what the first N urls visited are, and then + // resolve the associated hosts ASAP during our next startup. + void LearnAboutInitialNavigation(const GURL& url); + + // Renderer bundles up list and sends to this browser API via IPC. + // TODO(jar): Use UrlList instead to include port and scheme. + void DnsPrefetchList(const NameList& hostnames); + + // May be called from either the IO or UI thread and will PostTask + // to the IO thread if necessary. + void DnsPrefetchMotivatedList(const UrlList& urls, + UrlInfo::ResolutionMotivation motivation); + + // May be called from either the IO or UI thread and will PostTask + // to the IO thread if necessary. + void SaveStateForNextStartupAndTrim(PrefService* prefs); + + void SaveDnsPrefetchStateForNextStartupAndTrim( + base::ListValue* startup_list, + base::ListValue* referral_list, + base::WaitableEvent* completion); + + // May be called from either the IO or UI thread and will PostTask + // to the IO thread if necessary. + void EnablePredictor(bool enable); + + void EnablePredictorOnIOThread(bool enable); + + // ------------- End IO thread methods. + + // The following methods may be called on either the IO or UI threads. + + // Instigate pre-connection to any URLs, or pre-resolution of related host, + // that we predict will be needed after this navigation (typically + // more-embedded resources on a page). This method will actually post a task + // to do the actual work, so as not to jump ahead of the frame navigation that + // instigated this activity. + void PredictFrameSubresources(const GURL& url); // Put URL in canonical form, including a scheme, host, and port. // Returns GURL::EmptyGURL() if the scheme is not http/https or if the url // cannot be otherwise canonicalized. static GURL CanonicalizeUrl(const GURL& url); + // Used for testing. + void SetHostResolver(net::HostResolver* host_resolver) { + host_resolver_ = host_resolver; + } + // Used for testing. + size_t max_concurrent_dns_lookups() const { + return max_concurrent_dns_lookups_; + } + // Used for testing. + void SetShutdown(bool shutdown) { + shutdown_ = shutdown; + } + + // Flag setting to use preconnection instead of just DNS pre-fetching. + bool preconnect_enabled() const { + return preconnect_enabled_; + } + + // Flag setting for whether we are prefetching dns lookups. + bool predictor_enabled() const { + return predictor_enabled_; + } + + private: - friend class base::RefCountedThreadSafe<Predictor>; FRIEND_TEST_ALL_PREFIXES(PredictorTest, BenefitLookupTest); FRIEND_TEST_ALL_PREFIXES(PredictorTest, ShutdownWhenResolutionIsPendingTest); FRIEND_TEST_ALL_PREFIXES(PredictorTest, SingleLookupTest); @@ -178,6 +296,38 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { DISALLOW_COPY_AND_ASSIGN(HostNameQueue); }; + // The InitialObserver monitors navigations made by the network stack. This + // is only used to identify startup time resolutions (for re-resolution + // during our next process startup). + // TODO(jar): Consider preconnecting at startup, which may be faster than + // waiting for render process to start and request a connection. + class InitialObserver { + public: + InitialObserver(); + ~InitialObserver(); + // Recording of when we observed each navigation. + typedef std::map<GURL, base::TimeTicks> FirstNavigations; + + // Potentially add a new URL to our startup list. + void Append(const GURL& url, Predictor* predictor); + + // Get an HTML version of our current planned first_navigations_. + void GetFirstResolutionsHtml(std::string* output); + + // Persist the current first_navigations_ for storage in a list. + void GetInitialDnsResolutionList(base::ListValue* startup_list); + + // Discards all initial loading history. + void DiscardInitialNavigationHistory() { first_navigations_.clear(); } + + private: + // List of the first N URL resolutions observed in this run. + FirstNavigations first_navigations_; + + // The number of URLs we'll save for pre-resolving at next startup. + static const size_t kStartupResolutionCount = 10; + }; + // A map that is keyed with the host/port that we've learned were the cause // of loading additional URLs. The list of additional targets is held // in a Referrer instance, which is a value in this map. @@ -206,13 +356,6 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // Number of referring URLs processed in an incremental trimming. static const size_t kUrlsTrimmedPerIncrement; - ~Predictor(); - - // Perform actual resolution or preconnection to subresources now. This is - // an internal worker method that is reached via a post task from - // PredictFrameSubresources(). - void PrepareFrameSubresources(const GURL& url); - // Only for testing. Returns true if hostname has been successfully resolved // (name found). bool WasFound(const GURL& url) const { @@ -232,6 +375,13 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // Only for testing; size_t peak_pending_lookups() const { return peak_pending_lookups_; } + // ------------- Start IO thread methods. + + // Perform actual resolution or preconnection to subresources now. This is + // an internal worker method that is reached via a post task from + // PredictFrameSubresources(). + void PrepareFrameSubresources(const GURL& url); + // Access method for use by async lookup request to pass resolution result. void OnLookupFinished(LookupRequest* request, const GURL& url, bool found); @@ -277,6 +427,14 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // continue with them shortly (i.e., it yeilds and continues). void IncrementalTrimReferrers(bool trim_all_now); + // ------------- End IO thread methods. + + scoped_ptr<InitialObserver> initial_observer_; + + // Status of speculative DNS resolution and speculative TCP/IP connection + // feature. + bool predictor_enabled_; + // work_queue_ holds a list of names we need to look up. HostNameQueue work_queue_; @@ -302,7 +460,7 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { const base::TimeDelta max_dns_queue_delay_; // The host resolver we warm DNS entries for. - net::HostResolver* const host_resolver_; + net::HostResolver* host_resolver_; // Are we currently using preconnection, rather than just DNS resolution, for // subresources and omni-box search URLs. @@ -334,11 +492,23 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // A time after which we need to do more trimming of referrers. base::TimeTicks next_trim_time_; - ScopedRunnableMethodFactory<Predictor> trim_task_factory_; + scoped_ptr<ScopedRunnableMethodFactory<Predictor> > trim_task_factory_; DISALLOW_COPY_AND_ASSIGN(Predictor); }; +// This version of the predictor is used for testing. +class SimplePredictor : public Predictor { + public: + explicit SimplePredictor(bool preconnect_enabled) + : Predictor(preconnect_enabled) {} + virtual ~SimplePredictor() {} + virtual void InitNetworkPredictor(PrefService* user_prefs, + PrefService* local_state, + IOThread* io_thread); + virtual void ShutdownOnUIThread(PrefService* user_prefs); +}; + } // namespace chrome_browser_net #endif // CHROME_BROWSER_NET_PREDICTOR_H_ diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index 23b4ca0..c2426c8 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -13,7 +13,7 @@ #include "base/string_number_conversions.h" #include "base/timer.h" #include "base/values.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/url_info.h" #include "chrome/common/net/predictor_common.h" #include "content/browser/browser_thread.h" @@ -62,10 +62,9 @@ class WaitForResolutionHelper { class PredictorTest : public testing::Test { public: PredictorTest() - : io_thread_(BrowserThread::IO, &loop_), - host_resolver_(new net::MockCachingHostResolver()), - default_max_queueing_delay_(TimeDelta::FromMilliseconds( - PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) { + : ui_thread_(BrowserThread::UI, &loop_), + io_thread_(BrowserThread::IO, &loop_), + host_resolver_(new net::MockCachingHostResolver()) { } protected: @@ -73,6 +72,10 @@ class PredictorTest : public testing::Test { #if defined(OS_WIN) net::EnsureWinsockInit(); #endif + Predictor::set_max_parallel_resolves( + Predictor::kMaxSpeculativeParallelResolves); + Predictor::set_max_queueing_delay( + Predictor::kMaxSpeculativeResolveQueueDelayMs); // Since we are using a caching HostResolver, the following latencies will // only be incurred by the first request, after which the result will be // cached internally by |host_resolver_|. @@ -95,26 +98,19 @@ class PredictorTest : public testing::Test { // IMPORTANT: do not move this below |host_resolver_|; the host resolver // must not outlive the message loop, otherwise bad things can happen // (like posting to a deleted message loop). - MessageLoop loop_; + MessageLoopForUI loop_; + BrowserThread ui_thread_; BrowserThread io_thread_; protected: scoped_ptr<net::MockCachingHostResolver> host_resolver_; - - // Shorthand to access TimeDelta of PredictorInit::kMaxQueueingDelayMs. - // (It would be a static constant... except style rules preclude that :-/ ). - const TimeDelta default_max_queueing_delay_; }; //------------------------------------------------------------------------------ TEST_F(PredictorTest, StartupShutdownTest) { - scoped_refptr<Predictor> testing_master( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); - testing_master->Shutdown(); + Predictor testing_master(true); + testing_master.Shutdown(); } @@ -123,25 +119,22 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { new net::WaitingHostResolverProc(NULL)); host_resolver_->Reset(resolver_proc); - scoped_refptr<Predictor> testing_master( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor testing_master(true); + testing_master.SetHostResolver(host_resolver_.get()); GURL localhost("http://localhost:80"); UrlList names; names.push_back(localhost); - testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); + testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); MessageLoop::current()->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), 500); MessageLoop::current()->Run(); - EXPECT_FALSE(testing_master->WasFound(localhost)); + EXPECT_FALSE(testing_master.WasFound(localhost)); - testing_master->Shutdown(); + testing_master.Shutdown(); // Clean up after ourselves. resolver_proc->Signal(); @@ -149,11 +142,8 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { } TEST_F(PredictorTest, SingleLookupTest) { - scoped_refptr<Predictor> testing_master( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor testing_master(true); + testing_master.SetHostResolver(host_resolver_.get()); GURL goog("http://www.google.com:80"); @@ -162,30 +152,27 @@ TEST_F(PredictorTest, SingleLookupTest) { // Try to flood the predictor with many concurrent requests. for (int i = 0; i < 10; i++) - testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); + testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); - WaitForResolution(testing_master, names); + WaitForResolution(&testing_master, names); - EXPECT_TRUE(testing_master->WasFound(goog)); + EXPECT_TRUE(testing_master.WasFound(goog)); MessageLoop::current()->RunAllPending(); - EXPECT_GT(testing_master->peak_pending_lookups(), names.size() / 2); - EXPECT_LE(testing_master->peak_pending_lookups(), names.size()); - EXPECT_LE(testing_master->peak_pending_lookups(), - testing_master->max_concurrent_dns_lookups()); + EXPECT_GT(testing_master.peak_pending_lookups(), names.size() / 2); + EXPECT_LE(testing_master.peak_pending_lookups(), names.size()); + EXPECT_LE(testing_master.peak_pending_lookups(), + testing_master.max_concurrent_dns_lookups()); - testing_master->Shutdown(); + testing_master.Shutdown(); } TEST_F(PredictorTest, ConcurrentLookupTest) { host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<Predictor> testing_master( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor testing_master(true); + testing_master.SetHostResolver(host_resolver_.get()); GURL goog("http://www.google.com:80"), goog2("http://gmail.google.com.com:80"), @@ -205,37 +192,34 @@ TEST_F(PredictorTest, ConcurrentLookupTest) { // Try to flood the predictor with many concurrent requests. for (int i = 0; i < 10; i++) - testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); + testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); - WaitForResolution(testing_master, names); + WaitForResolution(&testing_master, names); - EXPECT_TRUE(testing_master->WasFound(goog)); - EXPECT_TRUE(testing_master->WasFound(goog3)); - EXPECT_TRUE(testing_master->WasFound(goog2)); - EXPECT_TRUE(testing_master->WasFound(goog4)); - EXPECT_FALSE(testing_master->WasFound(bad1)); - EXPECT_FALSE(testing_master->WasFound(bad2)); + EXPECT_TRUE(testing_master.WasFound(goog)); + EXPECT_TRUE(testing_master.WasFound(goog3)); + EXPECT_TRUE(testing_master.WasFound(goog2)); + EXPECT_TRUE(testing_master.WasFound(goog4)); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); MessageLoop::current()->RunAllPending(); - EXPECT_FALSE(testing_master->WasFound(bad1)); - EXPECT_FALSE(testing_master->WasFound(bad2)); + EXPECT_FALSE(testing_master.WasFound(bad1)); + EXPECT_FALSE(testing_master.WasFound(bad2)); - EXPECT_LE(testing_master->peak_pending_lookups(), names.size()); - EXPECT_LE(testing_master->peak_pending_lookups(), - testing_master->max_concurrent_dns_lookups()); + EXPECT_LE(testing_master.peak_pending_lookups(), names.size()); + EXPECT_LE(testing_master.peak_pending_lookups(), + testing_master.max_concurrent_dns_lookups()); - testing_master->Shutdown(); + testing_master.Shutdown(); } TEST_F(PredictorTest, MassiveConcurrentLookupTest) { host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<Predictor> testing_master( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor testing_master(true); + testing_master.SetHostResolver(host_resolver_.get()); UrlList names; for (int i = 0; i < 100; i++) @@ -244,17 +228,17 @@ TEST_F(PredictorTest, MassiveConcurrentLookupTest) { // Try to flood the predictor with many concurrent requests. for (int i = 0; i < 10; i++) - testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); + testing_master.ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); - WaitForResolution(testing_master, names); + WaitForResolution(&testing_master, names); MessageLoop::current()->RunAllPending(); - EXPECT_LE(testing_master->peak_pending_lookups(), names.size()); - EXPECT_LE(testing_master->peak_pending_lookups(), - testing_master->max_concurrent_dns_lookups()); + EXPECT_LE(testing_master.peak_pending_lookups(), names.size()); + EXPECT_LE(testing_master.peak_pending_lookups(), + testing_master.max_concurrent_dns_lookups()); - testing_master->Shutdown(); + testing_master.Shutdown(); } //------------------------------------------------------------------------------ @@ -352,30 +336,25 @@ static bool GetDataFromSerialization(const GURL& motivation, // Make sure nil referral lists really have no entries, and no latency listed. TEST_F(PredictorTest, ReferrerSerializationNilTest) { - scoped_refptr<Predictor> predictor( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor predictor(true); + predictor.SetHostResolver(host_resolver_.get()); + scoped_ptr<ListValue> referral_list(NewEmptySerializationList()); - predictor->SerializeReferrers(referral_list.get()); + predictor.SerializeReferrers(referral_list.get()); EXPECT_EQ(1U, referral_list->GetSize()); EXPECT_FALSE(GetDataFromSerialization( GURL("http://a.com:79"), GURL("http://b.com:78"), *referral_list.get(), NULL)); - predictor->Shutdown(); + predictor.Shutdown(); } // Make sure that when a serialization list includes a value, that it can be // deserialized into the database, and can be extracted back out via // serialization without being changed. TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { - scoped_refptr<Predictor> predictor( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor predictor(true); + predictor.SetHostResolver(host_resolver_.get()); const GURL motivation_url("http://www.google.com:91"); const GURL subresource_url("http://icons.google.com:90"); const double kUseRate = 23.4; @@ -384,17 +363,17 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { AddToSerializedList(motivation_url, subresource_url, kUseRate, referral_list.get()); - predictor->DeserializeReferrers(*referral_list.get()); + predictor.DeserializeReferrers(*referral_list.get()); ListValue recovered_referral_list; - predictor->SerializeReferrers(&recovered_referral_list); + predictor.SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); double rate; EXPECT_TRUE(GetDataFromSerialization( motivation_url, subresource_url, recovered_referral_list, &rate)); EXPECT_EQ(rate, kUseRate); - predictor->Shutdown(); + predictor.Shutdown(); } // Verify that two floats are within 1% of each other in value. @@ -409,11 +388,8 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { // Make sure the Trim() functionality works as expected. TEST_F(PredictorTest, ReferrerSerializationTrimTest) { - scoped_refptr<Predictor> predictor( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor predictor(true); + predictor.SetHostResolver(host_resolver_.get()); GURL motivation_url("http://www.google.com:110"); GURL icon_subresource_url("http://icons.google.com:111"); @@ -427,10 +403,10 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { AddToSerializedList( motivation_url, img_subresource_url, kRateImg, referral_list.get()); - predictor->DeserializeReferrers(*referral_list.get()); + predictor.DeserializeReferrers(*referral_list.get()); ListValue recovered_referral_list; - predictor->SerializeReferrers(&recovered_referral_list); + predictor.SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); double rate; EXPECT_TRUE(GetDataFromSerialization( @@ -445,8 +421,8 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { // Each time we Trim 24 times, the user_rate figures should reduce by a factor // of two, until they are small, and then a trim will delete the whole entry. for (int i = 0; i < 24; ++i) - predictor->TrimReferrersNow(); - predictor->SerializeReferrers(&recovered_referral_list); + predictor.TrimReferrersNow(); + predictor.SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( motivation_url, icon_subresource_url, recovered_referral_list, &rate)); @@ -457,8 +433,8 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { EXPECT_SIMILAR(rate, kRateImg / 2); for (int i = 0; i < 24; ++i) - predictor->TrimReferrersNow(); - predictor->SerializeReferrers(&recovered_referral_list); + predictor.TrimReferrersNow(); + predictor.SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( motivation_url, icon_subresource_url, recovered_referral_list, &rate)); @@ -468,8 +444,8 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { EXPECT_SIMILAR(rate, kRateImg / 4); for (int i = 0; i < 24; ++i) - predictor->TrimReferrersNow(); - predictor->SerializeReferrers(&recovered_referral_list); + predictor.TrimReferrersNow(); + predictor.SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( motivation_url, icon_subresource_url, recovered_referral_list, &rate)); @@ -480,8 +456,8 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { motivation_url, img_subresource_url, recovered_referral_list, &rate)); for (int i = 0; i < 24; ++i) - predictor->TrimReferrersNow(); - predictor->SerializeReferrers(&recovered_referral_list); + predictor.TrimReferrersNow(); + predictor.SerializeReferrers(&recovered_referral_list); // Icon is also trimmed away, so entire set gets discarded. EXPECT_EQ(1U, recovered_referral_list.GetSize()); EXPECT_FALSE(GetDataFromSerialization( @@ -489,7 +465,7 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { EXPECT_FALSE(GetDataFromSerialization( motivation_url, img_subresource_url, recovered_referral_list, &rate)); - predictor->Shutdown(); + predictor.Shutdown(); } @@ -601,27 +577,24 @@ TEST_F(PredictorTest, CanonicalizeUrl) { } TEST_F(PredictorTest, DiscardPredictorResults) { - scoped_refptr<Predictor> predictor( - new Predictor(host_resolver_.get(), - default_max_queueing_delay_, - PredictorInit::kMaxSpeculativeParallelResolves, - false)); + Predictor predictor(true); + predictor.SetHostResolver(host_resolver_.get()); ListValue referral_list; - predictor->SerializeReferrers(&referral_list); + predictor.SerializeReferrers(&referral_list); EXPECT_EQ(1U, referral_list.GetSize()); GURL host_1("http://test_1"); GURL host_2("http://test_2"); - predictor->LearnFromNavigation(host_1, host_2); + predictor.LearnFromNavigation(host_1, host_2); - predictor->SerializeReferrers(&referral_list); + predictor.SerializeReferrers(&referral_list); EXPECT_EQ(2U, referral_list.GetSize()); - predictor->DiscardAllResults(); - predictor->SerializeReferrers(&referral_list); + predictor.DiscardAllResults(); + predictor.SerializeReferrers(&referral_list); EXPECT_EQ(1U, referral_list.GetSize()); - predictor->Shutdown(); + predictor.Shutdown(); } } // namespace chrome_browser_net diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index d4d3daa..56ba81e 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -27,7 +27,7 @@ #include "chrome/browser/metrics/metrics_log.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/net/net_pref_observer.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/ssl_config_service_manager.h" #include "chrome/browser/notifications/desktop_notification_service.h" @@ -146,7 +146,7 @@ void RegisterUserPrefs(PrefService* user_prefs) { BookmarkModel::RegisterUserPrefs(user_prefs); Browser::RegisterUserPrefs(user_prefs); PasswordManager::RegisterUserPrefs(user_prefs); - chrome_browser_net::RegisterUserPrefs(user_prefs); + chrome_browser_net::Predictor::RegisterUserPrefs(user_prefs); DownloadPrefs::RegisterUserPrefs(user_prefs); bookmark_utils::RegisterUserPrefs(user_prefs); TabContentsWrapper::RegisterUserPrefs(user_prefs); diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index 22d1e85..23b24d3 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -225,3 +225,7 @@ bool Profile::IsSyncAccessible() { ProfileSyncService* syncService = GetProfileSyncService(); return syncService && !syncService->IsManaged(); } + +chrome_browser_net::Predictor* Profile::GetNetworkPredictor() { + return NULL; +} diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h index c2ba480..2cd49e4 100644 --- a/chrome/browser/profiles/profile.h +++ b/chrome/browser/profiles/profile.h @@ -52,6 +52,10 @@ namespace speech_input { class SpeechRecognizer; } +namespace chrome_browser_net { +class Predictor; +} + class AutocompleteClassifier; class BookmarkModel; class ChromeAppCacheService; @@ -543,6 +547,8 @@ class Profile : public content::BrowserContext { // Creates an OffTheRecordProfile which points to this Profile. Profile* CreateOffTheRecordProfile(); + virtual chrome_browser_net::Predictor* GetNetworkPredictor(); + protected: friend class OffTheRecordProfileImpl; diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 02a9374..af5435f 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -47,6 +47,7 @@ #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/net/net_pref_observer.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/ssl_config_service_manager.h" #include "chrome/browser/password_manager/password_store_default.h" @@ -308,7 +309,8 @@ ProfileImpl::ProfileImpl(const FilePath& path, #if defined(OS_WIN) checked_instant_promo_(false), #endif - delegate_(delegate) { + delegate_(delegate), + predictor_(NULL) { DCHECK(!path.empty()) << "Using an empty path will attempt to write " << "profile files to the root directory!"; @@ -316,6 +318,13 @@ ProfileImpl::ProfileImpl(const FilePath& path, TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this, &ProfileImpl::EnsureSessionServiceCreated); + // Determine if prefetch is enabled for this profile. + // If not profile_manager is present, it means we are in a unittest. + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + predictor_ = chrome_browser_net::Predictor::CreatePredictor( + !command_line->HasSwitch(switches::kDisablePreconnect), + g_browser_process->profile_manager() == NULL); + if (delegate_) { prefs_.reset(PrefService::CreatePrefService( GetPrefFilePath(), @@ -436,9 +445,12 @@ void ProfileImpl::DoFinalInit() { // Make sure we initialize the ProfileIOData after everything else has been // initialized that we might be reading from the IO thread. + io_data_.Init(cookie_path, origin_bound_cert_path, cache_path, cache_max_size, media_cache_path, media_cache_max_size, - extensions_cookie_path, app_path); + extensions_cookie_path, app_path, predictor_, + g_browser_process->local_state(), + g_browser_process->io_thread()); ChromePluginServiceFilter::GetInstance()->RegisterResourceContext( PluginPrefs::GetForProfile(this), &GetResourceContext()); @@ -922,7 +934,7 @@ void ProfileImpl::OnPrefsLoaded(bool success) { DCHECK(!net_pref_observer_.get()); net_pref_observer_.reset( - new NetPrefObserver(prefs_.get(), GetPrerenderManager())); + new NetPrefObserver(prefs_.get(), GetPrerenderManager(), predictor_)); DoFinalInit(); } @@ -1739,6 +1751,10 @@ prerender::PrerenderManager* ProfileImpl::GetPrerenderManager() { return prerender_manager_.get(); } +chrome_browser_net::Predictor* ProfileImpl::GetNetworkPredictor() { + return predictor_; +} + SpellCheckProfile* ProfileImpl::GetSpellCheckProfile() { if (!spellcheck_profile_.get()) spellcheck_profile_.reset(new SpellCheckProfile()); diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index cdc2985..850b421 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -122,6 +122,7 @@ class ProfileImpl : public Profile, virtual ExtensionInfoMap* GetExtensionInfoMap(); virtual PromoCounter* GetInstantPromoCounter(); virtual ChromeURLDataManager* GetChromeURLDataManager(); + virtual chrome_browser_net::Predictor* GetNetworkPredictor(); #if defined(OS_CHROMEOS) virtual void ChangeAppLocale(const std::string& locale, AppLocaleChangedVia); @@ -290,6 +291,8 @@ class ProfileImpl : public Profile, Profile::Delegate* delegate_; + chrome_browser_net::Predictor* predictor_; + DISALLOW_COPY_AND_ASSIGN(ProfileImpl); }; diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc index 8ef2f4a..c523c25 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc @@ -12,6 +12,8 @@ #include "chrome/browser/io_thread.h" #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_network_delegate.h" +#include "chrome/browser/net/connect_interceptor.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/sqlite_origin_bound_cert_store.h" #include "chrome/browser/net/sqlite_persistent_cookie_store.h" #include "chrome/browser/prefs/pref_member.h" @@ -25,6 +27,7 @@ #include "net/base/origin_bound_cert_service.h" #include "net/ftp/ftp_network_layer.h" #include "net/http/http_cache.h" +#include "net/url_request/url_request_job_factory.h" ProfileImplIOData::Handle::Handle(Profile* profile) : io_data_(new ProfileImplIOData), @@ -43,6 +46,8 @@ ProfileImplIOData::Handle::~Handle() { if (extensions_request_context_getter_) extensions_request_context_getter_->CleanupOnUIThread(); + io_data_->predictor_->ShutdownOnUIThread(profile_->GetPrefs()); + // Clean up all isolated app request contexts. for (ChromeURLRequestContextGetterMap::iterator iter = app_request_context_getter_map_.begin(); @@ -54,16 +59,22 @@ ProfileImplIOData::Handle::~Handle() { io_data_->ShutdownOnUIThread(); } -void ProfileImplIOData::Handle::Init(const FilePath& cookie_path, - const FilePath& origin_bound_cert_path, - const FilePath& cache_path, - int cache_max_size, - const FilePath& media_cache_path, - int media_cache_max_size, - const FilePath& extensions_cookie_path, - const FilePath& app_path) { +void ProfileImplIOData::Handle::Init( + const FilePath& cookie_path, + const FilePath& origin_bound_cert_path, + const FilePath& cache_path, + int cache_max_size, + const FilePath& media_cache_path, + int media_cache_max_size, + const FilePath& extensions_cookie_path, + const FilePath& app_path, + chrome_browser_net::Predictor* predictor, + PrefService* local_state, + IOThread* io_thread) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!io_data_->lazy_params_.get()); + DCHECK(predictor); + LazyParams* lazy_params = new LazyParams; lazy_params->cookie_path = cookie_path; @@ -78,6 +89,11 @@ void ProfileImplIOData::Handle::Init(const FilePath& cookie_path, // Keep track of isolated app path separately so we can use it on demand. io_data_->app_path_ = app_path; + + io_data_->predictor_.reset(predictor); + io_data_->predictor_->InitNetworkPredictor(profile_->GetPrefs(), + local_state, + io_thread); } base::Callback<ChromeURLDataManagerBackend*(void)> @@ -336,6 +352,9 @@ void ProfileImplIOData::LazyInitializeInternal( media_request_context_->set_job_factory(job_factory()); extensions_context->set_job_factory(job_factory()); + job_factory()->AddInterceptor( + new chrome_browser_net::ConnectInterceptor(predictor_.get())); + lazy_params_.reset(); } diff --git a/chrome/browser/profiles/profile_impl_io_data.h b/chrome/browser/profiles/profile_impl_io_data.h index 6a5f531..e0f61ff 100644 --- a/chrome/browser/profiles/profile_impl_io_data.h +++ b/chrome/browser/profiles/profile_impl_io_data.h @@ -12,6 +12,10 @@ #include "base/memory/ref_counted.h" #include "chrome/browser/profiles/profile_io_data.h" +namespace chrome_browser_net { +class Predictor; +} + namespace net { class HttpTransactionFactory; } // namespace net @@ -36,7 +40,10 @@ class ProfileImplIOData : public ProfileIOData { const FilePath& media_cache_path, int media_cache_max_size, const FilePath& extensions_cookie_path, - const FilePath& app_path); + const FilePath& app_path, + chrome_browser_net::Predictor* predictor, + PrefService* local_state, + IOThread* io_thread); base::Callback<ChromeURLDataManagerBackend*(void)> GetChromeURLDataManagerBackendGetter() const; @@ -127,6 +134,8 @@ class ProfileImplIOData : public ProfileIOData { mutable scoped_ptr<net::HttpTransactionFactory> main_http_factory_; mutable scoped_ptr<net::HttpTransactionFactory> media_http_factory_; + mutable scoped_ptr<chrome_browser_net::Predictor> predictor_; + // Parameters needed for isolated apps. FilePath app_path_; mutable bool clear_local_state_on_exit_; diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc index b7d438e..26e84bf 100644 --- a/chrome/browser/renderer_host/chrome_render_message_filter.cc +++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc @@ -17,7 +17,7 @@ #include "chrome/browser/metrics/histogram_synchronizer.h" #include "chrome/browser/nacl_host/nacl_process_host.h" #include "chrome/browser/net/chrome_url_request_context.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/task_manager/task_manager.h" @@ -196,7 +196,8 @@ void ChromeRenderMessageFilter::OnLaunchNaCl( void ChromeRenderMessageFilter::OnDnsPrefetch( const std::vector<std::string>& hostnames) { - chrome_browser_net::DnsPrefetchList(hostnames); + if (profile_->GetNetworkPredictor()) + profile_->GetNetworkPredictor()->DnsPrefetchList(hostnames); } void ChromeRenderMessageFilter::OnRendererHistograms( @@ -514,7 +515,9 @@ void ChromeRenderMessageFilter::OnCanTriggerClipboardWrite(const GURL& url, void ChromeRenderMessageFilter::OnClearPredictorCache(int* result) { // This function is disabled unless the user has enabled // benchmarking extensions. - chrome_browser_net::ClearPredictorCache(); + chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor(); + if (predictor) + predictor->DiscardAllResults(); *result = 0; } diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc index 7390cec..ada4a3b 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc @@ -7,7 +7,7 @@ #include "base/command_line.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" @@ -24,8 +24,9 @@ #include "content/common/view_messages.h" ChromeRenderViewHostObserver::ChromeRenderViewHostObserver( - RenderViewHost* render_view_host) - : RenderViewHostObserver(render_view_host) { + RenderViewHost* render_view_host, chrome_browser_net::Predictor* predictor) + : RenderViewHostObserver(render_view_host), + predictor_(predictor) { InitRenderViewHostForExtensions(); } @@ -39,9 +40,11 @@ void ChromeRenderViewHostObserver::RenderViewHostInitialized() { void ChromeRenderViewHostObserver::Navigate( const ViewMsg_Navigate_Params& params) { const GURL& url = params.url; + if (!predictor_) + return; if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame) && - (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme))) - chrome_browser_net::PreconnectUrlAndSubresources(url); + (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme))) + predictor_->PreconnectUrlAndSubresources(url); } bool ChromeRenderViewHostObserver::OnMessageReceived( diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.h b/chrome/browser/renderer_host/chrome_render_view_host_observer.h index 411a953..5a5eaa8 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.h +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.h @@ -8,13 +8,18 @@ #include "content/browser/renderer_host/render_view_host_observer.h" +namespace chrome_browser_net { +class Predictor; +} + class Extension; // This class holds the Chrome specific parts of RenderViewHost, and has the // same lifetime. class ChromeRenderViewHostObserver : public RenderViewHostObserver { public: - explicit ChromeRenderViewHostObserver(RenderViewHost* render_view_host); + ChromeRenderViewHostObserver(RenderViewHost* render_view_host, + chrome_browser_net::Predictor* predictor); virtual ~ChromeRenderViewHostObserver(); // RenderViewHostObserver overrides. @@ -35,6 +40,8 @@ class ChromeRenderViewHostObserver : public RenderViewHostObserver { void OnDomOperationResponse(const std::string& json_string, int automation_id); + chrome_browser_net::Predictor* predictor_; + DISALLOW_COPY_AND_ASSIGN(ChromeRenderViewHostObserver); }; diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc index 42d6dae..543d2fb 100644 --- a/chrome/browser/ui/browser_init.cc +++ b/chrome/browser/ui/browser_init.cc @@ -31,7 +31,7 @@ #include "chrome/browser/extensions/pack_extension_job.h" #include "chrome/browser/first_run/first_run.h" #include "chrome/browser/infobars/infobar_tab_helper.h" -#include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/predictor.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" @@ -668,8 +668,10 @@ bool BrowserInit::LaunchWithProfile::Launch( if (command_line_.HasSwitch(switches::kDnsLogDetails)) chrome_browser_net::EnablePredictorDetailedLog(true); - if (command_line_.HasSwitch(switches::kDnsPrefetchDisable)) - chrome_browser_net::EnablePredictor(false); + if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) && + profile->GetNetworkPredictor()) { + profile->GetNetworkPredictor()->EnablePredictor(false); + } if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit)) base::StatisticsRecorder::set_dump_on_exit(true); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 7740237..24a44a5 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1518,8 +1518,6 @@ 'browser/net/preconnect.h', 'browser/net/predictor.cc', 'browser/net/predictor.h', - 'browser/net/predictor_api.cc', - 'browser/net/predictor_api.h', 'browser/net/pref_proxy_config_service.cc', 'browser/net/pref_proxy_config_service.h', 'browser/net/proxy_service_factory.cc', |