Bug 1307791 - nsHostObjectURI must release BlobImpl when the underlying blob URL is revoked, r=smaug, a=ritu
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 06 Oct 2016 20:28:03 +0200
changeset 428818 015d719318b905ae303c8f79e8fd58b6529519ff
parent 428817 c55113c9de7783dd00974b3cf819813e15449d0a
child 428819 0c76efc924acd4b16e64aaaa07fe21462d0f7cfa
push id33416
push userpaul@paul.cx
push dateMon, 24 Oct 2016 16:26:20 +0000
reviewerssmaug, ritu
bugs1307791
milestone50.0
Bug 1307791 - nsHostObjectURI must release BlobImpl when the underlying blob URL is revoked, r=smaug, a=ritu
dom/base/nsHostObjectProtocolHandler.cpp
dom/base/nsHostObjectProtocolHandler.h
dom/base/nsHostObjectURI.cpp
dom/base/nsHostObjectURI.h
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -20,16 +20,18 @@
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPrincipal.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 
+#define RELEASING_TIMER 1000
+
 using mozilla::DOMMediaStream;
 using mozilla::dom::BlobImpl;
 using mozilla::dom::MediaSource;
 using mozilla::ErrorResult;
 using mozilla::net::LoadInfo;
 
 // -----------------------------------------------------------------------
 // Hash table
@@ -62,16 +64,19 @@ struct DataInfo
   ObjectType mObjectType;
 
   RefPtr<BlobImpl> mBlobImpl;
   RefPtr<DOMMediaStream> mMediaStream;
   RefPtr<MediaSource> mMediaSource;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mStack;
+
+  // WeakReferences of nsHostObjectURI objects.
+  nsTArray<nsWeakPtr> mURIs;
 };
 
 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
 
 static DataInfo*
 GetDataInfo(const nsACString& aUri)
 {
   if (!gDataTable) {
@@ -414,16 +419,62 @@ class BlobURLsReporter final : public ns
     } else {
       path += url;
     }
   }
 };
 
 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
 
+class ReleasingTimerHolder final : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  static void
+  Create(nsTArray<nsWeakPtr>&& aArray)
+  {
+    RefPtr<ReleasingTimerHolder> holder = new ReleasingTimerHolder(Move(aArray));
+    holder->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    if (NS_WARN_IF(!holder->mTimer)) {
+      return;
+    }
+
+    nsresult rv = holder->mTimer->InitWithCallback(holder, RELEASING_TIMER,
+                                                   nsITimer::TYPE_ONE_SHOT);
+    NS_ENSURE_SUCCESS_VOID(rv);
+  }
+
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    for (uint32_t i = 0; i < mURIs.Length(); ++i) {
+      nsCOMPtr<nsIURI> uri = do_QueryReferent(mURIs[i]);
+      if (uri) {
+        static_cast<nsHostObjectURI*>(uri.get())->ForgetBlobImpl();
+      }
+    }
+
+    return NS_OK;
+  }
+
+private:
+  explicit ReleasingTimerHolder(nsTArray<nsWeakPtr>&& aArray)
+    : mURIs(aArray)
+  {}
+
+  ~ReleasingTimerHolder()
+  {}
+
+  nsTArray<nsWeakPtr> mURIs;
+  nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(ReleasingTimerHolder, nsITimerCallback)
+
 } // namespace mozilla
 
 template<typename T>
 static nsresult
 AddDataEntryInternal(const nsACString& aURI, T aObject,
                      nsIPrincipal* aPrincipal)
 {
   if (!gDataTable) {
@@ -538,41 +589,45 @@ nsHostObjectProtocolHandler::GetAllBlobU
     aRegistrations.AppendElement(
       BlobURLRegistrationData(nsCString(iter.Key()), blobParent, nullptr,
                               IPC::Principal(info->mPrincipal)));
   }
 
   return true;
 }
 
-void
+/*static */ void
 nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri,
                                              bool aBroadcastToOtherProcesses)
 {
   if (!gDataTable) {
     return;
   }
 
   DataInfo* info = GetDataInfo(aUri);
   if (!info) {
     return;
   }
 
   if (aBroadcastToOtherProcesses && info->mObjectType == DataInfo::eBlobImpl) {
     BroadcastBlobURLUnregistration(aUri, info);
   }
 
+  if (!info->mURIs.IsEmpty()) {
+    ReleasingTimerHolder::Create(Move(info->mURIs));
+  }
+
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
 
-void
+/* static */ void
 nsHostObjectProtocolHandler::RemoveDataEntries()
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   if (!gDataTable) {
     return;
   }
 
@@ -582,17 +637,17 @@ nsHostObjectProtocolHandler::RemoveDataE
 }
 
 /* static */ bool
 nsHostObjectProtocolHandler::HasDataEntry(const nsACString& aUri)
 {
   return !!GetDataInfo(aUri);
 }
 
