Bug 1403706 - Remove race conditions in temporary blob - part 6 - Introducing PTemporaryIPCBlob, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 05 Oct 2017 07:41:42 +0200
changeset 435419 1cf438d8a527223b202a4c772fd0133c848406c6
parent 435418 cb4d136a609e64473d60da1581bd45785eab6227
child 435420 4888323f23616d82416cc4b357e2b6b38b7d508d
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1403706
milestone58.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 1403706 - Remove race conditions in temporary blob - part 6 - Introducing PTemporaryIPCBlob, r=smaug
dom/file/MutableBlobStorage.cpp
dom/file/MutableBlobStorage.h
dom/file/ipc/PTemporaryIPCBlob.ipdl
dom/file/ipc/TemporaryIPCBlobChild.cpp
dom/file/ipc/TemporaryIPCBlobChild.h
dom/file/ipc/TemporaryIPCBlobParent.cpp
dom/file/ipc/TemporaryIPCBlobParent.h
dom/file/ipc/moz.build
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
--- a/dom/file/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -2,16 +2,19 @@
 /* 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 "MutableBlobStorage.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TaskQueue.h"
 #include "File.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "WorkerPrivate.h"
 
@@ -69,88 +72,16 @@ private:
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
   RefPtr<MutableBlobStorageCallback> mCallback;
   RefPtr<Blob> mBlob;
   nsresult mRv;
 };
 
-// This runnable goes back to the main-thread and informs the BlobStorage about
-// the temporary file.
-class FileCreatedRunnable final : public Runnable
-{
-public:
-  FileCreatedRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD)
-    : Runnable("dom::FileCreatedRunnable")
-    , mBlobStorage(aBlobStorage)
-    , mFD(aFD)
-  {
-    MOZ_ASSERT(aBlobStorage);
-    MOZ_ASSERT(aFD);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mBlobStorage->TemporaryFileCreated(mFD);
-    mFD = nullptr;
-    return NS_OK;
-  }
-
-private:
-  ~FileCreatedRunnable()
-  {
-    // If something when wrong, we still have to close the FileDescriptor.
-    if (mFD) {
-      PR_Close(mFD);
-    }
-  }
-
-  RefPtr<MutableBlobStorage> mBlobStorage;
-  PRFileDesc* mFD;
-};
-
-// This runnable creates the temporary file. When done, FileCreatedRunnable is
-// dispatched back to the main-thread.
-class CreateTemporaryFileRunnable final : public Runnable
-{
-public:
-  explicit CreateTemporaryFileRunnable(MutableBlobStorage* aBlobStorage)
-    : Runnable("dom::CreateTemporaryFileRunnable")
-    , mBlobStorage(aBlobStorage)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(XRE_IsParentProcess());
-    MOZ_ASSERT(aBlobStorage);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(XRE_IsParentProcess());
-    MOZ_ASSERT(mBlobStorage);
-
-    PRFileDesc* tempFD = nullptr;
-    nsresult rv = NS_OpenAnonymousTemporaryFile(&tempFD);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_OK;
-    }
-
-    // The ownership of the tempFD is moved to the FileCreatedRunnable.
-    return mBlobStorage->EventTarget()->Dispatch(
-      new FileCreatedRunnable(mBlobStorage, tempFD), NS_DISPATCH_NORMAL);
-  }
-
-private:
-  RefPtr<MutableBlobStorage> mBlobStorage;
-};
-
 // Simple runnable to propagate the error to the BlobStorage.
 class ErrorPropagationRunnable final : public Runnable
 {
 public:
   ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
     : Runnable("dom::ErrorPropagationRunnable")
     , mBlobStorage(aBlobStorage)
     , mRv(aRv)
@@ -276,18 +207,21 @@ private:
   }
 
   PRFileDesc* mFD;
 };
 
 // This runnable is dispatched to the main-thread from the IO thread and its
 // task is to create the blob and inform the callback.
 class CreateBlobRunnable final : public Runnable
+                               , public TemporaryIPCBlobChildCallback
 {
 public:
+  NS_DECL_ISUPPORTS_INHERITED
+
   CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
                      already_AddRefed<nsISupports> aParent,
                      const nsACString& aContentType,
                      already_AddRefed<MutableBlobStorageCallback> aCallback)
     : Runnable("dom::CreateBlobRunnable")
     , mBlobStorage(aBlobStorage)
     , mParent(aParent)
     , mContentType(aContentType)
@@ -297,21 +231,37 @@ public:
     MOZ_ASSERT(aBlobStorage);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
-    mBlobStorage->CreateBlobAndRespond(mParent.forget(), mContentType,
-                                       mCallback.forget());
+    mBlobStorage->AskForBlob(this, mContentType);
     return NS_OK;
   }
 
+  void
+  OperationSucceeded(BlobImpl* aBlobImpl) override
+  {
+    nsCOMPtr<nsISupports> parent(Move(mParent));
+    RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
+
+    RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl);
+    callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK);
+  }
+
+  void
+  OperationFailed(nsresult aRv) override
+  {
+    RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
+    callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
+  }
+
 private:
   ~CreateBlobRunnable()
   {
     MOZ_ASSERT(mBlobStorage);
     // If something when wrong, we still have to release data in the correct
     // thread.
     NS_ProxyRelease(
       "CreateBlobRunnable::mParent",
@@ -322,41 +272,49 @@ private:
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
   nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
+NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
+
 // This task is used to know when the writing is completed. From the IO thread
 // it dispatches a CreateBlobRunnable to the main-thread.
 class LastRunnable final : public Runnable
 {
 public:
   LastRunnable(MutableBlobStorage* aBlobStorage,
+               PRFileDesc* aFD,
                nsISupports* aParent,
                const nsACString& aContentType,
                MutableBlobStorageCallback* aCallback)
     : Runnable("dom::LastRunnable")
     , mBlobStorage(aBlobStorage)
+    , mFD(aFD)
     , mParent(aParent)
     , mContentType(aContentType)
     , mCallback(aCallback)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
     MOZ_ASSERT(aCallback);
+    MOZ_ASSERT(aFD);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(mBlobStorage);
+
+    PR_Close(mFD);
+    mFD = nullptr;
+
     RefPtr<Runnable> runnable =
       new CreateBlobRunnable(mBlobStorage, mParent.forget(),
                              mContentType, mCallback.forget());
     return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   }
 
 private:
   ~LastRunnable()
@@ -368,16 +326,17 @@ private:
       "LastRunnable::mParent",
       mBlobStorage->EventTarget(), mParent.forget());
     NS_ProxyRelease(
       "LastRunnable::mCallback",
       mBlobStorage->EventTarget(), mCallback.forget());
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
+  PRFileDesc* mFD;
   nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
 } // anonymous namespace
 
 MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
@@ -413,16 +372,21 @@ MutableBlobStorage::~MutableBlobStorage(
   if (mFD) {
     RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
     DispatchToIOThread(runnable.forget());
   }
 
   if (mTaskQueue) {
     mTaskQueue->BeginShutdown();
   }
+
+  if (mActor) {
+    NS_ProxyRelease("MutableBlobStorage::mActor",
+                    EventTarget(), mActor.forget());
+  }
 }
 
 void
 MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
                                      const nsACString& aContentType,
                                      MutableBlobStorageCallback* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -432,28 +396,34 @@ MutableBlobStorage::GetBlobWhenReady(nsI
   MOZ_ASSERT(mStorageState != eClosed);
   StorageState previousState = mStorageState;
   mStorageState = eClosed;
 
   if (previousState == eInTemporaryFile) {
     MOZ_ASSERT(mFD);
 
     if (NS_FAILED(mErrorResult)) {
+      MOZ_ASSERT(!mActor);
+
       RefPtr<Runnable> runnable =
         new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
       EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
       return;
     }
 
+    MOZ_ASSERT(mActor);
+
     // We want to wait until all the WriteRunnable are completed. The way we do
     // this is to go to the I/O thread and then we come back: the runnables are
     // executed in order and this LastRunnable will be... the last one.
+    // This Runnable will also close the FD on the I/O thread.
     RefPtr<Runnable> runnable =
-      new LastRunnable(this, aParent, aContentType, aCallback);
+      new LastRunnable(this, mFD, aParent, aContentType, aCallback);
     DispatchToIOThread(runnable.forget());
+    mFD = nullptr;
     return;
   }
 
   // If we are waiting for the temporary file, it's better to wait...
   if (previousState == eWaitingForTemporaryFile) {
     mPendingParent = aParent;
     mPendingContentType = aContentType;
     mPendingCallback = aCallback;
@@ -492,20 +462,17 @@ MutableBlobStorage::Append(const void* a
 
   if (!aLength) {
     return NS_OK;
   }
 
   // If eInMemory is the current Storage state, we could maybe migrate to
   // a temporary file.
   if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength)) {
-    nsresult rv = MaybeCreateTemporaryFile();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    MaybeCreateTemporaryFile();
   }
 
   // If we are already in the temporaryFile mode, we have to dispatch a
   // runnable.
   if (mStorageState == eInTemporaryFile) {
     MOZ_ASSERT(mFD);
 
     RefPtr<WriteRunnable> runnable =
@@ -575,51 +542,80 @@ MutableBlobStorage::ShouldBeTemporarySto
 
   if (!bufferSize.isValid()) {
     return false;
   }
 
   return bufferSize.value() >= mMaxMemory;
 }
 
-nsresult
+void
 MutableBlobStorage::MaybeCreateTemporaryFile()
 {
-  if (XRE_IsParentProcess()) {
-    RefPtr<Runnable> runnable = new CreateTemporaryFileRunnable(this);
-    DispatchToIOThread(runnable.forget());
+  mStorageState = eWaitingForTemporaryFile;
+
+  mozilla::ipc::PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  if (actor) {
+    ActorCreated(actor);
   } else {
-    RefPtr<MutableBlobStorage> self(this);
-    ContentChild::GetSingleton()->
-      AsyncOpenAnonymousTemporaryFile([self](PRFileDesc* prfile) {
-        if (prfile) {
-          // The ownership of the prfile is moved to the FileCreatedRunnable.
-          self->EventTarget()->Dispatch(
-            new FileCreatedRunnable(self, prfile), NS_DISPATCH_NORMAL);
-        }
-      });
+    mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this);
+  }
+}
+
+void
+MutableBlobStorage::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+MutableBlobStorage::ActorCreated(PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
+             mStorageState == eClosed);
+  MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
+
+  // If the object has been already closed and we don't need to execute a
+  // callback, we have nothing else to do.
+  if (mStorageState == eClosed && !mPendingCallback) {
+    return;
   }
 
-  mStorageState = eWaitingForTemporaryFile;
-  return NS_OK;
+  mActor = new TemporaryIPCBlobChild(this);
+  aActor->SendPTemporaryIPCBlobConstructor(mActor);
+
+  // We need manually to increase the reference for this actor because the
+  // IPC allocator method is not triggered. The Release() is called by IPDL
+  // when the actor is deleted.
+  mActor.get()->AddRef();
+
+  // The actor will call us when the FileDescriptor is received.
 }
 
 void
 MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
              mStorageState == eClosed);
   MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aFD);
 
   // If the object has been already closed and we don't need to execute a
   // callback, we need just to close the file descriptor in the correct thread.
   if (mStorageState == eClosed && !mPendingCallback) {
     RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
     DispatchToIOThread(runnable.forget());
+
+    // Let's inform the parent that we have nothing else to do.
+    mActor->SendOperationDone(false, EmptyCString());
+    mActor = nullptr;
     return;
   }
 
   // If we still receiving data, we can proceed in temporary-file mode.
   if (mStorageState == eWaitingForTemporaryFile) {
     mStorageState = eInTemporaryFile;
   }
 
@@ -639,51 +635,48 @@ MutableBlobStorage::TemporaryFileCreated
   // were already waiting for a temporary file-descriptor. Finally we are here,
   // AdoptBuffer runnable is going to write the current buffer into this file.
   // After that, there is nothing else to write, and we dispatch LastRunnable
   // which ends up calling mPendingCallback via CreateBlobRunnable.
   if (mStorageState == eClosed) {
     MOZ_ASSERT(mPendingCallback);
 
     RefPtr<Runnable> runnable =
-      new LastRunnable(this, mPendingParent, mPendingContentType,
+      new LastRunnable(this, mFD, mPendingParent, mPendingContentType,
                        mPendingCallback);
     DispatchToIOThread(runnable.forget());
+    mFD = nullptr;
 
     mPendingParent = nullptr;
     mPendingCallback = nullptr;
   }
 }
 
 void
-MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
-                                         const nsACString& aContentType,
-                                         already_AddRefed<MutableBlobStorageCallback> aCallback)
+MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                               const nsACString& aContentType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState == eClosed);
-  MOZ_ASSERT(mFD);
-
-  nsCOMPtr<nsISupports> parent(aParent);
-  RefPtr<MutableBlobStorageCallback> callback(aCallback);
+  MOZ_ASSERT(!mFD);
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aCallback);
 
-  RefPtr<Blob> blob =
-    File::CreateTemporaryBlob(parent, mFD, 0, mDataLen,
-                              NS_ConvertUTF8toUTF16(aContentType));
-  callback->BlobStoreCompleted(this, blob, NS_OK);
-
-  // ownership of this FD is moved to the BlobImpl.
-  mFD = nullptr;
+  mActor->AskForBlob(aCallback, aContentType);
+  mActor = nullptr;
 }
 
 void
 MutableBlobStorage::ErrorPropagated(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mErrorResult = aRv;
+
+  mActor->SendOperationDone(false, EmptyCString());
+  mActor = nullptr;
 }
 
 void
 MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
 {
   if (!mTaskQueue) {
     nsCOMPtr<nsIEventTarget> target
       = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@@ -698,10 +691,12 @@ MutableBlobStorage::DispatchToIOThread(a
 
 size_t
 MutableBlobStorage::SizeOfCurrentMemoryBuffer() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mStorageState < eInTemporaryFile ? mDataLen : 0;
 }
 
+NS_IMPL_ISUPPORTS(MutableBlobStorage, nsIIPCBackgroundChildCreateCallback)
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -3,46 +3,50 @@
 /* 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/. */
 
 #ifndef mozilla_dom_MutableBlobStorage_h
 #define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "prio.h"
 
 class nsIEventTarget;
 class nsIRunnable;
 
 namespace mozilla {
 
 class TaskQueue;
 
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class MutableBlobStorage;
+class TemporaryIPCBlobChild;
+class TemporaryIPCBlobChildCallback;
 
 class MutableBlobStorageCallback
 {
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                                   Blob* aBlob,
                                   nsresult aRv) = 0;
 };
 
 // This class is main-thread only.
