Bug 1279186 - Blob URLs in multi-e10s - part 1 - BlobURLs broadcasted, r=gabor
☠☠ backed out by 7bda4f49e0f5 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Sun, 17 Jul 2016 16:50:50 +0200
changeset 345350 effbc83efa08f2fa61408ea1dba38005a5648707
parent 345349 dd77af0226275b8c5f312379411f11d130de0734
child 345351 f9e641cbce0519105ca330e7862dbf6579f1c70c
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs1279186
milestone50.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 1279186 - Blob URLs in multi-e10s - part 1 - BlobURLs broadcasted, r=gabor
dom/base/nsHostObjectProtocolHandler.cpp
dom/base/nsHostObjectProtocolHandler.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/PermissionMessageUtils.h
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -2,18 +2,21 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHostObjectProtocolHandler.h"
 
 #include "DOMMediaStream.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsClassHashtable.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
@@ -33,19 +36,89 @@ struct DataInfo
   // mObject is expected to be an BlobImpl, DOMMediaStream, or MediaSource
   nsCOMPtr<nsISupports> mObject;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mStack;
 };
 
 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
 
+static DataInfo*
+GetDataInfo(const nsACString& aUri)
+{
+  if (!gDataTable) {
+    return nullptr;
+  }
+
+  DataInfo* res;
+
+  // Let's remove any fragment and query from this URI.
+  int32_t hasFragmentPos = aUri.FindChar('#');
+  int32_t hasQueryPos = aUri.FindChar('?');
+
+  int32_t pos = -1;
+  if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
+    pos = std::min(hasFragmentPos, hasQueryPos);
+  } else if (hasFragmentPos >= 0) {
+    pos = hasFragmentPos;
+  } else {
+    pos = hasQueryPos;
+  }
+
+  if (pos < 0) {
+    gDataTable->Get(aUri, &res);
+  } else {
+    gDataTable->Get(StringHead(aUri, pos), &res);
+  }
+
+  return res;
+}
+
 // Memory reporting for the hash table.
 namespace mozilla {
 
+void
+BroadcastBlobURLRegistration(const nsACString& aURI,
+                             BlobImpl* aBlobImpl,
+                             nsIPrincipal* aPrincipal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aBlobImpl);
+
+  if (XRE_IsParentProcess()) {
+    ContentParent::BroadcastBlobURLRegistration(aURI, aBlobImpl,
+                                                aPrincipal);
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  BlobChild* actor = cc->GetOrCreateActorForBlobImpl(aBlobImpl);
+  if (NS_WARN_IF(!actor)) {
+    return;
+  }
+
+  NS_WARN_IF(!cc->SendStoreAndBroadcastBlobURLRegistration(nsCString(aURI), actor,
+                                                           IPC::Principal(aPrincipal)));
+}
+
+void
+BroadcastBlobURLUnregistration(const nsACString& aURI, DataInfo* aInfo)
+{
+  MOZ_ASSERT(aInfo);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_IsParentProcess()) {
+    ContentParent::BroadcastBlobURLUnregistration(aURI);
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  NS_WARN_IF(!cc->SendUnstoreAndBroadcastBlobURLUnregistration(nsCString(aURI)));
+}
+
 class HostObjectURLsReporter final : public nsIMemoryReporter
 {
   ~HostObjectURLsReporter() {}
 
  public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
@@ -316,50 +389,113 @@ nsHostObjectProtocolHandler::nsHostObjec
 
 nsresult
 nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aScheme,
                                           nsISupports* aObject,
                                           nsIPrincipal* aPrincipal,
                                           nsACString& aUri)
 {
 #ifdef DEBUG
-  nsCOMPtr<BlobImpl> blobImpl(do_QueryInterface(aObject));
-  nsCOMPtr<MediaSource> mediaSource(do_QueryInterface(aObject));
-  nsCOMPtr<DOMMediaStream> mediaStream(do_QueryInterface(aObject));
+  {
+    nsCOMPtr<BlobImpl> blobImpl(do_QueryInterface(aObject));
+    nsCOMPtr<MediaSource> mediaSource(do_QueryInterface(aObject));
+    nsCOMPtr<DOMMediaStream> mediaStream(do_QueryInterface(aObject));
 
-  // We support only these types.
-  MOZ_ASSERT(blobImpl || mediaSource || mediaStream);
+    // We support only these types.
+    MOZ_ASSERT(blobImpl || mediaSource || mediaStream);
+  }
 #endif
 
   Init();
 
   nsresult rv = GenerateURIString(aScheme, aPrincipal, aUri);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = AddDataEntry(aUri, aObject, aPrincipal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(aObject);
+  if (blobImpl) {
+    BroadcastBlobURLRegistration(aUri, blobImpl, aPrincipal);
+  }
+
+  return NS_OK;
+}
+
+/* static */ nsresult
+nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aURI,
+                                          nsISupports* aObject,
+                                          nsIPrincipal* aPrincipal)
+{
   if (!gDataTable) {
     gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
   }
 
   DataInfo* info = new DataInfo;
 
   info->mObject = aObject;
   info->mPrincipal = aPrincipal;
   mozilla::BlobURLsReporter::GetJSStackForBlob(info);
 
-  gDataTable->Put(aUri, info);
+  gDataTable->Put(aURI, info);
   return NS_OK;
 }
 
+/* static */ bool
+nsHostObjectProtocolHandler::GetAllBlobURLEntries(nsTArray<BlobURLRegistrationData>& aRegistrations,
+                                                  ContentParent* aCP)
+{
+  MOZ_ASSERT(aCP);
+
+  if (!gDataTable) {
+    return true;
+  }
+
+  for (auto iter = gDataTable->ConstIter(); !iter.Done(); iter.Next()) {
+    DataInfo* info = iter.UserData();
+    MOZ_ASSERT(info);
+
+    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
+    if (!blobImpl) {
+      continue;
+    }
+
+    PBlobParent* blobParent = aCP->GetOrCreateActorForBlobImpl(blobImpl);
+    if (!blobParent) {
+      return false;
+    }
+
+    aRegistrations.AppendElement(
+      BlobURLRegistrationData(nsCString(iter.Key()), blobParent, nullptr,
+                              IPC::Principal(info->mPrincipal)));
+  }
+
+  return true;
+}
+
 void
-nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri)
+nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri,
+                                             bool aBroadcastToOtherProcesses)
 {
   if (!gDataTable) {
     return;
   }
 
+  DataInfo* info = GetDataInfo(aUri);
+  if (!info) {
+    return;
+  }
+
+  if (aBroadcastToOtherProcesses) {
+    nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(info->mObject);
+    if (blobImpl) {
+      BroadcastBlobURLUnregistration(aUri, info);
+    }
+  }
+
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
 
 nsresult
@@ -393,47 +529,16 @@ nsHostObjectProtocolHandler::GenerateURI
     aUri.Append('/');
   }
 
   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
 
   return NS_OK;
 }
 
