Bug 464838. Try to resolve performance issues in DNS prefetch by putting elements, not URIs, in the queue, and deferring URI creation to pageload complete (or a timer, if we're not in pageload). Now with leak fixes. r+sr=bzbarsky
authorPatrick McManus <mcmanus@ducksong.com>
Tue, 13 Jan 2009 10:08:43 -0500
changeset 22803 049bd5ae4aadbdd3daadeda8f31403f331da9d75
parent 22802 1d485fd75e5d58b5bc92d46af180eb784d420caa
child 22804 a0d75a7e07195380859974a41700d05ff9e9ab75
push id421
push userbzbarsky@mozilla.com
push dateTue, 13 Jan 2009 18:18:57 +0000
bugs464838
milestone1.9.1b3pre
Bug 464838. Try to resolve performance issues in DNS prefetch by putting elements, not URIs, in the queue, and deferring URI creation to pageload complete (or a timer, if we're not in pageload). Now with leak fixes. r+sr=bzbarsky
content/base/src/nsContentSink.cpp
content/html/content/src/nsHTMLAnchorElement.cpp
content/html/content/src/nsHTMLDNSPrefetch.cpp
content/html/content/src/nsHTMLDNSPrefetch.h
netwerk/base/src/nsDNSPrefetch.h
netwerk/protocol/http/src/nsHttpChannel.cpp
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -864,20 +864,19 @@ nsContentSink::PrefetchDNS(const nsAStri
 {
   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();
+
+  if (nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
+    nsHTMLDNSPrefetch::PrefetchLow(hostname);
   }
 }
 
 nsresult
 nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey)
 {
   aCacheKey.Truncate();
 
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -132,37 +132,21 @@ 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()
 {
@@ -224,17 +208,20 @@ nsHTMLAnchorElement::BindToTree(nsIDocum
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     RegUnRegAccessKey(PR_TRUE);
   }
 
-  PrefetchDNS();
+  // Prefetch links
+  if (aDocument && nsHTMLDNSPrefetch::IsAllowed(GetOwnerDoc())) {
+    nsHTMLDNSPrefetch::PrefetchLow(this);
+  }
   return rv;
 }
 
 void
 nsHTMLAnchorElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
   if (IsInDoc()) {
     RegUnRegAccessKey(PR_FALSE);
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp
+++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp
@@ -38,36 +38,51 @@
 
 #include "nsHTMLDNSPrefetch.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
 #include "nsNetUtil.h"
 
 #include "nsIDNSListener.h"
+#include "nsIWebProgressListener.h"
+#include "nsIWebProgress.h"
+#include "nsCURILoader.h"
 #include "nsIDNSRecord.h"
 #include "nsIDNSService.h"
 #include "nsICancelable.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
+#include "nsThreadUtils.h"
+#include "nsGenericHTMLElement.h"
+#include "nsITimer.h"
+#include "nsIObserverService.h"
 
 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
 static PRBool sDisablePrefetchHTTPSPref;
 static PRBool sInitialized = PR_FALSE;
 static nsIDNSService *sDNSService = nsnull;
+static nsHTMLDNSPrefetch::nsDeferrals *sPrefetches = nsnull;
 
 nsresult
 nsHTMLDNSPrefetch::Initialize()
 {
   if (sInitialized) {
     NS_WARNING("Initialize() called twice");
     return NS_OK;
   }
   
+  sPrefetches = new nsHTMLDNSPrefetch::nsDeferrals();
+  if (!sPrefetches)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  NS_ADDREF(sPrefetches);
+  sPrefetches->Activate();
+
   nsContentUtils::AddBoolPrefVarCache("network.dns.disablePrefetchFromHTTPS", 
                                       &sDisablePrefetchHTTPSPref);
   
   // Default is false, so we need an explicit call to prime the cache.
   sDisablePrefetchHTTPSPref = 
     nsContentUtils::GetBoolPref("network.dns.disablePrefetchFromHTTPS", PR_TRUE);
   
   NS_IF_RELEASE(sDNSService);
@@ -83,52 +98,35 @@ nsresult
 nsHTMLDNSPrefetch::Shutdown()
 {
   if (!sInitialized) {
     NS_WARNING("Not Initialized");
     return NS_OK;
   }
   sInitialized = PR_FALSE;
   NS_IF_RELEASE(sDNSService);
-  return NS_OK;
-}
-
-nsHTMLDNSPrefetch::nsHTMLDNSPrefetch(nsAString &hostname, nsIDocument *aDocument)
-{
-  NS_ASSERTION(aDocument, "Document Required");
-  NS_ASSERTION(sInitialized, "nsHTMLDNSPrefetch is not initialized");
+  NS_IF_RELEASE(sPrefetches);
   
-  mAllowed = IsAllowed(aDocument);
-  CopyUTF16toUTF8(hostname, mHostname);
-}
-
-nsHTMLDNSPrefetch::nsHTMLDNSPrefetch(nsIURI *aURI, nsIDocument *aDocument)
-{
-  NS_ASSERTION(aDocument, "Document Required");
-  NS_ASSERTION(aURI, "URI Required");
-  NS_ASSERTION(sInitialized, "nsHTMLDNSPrefetch is not initialized");
-
-  mAllowed = IsAllowed(aDocument);
-  aURI->GetAsciiHost(mHostname);
+  return NS_OK;
 }
 
 PRBool
 nsHTMLDNSPrefetch::IsSecureBaseContext (nsIDocument *aDocument)
 {
   nsIURI *docURI = aDocument->GetDocumentURI();
   nsCAutoString scheme;
   docURI->GetScheme(scheme);
   return scheme.EqualsLiteral("https");
 }
 
 PRBool
 nsHTMLDNSPrefetch::IsAllowed (nsIDocument *aDocument)
 {
   if (IsSecureBaseContext(aDocument) && sDisablePrefetchHTTPSPref)
-      return PR_FALSE;
+    return PR_FALSE;
     
   // Check whether the x-dns-prefetch-control HTTP response header is set to override 
   // the default. This may also be set by meta tag. Chromium treats any value other
   // than 'on' (case insensitive) as 'off'.
 
   nsAutoString control;
   aDocument->GetHeaderData(nsGkAtoms::headerDNSPrefetchControl, control);
   
@@ -137,53 +135,275 @@ nsHTMLDNSPrefetch::IsAllowed (nsIDocumen
 
   // There is no need to do prefetch on non UI scenarios such as XMLHttpRequest.
   if (!aDocument->GetWindow())
     return PR_FALSE;
 
   return PR_TRUE;
 }
 
-nsresult 
-nsHTMLDNSPrefetch::Prefetch(PRUint16 flags)
+nsresult
+nsHTMLDNSPrefetch::Prefetch(nsGenericHTMLElement *aElement, PRUint16 flags)
+{
+  if (!(sInitialized && sPrefetches && sDNSService))
+    return NS_ERROR_NOT_AVAILABLE;
+
+  return sPrefetches->Add(flags, aElement);
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchLow(nsGenericHTMLElement *aElement)
 {
-  if (mHostname.IsEmpty())
-    return NS_ERROR_NOT_AVAILABLE;
-  
-  if (!mAllowed)
+  return Prefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW);
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchMedium(nsGenericHTMLElement *aElement)
+{
+  return Prefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_MEDIUM);
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchHigh(nsGenericHTMLElement *aElement)
+{
+  return Prefetch(aElement, 0);
+}
+
+nsresult
+nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags)
+{
+  if (!(sInitialized && sDNSService && sPrefetches))
     return NS_ERROR_NOT_AVAILABLE;
 
-  if (!sDNSService)
-    return NS_ERROR_NOT_AVAILABLE;
-  
   nsCOMPtr<nsICancelable> tmpOutstanding;
-  
-  return sDNSService->AsyncResolve(mHostname, flags, this, nsnull,
-                                   getter_AddRefs(tmpOutstanding));
+  return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags,
+                                   sPrefetches, nsnull, getter_AddRefs(tmpOutstanding));
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname)
+{
+  return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW);
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchMedium(nsAString &hostname)
+{
+  return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM);
+}
+
+nsresult
+nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname)
+{
+  return Prefetch(hostname, 0);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+nsHTMLDNSPrefetch::nsDeferrals::nsDeferrals()
+  : mHead(0),
+    mTail(0),
+    mActiveLoaderCount(0),
+    mTimerArmed(PR_FALSE)
+{
+  mTimer = do_CreateInstance("@mozilla.org/timer;1");
+}
+
+nsHTMLDNSPrefetch::nsDeferrals::~nsDeferrals()
+{
+  if (mTimerArmed) {
+    mTimerArmed = PR_FALSE;
+    mTimer->Cancel();
+  }
+
+  Flush();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsHTMLDNSPrefetch::nsDeferrals,
+                              nsIDNSListener,
+                              nsIWebProgressListener,
+                              nsISupportsWeakReference,
+                              nsIObserver)
+
+void
+nsHTMLDNSPrefetch::nsDeferrals::Flush()
+{
+  while (mHead != mTail) {
+    mEntries[mTail].mElement = nsnull;
+    mTail = (mTail + 1) & sMaxDeferredMask;
+  }
 }
 
 nsresult
-nsHTMLDNSPrefetch::PrefetchLow()
+nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, nsGenericHTMLElement *aElement)
 {
-  return Prefetch(nsIDNSService::RESOLVE_PRIORITY_LOW);
-}
+  // The FIFO has no lock, so it can only be accessed on main thread
+  NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread");
 
-nsresult
-nsHTMLDNSPrefetch::PrefetchMedium()
-{
-  return Prefetch(nsIDNSService::RESOLVE_PRIORITY_MEDIUM);
+  if (((mHead + 1) & sMaxDeferredMask) == mTail)
+    return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+    
+  mEntries[mHead].mFlags = flags;
+  mEntries[mHead].mElement = aElement;
+  mHead = (mHead + 1) & sMaxDeferredMask;
+
+  if (!mActiveLoaderCount && !mTimerArmed && mTimer) {
+    mTimerArmed = PR_TRUE;
+    mTimer->InitWithFuncCallback(Tick, this, 2000, nsITimer::TYPE_ONE_SHOT);
+  }
+  
+  return NS_OK;
 }
 
-nsresult
-nsHTMLDNSPrefetch::PrefetchHigh()
+void
+nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue()
 {
-  return Prefetch(0);
+  NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::SubmitQueue must be on main thread");
+  nsCString hostName;
+  if (!sDNSService) return;
+
+  while (mHead != mTail) {
+    nsCOMPtr<nsIURI> hrefURI;
+    mEntries[mTail].mElement->GetHrefURIForAnchors(getter_AddRefs(hrefURI));
+    if (hrefURI)
+      hrefURI->GetAsciiHost(hostName);
+    
+    if (!hostName.IsEmpty()) {
+      nsCOMPtr<nsICancelable> tmpOutstanding;
+
+      sDNSService->AsyncResolve(hostName, 
+                                mEntries[mTail].mFlags,
+                                this, nsnull, getter_AddRefs(tmpOutstanding));
+    }
+    mEntries[mTail].mElement = nsnull;
+    mTail = (mTail + 1) & sMaxDeferredMask;
+  }
+  
+  if (mTimerArmed) {
+    mTimerArmed = PR_FALSE;
+    mTimer->Cancel();
+  }
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch, nsIDNSListener)
+void
+nsHTMLDNSPrefetch::nsDeferrals::Activate()
+{
+  // Register as an observer for the document loader  
+  nsCOMPtr<nsIWebProgress> progress = 
+    do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
+  if (progress)
+    progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+
+  // Register as an observer for xpcom shutdown events so we can drop any element refs
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1", &rv);
+  if (NS_SUCCEEDED(rv))
+    observerService->AddObserver(this, "xpcom-shutdown", PR_TRUE);
+}
+
+// nsITimer related method
+
+void 
+nsHTMLDNSPrefetch::nsDeferrals::Tick(nsITimer *aTimer, void *aClosure)
+{
+  nsHTMLDNSPrefetch::nsDeferrals *self = (nsHTMLDNSPrefetch::nsDeferrals *) aClosure;
+
+  NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Tick must be on main thread");
+  NS_ASSERTION(self->mTimerArmed, "Timer is not armed");
+  
+  self->mTimerArmed = PR_FALSE;
+
+  // If the queue is not submitted here because there are outstanding pages being loaded,
+  // there is no need to rearm the timer as the queue will be submtited when those 
+  // loads complete.
+  if (!self->mActiveLoaderCount) 
+    self->SubmitQueue();
+}
+
+//////////// nsIDNSListener method
 
 NS_IMETHODIMP
-nsHTMLDNSPrefetch::OnLookupComplete(nsICancelable *request,
-                                    nsIDNSRecord  *rec,
-                                    nsresult       status)
+nsHTMLDNSPrefetch::nsDeferrals::OnLookupComplete(nsICancelable *request,
+                                                 nsIDNSRecord  *rec,
+                                                 nsresult       status)
 {
   return NS_OK;
 }
+
+//////////// nsIWebProgressListener methods
+
+NS_IMETHODIMP 
+nsHTMLDNSPrefetch::nsDeferrals::OnStateChange(nsIWebProgress* aWebProgress, 
+                                              nsIRequest *aRequest, 
+                                              PRUint32 progressStateFlags, 
+                                              nsresult aStatus)
+{
+  // The FIFO has no lock, so it can only be accessed on main thread
+  NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::OnStateChange must be on main thread");
+  
+  if (progressStateFlags & STATE_IS_DOCUMENT) {
+    if (progressStateFlags & STATE_STOP) {
+
+      // Initialization may have missed a STATE_START notification, so do
+      // not go negative
+      if (mActiveLoaderCount)
+        mActiveLoaderCount--;
+
+      if (!mActiveLoaderCount)
+        SubmitQueue();
+    }
+    else if (progressStateFlags & STATE_START)
+      mActiveLoaderCount++;
+  }
+            
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLDNSPrefetch::nsDeferrals::OnProgressChange(nsIWebProgress *aProgress,
+                                                 nsIRequest *aRequest, 
+                                                 PRInt32 curSelfProgress, 
+                                                 PRInt32 maxSelfProgress, 
+                                                 PRInt32 curTotalProgress, 
+                                                 PRInt32 maxTotalProgress)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLDNSPrefetch::nsDeferrals::OnLocationChange(nsIWebProgress* aWebProgress,
+                                                 nsIRequest* aRequest,
+                                                 nsIURI *location)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsHTMLDNSPrefetch::nsDeferrals::OnStatusChange(nsIWebProgress* aWebProgress,
+                                               nsIRequest* aRequest,
+                                               nsresult aStatus,
+                                               const PRUnichar* aMessage)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsHTMLDNSPrefetch::nsDeferrals::OnSecurityChange(nsIWebProgress *aWebProgress, 
+                                                 nsIRequest *aRequest, 
+                                                 PRUint32 state)
+{
+  return NS_OK;
+}
+
+//////////// nsIObserver method
+
+NS_IMETHODIMP
+nsHTMLDNSPrefetch::nsDeferrals::Observe(nsISupports *subject,
+                                        const char *topic,
+                                        const PRUnichar *data)
+{
+  if (!strcmp(topic, "xpcom-shutdown"))
+    Flush();
+  
+  return NS_OK;
+}
--- a/content/html/content/src/nsHTMLDNSPrefetch.h
+++ b/content/html/content/src/nsHTMLDNSPrefetch.h
@@ -31,49 +31,98 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef nsHMTLDNSPrefetch_h___
+#ifndef nsHTMLDNSPrefetch_h___
+#define nsHTMLDNSPrefetch_h___
 
 #include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
 #include "nsString.h"
 
 #include "nsIDNSListener.h"
+#include "nsIWebProgressListener.h"
+#include "nsWeakReference.h"
+#include "nsIObserver.h"
 
-class nsIURI;
 class nsIDocument;
+class nsGenericHTMLElement;
+class nsITimer;
 
-class nsHTMLDNSPrefetch : public nsIDNSListener
+class nsHTMLDNSPrefetch 
 {
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDNSLISTENER
-  
   // The required aDocument parameter is the context requesting the prefetch - under
   // certain circumstances (e.g. headers, or security context) associated with
   // the context the prefetch will not be performed. 
-
-  nsHTMLDNSPrefetch(nsAString &aHostname, nsIDocument *aDocument);
-  nsHTMLDNSPrefetch(nsIURI *aURI,         nsIDocument *aDocument);
-  
+  static PRBool   IsAllowed(nsIDocument *aDocument);
+ 
   static nsresult Initialize();
   static nsresult Shutdown();
   
-  // Call one of the following methods to start the Prefetch.
-  nsresult PrefetchHigh();
-  nsresult PrefetchMedium();
-  nsresult PrefetchLow();
-  
+  // Call one of the Prefetch* methods to start the lookup.
+  //
+  // The URI versions will defer DNS lookup until pageload is
+  // complete, while the string versions submit the lookup to 
+  // the DNS system immediately. The URI version is somewhat lighter
+  // weight, but its request is also more likely to be dropped due to a 
+  // full queue and it may only be used from the main thread.
+
+  static nsresult PrefetchHigh(nsGenericHTMLElement *aElement);
+  static nsresult PrefetchMedium(nsGenericHTMLElement *aElement);
+  static nsresult PrefetchLow(nsGenericHTMLElement *aElement);
+  static nsresult PrefetchHigh(nsAString &host);
+  static nsresult PrefetchMedium(nsAString &host);
+  static nsresult PrefetchLow(nsAString &host);
+
 private:
-  nsCString  mHostname;
-  PRBool     mAllowed;
+  static nsresult Prefetch(nsAString &host, PRUint16 flags);
+  static nsresult Prefetch(nsGenericHTMLElement *aElement, PRUint16 flags);
+  static PRBool   IsSecureBaseContext(nsIDocument *aDocument);
+  
+public:
+  class nsDeferrals : public nsIDNSListener
+                    , public nsIWebProgressListener
+                    , public nsSupportsWeakReference
+                    , public nsIObserver
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDNSLISTENER
+    NS_DECL_NSIWEBPROGRESSLISTENER
+    NS_DECL_NSIOBSERVER
+
+    nsDeferrals();
+    
+    void Activate();
+    nsresult Add(PRUint16 flags, nsGenericHTMLElement *aElement);
     
-  nsresult Prefetch(PRUint16 flags);
-  PRBool   IsSecureBaseContext(nsIDocument *aDocument);
-  PRBool   IsAllowed(nsIDocument *aDocument);
+  private:
+    ~nsDeferrals();
+    void Flush();
+    
+    void SubmitQueue();
+    
+    PRUint16                  mHead;
+    PRUint16                  mTail;
+    PRUint32                  mActiveLoaderCount;
+
+    nsCOMPtr<nsITimer>        mTimer;
+    PRBool                    mTimerArmed;
+    static void Tick(nsITimer *aTimer, void *aClosure);
+    
+    static const int          sMaxDeferred = 512;  // keep power of 2 for masking
+    static const int          sMaxDeferredMask = (sMaxDeferred - 1);
+    
+    struct deferred_entry
+    {
+      PRUint16                         mFlags;
+      nsRefPtr<nsGenericHTMLElement>   mElement;
+    } mEntries[sMaxDeferred];
+  };
 };
 
 #endif 
--- a/netwerk/base/src/nsDNSPrefetch.h
+++ b/netwerk/base/src/nsDNSPrefetch.h
@@ -32,16 +32,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDNSPrefetch_h___
+#define nsDNSPrefetch_h___
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
 #include "nsIDNSListener.h"
 
 class nsIURI;
 class nsIDNSService;
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -4020,23 +4020,26 @@ 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->PrefetchHigh();
+    if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) {
+        // Start a DNS lookup very early in case the real open is queued the DNS can 
+        // happen in parallel. Do not do so in the presence of an HTTP proxy as 
+        // all lookups other than for the proxy itself are done by the proxy.
+        nsRefPtr<nsDNSPrefetch> prefetch = new nsDNSPrefetch(mURI);
+        if (prefetch) {
+            prefetch->PrefetchHigh();
+        }
     }
-
+    
     // 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();