Bug 999742, Split Blob<> into BlobParent/BlobChild. rs=sicking.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 23 Apr 2014 21:43:57 -0700
changeset 199400 6ea4ce828d069fa99f3e1526f40eaeb193de17a3
parent 199399 0aab0a3738cf076dafcbc31fd95b0d6f9c8ff1e0
child 199401 6965a913e858b6feb2944649940a646f40eb4037
child 199487 3be5a3af4e6a4dbfede7f25b588949933c59a288
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs999742
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 999742, Split Blob<> into BlobParent/BlobChild. rs=sicking.
content/base/src/nsFrameMessageManager.cpp
dom/ipc/Blob.cpp
dom/ipc/Blob.h
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -27,16 +27,18 @@
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/dom/PBlobChild.h"
+#include "mozilla/dom/PBlobParent.h"
 #include "JavaScriptChild.h"
 #include "JavaScriptParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsPrintfCString.h"
 #include <algorithm>
 
 #ifdef ANDROID
 #include <android/log.h>
@@ -123,16 +125,41 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
                                                    mChrome && mIsBroadcaster)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
                                                    mChrome && !mIsBroadcaster)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
 
+enum ActorFlavorEnum {
+  Parent = 0,
+  Child
+};
+
+template <ActorFlavorEnum>
+struct BlobTraits
+{ };
+
+template <>
+struct BlobTraits<Parent>
+{
+  typedef mozilla::dom::BlobParent BlobType;
+  typedef mozilla::dom::PBlobParent ProtocolType;
+  typedef mozilla::dom::ContentParent ConcreteContentManagerType;
+};
+
+template <>
+struct BlobTraits<Child>
+{
+  typedef mozilla::dom::BlobChild BlobType;
+  typedef mozilla::dom::PBlobChild ProtocolType;
+  typedef mozilla::dom::ContentChild ConcreteContentManagerType;
+};
+
 template<ActorFlavorEnum>
 struct DataBlobs
 { };
 
 template<>
 struct DataBlobs<Parent>
 {
   typedef BlobTraits<Parent>::ProtocolType ProtocolType;
@@ -175,17 +202,18 @@ BuildClonedMessageData(typename BlobTrai
   buffer.dataLength = aData.mDataLength;
   const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
   if (!blobs.IsEmpty()) {
     typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
     InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
     uint32_t length = blobs.Length();
     blobList.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
-      Blob<Flavor>* protocolActor = aManager->GetOrCreateActorForBlob(blobs[i]);
+      typename BlobTraits<Flavor>::BlobType* protocolActor =
+        aManager->GetOrCreateActorForBlob(blobs[i]);
       if (!protocolActor) {
         return false;
       }
       blobList.AppendElement(protocolActor);
     }
   }
   return true;
 }
@@ -215,17 +243,18 @@ UnpackClonedMessageData(const ClonedMess
   const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
   StructuredCloneData cloneData;
   cloneData.mData = buffer.data;
   cloneData.mDataLength = buffer.dataLength;
   if (!blobs.IsEmpty()) {
     uint32_t length = blobs.Length();
     cloneData.mClosure.mBlobs.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
-      Blob<Flavor>* blob = static_cast<Blob<Flavor>*>(blobs[i]);
+      auto* blob =
+        static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
       MOZ_ASSERT(blob);
       nsCOMPtr<nsIDOMBlob> domBlob = blob->GetBlob();
       MOZ_ASSERT(domBlob);
       cloneData.mClosure.mBlobs.AppendElement(domBlob);
     }
   }
   return cloneData;
 }
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1,69 +1,68 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=4 ts=8 et 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 "Blob.h"
+
+#include "ContentChild.h"
+#include "ContentParent.h"
+#include "FileDescriptorSetChild.h"
+#include "jsapi.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
-
-#include "base/basictypes.h"
-
-#include "Blob.h"
-
+#include "mozilla/Monitor.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/PBlobStreamChild.h"
+#include "mozilla/dom/PBlobStreamParent.h"
+#include "mozilla/dom/PFileDescriptorSetParent.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMFile.h"
 #include "nsIDOMFile.h"
 #include "nsIInputStream.h"
 #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 "nsNetCID.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;
 using namespace mozilla::dom;
-using namespace mozilla::dom::ipc;
 using namespace mozilla::ipc;
 
 namespace {
 
-/**
- * Ensure that a nsCOMPtr/nsRefPtr is released on the main thread.
- */
+enum ActorType
+{
+  ChildActor,
+  ParentActor
+};
+
+// Ensure that a nsCOMPtr/nsRefPtr is released on the main thread.
 template <template <class> class SmartPtr, class T>
 void
 ProxyReleaseToMainThread(SmartPtr<T>& aDoomed)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   NS_ENSURE_TRUE_VOID(mainThread);
 
   if (NS_FAILED(NS_ProxyRelease(mainThread, aDoomed, true))) {
     NS_WARNING("Failed to proxy release to main thread!");
   }
 }
 
-
 class NS_NO_VTABLE IPrivateRemoteInputStream : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
 
   // This will return the underlying stream.
   virtual nsIInputStream*
   BlockAndGetInternalStream() = 0;
@@ -150,40 +149,41 @@ class RemoteInputStream : public nsIInpu
                           public nsISeekableStream,
                           public nsIIPCSerializableInputStream,
                           public IPrivateRemoteInputStream
 {
   mozilla::Monitor mMonitor;
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIDOMBlob> mSourceBlob;
   nsISeekableStream* mWeakSeekableStream;
-  ActorFlavorEnum mOrigin;
+  ActorType mOrigin;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorFlavorEnum aOrigin)
+  RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorType aOrigin)
   : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
     mWeakSeekableStream(nullptr), mOrigin(aOrigin)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aSourceBlob);
   }
 
   void
   Serialize(InputStreamParams& aParams,
             FileDescriptorArray& /* aFileDescriptors */)
   {
     nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
     MOZ_ASSERT(remote);
 
-    if (mOrigin == Parent) {
+    if (mOrigin == ParentActor) {
       aParams = RemoteInputStreamParams(
         static_cast<PBlobParent*>(remote->GetPBlob()), nullptr);
     } else {
+      MOZ_ASSERT(mOrigin == ChildActor);
       aParams = RemoteInputStreamParams(
         nullptr, static_cast<PBlobChild*>(remote->GetPBlob()));
     }
   }
 
   bool
   Deserialize(const InputStreamParams& aParams,
               const FileDescriptorArray& /* aFileDescriptors */)
@@ -426,182 +426,102 @@ NS_IMPL_RELEASE(RemoteInputStream)
 NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
 NS_INTERFACE_MAP_END
 
-template <ActorFlavorEnum ActorFlavor>
-class InputStreamActor : public BlobTraits<ActorFlavor>::StreamType
+class InputStreamChild MOZ_FINAL
+  : public PBlobStreamChild
 {
   nsRefPtr<RemoteInputStream> mRemoteStream;
 
 public:
-  InputStreamActor(RemoteInputStream* aRemoteStream)
+  InputStreamChild(RemoteInputStream* aRemoteStream)
   : mRemoteStream(aRemoteStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aRemoteStream);
   }
 
-  InputStreamActor()
+  InputStreamChild()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
 private:
   // This method is only called by the IPDL message machinery.
   virtual bool
   Recv__delete__(const InputStreamParams& aParams,
                  const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
 };
 
-template <>
-bool
-InputStreamActor<Parent>::Recv__delete__(const InputStreamParams& aParams,
-                                         const OptionalFileDescriptorSet& aFDs)
+class InputStreamParent MOZ_FINAL
+  : public PBlobStreamParent
 {
-  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());
+  nsRefPtr<RemoteInputStream> mRemoteStream;
 
-  mRemoteStream->SetStream(stream);
-  return true;
-}
-
-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);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    fdSetActor->Send__delete__(fdSetActor);
+public:
+  InputStreamParent(RemoteInputStream* aRemoteStream)
+  : mRemoteStream(aRemoteStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aRemoteStream);
   }
 
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
-  if (!stream) {
-    return false;
+  InputStreamParent()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
   }
 
-  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!");
-
-  BlobParent* actor =
-    const_cast<BlobParent*>(
-      static_cast<const BlobParent*>(aParams.sourceParent()));
-  MOZ_ASSERT(actor);
-
-  return actor->GetBlob();
-}
+private:
+  // This method is only called by the IPDL message machinery.
+  virtual bool
+  Recv__delete__(const InputStreamParams& aParams,
+                 const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
+};
 