-class MutableBlobStorage final
+class MutableBlobStorage final : public nsIIPCBackgroundChildCreateCallback
 {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage);
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   enum MutableBlobStorageType
   {
     eOnlyInMemory,
     eCouldBeInTemporaryFile,
   };
 
   explicit MutableBlobStorage(MutableBlobStorageType aType,
@@ -54,19 +58,18 @@ public:
   // This method can be called just once.
   // The callback will be called when the Blob is ready.
   void GetBlobWhenReady(nsISupports* aParent,
                         const nsACString& aContentType,
                         MutableBlobStorageCallback* aCallback);
 
   void TemporaryFileCreated(PRFileDesc* aFD);
 
-  void  CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
-                             const nsACString& aContentType,
-                             already_AddRefed<MutableBlobStorageCallback> aCallback);
+  void AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                  const nsACString& aContentType);
 
   void ErrorPropagated(nsresult aRv);
 
   nsIEventTarget* EventTarget()
   {
     MOZ_ASSERT(mEventTarget);
     return mEventTarget;
   }
@@ -77,17 +80,17 @@ public:
 
 private:
   ~MutableBlobStorage();
 
   bool ExpandBufferSize(uint64_t aSize);
 
   bool ShouldBeTemporaryStorage(uint64_t aSize) const;
 
