Bug 999742, Split Blob<> into BlobParent/BlobChild. rs=sicking.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 23 Apr 2014 21:43:57 -0700
changeset 180242 6ea4ce828d069fa99f3e1526f40eaeb193de17a3
parent 180241 0aab0a3738cf076dafcbc31fd95b0d6f9c8ff1e0
child 180243 3be5a3af4e6a4dbfede7f25b588949933c59a288
child 180279 6965a913e858b6feb2944649940a646f40eb4037
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewerssicking
bugs999742
milestone31.0a1
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