Bug 759427 - 'Multiprocess blob support for MessageManager and IndexedDB'. r=smaug+janv+khuey (rs=sicking for nsBlobURI changes).
authorBen Turner <bent.mozilla@gmail.com>
Wed, 01 Aug 2012 23:02:29 -0700
changeset 101204 4048c64af6de6c36e592300e9b44ba500635559f
parent 101203 b83188166929f88004baddef6420b6a5f7c535dd
child 101205 482dd6331e42acb5088f36f432ba92690398a98a
push id23220
push useremorley@mozilla.com
push dateThu, 02 Aug 2012 13:19:38 +0000
treeherdermozilla-central@074fb996dfd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, sicking
bugs759427
milestone17.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 759427 - 'Multiprocess blob support for MessageManager and IndexedDB'. r=smaug+janv+khuey (rs=sicking for nsBlobURI changes).
content/base/public/nsIFrameMessageManager.idl
content/base/src/Makefile.in
content/base/src/nsBlobProtocolHandler.cpp
content/base/src/nsBlobURI.cpp
content/base/src/nsBlobURI.h
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/src/nsInProcessTabChildGlobal.cpp
content/base/test/Makefile.in
content/base/test/test_ipc_messagemanager_blob.html
dom/base/nsGlobalWindow.cpp
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBIndex.h
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/IndexedDatabaseInlines.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/OpenDatabaseHelper.cpp
dom/indexedDB/OpenDatabaseHelper.h
dom/indexedDB/ipc/IndexedDBChild.cpp
dom/indexedDB/ipc/IndexedDBParent.cpp
dom/indexedDB/ipc/IndexedDBParent.h
dom/indexedDB/ipc/Makefile.in
dom/indexedDB/ipc/PIndexedDBIndex.ipdl
dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
dom/indexedDB/ipc/PIndexedDBRequest.ipdl
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/test_blob_simple.html
dom/ipc/Blob.cpp
dom/ipc/Blob.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/Makefile.in
dom/ipc/PBlob.ipdl
dom/ipc/PBlobStream.ipdl
dom/ipc/PBrowser.ipdl
dom/ipc/PContent.ipdl
dom/ipc/StructuredCloneUtils.cpp
dom/ipc/StructuredCloneUtils.h
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/ipc/ipdl.mk
dom/ipc/nsIRemoteBlob.h
ipc/chromium/src/base/pickle.cc
ipc/chromium/src/base/pickle.h
ipc/glue/IPCMessageUtils.h
layout/build/nsLayoutModule.cpp
--- a/content/base/public/nsIFrameMessageManager.idl
+++ b/content/base/public/nsIFrameMessageManager.idl
@@ -12,25 +12,29 @@ interface nsIContent;
 [scriptable, function, uuid(938fcb95-3d63-46be-aa72-94d08fd3b418)]
 interface nsIFrameMessageListener : nsISupports
 {
   /**
    * This is for JS only.
    * receiveMessage is called with one parameter, which has the following
    * properties:
    *   {
+   *     target:  %the target of the message. Either an element owning
+   *               the message manager, or message manager itself if no
+   *               element owns it%
    *     name:    %message name%,
    *     sync:    %true or false%.
-   *     json:    %json object or null%,
+   *     json:    %structured clone of the sent message data%,
+   *     json:    %same as .data, deprecated%,
    *     objects: %array of handles or null, always null if sync is false%
    *   }
    * @note objects property isn't implemented yet.
    *
    * if the message is synchronous, possible return value is sent back
-   * as JSON.
+   * as JSON (will be changed to use structured clones).
    *
    * When the listener is called, 'this' value is the target of the message.
    */
   void receiveMessage();
 };
 
 [scriptable, builtinclass, uuid(9be42627-a5db-456f-8de2-9097da45a8c3)]
 interface nsIFrameMessageManager : nsISupports
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -123,16 +123,17 @@ CPPSRCS		= \
 		nsWebSocket.cpp \
 		nsXHTMLContentSerializer.cpp \
 		nsXMLContentSerializer.cpp \
 		nsXMLHttpRequest.cpp \
 		nsXMLNameSpaceMap.cpp \
 		FragmentOrElement.cpp \
 		Link.cpp \
 		nsBlobProtocolHandler.cpp \
+		nsBlobURI.cpp \
 		nsFrameMessageManager.cpp \
 		nsInProcessTabChildGlobal.cpp \
 		ThirdPartyUtil.cpp \
 		nsEventSource.cpp \
 		FileIOObject.cpp \
 		nsDOMMutationObserver.cpp \
 		$(NULL)
 
--- a/content/base/src/nsBlobProtocolHandler.cpp
+++ b/content/base/src/nsBlobProtocolHandler.cpp
@@ -1,26 +1,19 @@
 /* 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 "nsBlobProtocolHandler.h"
-#include "nsSimpleURI.h"
+#include "nsBlobURI.h"
 #include "nsDOMError.h"
-#include "nsCOMPtr.h"
 #include "nsClassHashtable.h"
 #include "nsNetUtil.h"
-#include "nsIURIWithPrincipal.h"
 #include "nsIPrincipal.h"
 #include "nsIDOMFile.h"
-#include "nsISerializable.h"
-#include "nsIClassInfo.h"
-#include "nsIObjectInputStream.h"
-#include "nsIObjectOutputStream.h"
-#include "nsIProgrammingLanguage.h"
 
 // -----------------------------------------------------------------------
 // Hash table
 struct FileDataInfo
 {
   nsCOMPtr<nsIDOMBlob> mFile;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
@@ -85,243 +78,16 @@ GetFileDataInfo(const nsACString& aUri)
   }
   
   FileDataInfo* res;
   gFileDataTable->Get(aUri, &res);
   return res;
 }
 
 // -----------------------------------------------------------------------
-// Uri
-
-#define NS_BLOBURI_CID \
-{ 0xf5475c51, 0x59a7, 0x4757, \
-  { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
-
-static NS_DEFINE_CID(kBLOBURICID, NS_BLOBURI_CID);
-
-class nsBlobURI : public nsSimpleURI,
-                      public nsIURIWithPrincipal
-{
-public:
-  nsBlobURI(nsIPrincipal* aPrincipal) :
-      nsSimpleURI(), mPrincipal(aPrincipal)
-  {}
-  virtual ~nsBlobURI() {}
-
-  // For use only from deserialization
-  nsBlobURI() : nsSimpleURI() {}
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIURIWITHPRINCIPAL
-  NS_DECL_NSISERIALIZABLE
-  NS_DECL_NSICLASSINFO
-
-  // Override CloneInternal() and EqualsInternal()
-  virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
-                                 nsIURI** aClone);
-  virtual nsresult EqualsInternal(nsIURI* aOther,
-                                  RefHandlingEnum aRefHandlingMode,
-                                  bool* aResult);
-
-  // Override StartClone to hand back a nsBlobURI
-  virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */)
-  { return new nsBlobURI(); }
-
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-};
-
-static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
-                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
-
-NS_IMPL_ADDREF_INHERITED(nsBlobURI, nsSimpleURI)
-NS_IMPL_RELEASE_INHERITED(nsBlobURI, nsSimpleURI)
-
-NS_INTERFACE_MAP_BEGIN(nsBlobURI)
-  NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
-  if (aIID.Equals(kBLOBURICID))
-    foundInterface = static_cast<nsIURI*>(this);
-  else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
-    // Need to return explicitly here, because if we just set foundInterface
-    // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
-    // nsSimplURI::QueryInterface and finding something for this CID.
-    *aInstancePtr = nullptr;
-    return NS_NOINTERFACE;
-  }
-  else
-NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
-
-// nsIURIWithPrincipal methods:
-
-NS_IMETHODIMP
-nsBlobURI::GetPrincipal(nsIPrincipal** aPrincipal)
-{
-  NS_IF_ADDREF(*aPrincipal = mPrincipal);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsBlobURI::GetPrincipalUri(nsIURI** aUri)
-{
-  if (mPrincipal) {
-    mPrincipal->GetURI(aUri);
-  }
-  else {
-    *aUri = nullptr;
-  }
-
-  return NS_OK;
-}
-
-// nsISerializable methods:
-
-NS_IMETHODIMP
-nsBlobURI::Read(nsIObjectInputStream* aStream)
-{
-  nsresult rv = nsSimpleURI::Read(aStream);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_ReadOptionalObject(aStream, true, getter_AddRefs(mPrincipal));
-}
-
-NS_IMETHODIMP
-nsBlobURI::Write(nsIObjectOutputStream* aStream)
-{
-  nsresult rv = nsSimpleURI::Write(aStream);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
-                                        NS_GET_IID(nsIPrincipal),
-                                        true);
-}
-
-// nsIURI methods:
-nsresult
-nsBlobURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
-                             nsIURI** aClone)
-{
-  nsCOMPtr<nsIURI> simpleClone;
-  nsresult rv =
-    nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef DEBUG
-  nsRefPtr<nsBlobURI> uriCheck;
-  rv = simpleClone->QueryInterface(kBLOBURICID, getter_AddRefs(uriCheck));
-  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && uriCheck,
-		    "Unexpected!");
-#endif
-
-  nsBlobURI* blobURI = static_cast<nsBlobURI*>(simpleClone.get());
-
-  blobURI->mPrincipal = mPrincipal;
-
-  simpleClone.forget(aClone);
-  return NS_OK;
-}
-
-/* virtual */ nsresult
-nsBlobURI::EqualsInternal(nsIURI* aOther,
-                              nsSimpleURI::RefHandlingEnum aRefHandlingMode,
-                              bool* aResult)
-{
-  if (!aOther) {
-    *aResult = false;
-    return NS_OK;
-  }
-  
-  nsRefPtr<nsBlobURI> otherBlobUri;
-  aOther->QueryInterface(kBLOBURICID, getter_AddRefs(otherBlobUri));
-  if (!otherBlobUri) {
-    *aResult = false;
-    return NS_OK;
-  }
-
-  // Compare the member data that our base class knows about.
-  if (!nsSimpleURI::EqualsInternal(otherBlobUri, aRefHandlingMode)) {
-    *aResult = false;
-    return NS_OK;
-   }
-
-  // Compare the piece of additional member data that we add to base class.
-  if (mPrincipal && otherBlobUri->mPrincipal) {
-    // Both of us have mPrincipals. Compare them.
-    return mPrincipal->Equals(otherBlobUri->mPrincipal, aResult);
-  }
-  // else, at least one of us lacks a principal; only equal if *both* lack it.
-  *aResult = (!mPrincipal && !otherBlobUri->mPrincipal);
-  return NS_OK;
-}
-
-// nsIClassInfo methods:
-NS_IMETHODIMP 
-nsBlobURI::GetInterfaces(PRUint32 *count, nsIID * **array)
-{
-  *count = 0;
-  *array = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
-{
-  *_retval = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetContractID(char * *aContractID)
-{
-  // Make sure to modify any subclasses as needed if this ever
-  // changes.
-  *aContractID = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassDescription(char * *aClassDescription)
-{
-  *aClassDescription = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassID(nsCID * *aClassID)
-{
-  // Make sure to modify any subclasses as needed if this ever
-  // changes to not call the virtual GetClassIDNoAlloc.
-  *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
-  NS_ENSURE_TRUE(*aClassID, NS_ERROR_OUT_OF_MEMORY);
-
-  return GetClassIDNoAlloc(*aClassID);
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
-{
-  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetFlags(PRUint32 *aFlags)
-{
-  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
-{
-  *aClassIDNoAlloc = kBLOBURICID;
-  return NS_OK;
-}
-
-// -----------------------------------------------------------------------
 // Protocol handler
 
 NS_IMPL_ISUPPORTS1(nsBlobProtocolHandler, nsIProtocolHandler)
 
 NS_IMETHODIMP
 nsBlobProtocolHandler::GetScheme(nsACString &result)
 {
   result.AssignLiteral(BLOBURI_SCHEME);
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsBlobURI.cpp
@@ -0,0 +1,199 @@
+/* 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 "nsBlobURI.h"
+
+#include "nsAutoPtr.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIProgrammingLanguage.h"
+
+static NS_DEFINE_CID(kBLOBURICID, NS_BLOBURI_CID);
+
+static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
+                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
+
+NS_IMPL_ADDREF_INHERITED(nsBlobURI, nsSimpleURI)
+NS_IMPL_RELEASE_INHERITED(nsBlobURI, nsSimpleURI)
+
+NS_INTERFACE_MAP_BEGIN(nsBlobURI)
+  NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
+  if (aIID.Equals(kBLOBURICID))
+    foundInterface = static_cast<nsIURI*>(this);
+  else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
+    // Need to return explicitly here, because if we just set foundInterface
+    // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
+    // nsSimplURI::QueryInterface and finding something for this CID.
+    *aInstancePtr = nullptr;
+    return NS_NOINTERFACE;
+  }
+  else
+NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
+
+// nsIURIWithPrincipal methods:
+
+NS_IMETHODIMP
+nsBlobURI::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  NS_IF_ADDREF(*aPrincipal = mPrincipal);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBlobURI::GetPrincipalUri(nsIURI** aUri)
+{
+  if (mPrincipal) {
+    mPrincipal->GetURI(aUri);
+  }
+  else {
+    *aUri = nullptr;
+  }
+
+  return NS_OK;
+}
+
+// nsISerializable methods:
+
+NS_IMETHODIMP
+nsBlobURI::Read(nsIObjectInputStream* aStream)
+{
+  nsresult rv = nsSimpleURI::Read(aStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_ReadOptionalObject(aStream, true, getter_AddRefs(mPrincipal));
+}
+
+NS_IMETHODIMP
+nsBlobURI::Write(nsIObjectOutputStream* aStream)
+{
+  nsresult rv = nsSimpleURI::Write(aStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
+                                        NS_GET_IID(nsIPrincipal),
+                                        true);
+}
+
+// nsIURI methods:
+nsresult
+nsBlobURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                             nsIURI** aClone)
+{
+  nsCOMPtr<nsIURI> simpleClone;
+  nsresult rv =
+    nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef DEBUG
+  nsRefPtr<nsBlobURI> uriCheck;
+  rv = simpleClone->QueryInterface(kBLOBURICID, getter_AddRefs(uriCheck));
+  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && uriCheck,
+		    "Unexpected!");
+#endif
+
+  nsBlobURI* blobURI = static_cast<nsBlobURI*>(simpleClone.get());
+
+  blobURI->mPrincipal = mPrincipal;
+
+  simpleClone.forget(aClone);
+  return NS_OK;
+}
+
+/* virtual */ nsresult
+nsBlobURI::EqualsInternal(nsIURI* aOther,
+                              nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                              bool* aResult)
+{
+  if (!aOther) {
+    *aResult = false;
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsBlobURI> otherBlobUri;
+  aOther->QueryInterface(kBLOBURICID, getter_AddRefs(otherBlobUri));
+  if (!otherBlobUri) {
+    *aResult = false;
+    return NS_OK;
+  }
+
+  // Compare the member data that our base class knows about.
+  if (!nsSimpleURI::EqualsInternal(otherBlobUri, aRefHandlingMode)) {
+    *aResult = false;
+    return NS_OK;
+   }
+
+  // Compare the piece of additional member data that we add to base class.
+  if (mPrincipal && otherBlobUri->mPrincipal) {
+    // Both of us have mPrincipals. Compare them.
+    return mPrincipal->Equals(otherBlobUri->mPrincipal, aResult);
+  }
+  // else, at least one of us lacks a principal; only equal if *both* lack it.
+  *aResult = (!mPrincipal && !otherBlobUri->mPrincipal);
+  return NS_OK;
+}
+
+// nsIClassInfo methods:
+NS_IMETHODIMP 
+nsBlobURI::GetInterfaces(PRUint32 *count, nsIID * **array)
+{
+  *count = 0;
+  *array = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
+{
+  *_retval = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetContractID(char * *aContractID)
+{
+  // Make sure to modify any subclasses as needed if this ever
+  // changes.
+  *aContractID = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassDescription(char * *aClassDescription)
+{
+  *aClassDescription = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassID(nsCID * *aClassID)
+{
+  // Make sure to modify any subclasses as needed if this ever
+  // changes to not call the virtual GetClassIDNoAlloc.
+  *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
+  NS_ENSURE_TRUE(*aClassID, NS_ERROR_OUT_OF_MEMORY);
+
+  return GetClassIDNoAlloc(*aClassID);
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
+{
+  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetFlags(PRUint32 *aFlags)
+{
+  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
+{
+  *aClassIDNoAlloc = kBLOBURICID;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsBlobURI.h
@@ -0,0 +1,50 @@
+/* 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 nsBlobURI_h
+#define nsBlobURI_h
+
+#include "nsCOMPtr.h"
+#include "nsIClassInfo.h"
+#include "nsIPrincipal.h"
+#include "nsISerializable.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsSimpleURI.h"
+
+class nsBlobURI : public nsSimpleURI,
+                  public nsIURIWithPrincipal
+{
+public:
+  nsBlobURI(nsIPrincipal* aPrincipal) :
+      nsSimpleURI(), mPrincipal(aPrincipal)
+  {}
+  virtual ~nsBlobURI() {}
+
+  // For use only from deserialization
+  nsBlobURI() : nsSimpleURI() {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIURIWITHPRINCIPAL
+  NS_DECL_NSISERIALIZABLE
+  NS_DECL_NSICLASSINFO
+
+  // Override CloneInternal() and EqualsInternal()
+  virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+                                 nsIURI** aClone);
+  virtual nsresult EqualsInternal(nsIURI* aOther,
+                                  RefHandlingEnum aRefHandlingMode,
+                                  bool* aResult);
+
+  // Override StartClone to hand back a nsBlobURI
+  virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */)
+  { return new nsBlobURI(); }
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+#define NS_BLOBURI_CID \
+{ 0xf5475c51, 0x59a7, 0x4757, \
+  { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
+
+#endif /* nsBlobURI_h */
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -17,16 +17,17 @@
 #include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMMozBrowserFrame.h"
 #include "nsIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOMFile.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebProgress.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellLoadInfo.h"
@@ -78,16 +79,17 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
 
 #include "jsapi.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
 class nsAsyncDocShellDestroyer : public nsRunnable
@@ -2151,48 +2153,88 @@ bool LoadScript(void* aCallbackData, con
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsRunnable
 {
 public:
   nsAsyncMessageToChild(nsFrameLoader* aFrameLoader,
-                        const nsAString& aMessage, const nsAString& aJSON)
-    : mFrameLoader(aFrameLoader), mMessage(aMessage), mJSON(aJSON) {}
+                              const nsAString& aMessage,
+                              const StructuredCloneData& aData)
+    : mFrameLoader(aFrameLoader), mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     nsInProcessTabChildGlobal* tabChild =
       static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
     if (tabChild && tabChild->GetInnerManager()) {
       nsFrameScriptCx cx(static_cast<nsIDOMEventTarget*>(tabChild), tabChild);
+
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> mm = tabChild->GetInnerManager();
       mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(tabChild), mMessage,
-                         false, mJSON, nullptr, nullptr);
+                         false, &data, nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsRefPtr<nsFrameLoader> mFrameLoader;
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToChild(void* aCallbackData,
                              const nsAString& aMessage,
-                             const nsAString& aJSON)
+                                   const StructuredCloneData& aData)
 {
-  mozilla::dom::PBrowserParent* tabParent =
+  PBrowserParent* tabParent =
     static_cast<nsFrameLoader*>(aCallbackData)->GetRemoteBrowser();
   if (tabParent) {
-    return tabParent->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
+
+      PRUint32 length = blobs.Length();
+      blobParents.SetCapacity(length);
+
+      ContentParent* cp = static_cast<ContentParent*>(tabParent->Manager());
+
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobParent) {
+          return false;
+        }
+
+        blobParents.AppendElement(blobParent);
+      }
+    }
+
+    return tabParent->SendAsyncMessage(nsString(aMessage), data);
   }
+
   nsRefPtr<nsIRunnable> ev =
     new nsAsyncMessageToChild(static_cast<nsFrameLoader*>(aCallbackData),
-                              aMessage, aJSON);
+                                    aMessage, aData);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetMessageManager(nsIChromeFrameMessageManager** aManager)
 {
   EnsureMessageManager();
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1,38 +1,44 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "base/basictypes.h"
+
+#include "nsFrameMessageManager.h"
+
 #include "ContentChild.h"
 #include "ContentParent.h"
-#include "nsFrameMessageManager.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSRuntimeService.h"
+#include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 static bool
 IsChromeProcess()
 {
   nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
   if (!rt)
     return true;
 
@@ -165,46 +171,68 @@ static JSBool
 JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData)
 {
   nsAString* result = static_cast<nsAString*>(aData);
   result->Append(static_cast<const PRUnichar*>(aBuf),
                  static_cast<PRUint32>(aLen));
   return true;
 }
 
-void
-nsFrameMessageManager::GetParamsForMessage(const jsval& aObject,
-                                           JSContext* aCx,
-                                           nsAString& aJSON)
+static bool
+GetParamsForMessage(JSContext* aCx,
+                    const jsval& aObject,
+                    JSAutoStructuredCloneBuffer& aBuffer,
+                    StructuredCloneClosure& aClosure)
 {
-  aJSON.Truncate();
+  if (WriteStructuredClone(aCx, aObject, aBuffer, aClosure)) {
+    return true;
+  }
+  JS_ClearPendingException(aCx);
+
+  // Not clonable, try JSON
+  //XXX This is ugly but currently structured cloning doesn't handle
+  //    properly cases when interface is implemented in JS and used
+  //    as a dictionary.
+  nsAutoString json;
   JSAutoRequest ar(aCx);
   jsval v = aObject;
-  JS_Stringify(aCx, &v, nullptr, JSVAL_NULL, JSONCreator, &aJSON);
+  NS_ENSURE_TRUE(JS_Stringify(aCx, &v, nullptr, JSVAL_NULL, JSONCreator, &json), false);
+  NS_ENSURE_TRUE(!json.IsEmpty(), false);
+
+  jsval val = JSVAL_NULL;
+  NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const jschar*>(PromiseFlatString(json).get()),
+                              json.Length(), &val), false);
+
+  return WriteStructuredClone(aCx, val, aBuffer, aClosure);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
                                        const jsval& aObject,
                                        JSContext* aCx,
                                        PRUint8 aArgc,
                                        jsval* aRetval)
 {
   NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
   NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
   NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
   *aRetval = JSVAL_VOID;
   if (mSyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-    nsString json;
-    if (aArgc >= 2) {
-      GetParamsForMessage(aObject, aCx, json);
+    StructuredCloneData data;
+    JSAutoStructuredCloneBuffer buffer;
+    if (aArgc >= 2 &&
+        !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
+      return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
+    data.mData = buffer.data();
+    data.mDataLength = buffer.nbytes();
+
     InfallibleTArray<nsString> retval;
-    if (mSyncCallback(mCallbackData, aMessageName, json, &retval)) {
+    if (mSyncCallback(mCallbackData, aMessageName, data, &retval)) {
       JSAutoRequest ar(aCx);
       PRUint32 len = retval.Length();
       JSObject* dataArray = JS_NewArrayObject(aCx, len, NULL);
       NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
 
       for (PRUint32 i = 0; i < len; ++i) {
         if (retval[i].IsEmpty()) {
           continue;
@@ -221,41 +249,48 @@ nsFrameMessageManager::SendSyncMessage(c
       *aRetval = OBJECT_TO_JSVAL(dataArray);
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::SendAsyncMessageInternal(const nsAString& aMessage,
-                                                const nsAString& aJSON)
+                                                const StructuredCloneData& aData)
 {
   if (mAsyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-    mAsyncCallback(mCallbackData, aMessage, aJSON);
+    mAsyncCallback(mCallbackData, aMessage, aData);
   }
   PRInt32 len = mChildManagers.Count();
   for (PRInt32 i = 0; i < len; ++i) {
     static_cast<nsFrameMessageManager*>(mChildManagers[i])->
-      SendAsyncMessageInternal(aMessage, aJSON);
+      SendAsyncMessageInternal(aMessage, aData);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
                                         const jsval& aObject,
                                         JSContext* aCx,
                                         PRUint8 aArgc)
 {
-  nsString json;
-  if (aArgc >= 2) {
-    GetParamsForMessage(aObject, aCx, json);
+  StructuredCloneData data;
+  JSAutoStructuredCloneBuffer buffer;
+
+  if (aArgc >= 2 &&
+      !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
-  return SendAsyncMessageInternal(aMessageName, json);
+
+  data.mData = buffer.data();
+  data.mDataLength = buffer.nbytes();
+
+  return SendAsyncMessageInternal(aMessageName, data);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
 #endif
@@ -337,17 +372,18 @@ public:
 
   bool mWasHandlingMessage;
   nsRefPtr<nsFrameMessageManager> mMM;
 };
 
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
-                                      bool aSync, const nsAString& aJSON,
+                                      bool aSync,
+                                      const StructuredCloneData* aCloneData,
                                       JSObject* aObjectsArray,
                                       InfallibleTArray<nsString>* aJSONRetVal,
                                       JSContext* aContext)
 {
   JSContext* ctx = mContext ? mContext : aContext;
   if (!ctx) {
     ctx = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext();
   }
@@ -397,33 +433,33 @@ nsFrameMessageManager::ReceiveMessage(ns
         }
 
         JS::AutoValueRooter objectsv(ctx);
         objectsv.set(OBJECT_TO_JSVAL(aObjectsArray));
         if (!JS_WrapValue(ctx, objectsv.jsval_addr()))
             return NS_ERROR_UNEXPECTED;
 
         jsval json = JSVAL_NULL;
-        if (!aJSON.IsEmpty()) {
-          if (!JS_ParseJSON(ctx, static_cast<const jschar*>(PromiseFlatString(aJSON).get()),
-                            aJSON.Length(), &json)) {
-            json = JSVAL_NULL;
-          }
+        if (aCloneData && aCloneData->mDataLength &&
+            !ReadStructuredClone(ctx, *aCloneData, &json)) {
+          JS_ClearPendingException(ctx);
+          return NS_OK;
         }
         JSString* jsMessage =
           JS_NewUCStringCopyN(ctx,
                               static_cast<const jschar*>(PromiseFlatString(aMessage).get()),
                               aMessage.Length());
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
         JS_DefineProperty(ctx, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "name",
                           STRING_TO_JSVAL(jsMessage), NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "sync",
                           BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE); // deprecated
+        JS_DefineProperty(ctx, param, "data", json, NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "objects", objectsv.jsval_value(), NULL, NULL, JSPROP_ENUMERATE);
 
         jsval thisValue = JSVAL_VOID;
 
         JS::Value funval;
         if (JS_ObjectIsCallable(ctx, object)) {
           // If the listener is a JS function:
           funval.setObject(*object);
@@ -474,17 +510,18 @@ nsFrameMessageManager::ReceiveMessage(ns
             }
           }
         }
       }
     }
   }
   nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
-                                                         aSync, aJSON, aObjectsArray,
+                                                         aSync, aCloneData,
+                                                         aObjectsArray,
                                                          aJSONRetVal, mContext) : NS_OK;
 }
 
 void
 nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager,
                                        bool aLoadScripts)
 {
   mChildManagers.AppendObject(aManager);
@@ -883,135 +920,214 @@ NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner,
 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr;
 
 bool SendAsyncMessageToChildProcess(void* aCallbackData,
                                     const nsAString& aMessage,
-                                    const nsAString& aJSON)
+                                    const StructuredCloneData& aData)
 {
   mozilla::dom::ContentParent* cp =
     static_cast<mozilla::dom::ContentParent*>(aCallbackData);
   NS_WARN_IF_FALSE(cp, "No child process!");
   if (cp) {
-    return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
+      PRUint32 length = blobs.Length();
+      blobParents.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobParent) {
+          return false;
+  }
+        blobParents.AppendElement(blobParent);
+      }
+    }
+
+    return cp->SendAsyncMessage(nsString(aMessage), data);
   }
   return true;
 }
 
 class nsAsyncMessageToSameProcessChild : public nsRunnable
 {
 public:
-  nsAsyncMessageToSameProcessChild(const nsAString& aMessage, const nsAString& aJSON)
-    : mMessage(aMessage), mJSON(aJSON) {}
+  nsAsyncMessageToSameProcessChild(const nsAString& aMessage,
+                                   const StructuredCloneData& aData)
+    : mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     if (nsFrameMessageManager::sChildProcessManager) {
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sChildProcessManager;
       ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage,
-                          false, mJSON, nullptr, nullptr);
+                          false, &data, nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToSameProcessChild(void* aCallbackData,
                                         const nsAString& aMessage,
-                                        const nsAString& aJSON)
+                                        const StructuredCloneData& aData)
 {
   nsRefPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessChild(aMessage, aJSON);
+    new nsAsyncMessageToSameProcessChild(aMessage, aData);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 bool SendSyncMessageToParentProcess(void* aCallbackData,
                                     const nsAString& aMessage,
-                                    const nsAString& aJSON,
+                                    const StructuredCloneData& aData,
                                     InfallibleTArray<nsString>* aJSONRetVal)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+      PRUint32 length = blobs.Length();
+      blobChildList.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobChild) {
+          return false;
+        }
+        blobChildList.AppendElement(blobChild);
+      }
+    }
     return
-      cc->SendSyncMessage(nsString(aMessage), nsString(aJSON), aJSONRetVal);
+      cc->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
   }
   return true;
 }
 
 bool SendSyncMessageToSameProcessParent(void* aCallbackData,
                                         const nsAString& aMessage,
-                                        const nsAString& aJSON,
+                                        const StructuredCloneData& aData,
                                         InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
     asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages);
     PRUint32 len = asyncMessages.Length();
     for (PRUint32 i = 0; i < len; ++i) {
       nsCOMPtr<nsIRunnable> async = asyncMessages[i];
       async->Run();
     }
   }
   if (nsFrameMessageManager::sSameProcessParentManager) {
     nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
-                        true, aJSON, nullptr, aJSONRetVal);
+                        true, &aData, nullptr, aJSONRetVal);
   }
   return true;
 }
 
 bool SendAsyncMessageToParentProcess(void* aCallbackData,
                                      const nsAString& aMessage,
-                                     const nsAString& aJSON)
+                                          const StructuredCloneData& aData)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
-    return cc->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+      PRUint32 length = blobs.Length();
+      blobChildList.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobChild) {
+          return false;
+        }
+        blobChildList.AppendElement(blobChild);
+      }
+    }
+    return cc->SendAsyncMessage(nsString(aMessage), data);
   }
   return true;
 }
 
 class nsAsyncMessageToSameProcessParent : public nsRunnable
 {
 public:
-  nsAsyncMessageToSameProcessParent(const nsAString& aMessage, const nsAString& aJSON)
-    : mMessage(aMessage), mJSON(aJSON) {}
+  nsAsyncMessageToSameProcessParent(const nsAString& aMessage,
+                                         const StructuredCloneData& aData)
+    : mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
       nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this);
     }
     if (nsFrameMessageManager::sSameProcessParentManager) {
-      nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
-      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage, false,
-                          mJSON, nullptr, nullptr);
-    }
-    return NS_OK;
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
+      nsRefPtr<nsFrameMessageManager> ppm =
+        nsFrameMessageManager::sSameProcessParentManager;
+      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
+                          mMessage, false, &data, nullptr, nullptr, nullptr);
+     }
+     return NS_OK;
   }
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToSameProcessParent(void* aCallbackData,
-                                         const nsAString& aMessage,
-                                         const nsAString& aJSON)
+                                              const nsAString& aMessage,
+                                              const StructuredCloneData& aData)
 {
   if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
     nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
   }
   nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessParent(aMessage, aJSON);