-template <>
-inline
-already_AddRefed<nsIDOMBlob>
-GetBlobFromParams<Child>(const SlicedBlobConstructorParams& aParams)
-{
-  BlobChild* actor =
-    const_cast<BlobChild*>(
-      static_cast<const BlobChild*>(aParams.sourceChild()));
-  MOZ_ASSERT(actor);
-
-  return actor->GetBlob();
-}
-
-inline
-void
-SetBlobOnParams(BlobChild* aActor, SlicedBlobConstructorParams& aParams)
-{
-  aParams.sourceChild() = aActor;
-}
-
-inline
-void
-SetBlobOnParams(BlobParent* aActor, SlicedBlobConstructorParams& aParams)
-{
-  aParams.sourceParent() = aActor;
-}
-
-inline
 nsDOMFileBase*
 ToConcreteBlob(nsIDOMBlob* aBlob)
 {
   // XXX This is only safe so long as all blob implementations in our tree
   //     inherit nsDOMFileBase. If that ever changes then this will need to grow
   //     a real interface or something.
   return static_cast<nsDOMFileBase*>(aBlob);
 }
 
 } // anonymous namespace
 
-namespace mozilla {
-namespace dom {
-namespace ipc {
-
 // Each instance of this class will be dispatched to the network stream thread
 // pool to run the first time where it will open the file input stream. It will
 // then dispatch itself back to the main thread to send the child process its
 // response (assuming that the child has not crashed). The runnable will then
 // dispatch itself to the thread pool again in order to close the file input
 // stream.
-class BlobTraits<Parent>::BaseType::OpenStreamRunnable : public nsRunnable
+class BlobParent::OpenStreamRunnable MOZ_FINAL
+  : public nsRunnable
 {
   friend class nsRevocableEventPtr<OpenStreamRunnable>;
 
-  typedef BlobTraits<Parent> TraitsType;
-  typedef TraitsType::BaseType BlobActorType;
-  typedef TraitsType::StreamType BlobStreamProtocolType;
-
   // Only safe to access these pointers if mRevoked is false!
-  BlobActorType* mBlobActor;
-  BlobStreamProtocolType* mStreamActor;
+  BlobParent* mBlobActor;
+  PBlobStreamParent* mStreamActor;
 
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
   nsCOMPtr<nsIEventTarget> mTarget;
 
   bool mRevoked;
   bool mClosing;
 
 public:
-  OpenStreamRunnable(BlobActorType* aBlobActor,
-                     BlobStreamProtocolType* aStreamActor,
+  OpenStreamRunnable(BlobParent* aBlobActor,
+                     PBlobStreamParent* aStreamActor,
                      nsIInputStream* aStream,
                      nsIIPCSerializableInputStream* aSerializable,
                      nsIEventTarget* aTarget)
   : mBlobActor(aBlobActor), mStreamActor(aStreamActor), mStream(aStream),
     mSerializable(aSerializable), mTarget(aTarget), mRevoked(false),
     mClosing(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -728,36 +648,36 @@ private:
       nsAutoTArray<FileDescriptor, 10> fds;
       serializable->Serialize(params, fds);
 
       MOZ_ASSERT(params.type() != InputStreamParams::T__None);
 
       PFileDescriptorSetParent* fdSet = nullptr;
 
       if (!fds.IsEmpty()) {
-        Blob<Parent>* blob = static_cast<Blob<Parent>*>(mBlobActor);
+        auto* manager = static_cast<ContentParent*>(mBlobActor->Manager());
+        MOZ_ASSERT(manager);
 
-        MOZ_ASSERT(blob->Manager());
-
-        fdSet = blob->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
+        fdSet = 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);
+      unused <<
+        PBlobStreamParent::Send__delete__(mStreamActor, params, optionalFDs);
 
       mBlobActor->NoteRunnableCompleted(this);
 
 #ifdef DEBUG
       mBlobActor = nullptr;
       mStreamActor = nullptr;
 #endif
     }
@@ -769,513 +689,465 @@ private:
 
     nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 };
 
-BlobTraits<Parent>::BaseType::BaseType()
-{
-}
+/*******************************************************************************
+ * BlobChild::RemoteBlob Declaration
+ ******************************************************************************/
 
-BlobTraits<Parent>::BaseType::~BaseType()
-{
-}
-
-void
-BlobTraits<Parent>::BaseType::NoteRunnableCompleted(
-                    BlobTraits<Parent>::BaseType::OpenStreamRunnable* aRunnable)
+class BlobChild::RemoteBlob MOZ_FINAL
+  : public nsDOMFile
+  , public nsIRemoteBlob
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  class StreamHelper;
+  class SliceHelper;
 
-  for (uint32_t index = 0; index < mOpenStreamRunnables.Length(); index++) {
-    nsRevocableEventPtr<BaseType::OpenStreamRunnable>& runnable =
-      mOpenStreamRunnables[index];
+  BlobChild* mActor;
 
-    if (runnable.get() == aRunnable) {
-      runnable.Forget();
-      mOpenStreamRunnables.RemoveElementAt(index);
-      return;
-    }
+public:
+  RemoteBlob(const nsAString& aName,
+             const nsAString& aContentType,
+             uint64_t aLength,
+             uint64_t aModDate)
+    : nsDOMFile(aName, aContentType, aLength, aModDate)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
+    : nsDOMFile(aContentType, aLength)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
   }
 
-  MOZ_CRASH("Runnable not in our array!");
-}
-
-template <ActorFlavorEnum ActorFlavor>
-struct RemoteBlobBase;
+  RemoteBlob()
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
 
-template <>
-struct RemoteBlobBase<Parent>
-{
-  InputStreamParams mInputStreamParams;
-};
+  void
+  SetActor(BlobChild* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
 
-template <>
-struct RemoteBlobBase<Child>
-{ };
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE;
 
-template <ActorFlavorEnum ActorFlavor>
-class RemoteBlob : public RemoteBlobBase<ActorFlavor>,
-                   public nsDOMFile,
-                   public nsIRemoteBlob
-{
-public:
-  typedef RemoteBlob<ActorFlavor> SelfType;
-  typedef Blob<ActorFlavor> ActorType;
-  typedef InputStreamActor<ActorFlavor> StreamActorType;
-  typedef typename ActorType::ConstructorParamsType ConstructorParamsType;
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetLastModifiedDate(JSContext* cx,
+                      JS::MutableHandle<JS::Value> aLastModifiedDate)
+                      MOZ_OVERRIDE;
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE;
 
 private:
-  ActorType* mActor;
-
-  virtual ~RemoteBlob()
+  ~RemoteBlob()
   {
     if (mActor) {
       mActor->NoteDyingRemoteBlob();
     }
   }
+};
+
+class BlobChild::RemoteBlob::StreamHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobChild* mActor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsRefPtr<RemoteInputStream> mInputStream;
+  bool mDone;
+
+public:
+  StreamHelper(BlobChild* aActor, nsIDOMBlob* aSourceBlob)
+    : mMonitor("BlobChild::RemoteBlob::StreamHelper::mMonitor")
+    , mActor(aActor)
+    , mSourceBlob(aSourceBlob)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aSourceBlob);
+  }
 
   nsresult
-  GetInternalStreamViaHelper(nsIInputStream** aStream)
+  GetStream(nsIInputStream** aInputStream)
   {
-    if (!mActor) {
+    // This may be called on any thread.
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mInputStream) {
       return NS_ERROR_UNEXPECTED;
     }
 
-    nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
-    return helper->GetStream(aStream);
+    mInputStream.forget(aInputStream);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
   }
 
-  class StreamHelper : public nsRunnable
+private:
+  void
+  RunInternal(bool aNotify)
   {
-    typedef Blob<ActorFlavor> ActorType;
-    typedef InputStreamActor<ActorFlavor> StreamActorType;
-
-    mozilla::Monitor mMonitor;
-    ActorType* mActor;
-    nsCOMPtr<nsIDOMBlob> mSourceBlob;
-    nsRefPtr<RemoteInputStream> mInputStream;
-    bool mDone;
-
-  public:
-    StreamHelper(ActorType* aActor, nsIDOMBlob* aSourceBlob)
-    : mMonitor("RemoteBlob::StreamHelper::mMonitor"), mActor(aActor),
-      mSourceBlob(aSourceBlob), mDone(false)
-    {
-      // This may be created on any thread.
-      MOZ_ASSERT(aActor);
-      MOZ_ASSERT(aSourceBlob);
-    }
-
-    nsresult
-    GetStream(nsIInputStream** aInputStream)
-    {
-      // This may be called on any thread.
-      MOZ_ASSERT(aInputStream);
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mInputStream);
-      MOZ_ASSERT(!mDone);
-
-      if (NS_IsMainThread()) {
-        RunInternal(false);
-      }
-      else {
-        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
-
-        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        {
-          MonitorAutoLock lock(mMonitor);
-          while (!mDone) {
-            lock.Wait();
-          }
-        }
-      }
-
-      MOZ_ASSERT(!mActor);
-      MOZ_ASSERT(mDone);
-
-      if (!mInputStream) {
-        return NS_ERROR_UNEXPECTED;
-      }
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
 
-      mInputStream.forget(aInputStream);
-      return NS_OK;
-    }
-
-    NS_IMETHOD
-    Run()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      RunInternal(true);
-      return NS_OK;
-    }
-
-  private:
-    void
-    RunInternal(bool aNotify)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mInputStream);
-      MOZ_ASSERT(!mDone);
-
-      nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob,
-                                                                 ActorFlavor);
-
-      StreamActorType* streamActor = new StreamActorType(stream);
-      if (mActor->SendPBlobStreamConstructor(streamActor)) {
-        stream.swap(mInputStream);
-      }
-
-      mActor = nullptr;
+    nsRefPtr<RemoteInputStream> stream =
+      new RemoteInputStream(mSourceBlob, ChildActor);
 
