Bug 1353629 - PBlob refactoring - part 7 - IPCBlobInputStream must implement nsIAsyncInputStream, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 24 Apr 2017 12:09:40 +0200
changeset 354622 a1ff18334e6b81c5f8886feaf39610e790fcfc96
parent 354621 8496fe603461e33bff5caf5b42509476c0488aac
child 354623 cee81d4988a513ca5f98bd81d94214ea5697d70d
push id31707
push userkwierso@gmail.com
push dateMon, 24 Apr 2017 22:53:41 +0000
treeherdermozilla-central@abdcc8dfc283 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1353629
milestone55.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 1353629 - PBlob refactoring - part 7 - IPCBlobInputStream must implement nsIAsyncInputStream, r=smaug In order to retrieve data from an IPCBlobInputStream, it must be used as nsIAsyncInputStream.
dom/file/ipc/IPCBlobInputStream.cpp
dom/file/ipc/IPCBlobInputStream.h
dom/file/ipc/IPCBlobInputStreamChild.cpp
dom/file/ipc/IPCBlobInputStreamChild.h
dom/file/ipc/IPCBlobInputStreamParent.cpp
dom/file/ipc/IPCBlobInputStreamParent.h
dom/file/ipc/IPCBlobUtils.cpp
dom/file/ipc/PIPCBlobInputStream.ipdl
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -1,105 +1,273 @@
 /* -*- 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 "IPCBlobInputStream.h"
 #include "IPCBlobInputStreamChild.h"
+#include "nsIAsyncInputStream.h"
+#include "mozilla/SystemGroup.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+class CallbackRunnable final : public CancelableRunnable
+{
+public:
+  static void
+  Execute(nsIInputStreamCallback* aCallback,
+          nsIEventTarget* aEventTarget,
+          IPCBlobInputStream* aStream)
+  {
+    RefPtr<CallbackRunnable> runnable =
+      new CallbackRunnable(aCallback, aStream);
+
+    nsCOMPtr<nsIEventTarget> target = aEventTarget;
+    if (!target) {
+      target = SystemGroup::EventTargetFor(TaskCategory::Other);
+    }
+
+    target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mCallback->OnInputStreamReady(mStream);
+    mCallback = nullptr;
+    mStream = nullptr;
+    return NS_OK;
+  }
+
+private:
+  CallbackRunnable(nsIInputStreamCallback* aCallback,
+                   IPCBlobInputStream* aStream)
+    : mCallback(aCallback)
+    , mStream(aStream)
+  {
+    MOZ_ASSERT(mCallback);
+    MOZ_ASSERT(mStream);
+  }
+
+  nsCOMPtr<nsIInputStreamCallback> mCallback;
+  RefPtr<IPCBlobInputStream> mStream;
+};
+
+} // anonymous
+
 NS_IMPL_ADDREF(IPCBlobInputStream);
 NS_IMPL_RELEASE(IPCBlobInputStream);
 
 NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
   : mActor(aActor)
+  , mState(eInit)
 {
   MOZ_ASSERT(aActor);
 }
 
 IPCBlobInputStream::~IPCBlobInputStream()
 {
   Close();
 }
 
 // nsIInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::Available(uint64_t* aLength)
 {
-  if (!mActor) {
-    return NS_BASE_STREAM_CLOSED;
+  // We don't have a remoteStream yet. Let's return the full known size.
+  if (mState == eInit || mState == ePending) {
+    *aLength = mActor->Size();
+    return NS_OK;
   }
 
-  *aLength = mActor->Size();
-  return NS_OK;
+  if (mState == eRunning) {
+    MOZ_ASSERT(mRemoteStream);
+    return mRemoteStream->Available(aLength);
+  }
+
+  MOZ_ASSERT(mState == eClosed);
+  return NS_BASE_STREAM_CLOSED;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // Read is not available is we don't have a remoteStream.
+  if (mState == eInit || mState == ePending) {
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  }
+
+  if (mState == eRunning) {
+    return mRemoteStream->Read(aBuffer, aCount, aReadCount);
+  }
+
+  MOZ_ASSERT(mState == eClosed);
+  return NS_BASE_STREAM_CLOSED;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                  uint32_t aCount, uint32_t *aResult)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // ReadSegments is not available is we don't have a remoteStream.
+  if (mState == eInit || mState == ePending) {
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  }
+
+  if (mState == eRunning) {
+    return mRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+  }
+
+  MOZ_ASSERT(mState == eClosed);
+  return NS_BASE_STREAM_CLOSED;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  *aNonBlocking = true;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Close()
 {
   if (mActor) {
     mActor->ForgetStream(this);
     mActor = nullptr;
   }
 
+  if (mRemoteStream) {
+    mRemoteStream->Close();
+    mRemoteStream = nullptr;
+  }
+
+  mCallback = nullptr;
+
+  mState = eClosed;
   return NS_OK;
 }
 
 // nsICloneableInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetCloneable(bool* aCloneable)
 {
-  *aCloneable = !!mActor;
+  *aCloneable = mState != eClosed;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Clone(nsIInputStream** aResult)
 {
-  if (!mActor) {
-    return NS_ERROR_FAILURE;
+  if (mState == eClosed) {
+    return NS_BASE_STREAM_CLOSED;
   }
 
+  MOZ_ASSERT(mActor);
+
   nsCOMPtr<nsIInputStream> stream = mActor->CreateStream();
   stream.forget(aResult);
   return NS_OK;
 }
 
+// nsIAsyncInputStream interface
+
+NS_IMETHODIMP
+IPCBlobInputStream::CloseWithStatus(nsresult aStatus)
+{
+  return Close();
+}
+
+NS_IMETHODIMP
+IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+                              uint32_t aFlags, uint32_t aRequestedCount,
+                              nsIEventTarget* aEventTarget)
+{
+  // See IPCBlobInputStream.h for more information about this state machine.
+
+  switch (mState) {
+  // First call, we need to retrieve the stream from the parent actor.
+  case eInit:
+    MOZ_ASSERT(mActor);
+
+    mCallback = aCallback;
+    mCallbackEventTarget = aEventTarget;
+    mState = ePending;
+
+    mActor->StreamNeeded(this);
+    return NS_OK;
+
+  // We are still waiting for the remote inputStream
+  case ePending:
+    if (mCallback && aCallback) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mCallback = aCallback;
+    mCallbackEventTarget = aEventTarget;
+    return NS_OK;
+
+  // We have the remote inputStream, let's execute the callback immediately.
+  case eRunning:
+    if (aCallback) {
+      CallbackRunnable::Execute(aCallback, aEventTarget, this);
+    }
+    return NS_OK;
+
+  // Stream is closed.
+  default:
+    MOZ_ASSERT(mState == eClosed);
+    return NS_BASE_STREAM_CLOSED;
+  }
+}
+
+void
+IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
+{
+  // We have been closed in the meantime.
+  if (mState == eClosed) {
+    if (aInputStream) {
+      aInputStream->Close();
+    }
+    return;
+  }
+
+  // If aInputStream is null, it means that the serialization went wrong or the
+  // stream is not available anymore. We keep the state as pending just to block
+  // any additional operation.
+
+  if (aInputStream && mCallback) {
+    MOZ_ASSERT(mState == ePending);
+    MOZ_ASSERT(mCallback);
+
+    mRemoteStream = aInputStream;
+    mState = eRunning;
+
+    CallbackRunnable::Execute(mCallback, mCallbackEventTarget, this);
+  }
+
+  mCallback = nullptr;
+  mCallbackEventTarget = nullptr;
+}
+
 // nsIIPCSerializableInputStream
 
 void
 IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors)
 {
   IPCBlobInputStreamParams params;
   params.id() = mActor->ID();
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -2,39 +2,70 @@
 /* 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_IPCBlobInputStream_h
 #define mozilla_dom_ipc_IPCBlobInputStream_h
 
-#include "nsIInputStream.h"
+#include "nsIAsyncInputStream.h"
 #include "nsICloneableInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 
 namespace mozilla {
 namespace dom {
 
 class IPCBlobInputStreamChild;
 
-class IPCBlobInputStream final : public nsIInputStream
+class IPCBlobInputStream final : public nsIAsyncInputStream
                                , public nsICloneableInputStream
                                , public nsIIPCSerializableInputStream
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
+  void
+  StreamReady(nsIInputStream* aInputStream);
+
 private:
   ~IPCBlobInputStream();
 
   RefPtr<IPCBlobInputStreamChild> mActor;
+
+  // This is the list of possible states.
+  enum {
+    // The initial state. Only ::Available() can be used without receiving an
+    // error. The available size is known by the actor.
+    eInit,
+
+    // AsyncWait() has been called for the first time. SendStreamNeeded() has
+    // been called and we are waiting for the 'real' inputStream.
+    ePending,
+
+    // When the child receives the stream from the parent, we move to this
+    // state. The received stream is stored in mRemoteStream. From now on, any
+    // method call will be forwared to mRemoteStream.
+    eRunning,
+
+    // If Close() or CloseWithStatus() is called, we move to this state.
+    // mRemoveStream is released and any method will return
+    // NS_BASE_STREAM_CLOSED.
+    eClosed,
+  } mState;
+
+  nsCOMPtr<nsIInputStream> mRemoteStream;
+
+  // These 2 values are set only if mState is ePending.
+  nsCOMPtr<nsIInputStreamCallback> mCallback;
+  nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_IPCBlobInputStream_h
--- a/dom/file/ipc/IPCBlobInputStreamChild.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp
@@ -18,24 +18,74 @@ class DeleteRunnable final : public Runn
 public:
   explicit DeleteRunnable(IPCBlobInputStreamChild* aActor)
     : mActor(aActor)
   {}
 
   NS_IMETHOD
   Run() override
   {
-    mActor->Send__delete__(mActor);
+    if (mActor->IsAlive()) {
+      mActor->Send__delete__(mActor);
+    }
     return NS_OK;
   }
 
 private:
   RefPtr<IPCBlobInputStreamChild> mActor;
 };
 
+// This runnable is used in case StreamNeeded() has been called on a non-owning
+// thread.
+class StreamNeededRunnable final : public Runnable
+{
+public:
+  explicit StreamNeededRunnable(IPCBlobInputStreamChild* aActor)
+    : mActor(aActor)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    if (mActor->IsAlive()) {
+      mActor->SendStreamNeeded();
+    }
+    return NS_OK;
+  }
+
+private:
+  RefPtr<IPCBlobInputStreamChild> mActor;
+};
+
+// When the stream has been received from the parent, we inform the
+// IPCBlobInputStream.
+class StreamReadyRunnable final : public CancelableRunnable
+{
+public:
+  StreamReadyRunnable(IPCBlobInputStream* aDestinationStream,
+                      nsIInputStream* aCreatedStream)
+    : mDestinationStream(aDestinationStream)
+    , mCreatedStream(aCreatedStream)
+  {
+    MOZ_ASSERT(mDestinationStream);
+    // mCreatedStream can be null.
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mDestinationStream->StreamReady(mCreatedStream);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<IPCBlobInputStream> mDestinationStream;
+  nsCOMPtr<nsIInputStream> mCreatedStream;
+};
+
 } // anonymous
 
 IPCBlobInputStreamChild::IPCBlobInputStreamChild(const nsID& aID,
                                                  uint64_t aSize)
   : mMutex("IPCBlobInputStreamChild::mMutex")
   , mID(aID)
   , mSize(aSize)
   , mActorAlive(true)
@@ -43,19 +93,27 @@ IPCBlobInputStreamChild::IPCBlobInputStr
 {}
 
 IPCBlobInputStreamChild::~IPCBlobInputStreamChild()
 {}
 
 void
 IPCBlobInputStreamChild::ActorDestroy(IProtocol::ActorDestroyReason aReason)
 {
+  MutexAutoLock lock(mMutex);
   mActorAlive = false;
 }
 
+bool
+IPCBlobInputStreamChild::IsAlive()
+{
+  MutexAutoLock lock(mMutex);
+  return mActorAlive;
+}
+
 already_AddRefed<nsIInputStream>
 IPCBlobInputStreamChild::CreateStream()
 {
   MutexAutoLock lock(mMutex);
 
   RefPtr<IPCBlobInputStream> stream = new IPCBlobInputStream(this);
   mStreams.AppendElement(stream);
   return stream.forget();
@@ -81,10 +139,45 @@ IPCBlobInputStreamChild::ForgetStream(IP
     Send__delete__(this);
     return;
   }
 
   RefPtr<DeleteRunnable> runnable = new DeleteRunnable(this);
   mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
+void
+IPCBlobInputStreamChild::StreamNeeded(IPCBlobInputStream* aStream)
+{
+  MutexAutoLock lock(mMutex);
+  MOZ_ASSERT(mStreams.Contains(aStream));
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mStream = aStream;
+  opt->mThread = NS_GetCurrentThread();
+
+  if (mOwningThread == NS_GetCurrentThread()) {
+    SendStreamNeeded();
+    return;
+  }
+
+  RefPtr<StreamNeededRunnable> runnable = new StreamNeededRunnable(this);
+  mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+}
+
+mozilla::ipc::IPCResult
+IPCBlobInputStreamChild::RecvStreamReady(const OptionalIPCStream& aStream)
+{
+  MOZ_ASSERT(!mPendingOperations.IsEmpty());
+
+  nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
+
+  RefPtr<StreamReadyRunnable> runnable =
+    new StreamReadyRunnable(mPendingOperations[0].mStream, stream);
+  mPendingOperations[0].mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+
+  mPendingOperations.RemoveElementAt(0);
+
+  return IPC_OK();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/file/ipc/IPCBlobInputStreamChild.h
+++ b/dom/file/ipc/IPCBlobInputStreamChild.h
@@ -23,16 +23,19 @@ class IPCBlobInputStreamChild final
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IPCBlobInputStreamChild)
 
   IPCBlobInputStreamChild(const nsID& aID, uint64_t aSize);
 
   void
   ActorDestroy(IProtocol::ActorDestroyReason aReason) override;
 
+  bool
+  IsAlive();
+
   already_AddRefed<nsIInputStream>
   CreateStream();
 
   void
   ForgetStream(IPCBlobInputStream* aStream);
 
   const nsID&
   ID() const
@@ -41,16 +44,22 @@ public:
   }
 
   uint64_t
   Size() const
   {
     return mSize;
   }
 
+  void
+  StreamNeeded(IPCBlobInputStream* aStream);
+
+  mozilla::ipc::IPCResult
+  RecvStreamReady(const OptionalIPCStream& aStream) override;
+
 private:
   ~IPCBlobInputStreamChild();
 
   // Raw pointers because these streams keep this actor alive. When the last
   // stream is unregister, the actor will be deleted. This list is protected by
   // mutex.
   nsTArray<IPCBlobInputStream*> mStreams;
 
@@ -58,15 +67,23 @@ private:
   Mutex mMutex;
 
   const nsID mID;
   const uint64_t mSize;
 
   // false when ActorDestroy() is called.
   bool mActorAlive;
 
+  // This struct and the array are used for creating streams when needed.
+  struct PendingOperation
+  {
+    RefPtr<IPCBlobInputStream> mStream;
+    nsCOMPtr<nsIThread> mThread;
+  };
+  nsTArray<PendingOperation> mPendingOperations;
+
   nsCOMPtr<nsIThread> mOwningThread;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_IPCBlobInputStreamChild_h
--- a/dom/file/ipc/IPCBlobInputStreamParent.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamParent.cpp
@@ -6,40 +6,95 @@
 
 #include "IPCBlobInputStreamParent.h"
 #include "IPCBlobInputStreamStorage.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+template<typename M>
 /* static */ IPCBlobInputStreamParent*
 IPCBlobInputStreamParent::Create(nsIInputStream* aInputStream, uint64_t aSize,
-                                 nsresult* aRv)
+                                 nsresult* aRv, M* aManager)
 {
   MOZ_ASSERT(aInputStream);
   MOZ_ASSERT(aRv);
 
   nsID id;
   *aRv = nsContentUtils::GenerateUUIDInPlace(id);
   if (NS_WARN_IF(NS_FAILED(*aRv))) {
     return nullptr;
   }
 
   IPCBlobInputStreamStorage::Get()->AddStream(aInputStream, id);
 
-  return new IPCBlobInputStreamParent(id, aSize);
+  return new IPCBlobInputStreamParent(id, aSize, aManager);
 }
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
-                                                   uint64_t aSize)
+                                                   uint64_t aSize,
+                                                   nsIContentParent* aManager)
   : mID(aID)
   , mSize(aSize)
