Bug 453403. Add DNS prefetching, similar to what Google chrome does. r+sr=bzbarsky
authorPatrick McManus <mcmanus@ducksong.com>
Tue, 04 Nov 2008 10:14:50 -0500
changeset 21284 8c4f57a5674d1ccfea80b7e8a9f0b1d534abcf95
parent 21283 5d9dc9b6127e22d87963fed9f82f6d6e77ba4e5a
child 21285 cd9a392337b799124fce00d8ed9f5269c128dcb3
child 21297 9479b3d76a130b4ae1e944b25fc251edce07fea1
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs453403
milestone1.9.1b2pre
Bug 453403. Add DNS prefetching, similar to what Google chrome does. r+sr=bzbarsky
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
content/base/src/nsDocument.cpp
content/base/src/nsGkAtomList.h
content/html/content/src/Makefile.in
content/html/content/src/nsHTMLAnchorElement.cpp
content/html/document/src/nsHTMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.cpp
layout/build/nsLayoutStatics.cpp
netwerk/base/public/nsNetError.h
netwerk/base/src/Makefile.in
netwerk/build/nsNetModule.cpp
netwerk/dns/public/nsIDNSService.idl
netwerk/dns/src/nsDNSService2.cpp
netwerk/dns/src/nsDNSService2.h
netwerk/dns/src/nsHostResolver.cpp
netwerk/dns/src/nsHostResolver.h
netwerk/protocol/http/src/nsHttpChannel.cpp
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -96,16 +96,18 @@
 #include "nsThreadUtils.h"
 #include "nsPresShellIterator.h"
 #include "nsPIDOMWindow.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIWebNavigation.h"
 #include "nsIDocumentLoader.h"
 #include "nsICachingChannel.h"
 #include "nsICacheEntryDescriptor.h"
+#include "nsGenericHTMLElement.h"
+#include "nsHTMLDNSPrefetch.h"
 
 PRLogModuleInfo* gContentSinkLogModuleInfo;
 
 class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver
 {
 public:
   nsScriptLoaderObserverProxy(nsIScriptLoaderObserver* aInner)
     : mInner(do_GetWeakReference(aInner))
@@ -716,16 +718,20 @@ nsContentSink::ProcessLink(nsIContent* a
   nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
 
   PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
   // prefetch href if relation is "next" or "prefetch"
   if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
     PrefetchHref(aHref, aElement, hasPrefetch);
   }
 
+  if ((!aHref.IsEmpty()) && linkTypes.IndexOf(NS_LITERAL_STRING("dns-prefetch")) != -1) {
+    PrefetchDNS(aHref);
+  }
+
   // is it a stylesheet link?
   if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) {
     return NS_OK;
   }
 
   PRBool isAlternate = linkTypes.IndexOf(NS_LITERAL_STRING("alternate")) != -1;
   return ProcessStyleLink(aElement, aHref, isAlternate, aTitle, aType,
                           aMedia);
@@ -847,16 +853,33 @@ nsContentSink::PrefetchHref(const nsAStr
               mDocumentBaseURI);
     if (uri) {
       nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
       prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
     }
   }
 }
 
+void
+nsContentSink::PrefetchDNS(const nsAString &aHref)
+{
+  nsAutoString hostname;
+
+  if (StringBeginsWith(aHref, NS_LITERAL_STRING("//")))  {
+    hostname = Substring(aHref, 2);
+  }
+  else
+    nsGenericHTMLElement::GetHostnameFromHrefString(aHref, hostname);
+      
+  nsRefPtr<nsHTMLDNSPrefetch> prefetch = new nsHTMLDNSPrefetch(hostname, mDocument);
+  if (prefetch) {
+    prefetch->PrefetchLow();
+  }
+}
+
 nsresult
 nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey)
 {
   aCacheKey.Truncate();
 
   nsresult rv;
   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -187,16 +187,20 @@ protected:
                                     PRBool aAlternate,
                                     const nsSubstring& aTitle,
                                     const nsSubstring& aType,
                                     const nsSubstring& aMedia);
 
   void PrefetchHref(const nsAString &aHref, nsIContent *aSource,
                     PRBool aExplicit);
 
+  // aHref can either be the usual URI format or of the form "//www.hostname.com"
+  // without a scheme.
+  void PrefetchDNS(const nsAString &aHref);
+
   // Gets the cache key (used to identify items in a cache) of the channel.
   nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);
 
   // There is an offline cache manifest attribute specified and the
   // document is allowed to use the offline cache.  Process the cache
   // selection algorithm for this document and the manifest. Result is
   // an action that must be taken on the manifest, see
   // CacheSelectionAction enum above.
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6556,16 +6556,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
     }
 
     static const char *const headers[] = {
       "default-style",
       "content-style-type",
       "content-language",
       "content-disposition",
       "refresh",
+      "x-dns-prefetch-control",
       // add more http headers if you need
       // XXXbz don't add content-location support without reading bug
       // 238654 and its dependencies/dups first.
       0
     };
     
     nsCAutoString headerVal;
     const char *const *name = headers;
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -982,16 +982,17 @@ GK_ATOM(when, "when")
 GK_ATOM(where, "where")
 GK_ATOM(widget, "widget")
 GK_ATOM(width, "width")
 GK_ATOM(window, "window")
 GK_ATOM(headerWindowTarget, "window-target")
 GK_ATOM(withParam, "with-param")
 GK_ATOM(wizard, "wizard")
 GK_ATOM(wrap, "wrap")