+    new nsAsyncMessageToSameProcessParent(aMessage, aData);
   nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIFrameMessageManager** aResult)
@@ -1084,9 +1200,9 @@ NS_NewChildProcessMessageManager(nsISync
 bool
 nsFrameMessageManager::MarkForCC()
 {
   PRUint32 len = mListeners.Length();
   for (PRUint32 i = 0; i < len; ++i) {
     xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
   }
   return true;
-}
+}
\ No newline at end of file
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -20,55 +20,58 @@
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
+struct StructuredCloneData;
 }
 }
 
 class nsAXPCNativeCallContext;
 struct JSContext;
 struct JSObject;
 
 struct nsMessageListenerInfo
 {
   nsCOMPtr<nsIFrameMessageListener> mListener;
   nsCOMPtr<nsIAtom> mMessage;
 };
 
 typedef bool (*nsLoadScriptCallback)(void* aCallbackData, const nsAString& aURL);
 typedef bool (*nsSyncMessageCallback)(void* aCallbackData,
                                       const nsAString& aMessage,
-                                      const nsAString& aJSON,
+                                      const mozilla::dom::StructuredCloneData& aData,
                                       InfallibleTArray<nsString>* aJSONRetVal);
 typedef bool (*nsAsyncMessageCallback)(void* aCallbackData,
                                        const nsAString& aMessage,
-                                       const nsAString& aJSON);
+                                             const mozilla::dom::StructuredCloneData& aData);
 
 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager,
                                         public nsIChromeFrameMessageManager
 {
+  typedef mozilla::dom::StructuredCloneData StructuredCloneData;
 public:
   nsFrameMessageManager(bool aChrome,
                         nsSyncMessageCallback aSyncCallback,
                         nsAsyncMessageCallback aAsyncCallback,
                         nsLoadScriptCallback aLoadScriptCallback,
                         void* aCallbackData,
                         nsFrameMessageManager* aParentManager,
                         JSContext* aContext,
                         bool aGlobal = false,
                         bool aProcessManager = false)
   : mChrome(aChrome), mGlobal(aGlobal), mIsProcessManager(aProcessManager),
     mHandlingMessage(false), mDisconnected(false), mParentManager(aParentManager),
     mSyncCallback(aSyncCallback), mAsyncCallback(aAsyncCallback),
-    mLoadScriptCallback(aLoadScriptCallback), mCallbackData(aCallbackData),
+    mLoadScriptCallback(aLoadScriptCallback),
+    mCallbackData(aCallbackData),
     mContext(aContext)
   {
     NS_ASSERTION(mContext || (aChrome && !aParentManager) || aProcessManager,
                  "Should have mContext in non-global/non-process manager!");
     NS_ASSERTION(aChrome || !aParentManager, "Should not set parent manager!");
     // This is a bit hackish. When parent manager is global, we want
     // to attach the window message manager to it immediately.
     // Is it just the frame message manager which waits until the
@@ -107,35 +110,33 @@ public:
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSICHROMEFRAMEMESSAGEMANAGER
   NS_DECL_NSITREEITEMFRAMEMESSAGEMANAGER
 
   static nsFrameMessageManager*
   NewProcessMessageManager(mozilla::dom::ContentParent* aProcess);
 
   nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
-                          bool aSync, const nsAString& aJSON,
+                          bool aSync, const StructuredCloneData* aCloneData,
                           JSObject* aObjectsArray,
                           InfallibleTArray<nsString>* aJSONRetVal,
                           JSContext* aContext = nullptr);
+
   void AddChildManager(nsFrameMessageManager* aManager,
                        bool aLoadScripts = true);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
 
   void Disconnect(bool aRemoveFromParent = true);
   void SetCallbackData(void* aData, bool aLoadScripts = true);
   void* GetCallbackData() { return mCallbackData; }
-  void GetParamsForMessage(const jsval& aObject,
-                           JSContext* aCx,
-                           nsAString& aJSON);
   nsresult SendAsyncMessageInternal(const nsAString& aMessage,
-                                    const nsAString& aJSON);
+                                          const StructuredCloneData& aData);
   JSContext* GetJSContext() { return mContext; }
   void SetJSContext(JSContext* aCx) { mContext = aCx; }
   void RemoveFromParent();
   nsFrameMessageManager* GetParentManager() { return mParentManager; }
   void SetParentManager(nsFrameMessageManager* aParent)
   {
     NS_ASSERTION(!mParentManager, "We have parent manager already!");
     NS_ASSERTION(mChrome, "Should not set parent manager!");
@@ -252,9 +253,9 @@ class nsScriptCacheCleaner MOZ_FINAL : p
                         const char *aTopic,
                         const PRUnichar *aData)
   {
     nsFrameScriptExecutor::Shutdown();
     return NS_OK;
   }
 };
 
-#endif
+#endif
\ No newline at end of file
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -15,69 +15,86 @@
 #include "nsComponentManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsDOMClassInfoID.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
+
+using mozilla::dom::StructuredCloneData;
+using mozilla::dom::StructuredCloneClosure;
 
 bool SendSyncMessageToParent(void* aCallbackData,
                              const nsAString& aMessage,
-                             const nsAString& aJSON,
+                             const StructuredCloneData& aData,
                              InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIContent> owner = tabChild->mOwner;
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   asyncMessages.SwapElements(tabChild->mASyncMessages);
   PRUint32 len = asyncMessages.Length();
   for (PRUint32 i = 0; i < len; ++i) {
     nsCOMPtr<nsIRunnable> async = asyncMessages[i];
     async->Run();
   }
   if (tabChild->mChromeMessageManager) {
     nsRefPtr<nsFrameMessageManager> mm = tabChild->mChromeMessageManager;
-    mm->ReceiveMessage(owner, aMessage, true, aJSON, nullptr, aJSONRetVal);
+    mm->ReceiveMessage(owner, aMessage, true, &aData, nullptr, aJSONRetVal);
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsRunnable
 {
 public:
   nsAsyncMessageToParent(nsInProcessTabChildGlobal* aTabChild,
-                         const nsAString& aMessage, const nsAString& aJSON)
-    : mTabChild(aTabChild), mMessage(aMessage), mJSON(aJSON) {}
+                         const nsAString& aMessage,
+                         const StructuredCloneData& aData)
+    : mTabChild(aTabChild), mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     mTabChild->mASyncMessages.RemoveElement(this);
     if (mTabChild->mChromeMessageManager) {
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> mm = mTabChild->mChromeMessageManager;
-      mm->ReceiveMessage(mTabChild->mOwner, mMessage, false,
-                         mJSON, nullptr, nullptr);
+      mm->ReceiveMessage(mTabChild->mOwner, mMessage, false, &data,
+                         nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToParent(void* aCallbackData,
                               const nsAString& aMessage,
-                              const nsAString& aJSON)
+                              const StructuredCloneData& aData)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToParent(tabChild, aMessage, aJSON);
+    new nsAsyncMessageToParent(tabChild, aMessage, aData);
   tabChild->mASyncMessages.AppendElement(ev);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -547,16 +547,17 @@ MOCHITEST_FILES_B = \
 		test_bug749367.html \
 		test_bug753278.html \
 		test_bug761120.html \
 		test_XHR_onuploadprogress.html \
 		test_XHR_anon.html \
 		file_XHR_anon.sjs \
 		test_XHR_system.html \
 		test_XHR_parameters.html \
+		test_ipc_messagemanager_blob.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES =	\
 		test_bug357450.js \
 		$(NULL)
 
 MOCHITEST_FILES_PARTS = $(foreach s,A B,MOCHITEST_FILES_$(s))
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for OOP Blobs in MessageManager</title>
+  <script type="application/javascript"
+          src="/tests/SimpleTest/SimpleTest.js">
+  </script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    const childFrameURL =
+      "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+    function childFrameScript() {
+      "use strict";
+
+      addMessageListener("test:ipcClonedMessage", function(message) {
+        if (!(message.json instanceof Components.interfaces.nsIDOMBlob)) {
+          sendAsyncMessage(message.name, message.json);
+          return;
+        }
+
+        let reader =
+          Components.classes["@mozilla.org/files/filereader;1"]
+                    .createInstance(Components.interfaces.nsIDOMFileReader);
+        reader.addEventListener("load", function() {
+          let response = reader.result == "this is a great success!" ?
+                         message.json :
+                         "error";
+          sendAsyncMessage(message.name, response);
+        });
+        reader.readAsText(message.json);
+      });
+    }
+
+    function runTests() {
+      ok("Browser prefs set.");
+
+      let iframe = document.createElement("iframe");
+      iframe.mozbrowser = true;
+      iframe.id = "iframe";
+      iframe.src = childFrameURL;
+
+      iframe.addEventListener("mozbrowserloadend", function() {
+        ok(true, "Got iframe load event.");
+
+        const messages = [
+          "hi!",
+          "",
+          2,
+          -.04,
+          3432987324987239872948732982,
+          true,
+          false,
+          null,
+          0,
+          new Blob(["this ", "is ", "a ", "great ", "success!"],
+                   {"type" : "text\/plain"}),
+        ];
+        let receivedMessageIndex = 0;
+
+        let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+        mm.addMessageListener("test:ipcClonedMessage", function(message) {
+          is(message.json, messages[receivedMessageIndex++],
+             "Got correct round-tripped response");
+          if (receivedMessageIndex == messages.length) {
+            SimpleTest.finish();
+          }
+        });
+        mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+                           false);
+
+        for each (let message in messages) {
+          mm.sendAsyncMessage("test:ipcClonedMessage", message);
+        }
+      });
+
+      document.body.appendChild(iframe);
+    }
+
+    addEventListener("load", function() {
+      info("Got load event.");
+
+      let whitelist;
+      try {
+        whitelist =
+          SpecialPowers.getCharPref("dom.mozBrowserFramesWhitelist") + ", ";
+      } catch (e) {
+        whitelist = "";
+      }
+
+      whitelist += window.location.protocol + "//" + window.location.host;
+
+      SpecialPowers.pushPrefEnv({
+        "set": [
+          ["dom.ipc.browser_frames.oop_by_default", true],
+          ["dom.mozBrowserFramesEnabled", true],
+          ["dom.mozBrowserFramesWhitelist", whitelist],
+          ["browser.pageThumbs.enabled", false]
+        ]
+      }, runTests);
+    });
+  </script>
+</body>
+</html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8344,17 +8344,18 @@ nsGlobalWindow::GetIndexedDB(nsIIDBFacto
       if (isThirdParty) {
         NS_WARNING("IndexedDB is not permitted in a third-party window.");
         *_retval = nullptr;
         return NS_OK;
       }
     }
 
     // This may be null if being created from a file.
-    rv = indexedDB::IDBFactory::Create(this, getter_AddRefs(mIndexedDB));
+    rv = indexedDB::IDBFactory::Create(this, nullptr,
+                                       getter_AddRefs(mIndexedDB));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsIIDBFactory> request(mIndexedDB);
   request.forget(_retval);
   return NS_OK;
 }
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -744,17 +744,17 @@ IDBCursor::Advance(PRInt64 aCount)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aCount < 1 || aCount > PR_UINT32_MAX) {
     return NS_ERROR_TYPE_ERR;
   }
 
   Key key;
-  return ContinueInternal(key, aCount);
+  return ContinueInternal(key, PRInt32(aCount));
 }
 
 void
 CursorHelper::ReleaseMainThreadObjects()
 {
   mCursor = nullptr;
   AsyncConnectionHelper::ReleaseMainThreadObjects();
 }
@@ -871,30 +871,48 @@ ContinueHelper::MaybeSendResponseToChild
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mTransaction->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     ContinueResponse continueResponse;
     continueResponse.key() = mKey;
     continueResponse.objectKey() = mObjectKey;
     continueResponse.cloneInfo() = mCloneReadInfo;
+    continueResponse.blobsParent().SwapElements(blobsParent);
     response = continueResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   UpdateCursorState();
@@ -920,16 +938,18 @@ ContinueHelper::UnpackResponseFromParent
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::BindArgumentsToStatement(
                                                mozIStorageStatement* aStatement)
 {
   // Bind object store id.
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
+#include "mozilla/dom/ContentParent.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMLists.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "CheckQuotaHelper.h"
@@ -28,16 +29,17 @@
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::ContentParent;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   NoRequestDatabaseHelper(IDBTransaction* aTransaction)
   : AsyncConnectionHelper(aTransaction, nullptr)
@@ -165,17 +167,18 @@ private:
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBDatabase>
 IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
                     already_AddRefed<DatabaseInfo> aDatabaseInfo,
                     const nsACString& aASCIIOrigin,
-                    FileManager* aFileManager)
+                    FileManager* aFileManager,
+                    mozilla::dom::ContentParent* aContentParent)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
 
   nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
   NS_ASSERTION(databaseInfo, "Null pointer!");
 
   nsRefPtr<IDBDatabase> db(new IDBDatabase());