-static DataInfo*
-GetDataInfo(const nsACString& aUri)
-{
-  if (!gDataTable) {
-    return nullptr;
-  }
-
-  DataInfo* res;
-
-  // Let's remove any fragment and query from this URI.
-  int32_t hasFragmentPos = aUri.FindChar('#');
-  int32_t hasQueryPos = aUri.FindChar('?');
-
-  int32_t pos = -1;
-  if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
-    pos = std::min(hasFragmentPos, hasQueryPos);
-  } else if (hasFragmentPos >= 0) {
-    pos = hasFragmentPos;
-  } else {
-    pos = hasQueryPos;
-  }
-
-  if (pos < 0) {
-    gDataTable->Get(aUri, &res);
-  } else {
-    gDataTable->Get(StringHead(aUri, pos), &res);
-  }
-
-  return res;
-}
-
 nsIPrincipal*
 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
 {
   if (!gDataTable) {
     return nullptr;
   }
 
   DataInfo* res = GetDataInfo(aUri);
@@ -799,8 +904,9 @@ static const mozilla::Module::ContractID
 
 static const mozilla::Module kHostObjectProtocolHandlerModule = {
   mozilla::Module::kVersion,
   kHostObjectProtocolHandlerCIDs,
   kHostObjectProtocolHandlerContracts
 };
 
 NSMODULE_DEFN(HostObjectProtocolHandler) = &kHostObjectProtocolHandlerModule;
+
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -7,29 +7,32 @@
 #ifndef nsHostObjectProtocolHandler_h
 #define nsHostObjectProtocolHandler_h
 
 #include "mozilla/Attributes.h"
 #include "nsIProtocolHandler.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
+#include "nsTArray.h"
 
 #define BLOBURI_SCHEME "blob"
 #define MEDIASTREAMURI_SCHEME "mediastream"
 #define MEDIASOURCEURI_SCHEME "mediasource"
 #define FONTTABLEURI_SCHEME "moz-fonttable"
 #define RTSPURI_SCHEME "rtsp"
 
 class nsIPrincipal;
 
 namespace mozilla {
 class DOMMediaStream;
 namespace dom {
 class BlobImpl;
+class BlobURLRegistrationData;
+class ContentParent;
 class MediaSource;
 } // namespace dom
 } // namespace mozilla
 
 class nsHostObjectProtocolHandler : public nsIProtocolHandler
 {
 public:
   nsHostObjectProtocolHandler();
@@ -50,20 +53,29 @@ public:
                                     nsACString &aUri);
 
   // Methods for managing uri->object mapping
   // AddDataEntry creates the URI with the given scheme and returns it in aUri
   static nsresult AddDataEntry(const nsACString& aScheme,
                                nsISupports* aObject,
                                nsIPrincipal* aPrincipal,
                                nsACString& aUri);
-  static void RemoveDataEntry(const nsACString& aUri);
+  static void RemoveDataEntry(const nsACString& aUri,
+                              bool aBroadcastToOTherProcesses = true);
   static nsIPrincipal* GetDataEntryPrincipal(const nsACString& aUri);
   static void Traverse(const nsACString& aUri, nsCycleCollectionTraversalCallback& aCallback);
 
+  // IPC or internal use only
+  static nsresult AddDataEntry(const nsACString& aURI,
+                               nsISupports* aObject,
+                               nsIPrincipal* aPrincipal);
+  static bool
+  GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
+                       mozilla::dom::ContentParent* aCP);
+
 protected:
   virtual ~nsHostObjectProtocolHandler() {}
 
 private:
   static void Init(void);
 };
 
 class nsBlobProtocolHandler : public nsHostObjectProtocolHandler
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -124,16 +124,17 @@
 #include "nsChromeRegistryContent.h"
 #include "nsFrameMessageManager.h"
 
 #include "nsIGeolocationProvider.h"
 #include "mozilla/dom/PMemoryReportRequestChild.h"
 #include "mozilla/dom/PCycleCollectWithLogsChild.h"
 
 #include "nsIScriptSecurityManager.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #ifdef MOZ_WEBRTC
 #include "signaling/src/peerconnection/WebrtcGlobalChild.h"
 #endif
 
 #ifdef MOZ_PERMISSIONS
 #include "nsPermission.h"
 #include "nsPermissionManager.h"