-  nsresult MaybeCreateTemporaryFile();
+  void MaybeCreateTemporaryFile();
 
   void DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable);
 
   // All these variables are touched on the main thread only.
 
   void* mData;
   uint64_t mDataLen;
   uint64_t mDataBufferLen;
@@ -108,16 +111,18 @@ private:
 
   RefPtr<TaskQueue> mTaskQueue;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 
   nsCOMPtr<nsISupports> mPendingParent;
   nsCString mPendingContentType;
   RefPtr<MutableBlobStorageCallback> mPendingCallback;
 
+  RefPtr<TemporaryIPCBlobChild> mActor;
+
   // This value is used when we go from eInMemory to eWaitingForTemporaryFile
   // and eventually eInTemporaryFile. If the size of the buffer is >=
   // mMaxMemory, the creation of the temporary file will start.
   // It's not used if mStorageState is eKeepInMemory.
   uint32_t mMaxMemory;
 };
 
 } // namespace dom
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/PTemporaryIPCBlob.ipdl
@@ -0,0 +1,41 @@
+/* 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 protocol PBackground;
+include protocol PChildToParentStream;
+include protocol PFileDescriptorSet;
+include protocol PIPCBlobInputStream;
+include protocol PParentToChildStream;
+
+include IPCBlob;
+
+namespace mozilla {
+namespace ipc {
+
+union IPCBlobOrError
+{
+  IPCBlob;
+  nsresult;
+};
+
+protocol PTemporaryIPCBlob
+{
+  manager PBackground;
+
+  // When this actor is created on the child side, the parent will send
+  // immediatelly back a FileDescriptor or a __delete__ in case of error.
+  // When the FileDescriptor is received, the child has to call
+  // OperationDone(). When OperationDone() is received on the parent side, the
+  // parent actor will send a __delete__.
+
+child:
+  async FileDesc(FileDescriptor aFD);
+  async __delete__(IPCBlobOrError aBlobOrError);
+
+parent:
+  async OperationDone(bool aSuccess, nsCString aContentType);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobChild.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "TemporaryIPCBlobChild.h"
+#include "mozilla/dom/MutableBlobStorage.h"
+#include <private/pprio.h>
+
+namespace mozilla {
+namespace dom {
+
+TemporaryIPCBlobChild::TemporaryIPCBlobChild(MutableBlobStorage* aStorage)
+  : mMutableBlobStorage(aStorage)
+  , mActive(true)
+{
+  MOZ_ASSERT(aStorage);
+}
+
+TemporaryIPCBlobChild::~TemporaryIPCBlobChild()
+{}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobChild::RecvFileDesc(const FileDescriptor& aFD)
+{
+  MOZ_ASSERT(mActive);
+
+  auto rawFD = aFD.ClonePlatformHandle();
+  PRFileDesc* prfile = PR_ImportFile(PROsfd(rawFD.release()));
+
+  mMutableBlobStorage->TemporaryFileCreated(prfile);
+  mMutableBlobStorage = nullptr;
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobChild::Recv__delete__(const IPCBlobOrError& aData)
+{
+  mActive = false;
+  mMutableBlobStorage = nullptr;
+
+  if (aData.type() == IPCBlobOrError::TIPCBlob) {
+    // This must be always deserialized.
+    RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aData.get_IPCBlob());
+    MOZ_ASSERT(blobImpl);
+
+    if (mCallback) {
+      mCallback->OperationSucceeded(blobImpl);
+    }
+  } else if(mCallback) {
+    MOZ_ASSERT(aData.type() == IPCBlobOrError::Tnsresult);
+    mCallback->OperationFailed(aData.get_nsresult());
+  }
+
+  mCallback = nullptr;
+
+  return IPC_OK();
+}
+
+void
+TemporaryIPCBlobChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActive = false;
+  mMutableBlobStorage = nullptr;
+
+  if (mCallback) {
+    mCallback->OperationFailed(NS_ERROR_FAILURE);
+    mCallback = nullptr;
+  }
+}
+
+void
+TemporaryIPCBlobChild::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+                                  const nsACString& aContentType)
+{
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!mCallback);
+
+  if (!mActive) {
+    aCallback->OperationFailed(NS_ERROR_FAILURE);
+    return;
+  }
+
+  mCallback = aCallback;
+  SendOperationDone(true, nsCString(aContentType));
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobChild.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_ipc_TemporaryIPCBlobChild_h
+#define mozilla_dom_ipc_TemporaryIPCBlobChild_h
+
+#include "mozilla/ipc/PTemporaryIPCBlob.h"
+#include "mozilla/ipc/PTemporaryIPCBlobChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class MutableBlobStorage;
+
+class TemporaryIPCBlobChildCallback
+{
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  virtual void OperationSucceeded(BlobImpl* aBlobImpl) = 0;
+  virtual void OperationFailed(nsresult aRv) = 0;
+};
+
+class TemporaryIPCBlobChild final : public mozilla::ipc::PTemporaryIPCBlobChild
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(TemporaryIPCBlobChild)
+
+  explicit TemporaryIPCBlobChild(MutableBlobStorage* aMutableBlobStorage);
+
+  void
+  AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
+             const nsACString& aContentType);
+
+private:
+  ~TemporaryIPCBlobChild();
+
+  mozilla::ipc::IPCResult
+  RecvFileDesc(const FileDescriptor& aFD) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const IPCBlobOrError& aBlobOrError) override;
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  RefPtr<MutableBlobStorage> mMutableBlobStorage;
+  RefPtr<TemporaryIPCBlobChildCallback> mCallback;
+  bool mActive;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_TemporaryIPCBlobChild_h
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobParent.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "TemporaryIPCBlobParent.h"
+
+#include "mozilla/dom/FileBlobImpl.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsIFileStreams.h"
+#include "TemporaryFileBlobImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+TemporaryIPCBlobParent::TemporaryIPCBlobParent()
+  : mActive(true)
+{}
+
+TemporaryIPCBlobParent::~TemporaryIPCBlobParent()
+{
+  // If we still have mFile, let's remove it.
+  if (mFile) {
+    mFile->Remove(false);
+  }
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::CreateAndShareFile()
+{
+  MOZ_ASSERT(mActive);
+  MOZ_ASSERT(!mFile);
+
+  nsresult rv = NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SendDeleteError(rv);
+  }
+
+  PRFileDesc* fd;
+  rv = mFile->OpenNSPRFileDesc(PR_RDWR, PR_IRWXU, &fd);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SendDeleteError(rv);
+  }
+
+  FileDescriptor fdd =
+    FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd)));
+
+  // The FileDescriptor object owns a duplicate of the file handle; we
+  // must close the original (and clean up the NSPR descriptor).
+  PR_Close(fd);
+
+  Unused << SendFileDesc(fdd);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::RecvOperationDone(const bool& aSuccess,
+                                          const nsCString& aContentType)
+{
+  MOZ_ASSERT(mActive);
+  mActive = false;
+
+  if (!aSuccess) {
+    // Nothing to do.
+    Unused << Send__delete__(this, NS_ERROR_FAILURE);
+    return IPC_OK();
+  }
+
+  // Let's create the BlobImpl.
+  nsCOMPtr<nsIFile> file = Move(mFile);
+
+  RefPtr<TemporaryFileBlobImpl> blobImpl =
+    new TemporaryFileBlobImpl(file, NS_ConvertUTF8toUTF16(aContentType));
+
+  IPCBlob ipcBlob;
+  nsresult rv = IPCBlobUtils::Serialize(blobImpl, Manager(), ipcBlob);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    Unused << Send__delete__(this, NS_ERROR_FAILURE);
+    return IPC_OK();
+  }
+
+  Unused << Send__delete__(this, ipcBlob);
+  return IPC_OK();
+}
+
+void
+TemporaryIPCBlobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActive = false;
+}
+
+mozilla::ipc::IPCResult
+TemporaryIPCBlobParent::SendDeleteError(nsresult aRv)
+{
+  MOZ_ASSERT(mActive);
+  mActive = false;
+
+  Unused << Send__delete__(this, aRv);
+  return IPC_OK();
+}
+
+} // dom namespace
+} // mozilla namespace
+
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/TemporaryIPCBlobParent.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_ipc_TemporaryIPCBlobParent_h
+#define mozilla_dom_ipc_TemporaryIPCBlobParent_h
+
+#include "mozilla/ipc/PTemporaryIPCBlob.h"
+#include "mozilla/ipc/PTemporaryIPCBlobParent.h"
+
+class nsIFile;
+
+namespace mozilla {
+namespace dom {
+
+class TemporaryIPCBlobParent final : public mozilla::ipc::PTemporaryIPCBlobParent
+{
+public:
+  explicit TemporaryIPCBlobParent();
+
+  mozilla::ipc::IPCResult
+  CreateAndShareFile();
+
+private:
+  ~TemporaryIPCBlobParent();
+
+  mozilla::ipc::IPCResult
+  RecvOperationDone(const bool& aSuccess,
+                    const nsCString& aContentType) override;
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  SendDeleteError(nsresult aRv);
+
+  nsCOMPtr<nsIFile> mFile;
+  bool mActive;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_TemporaryIPCBlobParent_h
--- a/dom/file/ipc/moz.build
+++ b/dom/file/ipc/moz.build
@@ -6,38 +6,43 @@
 
 EXPORTS.mozilla.dom.ipc += [
     'IPCBlobInputStream.h',
     'IPCBlobInputStreamChild.h',
     'IPCBlobInputStreamParent.h',
     'IPCBlobInputStreamStorage.h',
     'PendingIPCBlobChild.h',
     'PendingIPCBlobParent.h',
+    'TemporaryIPCBlobChild.h',
+    'TemporaryIPCBlobParent.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'IPCBlobUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'IPCBlobInputStream.cpp',
     'IPCBlobInputStreamChild.cpp',
     'IPCBlobInputStreamParent.cpp',
     'IPCBlobInputStreamStorage.cpp',
     'IPCBlobInputStreamThread.cpp',
     'IPCBlobUtils.cpp',
     'PendingIPCBlobChild.cpp',
     'PendingIPCBlobParent.cpp',
+    'TemporaryIPCBlobChild.cpp',
+    'TemporaryIPCBlobParent.cpp',
 ]
 
 IPDL_SOURCES += [
     'BlobTypes.ipdlh',
     'IPCBlob.ipdlh',
     'PIPCBlobInputStream.ipdl',
     'PPendingIPCBlob.ipdl',
+    'PTemporaryIPCBlob.ipdl',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/file',
     '/dom/ipc',
     '/dom/workers',
     '/xpcom/build',
 ]
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/ipc/PendingIPCBlobChild.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
@@ -233,16 +234,31 @@ BackgroundChildImpl::AllocPPendingIPCBlo
 
 bool
 BackgroundChildImpl::DeallocPPendingIPCBlobChild(PPendingIPCBlobChild* aActor)
 {
   delete aActor;
   return true;
 }
 
+PTemporaryIPCBlobChild*
+BackgroundChildImpl::AllocPTemporaryIPCBlobChild()
+{
+  MOZ_CRASH("This is not supposed to be called.");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPTemporaryIPCBlobChild(PTemporaryIPCBlobChild* aActor)
+{
+  RefPtr<mozilla::dom::TemporaryIPCBlobChild> actor =
+    dont_AddRef(static_cast<mozilla::dom::TemporaryIPCBlobChild*>(aActor));
+  return true;
+}
+
 PIPCBlobInputStreamChild*
 BackgroundChildImpl::AllocPIPCBlobInputStreamChild(const nsID& aID,
                                                    const uint64_t& aSize)
 {
   // IPCBlobInputStreamChild is refcounted. Here it's created and in
   // DeallocPIPCBlobInputStreamChild is released.
 
   RefPtr<mozilla::dom::IPCBlobInputStreamChild> actor =
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -84,16 +84,22 @@ protected:
 
   virtual PIPCBlobInputStreamChild*
   AllocPIPCBlobInputStreamChild(const nsID& aID,
                                 const uint64_t& aSize) override;
 
   virtual bool
   DeallocPIPCBlobInputStreamChild(PIPCBlobInputStreamChild* aActor) override;
 
+  virtual PTemporaryIPCBlobChild*
+  AllocPTemporaryIPCBlobChild() override;
+
+  virtual bool
+  DeallocPTemporaryIPCBlobChild(PTemporaryIPCBlobChild* aActor) override;
+
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
                                override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
 
   virtual PCamerasChild*
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/PGamepadTestChannelParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/PendingIPCBlobParent.h"
+#include "mozilla/dom/ipc/TemporaryIPCBlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
@@ -313,16 +314,37 @@ BackgroundParentImpl::DeallocPPendingIPC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+PTemporaryIPCBlobParent*
+BackgroundParentImpl::AllocPTemporaryIPCBlobParent()
+{
+  return new mozilla::dom::TemporaryIPCBlobParent();
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPTemporaryIPCBlobConstructor(PTemporaryIPCBlobParent* aActor)
+{
+  mozilla::dom::TemporaryIPCBlobParent* actor =
+    static_cast<mozilla::dom::TemporaryIPCBlobParent*>(aActor);
+  return actor->CreateAndShareFile();
+}
+
+bool
+BackgroundParentImpl::DeallocPTemporaryIPCBlobParent(PTemporaryIPCBlobParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PIPCBlobInputStreamParent*
 BackgroundParentImpl::AllocPIPCBlobInputStreamParent(const nsID& aID,
                                                      const uint64_t& aSize)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -94,16 +94,25 @@ protected:
   virtual mozilla::ipc::IPCResult
   RecvPIPCBlobInputStreamConstructor(PIPCBlobInputStreamParent* aActor,
                                      const nsID& aID,
                                      const uint64_t& aSize) override;
 
   virtual bool
   DeallocPIPCBlobInputStreamParent(PIPCBlobInputStreamParent* aActor) override;
 
+  virtual PTemporaryIPCBlobParent*
+  AllocPTemporaryIPCBlobParent() override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPTemporaryIPCBlobConstructor(PTemporaryIPCBlobParent* actor) override;
+
+  virtual bool
+  DeallocPTemporaryIPCBlobParent(PTemporaryIPCBlobParent* aActor) override;
+
   virtual PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const FileDescriptor& aFileDescriptor)
                                 override;
 
   virtual bool
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
                                   override;
 
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -13,16 +13,17 @@ include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
 include protocol PHttpBackgroundChannel;
 include protocol PIPCBlobInputStream;
 include protocol PPendingIPCBlob;
+include protocol PTemporaryIPCBlob;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PQuota;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PServiceWorkerManager;
 include protocol PWebAuthnTransaction;
 include protocol PUDPSocket;
@@ -62,16 +63,17 @@ sync protocol PBackground
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
   manages PHttpBackgroundChannel;
   manages PIPCBlobInputStream;
   manages PPendingIPCBlob;
+  manages PTemporaryIPCBlob;
   manages PMessagePort;
   manages PCameras;
   manages PQuota;
   manages PChildToParentStream;
   manages PParentToChildStream;
   manages PServiceWorkerManager;
   manages PWebAuthnTransaction;
   manages PUDPSocket;
@@ -127,16 +129,18 @@ parent:
   async PGamepadEventChannel();
 
   async PGamepadTestChannel();
 
   async PHttpBackgroundChannel(uint64_t channelId);
 
   async PWebAuthnTransaction();
 
+  async PTemporaryIPCBlob();
+
 child:
   async PCache();
   async PCacheStreamControl();
 
   async PParentToChildStream();
 
   async PPendingIPCBlob(IPCBlob blob);