@@ -186,32 +189,34 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   }
 
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
+  db->mContentParent = aContentParent;
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never be null!");
 
   if (!mgr->RegisterDatabase(db)) {
     // Either out of memory or shutting down.
     return nullptr;
   }
 
   return db.forget();
 }
 
 IDBDatabase::IDBDatabase()
 : mDatabaseId(0),
   mActorChild(nullptr),
   mActorParent(nullptr),
+  mContentParent(nullptr),
   mInvalidated(0),
   mRegistered(false),
   mClosed(false),
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -14,16 +14,22 @@
 #include "nsIIDBDatabase.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 #include "mozilla/dom/indexedDB/FileManager.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
 struct DatabaseInfo;
 class IDBIndex;
 class IDBObjectStore;
 class IDBTransaction;
 class IndexedDatabaseManager;
@@ -45,17 +51,18 @@ public:
   NS_DECL_NSIFILESTORAGE
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache)
 
   static already_AddRefed<IDBDatabase>
   Create(IDBWrapperCache* aOwnerCache,
          already_AddRefed<DatabaseInfo> aDatabaseInfo,
          const nsACString& aASCIIOrigin,
-         FileManager* aFileManager);
+         FileManager* aFileManager,
+         mozilla::dom::ContentParent* aContentParent);
 
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
   nsIAtom* Id() const
   {
     return mDatabaseId;
   }
@@ -130,16 +137,22 @@ public:
   }
 
   IndexedDBDatabaseChild*
   GetActorChild() const
   {
     return mActorChild;
   }
 
+  mozilla::dom::ContentParent*
+  GetContentParent() const
+  {
+    return mContentParent;
+  }
+
   nsresult
   CreateObjectStoreInternal(IDBTransaction* aTransaction,
                             const ObjectStoreInfoGuts& aInfo,
                             IDBObjectStore** _retval);
 
 private:
   IDBDatabase();
   ~IDBDatabase();
@@ -160,16 +173,18 @@ private:
   // Only touched on the main thread.
   NS_DECL_EVENT_HANDLER(abort)
   NS_DECL_EVENT_HANDLER(error)
   NS_DECL_EVENT_HANDLER(versionchange)
 
   IndexedDBDatabaseChild* mActorChild;
   IndexedDBDatabaseParent* mActorParent;
 
+  mozilla::dom::ContentParent* mContentParent;
+
   PRInt32 mInvalidated;
   bool mRegistered;
   bool mClosed;
   bool mRunningVersionChange;
 };
 
 END_INDEXEDDB_NAMESPACE
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -2,25 +2,25 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBFactory.h"
-
 #include "nsIFile.h"
 #include "nsIJSContextStack.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include "jsdbgapi.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
@@ -41,16 +41,17 @@
 #include "IndexedDatabaseManager.h"
 #include "Key.h"
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 using mozilla::dom::ContentChild;
+using mozilla::dom::ContentParent;
 using mozilla::dom::TabChild;
 
 namespace {
 
 struct ObjectStoreInfoMap
 {
   ObjectStoreInfoMap()
   : id(LL_MININT), info(nullptr) { }
@@ -58,17 +59,17 @@ struct ObjectStoreInfoMap
   PRInt64 id;
   ObjectStoreInfo* info;
 };
 
 } // anonymous namespace
 
 IDBFactory::IDBFactory()
 : mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
-  mRootedOwningObject(false)
+  mContentParent(nullptr), mRootedOwningObject(false)
 {
 }
 
 IDBFactory::~IDBFactory()
 {
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
@@ -79,16 +80,17 @@ IDBFactory::~IDBFactory()
     NS_DROP_JS_OBJECTS(this, IDBFactory);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
                    const nsACString& aASCIIOrigin,
+                   ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(),
                "Non-chrome may not supply their own origin!");
 
   NS_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -113,16 +115,17 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
       *aFactory = nullptr;
       return NS_OK;
     }
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mASCIIOrigin = origin;
   factory->mWindow = aWindow;
+  factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     TabChild* tabChild = GetTabChildFrom(aWindow);
     NS_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     bool allowed;
@@ -140,16 +143,17 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::Create(JSContext* aCx,
                    JSObject* aOwningObject,
+                   ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
@@ -157,16 +161,17 @@ IDBFactory::Create(JSContext* aCx,
   nsCString origin;
   nsresult rv =
     IndexedDatabaseManager::GetASCIIOriginFromWindow(nullptr, origin);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mASCIIOrigin = origin;
   factory->mOwningObject = aOwningObject;
+  factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     ContentChild* contentChild = ContentChild::GetSingleton();
     NS_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     contentChild->SendPIndexedDBConstructor(actor);
@@ -175,21 +180,23 @@ IDBFactory::Create(JSContext* aCx,
   }
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
-IDBFactory::Create(IDBFactory** aFactory)
+IDBFactory::Create(ContentParent* aContentParent,
+                   IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  NS_ASSERTION(aContentParent, "Null ContentParent!");
 
 #ifdef DEBUG
   {
     nsIThreadJSContextStack* cxStack = nsContentUtils::ThreadJSContextStack();
     NS_ASSERTION(cxStack, "Couldn't get ThreadJSContextStack!");
 
     JSContext* lastCx;
     if (NS_SUCCEEDED(cxStack->Peek(&lastCx))) {
@@ -238,17 +245,17 @@ IDBFactory::Create(IDBFactory** aFactory
 
   JSAutoEnterCompartment ac;
   if (!ac.enter(cx, global)) {
     NS_WARNING("Failed to enter compartment!");
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<IDBFactory> factory;
-  rv = Create(cx, global, getter_AddRefs(factory));
+  rv = Create(cx, global, aContentParent, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_HOLD_JS_OBJECTS(factory, IDBFactory);
   factory->mRootedOwningObject = true;
 
   factory.forget(aFactory);
   return NS_OK;
 }
@@ -524,20 +531,20 @@ IDBFactory::OpenCommon(const nsAString& 
   }
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(window, scriptOwner, aCallingCx);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
-  if (IndexedDatabaseManager::IsMainProcess()) {                       
+  if (IndexedDatabaseManager::IsMainProcess()) {
     nsRefPtr<OpenDatabaseHelper> openHelper =
       new OpenDatabaseHelper(request, aName, mASCIIOrigin, aVersion, aDeleting,
-                             privilege);
+                             mContentParent, privilege);
 
     rv = openHelper->Init();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     nsRefPtr<CheckPermissionsHelper> permissionHelper =
       new CheckPermissionsHelper(openHelper, window, aDeleting);
 
     IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@@ -623,9 +630,9 @@ IDBFactory::Cmp(const jsval& aFirst,
   }
 
   if (first.IsUnset() || second.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   *_retval = Key::CompareKeys(first, second);
   return NS_OK;
-}
+}
\ No newline at end of file
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -12,61 +12,73 @@
 #include "mozIStorageConnection.h"
 #include "nsIIDBFactory.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 class nsIAtom;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct DatabaseInfo;
 class IDBDatabase;
 class IDBOpenDBRequest;
 class IndexedDBChild;
 class IndexedDBParent;
 
 struct ObjectStoreInfo;
 
 class IDBFactory MOZ_FINAL : public nsIIDBFactory
 {
+  typedef mozilla::dom::ContentParent ContentParent;
   typedef nsTArray<nsRefPtr<ObjectStoreInfo> > ObjectStoreInfoArray;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory)
   NS_DECL_NSIIDBFACTORY
 
   // Called when using IndexedDB from a window in a different process.
   static nsresult Create(nsPIDOMWindow* aWindow,
                          const nsACString& aASCIIOrigin,
+                         ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a window in the current process.
   static nsresult Create(nsPIDOMWindow* aWindow,
+                         ContentParent* aContentParent,
                          nsIIDBFactory** aFactory)
   {
     nsRefPtr<IDBFactory> factory;
-    nsresult rv = Create(aWindow, EmptyCString(), getter_AddRefs(factory));
+    nsresult rv =
+      Create(aWindow, EmptyCString(), aContentParent, getter_AddRefs(factory));
     NS_ENSURE_SUCCESS(rv, rv);
 
     factory.forget(aFactory);
     return NS_OK;
   }
 
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
                          JSObject* aOwningObject,
+                         ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
   // process.
-  static nsresult Create(IDBFactory** aFactory);
+  static nsresult Create(ContentParent* aContentParent,
+                         IDBFactory** aFactory);
 
   static already_AddRefed<mozIStorageConnection>
   GetConnection(const nsAString& aDatabaseFilePath);
 
   static nsresult
   LoadDatabaseInformation(mozIStorageConnection* aConnection,
                           nsIAtom* aDatabaseId,
                           PRUint64* aVersion,
@@ -113,14 +125,16 @@ private:
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
   JSObject* mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
 
+  mozilla::dom::ContentParent* mContentParent;
+
   bool mRootedOwningObject;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbfactory_h__
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -6,16 +6,19 @@
 
 #include "base/basictypes.h"
 
 #include "IDBIndex.h"
 
 #include "nsIIDBKeyRange.h"
 #include "nsIJSContextStack.h"
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 #include "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
@@ -27,16 +30,17 @@
 #include "DatabaseInfo.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 #include "IndexedDatabaseInlines.h"
 
 USING_INDEXEDDB_NAMESPACE
+using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
 class IndexHelper : public AsyncConnectionHelper
 {
 public:
   IndexHelper(IDBTransaction* aTransaction,
@@ -629,16 +633,17 @@ IDBIndex::OpenCursorFromChildProcess(IDB
 
 nsresult
 IDBIndex::OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const Key& aObjectKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
                (aCloneInfo.dataLength && aCloneInfo.data),
                "Inconsistent clone info!");
 
   IDBCursor::Direction direction =
@@ -646,16 +651,18 @@ IDBIndex::OpenCursorFromChildProcess(
 
   StructuredCloneReadInfo cloneInfo;
 
   if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  cloneInfo.mFiles.SwapElements(aBlobs);
+
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction,
                       Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey,
                       cloneInfo);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
@@ -1231,57 +1238,76 @@ GetHelper::MaybeSendResponseToChildProce
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobActors failed!");
+    }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    SerializedStructuredCloneReadInfo readInfo;
-    readInfo = mCloneReadInfo;
-    GetResponse getResponse = readInfo;
+    GetResponse getResponse;
+    getResponse.cloneInfo() = mCloneReadInfo;
+    getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
                "Bad response type!");
 
-  const SerializedStructuredCloneReadInfo& cloneInfo =
-    aResponseValue.get_GetResponse().cloneInfo();
+  const GetResponse& getResponse = aResponseValue.get_GetResponse();
+  const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
 
   NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
   nsCString tableName;
   if (mIndex->IsUnique()) {
@@ -1562,40 +1588,68 @@ GetAllHelper::MaybeSendResponseToChildPr
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-    if (!mCloneReadInfos[index].mFileInfos.IsEmpty()) {
-      NS_WARNING("No support for transferring blobs across processes yet!");
-      return Error;
+  GetAllResponse getAllResponse;
+
+  if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    PRUint32 length = mCloneReadInfos.Length();
+
+    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
+      getAllResponse.cloneInfos();
+    infos.SetCapacity(length);
+
+    InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
+    blobArrays.SetCapacity(length);
+
+    for (PRUint32 index = 0;
+         NS_SUCCEEDED(aResultCode) && index < length;
+         index++) {
+      const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
+
+      // Append the structured clone data.
+      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
+      *info = clone;
+
+      const nsTArray<StructuredCloneFile>& files = clone.mFiles;
+
+      // Now take care of the files.
+      BlobArray* blobArray = blobArrays.AppendElement();
+
+      InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
+
+      aResultCode =
+        IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                             blobs);
+      if (NS_FAILED(aResultCode)) {
+        NS_WARNING("ConvertBlobsToActors failed!");
+        break;
+      }
     }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    GetAllResponse getAllResponse;
-
-    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
-      getAllResponse.cloneInfos();
-
-    infos.SetCapacity(mCloneReadInfos.Length());
-
-    for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
-      *info = mCloneReadInfos[index];
-    }
-
     response = getAllResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
@@ -1603,29 +1657,34 @@ GetAllHelper::MaybeSendResponseToChildPr
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
                                             const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
                "Bad response type!");
 
+  const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
   const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
-    aResponseValue.get_GetAllResponse().cloneInfos();
+    getAllResponse.cloneInfos();
+  const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
 
   mCloneReadInfos.SetCapacity(cloneInfos.Length());
 
   for (PRUint32 index = 0; index < cloneInfos.Length(); index++) {
     const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
+    const InfallibleTArray<PBlobChild*> blobs = blobArrays[index].blobsChild();
 
     StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
     if (!destInfo->SetFromSerialized(srcInfo)) {
       NS_WARNING("Failed to copy clone buffer!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
+
+    IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
   }
 
   return NS_OK;
 }
 
 nsresult
 OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
@@ -2194,23 +2253,40 @@ OpenCursorHelper::MaybeSendResponseToChi
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
+
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
-  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
-
   if (NS_SUCCEEDED(aResultCode)) {
     nsresult rv = EnsureCursor();
     if (NS_FAILED(rv)) {
       NS_WARNING("EnsureCursor failed!");
       aResultCode = rv;
     }
   }
 
@@ -2236,16 +2312,17 @@ OpenCursorHelper::MaybeSendResponseToChi
                    "Shouldn't be possible!");
 
       IndexCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.objectKey() = mObjectKey;
       params.optionalCloneInfo() = mSerializedCloneReadInfo;
+      params.blobsParent().SwapElements(blobsParent);
 
       IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
 
       if (!indexActor->SendPIndexedDBCursorConstructor(cursorActor, params)) {
         return Error;
       }
 
       openCursorResponse = cursorActor;
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -140,16 +140,17 @@ public:
                               IDBRequest** _retval);
 
   nsresult OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const Key& aObjectKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
 private:
   IDBIndex();
   ~IDBIndex();
 
   nsRefPtr<IDBObjectStore> mObjectStore;
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -7,17 +7,20 @@
 #include "base/basictypes.h"
 
 #include "IDBObjectStore.h"
 
 #include "nsIJSContextStack.h"
 #include "nsIOutputStream.h"
 
 #include "jsfriendapi.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMFile.h"
 #include "nsDOMLists.h"
 #include "nsEventDispatcher.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
@@ -40,16 +43,17 @@
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 #include "IndexedDatabaseInlines.h"
 
 #define FILE_COPY_BUFFER_SIZE 32768
 
 USING_INDEXEDDB_NAMESPACE
+using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
 inline
 bool
 IgnoreNothing(PRUnichar c)
 {
@@ -876,17 +880,18 @@ IDBObjectStore::GetStructuredCloneReadIn
     FileManager* fileManager = aDatabase->Manager();
 
     for (PRUint32 i = 0; i < array.Length(); i++) {
       const PRInt64& id = array[i];
 
       nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
       NS_ASSERTION(fileInfo, "Null file info!");
 
-      aInfo.mFileInfos.AppendElement(fileInfo);
+      StructuredCloneFile* file = aInfo.mFiles.AppendElement();
+      file->mFileInfo.swap(fileInfo);
     }
   }
 
   aInfo.mDatabase = aDatabase;
 
   return NS_OK;
 }
 
@@ -949,19 +954,19 @@ IDBObjectStore::SerializeValue(JSContext
 
   return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
 }
 
 static inline PRUint32
 SwapBytes(PRUint32 u)
 {
 #ifdef IS_BIG_ENDIAN
-  return ((u & 0x000000ffU) << 24) |                                          
-         ((u & 0x0000ff00U) << 8) |                                           
-         ((u & 0x00ff0000U) >> 8) |                                           
+  return ((u & 0x000000ffU) << 24) |
+         ((u & 0x0000ff00U) << 8) |
+         ((u & 0x00ff0000U) >> 8) |
          ((u & 0xff000000U) >> 24);
 #else
   return u;
 #endif
 }
 
 static inline double
 SwapBytes(PRUint64 u)
@@ -1000,34 +1005,38 @@ StructuredCloneReadString(JSStructuredCl
   if (!JS_ReadBytes(aReader, buffer, length)) {
     NS_WARNING("Failed to read type!");
     return false;
   }
 
   return true;
 }
 
+// static
 JSObject*
 IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             uint32_t aData,
                                             void* aClosure)
 {
   if (aTag == SCTAG_DOM_FILEHANDLE || aTag == SCTAG_DOM_BLOB ||
       aTag == SCTAG_DOM_FILE) {
     StructuredCloneReadInfo* cloneReadInfo =
       reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
 
-    if (aData >= cloneReadInfo->mFileInfos.Length()) {
+    if (aData >= cloneReadInfo->mFiles.Length()) {
       NS_ERROR("Bad blob index!");
       return nullptr;
     }
 
-    nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
+    nsresult rv;
+
+    StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
+    nsRefPtr<FileInfo>& fileInfo = file.mFileInfo;
     IDBDatabase* database = cloneReadInfo->mDatabase;
 
     if (aTag == SCTAG_DOM_FILEHANDLE) {
       nsCString type;
       if (!StructuredCloneReadString(aReader, type)) {
         return nullptr;
       }
       NS_ConvertUTF8toUTF16 convType(type);
@@ -1037,85 +1046,102 @@ IDBObjectStore::StructuredCloneReadCallb
         return nullptr;
       }
       NS_ConvertUTF8toUTF16 convName(name);
 
       nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(database,
         convName, convType, fileInfo.forget());
 
       jsval wrappedFileHandle;
-      nsresult rv =
+      rv =
         nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx),
                                    static_cast<nsIDOMFileHandle*>(fileHandle),
                                    &NS_GET_IID(nsIDOMFileHandle),
                                    &wrappedFileHandle);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to wrap native!");
         return nullptr;
       }
 
       return JSVAL_TO_OBJECT(wrappedFileHandle);
     }
 
-    FileManager* fileManager = database->Manager();
-
-    nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
-    if (!directory) {
-      NS_WARNING("Failed to get directory!");
-      return nullptr;
-    }
-
-    nsCOMPtr<nsIFile> nativeFile =
-      fileManager->GetFileForId(directory, fileInfo->Id());
-    if (!nativeFile) {
-      NS_WARNING("Failed to get file!");
-      return nullptr;
-    }
-
     PRUint64 size;
     if (!JS_ReadBytes(aReader, &size, sizeof(PRUint64))) {
       NS_WARNING("Failed to read size!");
       return nullptr;
     }
     size = SwapBytes(size);
 
     nsCString type;
     if (!StructuredCloneReadString(aReader, type)) {
       return nullptr;
     }
     NS_ConvertUTF8toUTF16 convType(type);
 
+    nsCOMPtr<nsIFile> nativeFile;
+    if (!file.mFile) {
+      FileManager* fileManager = database->Manager();
+        NS_ASSERTION(fileManager, "This should never be null!");
+
+      nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
+      if (!directory) {
+        NS_WARNING("Failed to get directory!");
+        return nullptr;
+      }
+
+      nativeFile = fileManager->GetFileForId(directory, fileInfo->Id());
+      if (!nativeFile) {
+        NS_WARNING("Failed to get file!");
+        return nullptr;
+      }
+    }
+
     if (aTag == SCTAG_DOM_BLOB) {
-      nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(convType, size,
-                                                    nativeFile, fileInfo);
+      nsCOMPtr<nsIDOMBlob> domBlob;
+      if (file.mFile) {
+        domBlob = file.mFile;
+      }
+      else {
+        domBlob = new nsDOMFileFile(convType, size, nativeFile, fileInfo);
+      }
 
       jsval wrappedBlob;
-      nsresult rv =
-        nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
+       rv =
+        nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domBlob,
                                    &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to wrap native!");
         return nullptr;
       }
 
       return JSVAL_TO_OBJECT(wrappedBlob);
     }
 
+    NS_ASSERTION(aTag == SCTAG_DOM_FILE, "Huh?!");
+
     nsCString name;
     if (!StructuredCloneReadString(aReader, name)) {
       return nullptr;
     }
     NS_ConvertUTF8toUTF16 convName(name);
 
-    nsCOMPtr<nsIDOMFile> file = new nsDOMFileFile(convName, convType, size,
-                                                  nativeFile, fileInfo);
+    nsCOMPtr<nsIDOMFile> domFile;
+    if (file.mFile) {
+      domFile = do_QueryInterface(file.mFile);
+      NS_ASSERTION(domFile, "This should never fail!");
+    }
+    else {
+      domFile = new nsDOMFileFile(convName, convType, size, nativeFile,
+                                  fileInfo);
+    }
 
     jsval wrappedFile;
-    nsresult rv =
-      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
+    rv =
+      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domFile,
                                  &NS_GET_IID(nsIDOMFile), &wrappedFile);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to wrap native!");
       return nullptr;
     }
 
     return JSVAL_TO_OBJECT(wrappedFile);
   }
@@ -1125,16 +1151,17 @@ IDBObjectStore::StructuredCloneReadCallb
 
   if (runtimeCallbacks) {
     return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr);
   }
 
   return nullptr;
 }
 
+// static
 JSBool
 IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
                                              JSStructuredCloneWriter* aWriter,
                                              JSObject* aObj,
                                              void* aClosure)
 {
   StructuredCloneWriteInfo* cloneWriteInfo =
     reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
@@ -1155,39 +1182,38 @@ IDBObjectStore::StructuredCloneWriteCall
   if (wrappedNative) {
     nsISupports* supports = wrappedNative->Native();
 
     IDBTransaction* transaction = cloneWriteInfo->mTransaction;
     FileManager* fileManager = transaction->Database()->Manager();
 
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
     if (blob) {
+      nsCOMPtr<nsIInputStream> inputStream;
+
       // Check if it is a blob created from this db or the blob was already
       // stored in this db
-
       nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
-      nsCOMPtr<nsIInputStream> inputStream;
-
-      if (!fileInfo) {
+      if (!fileInfo && fileManager) {
         fileInfo = blob->GetFileInfo(fileManager);
-      }
-
-      if (!fileInfo) {
-        fileInfo = fileManager->GetNewFileInfo();
+
         if (!fileInfo) {
-          NS_WARNING("Failed to get new file info!");
-          return false;
+          fileInfo = fileManager->GetNewFileInfo();
+          if (!fileInfo) {
+            NS_WARNING("Failed to get new file info!");
+            return false;
+          }
+
+          if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
+            NS_WARNING("Failed to get internal steam!");
+            return false;
+          }
+
+          transaction->AddFileInfo(blob, fileInfo);
         }
-
-        if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
-          NS_WARNING("Failed to get internal steam!");
-          return false;
-        }
-
-        transaction->AddFileInfo(blob, fileInfo);
       }
 
       PRUint64 size;
       if (NS_FAILED(blob->GetSize(&size))) {
         NS_WARNING("Failed to get size!");
         return false;
       }
       size = SwapBytes(size);
@@ -1199,32 +1225,32 @@ IDBObjectStore::StructuredCloneWriteCall
       }
       NS_ConvertUTF16toUTF8 convType(type);
       PRUint32 convTypeLength = SwapBytes(convType.Length());
 
       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
 
       if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
                               cloneWriteInfo->mFiles.Length()) ||
