Bug 509929 - Reference cycle between nsProgressNotificationProxy and nsHttpChannel on channel redirect. r=biesi
authorBobby Holley <bobbyholley@stanford.edu>
Thu, 13 Aug 2009 13:20:41 +0200
changeset 31432 7b05cc2cb9ee4f5fa6fe80ff93e17dc6d9b7b455
parent 31431 b50beb2bef4774a8b5c1f276c88600eff4db4de0
child 31433 376b78fc72230aaf2ca4e279a8f4ef1efd4a1d9f
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersbiesi
bugs509929
milestone1.9.2a2pre
Bug 509929 - Reference cycle between nsProgressNotificationProxy and nsHttpChannel on channel redirect. r=biesi
modules/libpr0n/src/imgLoader.cpp
netwerk/protocol/http/src/nsHttpChannel.cpp
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -44,16 +44,17 @@
 
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsICachingChannel.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIPrefBranch2.h"
 #include "nsIPrefService.h"
 #include "nsIProgressEventSink.h"
+#include "nsIChannelEventSink.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
 #include "nsIFileURL.h"
 #include "nsThreadUtils.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 
 #include "netCore.h"
@@ -113,38 +114,41 @@ static void PrintImageDecoders()
 
 /**
  * A class that implements nsIProgressEventSink and forwards all calls to it to
  * the original notification callbacks of the channel. Also implements
  * nsIInterfaceRequestor and gives out itself for nsIProgressEventSink calls,
  * and forwards everything else to the channel's notification callbacks.
  */
 class nsProgressNotificationProxy : public nsIProgressEventSink
+                                  , public nsIChannelEventSink
                                   , public nsIInterfaceRequestor
 {
   public:
     nsProgressNotificationProxy(nsIChannel* channel,
                                 imgIRequest* proxy)
         : mChannel(channel), mImageRequest(proxy) {
       channel->GetNotificationCallbacks(getter_AddRefs(mOriginalCallbacks));
     }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIPROGRESSEVENTSINK
+    NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIINTERFACEREQUESTOR
   private:
     ~nsProgressNotificationProxy() {}
 
     nsCOMPtr<nsIChannel> mChannel;
     nsCOMPtr<nsIInterfaceRequestor> mOriginalCallbacks;
     nsCOMPtr<nsIRequest> mImageRequest;
 };
 
-NS_IMPL_ISUPPORTS2(nsProgressNotificationProxy,
+NS_IMPL_ISUPPORTS3(nsProgressNotificationProxy,
                      nsIProgressEventSink,
+                     nsIChannelEventSink,
                      nsIInterfaceRequestor)
 
 NS_IMETHODIMP
 nsProgressNotificationProxy::OnProgress(nsIRequest* request,
                                         nsISupports* ctxt,
                                         PRUint64 progress,
                                         PRUint64 progressMax) {
   nsCOMPtr<nsILoadGroup> loadGroup;
@@ -174,23 +178,52 @@ nsProgressNotificationProxy::OnStatus(ns
                                 NS_GET_IID(nsIProgressEventSink),
                                 getter_AddRefs(target));
   if (!target)
     return NS_OK;
   return target->OnStatus(mImageRequest, ctxt, status, statusArg);
 }
 
 NS_IMETHODIMP
+nsProgressNotificationProxy::OnChannelRedirect(nsIChannel *oldChannel,
+                                               nsIChannel *newChannel,
+                                               PRUint32 flags) {
+  // The 'old' channel should match the current one
+  NS_ABORT_IF_FALSE(oldChannel == mChannel,
+                    "old channel doesn't match current!");
+
+  // Save the new channel
+  mChannel = newChannel;
+
+  // Tell the original original callbacks about it too
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+  nsCOMPtr<nsIChannelEventSink> target;
+  NS_QueryNotificationCallbacks(mOriginalCallbacks,
+                                loadGroup,
+                                NS_GET_IID(nsIChannelEventSink),
+                                getter_AddRefs(target));
+  if (!target)
+    return NS_OK;
+  return target->OnChannelRedirect(oldChannel, newChannel, flags);
+}
+
+NS_IMETHODIMP
 nsProgressNotificationProxy::GetInterface(const nsIID& iid,
                                           void** result) {
   if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
     *result = static_cast<nsIProgressEventSink*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
+  if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
+    *result = static_cast<nsIChannelEventSink*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
   if (mOriginalCallbacks)
     return mOriginalCallbacks->GetInterface(iid, result);
   return NS_NOINTERFACE;
 }
 
 static PRBool NewRequestAndEntry(nsIURI *uri, imgRequest **request, imgCacheEntry **entry)
 {
   // If file, force revalidation on expiration
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -1274,18 +1274,25 @@ nsHttpChannel::DoReplaceWithProxy(nsIPro
     newChannel->SetOriginalURI(mOriginalURI);
 
     // open new channel
     rv = newChannel->AsyncOpen(mListener, mListenerContext);
     if (NS_FAILED(rv))
         return rv;
 
     mStatus = NS_BINDING_REDIRECTED;
+
+    // disconnect from the old listeners...
     mListener = nsnull;
     mListenerContext = nsnull;
+
+    // ...and the old callbacks
+    mCallbacks = nsnull;
+    mProgressSink = nsnull;
+
     return rv;
 }
 
 nsresult
 nsHttpChannel::ResolveProxy()
 {
     LOG(("nsHttpChannel::ResolveProxy [this=%x]\n", this));