+  , mContentManager(aManager)
+  , mPBackgroundManager(nullptr)
+{}
+
+IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
+                                                   uint64_t aSize,
+                                                   PBackgroundParent* aManager)
+  : mID(aID)
+  , mSize(aSize)
+  , mContentManager(nullptr)
+  , mPBackgroundManager(aManager)
 {}
 
 void
 IPCBlobInputStreamParent::ActorDestroy(IProtocol::ActorDestroyReason aReason)
 {
+  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+
+  mContentManager = nullptr;
+  mPBackgroundManager = nullptr;
+
   IPCBlobInputStreamStorage::Get()->ForgetStream(mID);
 }
 
+mozilla::ipc::IPCResult
+IPCBlobInputStreamParent::RecvStreamNeeded()
+{
+  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+
+  nsCOMPtr<nsIInputStream> stream;
+  IPCBlobInputStreamStorage::Get()->GetStream(mID, getter_AddRefs(stream));
+  if (!stream) {
+    if (!SendStreamReady(void_t())) {
+      return IPC_FAIL(this, "SendStreamReady failed");
+    }
+
+    return IPC_OK();
+  }
+
+  AutoIPCStream ipcStream;
+  bool ok = false;
+
+  if (mContentManager) {
+    MOZ_ASSERT(NS_IsMainThread());
+    ok = ipcStream.Serialize(stream, mContentManager);
+  } else {
+    MOZ_ASSERT(mPBackgroundManager);
+    ok = ipcStream.Serialize(stream, mPBackgroundManager);
+  }
+
+  if (NS_WARN_IF(!ok)) {
+    return IPC_FAIL(this, "SendStreamReady failed");
+  }
+
+  if (!SendStreamReady(ipcStream.TakeValue())) {
+    return IPC_FAIL(this, "SendStreamReady failed");
+  }
+
+  return IPC_OK();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/file/ipc/IPCBlobInputStreamParent.h
+++ b/dom/file/ipc/IPCBlobInputStreamParent.h
@@ -16,37 +16,51 @@ namespace dom {
 
 class IPCBlobInputStreamParent final
   : public mozilla::ipc::PIPCBlobInputStreamParent
 {
 public:
   // The size of the inputStream must be passed as argument in order to avoid
   // the use of nsIInputStream::Available() which could open a fileDescriptor in
   // case the stream is a nsFileStream.
+  template<typename M>
   static IPCBlobInputStreamParent*
-  Create(nsIInputStream* aInputStream, uint64_t aSize, nsresult* aRv);
+  Create(nsIInputStream* aInputStream, uint64_t aSize, nsresult* aRv,
+         M* aManager);
 
   void
   ActorDestroy(IProtocol::ActorDestroyReason aReason) override;
 
   const nsID&
   ID() const
   {
     return mID;
   }
 
   uint64_t
   Size() const
   {
     return mSize;
   }
 
+  mozilla::ipc::IPCResult
+  RecvStreamNeeded() override;
+
 private:
-  IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize);
+  IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
+                           nsIContentParent* aManager);
+
+  IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
+                           mozilla::ipc::PBackgroundParent* aManager);
 
   const nsID mID;
   const uint64_t mSize;