-      if (aNotify) {
-        MonitorAutoLock lock(mMonitor);
-        mDone = true;
-        lock.Notify();
-      }
-      else {
-        mDone = true;
-      }
-    }
-  };
-
-  class SliceHelper : public nsRunnable
-  {
-    typedef Blob<ActorFlavor> ActorType;
-
-    mozilla::Monitor mMonitor;
-    ActorType* mActor;
-    nsCOMPtr<nsIDOMBlob> mSlice;
-    uint64_t mStart;
-    uint64_t mLength;
-    nsString mContentType;
-    bool mDone;
-
-  public:
-    SliceHelper(ActorType* aActor)
-    : mMonitor("RemoteBlob::SliceHelper::mMonitor"), mActor(aActor), mStart(0),
-      mLength(0), mDone(false)
-    {
-      // This may be created on any thread.
-      MOZ_ASSERT(aActor);
+    InputStreamChild* streamActor = new InputStreamChild(stream);
+    if (mActor->SendPBlobStreamConstructor(streamActor)) {
+      stream.swap(mInputStream);
     }
 
-    nsresult
-    GetSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
-             nsIDOMBlob** aSlice)
-    {
-      // This may be called on any thread.
-      MOZ_ASSERT(aSlice);
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mSlice);
-      MOZ_ASSERT(!mDone);
+    mActor = nullptr;
 
-      mStart = aStart;
-      mLength = aLength;
-      mContentType = aContentType;
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+class BlobChild::RemoteBlob::SliceHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobChild* mActor;
+  nsCOMPtr<nsIDOMBlob> mSlice;
+  uint64_t mStart;
+  uint64_t mLength;
+  nsString mContentType;
+  bool mDone;
 
-      if (NS_IsMainThread()) {
-        RunInternal(false);
-      }
-      else {
-        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+public:
+  SliceHelper(BlobChild* aActor)
+    : mMonitor("BlobChild::RemoteBlob::SliceHelper::mMonitor")
+    , mActor(aActor)
+    , mStart(0)
+    , mLength(0)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+  }
 
-        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
-        NS_ENSURE_SUCCESS(rv, rv);
+  nsresult
+  GetSlice(uint64_t aStart,
+           uint64_t aLength,
+           const nsAString& aContentType,
+           nsIDOMBlob** aSlice)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aSlice);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
 
-        {
-          MonitorAutoLock lock(mMonitor);
-          while (!mDone) {
-            lock.Wait();
-          }
+    mStart = aStart;
+    mLength = aLength;
+    mContentType = aContentType;
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
         }
       }
-
-      MOZ_ASSERT(!mActor);
-      MOZ_ASSERT(mDone);
-
-      if (!mSlice) {
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      mSlice.forget(aSlice);
-      return NS_OK;
-    }
-
-    NS_IMETHOD
-    Run()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      RunInternal(true);
-      return NS_OK;
     }
 
-  private:
-    void
-    RunInternal(bool aNotify)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mSlice);
-      MOZ_ASSERT(!mDone);
-
-      NS_ENSURE_TRUE_VOID(mActor->Manager());
-
-      NormalBlobConstructorParams normalParams;
-      normalParams.contentType() = mContentType;
-      normalParams.length() = mLength;
-
-      typename ActorType::ConstructorParamsType params;
-      ActorType::BaseType::SetBlobConstructorParams(params, normalParams);
-
-      ActorType* newActor = ActorType::Create(mActor->Manager(), params);
-      MOZ_ASSERT(newActor);
-
-      SlicedBlobConstructorParams slicedParams;
-      slicedParams.contentType() = mContentType;
-      slicedParams.begin() = mStart;
-      slicedParams.end() = mStart + mLength;
-      SetBlobOnParams(mActor, slicedParams);
-
-      typename ActorType::OtherSideConstructorParamsType otherSideParams;
-      ActorType::BaseType::SetBlobConstructorParams(otherSideParams,
-                                                    slicedParams);
-
-      if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
-        mSlice = newActor->GetBlob();
-      }
-
-      mActor = nullptr;
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
 
-      if (aNotify) {
-        MonitorAutoLock lock(mMonitor);
-        mDone = true;
-        lock.Notify();
-      }
-      else {
-        mDone = true;
-      }
-    }
-  };
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  RemoteBlob(const nsAString& aName, const nsAString& aContentType,
-             uint64_t aLength, uint64_t aModDate)
-  : nsDOMFile(aName, aContentType, aLength, aModDate), mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
-  : nsDOMFile(aContentType, aLength), mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  RemoteBlob()
-  : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
-  , mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  void
-  SetActor(ActorType* aActor)
-  {
-    MOZ_ASSERT(!aActor || !mActor);
-    mActor = aActor;
-  }
-
-  void
-  MaybeSetInputStream(const ConstructorParamsType& aParams);
-
-  virtual already_AddRefed<nsIDOMBlob>
-  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
-              MOZ_OVERRIDE
-  {
-    if (!mActor) {
-      return nullptr;
+    if (!mSlice) {
+      return NS_ERROR_UNEXPECTED;
     }
 
-    nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
-
-    nsCOMPtr<nsIDOMBlob> slice;
-    nsresult rv =
-      helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    return slice.forget();
+    mSlice.forget(aSlice);
+    return NS_OK;
   }
 
   NS_IMETHOD
-  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
-
-  virtual void*
-  GetPBlob() MOZ_OVERRIDE
+  Run()
   {
-    return static_cast<typename ActorType::ProtocolType*>(mActor);
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
   }
 
-  NS_IMETHOD
-  GetLastModifiedDate(JSContext* cx,
-                      JS::MutableHandle<JS::Value> aLastModifiedDate) MOZ_OVERRIDE
+private:
+  void
+  RunInternal(bool aNotify)
   {
-    if (IsDateUnknown()) {
-      aLastModifiedDate.setNull();
-    } else {
-      JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
-      if (!date) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      aLastModifiedDate.setObject(*date);
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    NS_ENSURE_TRUE_VOID(mActor->Manager());
+
+    NormalBlobConstructorParams normalParams;
+    normalParams.contentType() = mContentType;
+    normalParams.length() = mLength;
+
+    auto* manager = static_cast<ContentChild*>(mActor->Manager());
+    MOZ_ASSERT(manager);
+
+    BlobChild* newActor = BlobChild::Create(manager, normalParams);
+    MOZ_ASSERT(newActor);
+
+    SlicedBlobConstructorParams slicedParams;
+    slicedParams.contentType() = mContentType;
+    slicedParams.begin() = mStart;
+    slicedParams.end() = mStart + mLength;
+    slicedParams.sourceChild() = mActor;
+
+    ParentBlobConstructorParams otherSideParams;
+    otherSideParams.blobParams() = slicedParams;
+    otherSideParams.optionalInputStreamParams() = mozilla::void_t();
+
+    if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
+      mSlice = newActor->GetBlob();
     }
-    return NS_OK;
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
   }
 };
 