-          !JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
-          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
+          !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
+          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
           !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
         return false;
       }
 
       if (file) {
         nsString name;
         if (NS_FAILED(file->GetName(name))) {
           NS_WARNING("Failed to get name!");
           return false;
         }
         NS_ConvertUTF16toUTF8 convName(name);
         PRUint32 convNameLength = SwapBytes(convName.Length());
 
-        if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
+        if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
             !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
           return false;
         }
       }
 
       StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
       cloneFile->mFile = blob.forget();
       cloneFile->mFileInfo = fileInfo.forget();
@@ -1280,16 +1306,17 @@ IDBObjectStore::StructuredCloneWriteCall
     js::GetContextStructuredCloneCallbacks(aCx);
   if (runtimeCallbacks) {
     return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr);
   }
 
   return false;
 }
 
+// static
 nsresult
 IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
                                       nsTArray<PRInt64>& aResult)
 {
   nsCharSeparatedTokenizerTemplate<IgnoreNothing> tokenizer(aFileIds, ' ');
 
   while (tokenizer.hasMoreTokens()) {
     nsString token(tokenizer.nextToken());
@@ -1302,16 +1329,89 @@ IDBObjectStore::ConvertFileIdsToArray(co
     
     PRInt64* element = aResult.AppendElement();
     *element = id;
   }
 
   return NS_OK;
 }
 
+// static
+void
+IDBObjectStore::ConvertActorsToBlobs(
+                                   const InfallibleTArray<PBlobChild*>& aActors,
+                                   nsTArray<StructuredCloneFile>& aFiles)
+{
+  NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!");
+
+  if (!aActors.IsEmpty()) {
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    NS_ASSERTION(contentChild, "This should never be null!");
+
+    PRUint32 length = aActors.Length();
+    aFiles.SetCapacity(length);
+
+    for (PRUint32 index = 0; index < length; index++) {
+      BlobChild* actor = static_cast<BlobChild*>(aActors[index]);
+
+      StructuredCloneFile* file = aFiles.AppendElement();
+      file->mFile = actor->GetBlob();
+    }
+  }
+}
+
+// static
+nsresult
+IDBObjectStore::ConvertBlobsToActors(
+                                    ContentParent* aContentParent,
+                                    FileManager* aFileManager,
+                                    const nsTArray<StructuredCloneFile>& aFiles,
+                                    InfallibleTArray<PBlobParent*>& aActors)
+{
+  NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aContentParent, "Null contentParent!");
+  NS_ASSERTION(aFileManager, "Null file manager!");
+
+  if (!aFiles.IsEmpty()) {
+    nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
+    if (!directory) {
+      NS_WARNING("Failed to get directory!");
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    PRUint32 fileCount = aFiles.Length();
+    aActors.SetCapacity(fileCount);
+
+    for (PRUint32 index = 0; index < fileCount; index++) {
+      const StructuredCloneFile& file = aFiles[index];
+      NS_ASSERTION(file.mFileInfo, "This should never be null!");
+
+      nsCOMPtr<nsIFile> nativeFile =
+        aFileManager->GetFileForId(directory, file.mFileInfo->Id());
+      if (!nativeFile) {
+        NS_WARNING("Failed to get file!");
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+
+      nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(nativeFile);
+
+      BlobParent* actor =
+        aContentParent->GetOrCreateActorForBlob(blob);
+      NS_ASSERTION(actor, "This should never fail without aborting!");
+
+      aActors.AppendElement(actor);
+    }
+  }
+
+  return NS_OK;
+}
+
 IDBObjectStore::IDBObjectStore()
 : mId(LL_MININT),
   mKeyPath(0),
   mCachedKeyPath(JSVAL_VOID),
   mRooted(false),
   mAutoIncrement(false),
   mActorChild(nullptr),
   mActorParent(nullptr)
@@ -1443,16 +1543,17 @@ IDBObjectStore::AddOrPut(const jsval& aV
   return NS_OK;
 }
 
 nsresult
 IDBObjectStore::AddOrPutInternal(
                       const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
                       const Key& aKey,
                       const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
+                      const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
                       bool aOverwrite,
                       IDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
@@ -1466,16 +1567,58 @@ IDBObjectStore::AddOrPutInternal(
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   StructuredCloneWriteInfo cloneWriteInfo;
   if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) {
     NS_WARNING("Failed to copy structured clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  if (!aBlobs.IsEmpty()) {
+    FileManager* fileManager = Transaction()->Database()->Manager();
+    NS_ASSERTION(fileManager, "Null file manager?!");
+
+    PRUint32 length = aBlobs.Length();
+    cloneWriteInfo.mFiles.SetCapacity(length);
+
+    for (PRUint32 index = 0; index < length; index++) {
+      const nsCOMPtr<nsIDOMBlob>& blob = aBlobs[index];
+
+      nsCOMPtr<nsIInputStream> inputStream;
+
+      nsRefPtr<FileInfo> fileInfo = Transaction()->GetFileInfo(blob);
+      if (!fileInfo) {
+        fileInfo = blob->GetFileInfo(fileManager);
+
+        if (!fileInfo) {
+          fileInfo = fileManager->GetNewFileInfo();
+          if (!fileInfo) {
+            NS_WARNING("Failed to get new file info!");
+            return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          }
+
+          if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
+            NS_WARNING("Failed to get internal steam!");
+            return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          }
+
+          // XXXbent This is where we should send a message back to the child to
+          //         update the file id.
+
+          Transaction()->AddFileInfo(blob, fileInfo);
+        }
+      }
+
+      StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement();
+      file->mFile = blob;
+      file->mFileInfo.swap(fileInfo);
+      file->mInputStream.swap(inputStream);
+    }
+  }
+
   Key key(aKey);
 
   nsTArray<IndexUpdateInfo> updateInfo(aUpdateInfoArray);
 
   nsRefPtr<AddHelper> helper =
     new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
                   updateInfo);
 
@@ -1643,16 +1786,17 @@ IDBObjectStore::OpenCursorInternal(IDBKe
 }
 
 nsresult
 IDBObjectStore::OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
                (aCloneInfo.dataLength && aCloneInfo.data),
                "Inconsistent clone info!");
 
   IDBCursor::Direction direction =
@@ -1660,16 +1804,18 @@ IDBObjectStore::OpenCursorFromChildProce
 
   StructuredCloneReadInfo cloneInfo;
 
   if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  cloneInfo.mFiles.SwapElements(aBlobs);
+
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
                       EmptyCString(), EmptyCString(), aKey, cloneInfo);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
@@ -2541,16 +2687,41 @@ AddHelper::ReleaseMainThreadObjects()
 nsresult
 AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
 {
   AddPutParams commonParams;
   commonParams.cloneInfo() = mCloneWriteInfo;
   commonParams.key() = mKey;
   commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo);
 
+  const nsTArray<StructuredCloneFile>& files = mCloneWriteInfo.mFiles;
+
+  if (!files.IsEmpty()) {
+    PRUint32 fileCount = files.Length();
+
+    InfallibleTArray<PBlobChild*>& blobsChild = commonParams.blobsChild();
+    blobsChild.SetCapacity(fileCount);
+
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    NS_ASSERTION(contentChild, "This should never be null!");
+
+    for (PRUint32 index = 0; index < fileCount; index++) {
+      const StructuredCloneFile& file = files[index];
+
+      NS_ASSERTION(file.mFile, "This should never be null!");
+      NS_ASSERTION(!file.mFileInfo, "This is not yet supported!");
+
+      BlobChild* actor =
+        contentChild->GetOrCreateActorForBlob(file.mFile);
+      NS_ASSERTION(actor, "This should never fail without aborting!");
+
+      blobsChild.AppendElement(actor);
+    }
+  }
+
   if (mOverwrite) {
     PutParams putParams;
     putParams.commonParams() = commonParams;
     aParams = putParams;
   }
   else {
     AddParams addParams;
     addParams.commonParams() = commonParams;
@@ -2685,57 +2856,76 @@ GetHelper::MaybeSendResponseToChildProce
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+  }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    SerializedStructuredCloneReadInfo readInfo;
-    readInfo = mCloneReadInfo;
-    GetResponse getResponse = readInfo;
+    GetResponse getResponse;
+    getResponse.cloneInfo() = mCloneReadInfo;
+    getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
                "Bad response type!");
 
-  const SerializedStructuredCloneReadInfo& cloneInfo =
-    aResponseValue.get_GetResponse().cloneInfo();
+  const GetResponse& getResponse = aResponseValue.get_GetResponse();
+  const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
 
   NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
 {
   NS_ASSERTION(mKeyRange, "Must have a key range here!");
 
@@ -3094,23 +3284,40 @@ OpenCursorHelper::MaybeSendResponseToChi
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
+
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
-  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
-
   if (NS_SUCCEEDED(aResultCode)) {
     nsresult rv = EnsureCursor();
     if (NS_FAILED(rv)) {
       NS_WARNING("EnsureCursor failed!");
       aResultCode = rv;
     }
   }
 
@@ -3136,16 +3343,17 @@ OpenCursorHelper::MaybeSendResponseToChi
                    mSerializedCloneReadInfo.dataLength,
                    "Shouldn't be possible!");
 
       ObjectStoreCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.cloneInfo() = mSerializedCloneReadInfo;
+      params.blobsParent().SwapElements(blobsParent);
 
       IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
 
       if (!objectStoreActor->SendPIndexedDBCursorConstructor(cursorActor,
                                                              params)) {
         return Error;
       }
 
@@ -3520,41 +3728,65 @@ GetAllHelper::MaybeSendResponseToChildPr
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-    if (!mCloneReadInfos[index].mFileInfos.IsEmpty()) {
-      NS_WARNING("No support for transferring blobs across processes yet!");
-      return Error;
+    GetAllResponse getAllResponse;
+
+  if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    PRUint32 length = mCloneReadInfos.Length();
+
+    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
+      getAllResponse.cloneInfos();
+    infos.SetCapacity(length);
+
+    InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
+    blobArrays.SetCapacity(length);
+
+    for (PRUint32 index = 0;
+         NS_SUCCEEDED(aResultCode) && index < length;
+         index++) {
+      // Append the structured clone data.
+      const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
+      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
+      *info = clone;
+
+      // Now take care of the files.
+      const nsTArray<StructuredCloneFile>& files = clone.mFiles;
+      BlobArray* blobArray = blobArrays.AppendElement();
+      InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
+
+      aResultCode =
+        IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                             blobs);
+      if (NS_FAILED(aResultCode)) {
+        NS_WARNING("ConvertBlobsToActors failed!");
+        break;
+    }
     }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    GetAllResponse getAllResponse;
-
-    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
-      getAllResponse.cloneInfos();
-
-    infos.SetCapacity(mCloneReadInfos.Length());
-
-    for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
-      *info = mCloneReadInfos[index];
-    }
-
-    getAllResponse = infos;
     response = getAllResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
@@ -3562,29 +3794,34 @@ GetAllHelper::MaybeSendResponseToChildPr
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
                                             const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
                "Bad response type!");
 
+  const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
   const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
-    aResponseValue.get_GetAllResponse().cloneInfos();
+    getAllResponse.cloneInfos();
+  const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
 
   mCloneReadInfos.SetCapacity(cloneInfos.Length());
 
   for (PRUint32 index = 0; index < cloneInfos.Length(); index++) {
     const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
+    const InfallibleTArray<PBlobChild*> blobs = blobArrays[index].blobsChild();
 
     StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
     if (!destInfo->SetFromSerialized(srcInfo)) {
       NS_WARNING("Failed to copy clone buffer!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
+
+    IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
   }
 
   return NS_OK;
 }
 
 nsresult
 CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -12,34 +12,42 @@
 #include "nsIIDBObjectStore.h"
 #include "nsIIDBTransaction.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 
+class nsIDOMBlob;
 class nsIScriptContext;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class PBlobChild;
+class PBlobParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
+class FileManager;
 class IDBCursor;
 class IDBKeyRange;
 class IDBRequest;
 class IndexedDBObjectStoreChild;
 class IndexedDBObjectStoreParent;
 class Key;
 
 struct IndexInfo;
 struct IndexUpdateInfo;
 struct ObjectStoreInfo;
-struct StructuredCloneReadInfo;
-struct StructuredCloneWriteInfo;
 
 class IDBObjectStore MOZ_FINAL : public nsIIDBObjectStore
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBOBJECTSTORE
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore)
@@ -98,16 +106,28 @@ public:
                                JSStructuredCloneWriter* aWriter,
                                JSObject* aObj,
                                void* aClosure);
 
   static nsresult
   ConvertFileIdsToArray(const nsAString& aFileIds,
                         nsTArray<PRInt64>& aResult);
 
+  // Called only in the main process.
+  static nsresult
+  ConvertBlobsToActors(ContentParent* aContentParent,
+                       FileManager* aFileManager,
+                       const nsTArray<StructuredCloneFile>& aFiles,
+                       InfallibleTArray<PBlobParent*>& aActors);
+
+  // Called only in the child process.
+  static void
+  ConvertActorsToBlobs(const InfallibleTArray<PBlobChild*>& aActors,
+                       nsTArray<StructuredCloneFile>& aFiles);
+
   const nsString& Name() const
   {
     return mName;
   }
 
   bool IsAutoIncrement() const
   {
     return mAutoIncrement;
@@ -178,16 +198,17 @@ public:
   nsresult
   IndexInternal(const nsAString& aName,
                 IDBIndex** _retval);
 
   nsresult AddOrPutInternal(
                       const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
                       const Key& aKey,
                       const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
+                      const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
                       bool aOverwrite,
                       IDBRequest** _retval);
 
   nsresult GetInternal(IDBKeyRange* aKeyRange,
                        JSContext* aCx,
                        IDBRequest** _retval);
 
   nsresult GetAllInternal(IDBKeyRange* aKeyRange,
@@ -211,16 +232,17 @@ public:
                               JSContext* aCx,
                               IDBRequest** _retval);
 
   nsresult OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
   void
   SetInfo(ObjectStoreInfo* aInfo);
 
   static JSClass sDummyPropJSClass;
 
 protected:
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -44,36 +44,50 @@ enum FactoryPrivilege {
 template <class T>
 void SwapData(T& aData1, T& aData2)
 {
   T temp = aData2;
   aData2 = aData1;
   aData1 = temp;
 }
 
+struct StructuredCloneFile
+{
+  bool operator==(const StructuredCloneFile& aOther) const
+  {
+    return this->mFile == aOther.mFile &&
+           this->mFileInfo == aOther.mFileInfo &&
+           this->mInputStream == aOther.mInputStream;
+  }
+
+  nsCOMPtr<nsIDOMBlob> mFile;
+  nsRefPtr<FileInfo> mFileInfo;
+  nsCOMPtr<nsIInputStream> mInputStream;
+};
+
 struct SerializedStructuredCloneReadInfo;
 
 struct StructuredCloneReadInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneReadInfo();
 
   void Swap(StructuredCloneReadInfo& aCloneReadInfo)
   {
     mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
-    mFileInfos.SwapElements(aCloneReadInfo.mFileInfos);
+    mFiles.SwapElements(aCloneReadInfo.mFiles);
     SwapData(mDatabase, aCloneReadInfo.mDatabase);
   }
 
   // In IndexedDatabaseInlines.h
   inline bool
   SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther);
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
-  nsTArray<nsRefPtr<FileInfo> > mFileInfos;
+  nsTArray<StructuredCloneFile> mFiles;
   IDBDatabase* mDatabase;
 };
 
 struct SerializedStructuredCloneReadInfo
 {
   SerializedStructuredCloneReadInfo()
   : data(nullptr), dataLength(0)
   { }
@@ -93,30 +107,16 @@ struct SerializedStructuredCloneReadInfo
     return *this;
   }
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   uint64_t* data;
   size_t dataLength;
 };
 
-struct StructuredCloneFile
-{
-  bool operator==(const StructuredCloneFile& aOther) const
-  {
-    return this->mFile == aOther.mFile &&
-           this->mFileInfo == aOther.mFileInfo &&
-           this->mInputStream == aOther.mInputStream;
-  }
-
-  nsCOMPtr<nsIDOMBlob> mFile;
-  nsRefPtr<FileInfo> mFileInfo;
-  nsCOMPtr<nsIInputStream> mInputStream;
-};
-
 struct SerializedStructuredCloneWriteInfo;
 
 struct StructuredCloneWriteInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneWriteInfo();
 
   void Swap(StructuredCloneWriteInfo& aCloneWriteInfo)
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -45,17 +45,17 @@ bool
 StructuredCloneReadInfo::SetFromSerialized(
                                 const SerializedStructuredCloneReadInfo& aOther)
 {
   if (aOther.dataLength &&
       !mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
     return false;
   }
 
-  mFileInfos.Clear();
+  mFiles.Clear();
   return true;
 }
 
 inline
 void
 AppendConditionClause(const nsACString& aColumnName,
                       const nsACString& aArgName,
                       bool aLessThan,
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -212,33 +212,36 @@ IndexedDatabaseManager::GetOrCreate()
       instance->mCurrentWindowIndex = BAD_TLS_INDEX;
       return nullptr;
     }
 
     nsresult rv;
 
     if (sIsMainProcess) {
       nsCOMPtr<nsIFile> dbBaseDirectory;
-      rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR, getter_AddRefs(dbBaseDirectory));
+      rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
+                                  getter_AddRefs(dbBaseDirectory));
       if (NS_FAILED(rv)) {
-          rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(dbBaseDirectory));
+          rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                      getter_AddRefs(dbBaseDirectory));
       }
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       rv = dbBaseDirectory->Append(NS_LITERAL_STRING("indexedDB"));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       rv = dbBaseDirectory->GetPath(instance->mDatabaseBasePath);
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       // Make a lazy thread for any IO we need (like clearing or enumerating the
       // contents of indexedDB database directories).
-      instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
-                                               NS_LITERAL_CSTRING("IndexedDB I/O"),
-                                               LazyIdleThread::ManualShutdown);
+      instance->mIOThread =
+        new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+                           NS_LITERAL_CSTRING("IndexedDB I/O"),
+                           LazyIdleThread::ManualShutdown);
 
       // We need one quota callback object to hand to SQLite.
       instance->mQuotaCallbackSingleton = new QuotaCallback();
 
       // Make a timer here to avoid potential failures later. We don't actually
       // initialize the timer until shutdown.
       instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
       NS_ENSURE_TRUE(instance->mShutdownTimer, nullptr);
