Bug 473089. Make sure that our DNS listener (which may be released on random threads) doesn't hold refs to Elements. r+sr=bzbarsky
authorPatrick McManus <mcmanus@ducksong.com>
Mon, 12 Jan 2009 10:38:32 -0500
changeset 22804 a0d75a7e07195380859974a41700d05ff9e9ab75
parent 22803 049bd5ae4aadbdd3daadeda8f31403f331da9d75
child 22805 961f1ed53daa59fd0b614137b29650ff84593169
push id421
push userbzbarsky@mozilla.com
push dateTue, 13 Jan 2009 18:18:57 +0000
bugs473089
milestone1.9.1b3pre
Bug 473089. Make sure that our DNS listener (which may be released on random threads) doesn't hold refs to Elements. r+sr=bzbarsky
content/html/content/src/nsHTMLDNSPrefetch.cpp
content/html/content/src/nsHTMLDNSPrefetch.h
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp
+++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp
@@ -57,30 +57,38 @@
 #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;
+static nsHTMLDNSPrefetch::nsListener *sDNSListener = 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);
 
-  NS_ADDREF(sPrefetches);
+  sDNSListener = new nsHTMLDNSPrefetch::nsListener();
+  if (!sDNSListener) {
+    NS_IF_RELEASE(sPrefetches);
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  NS_ADDREF(sDNSListener);
+
   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);
@@ -99,16 +107,17 @@ nsHTMLDNSPrefetch::Shutdown()
 {
   if (!sInitialized) {
     NS_WARNING("Not Initialized");
     return NS_OK;
   }
   sInitialized = PR_FALSE;
   NS_IF_RELEASE(sDNSService);
   NS_IF_RELEASE(sPrefetches);
+  NS_IF_RELEASE(sDNSListener);
   
   return NS_OK;
 }
 
 PRBool
 nsHTMLDNSPrefetch::IsSecureBaseContext (nsIDocument *aDocument)
 {
   nsIURI *docURI = aDocument->GetDocumentURI();
@@ -138,17 +147,17 @@ nsHTMLDNSPrefetch::IsAllowed (nsIDocumen
     return PR_FALSE;
 
   return PR_TRUE;
 }
 
 nsresult
 nsHTMLDNSPrefetch::Prefetch(nsGenericHTMLElement *aElement, PRUint16 flags)
 {
-  if (!(sInitialized && sPrefetches && sDNSService))
+  if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
     return NS_ERROR_NOT_AVAILABLE;
 
   return sPrefetches->Add(flags, aElement);
 }
 
 nsresult
 nsHTMLDNSPrefetch::PrefetchLow(nsGenericHTMLElement *aElement)
 {
@@ -165,22 +174,22 @@ nsresult
 nsHTMLDNSPrefetch::PrefetchHigh(nsGenericHTMLElement *aElement)
 {
   return Prefetch(aElement, 0);
 }
 
 nsresult
 nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags)
 {
-  if (!(sInitialized && sDNSService && sPrefetches))
+  if (!(sInitialized && sDNSService && sPrefetches && sDNSListener))
     return NS_ERROR_NOT_AVAILABLE;
 
   nsCOMPtr<nsICancelable> tmpOutstanding;
   return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags,
-                                   sPrefetches, nsnull, getter_AddRefs(tmpOutstanding));
+                                   sDNSListener, nsnull, getter_AddRefs(tmpOutstanding));
 }
 
 nsresult
 nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname)
 {
   return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW);
 }
 
@@ -191,16 +200,28 @@ nsHTMLDNSPrefetch::PrefetchMedium(nsAStr
 }
 
 nsresult
 nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname)
 {
   return Prefetch(hostname, 0);
 }
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener,
+                              nsIDNSListener)
+
+NS_IMETHODIMP
+nsHTMLDNSPrefetch::nsListener::OnLookupComplete(nsICancelable *request,
+                                              nsIDNSRecord  *rec,
+                                              nsresult       status)
+{
+  return NS_OK;
+}
 
 /////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 nsHTMLDNSPrefetch::nsDeferrals::nsDeferrals()
   : mHead(0),
     mTail(0),
     mActiveLoaderCount(0),
     mTimerArmed(PR_FALSE)
@@ -213,21 +234,20 @@ nsHTMLDNSPrefetch::nsDeferrals::~nsDefer
   if (mTimerArmed) {
     mTimerArmed = PR_FALSE;
     mTimer->Cancel();
   }
 
   Flush();
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsHTMLDNSPrefetch::nsDeferrals,
-                              nsIDNSListener,
-                              nsIWebProgressListener,
-                              nsISupportsWeakReference,
-                              nsIObserver)
+NS_IMPL_ISUPPORTS3(nsHTMLDNSPrefetch::nsDeferrals,
+                   nsIWebProgressListener,
+                   nsISupportsWeakReference,
+                   nsIObserver)
 
 void
 nsHTMLDNSPrefetch::nsDeferrals::Flush()
 {
   while (mHead != mTail) {
     mEntries[mTail].mElement = nsnull;
     mTail = (mTail + 1) & sMaxDeferredMask;
   }
@@ -267,17 +287,17 @@ nsHTMLDNSPrefetch::nsDeferrals::SubmitQu
     if (hrefURI)
       hrefURI->GetAsciiHost(hostName);
     
     if (!hostName.IsEmpty()) {
       nsCOMPtr<nsICancelable> tmpOutstanding;
 
       sDNSService->AsyncResolve(hostName, 
                                 mEntries[mTail].mFlags,
-                                this, nsnull, getter_AddRefs(tmpOutstanding));
+                                sDNSListener, nsnull, getter_AddRefs(tmpOutstanding));
     }
     mEntries[mTail].mElement = nsnull;
     mTail = (mTail + 1) & sMaxDeferredMask;
   }
   
   if (mTimerArmed) {
     mTimerArmed = PR_FALSE;
     mTimer->Cancel();
@@ -315,26 +335,16 @@ nsHTMLDNSPrefetch::nsDeferrals::Tick(nsI
 
   // 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::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)
 {
--- a/content/html/content/src/nsHTMLDNSPrefetch.h
+++ b/content/html/content/src/nsHTMLDNSPrefetch.h
@@ -79,24 +79,34 @@ public:
   static nsresult PrefetchLow(nsAString &host);
 
 private:
   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
+  class nsListener : public nsIDNSListener
+  {
+    // This class exists to give a safe callback no-op DNSListener
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDNSLISTENER
+
+    nsListener()  {}
+  private:
+    ~nsListener() {}
+  };
+  
+  class nsDeferrals : 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);