+GK_ATOM(headerDNSPrefetchControl,"x-dns-prefetch-control")
 GK_ATOM(xml, "xml")
 GK_ATOM(xmlns, "xmlns")
 GK_ATOM(xmp, "xmp")
 GK_ATOM(xulcontentsgenerated, "xulcontentsgenerated")
 GK_ATOM(yes, "yes")
 GK_ATOM(z_index, "z-index")
 GK_ATOM(zeroDigit, "zero-digit")
 
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -78,16 +78,17 @@ REQUIRES	= xpcom \
 
 EXPORTS		= \
 		nsImageMapUtils.h \
 		nsClientRect.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsClientRect.cpp \
+		nsHTMLDNSPrefetch.cpp \
 		nsGenericHTMLElement.cpp \
 		nsFormSubmission.cpp \
 		nsImageMapUtils.cpp \
 		nsHTMLAnchorElement.cpp \
 		nsHTMLAreaElement.cpp \
 		nsHTMLBRElement.cpp \
 		nsHTMLBodyElement.cpp \
 		nsHTMLButtonElement.cpp \
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -59,16 +59,18 @@
 #include "nsIContentIterator.h"
 #include "nsIDOMText.h"
 #include "nsIEnumerator.h"
 
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 
+#include "nsHTMLDNSPrefetch.h"
+
 nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
 
 class nsHTMLAnchorElement : public nsGenericHTMLElement,
                             public nsIDOMHTMLAnchorElement,
                             public nsIDOMNSHTMLAnchorElement2,
                             public nsILink
 {
 public:
@@ -130,21 +132,36 @@ public:
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                              PRBool aNotify);
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
 protected:
   // The cached visited state
   nsLinkState mLinkState;
+
+  void PrefetchDNS();
 };
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
 
+void
+nsHTMLAnchorElement::PrefetchDNS()
+{
+  nsCOMPtr<nsIURI> hrefURI;
+  GetHrefURI(getter_AddRefs(hrefURI));
+
+  if (hrefURI) {
+    nsRefPtr<nsHTMLDNSPrefetch> prefetch = 
+      new nsHTMLDNSPrefetch(hrefURI, GetOwnerDoc());
+    if (prefetch) 
+      prefetch->PrefetchLow();
+  }
+}
 
 nsHTMLAnchorElement::nsHTMLAnchorElement(nsINodeInfo *aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mLinkState(eLinkState_Unknown)
 {
 }
 
 nsHTMLAnchorElement::~nsHTMLAnchorElement()
@@ -207,16 +224,17 @@ nsHTMLAnchorElement::BindToTree(nsIDocum
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     RegUnRegAccessKey(PR_TRUE);
   }
 
+  PrefetchDNS();
   return rv;
 }
 
 void
 nsHTMLAnchorElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
   if (IsInDoc()) {
     RegUnRegAccessKey(PR_FALSE);
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -2945,16 +2945,23 @@ HTMLContentSink::ProcessLINKTag(const ns
         PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
         if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
           nsAutoString hrefVal;
           element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
           if (!hrefVal.IsEmpty()) {
             PrefetchHref(hrefVal, element, hasPrefetch);
           }
         }
+        if (linkTypes.IndexOf(NS_LITERAL_STRING("dns-prefetch")) != -1) {
+          nsAutoString hrefVal;
+          element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+          if (!hrefVal.IsEmpty()) {
+            PrefetchDNS(hrefVal);
+          }
+        }
       }
     }
   }
 
   return result;
 }
 
 /* 
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -652,16 +652,28 @@ nsXMLContentSink::CloseElement(nsIConten
       PRBool willNotify;
       PRBool isAlternate;
       rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
       if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
         ++mPendingSheetCount;
         mScriptLoader->AddExecuteBlocker();
       }
     }
+    // Look for <link rel="dns-prefetch" href="hostname">
+    if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
+      nsAutoString relVal;
+      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
+      if (relVal.EqualsLiteral("dns-prefetch")) {
+        nsAutoString hrefVal;
+        aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+        if (!hrefVal.IsEmpty()) {
+          PrefetchDNS(hrefVal);
+        }
+      }
+    }
   }
 
   return rv;
 }  
 
 nsresult
 nsXMLContentSink::AddContentAsLeaf(nsIContent *aContent)
 {
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -78,16 +78,17 @@
 #include "nsCellMap.h"
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsXMLHttpRequest.h"
 #include "nsIFocusEventSuppressor.h"
 #include "nsDOMThreadService.h"
+#include "nsHTMLDNSPrefetch.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULElement.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
@@ -180,16 +181,22 @@ nsLayoutStatics::Initialize()
   }
 
   rv = nsTextFrameTextRunCache::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize textframe textrun cache");
     return rv;
   }
 
+  rv = nsHTMLDNSPrefetch::Initialize();
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Could not initialize HTML DNS prefetch");
+    return rv;
+  }
+
 #ifdef MOZ_XUL
   rv = nsXULContentUtils::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsXULContentUtils");
     return rv;
   }
 
 #ifndef MOZ_NO_INSPECTOR_APIS
@@ -275,16 +282,17 @@ nsLayoutStatics::Shutdown()
   nsDOMAttribute::Shutdown();
   nsDOMEventRTTearoff::Shutdown();
   nsEventListenerManager::Shutdown();
   nsContentList::Shutdown();
   nsComputedDOMStyle::Shutdown();
   CSSLoaderImpl::Shutdown();
   nsCSSRuleProcessor::FreeSystemMetrics();
   nsTextFrameTextRunCache::Shutdown();
+  nsHTMLDNSPrefetch::Shutdown();
   nsCSSRendering::Shutdown();
 #ifdef DEBUG
   nsFrame::DisplayReflowShutdown();
 #endif
   nsCellMap::Shutdown();
 
   // Release all of our atoms
   nsColorNames::ReleaseTable();
--- a/netwerk/base/public/nsNetError.h
+++ b/netwerk/base/public/nsNetError.h
@@ -268,16 +268,24 @@
 /**
  * The lookup of a hostname failed.  This generally refers to the hostname
  * from the URL being loaded.
  */
 #define NS_ERROR_UNKNOWN_HOST \
     NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 30)
 
 /**
+ * A low or medium priority DNS lookup failed because the pending
+ * queue was already full. High priorty (the default) always
+ * makes room
+ */
+#define NS_ERROR_DNS_LOOKUP_QUEUE_FULL \
+    NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 33)
+
+/**
  * The lookup of a proxy hostname failed.
  *
  * If a channel is configured to speak to a proxy server, then it will
  * generate this error if the proxy hostname cannot be resolved.
  */
 #define NS_ERROR_UNKNOWN_PROXY_HOST \
     NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 42)
 