@@ -1905,17 +1908,18 @@ IndexedDatabaseManager::InitWindowless(c
   // exceptions.
   nsCOMPtr<nsIDOMScriptObjectFactory> sof(do_GetService(kDOMSOF_CID));
 
   JSObject* obj = JSVAL_TO_OBJECT(aObj);
 
   JSObject* global = JS_GetGlobalForObject(aCx, obj);
 
   nsRefPtr<IDBFactory> factory;
-  nsresult rv = IDBFactory::Create(aCx, global, getter_AddRefs(factory));
+  nsresult rv =
+    IDBFactory::Create(aCx, global, nullptr, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(factory, "This should never fail for chrome!");
 
   jsval indexedDBVal;
   rv = nsContentUtils::WrapNative(aCx, obj, factory, &indexedDBVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -2101,20 +2101,18 @@ OpenDatabaseHelper::EnsureSuccessResult(
 
     NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!");
   }
 
   dbInfo->nextObjectStoreId = mLastObjectStoreId + 1;
   dbInfo->nextIndexId = mLastIndexId + 1;
 
   nsRefPtr<IDBDatabase> database =
-    IDBDatabase::Create(mOpenDBRequest,
-                        dbInfo.forget(),
-                        mASCIIOrigin,
-                        mFileManager);
+    IDBDatabase::Create(mOpenDBRequest, dbInfo.forget(), mASCIIOrigin,
+                        mFileManager, mContentParent);
   if (!database) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
   mDatabase.swap(database);
 
   return NS_OK;
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -9,32 +9,40 @@
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBRequest.h"
 
 #include "nsIRunnable.h"
 
 class mozIStorageConnection;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class OpenDatabaseHelper : public HelperBase
 {
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
                      const nsACString& aASCIIOrigin,
                      PRUint64 aRequestedVersion,
                      bool aForDeletion,
+                     mozilla::dom::ContentParent* aContentParent,
                      FactoryPrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
       mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
-      mCurrentVersion(0), mLastObjectStoreId(0), mLastIndexId(0),
-      mState(eCreated), mResultCode(NS_OK), mLoadDBMetadata(false)
+      mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
+      mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
+      mLoadDBMetadata(false)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
                  "Can't be for deletion and request a version!");
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
@@ -98,16 +106,17 @@ protected:
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
   nsCString mASCIIOrigin;
   PRUint64 mRequestedVersion;
   bool mForDeletion;
   FactoryPrivilege mPrivilege;
   nsCOMPtr<nsIAtom> mDatabaseId;
+  mozilla::dom::ContentParent* mContentParent;
 
   // Out-params.
   nsTArray<nsRefPtr<ObjectStoreInfo> > mObjectStores;
   PRUint64 mCurrentVersion;
   nsString mDatabaseFilePath;
   PRInt64 mLastObjectStoreId;
   PRInt64 mLastIndexId;
   nsRefPtr<IDBDatabase> mDatabase;
@@ -128,9 +137,9 @@ protected:
   nsRefPtr<FileManager> mFileManager;
 
   nsRefPtr<DatabaseInfo> mDBInfo;
   bool mLoadDBMetadata;
 };
 
 END_INDEXEDDB_NAMESPACE
 
-#endif // mozilla_dom_indexeddb_opendatabasehelper_h__
+#endif // mozilla_dom_indexeddb_opendatabasehelper_h__
\ No newline at end of file
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -1,31 +1,34 @@
 /* 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 "base/basictypes.h"
 
 #include "IndexedDBChild.h"
 
-#include "mozilla/Assertions.h"
+#include "nsIAtom.h"
 
-#include "nsIAtom.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentChild.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabaseManager.h"
 
 USING_INDEXEDDB_NAMESPACE
 
+using namespace mozilla::dom;
+
 namespace {
 
 class IPCOpenDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   IPCOpenDatabaseHelper(IDBDatabase* aDatabase, IDBOpenDBRequest* aRequest)
   : AsyncConnectionHelper(aDatabase, aRequest)
   {
@@ -284,17 +287,18 @@ IndexedDBDatabaseChild::EnsureDatabase(
         NS_WARNING("Out of memory!");
         return false;
       }
     }
   }
 
   if (!mDatabase) {
     nsRefPtr<IDBDatabase> database =
-      IDBDatabase::Create(aRequest, dbInfo.forget(), aDBInfo.origin, NULL);
+      IDBDatabase::Create(aRequest, dbInfo.forget(), aDBInfo.origin, NULL,
+                          NULL);
     if (!database) {
       NS_WARNING("Failed to create database!");
       return false;
     }
 
     database->SetActor(this);
 
     mDatabase = database;
@@ -641,23 +645,28 @@ IndexedDBObjectStoreChild::RecvPIndexedD
     static_cast<IndexedDBObjectStoreRequestChild*>(aParams.requestChild());
   NS_ASSERTION(requestActor, "Must have an actor here!");
 
   nsRefPtr<IDBRequest> request = requestActor->GetRequest();
   NS_ASSERTION(request, "Must have a request here!");
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
+  nsTArray<StructuredCloneFile> blobs;
+  IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+
   nsRefPtr<IDBCursor> cursor;
   nsresult rv =
     mObjectStore->OpenCursorFromChildProcess(request, direction, aParams.key(),
-                                             aParams.cloneInfo(),
+                                             aParams.cloneInfo(), blobs,
                                              getter_AddRefs(cursor));
   NS_ENSURE_SUCCESS(rv, false);
 
+  MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
+
   actor->SetCursor(cursor);
   return true;
 }
 
 PIndexedDBRequestChild*
 IndexedDBObjectStoreChild::AllocPIndexedDBRequest(
                                         const ObjectStoreRequestParams& aParams)
 {
@@ -749,26 +758,32 @@ IndexedDBIndexChild::RecvPIndexedDBCurso
 
   nsRefPtr<IDBCursor> cursor;
   nsresult rv;
 
   typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType;
 
   switch (aParams.optionalCloneInfo().type()) {
     case CursorUnionType::TSerializedStructuredCloneReadInfo: {
+      nsTArray<StructuredCloneFile> blobs;
+      IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+
       const SerializedStructuredCloneReadInfo& cloneInfo =
         aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo();
 
       rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(),
                                               aParams.objectKey(), cloneInfo,
+                                              blobs,
                                               getter_AddRefs(cursor));
       NS_ENSURE_SUCCESS(rv, false);
     } break;
 
     case CursorUnionType::Tvoid_t:
+      MOZ_ASSERT(aParams.blobsChild().IsEmpty());
+
       rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(),
                                               aParams.objectKey(),
                                               getter_AddRefs(cursor));
       NS_ENSURE_SUCCESS(rv, false);
       break;
 
     default:
       MOZ_NOT_REACHED("Unknown union type!");
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -1,36 +1,41 @@
 /* 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 "base/basictypes.h"
 
 #include "IndexedDBParent.h"
 
+#include "nsIDOMFile.h"
 #include "nsIDOMEvent.h"
 #include "nsIIDBVersionChangeEvent.h"
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsContentUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBKeyRange.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 
 USING_INDEXEDDB_NAMESPACE
 
+using namespace mozilla::dom;
+
 /*******************************************************************************
  * AutoSetCurrentTransaction
  ******************************************************************************/
 
 AutoSetCurrentTransaction::AutoSetCurrentTransaction(
                                                    IDBTransaction* aTransaction)
 {
   MOZ_ASSERT(aTransaction);
@@ -1178,16 +1183,39 @@ IndexedDBObjectStoreRequestParent::Index
              aRequestType <= ParamsUnionType::T__Last);
 }
 
 IndexedDBObjectStoreRequestParent::~IndexedDBObjectStoreRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestParent);
 }
 
+void
+IndexedDBObjectStoreRequestParent::ConvertBlobActors(
+                                  const InfallibleTArray<PBlobParent*>& aActors,
+                                  nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs)
+{
+  MOZ_ASSERT(aBlobs.IsEmpty());
+
+  if (!aActors.IsEmpty()) {
+    // Walk the chain to get to ContentParent.
+    ContentParent* contentParent =
+      mObjectStore->Transaction()->Database()->GetContentParent();
+    MOZ_ASSERT(contentParent);
+
+    uint32_t length = aActors.Length();
+    aBlobs.SetCapacity(length);
+
+    for (uint32_t index = 0; index < length; index++) {
+      BlobParent* actor = static_cast<BlobParent*>(aActors[index]);
+      aBlobs.AppendElement(actor->GetBlob());
+    }
+  }
+}
+
 bool
 IndexedDBObjectStoreRequestParent::Get(const GetParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
@@ -1249,48 +1277,54 @@ IndexedDBObjectStoreRequestParent::GetAl
 
 bool
 IndexedDBObjectStoreRequestParent::Add(const AddParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams);
 
   ipc::AddPutParams params = aParams.commonParams();
 
+  nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
+  ConvertBlobActors(params.blobsParent(), blobs);
+
   nsRefPtr<IDBRequest> request;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     nsresult rv =
       mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(),
-                                     params.indexUpdateInfos(), false,
+                                     params.indexUpdateInfos(), blobs, false,
                                      getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Put(const PutParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams);
 
   ipc::AddPutParams params = aParams.commonParams();
 
+  nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
+  ConvertBlobActors(params.blobsParent(), blobs);
+
   nsRefPtr<IDBRequest> request;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     nsresult rv =
       mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(),
-                                     params.indexUpdateInfos(), true,
+                                     params.indexUpdateInfos(), blobs, true,
                                      getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
   return true;
 }
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -18,20 +18,22 @@
 
 #include "mozilla/Attributes.h"
 
 #include "nsIDOMEventListener.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
+class PBlobParent;
 class TabParent;
 }
 }
 
+class nsIDOMBlob;
 class nsIDOMEvent;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBIndex;
@@ -526,16 +528,21 @@ public:
   bool
   Clear(const ClearParams& aParams);
 
   bool
   Count(const CountParams& aParams);
 
   bool
   OpenCursor(const OpenCursorParams& aParams);
+
+protected:
+  void
+  ConvertBlobActors(const InfallibleTArray<PBlobParent*>& aActors,
+                    nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs);
 };
 
 /*******************************************************************************
  * IndexedDBIndexRequestParent
  ******************************************************************************/
 
 class IndexedDBIndexRequestParent : public IndexedDBRequestParentBase
 {
--- a/dom/indexedDB/ipc/Makefile.in
+++ b/dom/indexedDB/ipc/Makefile.in
@@ -43,9 +43,9 @@ include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 # Copy all the normal xpcshell tests from the regular unit directory.
 copy-xpcshell-tests:
 	$(call install_cmd,$(wildcard $(topsrcdir)/dom/indexedDB/test/unit/test_*.js) \
 		$(testxpcobjdir)/$(relativesrcdir)/$(XPCSHELL_TESTS))
 
-libs-xpcshell-tests: copy-xpcshell-tests
+libs-xpcshell-tests: copy-xpcshell-tests
\ No newline at end of file
--- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBObjectStore;
 include protocol PIndexedDBRequest;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::IDBCursor::Direction;
@@ -97,16 +98,17 @@ union OptionalStructuredCloneReadInfo
 
 struct IndexCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
   Key objectKey;
   OptionalStructuredCloneReadInfo optionalCloneInfo;
+  PBlob[] blobs;
 };
 
 } // namespace ipc
 
 protocol PIndexedDBIndex
 {
   manager PIndexedDBObjectStore;
 
--- a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBRequest;
 include protocol PIndexedDBTransaction;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
@@ -65,16 +66,17 @@ struct OpenCursorParams
 
 } // namespace FIXME_Bug_521898_objectstore
 
 struct AddPutParams
 {
   SerializedStructuredCloneWriteInfo cloneInfo;
   Key key;
   IndexUpdateInfo[] indexUpdateInfos;
+  PBlob[] blobs;
 };
 
 struct AddParams
 {
   AddPutParams commonParams;
 };
 
 struct PutParams
