Bug 1645786 - IPCBlobInputStream for socket process, r=smaug,kershaw
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 25 Jun 2020 12:21:14 +0000
changeset 537377 a6de6ce387db3bd7f362ce5ff120af413ecdd10f
parent 537376 3022366753c97ef9225c83250d9ff0c76ddf7b4e
child 537378 93905b640ebaacaaad06a7130fa894bded52fc6f
push id37541
push usercbrindusan@mozilla.com
push dateThu, 25 Jun 2020 16:18:39 +0000
treeherdermozilla-central@324d5257f6f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, kershaw
bugs1645786
milestone79.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 1645786 - IPCBlobInputStream for socket process, r=smaug,kershaw Differential Revision: https://phabricator.services.mozilla.com/D80680
dom/file/ipc/IPCBlobInputStream.cpp
dom/file/ipc/IPCBlobInputStream.h
dom/file/ipc/IPCBlobInputStreamParent.cpp
dom/file/ipc/IPCBlobInputStreamParent.h
dom/file/ipc/IPCBlobInputStreamThread.cpp
dom/file/ipc/IPCBlobUtils.h
dom/file/ipc/PIPCBlobInputStream.ipdl
dom/indexedDB/ActorsParent.cpp
ipc/glue/InputStreamParams.ipdlh
ipc/glue/InputStreamUtils.cpp
netwerk/ipc/PSocketProcess.ipdl
netwerk/ipc/SocketProcessChild.cpp
netwerk/ipc/SocketProcessChild.h
netwerk/ipc/SocketProcessParent.cpp
netwerk/ipc/SocketProcessParent.h
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -1,29 +1,34 @@
 /* -*- 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 "IPCBlobInputStreamParent.h"
 #include "IPCBlobInputStreamStorage.h"
 #include "mozilla/ipc/InputStreamParams.h"
+#include "mozilla/net/SocketProcessParent.h"
 #include "mozilla/SlicedInputStream.h"
 #include "mozilla/NonBlockingAsyncInputStream.h"
 #include "IPCBlobInputStreamThread.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIPipe.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 namespace mozilla {
+
+using net::SocketProcessParent;
+
 namespace dom {
 
 class IPCBlobInputStream;
 
 namespace {
 
 class InputStreamCallbackRunnable final : public CancelableRunnable {
  public:
@@ -579,35 +584,59 @@ IPCBlobInputStream::OnInputStreamReady(n
 void IPCBlobInputStream::Serialize(
     mozilla::ipc::InputStreamParams& aParams,
     FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
     uint32_t aMaxSize, uint32_t* aSizeUsed,
     mozilla::ipc::ParentToChildStreamActorManager* aManager) {
   MOZ_ASSERT(aSizeUsed);
   *aSizeUsed = 0;
 
-  SerializeInternal(aParams);
+  // So far we support only socket process serialization.
+  MOZ_DIAGNOSTIC_ASSERT(aManager == SocketProcessParent::GetSingleton(),
+                        "Serializing an IPCBlobInputStream parent to child is "
+                        "wrong! The caller must be fixed! See IPCBlobUtils.h.");
+  SocketProcessParent* socketActor = SocketProcessParent::GetSingleton();
+
+  nsresult rv;
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  RefPtr<IPCBlobInputStreamParent> parentActor;
+  {
+    MutexAutoLock lock(mMutex);
+    rv = EnsureAsyncRemoteStream(lock);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    asyncRemoteStream = mAsyncRemoteStream;
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+
+  MOZ_ASSERT(asyncRemoteStream);
+
+  parentActor = IPCBlobInputStreamParent::Create(asyncRemoteStream, mLength, 0,
+                                                 &rv, socketActor);
+  MOZ_ASSERT(parentActor);
+
+  if (!socketActor->SendPIPCBlobInputStreamConstructor(
+          parentActor, parentActor->ID(), parentActor->Size())) {
+    MOZ_CRASH("The serialization is not supposed to fail");
+  }
+
+  aParams = mozilla::ipc::IPCBlobInputStreamParams(parentActor);
 }
 
 void IPCBlobInputStream::Serialize(
     mozilla::ipc::InputStreamParams& aParams,
     FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
     uint32_t aMaxSize, uint32_t* aSizeUsed,
     mozilla::ipc::ChildToParentStreamActorManager* aManager) {
   MOZ_ASSERT(aSizeUsed);
   *aSizeUsed = 0;
 
-  SerializeInternal(aParams);
-}
-
-void IPCBlobInputStream::SerializeInternal(
-    mozilla::ipc::InputStreamParams& aParams) {
   MutexAutoLock lock(mMutex);
 
-  mozilla::ipc::IPCBlobInputStreamParams params;
+  mozilla::ipc::IPCBlobInputStreamRef params;
   params.id() = mActor->ID();
   params.start() = mStart;
   params.length() = mLength;
 
   aParams = params;
 }
 
 bool IPCBlobInputStream::Deserialize(
@@ -847,17 +876,17 @@ class InputStreamLengthCallbackRunnable 
         mStream(aStream),
         mLength(aLength) {
     MOZ_ASSERT(mCallback);
     MOZ_ASSERT(mStream);
   }
 
   nsCOMPtr<nsIInputStreamLengthCallback> mCallback;
   RefPtr<IPCBlobInputStream> mStream;
-  int64_t mLength;
+  const int64_t mLength;
 };
 
 }  // namespace
 
 // nsIAsyncInputStreamLength
 
 NS_IMETHODIMP
 IPCBlobInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -50,18 +50,16 @@ class IPCBlobInputStream final : public 
   NS_DECL_NSIASYNCINPUTSTREAMLENGTH
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
   void StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
   void LengthReady(int64_t aLength);
 
-  void SerializeInternal(mozilla::ipc::InputStreamParams& aParams);
-
   // mozIIPCBlobInputStream
   NS_IMETHOD_(nsIInputStream*) GetInternalStream() override {
     if (mRemoteStream) {
       return mRemoteStream;
     }
 
     if (mAsyncRemoteStream) {
       return mAsyncRemoteStream;
--- a/dom/file/ipc/IPCBlobInputStreamParent.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamParent.cpp
@@ -46,45 +46,73 @@ already_AddRefed<IPCBlobInputStreamParen
   return actor.forget();
 }
 
 template already_AddRefed<IPCBlobInputStreamParent>
 IPCBlobInputStreamParent::Create<mozilla::ipc::PBackgroundParent>(
     nsIInputStream*, uint64_t, uint64_t, nsresult*,
     mozilla::ipc::PBackgroundParent*);
 
+/* static */
+already_AddRefed<IPCBlobInputStreamParent> IPCBlobInputStreamParent::Create(
+    const nsID& aID, uint64_t aSize, SocketProcessParent* aManager) {
+  RefPtr<IPCBlobInputStreamParent> actor =
+      new IPCBlobInputStreamParent(aID, aSize, aManager);
+
+  actor->mCallback = IPCBlobInputStreamStorage::Get()->TakeCallback(aID);
+
+  return actor.forget();
+}
+
+template already_AddRefed<IPCBlobInputStreamParent>
+IPCBlobInputStreamParent::Create<mozilla::net::SocketProcessParent>(
+    nsIInputStream*, uint64_t, uint64_t, nsresult*,
+    mozilla::net::SocketProcessParent*);
+
 template already_AddRefed<IPCBlobInputStreamParent>
 IPCBlobInputStreamParent::Create<ContentParent>(nsIInputStream*, uint64_t,
                                                 uint64_t, nsresult*,
                                                 ContentParent*);
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
                                                    uint64_t aSize,
                                                    ContentParent* aManager)
     : mID(aID),
       mSize(aSize),
       mContentManager(aManager),
       mPBackgroundManager(nullptr),