--- a/netwerk/base/src/Makefile.in
+++ b/netwerk/base/src/Makefile.in
@@ -88,16 +88,17 @@ CPPSRCS		= \
 		nsSyncStreamListener.cpp \
 		nsUnicharStreamLoader.cpp \
 		nsURIChecker.cpp \
 		nsURLHelper.cpp \
 		nsURLParsers.cpp \
 		nsNetStrings.cpp \
 		nsBase64Encoder.cpp \
 		nsSerializationHelper.cpp \
+		nsDNSPrefetch.cpp \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 	CPPSRCS += nsURLHelperOS2.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 	CPPSRCS += nsURLHelperWin.cpp
 ifneq ($(OS_ARCH), WINCE)
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -54,16 +54,17 @@
 #include "nsFileStreams.h"
 #include "nsBufferedStreams.h"
 #include "nsMIMEInputStream.h"
 #include "nsSOCKSSocketProvider.h"
 #include "nsCacheService.h"
 #include "nsDiskCacheDeviceSQL.h"
 #include "nsMimeTypes.h"
 #include "nsNetStrings.h"
+#include "nsDNSPrefetch.h"
 
 #include "nsNetCID.h"
 
 #if defined(XP_MACOSX)
 #define BUILD_APPLEFILE_DECODER 1
 #else
 #define BUILD_BINHEX_DECODER 1
 #endif