@@ -121,16 +123,17 @@ union IndexConstructorParams
 };
 
 struct ObjectStoreCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 } // namespace ipc
 
 protocol PIndexedDBObjectStore
 {
   manager PIndexedDBTransaction;
 
--- a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBObjectStore;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
@@ -17,26 +18,33 @@ namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
 struct GetResponse
 {
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 struct GetKeyResponse
 {
   Key key;
 };
 
+struct BlobArray
+{
+  PBlob[] blobs;
+};
+
 struct GetAllResponse
 {
   SerializedStructuredCloneReadInfo[] cloneInfos;
+  BlobArray[] blobs;
 };
 
 struct GetAllKeysResponse
 {
   Key[] keys;
 };
 
 struct AddResponse
@@ -70,16 +78,17 @@ union OpenCursorResponse
   void_t;
 };
 
 struct ContinueResponse
 {
   Key key;
   Key objectKey;
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 union ResponseValue
 {
   nsresult;
   GetResponse;
   GetKeyResponse;
   GetAllResponse;
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -24,16 +24,17 @@ MOCHITEST_FILES = \
   helpers.js \
   leaving_page_iframe.html \
   test_add_put.html \
   test_add_twice_failure.html \
   test_advance.html \
   test_autoIncrement_indexes.html \
   test_autoIncrement.html \
   test_bfcache.html \
+  test_blob_simple.html \
   test_clear.html \
   test_complex_keyPaths.html \
   test_count.html \
   test_create_index.html \
   test_create_index_with_integer_keys.html \
   test_create_objectStore.html \
   test_cursors.html \
   test_cursor_mutation.html \
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_blob_simple.html
@@ -0,0 +1,192 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
+    const INDEX_KEY = 5;
+
+    let request = indexedDB.open(window.location.pathname, 1);
+    request.onerror = errorHandler;
+    request.onupgradeneeded = grabEventAndContinueHandler;
+    request.onsuccess = unexpectedSuccessHandler;
+    let event = yield;
+
+    let db = event.target.result;
+    db.onerror = errorHandler;
+
+    let objectStore = db.createObjectStore("foo", { autoIncrement: true });
+    let index = objectStore.createIndex("foo", "index");
+
+    request.onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let blob = new Blob(BLOB_DATA, { type: "text/plain" });
+    let data = { blob: blob, index: INDEX_KEY };
+
+    objectStore = db.transaction("foo", "readwrite").objectStore("foo");
+    objectStore.add(data).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let key = event.target.result;
+
+    objectStore.add(data).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.get(key).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.mozGetAll().onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.target.result.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let cursorResults = [];
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.openCursor().onsuccess = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        continueToNextStep();
+      }
+    };
+    yield;
+
+    is(cursorResults.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let index = db.transaction("foo").objectStore("foo").index("foo");
+    index.get(INDEX_KEY).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    index.mozGetAll().onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.target.result.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    cursorResults = [];
+
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    index.openCursor().onsuccess = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        continueToNextStep();
+      }
+    };
+    yield;
+
+    is(cursorResults.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[1].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let slice = cursorResults[1].blob.slice(0, BLOB_DATA[0].length);
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(slice);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA[0], "Correct text");
+
+    function workerScript() {
+      onmessage = function(event) {
+        var reader = new FileReaderSync();
+        postMessage(reader.readAsText(event.data));
+
+        var slice = event.data.slice(1, 2);
+        postMessage(reader.readAsText(slice));
+
+      }
+    }
+
+    let url =
+      URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
+
+    let worker = new Worker(url);
+    worker.postMessage(slice);
+    worker.onmessage = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.data, BLOB_DATA[0], "Correct text");
+    event = yield;
+
+    is(event.data, BLOB_DATA[0][1], "Correct text");
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="file.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/ipc/Blob.cpp
@@ -0,0 +1,797 @@
+/* -*- 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/. */
+
+#include "base/basictypes.h"
+
+#include "Blob.h"
+
+#include "nsIDOMFile.h"
+#include "nsIInputStream.h"
+#include "nsIRemoteBlob.h"
+#include "nsISeekableStream.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "nsDOMFile.h"
+#include "nsThreadUtils.h"
+
+#include "ContentChild.h"
+#include "ContentParent.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
+
+namespace {
+
+class RemoteInputStream : public nsIInputStream,
+                          public nsISeekableStream
+{
+  mozilla::Monitor mMonitor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsCOMPtr<nsIInputStream> mStream;
+  nsCOMPtr<nsISeekableStream> mSeekableStream;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  RemoteInputStream(nsIDOMBlob* aSourceBlob)
+  : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aSourceBlob);
+  }
+
+  void
+  SetStream(nsIInputStream* aStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aStream);
+
+    nsCOMPtr<nsIInputStream> stream = aStream;
+    nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+
+    {
+      mozilla::MonitorAutoLock lock(mMonitor);
+
+      MOZ_ASSERT(!mStream);
+      MOZ_ASSERT(!mSeekableStream);
+
+      mStream.swap(stream);
+      mSeekableStream.swap(seekableStream);
+
+      mMonitor.Notify();
+    }
+  }
+
+  NS_IMETHOD
+  Close() MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIDOMBlob> sourceBlob;
+    mSourceBlob.swap(sourceBlob);
+
+    rv = mStream->Close();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Available(PRUint32* aAvailable) MOZ_OVERRIDE
+  {
+    // See large comment in FileInputStreamWrapper::Available.
+    if (NS_IsMainThread()) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->Available(aAvailable);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Read(char* aBuffer, PRUint32 aCount, PRUint32* aResult) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->Read(aBuffer, aCount, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, PRUint32 aCount,
+               PRUint32* aResult) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  IsNonBlocking(bool* aNonBlocking) MOZ_OVERRIDE
+  {
+    NS_ENSURE_ARG_POINTER(aNonBlocking);
+
+    *aNonBlocking = false;
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Seek(PRInt32 aWhence, PRInt64 aOffset) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->Seek(aWhence, aOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Tell(PRInt64* aResult)
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->Tell(aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  SetEOF()
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->SetEOF();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  virtual ~RemoteInputStream()
+  { }
+
+  void
+  ReallyBlockAndWaitForStream()
+  {
+    mozilla::MonitorAutoLock lock(mMonitor);
+    while (!mStream) {
+      mMonitor.Wait();
+    }
+  }
+
+  nsresult
+  BlockAndWaitForStream()
+  {
+    if (NS_IsMainThread()) {
+      NS_WARNING("Blocking the main thread is not supported!");
+      return NS_ERROR_FAILURE;
+    }
+
+    ReallyBlockAndWaitForStream();
+    return NS_OK;
+  }
+
+  bool
+  IsSeekableStream()
+  {
+    if (NS_IsMainThread()) {
+      if (!mStream) {
+        NS_WARNING("Don't know if this stream is seekable yet!");
+        return true;
+      }
+    }
+    else {
+      ReallyBlockAndWaitForStream();
+    }
+
+    return !!mSeekableStream;
+  }
+};
+
+template <ActorFlavorEnum ActorFlavor>
+class InputStreamActor : public BlobTraits<ActorFlavor>::StreamType
+{
+  typedef typename BlobTraits<ActorFlavor>::StreamType::InputStream InputStream;
+  nsRefPtr<RemoteInputStream> mRemoteStream;
+
+public:
+  InputStreamActor(RemoteInputStream* aRemoteStream)
+  : mRemoteStream(aRemoteStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aRemoteStream);
+  }
+
+  InputStreamActor()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+private:
+  // This method is only called by the IPDL message machinery.
+  virtual bool
+  Recv__delete__(const InputStream& aStream) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mRemoteStream);
+
+    mRemoteStream->SetStream(aStream);
+    return true;
+  }
+};
+
+template <ActorFlavorEnum ActorFlavor>
+inline
+already_AddRefed<nsIDOMBlob>
+GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
+{
+  MOZ_STATIC_ASSERT(ActorFlavor == mozilla::dom::ipc::Parent,
+                    "No other flavor is supported here!");
+
+  BlobParent* actor =
+    const_cast<BlobParent*>(
+      static_cast<const BlobParent*>(aParams.sourceParent()));
+  MOZ_ASSERT(actor);
+
+  return actor->GetBlob();
+}
+
+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;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+template <ActorFlavorEnum ActorFlavor>
+class RemoteBlob : public nsDOMFile,
+                   public nsIRemoteBlob
+{
+public:
+  typedef RemoteBlob<ActorFlavor> SelfType;
+  typedef Blob<ActorFlavor> ActorType;
+  typedef InputStreamActor<ActorFlavor> StreamActorType;
+
+private:
+  ActorType* mActor;
+
+  class StreamHelper : public nsRunnable
+  {
+    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;
+      }
+
+      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);
+
+      StreamActorType* streamActor = new StreamActorType(stream);
+      if (mActor->SendPBlobStreamConstructor(streamActor)) {
+        stream.swap(mInputStream);
+      }
+
+      mActor = nullptr;
+
+      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;
+    PRUint64 mStart;
+    PRUint64 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);
+    }
+
+    nsresult
+    GetSlice(PRUint64 aStart, PRUint64 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);
+
+      NormalBlobConstructorParams normalParams;
+      normalParams.contentType() = mContentType;
+      normalParams.length() = mLength;
+
+      ActorType* newActor = ActorType::Create(normalParams);
+      MOZ_ASSERT(newActor);
+
+      SlicedBlobConstructorParams slicedParams;
+      slicedParams.contentType() = mContentType;
+      slicedParams.begin() = mStart;
+      slicedParams.end() = mStart + mLength;
+      SetBlobOnParams(mActor, slicedParams);
+
+      if (mActor->Manager()->SendPBlobConstructor(newActor, slicedParams)) {
+        mSlice = newActor->GetBlob();
+      }
+
+      mActor = nullptr;
+
+      if (aNotify) {
+        MonitorAutoLock lock(mMonitor);
+        mDone = true;
+        lock.Notify();
+      }
+      else {
+        mDone = true;
+      }
+    }
+  };
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  RemoteBlob(const nsAString& aName, const nsAString& aContentType,
+              PRUint64 aLength)
+  : nsDOMFile(aName, aContentType, aLength), mActor(nullptr)
+  { }
+
+  RemoteBlob(const nsAString& aContentType, PRUint64 aLength)
+  : nsDOMFile(aContentType, aLength), mActor(nullptr)
+  { }
+
+  virtual ~RemoteBlob()
+  {
+    if (mActor) {
+      mActor->NoteDyingRemoteBlob();
+    }
+  }
+
+  void
+  SetActor(ActorType* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE
+  {
+    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_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
+  {
+    if (!mActor) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+    return helper->GetStream(aStream);
+  }
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE
+  {
+    return static_cast<typename ActorType::BaseType*>(mActor);
+  }
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>::Blob(nsIDOMBlob* aBlob)
+: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true)
+{
+  MOZ_ASSERT(aBlob);
+  aBlob->AddRef();
+}
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>::Blob(const BlobConstructorParams& aParams)
+: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(true)
+{
+  nsRefPtr<RemoteBlobType> remoteBlob;
+
+  switch (aParams.type()) {
+    case BlobConstructorParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        aParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlobType(params.contentType(), params.length());
+      break;
+    }
+
+    case BlobConstructorParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        aParams.get_FileBlobConstructorParams();
+      remoteBlob =
+        new RemoteBlobType(params.name(), params.contentType(),
+                           params.length());
+      break;
+    }
+
+    default:
+      MOZ_NOT_REACHED("Unknown params!");
+  }
+
+  MOZ_ASSERT(remoteBlob);
+
+  if (NS_FAILED(remoteBlob->SetMutable(false))) {
+    MOZ_NOT_REACHED("Failed to make remote blob immutable!");
+  }
+
+  remoteBlob->SetActor(this);
+  remoteBlob.forget(&mRemoteBlob);
+
+  mBlob = mRemoteBlob;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>*
+Blob<ActorFlavor>::Create(const BlobConstructorParams& aParams)
+{
+  switch (aParams.type()) {
+    case BlobConstructorParams::TNormalBlobConstructorParams:
+    case BlobConstructorParams::TFileBlobConstructorParams:
+      return new Blob<ActorFlavor>(aParams);
+
+    case BlobConstructorParams::TSlicedBlobConstructorParams: {
+      const SlicedBlobConstructorParams& params =
+        aParams.get_SlicedBlobConstructorParams();
+
+      nsCOMPtr<nsIDOMBlob> source = GetBlobFromParams<ActorFlavor>(params);
+      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>(slice);
+    }
+
+    default:
+      MOZ_NOT_REACHED("Unknown params!");
+  }
+
+  return nullptr;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+already_AddRefed<nsIDOMBlob>
+Blob<ActorFlavor>::GetBlob()
+{
+  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();
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::NoteDyingRemoteBlob()
+{
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(!mOwnsBlob);
+
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewNonOwningRunnableMethod(this,
+                                    &Blob<ActorFlavor>::NoteDyingRemoteBlob);
+    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+      MOZ_NOT_REACHED("Should never fail!");
+    }
+
+    return;
+  }
+
+  // Must do this before calling Send__delete__ or we'll crash there trying to
+  // access a dangling pointer.
+  mRemoteBlob = nullptr;
+
+  BaseType::Send__delete__(this);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(mBlob);
+
+  if (mRemoteBlob) {
+    mRemoteBlob->SetActor(nullptr);
+  }
+
+  if (mOwnsBlob) {
+    mBlob->Release();
+  }
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::RecvPBlobStreamConstructor(StreamType* aActor)
+{
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return aActor->Send__delete__(aActor, stream.get());
+}
+
+template <ActorFlavorEnum ActorFlavor>
+typename Blob<ActorFlavor>::StreamType*
+Blob<ActorFlavor>::AllocPBlobStream()
+{
+  return new InputStreamActor<ActorFlavor>();
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::DeallocPBlobStream(StreamType* aActor)
+{
+  delete aActor;
+  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)
+
+NS_IMPL_THREADSAFE_ADDREF(RemoteInputStream)
+NS_IMPL_THREADSAFE_RELEASE(RemoteInputStream)
+
+NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+// Explicit instantiation of both classes.
+template class Blob<Parent>;
+template class Blob<Child>;
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/Blob.h
@@ -0,0 +1,120 @@
+/* -*- 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 "mozilla/dom/PContentChild.h"
+#include "mozilla/dom/PContentParent.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+
+class nsIDOMBlob;
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+enum ActorFlavorEnum
+{
+  Parent = 0,
+  Child
+};
+
+template <ActorFlavorEnum>
+struct BlobTraits
+{ };
+
+template <>
+struct BlobTraits<Parent>
+{
+  typedef mozilla::dom::PBlobParent BaseType;
+  typedef mozilla::dom::PBlobStreamParent StreamType;
+  typedef mozilla::dom::PContentParent ManagerType;
+};
+
+template <>
+struct BlobTraits<Child>
+{
+  typedef mozilla::dom::PBlobChild BaseType;
+  typedef mozilla::dom::PBlobStreamChild StreamType;
+  typedef mozilla::dom::PContentChild ManagerType;
+};
+
+template <ActorFlavorEnum>
+class RemoteBlob;
+
+template <ActorFlavorEnum ActorFlavor>
+class Blob : public BlobTraits<ActorFlavor>::BaseType
+{
+public:
+  typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
+  typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
+  typedef typename BlobTraits<ActorFlavor>::ManagerType ManagerType;
+  typedef RemoteBlob<ActorFlavor> RemoteBlobType;
+  typedef mozilla::ipc::IProtocolManager<
+                      mozilla::ipc::RPCChannel::RPCListener>::ActorDestroyReason
+          ActorDestroyReason;
+  typedef mozilla::dom::BlobConstructorParams BlobConstructorParams;
+
+protected:
+  nsIDOMBlob* mBlob;
+  RemoteBlobType* mRemoteBlob;
+  bool mOwnsBlob;
+
+public:
+  // This create function is called on the sending side.
+  static Blob*
+  Create(nsIDOMBlob* aBlob)
+  {
+    return new Blob(aBlob);
+  }
+
+  // This create function is called on the receiving side.
+  static Blob*
+  Create(const BlobConstructorParams& aParams);
+
+  already_AddRefed<nsIDOMBlob>
+  GetBlob();
+
+  void
+  NoteDyingRemoteBlob();
+
+private:
+  // This constructor is called on the sending side.
+  Blob(nsIDOMBlob* aBlob);
+
+  // This constructor is called on the receiving side.
+  Blob(const BlobConstructorParams& aParams);
+
+  // These methods are only called by the IPDL message machinery.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBlobStreamConstructor(StreamType* aActor) MOZ_OVERRIDE;
+
+  virtual StreamType*
+  AllocPBlobStream() MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBlobStream(StreamType* aActor) MOZ_OVERRIDE;
+};
+
+} // namespace ipc
+
+typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Child> BlobChild;
+typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Parent> BlobParent;
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_Blob_h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -76,19 +76,23 @@
 #include <process.h>
 #define getpid _getpid
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 
+#include "mozilla/dom/indexedDB/PIndexedDBChild.h"
 #include "mozilla/dom/sms/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
-#include "mozilla/dom/indexedDB/PIndexedDBChild.h"
+
+#include "nsIDOMFile.h"
+#include "nsIRemoteBlob.h"
+#include "StructuredCloneUtils.h"
 
 using namespace mozilla::docshell;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
@@ -385,17 +389,17 @@ ContentChild::RecvPMemoryReportRequestCo
 bool
 ContentChild::DeallocPMemoryReportRequest(PMemoryReportRequestChild* actor)
 {
     delete actor;
     return true;
 }
 
 PCompositorChild*
-ContentChild::AllocPCompositor(ipc::Transport* aTransport,
+ContentChild::AllocPCompositor(mozilla::ipc::Transport* aTransport,
                                base::ProcessId aOtherProcess)
 {
     return CompositorChild::Create(aTransport, aOtherProcess);
 }
 
 PBrowserChild*
 ContentChild::AllocPBrowser(const PRUint32& aChromeFlags,
                             const bool& aIsBrowserElement,
@@ -408,16 +412,81 @@ ContentChild::AllocPBrowser(const PRUint
 bool
 ContentChild::DeallocPBrowser(PBrowserChild* iframe)
 {
     TabChild* child = static_cast<TabChild*>(iframe);
     NS_RELEASE(child);
     return true;
 }
 
+PBlobChild*
+ContentChild::AllocPBlob(const BlobConstructorParams& aParams)
+{
+  return BlobChild::Create(aParams);
+}
+
+bool
+ContentChild::DeallocPBlob(PBlobChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+BlobChild*
+ContentChild::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(aBlob, "Null pointer!");
+
+  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
+  if (remoteBlob) {
+    BlobChild* actor =
+      static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
+    NS_ASSERTION(actor, "Null actor?!");
+
+    return actor;
+  }
+
+  BlobConstructorParams params;
+
+  nsString contentType;
+  nsresult rv = aBlob->GetType(contentType);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  PRUint64 length;
+  rv = aBlob->GetSize(&length);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+  if (file) {
+    FileBlobConstructorParams fileParams;
+
+    rv = file->GetName(fileParams.name());
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    fileParams.contentType() = contentType;
+    fileParams.length() = length;
+
+    params = fileParams;
+  } else {
+    NormalBlobConstructorParams blobParams;
+    blobParams.contentType() = contentType;
+    blobParams.length() = length;
+    params = blobParams;
+  }
+
+  BlobChild* actor = BlobChild::Create(aBlob);
+  NS_ENSURE_TRUE(actor, nullptr);
+
+  if (!SendPBlobConstructor(actor, params)) {
+    return nullptr;
+  }
+
+  return actor;
+}
+
 PCrashReporterChild*
 ContentChild::AllocPCrashReporter(const mozilla::dom::NativeThreadId& id,
                                   const PRUint32& processType)
 {
 #ifdef MOZ_CRASHREPORTER
     return new CrashReporterChild();
 #else
     return nullptr;
@@ -699,24 +768,40 @@ ContentChild::RecvNotifyAlertsObserver(c
 bool
 ContentChild::RecvNotifyVisited(const IPC::URI& aURI)
 {
     nsCOMPtr<nsIURI> newURI(aURI);
     History::GetService()->NotifyVisited(newURI);
     return true;
 }
 
-
 bool
-ContentChild::RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON)
+ContentChild::RecvAsyncMessage(const nsString& aMsg,
+                                     const ClonedMessageData& aData)
 {
   nsRefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::sChildProcessManager;
   if (cpm) {
+    const SerializedStructuredCloneBuffer& buffer = aData.data();
+    const InfallibleTArray<PBlobChild*>& blobChildList = aData.blobsChild();
+    StructuredCloneData cloneData;
+    cloneData.mData = buffer.data;
+    cloneData.mDataLength = buffer.dataLength;
+    if (!blobChildList.IsEmpty()) {
+      PRUint32 length = blobChildList.Length();
+      cloneData.mClosure.mBlobs.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = static_cast<BlobChild*>(blobChildList[i]);
+        MOZ_ASSERT(blobChild);
+        nsCOMPtr<nsIDOMBlob> blob = blobChild->GetBlob();
+        MOZ_ASSERT(blob);
+        cloneData.mClosure.mBlobs.AppendElement(blob);
+      }
+    }
     cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
-                        aMsg, false, aJSON, nullptr, nullptr);
+                        aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
 ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
 {
   nsCOMPtr<nsIGeolocationUpdate> gs = do_GetService("@mozilla.org/geolocation/service;1");
@@ -822,9 +907,9 @@ bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
     return true;
 }
 
 } // namespace dom
-} // namespace mozilla
+} // namespace mozilla
\ No newline at end of file
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -4,41 +4,45 @@
  * 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_ContentChild_h
 #define mozilla_dom_ContentChild_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PContentChild.h"
+#include "mozilla/dom/ipc/Blob.h"
 
 #include "nsTArray.h"
 #include "nsIConsoleListener.h"
 
 struct ChromePackage;
+class nsIDOMBlob;
 class nsIObserver;
 struct ResourceMapping;
 struct OverrideMapping;
 
 namespace mozilla {
 
 namespace layers {
 class PCompositorChild;
 }
 
 namespace dom {
 
 class AlertObserver;
 class PrefObserver;
 class ConsoleListener;
 class PStorageChild;
+class ClonedMessageData;
 
 class ContentChild : public PContentChild
 {
     typedef layers::PCompositorChild PCompositorChild;
+    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
     ContentChild();
     virtual ~ContentChild();
 
     struct AppInfo
     {
         nsCString version;
@@ -54,27 +58,30 @@ public:
         NS_ASSERTION(sSingleton, "not initialized");
         return sSingleton;
     }
 
     const AppInfo& GetAppInfo() {
         return mAppInfo;
     }
 
-    PCompositorChild* AllocPCompositor(ipc::Transport* aTransport,
+    PCompositorChild* AllocPCompositor(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual PBrowserChild* AllocPBrowser(const PRUint32& aChromeFlags,
                                          const bool& aIsBrowserElement,
                                          const PRUint32& aAppId);
     virtual bool DeallocPBrowser(PBrowserChild*);
 
     virtual PDeviceStorageRequestChild* AllocPDeviceStorageRequest(const DeviceStorageParams&);
     virtual bool DeallocPDeviceStorageRequest(PDeviceStorageRequestChild*);
 
+    virtual PBlobChild* AllocPBlob(const BlobConstructorParams& aParams);
+    virtual bool DeallocPBlob(PBlobChild*);
+
     virtual PCrashReporterChild*
     AllocPCrashReporter(const mozilla::dom::NativeThreadId& id,
                         const PRUint32& processType);
     virtual bool
     DeallocPCrashReporter(PCrashReporterChild*);
 
     virtual PHalChild* AllocPHal() MOZ_OVERRIDE;
     virtual bool DeallocPHal(PHalChild*) MOZ_OVERRIDE;
@@ -129,17 +136,18 @@ public:
     // auto remove when alertfinished is received.
     nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
 
     virtual bool RecvPreferenceUpdate(const PrefTuple& aPref);
     virtual bool RecvClearUserPreference(const nsCString& aPrefName);
 
     virtual bool RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData);
 
-    virtual bool RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON);
+    virtual bool RecvAsyncMessage(const nsString& aMsg,
+                                  const ClonedMessageData& aData);
 
     virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere);
 
     virtual bool RecvAddPermission(const IPC::Permission& permission);
 
     virtual bool RecvScreenSizeChanged(const gfxIntSize &size);
 
     virtual bool RecvFlushMemory(const nsString& reason);
@@ -159,16 +167,18 @@ public:
 #endif
 
     // Get the directory for IndexedDB files. We query the parent for this and
     // cache the value
     nsString &GetIndexedDBPath();
 
     PRUint64 GetID() { return mID; }
 
+    BlobChild* GetOrCreateActorForBlob(nsIDOMBlob* aBlob);
+
 private:
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
     virtual void ProcessingError(Result what) MOZ_OVERRIDE;
 
     /**
      * Exit *now*.  Do not shut down XPCOM, do not pass Go, do not run
      * static destructors, do not collect $200.
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1,22 +1,23 @@
 /* -*- 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/. */
 
 #include "base/basictypes.h"
 
+#include "ContentParent.h"
+
 #if defined(ANDROID) || defined(LINUX)
 # include <sys/time.h>
 # include <sys/resource.h>
 #endif
 
-#include "ContentParent.h"
 #include "CrashReporterParent.h"
 #include "History.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
@@ -42,32 +43,35 @@
 #include "nsDebugImpl.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExternalHelperAppService.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIClipboard.h"
 #include "nsIConsoleService.h"
+#include "nsIDOMFile.h"
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMWindow.h"
 #include "nsIFilePicker.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
+#include "nsIRemoteBlob.h"
 #include "nsIScriptError.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWindowWatcher.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSystemInfo.h"
 #include "nsThreadUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsWidgetsCID.h"
 #include "SandboxHal.h"
+#include "StructuredCloneUtils.h"
 #include "TabParent.h"
 
 #ifdef ANDROID
 # include "gfxAndroidPlatform.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 # include "nsExceptionHandler.h"
@@ -822,17 +826,17 @@ ContentParent::Observe(nsISupports* aSub
         unused << SendActivateA11y();
     }
 #endif
 
     return NS_OK;
 }
 
 PCompositorParent*
-ContentParent::AllocPCompositor(ipc::Transport* aTransport,
+ContentParent::AllocPCompositor(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherProcess)
 {
     return CompositorParent::Create(aTransport, aOtherProcess);
 }
 
 PBrowserParent*
 ContentParent::AllocPBrowser(const PRUint32& aChromeFlags,
                              const bool& aIsBrowserElement,
@@ -861,16 +865,82 @@ ContentParent::AllocPDeviceStorageReques
 
 bool
 ContentParent::DeallocPDeviceStorageRequest(PDeviceStorageRequestParent* doomed)
 {
   delete doomed;
   return true;
 }
 
+PBlobParent*
+ContentParent::AllocPBlob(const BlobConstructorParams& aParams)
+{
+  return BlobParent::Create(aParams);
+}
+
+bool
+ContentParent::DeallocPBlob(PBlobParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+BlobParent*
+ContentParent::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(aBlob, "Null pointer!");
+
+  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
+  if (remoteBlob) {
+    BlobParent* actor =
+      static_cast<BlobParent*>(
+        static_cast<PBlobParent*>(remoteBlob->GetPBlob()));
+    NS_ASSERTION(actor, "Null actor?!");
+
+    return actor;
+  }
+
+  BlobConstructorParams params;
+
+  nsString contentType;
+  nsresult rv = aBlob->GetType(contentType);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  PRUint64 length;
+  rv = aBlob->GetSize(&length);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+  if (file) {
+    FileBlobConstructorParams fileParams;
+
+    rv = file->GetName(fileParams.name());
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    fileParams.contentType() = contentType;
+    fileParams.length() = length;
+
+    params = fileParams;
+  } else {
+    NormalBlobConstructorParams blobParams;
+    blobParams.contentType() = contentType;
+    blobParams.length() = length;
+    params = blobParams;
+  }
+
+  BlobParent* actor = BlobParent::Create(aBlob);
+  NS_ENSURE_TRUE(actor, nullptr);
+
+  if (!SendPBlobConstructor(actor, params)) {
+    return nullptr;
+  }
+
+  return actor;
+}
+
 PCrashReporterParent*
 ContentParent::AllocPCrashReporter(const NativeThreadId& tid,
                                    const PRUint32& processType)
 {
 #ifdef MOZ_CRASHREPORTER
   return new CrashReporterParent();
 #else
   return nullptr;
@@ -925,17 +995,17 @@ ContentParent::RecvPIndexedDBConstructor
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, false);
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     NS_RUNTIMEABORT("Not supported yet!");
   }
 
   nsRefPtr<IDBFactory> factory;
-  nsresult rv = IDBFactory::Create(getter_AddRefs(factory));
+  nsresult rv = IDBFactory::Create(this, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, false);
 
   NS_ASSERTION(factory, "This should never be null!");
 
   IndexedDBParent* actor = static_cast<IndexedDBParent*>(aActor);
   actor->mFactory = factory;
   actor->mASCIIOrigin = factory->GetASCIIOrigin();
 
@@ -1286,34 +1356,69 @@ ContentParent::RecvShowAlertNotification
         sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable,
                                          aCookie, this, aName);
     }
 
     return true;
 }
 
 bool
-ContentParent::RecvSyncMessage(const nsString& aMsg, const nsString& aJSON,
+ContentParent::RecvSyncMessage(const nsString& aMsg,
+                               const ClonedMessageData& aData,
                                InfallibleTArray<nsString>* aRetvals)
 {
   nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
+    const SerializedStructuredCloneBuffer& buffer = aData.data();
+    const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
+    StructuredCloneData cloneData;
+    cloneData.mData = buffer.data;
+    cloneData.mDataLength = buffer.dataLength;
+    if (!blobParents.IsEmpty()) {
+      PRUint32 length = blobParents.Length();
+      cloneData.mClosure.mBlobs.SetCapacity(length);
+      for (PRUint32 index = 0; index < length; index++) {
+        BlobParent* blobParent = static_cast<BlobParent*>(blobParents[index]);
+        MOZ_ASSERT(blobParent);
+        nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
+        MOZ_ASSERT(blob);
+        cloneData.mClosure.mBlobs.AppendElement(blob);
+  }
+    }
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
-                        aMsg,true, aJSON, nullptr, aRetvals);
+                        aMsg, true, &cloneData, nullptr, aRetvals);
   }
   return true;
 }
 
 bool
-ContentParent::RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON)
+ContentParent::RecvAsyncMessage(const nsString& aMsg,
+                                      const ClonedMessageData& aData)
 {
   nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
+    const SerializedStructuredCloneBuffer& buffer = aData.data();
+    const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
+    StructuredCloneData cloneData;
+    cloneData.mData = buffer.data;
+    cloneData.mDataLength = buffer.dataLength;
+    if (!blobParents.IsEmpty()) {
+      PRUint32 length = blobParents.Length();
+      cloneData.mClosure.mBlobs.SetCapacity(length);
+      for (PRUint32 index = 0; index < length; index++) {
+        BlobParent* blobParent = static_cast<BlobParent*>(blobParents[index]);
+        MOZ_ASSERT(blobParent);
+        nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
+        MOZ_ASSERT(blob);
+        cloneData.mClosure.mBlobs.AppendElement(blob);
+      }
+    }
+
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
-                        aMsg, false, aJSON, nullptr, nullptr);
+                        aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
 ContentParent::RecvAddGeolocationListener()
 {
   if (mGeolocationWatchID == -1) {
@@ -1398,9 +1503,9 @@ ContentParent::RecvPrivateDocShellsExist
       delete gPrivateContent;
       gPrivateContent = NULL;
     }
   }
   return true;
 }
 
 } // namespace dom
-} // namespace mozilla
+} // namespace mozilla
\ No newline at end of file
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -7,51 +7,57 @@
 #ifndef mozilla_dom_ContentParent_h
 #define mozilla_dom_ContentParent_h
 
 #include "base/waitable_event_watcher.h"
 
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/dom/ipc/Blob.h"
 
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIMemoryReporter.h"
 #include "nsCOMArray.h"
 #include "nsDataHashtable.h"
+#include "nsHashKeys.h"
 
 class nsFrameMessageManager;
