diff options
31 files changed, 629 insertions, 1241 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc index 98da003..5cea1c0 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" @@ -1083,11 +1083,8 @@ void AutocompleteEditModel::DoPrerender(const AutocompleteMatch& match) { void AutocompleteEditModel::DoPreconnect(const AutocompleteMatch& match) { if (!match.destination_url.SchemeIs(chrome::kExtensionScheme)) { // Warm up DNS Prefetch cache, or preconnect to a search service. - if (profile_->GetNetworkPredictor()) { - profile_->GetNetworkPredictor()->AnticipateOmniboxUrl( - match.destination_url, - IsPreconnectable(match.type)); - } + chrome_browser_net::AnticipateOmniboxUrl(match.destination_url, + IsPreconnectable(match.type)); // 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 59e1d44..7c41ba4 100644 --- a/chrome/browser/autofill/autofill_browsertest.cc +++ b/chrome/browser/autofill/autofill_browsertest.cc @@ -12,6 +12,7 @@ #include "chrome/browser/autofill/autofill_common_test.h" #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/personal_data_manager.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 340c604..c4158c2 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/plugin_prefs.h" #include "chrome/browser/profiles/profile.h" @@ -703,20 +703,18 @@ 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, predictor)); + NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread)); } - void StartOnIOThread(chrome_browser_net::Predictor* predictor) { + void StartOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); std::string data; AppendHeader(&data, 0, "About DNS"); AppendBody(&data); - chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data); + chrome_browser_net::PredictorGetHtmlInfo(&data); AppendFooter(&data); BrowserThread::PostTask( diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index a9063cf..be43c54 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -55,7 +55,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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" #include "chrome/browser/plugin_prefs.h" #include "chrome/browser/policy/browser_policy_connector.h" @@ -1172,88 +1172,6 @@ void BrowserMainParts::ConnectBackupJobsFieldTrial() { } } -void BrowserMainParts::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 BrowserMainParts::SuggestPrefixFieldTrial() { @@ -1354,7 +1272,6 @@ void BrowserMainParts::SetupFieldTrials(bool metrics_recording_enabled, ConnectBackupJobsFieldTrial(); SuggestPrefixFieldTrial(); WarmConnectionFieldTrial(); - PredictorFieldTrial(); } // ----------------------------------------------------------------------------- @@ -1927,6 +1844,20 @@ int BrowserMain(const MainFunctionParams& parameters) { 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) base::win::ScopedCOMInitializer com_initializer; diff --git a/chrome/browser/browser_main.h b/chrome/browser/browser_main.h index b6ee4f2..e4c1da21 100644 --- a/chrome/browser/browser_main.h +++ b/chrome/browser/browser_main.h @@ -142,10 +142,6 @@ class 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(); - // Used to initialize NSPR where appropriate. virtual void InitializeSSL() = 0; diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 39bf561..0a8a385 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -36,6 +36,7 @@ #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 c18d22f..92683fe 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -38,6 +38,7 @@ #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) @@ -139,6 +140,10 @@ 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 138ce0c..56bbd7d 100644 --- a/chrome/browser/browsing_data_remover.cc +++ b/chrome/browser/browsing_data_remover.cc @@ -19,7 +19,6 @@ #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/prerender/prerender_manager.h" @@ -366,13 +365,7 @@ void BrowsingDataRemover::ClearNetworkingHistory(IOThread* io_thread) { // This function should be called on the IO thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - io_thread->ClearHostCache(); - - chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor(); - if (predictor) { - predictor->DiscardInitialNavigationHistory(); - predictor->DiscardAllResults(); - } + io_thread->ClearNetworkingHistory(); // Notify the UI thread that we are done. BrowserThread::PostTask( diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 10fa779..f6f3f13 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -102,13 +102,7 @@ namespace chrome { void ChromeContentBrowserClient::RenderViewHostCreated( RenderViewHost* 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 ChromeRenderViewHostObserver(render_view_host); new DevToolsHandler(render_view_host); new ExtensionMessageHandler(render_view_host); } diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 7bea24f..b8f3557 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -24,6 +24,7 @@ #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" @@ -343,6 +344,8 @@ 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. @@ -384,6 +387,33 @@ 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_) { @@ -392,6 +422,15 @@ 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. @@ -488,6 +527,21 @@ 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_; @@ -544,6 +598,51 @@ 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 9340f39..a2b42b5 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -14,6 +14,7 @@ #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; @@ -28,6 +29,11 @@ namespace base { class ListValue; } +namespace chrome_browser_net { +class ConnectInterceptor; +class Predictor; +} // namespace chrome_browser_net + namespace net { class CertVerifier; class CookieStore; @@ -103,13 +109,28 @@ 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(); - // 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(); + // 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(); protected: virtual void Init(); @@ -131,6 +152,21 @@ 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(); @@ -172,6 +208,14 @@ 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 0ea6dbd..5938df2 100644 --- a/chrome/browser/net/connect_interceptor.cc +++ b/chrome/browser/net/connect_interceptor.cc @@ -4,9 +4,8 @@ #include "chrome/browser/net/connect_interceptor.h" -#include "chrome/browser/net/predictor.h" +#include "chrome/browser/net/predictor_api.h" #include "net/base/load_flags.h" -#include "net/url_request/url_request.h" namespace chrome_browser_net { @@ -17,24 +16,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(Predictor* predictor) +ConnectInterceptor::ConnectInterceptor() : timed_cache_(base::TimeDelta::FromSeconds( - kMaxUnusedSocketLifetimeSecondsWithoutAGet)), - predictor_(predictor) { - DCHECK(predictor); + kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { + net::URLRequest::Deprecated::RegisterRequestInterceptor(this); } ConnectInterceptor::~ConnectInterceptor() { + net::URLRequest::Deprecated::UnregisterRequestInterceptor(this); } net::URLRequestJob* ConnectInterceptor::MaybeIntercept( - net::URLRequest* request) const { + net::URLRequest* request) { 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. - predictor_->LearnAboutInitialNavigation(request_scheme_host); + LearnAboutInitialNavigation(request_scheme_host); bool redirected_host = false; if (request->referrer().empty()) { @@ -56,8 +55,7 @@ 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. - predictor_->LearnFromNavigation(original_scheme_host, - request_scheme_host); + LearnFromNavigation(original_scheme_host, request_scheme_host); } } } @@ -66,8 +64,7 @@ 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)) - predictor_->LearnFromNavigation(referring_scheme_host, - request_scheme_host); + 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. @@ -83,18 +80,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) - predictor_->PredictFrameSubresources(request_scheme_host); + PredictFrameSubresources(request_scheme_host); return NULL; } net::URLRequestJob* ConnectInterceptor::MaybeInterceptResponse( - net::URLRequest* request) const { + net::URLRequest* request) { return NULL; } net::URLRequestJob* ConnectInterceptor::MaybeInterceptRedirect( - const GURL& location, - net::URLRequest* request) const { + net::URLRequest* request, + const GURL& location) { return NULL; } @@ -106,7 +103,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) const { +bool ConnectInterceptor::TimedCache::WasRecentlySeen(const GURL& url) { DCHECK_EQ(url.GetWithEmptyPath(), url); // Evict any overly old entries. base::TimeTicks now = base::TimeTicks::Now(); @@ -120,7 +117,7 @@ bool ConnectInterceptor::TimedCache::WasRecentlySeen(const GURL& url) const { return mru_cache_.end() != mru_cache_.Peek(url); } -void ConnectInterceptor::TimedCache::SetRecentlySeen(const GURL& url) const { +void ConnectInterceptor::TimedCache::SetRecentlySeen(const GURL& url) { 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 657700e..eca1c17 100644 --- a/chrome/browser/net/connect_interceptor.h +++ b/chrome/browser/net/connect_interceptor.h @@ -6,34 +6,30 @@ #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::URLRequestJobFactory::Interceptor { +class ConnectInterceptor : public net::URLRequest::Interceptor { public: // Construction includes registration as an URL. - explicit ConnectInterceptor(Predictor* predictor); + ConnectInterceptor(); // 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) const OVERRIDE; - virtual net::URLRequestJob* MaybeInterceptResponse( - net::URLRequest* request) const OVERRIDE; - virtual net::URLRequestJob* MaybeInterceptRedirect( - const GURL& location, net::URLRequest* request) const OVERRIDE; + virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request); + virtual net::URLRequestJob* MaybeInterceptResponse(net::URLRequest* request); + virtual net::URLRequestJob* MaybeInterceptRedirect(net::URLRequest* request, + const GURL& location); private: // Provide access to local class TimedCache for testing. @@ -52,20 +48,17 @@ class ConnectInterceptor : public net::URLRequestJobFactory::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) const; + bool WasRecentlySeen(const GURL& url); // Adds the given url to the cache, where it will remain for max_duration_. - void SetRecentlySeen(const GURL& url) const; + void SetRecentlySeen(const GURL& url); 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; - // 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_; + UrlMruTimedCache mru_cache_; // The longest time an entry can persist in the cache, and still be found. const base::TimeDelta max_duration_; @@ -73,7 +66,6 @@ class ConnectInterceptor : public net::URLRequestJobFactory::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 5655f54..abfcc46 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" @@ -29,14 +29,10 @@ void SetEnforceThrottlingOnThrottlerManager(bool enforce) { } NetPrefObserver::NetPrefObserver(PrefService* prefs, - prerender::PrerenderManager* prerender_manager, - chrome_browser_net::Predictor* predictor) - : prerender_manager_(prerender_manager), - predictor_(predictor) { + prerender::PrerenderManager* prerender_manager) + : prerender_manager_(prerender_manager) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(prefs); - DCHECK(predictor); - network_prediction_enabled_.Init(prefs::kNetworkPredictionEnabled, prefs, this); spdy_disabled_.Init(prefs::kDisableSpdy, prefs, this); @@ -61,7 +57,7 @@ void NetPrefObserver::Observe(int type, void NetPrefObserver::ApplySettings(const std::string* pref_name) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - predictor_->EnablePredictor(*network_prediction_enabled_); + chrome_browser_net::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 4d2ca47..b7b60c0 100644 --- a/chrome/browser/net/net_pref_observer.h +++ b/chrome/browser/net/net_pref_observer.h @@ -14,10 +14,6 @@ class Profile; -namespace chrome_browser_net { -class Predictor; -} - namespace prerender { class PrerenderManager; } @@ -31,8 +27,7 @@ class NetPrefObserver : public NotificationObserver { // |prerender_manager| may be NULL. If not, |*prerender_manager| must // outlive this. NetPrefObserver(PrefService* prefs, - prerender::PrerenderManager* prerender_manager, - chrome_browser_net::Predictor* predictor); + prerender::PrerenderManager* prerender_manager); virtual ~NetPrefObserver(); // NotificationObserver @@ -50,7 +45,6 @@ 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 358c162..ffc5e57 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -9,23 +9,12 @@ #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" @@ -64,25 +53,6 @@ const TimeDelta Predictor::kDurationBetweenTrimmingIncrements = TimeDelta::FromSeconds(15); // static const size_t Predictor::kUrlsTrimmedPerIncrement = 5u; -// static -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; -// static -const int Predictor::kTypicalSpeculativeGroupSize = 8; -// static -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: @@ -129,88 +99,76 @@ class Predictor::LookupRequest { DISALLOW_COPY_AND_ASSIGN(LookupRequest); }; -Predictor::Predictor(bool preconnect_enabled) - : initial_observer_(NULL), - predictor_enabled_(true), - peak_pending_lookups_(0), +Predictor::Predictor(net::HostResolver* host_resolver, + TimeDelta max_dns_queue_delay, + size_t max_concurrent, + bool preconnect_enabled) + : peak_pending_lookups_(0), shutdown_(false), - max_concurrent_dns_lookups_(g_max_parallel_resolves), - max_dns_queue_delay_( - TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), - host_resolver_(NULL), + max_concurrent_dns_lookups_(max_concurrent), + max_dns_queue_delay_(max_dns_queue_delay), + host_resolver_(host_resolver), preconnect_enabled_(preconnect_enabled), consecutive_omnibox_preconnect_count_(0), - next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + next_trim_time_(base::TimeTicks::Now() + kDurationBetweenTrimmings), + ALLOW_THIS_IN_INITIALIZER_LIST(trim_task_factory_(this)) { } Predictor::~Predictor() { - // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the - // ProfileManagerTest has been updated with a mock profile. DCHECK(shutdown_); } -// static -Predictor* Predictor::CreatePredictor( - bool preconnect_enabled, bool simple_shutdown) { - if (simple_shutdown) - return new SimpleShutdownPredictor(preconnect_enabled); - return new Predictor(preconnect_enabled); -} +void Predictor::Shutdown() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!shutdown_); + shutdown_ = true; -void Predictor::RegisterUserPrefs(PrefService* user_prefs) { - user_prefs->RegisterListPref(prefs::kDnsPrefetchingStartupList, - PrefService::UNSYNCABLE_PREF); - user_prefs->RegisterListPref(prefs::kDnsPrefetchingHostReferralList, - PrefService::UNSYNCABLE_PREF); + std::set<LookupRequest*>::iterator it; + for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it) + delete *it; } -// --------------------- Start UI methods. ------------------------------------ - -void Predictor::InitNetworkPredictor(PrefService* user_prefs, - PrefService* local_state, - IOThread* io_thread) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - 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); +// 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); } +} - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind( - &Predictor::FinalizeInitializationOnIOThread, - base::Unretained(this), - urls, referral_list, - io_thread)); +// 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()); + + referrers_[referring_url].SuggestHost(target_url); + // Possibly do some referrer trimming. + TrimReferrers(); } +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; @@ -274,16 +232,11 @@ void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&Predictor::Resolve, base::Unretained(this), - CanonicalizeUrl(url), motivation)); + NewRunnableMethod(this, &Predictor::Resolve, 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); @@ -294,186 +247,63 @@ void Predictor::PreconnectUrlAndSubresources(const GURL& url) { } } -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); - +void Predictor::PredictFrameSubresources(const GURL& url) { + DCHECK_EQ(url.GetWithEmptyPath(), url); + // Add one pass through the message loop to allow current navigation to + // proceed. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&Predictor::Shutdown, base::Unretained(this))); -} - -// ---------------------- End UI methods. ------------------------------------- - -// --------------------- Start IO methods. ------------------------------------ - -void Predictor::Shutdown() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!shutdown_); - shutdown_ = true; - - STLDeleteElements(&pending_lookups_); + NewRunnableMethod(this, &Predictor::PrepareFrameSubresources, url)); } -void Predictor::DiscardAllResults() { +void Predictor::PrepareFrameSubresources(const GURL& url) { 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(); + 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; } - // Now every result_ is either resolved, or is being resolved - // (see LookupRequest). - // 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; + 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); } - 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. @@ -588,11 +418,6 @@ 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; @@ -628,306 +453,81 @@ void Predictor::GetHtmlInfo(std::string* output) { "Preresolving DNS records revealed non-existence for ", brief, output); } -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. -} - -void Predictor::SerializeReferrers(base::ListValue* referral_list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - referral_list->Clear(); - referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION)); - 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); - } -} - -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 == PREDICTOR_REFERRER_VERSION) { - 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); - } - } -} - -void Predictor::DeserializeReferrersThenDelete( - base::ListValue* referral_list) { - DeserializeReferrers(*referral_list); - delete referral_list; -} - -void Predictor::DiscardInitialNavigationHistory() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (initial_observer_.get()) - initial_observer_->DiscardInitialNavigationHistory(); -} - -void Predictor::FinalizeInitializationOnIOThread( - const UrlList& startup_urls, - base::ListValue* referral_list, - IOThread* io_thread) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - initial_observer_.reset(new InitialObserver()); - host_resolver_ = io_thread->globals()->host_resolver.get(); - - // 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); -} - -void Predictor::DnsPrefetchMotivatedList( - const UrlList& urls, +UrlInfo* Predictor::AppendToResolutionQueue( + const GURL& url, UrlInfo::ResolutionMotivation motivation) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || - BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!predictor_enabled_) - return; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(url.has_host()); - if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - ResolveList(urls, motivation); - } else { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&Predictor::ResolveList, base::Unretained(this), - urls, motivation)); - } -} + if (shutdown_) + return NULL; -//----------------------------------------------------------------------------- -// Functions to handle saving of hostnames from one session to the next, to -// expedite startup times. + 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 :-/ -static void SaveDnsPrefetchStateForNextStartupAndTrimOnIOThread( - base::ListValue* startup_list, - base::ListValue* referral_list, - base::WaitableEvent* completion, - Predictor* predictor) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(info->HasUrl(url)); - if (NULL == predictor) { - completion->Signal(); - return; + if (!info->NeedsDnsUpdate()) { + info->DLogResultsStats("DNS PrefetchNotUpdated"); + return NULL; } - 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(); - } + info->SetQueuedState(motivation); + work_queue_.Push(url, motivation); + StartSomeQueuedResolutions(); + return info; } -void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim( - base::ListValue* startup_list, - base::ListValue* referral_list, - base::WaitableEvent* completion) { +void Predictor::StartSomeQueuedResolutions() { 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)); - } -} + 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::EnablePredictorOnIOThread(bool enable) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - predictor_enabled_ = enable; -} + if (CongestionControlPerformed(info)) { + DCHECK(work_queue_.IsEmpty()); + return; + } -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)); + 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; + } } } -enum SubresourceValue { - PRECONNECTION, - PRERESOLUTION, - TOO_NEW, - SUBRESOURCE_VALUE_MAX -}; - -void Predictor::PrepareFrameSubresources(const GURL& url) { +bool Predictor::CongestionControlPerformed(UrlInfo* info) { 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); + // 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::OnLookupFinished(LookupRequest* request, const GURL& url, @@ -956,79 +556,96 @@ void Predictor::LookupFinished(LookupRequest* request, const GURL& url, } } -UrlInfo* Predictor::AppendToResolutionQueue( - const GURL& url, - UrlInfo::ResolutionMotivation motivation) { +void Predictor::DiscardAllResults() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(url.has_host()); - - if (shutdown_) - return NULL; + // Delete anything listed so far in this session that shows in about:dns. + referrers_.clear(); - 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 :-/ - DCHECK(info->HasUrl(url)); + // 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). - if (!info->NeedsDnsUpdate()) { - info->DLogResultsStats("DNS PrefetchNotUpdated"); - return NULL; + // 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; } +} - info->SetQueuedState(motivation); - work_queue_.Push(url, motivation); - StartSomeQueuedResolutions(); - return info; +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. } -bool Predictor::CongestionControlPerformed(UrlInfo* info) { +void Predictor::SerializeReferrers(ListValue* referral_list) { 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(); + referral_list->Clear(); + referral_list->Append(new base::FundamentalValue(PREDICTOR_REFERRER_VERSION)); + 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); } - return true; } -void Predictor::StartSomeQueuedResolutions() { +void Predictor::DeserializeReferrers(const 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 == PREDICTOR_REFERRER_VERSION) { + 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; + } - 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(); - - if (CongestionControlPerformed(info)) { - DCHECK(work_queue_.IsEmpty()); - return; - } + Value* subresource_list; + if (!motivator->Get(1, &subresource_list)) { + NOTREACHED(); + 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; + referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list); } } } @@ -1061,8 +678,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()); } @@ -1081,9 +698,7 @@ void Predictor::IncrementalTrimReferrers(bool trim_all_now) { PostIncrementalTrimTask(); } -// ---------------------- End IO methods. ------------------------------------- - -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ Predictor::HostNameQueue::HostNameQueue() { } @@ -1119,69 +734,15 @@ GURL Predictor::HostNameQueue::Pop() { return url; } -//----------------------------------------------------------------------------- -// Member definitions for InitialObserver class. - -Predictor::InitialObserver::InitialObserver() { -} - -Predictor::InitialObserver::~InitialObserver() { +void Predictor::DeserializeReferrersThenDelete(ListValue* referral_list) { + DeserializeReferrers(*referral_list); + delete referral_list; } -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) { @@ -1207,8 +768,5 @@ GURL Predictor::CanonicalizeUrl(const GURL& url) { return GURL(scheme + "://" + url.host() + colon_plus_port); } -void SimpleShutdownPredictor::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 89e0184..f1f0bcb 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/scoped_ptr.h" +#include "base/memory/ref_counted.h" #include "chrome/browser/net/url_info.h" #include "chrome/browser/net/referrer.h" #include "chrome/common/net/predictor_common.h" @@ -37,97 +37,29 @@ 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; -// 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 { +// 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> { public: // A version number for prefs that are saved. This should be incremented when // we change the format so that we discard old data. enum { PREDICTOR_REFERRER_VERSION = 2 }; - // 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|. - 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. - - 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. + Predictor(net::HostResolver* host_resolver, + base::TimeDelta max_queue_delay_ms, size_t max_concurrent, + bool preconnect_enabled); // Cancel pending requests and prevent new ones from being made. void Shutdown(); @@ -142,8 +74,23 @@ class 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); - void Resolve(const GURL& url, UrlInfo::ResolutionMotivation motivation); + // 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); // 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. @@ -151,14 +98,11 @@ class 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 for about:dns. + // domains. void GetHtmlInfo(std::string* output); // Discards any referrer for which all the suggested host names are currently @@ -180,82 +124,21 @@ class Predictor { void DeserializeReferrersThenDelete(base::ListValue* referral_list); - void DiscardInitialNavigationHistory(); - - void FinalizeInitializationOnIOThread( - const std::vector<GURL>& urls_to_prefetch, - base::ListValue* referral_list, - IOThread* io_thread); - - // 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. + // For unit test code only. 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_; - } + bool preconnect_enabled() const { return preconnect_enabled_; } + // 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); private: + friend class base::RefCountedThreadSafe<Predictor>; FRIEND_TEST_ALL_PREFIXES(PredictorTest, BenefitLookupTest); FRIEND_TEST_ALL_PREFIXES(PredictorTest, ShutdownWhenResolutionIsPendingTest); FRIEND_TEST_ALL_PREFIXES(PredictorTest, SingleLookupTest); @@ -295,38 +178,6 @@ class 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. @@ -355,6 +206,13 @@ class 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 { @@ -374,13 +232,6 @@ class 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); @@ -426,14 +277,6 @@ class 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_; @@ -459,7 +302,7 @@ class Predictor { const base::TimeDelta max_dns_queue_delay_; // The host resolver we warm DNS entries for. - net::HostResolver* host_resolver_; + net::HostResolver* const host_resolver_; // Are we currently using preconnection, rather than just DNS resolution, for // subresources and omni-box search URLs. @@ -491,20 +334,11 @@ class Predictor { // A time after which we need to do more trimming of referrers. base::TimeTicks next_trim_time_; - scoped_ptr<ScopedRunnableMethodFactory<Predictor> > trim_task_factory_; + ScopedRunnableMethodFactory<Predictor> trim_task_factory_; DISALLOW_COPY_AND_ASSIGN(Predictor); }; -// This version of hte predictor is used for testing -class SimpleShutdownPredictor : public Predictor { - public: - explicit SimpleShutdownPredictor(bool preconnect_enabled) - : Predictor(preconnect_enabled) {} - virtual ~SimpleShutdownPredictor() {} - 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 e5c7196..58aab26 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/url_info.h" #include "chrome/common/net/predictor_common.h" #include "content/browser/browser_thread.h" @@ -62,9 +62,10 @@ class WaitForResolutionHelper { class PredictorTest : public testing::Test { public: PredictorTest() - : ui_thread_(BrowserThread::UI, &loop_), - io_thread_(BrowserThread::IO, &loop_), - host_resolver_(new net::MockCachingHostResolver()) { + : io_thread_(BrowserThread::IO, &loop_), + host_resolver_(new net::MockCachingHostResolver()), + default_max_queueing_delay_(TimeDelta::FromMilliseconds( + PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) { } protected: @@ -72,10 +73,6 @@ 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_|. @@ -98,19 +95,26 @@ 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). - MessageLoopForUI loop_; - BrowserThread ui_thread_; + MessageLoop loop_; 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) { - Predictor testing_master(true); - testing_master.Shutdown(); + scoped_refptr<Predictor> testing_master( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); + testing_master->Shutdown(); } @@ -119,22 +123,25 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { new net::WaitingHostResolverProc(NULL)); host_resolver_->Reset(resolver_proc); - Predictor testing_master(true); - testing_master.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> testing_master( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); 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(); @@ -142,8 +149,11 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { } TEST_F(PredictorTest, SingleLookupTest) { - Predictor testing_master(true); - testing_master.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> testing_master( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL goog("http://www.google.com:80"); @@ -152,27 +162,30 @@ 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"); - Predictor testing_master(true); - testing_master.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> testing_master( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL goog("http://www.google.com:80"), goog2("http://gmail.google.com.com:80"), @@ -192,34 +205,37 @@ 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"); - Predictor testing_master(true); - testing_master.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> testing_master( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); UrlList names; for (int i = 0; i < 100; i++) @@ -228,17 +244,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(); } //------------------------------------------------------------------------------ @@ -336,25 +352,30 @@ static bool GetDataFromSerialization(const GURL& motivation, // Make sure nil referral lists really have no entries, and no latency listed. TEST_F(PredictorTest, ReferrerSerializationNilTest) { - Predictor predictor(true); - predictor.SetHostResolver(host_resolver_.get()); - + scoped_refptr<Predictor> predictor( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); 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) { - Predictor predictor(true); - predictor.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> predictor( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); const GURL motivation_url("http://www.google.com:91"); const GURL subresource_url("http://icons.google.com:90"); const double kUseRate = 23.4; @@ -363,17 +384,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. @@ -388,8 +409,11 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { // Make sure the Trim() functionality works as expected. TEST_F(PredictorTest, ReferrerSerializationTrimTest) { - Predictor predictor(true); - predictor.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> predictor( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); GURL motivation_url("http://www.google.com:110"); GURL icon_subresource_url("http://icons.google.com:111"); @@ -403,10 +427,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( @@ -421,8 +445,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)); @@ -433,8 +457,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)); @@ -444,8 +468,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)); @@ -456,8 +480,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( @@ -465,7 +489,7 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { EXPECT_FALSE(GetDataFromSerialization( motivation_url, img_subresource_url, recovered_referral_list, &rate)); - predictor.Shutdown(); + predictor->Shutdown(); } @@ -577,24 +601,27 @@ TEST_F(PredictorTest, CanonicalizeUrl) { } TEST_F(PredictorTest, DiscardPredictorResults) { - Predictor predictor(true); - predictor.SetHostResolver(host_resolver_.get()); + scoped_refptr<Predictor> predictor( + new Predictor(host_resolver_.get(), + default_max_queueing_delay_, + PredictorInit::kMaxSpeculativeParallelResolves, + false)); 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 fc9343e..3307aa1 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.h" +#include "chrome/browser/net/predictor_api.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" @@ -145,7 +145,7 @@ void RegisterUserPrefs(PrefService* user_prefs) { BookmarkModel::RegisterUserPrefs(user_prefs); Browser::RegisterUserPrefs(user_prefs); PasswordManager::RegisterUserPrefs(user_prefs); - chrome_browser_net::Predictor::RegisterUserPrefs(user_prefs); + chrome_browser_net::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 fe1ac1d..6020735 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -877,7 +877,3 @@ Profile* Profile::CreateOffTheRecordProfile() { #endif return new OffTheRecordProfileImpl(this); } - -chrome_browser_net::Predictor* Profile::GetNetworkPredictor() { - return NULL; -} diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h index fc3eedc..3165b33 100644 --- a/chrome/browser/profiles/profile.h +++ b/chrome/browser/profiles/profile.h @@ -48,10 +48,6 @@ namespace speech_input { class SpeechRecognizer; } -namespace chrome_browser_net { -class Predictor; -} - class AutocompleteClassifier; class BookmarkModel; class BrowserSignin; @@ -555,8 +551,6 @@ 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 42e574a..7790c47 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -47,7 +47,6 @@ #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" @@ -307,8 +306,7 @@ ProfileImpl::ProfileImpl(const FilePath& path, #if defined(OS_WIN) checked_instant_promo_(false), #endif - delegate_(delegate), - predictor_(NULL) { + delegate_(delegate) { DCHECK(!path.empty()) << "Using an empty path will attempt to write " << "profile files to the root directory!"; @@ -316,13 +314,6 @@ 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(), @@ -443,12 +434,9 @@ 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, predictor_, - g_browser_process->local_state(), - g_browser_process->io_thread()); + extensions_cookie_path, app_path); // Creation has been finished. if (delegate_) @@ -907,7 +895,7 @@ void ProfileImpl::OnPrefsLoaded(bool success) { DCHECK(!net_pref_observer_.get()); net_pref_observer_.reset( - new NetPrefObserver(prefs_.get(), GetPrerenderManager(), predictor_)); + new NetPrefObserver(prefs_.get(), GetPrerenderManager())); DoFinalInit(); } @@ -1730,10 +1718,6 @@ 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 be71059..aa1fee1 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -124,7 +124,6 @@ class ProfileImpl : public Profile, virtual PromoCounter* GetInstantPromoCounter(); virtual BrowserSignin* GetBrowserSignin(); virtual ChromeURLDataManager* GetChromeURLDataManager(); - virtual chrome_browser_net::Predictor* GetNetworkPredictor(); #if defined(OS_CHROMEOS) virtual void ChangeAppLocale(const std::string& locale, AppLocaleChangedVia); @@ -294,8 +293,6 @@ 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 b1698f4..37ed4d1 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc @@ -12,8 +12,6 @@ #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" @@ -27,7 +25,6 @@ #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), @@ -46,8 +43,6 @@ 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(); @@ -59,22 +54,16 @@ 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, - chrome_browser_net::Predictor* predictor, - PrefService* local_state, - IOThread* io_thread) { +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) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!io_data_->lazy_params_.get()); - DCHECK(predictor); - LazyParams* lazy_params = new LazyParams; lazy_params->cookie_path = cookie_path; @@ -89,11 +78,6 @@ void ProfileImplIOData::Handle::Init( // 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)> @@ -352,9 +336,6 @@ 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 e0f61ff..6a5f531 100644 --- a/chrome/browser/profiles/profile_impl_io_data.h +++ b/chrome/browser/profiles/profile_impl_io_data.h @@ -12,10 +12,6 @@ #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 @@ -40,10 +36,7 @@ class ProfileImplIOData : public ProfileIOData { 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); + const FilePath& app_path); base::Callback<ChromeURLDataManagerBackend*(void)> GetChromeURLDataManagerBackendGetter() const; @@ -134,8 +127,6 @@ 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 0c90d4b..5cc1293 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/task_manager/task_manager.h" @@ -191,8 +191,7 @@ void ChromeRenderMessageFilter::OnLaunchNaCl( void ChromeRenderMessageFilter::OnDnsPrefetch( const std::vector<std::string>& hostnames) { - if (profile_->GetNetworkPredictor()) - profile_->GetNetworkPredictor()->DnsPrefetchList(hostnames); + chrome_browser_net::DnsPrefetchList(hostnames); } void ChromeRenderMessageFilter::OnRendererHistograms( @@ -521,9 +520,7 @@ 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::Predictor* predictor = profile_->GetNetworkPredictor(); - if (predictor) - predictor->DiscardAllResults(); + chrome_browser_net::ClearPredictorCache(); *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 05347ac..20eb5a5 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.h" +#include "chrome/browser/net/predictor_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" @@ -24,9 +24,8 @@ #include "content/common/view_messages.h" ChromeRenderViewHostObserver::ChromeRenderViewHostObserver( - RenderViewHost* render_view_host, chrome_browser_net::Predictor* predictor) - : RenderViewHostObserver(render_view_host), - predictor_(predictor) { + RenderViewHost* render_view_host) + : RenderViewHostObserver(render_view_host) { InitRenderViewHostForExtensions(); } @@ -40,11 +39,9 @@ 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))) - predictor_->PreconnectUrlAndSubresources(url); + (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme))) + chrome_browser_net::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 5a5eaa8..411a953 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.h +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.h @@ -8,18 +8,13 @@ #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: - ChromeRenderViewHostObserver(RenderViewHost* render_view_host, - chrome_browser_net::Predictor* predictor); + explicit ChromeRenderViewHostObserver(RenderViewHost* render_view_host); virtual ~ChromeRenderViewHostObserver(); // RenderViewHostObserver overrides. @@ -40,8 +35,6 @@ 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 41f533c..9e26e58 100644 --- a/chrome/browser/ui/browser_init.cc +++ b/chrome/browser/ui/browser_init.cc @@ -30,7 +30,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/pack_extension_job.h" #include "chrome/browser/first_run/first_run.h" -#include "chrome/browser/net/predictor.h" +#include "chrome/browser/net/predictor_api.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" @@ -667,10 +667,8 @@ bool BrowserInit::LaunchWithProfile::Launch( if (command_line_.HasSwitch(switches::kDnsLogDetails)) chrome_browser_net::EnablePredictorDetailedLog(true); - if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) && - profile->GetNetworkPredictor()) { - profile->GetNetworkPredictor()->EnablePredictor(false); - } + if (command_line_.HasSwitch(switches::kDnsPrefetchDisable)) + chrome_browser_net::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 ca15603..d5439de 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1502,6 +1502,8 @@ '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', diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index c66b022..5eaf2bdb 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -178,6 +178,7 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe) { friend class appcache::AppCacheInterceptor; friend class appcache::AppCacheRequestHandlerTest; friend class appcache::AppCacheURLRequestJobTest; + friend class chrome_browser_net::ConnectInterceptor; friend class fileapi::FileSystemDirURLRequestJobTest; friend class fileapi::FileSystemOperationWriteTest; friend class fileapi::FileSystemURLRequestJobTest; |