@@ -614,20 +615,23 @@ static void nsNetShutdown(nsIModule *nec
     // Release buffer cache
     NS_IF_RELEASE(nsIOService::gBufferCache);
 
     // Release global state used by the URL helper module.
     net_ShutdownURLHelper();
 #ifdef XP_MACOSX
     net_ShutdownURLHelperOSX();
 #endif
-
+    
     // Release necko strings
     delete gNetStrings;
     gNetStrings = nsnull;
+    
+    // Release DNS service reference.
+    nsDNSPrefetch::Shutdown();
 }
 
 static const nsModuleComponentInfo gNetModuleInfo[] = {
     { NS_IOSERVICE_CLASSNAME,
       NS_IOSERVICE_CID,
       NS_IOSERVICE_CONTRACTID,
       nsIOServiceConstructor },
     { NS_IOSERVICE_CLASSNAME,
--- a/netwerk/dns/public/nsIDNSService.idl
+++ b/netwerk/dns/public/nsIDNSService.idl
@@ -41,17 +41,17 @@
 interface nsICancelable;
 interface nsIEventTarget;
 interface nsIDNSRecord;
 interface nsIDNSListener;
 
 /**
  * nsIDNSService
  */
-[scriptable, uuid(3ac9e611-e6b6-44b5-b312-c040e65b2929)]
+[scriptable, uuid(ee4d9f1d-4f99-4384-b547-29da735f8b6e)]
 interface nsIDNSService : nsISupports
 {
     /**
      * kicks off an asynchronous host lookup.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
@@ -102,9 +102,16 @@ interface nsIDNSService : nsISupports
      * if set, this flag suppresses the internal DNS lookup cache.
      */
     const unsigned long RESOLVE_BYPASS_CACHE = (1 << 0);
 
     /**
      * if set, the canonical name of the specified host will be queried.
      */
     const unsigned long RESOLVE_CANONICAL_NAME = (1 << 1);
+
+    /**
+     * if set, the query is given lower priority. Medium takes precedence
+     * if both are used.
+     */
+    const unsigned long RESOLVE_PRIORITY_MEDIUM = (1 << 2);
+    const unsigned long RESOLVE_PRIORITY_LOW    = (1 << 3);
 };
--- a/netwerk/dns/src/nsDNSService2.cpp
+++ b/netwerk/dns/src/nsDNSService2.cpp
@@ -45,27 +45,29 @@
 #include "nsIPrefBranch2.h"
 #include "nsIServiceManager.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsNetCID.h"
 #include "nsNetError.h"
+#include "nsDNSPrefetch.h"
 #include "prsystem.h"
 #include "prnetdb.h"
 #include "prmon.h"
 #include "prio.h"
 #include "plstr.h"
 
 static const char kPrefDnsCacheEntries[]    = "network.dnsCacheEntries";
 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
 static const char kPrefEnableIDN[]          = "network.enableIDN";
 static const char kPrefIPv4OnlyDomains[]    = "network.dns.ipv4OnlyDomains";
 static const char kPrefDisableIPv6[]        = "network.dns.disableIPv6";
+static const char kPrefDisablePrefetch[]    = "network.dns.disablePrefetch";
 
 //-----------------------------------------------------------------------------
 
 class nsDNSRecord : public nsIDNSRecord
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDNSRECORD
@@ -316,49 +318,53 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsDNSServi
 NS_IMETHODIMP
 nsDNSService::Init()
 {
     NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
 
     PRBool firstTime = (mLock == nsnull);
 
     // prefs
-    PRUint32 maxCacheEntries  = 20;
-    PRUint32 maxCacheLifetime = 1; // minutes
+    PRUint32 maxCacheEntries  = 400;
+    PRUint32 maxCacheLifetime = 3; // minutes
     PRBool   enableIDN        = PR_TRUE;
     PRBool   disableIPv6      = PR_FALSE;
+    PRBool   disablePrefetch  = PR_FALSE;
+    
     nsAdoptingCString ipv4OnlyDomains;
 
     // read prefs
     nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
         PRInt32 val;
         if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
             maxCacheEntries = (PRUint32) val;
         if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
             maxCacheLifetime = val / 60; // convert from seconds to minutes
 
         // ASSUMPTION: pref branch does not modify out params on failure
         prefs->GetBoolPref(kPrefEnableIDN, &enableIDN);
         prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
         prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
+        prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
     }
 
     if (firstTime) {
         mLock = PR_NewLock();
         if (!mLock)
             return NS_ERROR_OUT_OF_MEMORY;
 
         // register as prefs observer
         if (prefs) {
             prefs->AddObserver(kPrefDnsCacheEntries, this, PR_FALSE);
             prefs->AddObserver(kPrefDnsCacheExpiration, this, PR_FALSE);
             prefs->AddObserver(kPrefEnableIDN, this, PR_FALSE);
             prefs->AddObserver(kPrefIPv4OnlyDomains, this, PR_FALSE);
             prefs->AddObserver(kPrefDisableIPv6, this, PR_FALSE);
+            prefs->AddObserver(kPrefDisablePrefetch, this, PR_FALSE);
         }
     }
 
     // we have to null out mIDN since we might be getting re-initialized
     // as a result of a pref change.
     nsCOMPtr<nsIIDNService> idn;
     if (enableIDN)
         idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
@@ -369,18 +375,20 @@ nsDNSService::Init()
                                          getter_AddRefs(res));
     if (NS_SUCCEEDED(rv)) {
         // now, set all of our member variables while holding the lock
         nsAutoLock lock(mLock);
         mResolver = res;
         mIDN = idn;
         mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
         mDisableIPv6 = disableIPv6;
+        mDisablePrefetch = disablePrefetch;
     }
-
+    
+    nsDNSPrefetch::Initialize(this);
     return rv;
 }
 
 NS_IMETHODIMP
 nsDNSService::Shutdown()
 {
     nsRefPtr<nsHostResolver> res;
     {
@@ -401,16 +409,20 @@ nsDNSService::AsyncResolve(const nsACStr
                            nsICancelable    **result)
 {
     // grab reference to global host resolver and IDN service.  beware
     // simultaneous shutdown!!
     nsRefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     {
         nsAutoLock lock(mLock);
+
+        if (mDisablePrefetch && (flags & (RESOLVE_PRIORITY_LOW | RESOLVE_PRIORITY_MEDIUM)))
+            return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+
         res = mResolver;
         idn = mIDN;
     }
     NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
 
     const nsACString *hostPtr = &hostname;
 
     nsresult rv;
--- a/netwerk/dns/src/nsDNSService2.h
+++ b/netwerk/dns/src/nsDNSService2.h
@@ -63,9 +63,10 @@ private:
     // mLock protects access to mResolver and mIPv4OnlyDomains
     PRLock                   *mLock;
 
     // mIPv4OnlyDomains is a comma-separated list of domains for which only
     // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
     // a per-domain basis and work around broken DNS servers. See bug 68796.
     nsAdoptingCString         mIPv4OnlyDomains;
     PRBool                    mDisableIPv6;
+    PRBool                    mDisablePrefetch;
 };
--- a/netwerk/dns/src/nsHostResolver.cpp
+++ b/netwerk/dns/src/nsHostResolver.cpp
@@ -63,18 +63,38 @@
 #include "prlong.h"
 #include "prlog.h"
 #include "pldhash.h"
 #include "plstr.h"
 #include "nsURLHelper.h"
 
 //----------------------------------------------------------------------------
 
-#define MAX_THREADS 8
-#define IDLE_TIMEOUT PR_SecondsToInterval(60)
+// Use a persistent thread pool in order to avoid spinning up new threads all the time.
+// In particular, thread creation results in a res_init() call from libc which is 
+// quite expensive.
+//
+// The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
+// go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
+// currently in the pool a new thread is created for high priority requests. If
+// the new request is at a lower priority a new thread will only be created if 
+// there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
+// created or an idle thread located for the request it is queued.
+//
+// When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
+// ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a 
+// timeout period.
+
+#define MAX_NON_PRIORITY_REQUESTS 150
+
+#define HighThreadThreshold     4
+#define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
+#define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
+
+PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS);
 
 //----------------------------------------------------------------------------
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *gHostResolverLog = nsnull;
 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
 #else
 #define LOG(args)
@@ -179,16 +199,17 @@ nsHostRecord::Create(const nsHostKey *ke
     rec->_refc = 1; // addref
     NS_LOG_ADDREF(rec, 1, "nsHostRecord", sizeof(nsHostRecord));
     rec->addr_info_lock = lock;
     rec->addr_info = nsnull;
     rec->addr_info_gencnt = 0;
     rec->addr = nsnull;
     rec->expiration = NowInMinutes();
     rec->resolving = PR_FALSE;
+    rec->onQueue = PR_FALSE;
     PR_INIT_CLIST(rec);
     PR_INIT_CLIST(&rec->callbacks);
     rec->negative = PR_FALSE;
     memcpy((char *) rec->host, key->host, hostLen);
 
     *result = rec;
     return NS_OK;
 }
@@ -303,24 +324,36 @@ HostDB_RemoveEntry(PLDHashTable *table,
 //----------------------------------------------------------------------------
 
 nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
                                PRUint32 maxCacheLifetime)
     : mMaxCacheEntries(maxCacheEntries)
     , mMaxCacheLifetime(maxCacheLifetime)
     , mLock(nsnull)
     , mIdleThreadCV(nsnull)
-    , mHaveIdleThread(PR_FALSE)
+    , mNumIdleThreads(0)
     , mThreadCount(0)
+    , mAnyPriorityThreadCount(0)
     , mEvictionQSize(0)
+    , mPendingCount(0)
     , mShutdown(PR_TRUE)
 {
     mCreationTime = PR_Now();
-    PR_INIT_CLIST(&mPendingQ);
+    PR_INIT_CLIST(&mHighQ);
+    PR_INIT_CLIST(&mMediumQ);
+    PR_INIT_CLIST(&mLowQ);
     PR_INIT_CLIST(&mEvictionQ);
+
+    mHighPriorityInfo.self = this;
+    mHighPriorityInfo.onlyHighPriority = PR_TRUE;
+    mAnyPriorityInfo.self = this;
+    mAnyPriorityInfo.onlyHighPriority = PR_FALSE;
+
+    mLongIdleTimeout  = PR_SecondsToInterval(LongIdleTimeoutSeconds);
+    mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
 }
 
 nsHostResolver::~nsHostResolver()
 {
     if (mIdleThreadCV)
         PR_DestroyCondVar(mIdleThreadCV);
 
     if (mLock)
@@ -355,61 +388,101 @@ nsHostResolver::Init()
         LOG(("calling res_ninit\n"));
         res_ninit(&_res);
     }
 #endif
     return NS_OK;
 }
 
 void
+nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
+{
+    // loop through pending queue, erroring out pending lookups.
+    if (!PR_CLIST_IS_EMPTY(aPendingQ)) {
+        PRCList *node = aPendingQ->next;
+        while (node != aPendingQ) {
+            nsHostRecord *rec = static_cast<nsHostRecord *>(node);
+            node = node->next;
+            OnLookupComplete(rec, NS_ERROR_ABORT, nsnull);
+        }
+    }
+}
+
+void
 nsHostResolver::Shutdown()
 {
     LOG(("nsHostResolver::Shutdown\n"));
 
-    PRCList pendingQ, evictionQ;
-    PR_INIT_CLIST(&pendingQ);
+    PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
+    PR_INIT_CLIST(&pendingQHigh);
+    PR_INIT_CLIST(&pendingQMed);
+    PR_INIT_CLIST(&pendingQLow);
     PR_INIT_CLIST(&evictionQ);
 
     {
         nsAutoLock lock(mLock);
         
         mShutdown = PR_TRUE;
 
-        MoveCList(mPendingQ, pendingQ);
+        MoveCList(mHighQ, pendingQHigh);
+        MoveCList(mMediumQ, pendingQMed);
+        MoveCList(mLowQ, pendingQLow);
         MoveCList(mEvictionQ, evictionQ);
         mEvictionQSize = 0;
-
-        if (mHaveIdleThread)
+        mPendingCount = 0;
+        
+        if (mNumIdleThreads)
             PR_NotifyCondVar(mIdleThreadCV);
         
         // empty host database
         PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nsnull);
     }