+
+  // Only 1 of these 2 is set. Raw pointer because these 2 managers are keeping
+  // the parent actor alive. The pointers will be nullified in ActorDestroyed.
+  nsIContentParent* mContentManager;
+  mozilla::ipc::PBackgroundParent* mPBackgroundManager;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_IPCBlobInputStreamParent_h
--- a/dom/file/ipc/IPCBlobUtils.cpp
+++ b/dom/file/ipc/IPCBlobUtils.cpp
@@ -71,17 +71,17 @@ nsresult
 SerializeInputStreamParent(nsIInputStream* aInputStream, uint64_t aSize,
                            IPCBlob& aIPCBlob, M* aManager)
 {
   // Parent to Child we always send a IPCBlobInputStream.
   MOZ_ASSERT(XRE_IsParentProcess());
 
   nsresult rv;
   IPCBlobInputStreamParent* parentActor =
-    IPCBlobInputStreamParent::Create(aInputStream, aSize, &rv);
+    IPCBlobInputStreamParent::Create(aInputStream, aSize, &rv, aManager);
   if (!parentActor) {
     return rv;
   }
 
   if (!aManager->SendPIPCBlobInputStreamConstructor(parentActor,
                                                     parentActor->ID(),
                                                     parentActor->Size())) {
     return NS_ERROR_FAILURE;
--- a/dom/file/ipc/PIPCBlobInputStream.ipdl
+++ b/dom/file/ipc/PIPCBlobInputStream.ipdl
@@ -1,22 +1,32 @@
 /* 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 PContent;
 include protocol PContentBridge;
+include protocol PFileDescriptorSet;
+include protocol PParentToChildStream;
+
+include IPCStream;
 
 namespace mozilla {
 namespace ipc {
 
 protocol PIPCBlobInputStream
 {
   manager PBackground or PContent or PContentBridge;
 
 parent:
+  async StreamNeeded();
+
   async __delete__();
+
+child:
+  async StreamReady(OptionalIPCStream aStream);
 };
 
 } // namespace dom
 } // namespace mozilla