+      mSocketProcessManager(nullptr),
       mMigrating(false) {}
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
                                                    uint64_t aSize,
                                                    PBackgroundParent* aManager)
     : mID(aID),
       mSize(aSize),
       mContentManager(nullptr),
       mPBackgroundManager(aManager),
+      mSocketProcessManager(nullptr),
+      mMigrating(false) {}
+
+IPCBlobInputStreamParent::IPCBlobInputStreamParent(
+    const nsID& aID, uint64_t aSize, SocketProcessParent* aManager)
+    : mID(aID),
+      mSize(aSize),
+      mContentManager(nullptr),
+      mPBackgroundManager(nullptr),
+      mSocketProcessManager(aManager),
       mMigrating(false) {}
 
 void IPCBlobInputStreamParent::ActorDestroy(
     IProtocol::ActorDestroyReason aReason) {
-  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+  MOZ_ASSERT(mContentManager || mPBackgroundManager || mSocketProcessManager);
 
   mContentManager = nullptr;
   mPBackgroundManager = nullptr;
+  mSocketProcessManager = nullptr;
 
   RefPtr<IPCBlobInputStreamParentCallback> callback;
   mCallback.swap(callback);
 
   RefPtr<IPCBlobInputStreamStorage> storage = IPCBlobInputStreamStorage::Get();
 
   if (mMigrating) {
     if (callback && storage) {
@@ -107,17 +135,17 @@ void IPCBlobInputStreamParent::SetCallba
     IPCBlobInputStreamParentCallback* aCallback) {
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!mCallback);
 
   mCallback = aCallback;
 }
 
 mozilla::ipc::IPCResult IPCBlobInputStreamParent::RecvStreamNeeded() {
-  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+  MOZ_ASSERT(mContentManager || mPBackgroundManager || mSocketProcessManager);
 
   nsCOMPtr<nsIInputStream> stream;
   IPCBlobInputStreamStorage::Get()->GetStream(mID, 0, mSize,
                                               getter_AddRefs(stream));
   if (!stream) {
     if (!SendStreamReady(Nothing())) {
       return IPC_FAIL(this, "SendStreamReady failed");
     }
@@ -126,34 +154,36 @@ mozilla::ipc::IPCResult IPCBlobInputStre
   }
 
   mozilla::ipc::AutoIPCStream ipcStream;
   bool ok = false;
 
   if (mContentManager) {
     MOZ_ASSERT(NS_IsMainThread());
     ok = ipcStream.Serialize(stream, mContentManager);
+  } else if (mPBackgroundManager) {
+    ok = ipcStream.Serialize(stream, mPBackgroundManager);
   } else {
-    MOZ_ASSERT(mPBackgroundManager);
-    ok = ipcStream.Serialize(stream, mPBackgroundManager);
+    MOZ_ASSERT(mSocketProcessManager);
+    ok = ipcStream.Serialize(stream, mSocketProcessManager);
   }
 
   if (NS_WARN_IF(!ok)) {
     return IPC_FAIL(this, "SendStreamReady failed");
   }
 
   if (!SendStreamReady(Some(ipcStream.TakeValue()))) {
     return IPC_FAIL(this, "SendStreamReady failed");
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult IPCBlobInputStreamParent::RecvLengthNeeded() {
-  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+  MOZ_ASSERT(mContentManager || mPBackgroundManager || mSocketProcessManager);
 
   nsCOMPtr<nsIInputStream> stream;
   IPCBlobInputStreamStorage::Get()->GetStream(mID, 0, mSize,
                                               getter_AddRefs(stream));
   if (!stream) {
     if (!SendLengthReady(-1)) {
       return IPC_FAIL(this, "SendLengthReady failed");
     }
@@ -164,33 +194,34 @@ mozilla::ipc::IPCResult IPCBlobInputStre
   int64_t length = -1;
   if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
     Unused << SendLengthReady(length);
     return IPC_OK();
   }
 
   RefPtr<IPCBlobInputStreamParent> self = this;
   InputStreamLengthHelper::GetAsyncLength(stream, [self](int64_t aLength) {
-    if (self->mContentManager || self->mPBackgroundManager) {
+    if (self->mContentManager || self->mPBackgroundManager ||
+        self->mSocketProcessManager) {
       Unused << self->SendLengthReady(aLength);
     }
   });
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult IPCBlobInputStreamParent::RecvClose() {
-  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+  MOZ_ASSERT(mContentManager || mPBackgroundManager || mSocketProcessManager);
 
   Unused << Send__delete__(this);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult IPCBlobInputStreamParent::Recv__delete__() {
-  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+  MOZ_ASSERT(mContentManager || mPBackgroundManager || mSocketProcessManager);
   mMigrating = true;
   return IPC_OK();
 }
 
 bool IPCBlobInputStreamParent::HasValidStream() const {
   return IPCBlobInputStreamStorage::Get()->HasStream(mID);
 }
 
--- a/dom/file/ipc/IPCBlobInputStreamParent.h
+++ b/dom/file/ipc/IPCBlobInputStreamParent.h
@@ -7,16 +7,21 @@
 #ifndef mozilla_dom_IPCBlobInputStreamParent_h
 #define mozilla_dom_IPCBlobInputStreamParent_h
 
 #include "mozilla/dom/PIPCBlobInputStreamParent.h"
 
 class nsIInputStream;
 
 namespace mozilla {
+
+namespace net {
+class SocketProcessParent;
+}
+
 namespace dom {
 
 class NS_NO_VTABLE IPCBlobInputStreamParentCallback {
  public:
   virtual void ActorDestroyed(const nsID& aID) = 0;
 
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
@@ -35,16 +40,20 @@ class IPCBlobInputStreamParent final : p
   static already_AddRefed<IPCBlobInputStreamParent> Create(
       nsIInputStream* aInputStream, uint64_t aSize, uint64_t aChildID,
       nsresult* aRv, M* aManager);
 
   static already_AddRefed<IPCBlobInputStreamParent> Create(
       const nsID& aID, uint64_t aSize,
       mozilla::ipc::PBackgroundParent* aManager);
 
+  static already_AddRefed<IPCBlobInputStreamParent> Create(
+      const nsID& aID, uint64_t aSize,
+      mozilla::net::SocketProcessParent* aManager);
+
   void ActorDestroy(IProtocol::ActorDestroyReason aReason) override;
 
   const nsID& ID() const { return mID; }
 
   uint64_t Size() const { return mSize; }
 
   void SetCallback(IPCBlobInputStreamParentCallback* aCallback);
 
@@ -60,25 +69,29 @@ class IPCBlobInputStreamParent final : p
 
  private:
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
                            ContentParent* aManager);
 
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
                            mozilla::ipc::PBackgroundParent* aManager);
 
+  IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
+                           mozilla::net::SocketProcessParent* aManager);
+
   ~IPCBlobInputStreamParent() = default;
 
   const nsID mID;
   const uint64_t mSize;
 
-  // Only 1 of these 2 is set. Raw pointer because these 2 managers are keeping
+  // Only 1 of these is set. Raw pointer because these managers are keeping
   // the parent actor alive. The pointers will be nullified in ActorDestroyed.
   ContentParent* mContentManager;
   mozilla::ipc::PBackgroundParent* mPBackgroundManager;
+  mozilla::net::SocketProcessParent* mSocketProcessManager;
 
   RefPtr<IPCBlobInputStreamParentCallback> mCallback;
 
   bool mMigrating;
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/file/ipc/IPCBlobInputStreamThread.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamThread.cpp
@@ -73,16 +73,23 @@ class MigrateActorRunnable final : publi
 
 NS_IMPL_ISUPPORTS(IPCBlobInputStreamThread, nsIObserver, nsIEventTarget)
 
 /* static */
 bool IPCBlobInputStreamThread::IsOnFileEventTarget(
     nsIEventTarget* aEventTarget) {
   MOZ_ASSERT(aEventTarget);
 
+  // Note that we don't migrate actors when we are on the socket process
+  // because, on that process, we don't have complex life-time contexts such
+  // as workers and documents.
+  if (XRE_IsSocketProcess()) {
+    return true;
+  }
+
   mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex);
   return gIPCBlobThread && aEventTarget == gIPCBlobThread->mThread;
 }
 
 /* static */
 IPCBlobInputStreamThread* IPCBlobInputStreamThread::Get() {
   mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex);
 
--- a/dom/file/ipc/IPCBlobUtils.h
+++ b/dom/file/ipc/IPCBlobUtils.h
@@ -210,16 +210,35 @@
  *
  * If we want to represent the hierarchy of the InputStream classes, instead
  * of having: |SlicedInputStream(IPCBlobInputStream(Async Pipe(RemoteStream)))|,
  * we have: |IPCBlobInputStream(Async Pipe(SlicedInputStream(RemoteStream)))|.
  *
  * When IPCBlobInputStream is serialized and sent to the parent process, start
  * and range are sent too and SlicedInputStream is used in the parent side as
  * well.
+ *
+ * Socket Process
+ * ~~~~~~~~~~~~~~
+ *
+ * The socket process is a separate process used to do networking operations.
+ * When a website sends a blob as the body of a POST/PUT request, we need to
+ * send the corresponding IPCBlobInputStream to the socket process.
+ *
+ * This is the only serialization of IPCBlobInputStream from parent to child
+ * process and it works _only_ for the socket process. Do not expose this
+ * serialization to PContent or PBackground or any other top-level IPDL protocol
+ * without a DOM File peer review!
+ *
+ * The main difference between Socket Process is that DOM-File thread is not
+ * used. Here is a list of reasons:
+ * - DOM-File moves the ownership of the IPCBlobInputStream actors to
+ *   PBackground, but in the Socket Process we don't have PBackground (yet?)
+ * - Socket Process is a stable process with a simple life-time configuration:
+ *   we can keep the actors on the main-thread because no Workers are involved.
  */
 
 namespace mozilla {
 
 namespace ipc {
 class IProtocol;
 class PBackgroundChild;
 class PBackgroundParent;
--- a/dom/file/ipc/PIPCBlobInputStream.ipdl
+++ b/dom/file/ipc/PIPCBlobInputStream.ipdl
@@ -2,25 +2,26 @@
  * 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 PFileDescriptorSet;
 include protocol PParentToChildStream;
+include protocol PSocketProcess;
 
 include IPCStream;
 
 namespace mozilla {
 namespace dom {
 
 refcounted protocol PIPCBlobInputStream
 {
-  manager PBackground or PContent;
+  manager PBackground or PContent or PSocketProcess;
 
 parent:
   async StreamNeeded();
 
   async LengthNeeded();
 
   // When this is called, the parent releases the inputStream and sends a
   // __delete__.
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -13897,17 +13897,24 @@ SafeRefPtr<FileInfo> Database::GetBlob(c
   const IPCStream& ipcStream = stream.get_IPCStream();
 
   const InputStreamParams& inputStreamParams = ipcStream.stream();
   if (inputStreamParams.type() !=
       InputStreamParams::TIPCBlobInputStreamParams) {
     return nullptr;
   }
 
-  const nsID& id = inputStreamParams.get_IPCBlobInputStreamParams().id();
+  const IPCBlobInputStreamParams& ipcBlobInputStreamParams =
+      inputStreamParams.get_IPCBlobInputStreamParams();
+  if (ipcBlobInputStreamParams.type() !=
+      IPCBlobInputStreamParams::TIPCBlobInputStreamRef) {
+    return nullptr;
+  }
+
+  const nsID& id = ipcBlobInputStreamParams.get_IPCBlobInputStreamRef().id();
 
   RefPtr<FileInfo> fileInfo;
   if (!mMappedBlobs.Get(id, getter_AddRefs(fileInfo))) {
     return nullptr;
   }
 
   return SafeRefPtr{std::move(fileInfo)};
 }
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -1,16 +1,17 @@
 /* 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 ProtocolTypes;
 
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
+include protocol PIPCBlobInputStream;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace ipc {
 
 struct HeaderEntry
@@ -43,23 +44,29 @@ struct SlicedInputStreamParams
 {
   InputStreamParams stream;
   uint64_t start;
   uint64_t length;
   uint64_t curPos;
   bool closed;
 };
 
-struct IPCBlobInputStreamParams
+struct IPCBlobInputStreamRef
 {
   nsID id;
   uint64_t start;
   uint64_t length;
 };
 
+union IPCBlobInputStreamParams
+{
+  IPCBlobInputStreamRef;
+  PIPCBlobInputStream;
+};
+
 union IPCRemoteStreamType
 {
   PChildToParentStream;
   PParentToChildStream;
 };
 
 struct IPCRemoteStreamParams
 {
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -6,16 +6,17 @@
 
 #include "InputStreamUtils.h"
 
 #include "nsIIPCSerializableInputStream.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/IPCBlobInputStream.h"
+#include "mozilla/dom/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/IPCBlobInputStreamStorage.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/InputStreamLengthHelper.h"
 #include "mozilla/SlicedInputStream.h"
 #include "mozilla/InputStreamLengthWrapper.h"
 #include "nsBufferedStreams.h"
 #include "nsComponentManagerUtils.h"
@@ -262,26 +263,39 @@ void InputStreamHelper::PostSerializatio
     InputStreamHelper::PostSerializationActivation(
         aParams.ref(), aConsumedByIPC, aDelayedStart);
   }
 }
 
 already_AddRefed<nsIInputStream> InputStreamHelper::DeserializeInputStream(
     const InputStreamParams& aParams,
     const nsTArray<FileDescriptor>& aFileDescriptors) {
-  // IPCBlobInputStreams are not deserializable on the parent side.
   if (aParams.type() == InputStreamParams::TIPCBlobInputStreamParams) {
-    MOZ_ASSERT(XRE_IsParentProcess());
+    const IPCBlobInputStreamParams& params =
+        aParams.get_IPCBlobInputStreamParams();
+
+    // IPCBlobInputStreamRefs are not deserializable on the parent side, because
+    // the parent is the only one that has a copy of the original stream in the
+    // IPCBlobInputStreamStorage.
+    if (params.type() == IPCBlobInputStreamParams::TIPCBlobInputStreamRef) {
+      MOZ_ASSERT(XRE_IsParentProcess());
+      const IPCBlobInputStreamRef& ref = params.get_IPCBlobInputStreamRef();
 
-    nsCOMPtr<nsIInputStream> stream;
-    IPCBlobInputStreamStorage::Get()->GetStream(
-        aParams.get_IPCBlobInputStreamParams().id(),
-        aParams.get_IPCBlobInputStreamParams().start(),
-        aParams.get_IPCBlobInputStreamParams().length(),
-        getter_AddRefs(stream));
+      nsCOMPtr<nsIInputStream> stream;
+      IPCBlobInputStreamStorage::Get()->GetStream(
+          ref.id(), ref.start(), ref.length(), getter_AddRefs(stream));
+      return stream.forget();
+    }
+
+    // parent -> child serializations receive an IPCBlobInputStream actor.
+    MOZ_ASSERT(params.type() ==
+               IPCBlobInputStreamParams::TPIPCBlobInputStreamChild);
+    IPCBlobInputStreamChild* actor = static_cast<IPCBlobInputStreamChild*>(
+        params.get_PIPCBlobInputStreamChild());
+    nsCOMPtr<nsIInputStream> stream = actor->CreateStream();
     return stream.forget();
   }
 
   if (aParams.type() == InputStreamParams::TIPCRemoteStreamParams) {
     const IPCRemoteStreamParams& remoteStream =
         aParams.get_IPCRemoteStreamParams();
     const IPCRemoteStreamType& remoteStreamType = remoteStream.stream();
     IPCStreamDestination* destinationStream;
--- a/netwerk/ipc/PSocketProcess.ipdl
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -14,16 +14,17 @@ include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PInputChannelThrottleQueue;
 include protocol PBackground;
 include protocol PAltService;
 include protocol PAltSvcTransaction;
 include protocol PTRRService;
 include protocol PProxyConfigLookup;
 include protocol PNativeDNSResolverOverride;
+include protocol PIPCBlobInputStream;
 
 include MemoryReportTypes;
 include NeckoChannelParams;
 include PrefsTypes;
 include PSMIPCTypes;
 
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
@@ -32,16 +33,17 @@ using mozilla::Telemetry::KeyedHistogram
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using base::ProcessId from "base/process.h";
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using PRTime from "prtime.h";
 using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
+using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace net {
 
 struct HttpHandlerInitArgs {
   bool mFastOpenSupported;
   nsCString mLegacyAppName;
   nsCString mLegacyAppVersion;
@@ -67,16 +69,17 @@ sync protocol PSocketProcess
   manages PChildToParentStream;
   manages PParentToChildStream;
   manages PInputChannelThrottleQueue;
   manages PAltService;
   manages PAltSvcTransaction;
   manages PTRRService;
   manages PProxyConfigLookup;
   manages PNativeDNSResolverOverride;
+  manages PIPCBlobInputStream;
 
 parent:
   async InitCrashReporter(NativeThreadId threadId);
   async AddMemoryReport(MemoryReport aReport);
   async FinishMemoryReport(uint32_t aGeneration);
   // Messages for sending telemetry to parent process.
   async AccumulateChildHistograms(HistogramAccumulation[] accumulations);
   async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations);
@@ -136,16 +139,18 @@ child:
                            uint32_t aCaps);
   async ClearSessionCache();
   async PTRRService(bool aCaptiveIsPassed,
                     bool aParentalControlEnabled,
                     nsCString[] aDNSSuffixList);
   async PNativeDNSResolverOverride();
   async NotifyObserver(nsCString aTopic, nsString aData);
 
+  async PIPCBlobInputStream(nsID aID, uint64_t aSize);
+
 both:
   async PFileDescriptorSet(FileDescriptor fd);
   async PDNSRequest(nsCString hostName, nsCString trrServer, uint16_t type,
                     OriginAttributes originAttributes, uint32_t flags);
 };
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/SocketProcessChild.cpp
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -6,16 +6,17 @@
 #include "SocketProcessChild.h"
 #include "SocketProcessLogging.h"
 
 #include "base/task.h"
 #include "InputChannelThrottleQueueChild.h"
 #include "HttpTransactionChild.h"
 #include "HttpConnectionMgrChild.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/net/AltSvcTransactionChild.h"
@@ -458,10 +459,18 @@ mozilla::ipc::IPCResult SocketProcessChi
     const nsCString& aTopic, const nsString& aData) {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, aTopic.get(), aData.get());
   }
   return IPC_OK();
 }
 
+already_AddRefed<dom::PIPCBlobInputStreamChild>
+SocketProcessChild::AllocPIPCBlobInputStreamChild(const nsID& aID,
+                                                  const uint64_t& aSize) {
+  RefPtr<dom::IPCBlobInputStreamChild> actor =
+      new dom::IPCBlobInputStreamChild(aID, aSize);
+  return actor.forget();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/SocketProcessChild.h
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -118,16 +118,19 @@ class SocketProcessChild final
   already_AddRefed<PNativeDNSResolverOverrideChild>
   AllocPNativeDNSResolverOverrideChild();
   mozilla::ipc::IPCResult RecvPNativeDNSResolverOverrideConstructor(
       PNativeDNSResolverOverrideChild* aActor) override;
 
   mozilla::ipc::IPCResult RecvNotifyObserver(const nsCString& aTopic,
                                              const nsString& aData);
 
+  virtual already_AddRefed<PIPCBlobInputStreamChild>
+  AllocPIPCBlobInputStreamChild(const nsID& aID, const uint64_t& aSize);
+
  protected:
   friend class SocketProcessImpl;
   ~SocketProcessChild();
 
  private:
   // Mapping of content process id and the SocketProcessBridgeParent.
   // This table keeps SocketProcessBridgeParent alive in socket process.
   nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
--- a/netwerk/ipc/SocketProcessParent.cpp
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -5,16 +5,17 @@
 
 #include "SocketProcessParent.h"
 #include "SocketProcessLogging.h"
 
 #include "AltServiceParent.h"
 #include "CachePushChecker.h"
 #include "HttpTransactionParent.h"
 #include "SocketProcessHost.h"
+#include "mozilla/dom/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ProxyConfigLookupParent.h"
 #include "mozilla/Telemetry.h"
@@ -382,10 +383,28 @@ class DeferredDeleteSocketProcessParent 
 };
 
 /* static */
 void SocketProcessParent::Destroy(UniquePtr<SocketProcessParent>&& aParent) {
   NS_DispatchToMainThread(
       new DeferredDeleteSocketProcessParent(std::move(aParent)));
 }
 
+already_AddRefed<dom::PIPCBlobInputStreamParent>
+SocketProcessParent::AllocPIPCBlobInputStreamParent(const nsID& aID,
+                                                    const uint64_t& aSize) {
+  RefPtr<dom::IPCBlobInputStreamParent> actor =
+      dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
+  return actor.forget();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvPIPCBlobInputStreamConstructor(
+    dom::PIPCBlobInputStreamParent* aActor, const nsID& aID,
+    const uint64_t& aSize) {
+  if (!static_cast<dom::IPCBlobInputStreamParent*>(aActor)->HasValidStream()) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  return IPC_OK();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/SocketProcessParent.h
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -108,16 +108,23 @@ class SocketProcessParent final
   mozilla::ipc::IPCResult RecvPProxyConfigLookupConstructor(
       PProxyConfigLookupParent* aActor, nsIURI* aURI,
       const uint32_t& aProxyResolveFlags) override;
 
   mozilla::ipc::IPCResult RecvCachePushCheck(
       nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
       nsCString&& aRequestString, CachePushCheckResolver&& aResolver);
 
+  already_AddRefed<PIPCBlobInputStreamParent> AllocPIPCBlobInputStreamParent(
+      const nsID& aID, const uint64_t& aSize);
+
+  mozilla::ipc::IPCResult RecvPIPCBlobInputStreamConstructor(
+      PIPCBlobInputStreamParent* aActor, const nsID& aID,
+      const uint64_t& aSize);
+
  private:
   SocketProcessHost* mHost;
   UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
 
   static void Destroy(UniquePtr<SocketProcessParent>&& aParent);
 };
 
 }  // namespace net