-template <>
-void
-RemoteBlob<Parent>::MaybeSetInputStream(const ConstructorParamsType& aParams)
+/*******************************************************************************
+ * BlobChild::RemoteBlob Implementation
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS_INHERITED1(BlobChild::RemoteBlob, nsDOMFile, nsIRemoteBlob)
+
+already_AddRefed<nsIDOMBlob>
+BlobChild::
+RemoteBlob::CreateSlice(uint64_t aStart,
+                        uint64_t aLength,
+                        const nsAString& aContentType)
 {
-  if (aParams.optionalInputStreamParams().type() ==
-      OptionalInputStreamParams::TInputStreamParams) {
-    mInputStreamParams =
-      aParams.optionalInputStreamParams().get_InputStreamParams();
+  if (!mActor) {
+    return nullptr;
   }
+
+  nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
+
+  nsCOMPtr<nsIDOMBlob> slice;
+  nsresult rv =
+    helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return slice.forget();
 }
 
-template <>
-void
-RemoteBlob<Child>::MaybeSetInputStream(const ConstructorParamsType& aParams)
+NS_IMETHODIMP
+BlobChild::
+RemoteBlob::GetInternalStream(nsIInputStream** aStream)
 {
-  // Nothing needed on the child side!
+  if (!mActor) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+  return helper->GetStream(aStream);
 }
 
-template <>
 NS_IMETHODIMP
-RemoteBlob<Parent>::GetInternalStream(nsIInputStream** aStream)
+BlobChild::
+RemoteBlob::GetLastModifiedDate(JSContext* cx,
+                                JS::MutableHandle<JS::Value> aLastModifiedDate)
 {
-  if (mInputStreamParams.type() != InputStreamParams::T__None) {
-    nsTArray<FileDescriptor> fds;
-    nsCOMPtr<nsIInputStream> realStream =
-      DeserializeInputStream(mInputStreamParams, fds);
-    if (!realStream) {
-      NS_WARNING("Failed to deserialize stream!");
-      return NS_ERROR_UNEXPECTED;
+  if (IsDateUnknown()) {
+    aLastModifiedDate.setNull();
+  } else {
+    JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
+    if (!date) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
-
-    nsCOMPtr<nsIInputStream> stream =
-      new BlobInputStreamTether(realStream, this);
-    stream.forget(aStream);
-    return NS_OK;
+    aLastModifiedDate.setObject(*date);
   }
-
-  return GetInternalStreamViaHelper(aStream);
+  return NS_OK;
 }
 
-template <>
-NS_IMETHODIMP
-RemoteBlob<Child>::GetInternalStream(nsIInputStream** aStream)
+void*
+BlobChild::
+RemoteBlob::GetPBlob()
 {
-  return GetInternalStreamViaHelper(aStream);
+  return static_cast<PBlobChild*>(mActor);
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>::Blob(ContentManager* aManager, nsIDOMBlob* aBlob)
-: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true)
-, mBlobIsFile(false), mManager(aManager)
+/*******************************************************************************
+ * BlobChild
+ ******************************************************************************/
+
+BlobChild::BlobChild(ContentChild* aManager, nsIDOMBlob* aBlob)
+  : mBlob(aBlob)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(true)
+  , mBlobIsFile(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
   MOZ_ASSERT(aBlob);
-  MOZ_ASSERT(aManager);
+
   aBlob->AddRef();
 
   nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
   mBlobIsFile = !!file;
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>::Blob(ContentManager* aManager,
-                        const ConstructorParamsType& aParams)
-: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(false)
-, mBlobIsFile(false), mManager(aManager)
+BlobChild::BlobChild(ContentChild* aManager,
+                     const ChildBlobConstructorParams& aParams)
+  : mBlob(nullptr)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(false)
+  , mBlobIsFile(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
 
-  ChildBlobConstructorParams::Type paramType =
-    BaseType::GetBlobConstructorParams(aParams).type();
+  ChildBlobConstructorParams::Type paramsType = aParams.type();
 
   mBlobIsFile =
-    paramType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
-    paramType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
+    paramsType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
+    paramsType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
 
-  nsRefPtr<RemoteBlobType> remoteBlob = CreateRemoteBlob(aParams);
+  nsRefPtr<RemoteBlob> remoteBlob = CreateRemoteBlob(aParams);
   MOZ_ASSERT(remoteBlob);
 
   remoteBlob->SetActor(this);
-  remoteBlob->MaybeSetInputStream(aParams);
   remoteBlob.forget(&mRemoteBlob);
 
   mBlob = mRemoteBlob;
   mOwnsBlob = true;
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>*
-Blob<ActorFlavor>::Create(ContentManager* aManager,
-                          const ConstructorParamsType& aParams)
+BlobChild::~BlobChild()
+{
+}
+
+BlobChild*
+BlobChild::Create(ContentChild* aManager,
+                  const ChildBlobConstructorParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
 
-  const ChildBlobConstructorParams& blobParams =
-    BaseType::GetBlobConstructorParams(aParams);
-
-  switch (blobParams.type()) {
+  switch (aParams.type()) {
     case ChildBlobConstructorParams::TNormalBlobConstructorParams:
     case ChildBlobConstructorParams::TFileBlobConstructorParams:
     case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
-      return new Blob<ActorFlavor>(aManager, aParams);
+      return new BlobChild(aManager, aParams);
 
     case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
       const SlicedBlobConstructorParams& params =
-        blobParams.get_SlicedBlobConstructorParams();
+        aParams.get_SlicedBlobConstructorParams();
 
-      nsCOMPtr<nsIDOMBlob> source = GetBlobFromParams<ActorFlavor>(params);
+      auto* actor =
+        const_cast<BlobChild*>(
+          static_cast<const BlobChild*>(params.sourceChild()));
+      MOZ_ASSERT(actor);
+
+      nsCOMPtr<nsIDOMBlob> source = actor->GetBlob();
       MOZ_ASSERT(source);
 
       nsCOMPtr<nsIDOMBlob> slice;
       nsresult rv =
         source->Slice(params.begin(), params.end(), params.contentType(), 3,
                       getter_AddRefs(slice));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
-      return new Blob<ActorFlavor>(aManager, slice);
+      return new BlobChild(aManager, slice);
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   return nullptr;
 }
 
-template <ActorFlavorEnum ActorFlavor>
 already_AddRefed<nsIDOMBlob>
-Blob<ActorFlavor>::GetBlob()
+BlobChild::GetBlob()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
 
   nsCOMPtr<nsIDOMBlob> blob;
 
   // Remote blobs are held alive until the first call to GetBlob. Thereafter we
   // only hold a weak reference. Normal blobs are held alive until the actor is
@@ -1288,151 +1160,944 @@ Blob<ActorFlavor>::GetBlob()
     blob = mBlob;
   }
 
   MOZ_ASSERT(blob);
 
   return blob.forget();
 }
 
-template <ActorFlavorEnum ActorFlavor>
 bool
-Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aName,
-                                      const nsString& aContentType,
-                                      uint64_t aLength,
-                                      uint64_t aLastModifiedDate)
+BlobChild::SetMysteryBlobInfo(const nsString& aName,
+                              const nsString& aContentType,
+                              uint64_t aLength,
+                              uint64_t aLastModifiedDate)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
   MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
 
-  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType,
-                                     aLength, aLastModifiedDate);
+  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength,
+                                     aLastModifiedDate);
 
-  FileBlobConstructorParams params(aName, aContentType,
-                                   aLength, aLastModifiedDate);
-  return ProtocolType::SendResolveMystery(params);
+  FileBlobConstructorParams params(aName, aContentType, aLength,
+                                   aLastModifiedDate);
+  return SendResolveMystery(params);
 }
 
-template <ActorFlavorEnum ActorFlavor>
 bool
-Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aContentType,
-                                      uint64_t aLength)
+BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
 
   nsString voidString;
   voidString.SetIsVoid(true);
 
-  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType,
-                                     aLength, UINT64_MAX);
+  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength,
+                                     UINT64_MAX);
 
   NormalBlobConstructorParams params(aContentType, aLength);