-
-    // loop through pending queue, erroring out pending lookups.
-    if (!PR_CLIST_IS_EMPTY(&pendingQ)) {
-        PRCList *node = pendingQ.next;
-        while (node != &pendingQ) {
-            nsHostRecord *rec = static_cast<nsHostRecord *>(node);
-            node = node->next;
-            OnLookupComplete(rec, NS_ERROR_ABORT, nsnull);
-        }
-    }
+    
+    ClearPendingQueue(&pendingQHigh);
+    ClearPendingQueue(&pendingQMed);
+    ClearPendingQueue(&pendingQLow);
 
     if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
         PRCList *node = evictionQ.next;
         while (node != &evictionQ) {
             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
             node = node->next;
             NS_RELEASE(rec);
         }
     }
 
 }
 
+static inline PRBool
+IsHighPriority(PRUint16 flags)
+{
+    return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
+}
+
+static inline PRBool
+IsMediumPriority(PRUint16 flags)
+{
+    return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
+}
+
+static inline PRBool
+IsLowPriority(PRUint16 flags)
+{
+    return flags & nsHostResolver::RES_PRIORITY_LOW;
+}
+
+void 
+nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ)
+{
+    NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued");
+    
+    PR_REMOVE_LINK(aRec);
+    PR_APPEND_LINK(aRec, &aDestQ);
+}
+
 nsresult
 nsHostResolver::ResolveHost(const char            *host,
                             PRUint16               flags,
                             PRUint16               af,
                             nsResolveHostCallback *callback)
 {
     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
 
@@ -479,27 +552,48 @@ nsHostResolver::ResolveHost(const char  
                 he->rec->addr = (PRNetAddr *) malloc(sizeof(PRNetAddr));
                 if (!he->rec->addr)
                     status = NS_ERROR_OUT_OF_MEMORY;
                 else
                     memcpy(he->rec->addr, &tempAddr, sizeof(PRNetAddr));
                 // put reference to host record on stack...
                 result = he->rec;
             }
+            else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
+                     !IsHighPriority(flags) &&
+                     !he->rec->resolving) {
+                // This is a lower priority request and we are swamped, so refuse it.
+                rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+            }
             // otherwise, hit the resolver...
             else {
-                // add callback to the list of pending callbacks
+                // Add callback to the list of pending callbacks.
                 PR_APPEND_LINK(callback, &he->rec->callbacks);
 
                 if (!he->rec->resolving) {
                     he->rec->flags = flags;
                     rv = IssueLookup(he->rec);
                     if (NS_FAILED(rv))
                         PR_REMOVE_AND_INIT_LINK(callback);
                 }
+                else if (he->rec->onQueue) {
+                    // Consider the case where we are on a pending queue of 
+                    // lower priority than the request is being made at.
+                    // In that case we should upgrade to the higher queue.
+
+                    if (IsHighPriority(flags) && !IsHighPriority(he->rec->flags)) {
+                        // Move from (low|med) to high.
+                        MoveQueue(he->rec, mHighQ);
+                        he->rec->flags = flags;
+                    } else if (IsMediumPriority(flags) && IsLowPriority(he->rec->flags)) {
+                        // Move from low to med.
+                        MoveQueue(he->rec, mMediumQ);
+                        he->rec->flags = flags;
+                    }
+                }
             }
         }
     }
     if (result)
         callback->OnLookupComplete(this, result, status);
     return rv;
 }
 
