summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net/dns_global.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/net/dns_global.cc')
-rw-r--r--chrome/browser/net/dns_global.cc463
1 files changed, 463 insertions, 0 deletions
diff --git a/chrome/browser/net/dns_global.cc b/chrome/browser/net/dns_global.cc
new file mode 100644
index 0000000..114f548
--- /dev/null
+++ b/chrome/browser/net/dns_global.cc
@@ -0,0 +1,463 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/net/dns_global.h"
+
+#include <map>
+#include <string>
+
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/session_startup_pref.h"
+#include "chrome/common/notification_types.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/dns_resolution_observer.h"
+
+namespace chrome_browser_net {
+
+static void DiscardAllPrefetchState();
+
+//------------------------------------------------------------------------------
+// This section contains all the globally accessable API entry points for the
+// DNS Prefetching feature.
+//------------------------------------------------------------------------------
+
+// Status of prefetch feature, controlling whether any prefetching is done.
+static bool dns_prefetch_enabled = true;
+
+// Cached inverted copy of the off_the_record pref.
+static bool on_the_record_switch = true;
+
+// Enable/disable Dns prefetch activity (either via command line, or via pref).
+void EnableDnsPrefetch(bool enable) {
+ dns_prefetch_enabled = enable;
+}
+
+void OnTheRecord(bool enable) {
+ if (on_the_record_switch == enable)
+ return;
+ on_the_record_switch = enable;
+ if (on_the_record_switch)
+ DiscardAllPrefetchState(); // Destroy all evidence of our OTR session.
+}
+
+void RegisterPrefs(PrefService* local_state) {
+ local_state->RegisterListPref(prefs::kDnsStartupPrefetchList);
+}
+
+void RegisterUserPrefs(PrefService* user_prefs) {
+ user_prefs->RegisterBooleanPref(prefs::kDnsPrefetchingEnabled, true);
+}
+
+// When enabled, we use the following instance to service all requests in the
+// browser process.
+static DnsMaster* dns_master;
+
+// This API is only used in the browser process.
+void DnsPrefetchList(const NameList& hostnames) {
+ if (!dns_prefetch_enabled)
+ return;
+ DCHECK(NULL != dns_master);
+ if (NULL != dns_master)
+ dns_master->ResolveList(hostnames);
+}
+
+// This API is used by the autocomplete popup box (wher URLs are typed).
+void DnsPrefetchUrlString(const url_canon::UTF16String& url_string) {
+ if (!dns_prefetch_enabled || NULL == dns_master)
+ return;
+ GURL gurl(url_string);
+ if (gurl.is_valid()) {
+ DnsPrefetch(gurl.host());
+ }
+}
+
+// This API currently used after translating a url_string.
+void DnsPrefetch(const std::string& hostname) {
+ if (!dns_prefetch_enabled || NULL == dns_master || !hostname.size())
+ return;
+ dns_master->Resolve(hostname);
+}
+
+//------------------------------------------------------------------------------
+// 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.
+//------------------------------------------------------------------------------
+
+// This function determines if there was a saving by prefetching the hostname
+// for which the host_info is supplied.
+static bool AcruePrefetchBenefits(DnsHostInfo* host_info) {
+ if (!dns_prefetch_enabled || NULL == dns_master)
+ return false;
+ return dns_master->AcruePrefetchBenefits(host_info);
+}
+
+// The observer class needs to connect starts and finishes of HTTP network
+// resolutions. We use the following type for that map.
+typedef std::map<void*, DnsHostInfo> ObservedResolutionMap;
+
+// There will only be one instance ever created of the following Observer
+// class. As a result, we get away with using static members for data local
+// to that instance (to better comply with a google style guide exemption).
+class PrefetchObserver : public net::DnsResolutionObserver {
+ public:
+ PrefetchObserver();
+ ~PrefetchObserver();
+
+ virtual void OnStartResolution(const std::string& name, void* context);
+ virtual void OnFinishResolutionWithStatus(bool was_resolved, void* context);
+
+ static void DnsGetFirstResolutionsHtml(std::string* output);
+ static void SaveStartupListAsPref(PrefService* local_state);
+
+ private:
+ static void StartupListAppend(const DnsHostInfo& host_info);
+
+ // We avoid using member variables to better comply with the style guide.
+ // We had permission to instantiate only a very minimal class as a global
+ // data item, so we avoid putting members in that class.
+ // There is really only one instance of this class, and it would have been
+ // much simpler to use member variables than these static members.
+ static Lock* lock;
+ // Map of pending resolutions seen by observer.
+ static ObservedResolutionMap* resolutions;
+ // List of the first N hostname resolutions observed in this run.
+ static Results* first_resolutions;
+ // The number of hostnames we'll save for prefetching at next startup.
+ static const size_t kStartupResolutionCount = 10;
+};
+
+//------------------------------------------------------------------------------
+// Member definitions for above Observer class.
+
+PrefetchObserver::PrefetchObserver() {
+ DCHECK(!lock && !resolutions && !first_resolutions);
+ lock = new Lock;
+ resolutions = new ObservedResolutionMap;
+ first_resolutions = new Results;
+}
+
+PrefetchObserver::~PrefetchObserver() {
+ DCHECK(lock && resolutions && first_resolutions);
+ delete first_resolutions;
+ first_resolutions = NULL;
+ delete resolutions;
+ resolutions = NULL;
+ delete lock;
+ lock = NULL;
+}
+
+void PrefetchObserver::OnStartResolution(const std::string& name,
+ void* context) {
+ DCHECK_NE(0, name.length());
+ DnsHostInfo host_info;
+ host_info.SetHostname(name);
+ host_info.SetStartedState();
+
+ AutoLock auto_lock(*lock);
+ (*resolutions)[context] = host_info;
+}
+
+void PrefetchObserver::OnFinishResolutionWithStatus(bool was_resolved,
+ void* context) {
+ DnsHostInfo host_info;
+ size_t startup_count;
+ {
+ AutoLock auto_lock(*lock);
+ ObservedResolutionMap::iterator it = resolutions->find(context);
+ if (resolutions->end() == it) {
+ return;
+ }
+ host_info = it->second;
+ resolutions->erase(it);
+ startup_count = first_resolutions->size();
+ }
+ host_info.SetFinishedState(was_resolved); // Get timing info
+ AcruePrefetchBenefits(&host_info); // Update prefetch benefit (if any).
+ if (kStartupResolutionCount <= startup_count || !was_resolved)
+ return;
+ StartupListAppend(host_info);
+}
+
+// static
+void PrefetchObserver::StartupListAppend(const DnsHostInfo& host_info) {
+ if (!on_the_record_switch || NULL == dns_master)
+ return;
+ AutoLock auto_lock(*lock);
+ if (kStartupResolutionCount <= first_resolutions->size())
+ return; // Someone just added the last item.
+ std::string host_name = host_info.hostname();
+ if (first_resolutions->find(host_name) != first_resolutions->end())
+ return; // We already have this hostname listed.
+ (*first_resolutions)[host_name] = host_info;
+}
+
+// static
+void PrefetchObserver::SaveStartupListAsPref(PrefService* local_state) {
+ ListValue* startup_list =
+ local_state->GetMutableList(prefs::kDnsStartupPrefetchList);
+
+ DCHECK(startup_list);
+ if (!startup_list)
+ return;
+ startup_list->Clear();
+ DCHECK(startup_list->GetSize() == 0);
+ AutoLock auto_lock(*lock);
+ for (Results::iterator it = first_resolutions->begin();
+ it != first_resolutions->end();
+ it++) {
+ const std::wstring hostname = ASCIIToWide(it->first);
+ startup_list->Append(Value::CreateStringValue(hostname));
+ }
+}
+
+// static
+void PrefetchObserver::DnsGetFirstResolutionsHtml(std::string* output) {
+ DnsHostInfo::DnsInfoTable resolution_list;
+ {
+ AutoLock auto_lock(*lock);
+ for (Results::iterator it(first_resolutions->begin());
+ it != first_resolutions->end();
+ it++) {
+ resolution_list.push_back(it->second);
+ }
+ }
+ DnsHostInfo::GetHtmlTable(resolution_list,
+ "Future startups will prefetch DNS records for ", false, output);
+}
+
+// static
+Lock* PrefetchObserver::lock = NULL;
+// static
+ObservedResolutionMap* PrefetchObserver::resolutions = NULL;
+// static
+Results* PrefetchObserver::first_resolutions = NULL;
+
+//------------------------------------------------------------------------------
+// Support observer to detect opening and closing of OffTheRecord windows.
+
+class OffTheRecordObserver : public NotificationObserver {
+ public:
+ OffTheRecordObserver() : lock_(), count_off_the_record_windows_(0) { }
+
+ ~OffTheRecordObserver() { }
+
+ // Register as an observer, and rely on the NotificationSystem shutdown
+ // to unregister us (at the last possible moment).
+ void Register() {
+ NotificationService* service = NotificationService::current();
+ // TODO(tc): These notification observers are never removed.
+ service->AddObserver(this, NOTIFY_BROWSER_CLOSED,
+ NotificationService::AllSources());
+ service->AddObserver(this, NOTIFY_BROWSER_OPENED,
+ NotificationService::AllSources());
+ }
+
+ void Observe(NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_BROWSER_OPENED:
+ if (!Source<Browser>(source)->profile()->IsOffTheRecord())
+ break;
+ {
+ AutoLock lock(lock_);
+ ++count_off_the_record_windows_;
+ }
+ OnTheRecord(false);
+ break;
+
+ case NOTIFY_BROWSER_CLOSED:
+ if (!Source<Browser>(source)->profile()->IsOffTheRecord())
+ break; // Ignore ordinary windows.
+ {
+ AutoLock lock(lock_);
+ DCHECK(0 < count_off_the_record_windows_);
+ if (0 >= count_off_the_record_windows_) // Defensive coding.
+ break;
+ if (--count_off_the_record_windows_)
+ break; // Still some windows are incognito.
+ } // Release lock.
+ OnTheRecord(true);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private:
+ Lock lock_;
+ int count_off_the_record_windows_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordObserver);
+};
+
+// TODO(jar): Use static class object so that I don't have to get the
+// destruction time right (which requires unregistering just before the
+// notification-service shuts down).
+static OffTheRecordObserver off_the_record_observer;
+
+//------------------------------------------------------------------------------
+// This section supports the about:dns page.
+//------------------------------------------------------------------------------
+
+// Provide global support for the about:dns page.
+void DnsPrefetchGetHtmlInfo(std::string* output) {
+ 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 (!dns_prefetch_enabled || NULL == dns_master) {
+ output->append("Dns Prefetching is disabled.");
+ } else {
+ if (!on_the_record_switch) {
+ output->append("Incognito mode is active in a window.");
+ } else {
+ dns_master->GetHtmlInfo(output);
+ PrefetchObserver::DnsGetFirstResolutionsHtml(output);
+ }
+ }
+ output->append("</body></html>");
+}
+
+//------------------------------------------------------------------------------
+// This section intializes and tears down global DNS prefetch services.
+//------------------------------------------------------------------------------
+
+// Note: We have explicit permission to create the following global static
+// object (in opposition to Google style rules). By making it a static, we
+// can ensure its deletion.
+static PrefetchObserver dns_resolution_observer;
+
+void InitDnsPrefetch(PrefService* user_prefs) {
+ // Use a large shutdown time so that UI tests (that instigate lookups, and
+ // then try to shutdown the browser) don't instigate the CHECK about
+ // "some slaves have not finished"
+ const TimeDelta kAllowableShutdownTime(TimeDelta::FromSeconds(10));
+ DCHECK(NULL == dns_master);
+ if (!dns_master) {
+ DnsMaster* new_master = new DnsMaster(kAllowableShutdownTime);
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID*>(&dns_master), new_master, NULL)) {
+ delete new_master;
+ } else {
+ // We did the initialization, so we should prime the pump, and set up
+ // the DNS resolution system to run.
+ off_the_record_observer.Register();
+
+ if (user_prefs) {
+ bool enabled = user_prefs->GetBoolean(prefs::kDnsPrefetchingEnabled);
+ EnableDnsPrefetch(enabled);
+ }
+
+ DLOG(INFO) << "DNS Prefetch service started";
+
+ // Start observing real HTTP stack resolutions.
+ net::AddDnsResolutionObserver(&dns_resolution_observer);
+ }
+ }
+}
+
+void ShutdownDnsPrefetch() {
+ DCHECK(NULL != dns_master);
+ DnsMaster* master = dns_master;
+ dns_master = NULL;
+ if (master->ShutdownSlaves()) {
+ delete master;
+ } else {
+ // Leak instance if shutdown problem.
+ DCHECK(0);
+ }
+}
+
+static void DiscardAllPrefetchState() {
+ if (!dns_master)
+ return;
+ dns_master->DiscardAllResults();
+}
+
+//------------------------------------------------------------------------------
+// Functions to handle saving of hostnames from one session to the next, to
+// expedite startup times.
+
+void SaveHostNamesForNextStartup(PrefService* local_state) {
+ if (!dns_prefetch_enabled)
+ return;
+ PrefetchObserver::SaveStartupListAsPref(local_state);
+}
+
+// TODO(jar): correct typo in name change ...Pretch... to ...Prefetch...
+void DnsPretchHostNamesAtStartup(PrefService* user_prefs,
+ PrefService* local_state) {
+ NameList hostnames;
+ // Prefetch DNS for hostnames 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).
+ ListValue* startup_list =
+ local_state->GetMutableList(prefs::kDnsStartupPrefetchList);
+ if (startup_list) {
+ for (ListValue::iterator it = startup_list->begin();
+ it != startup_list->end();
+ it++) {
+ std::wstring w_hostname;
+ (*it)->GetAsString(&w_hostname);
+ hostnames.push_back(WideToASCII(w_hostname));
+ }
+ }
+
+ // 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.host().empty())
+ hostnames.push_back(gurl.host());
+ }
+ }
+
+ if (hostnames.size() > 0)
+ DnsPrefetchList(hostnames);
+ else
+ DnsPrefetch(std::string("www.google.com")); // Start a thread.
+}
+
+
+} // namespace chrome_browser_net