-  return ProtocolType::SendResolveMystery(params);
+  return SendResolveMystery(params);
 }
 
-template <ActorFlavorEnum ActorFlavor>
-already_AddRefed<typename Blob<ActorFlavor>::RemoteBlobType>
-Blob<ActorFlavor>::CreateRemoteBlob(const ConstructorParamsType& aParams)
+already_AddRefed<BlobChild::RemoteBlob>
+BlobChild::CreateRemoteBlob(const ChildBlobConstructorParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  const ChildBlobConstructorParams& blobParams =
-    BaseType::GetBlobConstructorParams(aParams);
+  nsRefPtr<RemoteBlob> remoteBlob;
 
-  nsRefPtr<RemoteBlobType> remoteBlob;
-
-  switch (blobParams.type()) {
+  switch (aParams.type()) {
     case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
       const NormalBlobConstructorParams& params =
-        blobParams.get_NormalBlobConstructorParams();
-      remoteBlob = new RemoteBlobType(params.contentType(), params.length());
+        aParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlob(params.contentType(), params.length());
       break;
     }
 
     case ChildBlobConstructorParams::TFileBlobConstructorParams: {
       const FileBlobConstructorParams& params =
-        blobParams.get_FileBlobConstructorParams();
+        aParams.get_FileBlobConstructorParams();
       remoteBlob =
-        new RemoteBlobType(params.name(), params.contentType(),
-                           params.length(), params.modDate());
+        new RemoteBlob(params.name(), params.contentType(), params.length(),
+                       params.modDate());
       break;
     }
 
     case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
-      remoteBlob = new RemoteBlobType();
+      remoteBlob = new RemoteBlob();
       break;
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   MOZ_ASSERT(remoteBlob);
 
   if (NS_FAILED(remoteBlob->SetMutable(false))) {
     MOZ_CRASH("Failed to make remote blob immutable!");
   }
 
   return remoteBlob.forget();
 }
 
-template <ActorFlavorEnum ActorFlavor>
 void
-Blob<ActorFlavor>::NoteDyingRemoteBlob()
+BlobChild::NoteDyingRemoteBlob()
 {
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
   MOZ_ASSERT(!mOwnsBlob);
 
   // This may be called on any thread due to the fact that RemoteBlob is
   // designed to be passed between threads. We must start the shutdown process
   // on the main thread, so we proxy here if necessary.
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> runnable =
-      NS_NewNonOwningRunnableMethod(this,
-                                    &Blob<ActorFlavor>::NoteDyingRemoteBlob);
+      NS_NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlob);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       MOZ_ASSERT(false, "Should never fail!");
     }
 
     return;
   }
 
   // Must do this before calling Send__delete__ or we'll crash there trying to
   // access a dangling pointer.
   mBlob = nullptr;
   mRemoteBlob = nullptr;
 
-  mozilla::unused << ProtocolType::Send__delete__(this);
+  PBlobChild::Send__delete__(this);
 }
 
-template <ActorFlavorEnum ActorFlavor>
 void
-Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
+BlobChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mRemoteBlob) {
     mRemoteBlob->SetActor(nullptr);
   }
 
   if (mBlob && mOwnsBlob) {
     mBlob->Release();
   }
 
-  mManager = nullptr;
+  mStrongManager = nullptr;
+}
+
+PBlobStreamChild*
+BlobChild::AllocPBlobStreamChild()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return new InputStreamChild();
+}
+
+bool
+BlobChild::RecvPBlobStreamConstructor(PBlobStreamChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+    do_QueryInterface(stream);
+  if (!serializable) {
+    MOZ_ASSERT(false, "Must be serializable!");
+    return false;
+  }
+
+  InputStreamParams 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, mozilla::void_t());
+}
+
+bool
+BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  delete static_cast<InputStreamChild*>(aActor);
+  return true;
+}
+
+bool
+BlobChild::RecvResolveMystery(const ResolveMysteryParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(mOwnsBlob);
+
+  if (!mBlobIsFile) {
+    MOZ_ASSERT(false, "Must always be a file!");
+    return false;
+  }
+
+  nsDOMFileBase* blob = ToConcreteBlob(mBlob);
+
+  switch (aParams.type()) {
+    case ResolveMysteryParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        aParams.get_NormalBlobConstructorParams();
+      nsString voidString;
+      voidString.SetIsVoid(true);
+      blob->SetLazyData(voidString, params.contentType(), params.length(),
+                        UINT64_MAX);
+      break;
+    }
+
+    case ResolveMysteryParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        aParams.get_FileBlobConstructorParams();
+      blob->SetLazyData(params.name(), params.contentType(), params.length(),
+                        params.modDate());
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  return true;
 }
 