@@ -2631,16 +2632,32 @@ ContentChild::RecvInitServiceWorkers(con
 {
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
   return true;
 }
 
 bool
+ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations)
+{
+  for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
+    BlobURLRegistrationData& registration = aRegistrations[i];
+    RefPtr<BlobImpl> blobImpl =
+      static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl();
+    MOZ_ASSERT(blobImpl);
+
+    nsHostObjectProtocolHandler::AddDataEntry(registration.url(), blobImpl,
+                                              registration.principal());
+  }
+
+  return true;
+}
+
+bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
   return true;
 }
 
 bool
@@ -3368,16 +3385,35 @@ ContentChild::RecvNotifyPushSubscription
 {
 #ifndef MOZ_SIMPLEPUSH
   PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
+bool
+ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+                                      const IPC::Principal& aPrincipal)
+{
+  RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl();
+  MOZ_ASSERT(blobImpl);
+
+  nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl, aPrincipal);
+  return true;
+}
+
+bool
+ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
+{
+  nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
+  return true;
+}
+
+
 void
 ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
                                     bool aRecursiveFlag,
                                     nsID& aUUID,
                                     GetFilesHelperChild* aChild)
 {
   MOZ_ASSERT(aChild);
   MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -457,16 +457,19 @@ public:
                            const nsCString& name, const nsCString& UAName,
                            const nsCString& ID, const nsCString& vendor) override;
 
   virtual bool RecvAppInit() override;
 
   virtual bool
   RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
 
