Bug 1307791 - nsHostObjectURI must release BlobImpl when the underlying blob URL is revoked, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 06 Oct 2016 20:28:03 +0200
changeset 316826 a7a43428c1932c98785c9fd696a15d57eb86d5bc
parent 316825 baabb29c04ef0d00cf76a2fc19ddf74e0e474252
child 316827 4fff2bf7911a47fb9dc444b8395fb78e1b5a8181
push id30783
push userphilringnalda@gmail.com
push dateFri, 07 Oct 2016 02:58:32 +0000
treeherdermozilla-central@a5b04b518afe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1307791
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1307791 - nsHostObjectURI must release BlobImpl when the underlying blob URL is revoked, r=smaug
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
@@ -21,16 +21,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
@@ -63,16 +65,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) {
@@ -413,16 +418,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) {
@@ -539,41 +590,45 @@ nsHostObjectProtocolHandler::GetAllBlobU
     aRegistrations.AppendElement(mozilla::dom::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) {
     mozilla::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;
   }
 
@@ -583,17 +638,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);
@@ -619,41 +674,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;
@@ -712,16 +767,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;
@@ -276,8 +277,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 \