-template <ActorFlavorEnum ActorFlavor>
+/*******************************************************************************
+ * BlobParent::RemoteBlob Declaration
+ ******************************************************************************/
+
+class BlobParent::RemoteBlob MOZ_FINAL
+  : public nsDOMFile
+  , public nsIRemoteBlob
+{
+  class StreamHelper;
+  class SliceHelper;
+
+  BlobParent* mActor;
+  InputStreamParams mInputStreamParams;
+
+public:
+  RemoteBlob(const nsAString& aName,
+             const nsAString& aContentType,
+             uint64_t aLength,
+             uint64_t aModDate)
+    : nsDOMFile(aName, aContentType, aLength, aModDate)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
+    : nsDOMFile(aContentType, aLength)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob()
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  void
+  SetActor(BlobParent* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
+
+  void
+  MaybeSetInputStream(const ParentBlobConstructorParams& aParams)
+  {
+    if (aParams.optionalInputStreamParams().type() ==
+        OptionalInputStreamParams::TInputStreamParams) {
+      mInputStreamParams =
+        aParams.optionalInputStreamParams().get_InputStreamParams();
+    }
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetLastModifiedDate(JSContext* cx,
+                      JS::MutableHandle<JS::Value> aLastModifiedDate)
+                      MOZ_OVERRIDE;
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE;
+
+private:
+  ~RemoteBlob()
+  {
+    if (mActor) {
+      mActor->NoteDyingRemoteBlob();
+    }
+  }
+};
+
+class BlobParent::RemoteBlob::StreamHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobParent* mActor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsRefPtr<RemoteInputStream> mInputStream;
+  bool mDone;
+
+public:
+  StreamHelper(BlobParent* aActor, nsIDOMBlob* aSourceBlob)
+    : mMonitor("BlobParent::RemoteBlob::StreamHelper::mMonitor")
+    , mActor(aActor)
+    , mSourceBlob(aSourceBlob)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aSourceBlob);
+  }
+
+  nsresult
+  GetStream(nsIInputStream** aInputStream)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mInputStream) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mInputStream.forget(aInputStream);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
+  }
+
+private:
+  void
+  RunInternal(bool aNotify)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    nsRefPtr<RemoteInputStream> stream =
+      new RemoteInputStream(mSourceBlob, ParentActor);
+
+    InputStreamParent* streamActor = new InputStreamParent(stream);
+    if (mActor->SendPBlobStreamConstructor(streamActor)) {
+      stream.swap(mInputStream);
+    }
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+class BlobParent::RemoteBlob::SliceHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobParent* mActor;
+  nsCOMPtr<nsIDOMBlob> mSlice;
+  uint64_t mStart;
+  uint64_t mLength;
+  nsString mContentType;
+  bool mDone;
+
+public:
+  SliceHelper(BlobParent* aActor)
+    : mMonitor("BlobParent::RemoteBlob::SliceHelper::mMonitor")
+    , mActor(aActor)
+    , mStart(0)
+    , mLength(0)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+  }
+
+  nsresult
+  GetSlice(uint64_t aStart,
+           uint64_t aLength,
+           const nsAString& aContentType,
+           nsIDOMBlob** aSlice)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aSlice);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    mStart = aStart;
+    mLength = aLength;
+    mContentType = aContentType;
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mSlice) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mSlice.forget(aSlice);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
+  }
+
+private:
+  void
+  RunInternal(bool aNotify)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    NS_ENSURE_TRUE_VOID(mActor->Manager());
+
+    NormalBlobConstructorParams normalParams;
+    normalParams.contentType() = mContentType;
+    normalParams.length() = mLength;
+
+    ParentBlobConstructorParams params;
+    params.blobParams() = normalParams;
+    params.optionalInputStreamParams() = void_t();
+
+    auto* manager = static_cast<ContentParent*>(mActor->Manager());
+    MOZ_ASSERT(manager);
+
+    BlobParent* newActor = BlobParent::Create(manager, params);
+    MOZ_ASSERT(newActor);
+
+    SlicedBlobConstructorParams slicedParams;
+    slicedParams.contentType() = mContentType;
+    slicedParams.begin() = mStart;
+    slicedParams.end() = mStart + mLength;
+    slicedParams.sourceParent() = mActor;
+
+    ChildBlobConstructorParams otherSideParams = slicedParams;
+
+    if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
+      mSlice = newActor->GetBlob();
+    }
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+/*******************************************************************************
+ * BlobChild::RemoteBlob Implementation
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS_INHERITED1(BlobParent::RemoteBlob, nsDOMFile, nsIRemoteBlob)
+
+already_AddRefed<nsIDOMBlob>
+BlobParent::
+RemoteBlob::CreateSlice(uint64_t aStart,
+                        uint64_t aLength,
+                        const nsAString& aContentType)
+{
+  if (!mActor) {
+    return nullptr;
+  }
+
+  nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
+
+  nsCOMPtr<nsIDOMBlob> slice;
+  nsresult rv =
+    helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return slice.forget();
+}
+
+NS_IMETHODIMP
+BlobParent::
+RemoteBlob::GetInternalStream(nsIInputStream** aStream)
+{
+  if (mInputStreamParams.type() != InputStreamParams::T__None) {
+    nsTArray<FileDescriptor> fds;
+    nsCOMPtr<nsIInputStream> realStream =
+      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);
+    return NS_OK;
+  }
+
+  if (!mActor) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+  return helper->GetStream(aStream);
+}
+
+NS_IMETHODIMP
+BlobParent::
+RemoteBlob::GetLastModifiedDate(JSContext* cx,
+                                JS::MutableHandle<JS::Value> aLastModifiedDate)
+{
+  if (IsDateUnknown()) {
+    aLastModifiedDate.setNull();
+  } else {
+    JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
+    if (!date) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    aLastModifiedDate.setObject(*date);
+  }
+  return NS_OK;
+}
+
+void*
+BlobParent::
+RemoteBlob::GetPBlob()
+{
+  return static_cast<PBlobParent*>(mActor);
+}
+
+/*******************************************************************************
+ * BlobParent
+ ******************************************************************************/
+
+BlobParent::BlobParent(ContentParent* aManager, nsIDOMBlob* aBlob)
+  : mBlob(aBlob)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(true)
+  , mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aBlob);
+
+  aBlob->AddRef();
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+  mBlobIsFile = !!file;
+}
+
+BlobParent::BlobParent(ContentParent* aManager,
+                       const ParentBlobConstructorParams& aParams)
+  : mBlob(nullptr)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(false)
+  , mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+
+  ChildBlobConstructorParams::Type paramsType = aParams.blobParams().type();
+
+  mBlobIsFile =
+    paramsType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
+    paramsType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
+
+  nsRefPtr<RemoteBlob> remoteBlob = CreateRemoteBlob(aParams);
+  MOZ_ASSERT(remoteBlob);
+
+  remoteBlob->SetActor(this);
+  remoteBlob->MaybeSetInputStream(aParams);
+  remoteBlob.forget(&mRemoteBlob);
+
+  mBlob = mRemoteBlob;
+  mOwnsBlob = true;
+}
+
+BlobParent::~BlobParent()
+{
+}
+
+BlobParent*
+BlobParent::Create(ContentParent* aManager,
+                   const ParentBlobConstructorParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+
+  const ChildBlobConstructorParams& blobParams = aParams.blobParams();
+
+  switch (blobParams.type()) {
+    case ChildBlobConstructorParams::TNormalBlobConstructorParams:
+    case ChildBlobConstructorParams::TFileBlobConstructorParams:
+    case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
+      return new BlobParent(aManager, aParams);
+
+    case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
+      const SlicedBlobConstructorParams& params =
+        blobParams.get_SlicedBlobConstructorParams();
+
+      auto* actor =
+        const_cast<BlobParent*>(
+          static_cast<const BlobParent*>(params.sourceParent()));
+      MOZ_ASSERT(actor);
+
+      nsCOMPtr<nsIDOMBlob> source = actor->GetBlob();
+      MOZ_ASSERT(source);
+
+      nsCOMPtr<nsIDOMBlob> slice;
+      nsresult rv =
+        source->Slice(params.begin(), params.end(), params.contentType(), 3,
+                      getter_AddRefs(slice));
+      NS_ENSURE_SUCCESS(rv, nullptr);
+
+      return new BlobParent(aManager, slice);
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  return nullptr;
+}
+
+already_AddRefed<nsIDOMBlob>
+BlobParent::GetBlob()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+
+  nsCOMPtr<nsIDOMBlob> blob;
+
+  // Remote blobs are held alive until the first call to GetBlob. Thereafter we
+  // only hold a weak reference. Normal blobs are held alive until the actor is
+  // destroyed.
+  if (mRemoteBlob && mOwnsBlob) {
+    blob = dont_AddRef(mBlob);
+    mOwnsBlob = false;
+  }
+  else {
+    blob = mBlob;
+  }
+
+  MOZ_ASSERT(blob);
+
+  return blob.forget();
+}
+
 bool
-Blob<ActorFlavor>::RecvResolveMystery(const ResolveMysteryParams& aParams)
+BlobParent::SetMysteryBlobInfo(const nsString& aName,
+                               const nsString& aContentType,
+                               uint64_t aLength,
+                               uint64_t aLastModifiedDate)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
+
+  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength,
+                                     aLastModifiedDate);
+
+  FileBlobConstructorParams params(aName, aContentType, aLength,
+                                   aLastModifiedDate);
+  return SendResolveMystery(params);
+}
+
+bool
+BlobParent::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+
+  nsString voidString;
+  voidString.SetIsVoid(true);
+
+  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength,
+                                     UINT64_MAX);
+
+  NormalBlobConstructorParams params(aContentType, aLength);
+  return SendResolveMystery(params);
+}
+
+already_AddRefed<BlobParent::RemoteBlob>
+BlobParent::CreateRemoteBlob(const ParentBlobConstructorParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  const ChildBlobConstructorParams& blobParams = aParams.blobParams();
+
+  nsRefPtr<RemoteBlob> remoteBlob;
+
+  switch (blobParams.type()) {
+    case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        blobParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlob(params.contentType(), params.length());
+      break;
+    }
+
+    case ChildBlobConstructorParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        blobParams.get_FileBlobConstructorParams();
+      remoteBlob =
+        new RemoteBlob(params.name(), params.contentType(), params.length(),
+                       params.modDate());
+      break;
+    }
+
+    case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
+      remoteBlob = new RemoteBlob();
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  MOZ_ASSERT(remoteBlob);
+
+  if (NS_FAILED(remoteBlob->SetMutable(false))) {
+    MOZ_CRASH("Failed to make remote blob immutable!");
+  }
+
+  return remoteBlob.forget();
+}
+
+void
+BlobParent::NoteDyingRemoteBlob()
+{
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(!mOwnsBlob);
+
+  // This may be called on any thread due to the fact that RemoteBlob is
+  // designed to be passed between threads. We must start the shutdown process
+  // on the main thread, so we proxy here if necessary.
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlob);
+    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+      MOZ_ASSERT(false, "Should never fail!");
+    }
+
+    return;
+  }
+
+  // Must do this before calling Send__delete__ or we'll crash there trying to
+  // access a dangling pointer.
+  mBlob = nullptr;
+  mRemoteBlob = nullptr;
+
+  mozilla::unused << PBlobParent::Send__delete__(this);
+}
+
+void
+BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (uint32_t index = 0; index < mOpenStreamRunnables.Length(); index++) {
+    nsRevocableEventPtr<OpenStreamRunnable>& runnable =
+      mOpenStreamRunnables[index];
+
+    if (runnable.get() == aRunnable) {
+      runnable.Forget();
+      mOpenStreamRunnables.RemoveElementAt(index);
+      return;
+    }
+  }
+
+  MOZ_CRASH("Runnable not in our array!");
+}
+
+void
+BlobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mRemoteBlob) {
+    mRemoteBlob->SetActor(nullptr);
+  }
+
+  if (mBlob && mOwnsBlob) {
+    mBlob->Release();
+  }
+
+  mStrongManager = nullptr;
+}
+
+PBlobStreamParent*
+BlobParent::AllocPBlobStreamParent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return new InputStreamParent();
+}
+
+bool
+BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlob);
+
+  nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
+  if (remoteBlob) {
+    remoteStream = do_QueryInterface(stream);
+  }
+
+  // There are three cases in which we can use the stream obtained from the blob
+  // directly as our serialized stream:
+  //
+  //   1. The blob is not a remote blob.
+  //   2. The blob is a remote blob that represents this actor.
+  //   3. The blob is a remote blob representing a different actor but we
+  //      already have a non-remote, i.e. serialized, serialized stream.
+  //
+  // In all other cases we need to be on a background thread before we can get
+  // to the real stream.
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
+  if (!remoteBlob ||
+      static_cast<PBlobParent*>(remoteBlob->GetPBlob()) == this ||
+      !remoteStream) {
+    serializableStream = do_QueryInterface(stream);
+    if (!serializableStream) {
+      MOZ_ASSERT(false, "Must be serializable!");
+      return false;
+    }
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(target, false);
+
+  nsRefPtr<OpenStreamRunnable> runnable =
+    new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
+
+  rv = runnable->Dispatch();
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // nsRevocableEventPtr lacks some of the operators needed for anything nicer.
+  *mOpenStreamRunnables.AppendElement() = runnable;
+  return true;
+}
+
+bool
+BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  delete static_cast<InputStreamParent*>(aActor);
+  return true;
+}
+
+bool
+BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(!mRemoteBlob);
   MOZ_ASSERT(mOwnsBlob);
 
   if (!mBlobIsFile) {
     MOZ_ASSERT(false, "Must always be a file!");
@@ -1462,141 +2127,59 @@ Blob<ActorFlavor>::RecvResolveMystery(co
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   return true;
 }
 
-template <>
 bool
-Blob<Parent>::RecvPBlobStreamConstructor(StreamType* aActor)
+InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
+                                 const OptionalFileDescriptorSet& aFDs)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBlob);
-  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(mRemoteStream);
 
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsTArray<FileDescriptor> fds;
+  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+    FileDescriptorSetChild* fdSetActor =
+      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
+    MOZ_ASSERT(fdSetActor);
 