-nsresult
+/* static */ nsresult
 nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
                                                nsIPrincipal* aPrincipal,
                                                nsACString& aUri)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -618,41 +673,41 @@ nsHostObjectProtocolHandler::GenerateURI
     aUri.Append('/');
   }
 
   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
 
   return NS_OK;
 }
 
-nsresult
+/* static */ nsresult
 nsHostObjectProtocolHandler::GenerateURIStringForBlobURL(nsIPrincipal* aPrincipal,
                                                          nsACString& aUri)
 {
   return
     GenerateURIString(NS_LITERAL_CSTRING(BLOBURI_SCHEME), aPrincipal, aUri);
 }
 
-nsIPrincipal*
+/* static */ nsIPrincipal*
 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
 {
   if (!gDataTable) {
     return nullptr;
   }
 
   DataInfo* res = GetDataInfo(aUri);
 
   if (!res) {
     return nullptr;
   }
 
   return res->mPrincipal;
 }
 
-void
+/* static */ void
 nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
                                       nsCycleCollectionTraversalCallback& aCallback)
 {
   if (!gDataTable) {
     return;
   }
 
   DataInfo* res;
@@ -711,16 +766,20 @@ nsHostObjectProtocolHandler::NewURI(cons
   }
 
   rv = uri->SetSpec(aSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_TryToSetImmutable(uri);
   uri.forget(aResult);
 
+  if (info && info->mObjectType == DataInfo::eBlobImpl) {
+    info->mURIs.AppendElement(do_GetWeakReference(*aResult));
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
                                          nsILoadInfo* aLoadInfo,
                                          nsIChannel** result)
 {
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -83,17 +83,17 @@ public:
   static bool
   GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
                        mozilla::dom::ContentParent* aCP);
 
 protected:
   virtual ~nsHostObjectProtocolHandler() {}
 
 private:
-  static void Init(void);
+  static void Init();
 };
 
 class nsBlobProtocolHandler : public nsHostObjectProtocolHandler
 {
 public:
   NS_IMETHOD GetScheme(nsACString &result) override;
 };
 
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -18,16 +18,17 @@ static NS_DEFINE_CID(kThisSimpleURIImple
                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
 
 NS_IMPL_ADDREF_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 NS_IMPL_RELEASE_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 
 NS_INTERFACE_MAP_BEGIN(nsHostObjectURI)
   NS_INTERFACE_MAP_ENTRY(nsIURIWithBlobImpl)
   NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   if (aIID.Equals(kHOSTOBJECTURICID))
     foundInterface = static_cast<nsIURI*>(this);
   else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
     // Need to return explicitly here, because if we just set foundInterface
     // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
     // nsSimplURI::QueryInterface and finding something for this CID.
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
@@ -271,8 +272,15 @@ nsHostObjectURI::GetFlags(uint32_t *aFla
 }
 
 NS_IMETHODIMP 
 nsHostObjectURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
   *aClassIDNoAlloc = kHOSTOBJECTURICID;
   return NS_OK;
 }
+
+void
+nsHostObjectURI::ForgetBlobImpl()
+{
+  MOZ_ASSERT(mBlobImpl);
+  mBlobImpl = nullptr;
+}
--- a/dom/base/nsHostObjectURI.h
+++ b/dom/base/nsHostObjectURI.h
@@ -12,25 +12,28 @@
 #include "nsCOMPtr.h"
 #include "nsIClassInfo.h"
 #include "nsIPrincipal.h"
 #include "nsISerializable.h"
 #include "nsIURIWithBlobImpl.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsSimpleURI.h"
 #include "nsIIPCSerializableURI.h"
+#include "nsWeakReference.h"
+
 
 /**
  * These URIs refer to host objects: Blobs, with scheme "blob",
  * MediaStreams, with scheme "mediastream", and MediaSources, with scheme
  * "mediasource".
  */
 class nsHostObjectURI : public mozilla::net::nsSimpleURI
                       , public nsIURIWithPrincipal
                       , public nsIURIWithBlobImpl
+                      , public nsSupportsWeakReference
 {
 public:
   nsHostObjectURI(nsIPrincipal* aPrincipal,
                   mozilla::dom::BlobImpl* aBlobImpl)
     : mozilla::net::nsSimpleURI()
     , mPrincipal(aPrincipal)
     , mBlobImpl(aBlobImpl)
   {}
@@ -59,16 +62,18 @@ public:
   virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
                                                 const nsACString& newRef) override
   {
     nsHostObjectURI* url = new nsHostObjectURI();
     SetRefOnClone(url, refHandlingMode, newRef);
     return url;
   }
 
+  void ForgetBlobImpl();
+
   nsCOMPtr<nsIPrincipal> mPrincipal;
   RefPtr<mozilla::dom::BlobImpl> mBlobImpl;
 
 protected:
   virtual ~nsHostObjectURI() {}
 };
 
 #define NS_HOSTOBJECTURI_CID \