Bug 910010 - Implementation of PFileDescriptorSet - part 1, r=khuey
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 25 Mar 2014 18:37:13 +0000
changeset 175268 6a131333a0b19f132690dfd82afd0854408bb1e7
parent 175267 b7015e01d136062a2a263a3fbef10a547f109d59
child 175269 9546241b934539f4f5b178dd7618f81d5a27b79f
push id26485
push userkwierso@gmail.com
push dateWed, 26 Mar 2014 02:55:13 +0000
treeherdermozilla-central@196bf8197122 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs910010
milestone31.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 910010 - Implementation of PFileDescriptorSet - part 1, r=khuey
docshell/base/nsDefaultURIFixup.cpp
dom/ipc/Blob.cpp
dom/ipc/Blob.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/FileDescriptorSetChild.h
dom/ipc/PBlobStream.ipdl
dom/ipc/PContent.ipdl
dom/ipc/PFileDescriptorSet.ipdl
dom/ipc/moz.build
ipc/chromium/src/chrome/common/ipc_message_utils.h
ipc/glue/InputStreamParams.ipdlh
ipc/glue/InputStreamUtils.cpp
ipc/glue/InputStreamUtils.h
ipc/glue/nsIIPCSerializableInputStream.h
netwerk/base/src/nsBufferedStreams.cpp
netwerk/base/src/nsFileStreams.cpp
netwerk/base/src/nsMIMEInputStream.cpp
netwerk/protocol/ftp/FTPChannelChild.cpp
netwerk/protocol/ftp/FTPChannelParent.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/websocket/WebSocketChannelChild.cpp
netwerk/protocol/websocket/WebSocketChannelParent.cpp
xpcom/io/nsMultiplexInputStream.cpp
xpcom/io/nsStringStream.cpp
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -371,18 +371,21 @@ NS_IMETHODIMP nsDefaultURIFixup::Keyword
 
         ipc::OptionalInputStreamParams postData;
         ipc::OptionalURIParams uri;
         if (!contentChild->SendKeywordToURI(keyword, &postData, &uri)) {
             return NS_ERROR_FAILURE;
         }
 
         if (aPostData) {
-            nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData);
+            nsTArray<ipc::FileDescriptor> fds;
+            nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds);
             temp.forget(aPostData);
+
+            MOZ_ASSERT(fds.IsEmpty());
         }
 
         nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
         temp.forget(aURI);
         return NS_OK;
     }
 
 #ifdef MOZ_TOOLKIT_SEARCH
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -16,24 +16,26 @@
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIRemoteBlob.h"
 #include "nsISeekableStream.h"
 
 #include "mozilla/Monitor.h"
 #include "mozilla/unused.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "nsDOMFile.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
 
 #include "ContentChild.h"
 #include "ContentParent.h"
 #include "nsNetCID.h"
+#include "FileDescriptorSetChild.h"
 
 #define PRIVATE_REMOTE_INPUT_STREAM_IID \
   {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::ipc;
 
@@ -162,32 +164,34 @@ public:
   : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
     mWeakSeekableStream(nullptr), mOrigin(aOrigin)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aSourceBlob);
   }
 
   void
-  Serialize(InputStreamParams& aParams)
+  Serialize(InputStreamParams& aParams,
+            FileDescriptorArray& /* aFileDescriptors */)
   {
     nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
     MOZ_ASSERT(remote);
 
     if (mOrigin == Parent) {
       aParams = RemoteInputStreamParams(
         static_cast<PBlobParent*>(remote->GetPBlob()), nullptr);
     } else {
       aParams = RemoteInputStreamParams(
         nullptr, static_cast<PBlobChild*>(remote->GetPBlob()));
     }
   }
 
   bool
-  Deserialize(const InputStreamParams& aParams)
+  Deserialize(const InputStreamParams& aParams,
+              const FileDescriptorArray& /* aFileDescriptors */)
   {
     // See InputStreamUtils.cpp to see how deserialization of a
     // RemoteInputStream is special-cased.
     MOZ_CRASH("RemoteInputStream should never be deserialized");
   }
 
   void
   SetStream(nsIInputStream* aStream)
@@ -443,30 +447,73 @@ public:
   InputStreamActor()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
 private:
   // This method is only called by the IPDL message machinery.
   virtual bool
-  Recv__delete__(const InputStreamParams& aParams) MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mRemoteStream);
+  Recv__delete__(const InputStreamParams& aParams,
+                 const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
+};
+
+template <>
+bool
+InputStreamActor<Parent>::Recv__delete__(const InputStreamParams& aParams,
+                                         const OptionalFileDescriptorSet& aFDs)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mRemoteStream);
+
+  if (aFDs.type() != OptionalFileDescriptorSet::Tvoid_t) {
+    NS_WARNING("Child cannot send FileDescriptors to the parent!");
+    return false;
+  }
+
+  nsTArray<FileDescriptor> fds;
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  if (!stream) {
+    return false;
+  }
+
+  MOZ_ASSERT(fds.IsEmpty());
+
+  mRemoteStream->SetStream(stream);
+  return true;
+}
 
-    nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams);
-    if (!stream) {
-      return false;
-    }
+template <>
+bool
+InputStreamActor<Child>::Recv__delete__(const InputStreamParams& aParams,
+                                        const OptionalFileDescriptorSet& aFDs)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mRemoteStream);
+
+  nsTArray<FileDescriptor> fds;
+  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+    FileDescriptorSetChild* fdSetActor =
+      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
+    MOZ_ASSERT(fdSetActor);
 
-    mRemoteStream->SetStream(stream);
-    return true;
+    fdSetActor->ForgetFileDescriptors(fds);
+    MOZ_ASSERT(!fds.IsEmpty());
+
+    fdSetActor->Send__delete__(fdSetActor);
   }