-  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlob);
+    fdSetActor->ForgetFileDescriptors(fds);
+    MOZ_ASSERT(!fds.IsEmpty());
 
-  nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
-  if (remoteBlob) {
-    remoteStream = do_QueryInterface(stream);
+    fdSetActor->Send__delete__(fdSetActor);
   }
 
-  // There are three cases in which we can use the stream obtained from the blob
-  // directly as our serialized stream:
-  //
-  //   1. The blob is not a remote blob.
-  //   2. The blob is a remote blob that represents this actor.
-  //   3. The blob is a remote blob representing a different actor but we
-  //      already have a non-remote, i.e. serialized, serialized stream.
-  //
-  // In all other cases we need to be on a background thread before we can get
-  // to the real stream.
-  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
-  if (!remoteBlob ||
-      static_cast<ProtocolType*>(remoteBlob->GetPBlob()) == this ||
-      !remoteStream) {
-    serializableStream = do_QueryInterface(stream);
-    if (!serializableStream) {
-      MOZ_ASSERT(false, "Must be serializable!");
-      return false;
-    }
-  }
-
-  nsCOMPtr<nsIEventTarget> target =
-    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(target, false);
-
-  nsRefPtr<BaseType::OpenStreamRunnable> runnable =
-    new BaseType::OpenStreamRunnable(this, aActor, stream, serializableStream,
-                                     target);
-
-  rv = runnable->Dispatch();
-  NS_ENSURE_SUCCESS(rv, false);
-
-  nsRevocableEventPtr<BaseType::OpenStreamRunnable>* arrayMember =
-    mOpenStreamRunnables.AppendElement();
-  *arrayMember = runnable;
-  return true;
-}
-
-template <>
-bool
-Blob<Child>::RecvPBlobStreamConstructor(StreamType* aActor)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBlob);
-  MOZ_ASSERT(!mRemoteBlob);
-
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-    do_QueryInterface(stream);
-  if (!serializable) {
-    MOZ_ASSERT(false, "Must be serializable!");
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  if (!stream) {
     return false;
   }
 
-  InputStreamParams 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, mozilla::void_t());
-}
-
-BlobTraits<Parent>::StreamType*
-BlobTraits<Parent>::BaseType::AllocPBlobStreamParent()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return new InputStreamActor<Parent>();
-}
-
-BlobTraits<Child>::StreamType*
-BlobTraits<Child>::BaseType::AllocPBlobStreamChild()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return new InputStreamActor<Child>();
-}
-
-bool
-BlobTraits<Parent>::BaseType::DeallocPBlobStreamParent(BlobTraits<Parent>::StreamType* aActor)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  delete aActor;
+  mRemoteStream->SetStream(stream);
   return true;
 }
 
 bool
-BlobTraits<Child>::BaseType::DeallocPBlobStreamChild(BlobTraits<Child>::StreamType* aActor)
+InputStreamParent::Recv__delete__(const InputStreamParams& aParams,
+                                  const OptionalFileDescriptorSet& aFDs)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  delete aActor;
+  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;
 }
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_ADDREF_INHERITED(RemoteBlob<ActorFlavor>, nsDOMFile)
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_RELEASE_INHERITED(RemoteBlob<ActorFlavor>, nsDOMFile)
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_QUERY_INTERFACE_INHERITED1(RemoteBlob<ActorFlavor>, nsDOMFile,
-                                                            nsIRemoteBlob)
-
-// Explicit instantiation of both classes.
-template class Blob<Parent>;
-template class Blob<Child>;
-
-} // namespace ipc
-} // namespace dom
-} // namespace mozilla
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -1,248 +1,196 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set sw=4 ts=8 et 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_Blob_h
 #define mozilla_dom_ipc_Blob_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/PBlobParent.h"
-#include "mozilla/dom/PBlobStreamChild.h"
-#include "mozilla/dom/PBlobStreamParent.h"
-
 #include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
 #include "nsTArray.h"
 
 class nsIDOMBlob;