+class nsIDOMBlob;
+
 namespace mozilla {
 
 namespace ipc {
 class TestShellParent;
 }
 
 namespace layers {
 class PCompositorParent;
 }
 
 namespace dom {
 
 class TabParent;
 class PStorageParent;
+class ClonedMessageData;
 
 class ContentParent : public PContentParent
                     , public nsIObserver
                     , public nsIThreadObserver
                     , public nsIDOMGeoPositionCallback
 {
 private:
     typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
     typedef mozilla::ipc::TestShellParent TestShellParent;
     typedef mozilla::layers::PCompositorParent PCompositorParent;
+    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
     static ContentParent* GetNewOrUsed();
 
     /**
      * Get or create a content process for the given app.  A given app
      * (identified by its manifest URL) gets one process all to itself.
      *
@@ -92,16 +98,18 @@ public:
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     bool NeedsPermissionsUpdate() {
         return mSendPermissionUpdates;
     }
 
+    BlobParent* GetOrCreateActorForBlob(nsIDOMBlob* aBlob);
+
 protected:
     void OnChannelConnected(int32 pid);
     virtual void ActorDestroy(ActorDestroyReason why);
 
 private:
     static nsDataHashtable<nsStringHashKey, ContentParent*> *gAppContentParents;
     static nsTArray<ContentParent*>* gNonAppContentParents;
     static nsTArray<ContentParent*>* gPrivateContent;
@@ -125,25 +133,28 @@ private:
     /**
      * Exit the subprocess and vamoose.  After this call IsAlive()
      * will return false and this ContentParent will not be returned
      * by the Get*() funtions.  However, the shutdown sequence itself
      * may be asynchronous.
      */
     void ShutDown();
 
-    PCompositorParent* AllocPCompositor(ipc::Transport* aTransport,
+    PCompositorParent* AllocPCompositor(mozilla::ipc::Transport* aTransport,
                                         base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual PBrowserParent* AllocPBrowser(const PRUint32& aChromeFlags, const bool& aIsBrowserElement, const PRUint32& aAppId);
     virtual bool DeallocPBrowser(PBrowserParent* frame);
 
     virtual PDeviceStorageRequestParent* AllocPDeviceStorageRequest(const DeviceStorageParams&);
     virtual bool DeallocPDeviceStorageRequest(PDeviceStorageRequestParent*);
 
+    virtual PBlobParent* AllocPBlob(const BlobConstructorParams& aParams);
+    virtual bool DeallocPBlob(PBlobParent*);
+
     virtual PCrashReporterParent* AllocPCrashReporter(const NativeThreadId& tid,
                                                       const PRUint32& processType);
     virtual bool DeallocPCrashReporter(PCrashReporterParent* crashreporter);
     virtual bool RecvPCrashReporterConstructor(PCrashReporterParent* actor,
                                                const NativeThreadId& tid,
                                                const PRUint32& processType);
 
     virtual PHalParent* AllocPHal() MOZ_OVERRIDE;
@@ -221,19 +232,21 @@ private:
                                     nsresult* result);
  
     virtual bool RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                            const nsString& aText, const bool& aTextClickable,
                                            const nsString& aCookie, const nsString& aName);
 
     virtual bool RecvLoadURIExternal(const IPC::URI& uri);
 
-    virtual bool RecvSyncMessage(const nsString& aMsg, const nsString& aJSON,
+    virtual bool RecvSyncMessage(const nsString& aMsg,
+                                 const ClonedMessageData& aData,
                                  InfallibleTArray<nsString>* aRetvals);
-    virtual bool RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON);
+    virtual bool RecvAsyncMessage(const nsString& aMsg,
+                                  const ClonedMessageData& aData);
 
     virtual bool RecvAddGeolocationListener();
     virtual bool RecvRemoveGeolocationListener();
 
     virtual bool RecvConsoleMessage(const nsString& aMessage);
     virtual bool RecvScriptError(const nsString& aMessage,
                                  const nsString& aSourceName,
                                  const nsString& aSourceLine,
new file mode 100644
--- /dev/null
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBlob;
+
+using mozilla::SerializedStructuredCloneBuffer;
+
+namespace mozilla {
+namespace dom {
+
+struct ClonedMessageData
+{
+  SerializedStructuredCloneBuffer data;
+  PBlob[] blobs;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -16,35 +16,46 @@ FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 TEST_DIRS += tests
 endif
 
 EXPORTS = PCOMContentPermissionRequestChild.h
 
-EXPORTS_NAMESPACES = mozilla/dom
+EXPORTS_NAMESPACES = \
+  mozilla/dom \
+  mozilla/dom/ipc \
+  $(NULL)
 
 EXPORTS_mozilla/dom = \
   ContentChild.h \
   ContentParent.h \
   ContentProcess.h \
   CrashReporterChild.h \
   CrashReporterParent.h \
+  StructuredCloneUtils.h \
   TabParent.h \
   TabChild.h \
   TabMessageUtils.h \
   $(NULL)
 
+EXPORTS_mozilla/dom/ipc = \
+  Blob.h \
+  nsIRemoteBlob.h \
+  $(NULL)
+
 CPPSRCS = \
+  Blob.cpp \
   ContentProcess.cpp \
   ContentParent.cpp \
   ContentChild.cpp \
   CrashReporterParent.cpp \
   CrashReporterChild.cpp \
+  StructuredCloneUtils.cpp \
   TabParent.cpp \
   TabChild.cpp \
   TabMessageUtils.cpp \
   $(NULL)
 
 ifdef MOZ_SYDNEYAUDIO
 EXPORTS_mozilla/dom += \
   AudioChild.h \
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PBlob.ipdl
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBlobStream;
+include protocol PContent;
+
+namespace mozilla {
+namespace dom {
+
+protocol PBlob
+{
+  manager PContent;
+  manages PBlobStream;
+
+both:
+  __delete__();
+
+  PBlobStream();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PBlobStream.ipdl
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBlob;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using IPC::InputStream;
+
+namespace mozilla {
+namespace dom {
+
+protocol PBlobStream
+{
+  manager PBlob;
+
+both:
+  __delete__(InputStream stream);
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -1,29 +1,32 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PContent;
 include protocol PContentDialog;
 include protocol PDocumentRenderer;
 include protocol PContentPermissionRequest;
 include protocol PRenderFrame;
 include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
 
 include "gfxMatrix.h";
 include "IPC/nsGUIEventIPC.h";
 include "mozilla/dom/TabMessageUtils.h";
 include "mozilla/layout/RenderFrameUtils.h";
 include "mozilla/net/NeckoMessageUtils.h";
 
+include DOMTypes;
+
 using IPC::URI;
 using gfxMatrix;
 using gfxSize;
 using mozilla::layers::LayersBackend;
 using mozilla::layout::ScrollingBehavior;
 using mozilla::WindowsHandle;
 using nscolor;
 using nsCompositionEvent;
@@ -51,30 +54,30 @@ rpc protocol PBrowser
     manages PContentDialog;
     manages PDocumentRenderer;
     manages PContentPermissionRequest;
     manages PRenderFrame;
     manages POfflineCacheUpdate;
     manages PIndexedDB;
 
 both:
-    AsyncMessage(nsString aMessage, nsString aJSON);
+    AsyncMessage(nsString aMessage, ClonedMessageData aData);
 
 parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
      */
     MoveFocus(bool forward);
 
     Event(RemoteDOMEvent aEvent);
 
     rpc CreateWindow() returns (PBrowser window);
 
-    sync SyncMessage(nsString aMessage, nsString aJSON)
+    sync SyncMessage(nsString aMessage, ClonedMessageData aData)
       returns (nsString[] retval);
 
     /**
      * The IME sequence number (seqno) parameter is used to make sure
      * that a notification is discarded if it arrives at the chrome process
      * too late. If the notification is late and we accept it, we will have
      * an out-of-date view of the content process, which means events that we
      * dispatch based on this out-of-date view will be wrong also.
@@ -334,9 +337,9 @@ state LIVE:
 state DYING:
     discard send blah;
 // etc.
     recv __delete__;
  */
 };
 
 }
-}
+}
\ No newline at end of file
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PAudio;
+include protocol PBlob;
 include protocol PBrowser;
 include protocol PCompositor;
 include protocol PCrashReporter;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PHal;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
@@ -20,25 +21,28 @@ include protocol PTestShell;
 
 include "mozilla/chrome/RegistryMessageUtils.h";
 include "mozilla/net/NeckoMessageUtils.h";
 include "mozilla/dom/TabMessageUtils.h";
 
 include "nsGeoPositionIPCSerialiser.h";
 include "PPrefTuple.h";
 
+include DOMTypes;
+
 using GeoPosition;
 using PrefTuple;
 
 using ChromePackage;
 using ResourceMapping;
 using OverrideMapping;
 using IPC::URI;
 using IPC::Permission;
 using mozilla::null_t;
+using mozilla::void_t;
 using mozilla::dom::NativeThreadId;
 using gfxIntSize;
 
 namespace mozilla {
 namespace dom {
 
 // Data required to clone an existing DOMStorageImpl in the parent
 struct StorageClone
@@ -91,21 +95,51 @@ struct DeviceStorageEnumerationParams
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
   DeviceStorageEnumerationParams;
 };
+
+struct NormalBlobConstructorParams
+{
+  nsString contentType;
+  uint64_t length;
+};
+
+struct FileBlobConstructorParams
+{
+  nsString name;
+  nsString contentType;
+  uint64_t length;
+};
+
+struct SlicedBlobConstructorParams
+{
+  PBlob source;
+  uint64_t begin;
+  uint64_t end;
+  nsString contentType;
+};
+
+union BlobConstructorParams
+{
+  NormalBlobConstructorParams;
+  FileBlobConstructorParams;
+  SlicedBlobConstructorParams;
+};
+
 rpc protocol PContent
 {
     parent opens PCompositor;
 
     manages PAudio;
+    manages PBlob;
     manages PBrowser;
     manages PCrashReporter;
     manages PDeviceStorageRequest;
     manages PExternalHelperApp;
     manages PHal;
     manages PIndexedDB;
     manages PMemoryReportRequest;
     manages PNecko;
@@ -117,16 +151,18 @@ both:
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
     // TabChild::BrowserFrameProvideWindow, and the parent creates the PBrowser
     // as part of ContentParent::CreateTab.
     async PBrowser(PRUint32 chromeFlags, bool isBrowserElement, PRUint32 appId);
 
+    async PBlob(BlobConstructorParams params);
+
 child:
     PMemoryReportRequest();
 
     PTestShell();
 
     RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
                    OverrideMapping[] overrides, nsCString locale);
 
@@ -194,17 +230,17 @@ parent:
 
     async LoadURIExternal(URI uri);
 
     // PrefService message
     sync ReadPrefsArray() returns (PrefTuple[] retValue);
 
     sync ReadFontList() returns (FontListEntry[] retValue);
 
-    sync SyncMessage(nsString aMessage, nsString aJSON)
+    sync SyncMessage(nsString aMessage, ClonedMessageData aData)
       returns (nsString[] retval);
 
     ShowAlertNotification(nsString imageUrl, 
                           nsString title, 
                           nsString text, 
                           bool textClickable,
                           nsString cookie,
                           nsString name);
@@ -241,14 +277,13 @@ parent:
 
     sync GetShowPasswordSetting()
         returns (bool showPassword);
 
     // Notify the parent of the presence or absence of private docshells
     PrivateDocShellsExist(bool aExist);
 
 both:
-     AsyncMessage(nsString aMessage, nsString aJSON);
-
+     AsyncMessage(nsString aMessage, ClonedMessageData aData);
 };
 
 }
 }
new file mode 100644
--- /dev/null
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -0,0 +1,187 @@
+/* -*- 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/. */
+
+#include "StructuredCloneUtils.h"
+
+#include "nsIDOMFile.h"
+#include "nsIDOMDOMException.h"
+#include "nsIMutable.h"
+#include "nsIXPConnect.h"
+
+#include "nsContentUtils.h"
+#include "nsJSEnvironment.h"
+#include "nsThreadUtils.h"
+#include "StructuredCloneTags.h"
+
+using namespace mozilla::dom;
+
+namespace {
+
+void
+Error(JSContext* aCx, uint32_t aErrorId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_DOMStructuredCloneError(aCx, aErrorId);
+}
+
+JSObject*
+Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
+     uint32_t aData, void* aClosure)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aClosure);
+
+  StructuredCloneClosure* closure =
+    static_cast<StructuredCloneClosure*>(aClosure);
+
+  if (aTag == SCTAG_DOM_FILE) {
+    MOZ_ASSERT(aData < closure->mBlobs.Length());
+
+    nsCOMPtr<nsIDOMFile> file = do_QueryInterface(closure->mBlobs[aData]);
+    MOZ_ASSERT(file);
+
+#ifdef DEBUG
+    {
+      // File should not be mutable.
+      nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+      bool isMutable;
+      if (NS_FAILED(mutableFile->GetMutable(&isMutable))) {
+        MOZ_NOT_REACHED("GetMutable failed!");
+      }
+      else {
+        MOZ_ASSERT(!isMutable);
+      }
+    }
+#endif
+
+    jsval wrappedFile;
+    nsresult rv =
+      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
+                                  &NS_GET_IID(nsIDOMFile), &wrappedFile);
+    if (NS_FAILED(rv)) {
+      Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
+      return nullptr;
+    }
+
+    return &wrappedFile.toObject();
+  }
+
+  if (aTag == SCTAG_DOM_BLOB) {
+    MOZ_ASSERT(aData < closure->mBlobs.Length());
+
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(closure->mBlobs[aData]);
+    MOZ_ASSERT(blob);
+
+#ifdef DEBUG
+    {
+      // Blob should not be mutable.
+      nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+      bool isMutable;
+      if (NS_FAILED(mutableBlob->GetMutable(&isMutable))) {
+        MOZ_NOT_REACHED("GetMutable failed!");
+      }
+      else {
+        MOZ_ASSERT(!isMutable);
+      }
+    }
+#endif
+
+    jsval wrappedBlob;
+    nsresult rv =
+      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
+                                  &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
+    if (NS_FAILED(rv)) {
+      Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
+      return nullptr;
+    }
+
+    return &wrappedBlob.toObject();
+  }
+
+  return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
+}
+
+JSBool
+Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
+      void* aClosure)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aClosure);
+
+  StructuredCloneClosure* closure =
+    static_cast<StructuredCloneClosure*>(aClosure);
+
+  // See if this is a wrapped native.
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  nsContentUtils::XPConnect()->
+    GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
+
+  if (wrappedNative) {
+    // Get the raw nsISupports out of it.
+    nsISupports* wrappedObject = wrappedNative->Native();
+    MOZ_ASSERT(wrappedObject);
+
+    // See if the wrapped native is a nsIDOMFile.
+    nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
+    if (file) {
+      nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+      if (mutableFile &&
+          NS_SUCCEEDED(mutableFile->SetMutable(false)) &&
+          JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILE,
+                             closure->mBlobs.Length())) {
+        closure->mBlobs.AppendElement(file);
+        return true;
+      }
+    }
+
+    // See if the wrapped native is a nsIDOMBlob.
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
+    if (blob) {
+      nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+      if (mutableBlob &&
+          NS_SUCCEEDED(mutableBlob->SetMutable(false)) &&
+          JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
+                             closure->mBlobs.Length())) {
+        closure->mBlobs.AppendElement(blob);
+        return true;
+      }
+    }
+  }
+
+  return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+}
+
+JSStructuredCloneCallbacks gCallbacks = {
+  Read,
+  Write,
+  Error
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace dom {
+
+bool
+ReadStructuredClone(JSContext* aCx, const uint64_t* aData, size_t aDataLength,
+                    const StructuredCloneClosure& aClosure, JS::Value* aClone)
+{
+  void* closure = &const_cast<StructuredCloneClosure&>(aClosure);
+  return !!JS_ReadStructuredClone(aCx, aData, aDataLength,
+                                  JS_STRUCTURED_CLONE_VERSION, aClone,
+                                  &gCallbacks, closure);
+}
+
+bool
+WriteStructuredClone(JSContext* aCx, const JS::Value& aSource,
+                     JSAutoStructuredCloneBuffer& aBuffer,
+                     StructuredCloneClosure& aClosure)
+{
+  return aBuffer.write(aCx, aSource, &gCallbacks, &aClosure);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/StructuredCloneUtils.h
@@ -0,0 +1,56 @@
+/* -*- 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_StructuredCloneUtils_h
+#define mozilla_dom_StructuredCloneUtils_h
+
+#include "jsapi.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsIDOMFile.h"
+
+namespace mozilla {
+
+struct SerializedStructuredCloneBuffer;
+
+namespace dom {
+
+struct
+StructuredCloneClosure
+{
+  nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
+};
+
+struct
+StructuredCloneData
+{
+  StructuredCloneData() : mData(nullptr), mDataLength(0) {}
+  uint64_t* mData;
+  size_t mDataLength;
+  StructuredCloneClosure mClosure;
+};
+
+bool
+ReadStructuredClone(JSContext* aCx, const uint64_t* aData, size_t aDataLength,
+                    const StructuredCloneClosure& aClosure, JS::Value* aClone);
+
+inline bool
+ReadStructuredClone(JSContext* aCx, const StructuredCloneData& aData,
+                    JS::Value* aClone)
+{
+  return ReadStructuredClone(aCx, aData.mData, aData.mDataLength,
+                             aData.mClosure, aClone);
+}
+
+bool
+WriteStructuredClone(JSContext* aCx, const JS::Value& aSource,
+                     JSAutoStructuredCloneBuffer& aBuffer,
+                     StructuredCloneClosure& aClosure);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_StructuredCloneUtils_h
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1,17 +1,21 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
 /* vim: set sw=2 sts=2 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 "base/basictypes.h"
 
+#include "TabChild.h"
+
 #include "BasicLayers.h"
+#include "Blob.h"
+#include "ContentChild.h"
 #include "IndexedDBChild.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/PContentChild.h"
 #include "mozilla/dom/PContentDialogChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/PLayersChild.h"
@@ -53,19 +57,20 @@
 #include "nsPIWindowRoot.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsScriptLoader.h"
 #include "nsSerializationHelper.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "PCOMContentPermissionRequestChild.h"
-#include "TabChild.h"
+#include "StructuredCloneUtils.h"
 #include "xpcpublic.h"
 
+using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::indexedDB;
 
 NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
@@ -641,16 +646,19 @@ TabChild::RecvUpdateDimensions(const nsR
 }
 
 bool
 TabChild::RecvUpdateFrame(const nsIntRect& aDisplayPort,
                           const nsIntPoint& aScrollOffset,
                           const gfxSize& aResolution,
                           const nsIntRect& aScreenSize)
 {
+    if (!mCx || !mTabChildGlobal) {
+        return true;
+    }
     nsCString data;
     data += nsPrintfCString("{ \"x\" : %d", aScrollOffset.x);
     data += nsPrintfCString(", \"y\" : %d", aScrollOffset.y);
     // We don't treat the x and y scales any differently for this
     // semi-platform-specific code.
     data += nsPrintfCString(", \"zoom\" : %f", aResolution.width);
     data += nsPrintfCString(", \"displayPort\" : ");
         data += nsPrintfCString("{ \"left\" : %d", aDisplayPort.X());
@@ -660,20 +668,35 @@ TabChild::RecvUpdateFrame(const nsIntRec
         data += nsPrintfCString(", \"resolution\" : %f", aResolution.width);
         data += nsPrintfCString(" }");
     data += nsPrintfCString(", \"screenSize\" : ");
         data += nsPrintfCString("{ \"width\" : %d", aScreenSize.width);
         data += nsPrintfCString(", \"height\" : %d", aScreenSize.height);
         data += nsPrintfCString(" }");
     data += nsPrintfCString(" }");
 
+    jsval json = JSVAL_NULL;
+    StructuredCloneData cloneData;
+    JSAutoStructuredCloneBuffer buffer;
+    if (JS_ParseJSON(mCx,
+                      static_cast<const jschar*>(NS_ConvertUTF8toUTF16(data).get()),
+                      data.Length(),
+                      &json)) {
+        WriteStructuredClone(mCx, json, buffer, cloneData.mClosure);
+    }
+
+    nsFrameScriptCx cx(static_cast<nsIWebBrowserChrome*>(this), this);
     // Let the BrowserElementScrolling helper (if it exists) for this
     // content manipulate the frame state.
-    return RecvAsyncMessage(NS_LITERAL_STRING("Viewport:Change"),
-                            NS_ConvertUTF8toUTF16(data));
+    nsRefPtr<nsFrameMessageManager> mm =
+      static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
+    mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(mTabChildGlobal),
+                       NS_LITERAL_STRING("Viewport:Change"), false,
+                       &cloneData, nullptr, nullptr);
+    return true;
 }
 
 bool
 TabChild::RecvActivate()
 {
   nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(mWebNav);
   browser->Activate();
   return true;
@@ -953,24 +976,46 @@ TabChild::RecvLoadRemoteScript(const nsS
     return true;
 
   LoadFrameScriptInternal(aURL);
   return true;
 }
 
 bool
 TabChild::RecvAsyncMessage(const nsString& aMessage,
-                           const nsString& aJSON)
+                           const ClonedMessageData& aData)
 {
   if (mTabChildGlobal) {
     nsFrameScriptCx cx(static_cast<nsIWebBrowserChrome*>(this), this);
+
+    const SerializedStructuredCloneBuffer& buffer = aData.data();
+    const InfallibleTArray<PBlobChild*>& blobChildList = aData.blobsChild();
+
+    StructuredCloneData cloneData;
+    cloneData.mData = buffer.data;
+    cloneData.mDataLength = buffer.dataLength;
+
+    if (!blobChildList.IsEmpty()) {
+      PRUint32 length = blobChildList.Length();
+      cloneData.mClosure.mBlobs.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = static_cast<BlobChild*>(blobChildList[i]);
+        MOZ_ASSERT(blobChild);
+
+        nsCOMPtr<nsIDOMBlob> blob = blobChild->GetBlob();
+        MOZ_ASSERT(blob);
+
+        cloneData.mClosure.mBlobs.AppendElement(blob);
+      }
+    }
+
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
     mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(mTabChildGlobal),
-                       aMessage, false, aJSON, nullptr, nullptr);
+                       aMessage, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 class UnloadScriptEvent : public nsRunnable
 {
 public:
   UnloadScriptEvent(TabChild* aTabChild, TabChildGlobal* aTabChildGlobal)
@@ -1179,33 +1224,72 @@ TabChild::DeallocPIndexedDB(PIndexedDBCh
 {
   delete aActor;
   return true;
 }
 
 static bool
 SendSyncMessageToParent(void* aCallbackData,
                         const nsAString& aMessage,
-                        const nsAString& aJSON,
+                        const StructuredCloneData& aData,
                         InfallibleTArray<nsString>* aJSONRetVal)
 {
-  return static_cast<TabChild*>(aCallbackData)->
-    SendSyncMessage(nsString(aMessage), nsString(aJSON),
-                    aJSONRetVal);
+  TabChild* tabChild = static_cast<TabChild*>(aCallbackData);
+  ContentChild* cc = static_cast<ContentChild*>(tabChild->Manager());
+  ClonedMessageData data;
+  SerializedStructuredCloneBuffer& buffer = data.data();
+  buffer.data = aData.mData;
+  buffer.dataLength = aData.mDataLength;
+
+  const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+  if (!blobs.IsEmpty()) {
+    InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+    PRUint32 length = blobs.Length();
+    blobChildList.SetCapacity(length);
+    for (PRUint32 i = 0; i < length; ++i) {
+      BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+      if (!blobChild) {
+        return false;
+      }
+      blobChildList.AppendElement(blobChild);
+    }
+  }
+  return tabChild->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
 }
 
 static bool
 SendAsyncMessageToParent(void* aCallbackData,
                          const nsAString& aMessage,
-                         const nsAString& aJSON)
+                         const StructuredCloneData& aData)
 {
-  return static_cast<TabChild*>(aCallbackData)->
-    SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+  TabChild* tabChild = static_cast<TabChild*>(aCallbackData);
+  ContentChild* cc = static_cast<ContentChild*>(tabChild->Manager());
+  ClonedMessageData data;
+  SerializedStructuredCloneBuffer& buffer = data.data();
+  buffer.data = aData.mData;
+  buffer.dataLength = aData.mDataLength;
+
+  const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+  if (!blobs.IsEmpty()) {
+    InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+    PRUint32 length = blobs.Length();
+    blobChildList.SetCapacity(length);
+    for (PRUint32 i = 0; i < length; ++i) {
+      BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+      if (!blobChild) {
+        return false;
+      }
+      blobChildList.AppendElement(blobChild);
+    }
+  }
+
+  return tabChild->SendAsyncMessage(nsString(aMessage), data);
 }
 
+
 TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
 : mTabChild(aTabChild)
 {
 }
 
 void
 TabChildGlobal::Init()
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -54,16 +54,17 @@ namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
 namespace dom {
 
 class TabChild;
 class PContentDialogChild;
+class ClonedMessageData;
 
 class TabChildGlobal : public nsDOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
                        public nsIScriptObjectPrincipal,
                        public nsIScriptContextPrincipal
 {
 public:
   TabChildGlobal(TabChild* aTabChild);
@@ -137,16 +138,17 @@ class TabChild : public PBrowserChild,
                  public nsIWebBrowserChromeFocus,
                  public nsIInterfaceRequestor,
                  public nsIWindowProvider,
                  public nsSupportsWeakReference,
                  public nsIDialogCreator,
                  public nsITabChild
 {
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
+    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
     /**
      * Create a new TabChild object.
      *
      * |aIsBrowserElement| indicates whether the tab is inside an <iframe mozbrowser>.
      * |aAppId| is the app id of the app containing this tab. If the tab isn't
      * contained in an app, aAppId will be nsIScriptSecurityManager::NO_APP_ID.
@@ -191,17 +193,17 @@ public:
                               const PRInt32&  aModifiers,
                               const bool&     aPreventDefault);
     virtual bool RecvCompositionEvent(const nsCompositionEvent& event);
     virtual bool RecvTextEvent(const nsTextEvent& event);
     virtual bool RecvSelectionEvent(const nsSelectionEvent& event);
     virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& capture);
     virtual bool RecvLoadRemoteScript(const nsString& aURL);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
-                                  const nsString& aJSON);
+                                  const ClonedMessageData& aData);
 
     virtual PDocumentRendererChild*
     AllocPDocumentRenderer(const nsRect& documentRect, const gfxMatrix& transform,
                            const nsString& bgcolor,
                            const PRUint32& renderFlags, const bool& flushLayout,
                            const nsIntSize& renderSize);
     virtual bool DeallocPDocumentRenderer(PDocumentRendererChild* actor);
     virtual bool RecvPDocumentRendererConstructor(PDocumentRendererChild* actor,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1,16 +1,19 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
 /* vim: set sw=2 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 "base/basictypes.h"
 
+#include "TabParent.h"
+
+#include "Blob.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layout/RenderFrameParent.h"
@@ -37,18 +40,18 @@
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
 #include "nsSerializationHelper.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
+#include "StructuredCloneUtils.h"
 #include "TabChild.h"
-#include "TabParent.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::widget;
 using namespace mozilla::dom::indexedDB;
 
@@ -339,27 +342,61 @@ bool TabParent::SendRealTouchEvent(nsTou
 {
   nsTouchEvent e(event);
   MaybeForwardEventToRenderFrame(event, &e);
   return PBrowserParent::SendRealTouchEvent(e);
 }
 
 bool
 TabParent::RecvSyncMessage(const nsString& aMessage,
-                           const nsString& aJSON,
+                           const ClonedMessageData& aData,
                            InfallibleTArray<nsString>* aJSONRetVal)
 {
-  return ReceiveMessage(aMessage, true, aJSON, aJSONRetVal);
+  const SerializedStructuredCloneBuffer& buffer = aData.data();
+  const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
+  StructuredCloneData cloneData;
+  cloneData.mData = buffer.data;
+  cloneData.mDataLength = buffer.dataLength;
+  if (!blobParents.IsEmpty()) {
+    PRUint32 length = blobParents.Length();
+    cloneData.mClosure.mBlobs.SetCapacity(length);
+    for (PRUint32 i = 0; i < length; ++i) {
+      BlobParent* blobParent = static_cast<BlobParent*>(blobParents[i]);
+      MOZ_ASSERT(blobParent);
+      nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
+      MOZ_ASSERT(blob);
+      cloneData.mClosure.mBlobs.AppendElement(blob);
+    }
+  }
+  return ReceiveMessage(aMessage, true, &cloneData, aJSONRetVal);
 }
 
 bool
 TabParent::RecvAsyncMessage(const nsString& aMessage,
-                            const nsString& aJSON)
+                                  const ClonedMessageData& aData)
 {
-  return ReceiveMessage(aMessage, false, aJSON, nullptr);
+    const SerializedStructuredCloneBuffer& buffer = aData.data();
+  const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
+
+    StructuredCloneData cloneData;
+    cloneData.mData = buffer.data;
+    cloneData.mDataLength = buffer.dataLength;
+
+  if (!blobParents.IsEmpty()) {
+    PRUint32 length = blobParents.Length();
+      cloneData.mClosure.mBlobs.SetCapacity(length);
+    for (PRUint32 i = 0; i < length; ++i) {
+      BlobParent* blobParent = static_cast<BlobParent*>(blobParents[i]);
+      MOZ_ASSERT(blobParent);
+      nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
+        MOZ_ASSERT(blob);
+        cloneData.mClosure.mBlobs.AppendElement(blob);
+      }
+    }
+  return ReceiveMessage(aMessage, false, &cloneData, nullptr);
 }
 
 bool
 TabParent::RecvSetCursor(const PRUint32& aCursor)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     widget->SetCursor((nsCursor) aCursor);
@@ -692,17 +729,17 @@ TabParent::RecvGetWidgetNativeData(Windo
     }
   }
   return false;
 }
 
 bool
 TabParent::ReceiveMessage(const nsString& aMessage,
                           bool aSync,
-                          const nsString& aJSON,
+                          const StructuredCloneData* aCloneData,
                           InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   if (frameLoader && frameLoader->GetFrameMessageManager()) {
     nsRefPtr<nsFrameMessageManager> manager =
       frameLoader->GetFrameMessageManager();
     JSContext* ctx = manager->GetJSContext();
     JSAutoRequest ar(ctx);
@@ -712,17 +749,17 @@ TabParent::ReceiveMessage(const nsString
     JSObject* objectsArray = JS_NewArrayObject(ctx, len, NULL);
     if (!objectsArray) {
       return false;
     }
 
     manager->ReceiveMessage(mFrameElement,
                             aMessage,
                             aSync,
-                            aJSON,
+                            aCloneData,
                             objectsArray,
                             aJSONRetVal);
   }
   return true;
 }
 
 PIndexedDBParent*
 TabParent::AllocPIndexedDB(const nsCString& aASCIIOrigin, bool* /* aAllowed */)
@@ -753,19 +790,23 @@ TabParent::RecvPIndexedDBConstructor(PIn
   NS_ENSURE_TRUE(node, false);
 
   nsIDocument* doc = node->GetOwnerDocument();
   NS_ENSURE_TRUE(doc, false);
 
   nsCOMPtr<nsPIDOMWindow> window = doc->GetInnerWindow();
   NS_ENSURE_TRUE(window, false);
 
+  ContentParent* contentParent = static_cast<ContentParent*>(Manager());
+  NS_ASSERTION(contentParent, "Null manager?!");
+
   nsRefPtr<IDBFactory> factory;
   nsresult rv =
-    IDBFactory::Create(window, aASCIIOrigin, getter_AddRefs(factory));
+    IDBFactory::Create(window, aASCIIOrigin, contentParent,
+                       getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, false);
 
   if (!factory) {
     *aAllowed = false;
     return true;
   }
 
   IndexedDBParent* actor = static_cast<IndexedDBParent*>(aActor);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -35,23 +35,28 @@ struct FrameMetrics;
 }
 
 namespace layout {
 class RenderFrameParent;
 }
 
 namespace dom {
 
+class ClonedMessageData;
+struct StructuredCloneData;
+
 class ContentDialogParent : public PContentDialogParent {};
 
 class TabParent : public PBrowserParent 
                 , public nsITabParent 
                 , public nsIAuthPromptProvider
                 , public nsISecureBrowserUI
 {
+    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+
 public:
     TabParent();
     virtual ~TabParent();
     nsIDOMElement* GetOwnerElement() { return mFrameElement; }
     void SetOwnerElement(nsIDOMElement* aElement);
     nsIBrowserDOMWindow *GetBrowserDOMWindow() { return mBrowserDOMWindow; }
     void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {
         mBrowserDOMWindow = aBrowserDOMWindow;
@@ -63,20 +68,20 @@ public:
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent);
     virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                             const nsString& aURL,
                                             const nsString& aName,
                                             const nsString& aFeatures,
                                             bool* aOutWindowOpened);
     virtual bool AnswerCreateWindow(PBrowserParent** retval);
     virtual bool RecvSyncMessage(const nsString& aMessage,
-                                 const nsString& aJSON,
+                                 const ClonedMessageData& aData,
                                  InfallibleTArray<nsString>* aJSONRetVal);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
-                                  const nsString& aJSON);
+                                  const ClonedMessageData& aData);
     virtual bool RecvNotifyIMEFocus(const bool& aFocus,
                                     nsIMEUpdatePreference* aPreference,
                                     PRUint32* aSeqno);
     virtual bool RecvNotifyIMETextChange(const PRUint32& aStart,
                                          const PRUint32& aEnd,
                                          const PRUint32& aNewEnd);
     virtual bool RecvNotifyIMESelection(const PRUint32& aSeqno,
                                         const PRUint32& aAnchor,
@@ -167,17 +172,17 @@ public:
     bool SendSelectionEvent(nsSelectionEvent& event);
 
     static TabParent* GetFrom(nsFrameLoader* aFrameLoader);
     static TabParent* GetFrom(nsIContent* aContent);
 
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
-                        const nsString& aJSON,
+                        const StructuredCloneData* aCloneData,
                         InfallibleTArray<nsString>* aJSONRetVal = nullptr);
 
     virtual bool Recv__delete__() MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
     virtual PIndexedDBParent* AllocPIndexedDB(const nsCString& aASCIIOrigin,
                                               bool* /* aAllowed */);
--- a/dom/ipc/ipdl.mk
+++ b/dom/ipc/ipdl.mk
@@ -1,14 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 IPDLSRCS = \
+  DOMTypes.ipdlh \
   PAudio.ipdl \
+  PBlob.ipdl \
+  PBlobStream.ipdl \
   PBrowser.ipdl \
   PContent.ipdl \
   PContentDialog.ipdl \
   PCrashReporter.ipdl \
   PDocumentRenderer.ipdl \
   PContentPermissionRequest.ipdl \
   PMemoryReportRequest.ipdl \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/ipc/nsIRemoteBlob.h
@@ -0,0 +1,28 @@
+/* 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_nsIRemoteBlob_h
+#define mozilla_dom_ipc_nsIRemoteBlob_h
+
+#include "nsISupports.h"
+
+#ifndef NS_NO_VTABLE
+#define NS_NO_VTABLE
+#endif
+
+#define NS_IREMOTEBLOB_IID \
+  {0x74ce3cdd, 0xbfc9, 0x4edb, {0x98, 0x26, 0x50, 0xcf, 0x00, 0x26, 0x58, 0x70}}
+
+class NS_NO_VTABLE nsIRemoteBlob : public nsISupports
+{
+public: 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IREMOTEBLOB_IID)
+
+  // This will either return a PBlobChild or PBlobParent.
+  virtual void* GetPBlob() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIRemoteBlob, NS_IREMOTEBLOB_IID)
+
+#endif // mozilla_dom_ipc_nsIRemoteBlob_h
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -12,16 +12,18 @@
 //------------------------------------------------------------------------------
 
 // static
 const int Pickle::kPayloadUnit = 64;
 
 // We mark a read only pickle with a special capacity_.
 static const uint32 kCapacityReadOnly = (uint32) -1;
 
+static const char kBytePaddingMarker = char(0xbf);
+
 // Payload is uint32 aligned.
 
 Pickle::Pickle()
     : header_(NULL),
       header_size_(sizeof(Header)),
       capacity_(0),
       variable_buffer_offset_(0) {
   Resize(kPayloadUnit);
@@ -354,26 +356,44 @@ bool Pickle::ReadString16(void** iter, s
 
   char16* chars = reinterpret_cast<char16*>(*iter);
   result->assign(chars, len);
 
   UpdateIter(iter, len * sizeof(char16));
   return true;
 }
 
-bool Pickle::ReadBytes(void** iter, const char** data, int length) const {
+bool Pickle::ReadBytes(void** iter, const char** data, int length,
+                       uint32 alignment) const {
   DCHECK(iter);
   DCHECK(data);
+  DCHECK(alignment == 4 || alignment == 8);
+  DCHECK(intptr_t(header_) % alignment == 0);
+
   if (!*iter)
     *iter = const_cast<char*>(payload());
 
+  uint32 paddingLen = intptr_t(*iter) % alignment;
+  if (paddingLen) {
+#ifdef DEBUG
+    {
+      const char* padding = static_cast<const char*>(*iter);
+      for (uint32 i = 0; i < paddingLen; i++) {
+        DCHECK(*(padding + i) == kBytePaddingMarker);
+      }
+    }
+#endif
+    length += paddingLen;
+  }
+
   if (!IteratorHasRoomFor(*iter, length))
     return false;
 
-  *data = reinterpret_cast<const char*>(*iter);
+  *data = static_cast<const char*>(*iter) + paddingLen;
+  DCHECK(intptr_t(*data) % alignment == 0);
 
   UpdateIter(iter, length);
   return true;
 }
 
 bool Pickle::ReadData(void** iter, const char** data, int* length) const {
   DCHECK(iter);
   DCHECK(data);
@@ -382,44 +402,60 @@ bool Pickle::ReadData(void** iter, const
     *iter = const_cast<char*>(payload());
 
   if (!ReadLength(iter, length))
     return false;
 
   return ReadBytes(iter, data, *length);
 }
 
-char* Pickle::BeginWrite(uint32 length) {
-  // write at a uint32-aligned offset from the beginning of the header
+char* Pickle::BeginWrite(uint32 length, uint32 alignment) {
+  DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!";
+
+  // write at an alignment-aligned offset from the beginning of the header
   uint32 offset = AlignInt(header_->payload_size, sizeof(uint32));
-  uint32 new_size = offset + AlignInt(length, sizeof(uint32));
+  uint32 padding = (header_size_ + offset) %  alignment;
+  uint32 new_size = offset + padding + AlignInt(length, sizeof(uint32));
   uint32 needed_size = header_size_ + new_size;
 
   if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
     return NULL;
 
+  DCHECK(intptr_t(header_) % alignment == 0);
+
 #ifdef ARCH_CPU_64_BITS
   DCHECK_LE(length, std::numeric_limits<uint32>::max());
 #endif
 
-  header_->payload_size = static_cast<uint32>(new_size);
-  return payload() + offset;
+  char* buffer = payload() + offset;
+
+  if (padding) {
+    memset(buffer, kBytePaddingMarker, padding);
+    buffer += padding;
+  }
+
+  DCHECK(intptr_t(buffer) % alignment == 0);
+
+  header_->payload_size = new_size;
+  return buffer;
 }
 
 void Pickle::EndWrite(char* dest, int length) {
   // Zero-pad to keep tools like purify from complaining about uninitialized
   // memory.
   if (length % sizeof(uint32))
     memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
 }
 
-bool Pickle::WriteBytes(const void* data, int data_len) {
+bool Pickle::WriteBytes(const void* data, int data_len, uint32 alignment) {
   DCHECK(capacity_ != kCapacityReadOnly) << "oops: pickle is readonly";
+  DCHECK(alignment == 4 || alignment == 8);
+  DCHECK(intptr_t(header_) % alignment == 0);
 
-  char* dest = BeginWrite(data_len);
+  char* dest = BeginWrite(data_len, alignment);
   if (!dest)
     return false;
 
   memcpy(dest, data, data_len);
 
   EndWrite(dest, data_len);
   return true;
 }
@@ -453,17 +489,17 @@ bool Pickle::WriteData(const char* data,
 
 char* Pickle::BeginWriteData(int length) {
   DCHECK_EQ(variable_buffer_offset_, 0U) <<
     "There can only be one variable buffer in a Pickle";
 
   if (!WriteInt(length))
     return false;
 
-  char *data_ptr = BeginWrite(length);
+  char *data_ptr = BeginWrite(length, sizeof(uint32));
   if (!data_ptr)
     return NULL;
 
   variable_buffer_offset_ =
       data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
 
   // EndWrite doesn't necessarily have to be called after the write operation,
   // so we call it here to pad out what the caller will eventually write.
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -78,17 +78,18 @@ class Pickle {
   bool ReadUInt64(void** iter, uint64* result) const;
   bool ReadDouble(void** iter, double* result) const;
   bool ReadIntPtr(void** iter, intptr_t* result) const;
   bool ReadUnsignedChar(void** iter, unsigned char* result) const;
   bool ReadString(void** iter, std::string* result) const;
   bool ReadWString(void** iter, std::wstring* result) const;
   bool ReadString16(void** iter, string16* result) const;
   bool ReadData(void** iter, const char** data, int* length) const;
-  bool ReadBytes(void** iter, const char** data, int length) const;
+  bool ReadBytes(void** iter, const char** data, int length,
+                 uint32 alignment = sizeof(uint32)) const;
 
   // Safer version of ReadInt() checks for the result not being negative.
   // Use it for reading the object sizes.
   bool ReadLength(void** iter, int* result) const;
 
   // Methods for adding to the payload of the Pickle.  These values are
   // appended to the end of the Pickle's payload.  When reading values from a
   // Pickle, it is important to read them in the order in which they were added
@@ -142,17 +143,18 @@ class Pickle {
   }
   bool WriteUnsignedChar(unsigned char value) {
     return WriteBytes(&value, sizeof(value));
   }
   bool WriteString(const std::string& value);
   bool WriteWString(const std::wstring& value);
   bool WriteString16(const string16& value);
   bool WriteData(const char* data, int length);
-  bool WriteBytes(const void* data, int data_len);
+  bool WriteBytes(const void* data, int data_len,
+                  uint32 alignment = sizeof(uint32));
 
   // Same as WriteData, but allows the caller to write directly into the
   // Pickle. This saves a copy in cases where the data is not already
   // available in a buffer. The caller should take care to not write more
   // than the length it declares it will. Use ReadData to get the data.
   // Returns NULL on failure.
   //
   // The returned pointer will only be valid until the next write operation
@@ -226,17 +228,17 @@ class Pickle {
   uint32 capacity() const {
     return capacity_;
   }
 
   // Resizes the buffer for use when writing the specified amount of data. The
   // location that the data should be written at is returned, or NULL if there
   // was an error. Call EndWrite with the returned offset and the given length
   // to pad out for the next write.
-  char* BeginWrite(uint32 length);
+  char* BeginWrite(uint32 length, uint32 alignment);
 
   // Completes the write operation by padding the data with NULL bytes until it
   // is padded. Should be paired with BeginWrite, but it does not necessarily
   // have to be called after the data is written.
   void EndWrite(char* dest, int length);
 
   // Resize the capacity, note that the input value should include the size of
   // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -21,16 +21,17 @@
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "gfxPoint.h"
 #include "nsRect.h"
 #include "nsRegion.h"
 #include "gfxASurface.h"
+#include "jsapi.h"
 #include "LayersTypes.h"
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 #if !defined(OS_POSIX)
 // This condition must be kept in sync with the one in
@@ -57,16 +58,46 @@ typedef uintptr_t WindowsHandle;
 // move to nscore.h or something.
 struct void_t {
   bool operator==(const void_t&) const { return true; }
 };
 struct null_t {
   bool operator==(const null_t&) const { return true; }
 };
 
+struct SerializedStructuredCloneBuffer
+{
+  SerializedStructuredCloneBuffer()
+  : data(nullptr), dataLength(0)
+  { }
+
+  SerializedStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& aOther)
+  {
+    *this = aOther;
+  }
+
+  bool
+  operator==(const SerializedStructuredCloneBuffer& aOther) const
+  {
+    return this->data == aOther.data &&
+           this->dataLength == aOther.dataLength;
+  }
+
+  SerializedStructuredCloneBuffer&
+  operator=(const JSAutoStructuredCloneBuffer& aOther)
+  {
+    data = aOther.data();
+    dataLength = aOther.nbytes();
+    return *this;
+  }
+
+  uint64_t* data;
+  size_t dataLength;
+};
+
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Generic enum serializer.
  *
  * This is a generic serializer for any enum type used in IPDL.
@@ -825,11 +856,52 @@ struct ParamTraits<mozilla::TimeStamp>
     WriteParam(aMsg, aParam.mValue);
   }
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mValue);
   };
 };
 
+template <>
+struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
+{
+  typedef mozilla::SerializedStructuredCloneBuffer paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.dataLength);
+    if (aParam.dataLength) {
+      // Structured clone data must be 64-bit aligned.
+      aMsg->WriteBytes(aParam.data, aParam.dataLength, sizeof(uint64_t));
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->dataLength)) {
+      return false;
+    }
+
+    if (aResult->dataLength) {
+      const char** buffer =
+        const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
+      // Structured clone data must be 64-bit aligned.
+      if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength,
+                           sizeof(uint64_t))) {
+        return false;
+      }
+    } else {
+      aResult->data = NULL;
+    }
+
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    LogParam(aParam.dataLength, aLog);
+  }
+};
+
 } /* namespace IPC */
 
 #endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -79,16 +79,17 @@
 #include "nsDOMFileReader.h"
 
 #include "ArchiveReader.h"
 
 using namespace mozilla::dom::file;
 
 #include "nsFormData.h"
 #include "nsBlobProtocolHandler.h"
+#include "nsBlobURI.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsIControllerCommandTable.h"
 #include "nsJSProtocolHandler.h"
 #include "nsScriptNameSpaceManager.h"
 #include "nsIControllerContext.h"
 #include "nsDOMScriptObjectFactory.h"
 #include "nsDOMStorage.h"
 #include "nsJSON.h"
@@ -255,16 +256,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSeri
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsEventSource)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocket)
 NS_GENERIC_FACTORY_CONSTRUCTOR(Activity)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(ArchiveReader)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobURI)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
                                          nsDOMStorageManager::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
                                          IndexedDatabaseManager::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