-};
+
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  if (!stream) {
+    return false;
+  }
+
+  mRemoteStream->SetStream(stream);
+  return true;
+}
 
 template <ActorFlavorEnum ActorFlavor>
 inline
 already_AddRefed<nsIDOMBlob>
 GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
 {
   static_assert(ActorFlavor == Parent,
                 "No other flavor is supported here!");
@@ -673,21 +720,44 @@ private:
       MOZ_ASSERT(!mBlobActor);
       MOZ_ASSERT(!mStreamActor);
     }
     else {
       MOZ_ASSERT(mBlobActor);
       MOZ_ASSERT(mStreamActor);
 
       InputStreamParams params;
-      serializable->Serialize(params);
+      nsAutoTArray<FileDescriptor, 10> fds;
+      serializable->Serialize(params, fds);
 
       MOZ_ASSERT(params.type() != InputStreamParams::T__None);
 
-      unused << mStreamActor->Send__delete__(mStreamActor, params);
+      PFileDescriptorSetParent* fdSet = nullptr;
+
+      if (!fds.IsEmpty()) {
+        Blob<Parent>* blob = static_cast<Blob<Parent>*>(mBlobActor);
+
+        MOZ_ASSERT(blob->Manager());
+
+        fdSet = blob->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
+        if (fdSet) {
+          for (uint32_t index = 1; index < fds.Length(); index++) {
+            unused << fdSet->SendAddFileDescriptor(fds[index]);
+          }
+        }
+      }
+
+      OptionalFileDescriptorSet optionalFDs;
+      if (fdSet) {
+        optionalFDs = fdSet;
+      } else {
+        optionalFDs = mozilla::void_t();
+      }
+
+      unused << mStreamActor->Send__delete__(mStreamActor, params, optionalFDs);
 
       mBlobActor->NoteRunnableCompleted(this);
 
 #ifdef DEBUG
       mBlobActor = nullptr;
       mStreamActor = nullptr;
 #endif
     }
@@ -1089,18 +1159,19 @@ RemoteBlob<Child>::MaybeSetInputStream(c
   // Nothing needed on the child side!
 }
 
 template <>
 NS_IMETHODIMP
 RemoteBlob<Parent>::GetInternalStream(nsIInputStream** aStream)
 {
   if (mInputStreamParams.type() != InputStreamParams::T__None) {
+    nsTArray<FileDescriptor> fds;
     nsCOMPtr<nsIInputStream> realStream =
-      DeserializeInputStream(mInputStreamParams);
+      DeserializeInputStream(mInputStreamParams, fds);
     if (!realStream) {
       NS_WARNING("Failed to deserialize stream!");
       return NS_ERROR_UNEXPECTED;
     }
 
     nsCOMPtr<nsIInputStream> stream =
       new BlobInputStreamTether(realStream, this);
     stream.forget(aStream);
@@ -1468,21 +1539,23 @@ Blob<Child>::RecvPBlobStreamConstructor(
   nsCOMPtr<nsIIPCSerializableInputStream> serializable =
     do_QueryInterface(stream);
   if (!serializable) {
     MOZ_ASSERT(false, "Must be serializable!");
     return false;
   }
 
   InputStreamParams params;
-  serializable->Serialize(params);
+  nsTArray<FileDescriptor> fds;
+  serializable->Serialize(params, fds);
 
   MOZ_ASSERT(params.type() != InputStreamParams::T__None);
+  MOZ_ASSERT(fds.IsEmpty());
 
-  return aActor->Send__delete__(aActor, params);
+  return aActor->Send__delete__(aActor, params, mozilla::void_t());
 }
 
 BlobTraits<Parent>::StreamType*
 BlobTraits<Parent>::BaseType::AllocPBlobStreamParent()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return new InputStreamActor<Parent>();
 }
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -152,16 +152,17 @@ struct BlobTraits<Child>
 
 template <ActorFlavorEnum>
 class RemoteBlob;
 
 template <ActorFlavorEnum ActorFlavor>
 class Blob : public BlobTraits<ActorFlavor>::BaseType
 {
   friend class RemoteBlob<ActorFlavor>;
+  friend class BlobTraits<ActorFlavor>::BaseType;
 
 public:
   typedef typename BlobTraits<ActorFlavor>::ConcreteContentManagerType ContentManager;
   typedef typename BlobTraits<ActorFlavor>::ProtocolType ProtocolType;
   typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
   typedef typename BlobTraits<ActorFlavor>::ConstructorParamsType
           ConstructorParamsType;
   typedef typename BlobTraits<ActorFlavor>::OtherSideConstructorParamsType
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -9,16 +9,17 @@
 #endif
 
 #ifdef MOZ_WIDGET_QT
 #include "nsQAppInstance.h"
 #endif
 
 #include "ContentChild.h"
 #include "CrashReporterChild.h"
+#include "FileDescriptorSetChild.h"
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/DOMStorageIPC.h"
@@ -793,16 +794,28 @@ ContentChild::RecvPBrowserConstructor(PB
         MOZ_ASSERT(!sFirstIdleTask);
         sFirstIdleTask = NewRunnableFunction(FirstIdle);
         MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask);
     }
 
     return true;
 }
 
+PFileDescriptorSetChild*
+ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
+{
+    return new FileDescriptorSetChild(aFD);
+}
+
+bool
+ContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor)
+{
+    delete static_cast<FileDescriptorSetChild*>(aActor);
+    return true;
+}
 
 bool
 ContentChild::DeallocPBrowserChild(PBrowserChild* iframe)
 {
     TabChild* child = static_cast<TabChild*>(iframe);
     NS_RELEASE(child);
     return true;
 }