-template<class T> class nsRevocableEventPtr;
+class nsString;
+template <class> class nsRevocableEventPtr;
 
 namespace mozilla {
 namespace dom {
 
+class ContentChild;
 class ContentParent;
-class ContentChild;
-
-namespace ipc {
-
-enum ActorFlavorEnum
-{
-  Parent = 0,
-  Child
-};
-
-template <ActorFlavorEnum>
-struct BlobTraits;
-
-template <>
-struct BlobTraits<Parent>
-{
-  typedef mozilla::dom::PBlobParent ProtocolType;
-  typedef mozilla::dom::PBlobStreamParent StreamType;
-  typedef mozilla::dom::ParentBlobConstructorParams ConstructorParamsType;
-  typedef mozilla::dom::ChildBlobConstructorParams
-          OtherSideConstructorParamsType;
-  typedef mozilla::dom::ContentParent ConcreteContentManagerType;
-
-  // BaseType on the parent side is a bit more complicated than for the child
-  // side. In the case of nsIInputStreams backed by files we need to ensure that
-  // the files are actually opened and closed on a background thread before we
-  // can send their file handles across to the child. The child process could
-  // crash during this process so we need to make sure we cancel the intended
-  // response in such a case. We do that by holding an array of
-  // nsRevocableEventPtr. If the child crashes then this actor will be destroyed
-  // and the nsRevocableEventPtr destructor will cancel any stream events that
-  // are currently in flight.
-  class BaseType : public ProtocolType
-  {
-  public:
-    static const ChildBlobConstructorParams&
-    GetBlobConstructorParams(const ConstructorParamsType& aParams)
-    {
-      return aParams.blobParams();
-    }
-
-    static void
-    SetBlobConstructorParams(ConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams.blobParams() = aBlobParams;
-      aParams.optionalInputStreamParams() = mozilla::void_t();
-    }
-
-    static void
-    SetBlobConstructorParams(OtherSideConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams = aBlobParams;
-    }
-
-  protected:
-    virtual StreamType*
-    AllocPBlobStreamParent() MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPBlobStreamParent(StreamType* aActor) MOZ_OVERRIDE;
-
-    BaseType();
-    virtual ~BaseType();
-
-    class OpenStreamRunnable;
-    friend class OpenStreamRunnable;
-
-    void
-    NoteRunnableCompleted(OpenStreamRunnable* aRunnable);
-
-    nsTArray<nsRevocableEventPtr<OpenStreamRunnable> > mOpenStreamRunnables;
-  };
-};
+class PBlobStreamChild;
+class PBlobStreamParent;
 
-template <>
-struct BlobTraits<Child>
+class BlobChild MOZ_FINAL
+  : public PBlobChild
 {
-  typedef mozilla::dom::PBlobChild ProtocolType;
-  typedef mozilla::dom::PBlobStreamChild StreamType;
-  typedef mozilla::dom::ChildBlobConstructorParams ConstructorParamsType;
-  typedef mozilla::dom::ParentBlobConstructorParams
-          OtherSideConstructorParamsType;
-  typedef mozilla::dom::ContentChild ConcreteContentManagerType;
-
-
-  class BaseType : public ProtocolType
-  {
-  public:
-    static const ChildBlobConstructorParams&
-    GetBlobConstructorParams(const ConstructorParamsType& aParams)
-    {
-      return aParams;
-    }
-
-    static void
-    SetBlobConstructorParams(ConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams = aBlobParams;
-    }
-
-    static void
-    SetBlobConstructorParams(OtherSideConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams.blobParams() = aBlobParams;
-      aParams.optionalInputStreamParams() = mozilla::void_t();
-    }
+  friend class ContentChild;
 
-  protected:
-    virtual StreamType*
-    AllocPBlobStreamChild() MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPBlobStreamChild(StreamType* aActor) MOZ_OVERRIDE;
-
-    BaseType()
-    { }
-
-    virtual ~BaseType()
-    { }
-  };
-};
-
-template <ActorFlavorEnum>
-class RemoteBlob;
+  class RemoteBlob;
+  friend class RemoteBlob;
 
-template <ActorFlavorEnum ActorFlavor>
-class Blob : public BlobTraits<ActorFlavor>::BaseType
-{
-  friend class RemoteBlob<ActorFlavor>;
-  friend class BlobTraits<ActorFlavor>::BaseType;
+  nsIDOMBlob* mBlob;
+  RemoteBlob* mRemoteBlob;
+  nsRefPtr<ContentChild> mStrongManager;
 
-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
-          OtherSideConstructorParamsType;
-  typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
-  typedef RemoteBlob<ActorFlavor> RemoteBlobType;
-  typedef mozilla::ipc::IProtocolManager<
-                      mozilla::ipc::IProtocol>::ActorDestroyReason
-          ActorDestroyReason;
-
-protected:
-  nsIDOMBlob* mBlob;
-  RemoteBlobType* mRemoteBlob;
   bool mOwnsBlob;
   bool mBlobIsFile;
 
 public:
   // This create function is called on the sending side.
-  static Blob*
-  Create(ContentManager* aManager, nsIDOMBlob* aBlob)
+  static BlobChild*
+  Create(ContentChild* aManager, nsIDOMBlob* aBlob)
   {
-    return new Blob(aManager, aBlob);
+    return new BlobChild(aManager, aBlob);
   }
 
-  // This create function is called on the receiving side.
-  static Blob*
-  Create(ContentManager* aManager, const ConstructorParamsType& aParams);
+  // Get the blob associated with this actor. This may always be called on the
+  // sending side. It may also be called on the receiving side unless this is a
+  // "mystery" blob that has not yet received a SetMysteryBlobInfo() call.
+  already_AddRefed<nsIDOMBlob>
+  GetBlob();
+
+  // Use this for files.
+  bool
+  SetMysteryBlobInfo(const nsString& aName,
+                     const nsString& aContentType,
+                     uint64_t aLength,
+                     uint64_t aLastModifiedDate);
+
+  // Use this for non-file blobs.
+  bool
+  SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
+
+private:
+  // This constructor is called on the sending side.
+  BlobChild(ContentChild* aManager, nsIDOMBlob* aBlob);
+
+  // This constructor is called on the receiving side.
+  BlobChild(ContentChild* aManager, const ChildBlobConstructorParams& aParams);
+
+  // Only destroyed by ContentChild.
+  ~BlobChild();
+
+  // This create function is called on the receiving side by ContentChild.
+  static BlobChild*
+  Create(ContentChild* aManager, const ChildBlobConstructorParams& aParams);
+
+  static already_AddRefed<RemoteBlob>
+  CreateRemoteBlob(const ChildBlobConstructorParams& aParams);
+
+  void
+  NoteDyingRemoteBlob();
+
+  // These methods are only called by the IPDL message machinery.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+
+  virtual PBlobStreamChild*
+  AllocPBlobStreamChild() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBlobStreamConstructor(PBlobStreamChild* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBlobStreamChild(PBlobStreamChild* aActor) MOZ_OVERRIDE;
+};
+
+class BlobParent MOZ_FINAL
+  : public PBlobParent
+{
+  friend class ContentParent;
+
+  class OpenStreamRunnable;
+  friend class OpenStreamRunnable;
+
+  class RemoteBlob;
+  friend class RemoteBlob;
+
+  nsIDOMBlob* mBlob;
+  RemoteBlob* mRemoteBlob;
+  nsRefPtr<ContentParent> mStrongManager;
+
+  // nsIInputStreams backed by files must ensure that the files are actually
+  // opened and closed on a background thread before we can send their file
+  // handles across to the child. The child process could crash during this
+  // process so we need to make sure we cancel the intended response in such a
+  // case. We do that by holding an array of nsRevocableEventPtr. If the child
+  // crashes then this actor will be destroyed and the nsRevocableEventPtr
+  // destructor will cancel any stream events that are currently in flight.
+  nsTArray<nsRevocableEventPtr<OpenStreamRunnable>> mOpenStreamRunnables;
+
+  bool mOwnsBlob;
+  bool mBlobIsFile;
+
+public:
+  // This create function is called on the sending side.
+  static BlobParent*
+  Create(ContentParent* aManager, nsIDOMBlob* aBlob)
+  {
+    return new BlobParent(aManager, aBlob);
+  }
 
   // Get the blob associated with this actor. This may always be called on the
   // sending side. It may also be called on the receiving side unless this is a
   // "mystery" blob that has not yet received a SetMysteryBlobInfo() call.
   already_AddRefed<nsIDOMBlob>
   GetBlob();
 
   // Use this for files.
   bool
   SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType,
                      uint64_t aLength, uint64_t aLastModifiedDate);
 
   // Use this for non-file blobs.
   bool
   SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
 
-  ContentManager* Manager()
-  {
-    return mManager;
-  }
-
 private:
   // This constructor is called on the sending side.
-  Blob(ContentManager* aManager, nsIDOMBlob* aBlob);
+  BlobParent(ContentParent* aManager, nsIDOMBlob* aBlob);
 
   // This constructor is called on the receiving side.
-  Blob(ContentManager* aManager, const ConstructorParamsType& aParams);
+  BlobParent(ContentParent* aManager,
+             const ParentBlobConstructorParams& aParams);
+
+  ~BlobParent();
 
-  static already_AddRefed<RemoteBlobType>
-  CreateRemoteBlob(const ConstructorParamsType& aParams);
+  // This create function is called on the receiving side by ContentParent.
+  static BlobParent*
+  Create(ContentParent* aManager, const ParentBlobConstructorParams& aParams);
+
+  static already_AddRefed<RemoteBlob>
+  CreateRemoteBlob(const ParentBlobConstructorParams& aParams);
 
   void
   NoteDyingRemoteBlob();
 
+  void
+  NoteRunnableCompleted(OpenStreamRunnable* aRunnable);
+
   // These methods are only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
-  virtual bool
-  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+  virtual PBlobStreamParent*
+  AllocPBlobStreamParent() MOZ_OVERRIDE;
 
   virtual bool
-  RecvPBlobStreamConstructor(StreamType* aActor) MOZ_OVERRIDE;
-
-  nsRefPtr<ContentManager> mManager;
-};
+  RecvPBlobStreamConstructor(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
-} // namespace ipc
+  virtual bool
+  DeallocPBlobStreamParent(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
-typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Child> BlobChild;
-typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Parent> BlobParent;
+  virtual bool
+  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+};
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_Blob_h