+  virtual bool
+  RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
+
   virtual bool RecvLastPrivateDocShellDestroyed() override;
 
   virtual bool RecvVolumes(InfallibleTArray<VolumeInfo>&& aVolumes) override;
 
   virtual bool RecvFilePathUpdate(const nsString& aStorageType,
                                   const nsString& aStorageName,
                                   const nsString& aPath,
                                   const nsCString& aReason) override;
@@ -638,16 +641,23 @@ public:
 
   void
   DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild);
 
   virtual bool
   RecvGetFilesResponse(const nsID& aUUID,
                        const GetFilesResponseResult& aResult) override;
 
+  virtual bool
+  RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+                          const IPC::Principal& aPrincipal) override;
+
+  virtual bool
+  RecvBlobURLUnregistration(const nsCString& aURI) override;
+
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -186,16 +186,17 @@
 #include "ContentProcessManager.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
 #include "nsIBlocklistService.h"
 #include "mozilla/StyleSheetHandle.h"
 #include "mozilla/StyleSheetHandleInlines.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #include "nsIBidiKeyboard.h"
 
 #ifdef MOZ_WEBRTC
 #include "signaling/src/peerconnection/WebrtcGlobalParent.h"
 #endif
 
 #if defined(ANDROID) || defined(LINUX)
@@ -2508,23 +2509,32 @@ ContentParent::InitInternal(ProcessPrior
   nsString sessionName;
   nsString iconPath;
   if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
                                                         iconPath))) {
     Unused << SendSetAudioSessionData(id, sessionName, iconPath);
   }
 #endif
 
-  RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
-  MOZ_ASSERT(swr);
-
-  nsTArray<ServiceWorkerRegistrationData> registrations;
-  swr->GetRegistrations(registrations);
-
-  Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+  {
+    RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(swr);
+
+    nsTArray<ServiceWorkerRegistrationData> registrations;
+    swr->GetRegistrations(registrations);
+    Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+  }
+
+  {
+    nsTArray<BlobURLRegistrationData> registrations;
+    if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations,
+                                                          this)) {
+      Unused << SendInitBlobURLs(registrations);
+    }
+  }
 }
 
 bool
 ContentParent::IsAlive() const
 {
   return mIsAlive;
 }
 
@@ -5691,16 +5701,73 @@ bool
 ContentParent::RecvNotifyLowMemory()
 {
 #ifdef MOZ_CRASHREPORTER
   nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
 #endif
   return true;
 }
 
+/* static */ void
+ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
+                                            BlobImpl* aBlobImpl,
+                                            nsIPrincipal* aPrincipal,
+                                            ContentParent* aIgnoreThisCP)
+{
+  nsCString uri(aURI);
+  IPC::Principal principal(aPrincipal);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    if (cp != aIgnoreThisCP) {
+      PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl);
+      if (blobParent) {
+        Unused << cp->SendBlobURLRegistration(uri, blobParent, principal);
+      }
+    }
+  }
+}
+
+/* static */ void
+ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI,
+                                              ContentParent* aIgnoreThisCP)
+{
+  nsCString uri(aURI);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    if (cp != aIgnoreThisCP) {
+      Unused << cp->SendBlobURLUnregistration(uri);
+    }
+  }
+}
+
+bool
+ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+                                                        PBlobParent* aBlobParent,
+                                                        const Principal& aPrincipal)
+{
+  RefPtr<BlobImpl> blobImpl =
+    static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
+  if (NS_WARN_IF(!blobImpl)) {
+    return false;
+  }
+
+  nsHostObjectProtocolHandler::AddDataEntry(aURI, blobImpl, aPrincipal);
+  BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
+  return true;
+}
+
+bool
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI)
+{
+  nsHostObjectProtocolHandler::RemoveDataEntry(aURI,
+                                               false /* Don't broadcast */);
+  BroadcastBlobURLUnregistration(aURI, this);
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -570,16 +570,34 @@ public:
                                 bool* aWindowIsNew,
                                 InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                 nsCString* aURLToLoad,
                                 layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                 uint64_t* aLayersId) override;
 
   static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
 