@@ -899,17 +912,19 @@ ContentChild::GetOrCreateActorForBlob(ns
     rv = aBlob->GetSize(&length);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     nsCOMPtr<nsIInputStream> stream;
     rv = aBlob->GetInternalStream(getter_AddRefs(stream));
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     InputStreamParams inputStreamParams;
-    SerializeInputStream(stream, inputStreamParams);
+    nsTArray<mozilla::ipc::FileDescriptor> fds;
+    SerializeInputStream(stream, inputStreamParams, fds);
+    MOZ_ASSERT(fds.IsEmpty());
 
     params.optionalInputStreamParams() = inputStreamParams;
 
     nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
     if (file) {
       FileBlobConstructorParams fileParams;
 
       rv = file->GetName(fileParams.name());
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -35,16 +35,17 @@ class PCompositorChild;
 
 namespace dom {
 
 class AlertObserver;
 class PrefObserver;
 class ConsoleListener;
 class PStorageChild;
 class ClonedMessageData;
+class PFileDescriptorSetChild;
 
 class ContentChild : public PContentChild
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
     typedef mozilla::ipc::URIParams URIParams;
 
 public:
@@ -266,16 +267,22 @@ public:
 
     uint64_t GetID() { return mID; }
 
     bool IsForApp() { return mIsForApp; }
     bool IsForBrowser() { return mIsForBrowser; }
 
     BlobChild* GetOrCreateActorForBlob(nsIDOMBlob* aBlob);
 
+    virtual PFileDescriptorSetChild*
+    AllocPFileDescriptorSetChild(const FileDescriptor&) MOZ_OVERRIDE;
+
+    virtual bool
+    DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*) MOZ_OVERRIDE;
+
 protected:
     virtual bool RecvPBrowserConstructor(PBrowserChild* actor,
                                          const IPCTabContext& context,
                                          const uint32_t& chromeFlags) MOZ_OVERRIDE;
 
 private:
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -29,16 +29,17 @@
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
+#include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
@@ -288,16 +289,29 @@ MaybeTestPBackground()
                                                false);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 #endif
 }
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+// The parent side of FileDescriptorSet doesn't need to do anything, really.
+class FileDescriptorSetParent MOZ_FINAL: public PFileDescriptorSetParent
+{
+    friend class mozilla::dom::ContentParent;
+
+    FileDescriptorSetParent() { }
+    ~FileDescriptorSetParent() { }
+};
+
+} // anonymous namespace
+
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
     MemoryReportRequestParent();
     virtual ~MemoryReportRequestParent();
 
@@ -3232,17 +3246,20 @@ ContentParent::RecvKeywordToURI(const ns
 
   nsCOMPtr<nsIInputStream> postData;
   nsCOMPtr<nsIURI> uri;
   if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData),
                                     getter_AddRefs(uri)))) {
     return true;
   }
 