@@ -538,93 +632,139 @@ nsHostResolver::DetachCallback(const cha
         callback->OnLookupComplete(this, rec, status);
 }
 
 nsresult
 nsHostResolver::IssueLookup(nsHostRecord *rec)
 {
     NS_ASSERTION(!rec->resolving, "record is already being resolved"); 
 
-    // add rec to mPendingQ, possibly removing it from mEvictionQ.
-    // if rec is on mEvictionQ, then we can just move the owning
-    // reference over to mPendingQ.
+    // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
+    // If rec is on mEvictionQ, then we can just move the owning
+    // reference over to the new active queue.
     if (rec->next == rec)
         NS_ADDREF(rec);
     else {
         PR_REMOVE_LINK(rec);
         mEvictionQSize--;
     }
-    PR_APPEND_LINK(rec, &mPendingQ);
+    
+    if (IsHighPriority(rec->flags))
+        PR_APPEND_LINK(rec, &mHighQ);
+    else if (IsMediumPriority(rec->flags))
+        PR_APPEND_LINK(rec, &mMediumQ);
+    else
+        PR_APPEND_LINK(rec, &mLowQ);
+    mPendingCount++;
+    
     rec->resolving = PR_TRUE;
+    rec->onQueue = PR_TRUE;
 
-    if (mHaveIdleThread) {
+    if (mNumIdleThreads) {
         // wake up idle thread to process this lookup
         PR_NotifyCondVar(mIdleThreadCV);
     }
-    else if (mThreadCount < MAX_THREADS) {
+    else if ((mThreadCount < HighThreadThreshold) ||
+             (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
         // dispatch new worker thread
         NS_ADDREF_THIS(); // owning reference passed to thread
+
+        struct nsHostResolverThreadInfo *info;
+        
+        if (mAnyPriorityThreadCount < HighThreadThreshold) {
+            info = &mAnyPriorityInfo;
+            mAnyPriorityThreadCount++;
+        }
+        else
+            info = &mHighPriorityInfo;
+
         mThreadCount++;
         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
                                         ThreadFunc,
-                                        this,
+                                        info,
                                         PR_PRIORITY_NORMAL,
                                         PR_GLOBAL_THREAD,
                                         PR_UNJOINABLE_THREAD,
                                         0);
         if (!thr) {
             mThreadCount--;
+            if (info == &mAnyPriorityInfo)
+                mAnyPriorityThreadCount--;
             NS_RELEASE_THIS();
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
 #if defined(PR_LOGGING)
     else
       LOG(("lookup waiting for thread - %s ...\n", rec->host));
 #endif
 
     return NS_OK;
 }
 
+void
+nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
+{
+    *aResult = static_cast<nsHostRecord *>(aQ.next);
+    PR_REMOVE_AND_INIT_LINK(*aResult);
+    mPendingCount--;
+    (*aResult)->onQueue = PR_FALSE;
+}
+
 PRBool
-nsHostResolver::GetHostToLookup(nsHostRecord **result)
+nsHostResolver::GetHostToLookup(nsHostRecord **result, struct nsHostResolverThreadInfo *aID)
 {
     nsAutoLock lock(mLock);
 
-    PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT;
-    //
-    // wait for one or more of the following to occur:
-    //  (1) the pending queue has a host record to process
-    //  (2) the shutdown flag has been set
-    //  (3) the thread has been idle for too long
-    //
-    // PR_WaitCondVar will return when any of these conditions is true.
-    //
-    while (PR_CLIST_IS_EMPTY(&mPendingQ) && !mHaveIdleThread && !mShutdown) {
+    PRIntervalTime start = PR_IntervalNow(), timeout;
+    
+    while (!mShutdown) {
+        // remove next record from Q; hand over owning reference. Check high, then med, then low
+        
+        if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
+            DeQueue (mHighQ, result);
+            return PR_TRUE;
+        }
+
+        if (! aID->onlyHighPriority) {
+            if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
+                DeQueue (mMediumQ, result);
+                return PR_TRUE;
+            }
+            
+            if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
+                DeQueue (mLowQ, result);
+                return PR_TRUE;
+            }
+        }
+        
+        timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
+        // wait for one or more of the following to occur:
+        //  (1) the pending queue has a host record to process
+        //  (2) the shutdown flag has been set
+        //  (3) the thread has been idle for too long
+        //
+        // PR_WaitCondVar will return when any of these conditions is true.
         // become the idle thread and wait for a lookup
-        mHaveIdleThread = PR_TRUE;
+        
+        mNumIdleThreads++;
         PR_WaitCondVar(mIdleThreadCV, timeout);
-        mHaveIdleThread = PR_FALSE;
+        mNumIdleThreads--;
 
         PRIntervalTime delta = PR_IntervalNow() - start;
         if (delta >= timeout)
             break;
         timeout -= delta;
         start += delta;
     }
 
-    if (!PR_CLIST_IS_EMPTY(&mPendingQ)) {
-        // remove next record from mPendingQ; hand over owning reference.
-        *result = static_cast<nsHostRecord *>(mPendingQ.next);
-        PR_REMOVE_AND_INIT_LINK(*result);
-        return PR_TRUE;
-    }
-
     // tell thread to exit...
     mThreadCount--;
+    if (!aID->onlyHighPriority)
+        mAnyPriorityThreadCount--;
     return PR_FALSE;
 }
 
 void
 nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo *result)
 {
     // get the list of pending callbacks for this lookup, and notify
     // them that the lookup is complete.
@@ -693,21 +833,21 @@ nsHostResolver::OnLookupComplete(nsHostR
 
 void
 nsHostResolver::ThreadFunc(void *arg)
 {
     LOG(("nsHostResolver::ThreadFunc entering\n"));
 #if defined(RES_RETRY_ON_FAILURE)
     nsResState rs;
 #endif
-
-    nsHostResolver *resolver = (nsHostResolver *) arg;
+    struct nsHostResolverThreadInfo *info = (struct nsHostResolverThreadInfo *) arg;
+    nsHostResolver *resolver = info->self;
     nsHostRecord *rec;
     PRAddrInfo *ai;
-    while (resolver->GetHostToLookup(&rec)) {
+    while (resolver->GetHostToLookup(&rec, info)) {
         LOG(("resolving %s ...\n", rec->host));
 
         PRIntn flags = PR_AI_ADDRCONFIG;
         if (!(rec->flags & RES_CANON_NAME))
             flags |= PR_AI_NOCANONNAME;
 
         ai = PR_GetAddrInfoByName(rec->host, rec->af, flags);
 #if defined(RES_RETRY_ON_FAILURE)
--- a/netwerk/dns/src/nsHostResolver.h
+++ b/netwerk/dns/src/nsHostResolver.h
@@ -63,16 +63,21 @@ class nsResolveHostCallback;
     PRInt32 Release() {                                                      \
         PRInt32 n = PR_AtomicDecrement((PRInt32*)&_refc);                    \
         NS_LOG_RELEASE(this, n, #classname);                                 \
         if (n == 0)                                                          \
             delete this;                                                     \
         return n;                                                            \
     }
 
+#define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY  5
+#define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 3
+#define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \
+                              MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY)
+
 struct nsHostKey
 {
     const char *host;
     PRUint16    flags;
     PRUint16    af;
 };
 
 /**
@@ -119,16 +124,19 @@ public:
 private:
     friend class nsHostResolver;
 
     PRCList callbacks; /* list of callbacks */
 
     PRBool  resolving; /* true if this record is being resolved, which means
                         * that it is either on the pending queue or owned by
                         * one of the worker threads. */ 
+    
+    PRBool  onQueue;  /* true if pending and on the queue (not yet given to getaddrinfo())*/
+        
 
    ~nsHostRecord();
 };
 
 /**
  * ResolveHost callback object.  It's PRCList members are used by
  * the nsHostResolver and should not be used by anything else.
  */
@@ -153,16 +161,26 @@ public:
      *        if successful, |record| contains non-null results
      */
     virtual void OnLookupComplete(nsHostResolver *resolver,
                                   nsHostRecord   *record,
                                   nsresult        status) = 0;
 };
 
 /**
+ * nsHostResolverThreadInfo structures are passed to the resolver
+ * thread.
+ */
+struct nsHostResolverThreadInfo
+{
+    nsHostResolver *self;
+    PRBool   onlyHighPriority;
+};
+
+/**
  * nsHostResolver - an asynchronous host name resolver.
  */
 class nsHostResolver
 {
 public:
     /**
      * host resolver instances are reference counted.
      */
@@ -209,37 +227,52 @@ public:
      * values for the flags parameter passed to ResolveHost and DetachCallback
      * that may be bitwise OR'd together.
      *
      * NOTE: in this implementation, these flags correspond exactly in value
      *       to the flags defined on nsIDNSService.
      */
     enum {
         RES_BYPASS_CACHE = 1 << 0,
-        RES_CANON_NAME   = 1 << 1
+        RES_CANON_NAME   = 1 << 1,
+        RES_PRIORITY_MEDIUM   = 1 << 2,
+        RES_PRIORITY_LOW  = 1 << 3
     };
 
 private:
     nsHostResolver(PRUint32 maxCacheEntries=50, PRUint32 maxCacheLifetime=1);
    ~nsHostResolver();
 
+   // nsHostResolverThreadInfo * is passed to the ThreadFunc
+   struct nsHostResolverThreadInfo  mHighPriorityInfo, mAnyPriorityInfo;
+   
     nsresult Init();
     nsresult IssueLookup(nsHostRecord *);
-    PRBool   GetHostToLookup(nsHostRecord **);
+    PRBool   GetHostToLookup(nsHostRecord **m, struct nsHostResolverThreadInfo *aID);
     void     OnLookupComplete(nsHostRecord *, nsresult, PRAddrInfo *);
+    void     DeQueue(PRCList &aQ, nsHostRecord **aResult);
+    void     ClearPendingQueue(PRCList *aPendingQueue);
 
+    static void  MoveQueue(nsHostRecord *aRec, PRCList &aDestQ);
+    
     static void ThreadFunc(void *);
 
     PRUint32      mMaxCacheEntries;
     PRUint32      mMaxCacheLifetime;
     PRLock       *mLock;
     PRCondVar    *mIdleThreadCV; // non-null if idle thread
-    PRBool        mHaveIdleThread;
+    PRUint32      mNumIdleThreads;
     PRUint32      mThreadCount;
+    PRUint32      mAnyPriorityThreadCount;
     PLDHashTable  mDB;
-    PRCList       mPendingQ;
+    PRCList       mHighQ;
+    PRCList       mMediumQ;
+    PRCList       mLowQ;
     PRCList       mEvictionQ;
     PRUint32      mEvictionQSize;
+    PRUint32      mPendingCount;
     PRTime        mCreationTime;
     PRBool        mShutdown;
+    PRIntervalTime mLongIdleTimeout;
+    PRIntervalTime mShortIdleTimeout;
 };
 
 #endif // nsHostResolver_h__
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -76,16 +76,17 @@
 #include "nsIResumableChannel.h"
 #include "nsInt64.h"
 #include "nsIVariant.h"
 #include "nsChannelProperties.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsAuthInformationHolder.h"
 #include "nsICacheService.h"
+#include "nsDNSPrefetch.h"
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
 
@@ -3988,16 +3989,23 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     nsresult rv;
 
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv))
         return rv;
 
+    // Start a DNS lookup very early in case the real open is queued the DNS can 
+    // happen in parallel.
+    nsRefPtr<nsDNSPrefetch> prefetch = new nsDNSPrefetch(mURI);
+    if (prefetch) {
+        prefetch->PrefetchMedium();
+    }
+
     // Remember the cookie header that was set, if any
     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
     if (cookieHeader)
         mUserSetCookieHeader = cookieHeader;
 
     // fetch cookies, and add them to the request header
     AddCookiesToRequest();