+  static void
+  BroadcastBlobURLRegistration(const nsACString& aURI,
+                               BlobImpl* aBlobImpl,
+                               nsIPrincipal* aPrincipal,
+                               ContentParent* aIgnoreThisCP = nullptr);
+
+  static void
+  BroadcastBlobURLUnregistration(const nsACString& aURI,
+                                 ContentParent* aIgnoreThisCP = nullptr);
+
+  virtual bool
+  RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+                                           PBlobParent* aBlobParent,
+                                           const Principal& aPrincipal) override;
+
+  virtual bool
+  RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
+
 protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   void OnNuwaForkTimeout();
 
   bool ShouldContinueFromReplyTimeout() override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -368,16 +368,23 @@ struct GetFilesResponseFailure
 };
 
 union GetFilesResponseResult
 {
   GetFilesResponseSuccess;
   GetFilesResponseFailure;
 };
 
+struct BlobURLRegistrationData
+{
+    nsCString url;
+    PBlob blob;
+    Principal principal;
+};
+
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
     parent opens PCompositorBridge;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
@@ -542,16 +549,21 @@ child:
                   nsCString ID, nsCString vendor);
     async AppInit();
 
     /**
      * Send ServiceWorkerRegistrationData to child process.
      */
     async InitServiceWorkers(ServiceWorkerConfiguration aConfig);
 
+    /**
+     * Send BlobURLRegistrationData to child process.
+     */
+    async InitBlobURLs(BlobURLRegistrationData[] registrations);
+
     // Notify child that last-pb-context-exited notification was observed
     async LastPrivateDocShellDestroyed();
 
     async FilePathUpdate(nsString storageType, nsString storageName, nsString filepath,
                          nsCString reasons);
 
     // Note: Any changes to this structure should also be changed in
     // VolumeInfo above.
@@ -671,16 +683,21 @@ child:
      * audio session.
      */
     async SetAudioSessionData(nsID aID,
                               nsString aDisplayName,
                               nsString aIconPath);
 
     async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
 
+    async BlobURLRegistration(nsCString aURI, PBlob aBlob,
+                              Principal aPrincipal);
+
+    async BlobURLUnregistration(nsCString aURI);
+
 parent:
     /**
      * Tell the content process some attributes of itself.  This is
      * among the first information queried by content processes after
      * startup.  (The message is sync to allow the content process to
      * control when it receives the information.)
      *
      * |id| is a unique ID among all subprocesses.  When |isForApp &&
@@ -1183,16 +1200,21 @@ parent:
      * allows the parent process to save a memory report that can potentially be
      * sent with a crash report from the content process.
      */
      async NotifyLowMemory();
 
      async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
      async DeleteGetFilesRequest(nsID aID);
 
+     async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
+                                                Principal principal);
+
+     async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
+
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/ipc/PermissionMessageUtils.h
+++ b/dom/ipc/PermissionMessageUtils.h
@@ -23,19 +23,23 @@ public:
   {}
 
   explicit Principal(nsIPrincipal* aPrincipal)
     : mPrincipal(aPrincipal)
   {}
 
   operator nsIPrincipal*() const { return mPrincipal.get(); }
 
+  Principal& operator=(const Principal& aOther)
+  {
+    mPrincipal = aOther.mPrincipal;
+    return *this;
+  }
+
 private:
-  // Unimplemented
-  Principal& operator=(Principal&);
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 template <>
 struct ParamTraits<Principal>
 {
   typedef Principal paramType;
   static void Write(Message* aMsg, const paramType& aParam);