-  SerializeInputStream(postData, *aPostData);
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  SerializeInputStream(postData, *aPostData, fds);
+  MOZ_ASSERT(fds.IsEmpty());
+
   SerializeURI(uri, *aURI);
   return true;
 }
 
 bool
 ContentParent::ShouldContinueFromReplyTimeout()
 {
   // The only time ContentParent sends blocking messages is for CPOWs, so
@@ -3346,16 +3363,29 @@ ContentParent::RecvBackUpXResources(cons
     mChildXSocketFdDup.forget();
     if (aXSocketFd.IsValid()) {
       mChildXSocketFdDup.reset(aXSocketFd.PlatformHandle());
     }
 #endif
     return true;
 }
 
+PFileDescriptorSetParent*
+ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& /* aFD */)
+{
+    return new FileDescriptorSetParent();
+}
+
+bool
+ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
+{
+    delete aActor;
+    return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS1(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
   mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -52,16 +52,17 @@ class PCompositorParent;
 namespace dom {
 
 class Element;
 class TabParent;
 class PStorageParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
+class PFileDescriptorSetParent;
 
 class ContentParent : public PContentParent
                     , public nsIObserver
                     , public nsIDOMGeoPositionCallback
                     , public mozilla::dom::ipc::MessageManagerCallback
                     , public mozilla::LinkedListElement<ContentParent>
 {
     typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
@@ -530,16 +531,22 @@ private:
     virtual bool RecvAddIdleObserver(const uint64_t& observerId,
                                      const uint32_t& aIdleTimeInS) MOZ_OVERRIDE;
     virtual bool RecvRemoveIdleObserver(const uint64_t& observerId,
                                         const uint32_t& aIdleTimeInS) MOZ_OVERRIDE;
 
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
 
+    virtual PFileDescriptorSetParent*
+    AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) MOZ_OVERRIDE;
+
+    virtual bool
+    DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) MOZ_OVERRIDE;
+
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
new file mode 100644
--- /dev/null
+++ b/dom/ipc/FileDescriptorSetChild.h
@@ -0,0 +1,63 @@
+/* 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_FileDescriptorSetChild_h__
+#define mozilla_dom_FileDescriptorSetChild_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/PFileDescriptorSetChild.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class FileDescriptor;
+
+} // namespace ipc
+
+namespace dom {
+
+class ContentChild;
+
+class FileDescriptorSetChild MOZ_FINAL: public PFileDescriptorSetChild
+{
+  friend class ContentChild;
+
+public:
+  typedef mozilla::ipc::FileDescriptor FileDescriptor;
+
+  void
+  ForgetFileDescriptors(nsTArray<FileDescriptor>& aFileDescriptors)
+  {
+    aFileDescriptors.Clear();
+    mFileDescriptors.SwapElements(aFileDescriptors);
+  }
+
+private:
+  FileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
+  {
+    mFileDescriptors.AppendElement(aFileDescriptor);
+  }
+
+  ~FileDescriptorSetChild()
+  {
+    MOZ_ASSERT(mFileDescriptors.IsEmpty());
+  }
+
+  virtual bool
+  RecvAddFileDescriptor(const FileDescriptor& aFileDescriptor) MOZ_OVERRIDE
+  {
+    mFileDescriptors.AppendElement(aFileDescriptor);
+    return true;
+  }
+
+  nsAutoTArray<FileDescriptor, 1> mFileDescriptors;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FileDescriptorSetChild_h__
--- a/dom/ipc/PBlobStream.ipdl
+++ b/dom/ipc/PBlobStream.ipdl
@@ -1,20 +1,29 @@
 /* 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 PBlob;
+include protocol PFileDescriptorSet;
 include InputStreamParams;
 
+using mozilla::void_t from "ipc/IPCMessageUtils.h";
+
 namespace mozilla {
 namespace dom {
 
+union OptionalFileDescriptorSet
+{
+  PFileDescriptorSet;
+  void_t;
+};
+
 protocol PBlobStream
 {
   manager PBlob;
 
 both:
-  __delete__(InputStreamParams params);
+  __delete__(InputStreamParams params, OptionalFileDescriptorSet fds);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -8,16 +8,17 @@ include protocol PAsmJSCacheEntry;
 include protocol PBackground;
 include protocol PBlob;
 include protocol PBluetooth;
 include protocol PBrowser;
 include protocol PCompositor;
 include protocol PCrashReporter;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
+include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
 include protocol PNecko;
 include protocol PSms;
@@ -253,16 +254,17 @@ intr protocol PContent
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCrashReporter;
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PExternalHelperApp;
+    manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PIndexedDB;
     manages PMemoryReportRequest;
     manages PNecko;
     manages PSms;
     manages PSpeechSynthesis;
     manages PStorage;
@@ -305,16 +307,18 @@ child:
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox();
 
     PMemoryReportRequest(uint32_t generation, bool minimizeMemoryUsage, nsString DMDDumpIdent);
 
+    PFileDescriptorSet(FileDescriptor fd);
+
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     async SpeakerManagerNotify();
 
     /**
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PFileDescriptorSet.ipdl
@@ -0,0 +1,22 @@
+/* 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 PContent;
+
+namespace mozilla {
+namespace dom {
+
+protocol PFileDescriptorSet
+{
+  manager PContent;
+
+child:
+  AddFileDescriptor(FileDescriptor fd);
+
+parent:
+  __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -18,16 +18,17 @@ EXPORTS.mozilla.dom.ipc += [
 ]
 
 EXPORTS.mozilla.dom += [
     'ContentChild.h',
     'ContentParent.h',
     'ContentProcess.h',
     'CrashReporterChild.h',
     'CrashReporterParent.h',
+    'FileDescriptorSetChild.h',
     'FilePickerParent.h',
     'PermissionMessageUtils.h',
     'StructuredCloneUtils.h',
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
     'TabParent.h',
 ]
@@ -39,16 +40,17 @@ EXPORTS.mozilla += [
 ]
 
 UNIFIED_SOURCES += [
     'AppProcessChecker.cpp',
     'ColorPickerParent.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'CrashReporterParent.cpp',
+    'PFileDescriptorSet.ipdl',
     'FilePickerParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'StructuredCloneUtils.cpp',
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
--- a/ipc/chromium/src/chrome/common/ipc_message_utils.h
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h
@@ -691,18 +691,19 @@ struct ParamTraitsIPC<base::Time> {
 template<>
 struct ParamTraitsIPC<base::FileDescriptor> {
   typedef base::FileDescriptor param_type;
   static void Write(Message* m, const param_type& p) {
     const bool valid = p.fd >= 0;
     WriteParam(m, valid);
 
     if (valid) {
-      if (!m->WriteFileDescriptor(p))
-        NOTREACHED();
+      if (!m->WriteFileDescriptor(p)) {
+        NOTREACHED() << "Too many file descriptors for one message!";
+      }
     }
   }
   static bool Read(const Message* m, void** iter, param_type* r) {
     bool valid;
     if (!ReadParam(m, iter, &valid))
       return false;
 
     if (!valid) {
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -11,17 +11,17 @@ namespace ipc {
 
 struct StringInputStreamParams
 {
   nsCString data;
 };
 
 struct FileInputStreamParams
 {
-  FileDescriptor file;
+  uint32_t fileDescriptorIndex;
   int32_t behaviorFlags;
   int32_t ioFlags;
 };
 
 struct PartialFileInputStreamParams
 {
   FileInputStreamParams fileStreamParams;
   uint64_t begin;
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -15,17 +15,16 @@
 #include "nsIXULRuntime.h"
 #include "nsMIMEInputStream.h"
 #include "nsMultiplexInputStream.h"
 #include "nsNetCID.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
-using namespace mozilla::ipc;
 using mozilla::dom::BlobChild;
 using mozilla::dom::BlobParent;
 
 namespace {
 
 NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID);
 NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_CID(kPartialFileInputStreamCID, NS_PARTIALLOCALFILEINPUTSTREAM_CID);
@@ -35,52 +34,55 @@ NS_DEFINE_CID(kMultiplexInputStreamCID, 
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
 void
 SerializeInputStream(nsIInputStream* aInputStream,
-                     InputStreamParams& aParams)
+                     InputStreamParams& aParams,
+                     nsTArray<FileDescriptor>& aFileDescriptors)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aInputStream);
 
   nsCOMPtr<nsIIPCSerializableInputStream> serializable =
     do_QueryInterface(aInputStream);
   if (!serializable) {
     MOZ_CRASH("Input stream is not serializable!");
   }
 
-  serializable->Serialize(aParams);
+  serializable->Serialize(aParams, aFileDescriptors);
 
   if (aParams.type() == InputStreamParams::T__None) {
     MOZ_CRASH("Serialize failed!");
   }
 }
 
 void
 SerializeInputStream(nsIInputStream* aInputStream,
-                     OptionalInputStreamParams& aParams)
+                     OptionalInputStreamParams& aParams,
+                     nsTArray<FileDescriptor>& aFileDescriptors)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aInputStream) {
     InputStreamParams params;
-    SerializeInputStream(aInputStream, params);
+    SerializeInputStream(aInputStream, params, aFileDescriptors);
     aParams = params;
   }
   else {
     aParams = mozilla::void_t();
   }
 }
 
 already_AddRefed<nsIInputStream>
-DeserializeInputStream(const InputStreamParams& aParams)
+DeserializeInputStream(const InputStreamParams& aParams,
+                       const nsTArray<FileDescriptor>& aFileDescriptors)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIIPCSerializableInputStream> serializable;
 
   switch (aParams.type()) {
     case InputStreamParams::TStringInputStreamParams:
       serializable = do_CreateInstance(kStringInputStreamCID);
@@ -131,41 +133,43 @@ DeserializeInputStream(const InputStream
 
     default:
       MOZ_ASSERT(false, "Unknown params!");
       return nullptr;
   }
 
   MOZ_ASSERT(serializable);
 
-  if (!serializable->Deserialize(aParams)) {
+  if (!serializable->Deserialize(aParams, aFileDescriptors)) {
     MOZ_ASSERT(false, "Deserialize failed!");
     return nullptr;
   }
 
   nsCOMPtr<nsIInputStream> stream = do_QueryInterface(serializable);
   MOZ_ASSERT(stream);
 
   return stream.forget();
 }
 
 already_AddRefed<nsIInputStream>
-DeserializeInputStream(const OptionalInputStreamParams& aParams)
+DeserializeInputStream(const OptionalInputStreamParams& aParams,
+                       const nsTArray<FileDescriptor>& aFileDescriptors)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIInputStream> stream;
 
   switch (aParams.type()) {
     case OptionalInputStreamParams::Tvoid_t:
       // Leave stream null.
       break;
 
     case OptionalInputStreamParams::TInputStreamParams:
-      stream = DeserializeInputStream(aParams.get_InputStreamParams());
+      stream = DeserializeInputStream(aParams.get_InputStreamParams(),
+                                      aFileDescriptors);
       break;
 
     default:
       MOZ_ASSERT(false, "Unknown params!");
   }
 
   return stream.forget();
 }
--- a/ipc/glue/InputStreamUtils.h
+++ b/ipc/glue/InputStreamUtils.h
@@ -3,30 +3,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_InputStreamUtils_h
 #define mozilla_ipc_InputStreamUtils_h
 
 #include "mozilla/ipc/InputStreamParams.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace ipc {
 
+class FileDescriptor;
+
 void
 SerializeInputStream(nsIInputStream* aInputStream,
-                     InputStreamParams& aParams);
+                     InputStreamParams& aParams,
+                     nsTArray<FileDescriptor>& aFileDescriptors);
 
 void
 SerializeInputStream(nsIInputStream* aInputStream,
-                     OptionalInputStreamParams& aParams);
+                     OptionalInputStreamParams& aParams,
+                     nsTArray<FileDescriptor>& aFileDescriptors);
 
 already_AddRefed<nsIInputStream>
-DeserializeInputStream(const InputStreamParams& aParams);
+DeserializeInputStream(const InputStreamParams& aParams,
+                       const nsTArray<FileDescriptor>& aFileDescriptors);
 
 already_AddRefed<nsIInputStream>
-DeserializeInputStream(const OptionalInputStreamParams& aParams);
+DeserializeInputStream(const OptionalInputStreamParams& aParams,
+                       const nsTArray<FileDescriptor>& aFileDescriptors);
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_InputStreamUtils_h
--- a/ipc/glue/nsIIPCSerializableInputStream.h
+++ b/ipc/glue/nsIIPCSerializableInputStream.h
@@ -3,55 +3,85 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_nsIIPCSerializableInputStream_h
 #define mozilla_ipc_nsIIPCSerializableInputStream_h
 
 #include "nsISupports.h"
 #include "mozilla/Attributes.h"
 
+struct nsTArrayDefaultAllocator;
+template <class> class nsTArray;
+
 namespace mozilla {
 namespace ipc {
+
+class FileDescriptor;
 class InputStreamParams;
-}
-}
+
+} // namespace ipc
+} // namespace mozilla
 
 #define NS_IIPCSERIALIZABLEINPUTSTREAM_IID \
-  {0x1f56a3f8, 0xc413, 0x4274, {0x88, 0xe6, 0x68, 0x50, 0x9d, 0xf8, 0x85, 0x2d}}
+  {0xb0211b14, 0xea6d, 0x40d4, {0x87, 0xb5, 0x7b, 0xe3, 0xdf, 0xac, 0x09, 0xd1}}
 
 class NS_NO_VTABLE nsIIPCSerializableInputStream : public nsISupports
 {
 public:
+  typedef nsTArray<mozilla::ipc::FileDescriptor>
+          FileDescriptorArray;
+
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIPCSERIALIZABLEINPUTSTREAM_IID)
 
   virtual void
-  Serialize(mozilla::ipc::InputStreamParams& aParams) = 0;
+  Serialize(mozilla::ipc::InputStreamParams& aParams,
+            FileDescriptorArray& aFileDescriptors) = 0;
 
   virtual bool
-  Deserialize(const mozilla::ipc::InputStreamParams& aParams) = 0;
+  Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+              const FileDescriptorArray& aFileDescriptors) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCSerializableInputStream,
                               NS_IIPCSERIALIZABLEINPUTSTREAM_IID)
 
-#define NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM \
-  virtual void \
-  Serialize(mozilla::ipc::InputStreamParams&) MOZ_OVERRIDE; \
-  virtual bool \
-  Deserialize(const mozilla::ipc::InputStreamParams&) MOZ_OVERRIDE;
+#define NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM                                  \
+  virtual void                                                                 \
+  Serialize(mozilla::ipc::InputStreamParams&,                                  \
+            FileDescriptorArray&) MOZ_OVERRIDE;                                \
+                                                                               \
+  virtual bool                                                                 \
+  Deserialize(const mozilla::ipc::InputStreamParams&,                          \
+              const FileDescriptorArray&) MOZ_OVERRIDE;
 
-#define NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(_to) \
-  virtual void \
-  Serialize(mozilla::ipc::InputStreamParams& aParams) MOZ_OVERRIDE \
-  { _to Serialize(aParams); } \
-  virtual bool \
-  Deserialize(const mozilla::ipc::InputStreamParams& aParams) MOZ_OVERRIDE \
-  { return _to Deserialize(aParams); }
+#define NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(_to)                          \
+  virtual void                                                                 \
+  Serialize(mozilla::ipc::InputStreamParams& aParams,                          \
+            FileDescriptorArray& aFileDescriptors) MOZ_OVERRIDE                \
+  {                                                                            \
+    _to Serialize(aParams, aFileDescriptors);                                  \
+  }                                                                            \
+                                                                               \
+  virtual bool                                                                 \
+  Deserialize(const mozilla::ipc::InputStreamParams& aParams,                  \
+              const FileDescriptorArray& aFileDescriptors) MOZ_OVERRIDE        \
+  {                                                                            \
+    return _to Deserialize(aParams, aFileDescriptors);                         \
+  }
 
-#define NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(_to) \
-  virtual void \
-  Serialize(mozilla::ipc::InputStreamParams& aParams) MOZ_OVERRIDE \
-  { if (_to) { _to->Serialize(aParams); } } \
-  virtual bool \
-  Deserialize(const mozilla::ipc::InputStreamParams& aParams) MOZ_OVERRIDE \
-  { if (_to) { return _to->Deserialize(aParams); } return false; }
+#define NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(_to)                     \
+  virtual void                                                                 \
+  Serialize(mozilla::ipc::InputStreamParams& aParams,                          \
+            FileDescriptorArray& aFileDescriptors) MOZ_OVERRIDE                \
+  {                                                                            \
+    if (_to) {                                                                 \
+      _to->Serialize(aParams, aFileDescriptors);                               \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  virtual bool                                                                 \
+  Deserialize(const mozilla::ipc::InputStreamParams& aParams,                  \
+              const FileDescriptorArray& aFileDescriptors) MOZ_OVERRIDE        \
+  {                                                                            \
+    return _to ? _to->Deserialize(aParams, aFileDescriptors) : false;          \
+  }
 
 #endif // mozilla_ipc_nsIIPCSerializableInputStream_h
--- a/netwerk/base/src/nsBufferedStreams.cpp
+++ b/netwerk/base/src/nsBufferedStreams.cpp
@@ -481,57 +481,56 @@ nsBufferedInputStream::GetUnbufferedStre
     mFillPoint = mCursor = 0;
 
     *aStream = mStream;
     NS_IF_ADDREF(*aStream);
     return NS_OK;
 }
 
 void
-nsBufferedInputStream::Serialize(InputStreamParams& aParams)
+nsBufferedInputStream::Serialize(InputStreamParams& aParams,
+                                 FileDescriptorArray& aFileDescriptors)
 {
     BufferedInputStreamParams params;
 
     if (mStream) {
-        nsCOMPtr<nsIIPCSerializableInputStream> stream =
-            do_QueryInterface(mStream);
-        NS_ASSERTION(stream, "Wrapped stream is not serializable!");
+        nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
+        MOZ_ASSERT(stream);
 
         InputStreamParams wrappedParams;
-        stream->Serialize(wrappedParams);
-
-        NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
-                     "Wrapped stream failed to serialize!");
+        SerializeInputStream(stream, wrappedParams, aFileDescriptors);
 
         params.optionalStream() = wrappedParams;
     }
     else {
         params.optionalStream() = mozilla::void_t();
     }
 
     params.bufferSize() = mBufferSize;
 
     aParams = params;
 }
 
 bool
-nsBufferedInputStream::Deserialize(const InputStreamParams& aParams)
+nsBufferedInputStream::Deserialize(const InputStreamParams& aParams,
+                                   const FileDescriptorArray& aFileDescriptors)
 {
     if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
         NS_ERROR("Received unknown parameters from the other process!");
         return false;
     }
 
     const BufferedInputStreamParams& params =
         aParams.get_BufferedInputStreamParams();
     const OptionalInputStreamParams& wrappedParams = params.optionalStream();
 
     nsCOMPtr<nsIInputStream> stream;
     if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
-        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams());
+        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
+                                        aFileDescriptors);
         if (!stream) {
             NS_WARNING("Failed to deserialize wrapped stream!");
             return false;
         }
     }
     else {
         NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
                      "Unknown type for OptionalInputStreamParams!");
--- a/netwerk/base/src/nsFileStreams.cpp
+++ b/netwerk/base/src/nsFileStreams.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "nsNetCID.h"
 
 #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067
 
 typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType;
 
 using namespace mozilla::ipc;
+using mozilla::DebugOnly;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsFileStreamBase
 
 nsFileStreamBase::nsFileStreamBase()
     : mFD(nullptr)
     , mBehaviorFlags(0)
     , mDeferredOpen(false)
@@ -528,27 +529,29 @@ nsFileInputStream::Tell(int64_t *aResult
 
 NS_IMETHODIMP
 nsFileInputStream::Available(uint64_t *aResult)
 {
     return nsFileStreamBase::Available(aResult);
 }
 
 void
-nsFileInputStream::Serialize(InputStreamParams& aParams)
+nsFileInputStream::Serialize(InputStreamParams& aParams,
+                             FileDescriptorArray& aFileDescriptors)
 {
     FileInputStreamParams params;
 
     if (mFD) {
         FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
         NS_ASSERTION(fd, "This should never be null!");
 
-        params.file() = FileDescriptor(fd);
-        NS_ASSERTION(params.file().IsValid(),
-                     "Sending an invalid file descriptor!");
+        DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
+        NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");
+
+        params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;
     } else {
         NS_WARNING("This file has not been opened (or could not be opened). "
                    "Sending an invalid file descriptor to the other process!");
     }
 
     int32_t behaviorFlags = mBehaviorFlags;
 
     // The other process shouldn't close when it reads the end because it will
@@ -565,32 +568,40 @@ nsFileInputStream::Serialize(InputStream
 
     params.behaviorFlags() = behaviorFlags;
     params.ioFlags() = mIOFlags;
 
     aParams = params;
 }
 
 bool
-nsFileInputStream::Deserialize(const InputStreamParams& aParams)
+nsFileInputStream::Deserialize(const InputStreamParams& aParams,
+                               const FileDescriptorArray& aFileDescriptors)
 {
     NS_ASSERTION(!mFD, "Already have a file descriptor?!");
     NS_ASSERTION(!mDeferredOpen, "Deferring open?!");
     NS_ASSERTION(!mFile, "Should never have a file here!");
     NS_ASSERTION(!mPerm, "This should always be 0!");
 
     if (aParams.type() != InputStreamParams::TFileInputStreamParams) {
         NS_WARNING("Received unknown parameters from the other process!");
         return false;
     }
 
     const FileInputStreamParams& params = aParams.get_FileInputStreamParams();
 
-    const FileDescriptor& fd = params.file();
-    NS_WARN_IF_FALSE(fd.IsValid(), "Received an invalid file descriptor!");
+    uint32_t fileDescriptorIndex = params.fileDescriptorIndex();
+
+    FileDescriptor fd;
+    if (fileDescriptorIndex < aFileDescriptors.Length()) {
+        fd = aFileDescriptors[fileDescriptorIndex];
+        NS_WARN_IF_FALSE(fd.IsValid(), "Received an invalid file descriptor!");
+    } else {
+        NS_WARNING("Received a bad file descriptor index!");
+    }
 
     if (fd.IsValid()) {
         PRFileDesc* fileDesc = PR_ImportFile(PROsfd(fd.PlatformHandle()));
         if (!fileDesc) {
             NS_WARNING("Failed to import file handle!");
             return false;
         }
         mFD = fileDesc;
@@ -721,56 +732,54 @@ nsPartialFileInputStream::Seek(int32_t a
     nsresult rv = nsFileInputStream::Seek(NS_SEEK_SET, offset);
     if (NS_SUCCEEDED(rv)) {
         mPosition = offset - mStart;
     }
     return rv;
 }
 
 void
-nsPartialFileInputStream::Serialize(InputStreamParams& aParams)
+nsPartialFileInputStream::Serialize(InputStreamParams& aParams,
+                                    FileDescriptorArray& aFileDescriptors)
 {
     // Serialize the base class first.
     InputStreamParams fileParams;
-    nsFileInputStream::Serialize(fileParams);
-
-    if (fileParams.type() != InputStreamParams::TFileInputStreamParams) {
-        NS_ERROR("Base class serialize failed!");
-        return;
-    }
+    nsFileInputStream::Serialize(fileParams, aFileDescriptors);
 
     PartialFileInputStreamParams params;
 
     params.fileStreamParams() = fileParams.get_FileInputStreamParams();
     params.begin() = mStart;
     params.length() = mLength;
 
     aParams = params;
 }
 
 bool
-nsPartialFileInputStream::Deserialize(const InputStreamParams& aParams)
+nsPartialFileInputStream::Deserialize(
+                                    const InputStreamParams& aParams,
+                                    const FileDescriptorArray& aFileDescriptors)
 {
     NS_ASSERTION(!mFD, "Already have a file descriptor?!");
     NS_ASSERTION(!mStart, "Already have a start?!");
     NS_ASSERTION(!mLength, "Already have a length?!");
     NS_ASSERTION(!mPosition, "Already have a position?!");
 
     if (aParams.type() != InputStreamParams::TPartialFileInputStreamParams) {
-        NS_ERROR("Received unknown parameters from the other process!");
+        NS_WARNING("Received unknown parameters from the other process!");
         return false;
     }
 
     const PartialFileInputStreamParams& params =
         aParams.get_PartialFileInputStreamParams();
 
     // Deserialize the base class first.
     InputStreamParams fileParams(params.fileStreamParams());
-    if (!nsFileInputStream::Deserialize(fileParams)) {
-        NS_ERROR("Base class deserialize failed!");
+    if (!nsFileInputStream::Deserialize(fileParams, aFileDescriptors)) {
+        NS_WARNING("Base class deserialize failed!");
         return false;
     }
 
     NS_ASSERTION(mFD, "Must have a file descriptor now!");
 
     mStart = params.begin();
     mLength = params.length();
     mPosition = 0;
--- a/netwerk/base/src/nsMIMEInputStream.cpp
+++ b/netwerk/base/src/nsMIMEInputStream.cpp
@@ -290,27 +290,27 @@ nsMIMEInputStreamConstructor(nsISupports
 
     rv = inst->QueryInterface(iid, result);
     NS_RELEASE(inst);
 
     return rv;
 }
 
 void
-nsMIMEInputStream::Serialize(InputStreamParams& aParams)
+nsMIMEInputStream::Serialize(InputStreamParams& aParams,
+                             FileDescriptorArray& aFileDescriptors)
 {
     MIMEInputStreamParams params;
 
     if (mData) {
-        nsCOMPtr<nsIIPCSerializableInputStream> stream =
-            do_QueryInterface(mData);
-        NS_ASSERTION(stream, "Wrapped stream is not serializable!");
+        nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
+        MOZ_ASSERT(stream);
 
         InputStreamParams wrappedParams;
-        stream->Serialize(wrappedParams);
+        SerializeInputStream(stream, wrappedParams, aFileDescriptors);
 
         NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
                      "Wrapped stream failed to serialize!");
 
         params.optionalStream() = wrappedParams;
     }
     else {
         params.optionalStream() = mozilla::void_t();
@@ -320,17 +320,18 @@ nsMIMEInputStream::Serialize(InputStream
     params.contentLength() = mContentLength;
     params.startedReading() = mStartedReading;
     params.addContentLength() = mAddContentLength;
 
     aParams = params;
 }
 
 bool
-nsMIMEInputStream::Deserialize(const InputStreamParams& aParams)
+nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
+                               const FileDescriptorArray& aFileDescriptors)
 {
     if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
         NS_ERROR("Received unknown parameters from the other process!");
         return false;
     }
 
     const MIMEInputStreamParams& params =
         aParams.get_MIMEInputStreamParams();
@@ -343,17 +344,18 @@ nsMIMEInputStream::Deserialize(const Inp
     // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
     mHeaderStream->ShareData(mHeaders.get(),
                              mStartedReading ? mHeaders.Length() : 0);
     mCLStream->ShareData(mContentLength.get(),
                          mStartedReading ? mContentLength.Length() : 0);
 
     nsCOMPtr<nsIInputStream> stream;
     if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
-        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams());
+        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
+                                        aFileDescriptors);
         if (!stream) {
             NS_WARNING("Failed to deserialize wrapped stream!");
             return false;
         }
 
         mData = stream;
 
         if (NS_FAILED(mStream->AppendStream(mData))) {
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -174,17 +174,20 @@ FTPChannelChild::AsyncOpen(::nsIStreamLi
   mListener = listener;
   mListenerContext = aContext;
 
   // add ourselves to the load group. 
   if (mLoadGroup)
     mLoadGroup->AddRequest(this, nullptr);
 
   OptionalInputStreamParams uploadStream;
-  SerializeInputStream(mUploadStream, uploadStream);
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  SerializeInputStream(mUploadStream, uploadStream, fds);
+
+  MOZ_ASSERT(fds.IsEmpty());
 
   FTPChannelOpenArgs openArgs;
   SerializeURI(nsBaseChannel::URI(), openArgs.uri());
   openArgs.startPos() = mStartPos;
   openArgs.entityID() = mEntityID;
   openArgs.uploadStream() = uploadStream;
 
   gNeckoChild->
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -119,17 +119,18 @@ FTPChannelParent::DoAsyncOpen(const URIP
   if (mPBOverride != kPBOverride_Unset) {
     mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   }
 
   rv = mChannel->SetNotificationCallbacks(this);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
-  nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream);
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
   if (upload) {
     // contentType and contentLength are ignored
     rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
     if (NS_FAILED(rv))
       return SendFailedAsyncOpen(rv);
   }
 
   rv = mChannel->ResumeAt(aStartPos, aEntityID);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1192,17 +1192,21 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   SerializeURI(mURI, openArgs.uri());
   SerializeURI(mOriginalURI, openArgs.original());
   SerializeURI(mDocumentURI, openArgs.doc());
   SerializeURI(mReferrer, openArgs.referrer());
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
   openArgs.requestMethod() = mRequestHead.Method();
-  SerializeInputStream(mUploadStream, openArgs.uploadStream());
+
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  SerializeInputStream(mUploadStream, openArgs.uploadStream(), fds);
+  MOZ_ASSERT(fds.IsEmpty());
+
   openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders;
   openArgs.priority() = mPriority;
   openArgs.redirectionLimit() = mRedirectionLimit;
   openArgs.allowPipelining() = mAllowPipelining;
   openArgs.forceAllowThirdPartyCookie() = mForceAllowThirdPartyCookie;
   openArgs.resumeAt() = mSendResumeAt;
   openArgs.startPos() = mStartPos;
   openArgs.entityID() = mEntityID;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -214,17 +214,18 @@ HttpChannelParent::DoAsyncOpen(  const U
   }
 
   mParentListener = new HttpChannelParentListener(this);
 
   mChannel->SetNotificationCallbacks(mParentListener);
 
   mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
 
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream);
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
   if (stream) {
     mChannel->InternalSetUploadStream(stream);
     mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL)
     mChannel->SetPriority(priority);
   mChannel->SetRedirectionLimit(redirectionLimit);
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -392,17 +392,20 @@ WebSocketChannelChild::SendBinaryMsg(con
 
 NS_IMETHODIMP
 WebSocketChannelChild::SendBinaryStream(nsIInputStream *aStream,
                                         uint32_t aLength)
 {
   LOG(("WebSocketChannelChild::SendBinaryStream() %p\n", this));
 
   OptionalInputStreamParams stream;
-  SerializeInputStream(aStream, stream);
+  nsTArray<mozilla::ipc::FileDescriptor> fds;
+  SerializeInputStream(aStream, stream, fds);
+
+  MOZ_ASSERT(fds.IsEmpty());
 
   if (!mIPCOpen || !SendSendBinaryStream(stream, aLength))
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -143,17 +143,18 @@ WebSocketChannelParent::RecvSendBinaryMs
 }
 
 bool
 WebSocketChannelParent::RecvSendBinaryStream(const InputStreamParams& aStream,
                                              const uint32_t& aLength)
 {
   LOG(("WebSocketChannelParent::RecvSendBinaryStream() %p\n", this));
   if (mChannel) {
-    nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aStream);
+    nsTArray<mozilla::ipc::FileDescriptor> fds;
+    nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aStream, fds);
     if (!stream) {
       return false;
     }
     nsresult rv = mChannel->SendBinaryStream(stream, aLength);
     NS_ENSURE_SUCCESS(rv, true);
   }
   return true;
 }
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -624,69 +624,62 @@ nsMultiplexInputStreamConstructor(nsISup
     NS_ADDREF(inst);
     nsresult rv = inst->QueryInterface(iid, result);
     NS_RELEASE(inst);
 
     return rv;
 }
 
 void
-nsMultiplexInputStream::Serialize(InputStreamParams& aParams)
+nsMultiplexInputStream::Serialize(InputStreamParams& aParams,
+                                  FileDescriptorArray& aFileDescriptors)
 {
     MultiplexInputStreamParams params;
 
     uint32_t streamCount = mStreams.Length();
 
     if (streamCount) {
         InfallibleTArray<InputStreamParams>& streams = params.streams();
 
         streams.SetCapacity(streamCount);
         for (uint32_t index = 0; index < streamCount; index++) {
-            nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-                do_QueryInterface(mStreams[index]);
-            NS_ASSERTION(serializable, "Child stream isn't serializable!");
+            InputStreamParams childStreamParams;
+            SerializeInputStream(mStreams[index], childStreamParams,
+                                 aFileDescriptors);
 
-            if (serializable) {
-                InputStreamParams childStreamParams;
-                serializable->Serialize(childStreamParams);
-
-                NS_ASSERTION(childStreamParams.type() !=
-                                 InputStreamParams::T__None,
-                             "Serialize failed!");
-
-                streams.AppendElement(childStreamParams);
-            }
+            streams.AppendElement(childStreamParams);
         }
     }
 
     params.currentStream() = mCurrentStream;
     params.status() = mStatus;
     params.startedReadingCurrent() = mStartedReadingCurrent;
 
     aParams = params;
 }
 
 bool
-nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams)
+nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams,
+                                    const FileDescriptorArray& aFileDescriptors)
 {
     if (aParams.type() !=
             InputStreamParams::TMultiplexInputStreamParams) {
         NS_ERROR("Received unknown parameters from the other process!");
         return false;
     }
 
     const MultiplexInputStreamParams& params =
         aParams.get_MultiplexInputStreamParams();
 
     const InfallibleTArray<InputStreamParams>& streams = params.streams();
 
     uint32_t streamCount = streams.Length();
     for (uint32_t index = 0; index < streamCount; index++) {
         nsCOMPtr<nsIInputStream> stream =
-            DeserializeInputStream(streams[index]);
+            DeserializeInputStream(streams[index], aFileDescriptors);
         if (!stream) {
             NS_WARNING("Deserialize failed!");
             return false;
         }
 
         if (NS_FAILED(AppendStream(stream))) {
             NS_WARNING("AppendStream failed!");
             return false;
--- a/xpcom/io/nsStringStream.cpp
+++ b/xpcom/io/nsStringStream.cpp
@@ -289,25 +289,27 @@ nsStringInputStream::SetEOF()
     if (Closed())
         return NS_BASE_STREAM_CLOSED;
 
     mOffset = Length();
     return NS_OK;
 }
 
 void
-nsStringInputStream::Serialize(InputStreamParams& aParams)
+nsStringInputStream::Serialize(InputStreamParams& aParams,
+                               FileDescriptorArray& /* aFDs */)
 {
     StringInputStreamParams params;
     params.data() = PromiseFlatCString(mData);
     aParams = params;
 }
 
 bool
-nsStringInputStream::Deserialize(const InputStreamParams& aParams)
+nsStringInputStream::Deserialize(const InputStreamParams& aParams,
+                                 const FileDescriptorArray& /* aFDs */)
 {
     if (aParams.type() != InputStreamParams::TStringInputStreamParams) {
         NS_ERROR("Received unknown parameters from the other process!");
         return false;
     }
 
     const StringInputStreamParams& params =
         aParams.get_StringInputStreamParams();