@@ -735,16 +737,17 @@ NS_DEFINE_NAMED_CID(NS_STYLESHEETSERVICE
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_XSLT_PROCESSOR_CID);
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_XPATH_EVALUATOR_CID);
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_NODESET_CID);
 NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_FILEREADER_CID);
 NS_DEFINE_NAMED_CID(NS_ARCHIVEREADER_CID);
 NS_DEFINE_NAMED_CID(NS_FORMDATA_CID);
 NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_BLOBURI_CID);
 NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID);
 NS_DEFINE_NAMED_CID(NS_WEBSOCKET_CID);
 NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
@@ -1008,16 +1011,17 @@ static const mozilla::Module::CIDEntry k
   { &kTRANSFORMIIX_XSLT_PROCESSOR_CID, false, NULL, txMozillaXSLTProcessorConstructor },
   { &kTRANSFORMIIX_XPATH_EVALUATOR_CID, false, NULL, nsXPathEvaluatorConstructor },
   { &kTRANSFORMIIX_NODESET_CID, false, NULL, txNodeSetAdaptorConstructor },
   { &kNS_XMLSERIALIZER_CID, false, NULL, nsDOMSerializerConstructor },
   { &kNS_FILEREADER_CID, false, NULL, nsDOMFileReaderConstructor },
   { &kNS_ARCHIVEREADER_CID, false, NULL, ArchiveReaderConstructor },
   { &kNS_FORMDATA_CID, false, NULL, nsFormDataConstructor },
   { &kNS_BLOBPROTOCOLHANDLER_CID, false, NULL, nsBlobProtocolHandlerConstructor },
+  { &kNS_BLOBURI_CID, false, NULL, nsBlobURIConstructor },
   { &kNS_XMLHTTPREQUEST_CID, false, NULL, nsXMLHttpRequestConstructor },
   { &kNS_EVENTSOURCE_CID, false, NULL, nsEventSourceConstructor },
   { &kNS_WEBSOCKET_CID, false, NULL, nsWebSocketConstructor },
   { &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
   { &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
   { &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
   { &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },