Bug 761930 - Multi-process support for Device Storage. r=sicking
authorDoug Turner <dougt@dougt.org>
Tue, 19 Jun 2012 16:14:39 -0700
changeset 99270 c7335ca109a4d3787046a3598194b5cd73cfdfff
parent 99269 f9d877d78294a2825c04248ce698628c4078d4ce
child 99271 53fcd978832ee2fe3601469dae458ac7f607cfc4
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerssicking
bugs761930
milestone16.0a1
Bug 761930 - Multi-process support for Device Storage. r=sicking
dom/devicestorage/DeviceStorageRequestChild.cpp
dom/devicestorage/DeviceStorageRequestChild.h
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/DeviceStorageRequestParent.h
dom/devicestorage/Makefile.in
dom/devicestorage/PDeviceStorageRequest.ipdl
dom/devicestorage/ipc/Makefile.in
dom/devicestorage/ipc/test_ipc.html
dom/devicestorage/ipdl.mk
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/devicestorage/test/test_overwrite.html
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/Makefile.in
dom/ipc/PContent.ipdl
ipc/ipdl/Makefile.in
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 "DeviceStorageRequestChild.h"
+#include "nsDeviceStorage.h"
+#include "nsDOMFile.h"
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+DeviceStorageRequestChild::DeviceStorageRequestChild()
+{
+  MOZ_COUNT_CTOR(DeviceStorageRequestChild);
+}
+
+DeviceStorageRequestChild::DeviceStorageRequestChild(DOMRequest* aRequest,
+                                                     DeviceStorageFile* aFile)
+  : mRequest(aRequest)
+  , mFile(aFile)
+{
+  MOZ_COUNT_CTOR(DeviceStorageRequestChild);
+}
+
+DeviceStorageRequestChild::~DeviceStorageRequestChild() {
+  MOZ_COUNT_DTOR(DeviceStorageRequestChild);
+}
+bool
+DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aValue)
+{
+  switch (aValue.type()) {
+
+    case DeviceStorageResponseValue::TErrorResponse:
+    {
+      ErrorResponse r = aValue;
+      mRequest->FireError(r.error());
+      break;
+    }
+
+    case DeviceStorageResponseValue::TSuccessResponse:
+    {
+      jsval result = StringToJsval(mRequest->GetOwner(), mFile->mPath);
+      mRequest->FireSuccess(result);
+      break;
+    }
+
+    case DeviceStorageResponseValue::TBlobResponse:
+    {
+      BlobResponse r = aValue;
+
+      // I am going to hell for this.  bent says he'll save me.
+      const InfallibleTArray<PRUint8> bits = r.bits();
+      void* buffer = PR_Malloc(bits.Length());
+      memcpy(buffer, (void*) bits.Elements(), bits.Length());
+
+      nsString mimeType;
+      mimeType.AssignWithConversion(r.contentType());
+
+      nsCOMPtr<nsIDOMBlob> blob = new nsDOMMemoryFile(buffer,
+                                                      bits.Length(),
+                                                      mFile->mPath,
+                                                      mimeType);
+
+      jsval result = BlobToJsval(mRequest->GetOwner(), blob);
+      mRequest->FireSuccess(result);
+      break;
+    }
+
+    case DeviceStorageResponseValue::TEnumerationResponse:
+    {
+      EnumerationResponse r = aValue;
+      nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
+
+      PRUint32 count = r.paths().Length();
+      for (PRUint32 i = 0; i < count; i++) {
+        nsCOMPtr<nsIFile> f;
+        NS_NewLocalFile(r.paths()[i].fullpath(), false, getter_AddRefs(f));
+
+        nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+        dsf->SetPath(r.paths()[i].path());
+        cursor->mFiles.AppendElement(dsf);
+      }
+
+      nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(cursor);
+      NS_DispatchToMainThread(event);
+      break;
+    }
+
+    default:
+    {
+      NS_RUNTIMEABORT("not reached");
+      break;
+    }
+  }
+  return true;
+}
+
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageRequestChild.h
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_devicestorage_DeviceStorageRequestChild_h
+#define mozilla_dom_devicestorage_DeviceStorageRequestChild_h
+
+#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
+#include "DOMRequest.h"
+#include "nsDeviceStorage.h"
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+class DeviceStorageRequestChild : public PDeviceStorageRequestChild
+{
+public:
+  DeviceStorageRequestChild();
+  DeviceStorageRequestChild(DOMRequest* aRequest, DeviceStorageFile* aFile);
+  ~DeviceStorageRequestChild();
+
+  virtual bool Recv__delete__(const DeviceStorageResponseValue& value);
+
+private:
+  nsRefPtr<DOMRequest> mRequest;
+  nsRefPtr<DeviceStorageFile> mFile;
+};
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -0,0 +1,360 @@
+/* -*- 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 "DeviceStorageRequestParent.h"
+#include "nsDOMFile.h"
+#include "nsIMIMEService.h"
+#include "nsCExternalHandlerService.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams& aParams)
+{
+  MOZ_COUNT_CTOR(DeviceStorageRequestParent);
+
+  switch (aParams.type()) {
+    case DeviceStorageParams::TDeviceStorageAddParams:
+    {
+      DeviceStorageAddParams p = aParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      nsRefPtr<WriteFileEvent> r = new WriteFileEvent(this, dsf, p.bits());
+
+      nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      NS_ASSERTION(target, "Must have stream transport service");
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageGetParams:
+    {
+      DeviceStorageGetParams p = aParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      nsRefPtr<ReadFileEvent> r = new ReadFileEvent(this, dsf);
+
+      nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      NS_ASSERTION(target, "Must have stream transport service");
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageDeleteParams:
+    {
+      DeviceStorageDeleteParams p = aParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      nsRefPtr<DeleteFileEvent> r = new DeleteFileEvent(this, dsf);
+
+      nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      NS_ASSERTION(target, "Must have stream transport service");
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageEnumerationParams:
+    {
+      DeviceStorageEnumerationParams p = aParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      nsRefPtr<EnumerateFileEvent> r = new EnumerateFileEvent(this, dsf, p.since());
+
+      nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      NS_ASSERTION(target, "Must have stream transport service");
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+    default:
+    {
+      NS_RUNTIMEABORT("not reached");
+      break;
+    }
+  }
+}
+
+DeviceStorageRequestParent::~DeviceStorageRequestParent()
+{
+  MOZ_COUNT_DTOR(DeviceStorageRequestParent);
+}
+
+DeviceStorageRequestParent::PostErrorEvent::PostErrorEvent(DeviceStorageRequestParent* aParent,
+                                                           const char* aError)
+  : mParent(aParent)
+{
+  mError.AssignWithConversion(aError);
+}
+
+DeviceStorageRequestParent::PostErrorEvent::~PostErrorEvent() {}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::PostErrorEvent::Run() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  ErrorResponse response(mError);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+
+DeviceStorageRequestParent::PostSuccessEvent::PostSuccessEvent(DeviceStorageRequestParent* aParent)
+  : mParent(aParent)
+{
+}
+
+DeviceStorageRequestParent::PostSuccessEvent::~PostSuccessEvent() {}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::PostSuccessEvent::Run() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  SuccessResponse response;
+  unused <<  mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::PostBlobSuccessEvent::PostBlobSuccessEvent(DeviceStorageRequestParent* aParent,
+                                                                       void* aBuffer,
+                                                                       PRUint32 aLength,
+                                                                       nsACString& aMimeType)
+  : mParent(aParent)
+  , mMimeType(aMimeType)
+{
+  mBits.SetCapacity(aLength);
+  void* bits = mBits.Elements();
+  memcpy(bits, aBuffer, aLength);
+}
+
+DeviceStorageRequestParent::PostBlobSuccessEvent::~PostBlobSuccessEvent() {}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::PostBlobSuccessEvent::Run() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  BlobResponse response(mBits, mMimeType);
+  unused <<  mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::PostEnumerationSuccessEvent::PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
+                                                                                     InfallibleTArray<DeviceStorageFileValue>& aPaths)
+  : mParent(aParent)
+  , mPaths(aPaths)
+{
+}
+
+DeviceStorageRequestParent::PostEnumerationSuccessEvent::~PostEnumerationSuccessEvent() {}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::PostEnumerationSuccessEvent::Run() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  EnumerationResponse response(mPaths);
+  unused <<  mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::WriteFileEvent::WriteFileEvent(DeviceStorageRequestParent* aParent,
+                                                           DeviceStorageFile* aFile,
+                                                           InfallibleTArray<PRUint8>& aBits)
+  : mParent(aParent)
+  , mFile(aFile)
+  , mBits(aBits)
+{
+}
+
+DeviceStorageRequestParent::WriteFileEvent::~WriteFileEvent()
+{
+}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::WriteFileEvent::Run()
+{
+  nsRefPtr<nsRunnable> r;
+  nsresult rv = mFile->Write(mBits);
+
+  if (NS_FAILED(rv)) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+  }
+  else {
+    r = new PostPathResultEvent(mParent, mFile->mPath);
+  }
+
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+
+DeviceStorageRequestParent::DeleteFileEvent::DeleteFileEvent(DeviceStorageRequestParent* aParent,
+                                                             DeviceStorageFile* aFile)
+  : mParent(aParent)
+  , mFile(aFile)
+{
+}
+
+DeviceStorageRequestParent::DeleteFileEvent::~DeleteFileEvent()
+{
+}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::DeleteFileEvent::Run()
+{
+  mFile->mFile->Remove(true);
+
+  nsRefPtr<nsRunnable> r;
+
+  bool check = false;
+  mFile->mFile->Exists(&check);
+  if (check) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+  }
+  else {
+    r = new PostPathResultEvent(mParent, mFile->mPath);
+  }
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::ReadFileEvent::ReadFileEvent(DeviceStorageRequestParent* aParent,
+                                                         DeviceStorageFile* aFile)
+  : mParent(aParent)
+  , mFile(aFile)
+{
+  nsCOMPtr<nsIMIMEService> mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID);
+  if (mimeService) {
+    nsresult rv = mimeService->GetTypeFromFile(mFile->mFile, mMimeType);
+    if (NS_FAILED(rv)) {
+      mMimeType.Truncate();
+    }
+  }
+}
+
+DeviceStorageRequestParent::ReadFileEvent::~ReadFileEvent()
+{
+}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::ReadFileEvent::Run()
+{
+  nsCOMPtr<nsIRunnable> r;
+  bool check = false;
+  mFile->mFile->Exists(&check);
+  if (!check) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+
+  PRInt64 fileSize;
+  nsresult rv = mFile->mFile->GetFileSize(&fileSize);
+  if (NS_FAILED(rv)) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+  
+  PRFileDesc *fileHandle;
+  rv = mFile->mFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
+
+  // i am going to hell.  this is temp until bent provides seralizaiton of blobs.
+  void* buf = (void*) malloc(fileSize);
+  PRInt32 read = PR_Read(fileHandle, buf, fileSize); 
+  if (read != fileSize) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+
+  r = new PostBlobSuccessEvent(mParent, buf, fileSize, mMimeType);
+
+  PR_Free(buf);
+  PR_Close(fileHandle);
+
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::EnumerateFileEvent::EnumerateFileEvent(DeviceStorageRequestParent* aParent,
+                                                                   DeviceStorageFile* aFile,
+                                                                   PRUint32 aSince)
+  : mParent(aParent)
+  , mFile(aFile)
+  , mSince(aSince)
+{
+}
+
+DeviceStorageRequestParent::EnumerateFileEvent::~EnumerateFileEvent()
+{
+}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::EnumerateFileEvent::Run()
+{
+  nsCOMPtr<nsIRunnable> r;
+  bool check = false;
+  mFile->mFile->Exists(&check);
+  if (!check) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+
+  nsTArray<nsRefPtr<DeviceStorageFile> > files;
+  mFile->CollectFiles(files, mSince);
+
+  InfallibleTArray<DeviceStorageFileValue> values;
+
+  PRUint32 count = files.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    nsString fullpath;
+    files[i]->mFile->GetPath(fullpath);
+    DeviceStorageFileValue dsvf(fullpath, files[i]->mPath);
+    values.AppendElement(dsvf);
+  }
+
+  r = new PostEnumerationSuccessEvent(mParent, values);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+
+DeviceStorageRequestParent::PostPathResultEvent::PostPathResultEvent(DeviceStorageRequestParent* aParent,
+                                                             const nsAString& aPath)
+  : mParent(aParent)
+  , mPath(aPath)
+{
+}
+
+DeviceStorageRequestParent::PostPathResultEvent::~PostPathResultEvent()
+{
+}
+
+NS_IMETHODIMP
+DeviceStorageRequestParent::PostPathResultEvent::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  SuccessResponse response;
+  unused <<  mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -0,0 +1,136 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_devicestorage_DeviceStorageRequestParent_h
+#define mozilla_dom_devicestorage_DeviceStorageRequestParent_h
+
+#include "mozilla/dom/devicestorage/PDeviceStorageRequestParent.h"
+#include "mozilla/dom/ContentChild.h"
+
+#include "nsThreadUtils.h"
+#include "nsDeviceStorage.h"
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+class DeviceStorageRequestParent : public PDeviceStorageRequestParent
+{
+public:
+  DeviceStorageRequestParent(const DeviceStorageParams& aParams);
+  ~DeviceStorageRequestParent();
+
+private:
+  class PostErrorEvent : public nsRunnable
+  {
+    public:
+      PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError);
+      ~PostErrorEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsString mError;
+  };
+
+  class PostSuccessEvent : public nsRunnable
+  {
+    public:
+      PostSuccessEvent(DeviceStorageRequestParent* aParent);
+      ~PostSuccessEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+  };
+
+  class PostBlobSuccessEvent : public nsRunnable
+  {
+    public:
+      PostBlobSuccessEvent(DeviceStorageRequestParent* aParent, void* aBuffer, PRUint32 aLength, nsACString& aMimeType);
+      ~PostBlobSuccessEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      InfallibleTArray<PRUint8> mBits;
+      nsCString mMimeType;
+  };
+
+  class PostEnumerationSuccessEvent : public nsRunnable
+  {
+    public:
+      PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, InfallibleTArray<DeviceStorageFileValue>& aPaths);
+      ~PostEnumerationSuccessEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      InfallibleTArray<DeviceStorageFileValue> mPaths;
+  };
+
+  class WriteFileEvent : public nsRunnable
+  {
+    public:
+      WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, InfallibleTArray<PRUint8>& aBits);
+      ~WriteFileEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsRefPtr<DeviceStorageFile> mFile;
+    InfallibleTArray<PRUint8> mBits; // another copy?
+  };
+
+  class DeleteFileEvent : public nsRunnable
+  {
+    public:
+      DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      ~DeleteFileEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsRefPtr<DeviceStorageFile> mFile;
+  };
+
+  class ReadFileEvent : public nsRunnable
+  {
+    public:
+      ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      ~ReadFileEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsRefPtr<DeviceStorageFile> mFile;
+      nsCString mMimeType;
+  };
+
+  class EnumerateFileEvent : public nsRunnable
+  {
+    public:
+      EnumerateFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, PRUint32 aSince);
+      ~EnumerateFileEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsRefPtr<DeviceStorageFile> mFile;
+      PRUint32 mSince;
+  };
+
+  class PostPathResultEvent : public nsRunnable
+  {
+    public:
+      PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
+      ~PostPathResultEvent();
+      NS_IMETHOD Run();
+    private:
+      DeviceStorageRequestParent* mParent;
+      nsRefPtr<DeviceStorageFile> mFile;
+      InfallibleTArray<PRUint8> mBits;
+      nsString mPath;
+  };
+
+};
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/devicestorage/Makefile.in
+++ b/dom/devicestorage/Makefile.in
@@ -12,31 +12,40 @@ include $(DEPTH)/config/autoconf.mk
 MODULE           = dom
 LIBRARY_NAME     = domdevicestorage_s
 XPIDL_MODULE     = dom_devicestorage
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
+EXPORTS_NAMESPACES = mozilla/dom/devicestorage
+
+EXPORTS_mozilla/dom/devicestorage = \
+  DeviceStorageRequestChild.h \
+  DeviceStorageRequestParent.h \
+  $(NULL)
+
 CPPSRCS		= \
 		nsDeviceStorage.cpp \
+		DeviceStorageRequestParent.cpp \
+		DeviceStorageRequestChild.cpp \
 		$(NULL)
 
 EXPORTS         = \
 		nsDeviceStorage.h \
 		$(NULL)
 
 LOCAL_INCLUDES = \
 		-I$(topsrcdir)/dom/base \
 		-I$(topsrcdir)/dom/ipc \
 		-I$(topsrcdir)/content/base/src \
 		-I$(topsrcdir)/content/events/src \
 		$(NULL)
 
-TEST_DIRS += test
+TEST_DIRS += test ipc
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -D_IMPL_NS_LAYOUT
 
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; 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 protocol PContent;
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+
+struct ErrorResponse
+{
+  nsString error;
+};
+
+struct SuccessResponse
+{
+};
+
+struct BlobResponse
+{
+  // todo going away
+  PRUint8[] bits;
+  nsCString contentType;
+};
+
+struct DeviceStorageFileValue
+{
+  // todo going away
+  nsString fullpath;
+  nsString path;
+};
+
+struct EnumerationResponse
+{
+  DeviceStorageFileValue[] paths;
+  // todo bent PBlob
+};
+
+union DeviceStorageResponseValue
+{
+  ErrorResponse;
+  SuccessResponse;
+  BlobResponse;
+  EnumerationResponse;
+};
+
+sync protocol PDeviceStorageRequest {
+    manager PContent;
+child:
+    __delete__(DeviceStorageResponseValue response);
+};
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/ipc/Makefile.in
@@ -0,0 +1,26 @@
+# 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/.
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = dom/devicestorage/ipc
+
+include $(DEPTH)/config/autoconf.mk
+
+
+TEST_FILES = \
+  test_ipc.html \
+  ../test/devicestorage_common.js \
+  $(NULL)
+
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
+
+libs:: $(TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") \
+	$(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)\
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/ipc/test_ipc.html
@@ -0,0 +1,160 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for OOP DeviceStorage</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();
+
+    function iframeScriptFirst() {
+      content.wrappedJSObject.RunSet.reloadAndRunAll({
+        preventDefault: function() { }
+      });
+    }
+
+    function iframeScriptSecond() {
+      let TestRunner = content.wrappedJSObject.TestRunner;
+
+      let oldComplete = TestRunner.onComplete;
+
+      TestRunner.onComplete = function() {
+        TestRunner.onComplete = oldComplete;
+
+        sendAsyncMessage("test:DeviceStorage:ipcTestComplete", {
+          result: JSON.stringify(TestRunner._failedTests)
+        });
+
+        if (oldComplete) {
+          oldComplete();
+        }
+      };
+
+      let oldLog = TestRunner.log;
+      TestRunner.log = function(msg) {
+        sendAsyncMessage("test:DeviceStorage:ipcTestMessage", { msg: msg });
+      }
+    }
+
+    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
+
+    function onTestMessage(data) {
+      let message = data.json.msg;
+      let match = regex.exec(message);
+      if (match) {
+        let state = match[1];
+        let details = match[2] + " | " + match[3];
+
+        switch (state) {
+          case "TEST-PASS":
+          case "TEST-KNOWN-FAIL":
+            ok(true, details);
+            break;
+
+          case "TEST-UNEXPECTED-FAIL":
+          case "TEST-UNEXPECTED-PASS":
+            ok(false, details);
+            break;
+
+          case "TEST-DEBUG-INFO":
+          default:
+            info(details);
+        }
+      }
+    }
+
+    function onTestComplete() {
+      let comp = SpecialPowers.wrap(Components);
+      SimpleTest.executeSoon(function () { SimpleTest.finish(); });
+    }
+
+    function runTests() {
+      let iframe = document.createElement("iframe");
+      iframe.mozbrowser = true;
+      iframe.id = "iframe";
+      iframe.style.width = "100%";
+      iframe.style.height = "1000px";
+
+      function iframeLoadSecond() {
+        ok(true, "Got second iframe load event.");
+        iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
+        let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+        mm.loadFrameScript("data:,(" + iframeScriptSecond.toString() + ")();",
+                           false);
+      }
+
+      function iframeLoadFirst() {
+        ok(true, "Got first iframe load event.");
+        iframe.removeEventListener("mozbrowserloadend", iframeLoadFirst);
+        iframe.addEventListener("mozbrowserloadend", iframeLoadSecond);
+
+        let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+
+        let comp = SpecialPowers.wrap(Components);
+
+        let spObserver =
+          comp.classes["@mozilla.org/special-powers-observer;1"]
+              .getService(comp.interfaces.nsIFrameMessageListener);
+
+        mm.addMessageListener("SPPrefService", spObserver);
+        mm.addMessageListener("SPProcessCrashService", spObserver);
+        mm.addMessageListener("SPPingService", spObserver);
+        mm.addMessageListener("SpecialPowers.Quit", spObserver);
+        mm.addMessageListener("SPPermissionManager", spObserver);
+
+        mm.addMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage);
+        mm.addMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete);
+
+        let specialPowersBase = "chrome://specialpowers/content/";
+        mm.loadFrameScript(specialPowersBase + "MozillaLogger.js", false);
+        mm.loadFrameScript(specialPowersBase + "specialpowersAPI.js", false);
+        mm.loadFrameScript(specialPowersBase + "specialpowers.js", false);
+
+        mm.loadFrameScript("data:,(" + iframeScriptFirst.toString() + ")();", false);
+      }
+
+      iframe.addEventListener("mozbrowserloadend", iframeLoadFirst);
+
+      // Strip this filename and one directory level and then add "/test".
+      let href =  window.location.href;
+      href = href.substring(0, href.lastIndexOf('/'));
+      href = href.substring(0, href.lastIndexOf('/'));
+      iframe.src = href + "/test?consoleLevel=INFO";
+
+      document.body.appendChild(iframe);
+    }
+
+    addEventListener("load", function() {
+      let whitelist;
+      try {
+        whitelist =
+          SpecialPowers.getCharPref("dom.mozBrowserFramesWhitelist") + ", ";
+      } catch (e) {
+        whitelist = "";
+      }
+
+      whitelist += window.location.protocol + "//" + window.location.host;
+
+      SpecialPowers.pushPrefEnv({
+        "set": [
+
+          ["device.storage.enabled", true],
+          ["device.storage.testing", true],
+          ["device.storage.prompt.testing", true],
+
+          ["dom.ipc.browser_frames.oop_by_default", true],
+          ["dom.mozBrowserFramesEnabled", true],
+          ["browser.pageThumbs.enabled", false],
+          ["dom.mozBrowserFramesWhitelist", whitelist]
+        ]
+      }, runTests);
+    });
+
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/ipdl.mk
@@ -0,0 +1,7 @@
+# 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 = \
+  PDeviceStorageRequest.ipdl \
+  $(NULL)
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1,142 +1,263 @@
 /* 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 "nsDeviceStorage.h"
-#include "DOMRequest.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsNetUtil.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIContentPermissionPrompt.h"
 #include "nsIPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "nsJSUtils.h"
 #include "DictionaryHelpers.h"
 #include "mozilla/Attributes.h"
 #include "nsContentUtils.h"
+#include "nsXULAppAPI.h"
+#include "TabChild.h"
+#include "DeviceStorageRequestChild.h"
+
+// Microsoft's API Name hackery sucks
+#undef CreateEvent
 
 using namespace mozilla::dom;
+using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
-class DeviceStorageFile MOZ_FINAL : public nsISupports {
-public:
-
-  nsCOMPtr<nsIFile> mFile;
-  nsString mPath;
-  bool mEditable;
-
-  DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
+DeviceStorageFile::DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
   : mPath(aPath)
   , mEditable(false)
-  {
-    NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
-    // always take a clone
-    nsCOMPtr<nsIFile> file;
-    aFile->Clone(getter_AddRefs(mFile));
+{
+  NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
+  // always take a clone
+  nsCOMPtr<nsIFile> file;
+  aFile->Clone(getter_AddRefs(mFile));
 
-    AppendRelativePath();
+  AppendRelativePath();
+  NormalizeFilePath();
+}
 
-    NormalizeFilePath();
-  }
-
-  DeviceStorageFile(nsIFile* aFile)
+DeviceStorageFile::DeviceStorageFile(nsIFile* aFile)
   : mEditable(false)
-  {
-    NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
-    // always take a clone
-    nsCOMPtr<nsIFile> file;
-    aFile->Clone(getter_AddRefs(mFile));
+{
+  NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
+  // always take a clone
+  nsCOMPtr<nsIFile> file;
+  aFile->Clone(getter_AddRefs(mFile));
+}
+
+void
+DeviceStorageFile::SetPath(const nsAString& aPath) {
+  mPath.Assign(aPath);
+  NormalizeFilePath();
+}
+
+void
+DeviceStorageFile::SetEditable(bool aEditable) {
+  mEditable = aEditable;
+}
+
+// we want to make sure that the names of file can't reach
+// outside of the type of storage the user asked for.
+bool
+DeviceStorageFile::IsSafePath()
+{
+  nsAString::const_iterator start, end;
+  mPath.BeginReading(start);
+  mPath.EndReading(end);
+
+  // if the path has a ~ or \ in it, return false.
+  NS_NAMED_LITERAL_STRING(tilde, "~");
+  NS_NAMED_LITERAL_STRING(bslash, "\\");
+  if (FindInReadable(tilde, start, end) ||
+      FindInReadable(bslash, start, end)) {
+    return false;
+   }
+  // split on /.  if any token is "", ., or .., return false.
+  NS_ConvertUTF16toUTF8 cname(mPath);
+  char* buffer = cname.BeginWriting();
+  const char* token;
+
+  while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
+    if (PL_strcmp(token, "") == 0 ||
+	PL_strcmp(token, ".") == 0 ||
+	PL_strcmp(token, "..") == 0 ) {
+      return false;
+    }
   }
+  return true;
+}
 
-  void
-  setPath(const nsAString& aPath) {
-    mPath.Assign(aPath);
-    NormalizeFilePath();
+void
+DeviceStorageFile::NormalizeFilePath() {
+#if defined(XP_WIN)
+  PRUnichar* cur = mPath.BeginWriting();
+  PRUnichar* end = mPath.EndWriting();
+  for (; cur < end; ++cur) {
+    if (PRUnichar('\\') == *cur)
+      *cur = PRUnichar('/');
   }
+#endif
+}
 
-  void
-  setEditable(bool aEditable) {
-    mEditable = aEditable;
+void
+DeviceStorageFile::AppendRelativePath() {
+#if defined(XP_WIN)
+  // replace forward slashes with backslashes,
+  // since nsLocalFileWin chokes on them
+  nsString temp;
+  temp.Assign(mPath);
+
+  PRUnichar* cur = temp.BeginWriting();
+  PRUnichar* end = temp.EndWriting();
+
+  for (; cur < end; ++cur) {
+    if (PRUnichar('/') == *cur)
+      *cur = PRUnichar('\\');
+  }
+  mFile->AppendRelativePath(temp);
+#else
+  mFile->AppendRelativePath(mPath);
+#endif
+}
+
+nsresult
+DeviceStorageFile::Write(nsIDOMBlob* aBlob)
+{
+  nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsCOMPtr<nsIInputStream> stream;
+  aBlob->GetInternalStream(getter_AddRefs(stream));
+
+  PRUint32 bufSize;
+  stream->Available(&bufSize);
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
+
+  if (!outputStream) {
+    return NS_ERROR_FAILURE;
   }
 
-  NS_DECL_ISUPPORTS
+  nsCOMPtr<nsIOutputStream> bufferedOutputStream;
+  NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
+			     outputStream,
+			     4096*4);
 
-  // we want to make sure that the names of file can't reach
-  // outside of the type of storage the user asked for.
-  bool
-  isSafePath()
-  {
-    nsAString::const_iterator start, end;
-    mPath.BeginReading(start);
-    mPath.EndReading(end);
+  if (!bufferedOutputStream) {
+    return NS_ERROR_FAILURE;
+  }
 
-    // if the path has a ~ or \ in it, return false.
-    NS_NAMED_LITERAL_STRING(tilde, "~");
-    NS_NAMED_LITERAL_STRING(bslash, "\\");
-    if (FindInReadable(tilde, start, end) ||
-        FindInReadable(bslash, start, end)) {
-      return false;
-    }
+  PRUint32 wrote;
+  bufferedOutputStream->WriteFrom(stream, bufSize, &wrote);
+  bufferedOutputStream->Close();
+  outputStream->Close();
+  if (bufSize != wrote) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
 
-    // split on /.  if any token is "", ., or .., return false.
-    NS_ConvertUTF16toUTF8 cname(mPath);
-    char* buffer = cname.BeginWriting();
-    const char* token;
-  
-    while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
-      if (PL_strcmp(token, "") == 0 ||
-          PL_strcmp(token, ".") == 0 ||
-          PL_strcmp(token, "..") == 0 ) {
-            return false;
-      }
-    }
-    return true;
+nsresult
+DeviceStorageFile::Write(InfallibleTArray<PRUint8>& aBits) {
+
+  nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
+
+  if (!outputStream) {
+    return NS_ERROR_FAILURE;
   }
 
-private:
-  void NormalizeFilePath() {
-#if defined(XP_WIN)
-    PRUnichar* cur = mPath.BeginWriting();
-    PRUnichar* end = mPath.EndWriting();
-    for (; cur < end; ++cur) {
-      if (PRUnichar('\\') == *cur)
-	*cur = PRUnichar('/');
-    }
-#endif
+  PRUint32 wrote;
+  outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
+  outputStream->Close();
+
+  if (aBits.Length() != wrote) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+void
+DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
+				PRUint64 aSince)
+{
+  nsString rootPath;
+  mFile->GetPath(rootPath);
+
+  return collectFilesInternal(aFiles, aSince, rootPath);
+}
+
+void
+DeviceStorageFile::collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
+					PRUint64 aSince,
+					nsAString& aRootPath)
+{
+  nsCOMPtr<nsISimpleEnumerator> e;
+  mFile->GetDirectoryEntries(getter_AddRefs(e));
+
+  if (!e) {
+    return;
   }
 
-  void AppendRelativePath() {
-#if defined(XP_WIN)
-    // replace forward slashes with backslashes,
-    // since nsLocalFileWin chokes on them
-    nsString temp;
-    temp.Assign(mPath);
+  nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
+  nsCOMPtr<nsIFile> f;
+
+  while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
+
+    PRInt64 msecs;
+    f->GetLastModifiedTime(&msecs);
 
-    PRUnichar* cur = temp.BeginWriting();
-    PRUnichar* end = temp.EndWriting();
+    if (msecs < aSince) {
+      continue;
+     }
+
+    bool isDir;
+    f->IsDirectory(&isDir);
+
+    bool isFile;
+    f->IsFile(&isFile);
 
-    for (; cur < end; ++cur) {
-      if (PRUnichar('/') == *cur)
-        *cur = PRUnichar('\\');
+    nsString fullpath;
+    f->GetPath(fullpath);
+
+    if (!StringBeginsWith(fullpath, aRootPath)) {
+      NS_ERROR("collectFiles returned a path that does not belong!");
+      continue;
     }
-    mFile->AppendRelativePath(temp);
-#else
-    mFile->AppendRelativePath(mPath);
-#endif
+
+    nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
+    nsDependentSubstring newPath = Substring(fullpath, len);
+
+    if (isDir) {
+      DeviceStorageFile dsf(f);
+      dsf.SetPath(newPath);
+      dsf.collectFilesInternal(aFiles, aSince, aRootPath);
+    } else if (isFile) {
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      dsf->SetPath(newPath);
+      aFiles.AppendElement(dsf);
+    }
   }
-
-};
+}
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
 
 
 // TODO - eventually, we will want to factor this method
 // out into different system specific subclasses (or
 // something)
 PRInt32
@@ -238,56 +359,60 @@ static jsval nsIFileToJsval(nsPIDOMWindo
     return JSVAL_NULL;
   }
 
   if (aFile == nsnull) {
     return JSVAL_NULL;
   }
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(aFile->mFile, aFile->mPath);
+  return BlobToJsval(aWindow, blob);
+}
 
+
+jsval BlobToJsval(nsPIDOMWindow* aWindow, nsIDOMBlob* aBlob)
+{
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
   }
-    
+
   nsIScriptContext *scriptContext = sgo->GetScriptContext();
   if (!scriptContext) {
     return JSVAL_NULL;
   }
 
   JSContext *cx = scriptContext->GetNativeContext();
   if (!cx) {
     return JSVAL_NULL;
   }
 
   jsval wrappedFile;
   nsresult rv = nsContentUtils::WrapNative(cx,
                                            JS_GetGlobalObject(cx),
-                                           blob,
+                                           aBlob,
                                            &NS_GET_IID(nsIDOMFile),
                                            &wrappedFile);
   if (NS_FAILED(rv)) {
     return JSVAL_NULL;
   }
 
   return wrappedFile;
 }
 
-
-static jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
+jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWindow, "Null Window");
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
   }
-    
+
   nsIScriptContext *scriptContext = sgo->GetScriptContext();
   if (!scriptContext) {
     return JSVAL_NULL;
   }
 
   JSContext *cx = scriptContext->GetNativeContext();
   if (!cx) {
     return JSVAL_NULL;
@@ -298,61 +423,47 @@ static jsval StringToJsval(nsPIDOMWindow
   jsval result = JSVAL_NULL;
   if (!xpc::StringToJsval(cx, aString, &result)) {
     return JSVAL_NULL;
   }
 
   return result;
 }
 
-
-class nsDOMDeviceStorageCursor
-  : public nsIDOMDeviceStorageCursor
-  , public DOMRequest
-  , public nsIContentPermissionRequest
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
-  NS_DECL_NSIDOMDEVICESTORAGECURSOR
-
-  nsDOMDeviceStorageCursor(nsIDOMWindow* aWindow,
-                           nsIURI* aURI,
-                           DeviceStorageFile* aFile,
-                           PRUint64 aSince);
-
-private:
-  ~nsDOMDeviceStorageCursor();
-
-protected:
-  nsTArray<nsRefPtr<DeviceStorageFile> > mFiles;
-
-  bool mOkToCallContinue;
-  nsRefPtr<DeviceStorageFile> mFile;
-  nsCOMPtr<nsIURI> mURI;
-  PRUint64 mSince;
-
-  // to access mFiles
-  friend class InitCursorEvent;
-  friend class ContinueCursorEvent;
-};
-
-class DeviceStorageCursorRequest MOZ_FINAL : public nsIContentPermissionRequest
+class DeviceStorageCursorRequest MOZ_FINAL
+  : public nsIContentPermissionRequest
+  , public PCOMContentPermissionRequestChild
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest, nsIContentPermissionRequest)
 
   NS_FORWARD_NSICONTENTPERMISSIONREQUEST(mCursor->);
 
   DeviceStorageCursorRequest(nsDOMDeviceStorageCursor* aCursor)
     : mCursor(aCursor) { }
 
   ~DeviceStorageCursorRequest() {}
 
+  bool Recv__delete__(const bool& allow)
+  {
+    if (allow) {
+      Allow();
+    }
+    else {
+      Cancel();
+    }
+    return true;
+  }
+
+  void IPDLRelease()
+  {
+    Release();
+  }
+
 private:
   nsRefPtr<nsDOMDeviceStorageCursor> mCursor;
 };
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
 NS_INTERFACE_MAP_END
@@ -365,23 +476,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(De
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCursor)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DeviceStorageCursorRequest)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCursor, nsIDOMDeviceStorageCursor)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
-#define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "File location doesn't exists"
-#define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE         "File location is not enumerable"
-#define POST_ERROR_EVENT_PERMISSION_DENIED           "Permission Denied"
-#define POST_ERROR_EVENT_ILLEGAL_FILE_NAME           "Illegal file name"
-#define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
-#define POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED "Non-string type unsupported"
-
 class PostErrorEvent : public nsRunnable
 {
 public:
   PostErrorEvent(nsRefPtr<DOMRequest>& aRequest, const char* aMessage, DeviceStorageFile* aFile)
   {
     mRequest.swap(aRequest);
     BuildErrorString(aMessage, aFile);
   }
@@ -399,84 +503,79 @@ public:
     nsAutoString fullPath;
 
     if (aFile && aFile->mFile) {
       aFile->mFile->GetPath(fullPath);
     }
     else {
       fullPath.Assign(NS_LITERAL_STRING("null file"));
     }
-      
+
     mError = NS_ConvertASCIItoUTF16(aMessage);
     mError.Append(NS_LITERAL_STRING(" file path = "));
     mError.Append(fullPath.get());
     mError.Append(NS_LITERAL_STRING(" path = "));
 
     if (aFile) {
       mError.Append(aFile->mPath);
     }
     else {
       mError.Append(NS_LITERAL_STRING("null path"));
     }
   }
 
-  NS_IMETHOD Run() {
+  NS_IMETHOD Run()
+  {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     mRequest->FireError(mError);
     mRequest = nsnull;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DOMRequest> mRequest;
   nsString mError;
 };
 
-class ContinueCursorEvent : public nsRunnable
+ContinueCursorEvent::ContinueCursorEvent(nsRefPtr<DOMRequest>& aRequest)
 {
-public:
+  mRequest.swap(aRequest);
+}
+
+ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest)
+  : mRequest(aRequest)
+{
+}
+
+ContinueCursorEvent::~ContinueCursorEvent() {}
 
-  ContinueCursorEvent(nsRefPtr<DOMRequest>& aRequest)
-  {
-    mRequest.swap(aRequest);
+NS_IMETHODIMP
+ContinueCursorEvent::Run() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  jsval val;
+
+  nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
+  if (cursor->mFiles.Length() == 0) {
+    val = JSVAL_NULL;
   }
+  else {
+    nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
+    cursor->mFiles.RemoveElementAt(0);
 
-  ContinueCursorEvent(DOMRequest* aRequest)
-    : mRequest(aRequest)
-  {
+    // todo, this blob needs to be opened in the parent.  This will be signifincally easier when bent lands
+    val = nsIFileToJsval(cursor->GetOwner(), file);
+    cursor->mOkToCallContinue = true;
   }
 
-  ~ContinueCursorEvent() {}
-
-  NS_IMETHOD Run() {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-    jsval val;
-
-    nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
-    if (cursor->mFiles.Length() == 0) {
-      val = JSVAL_NULL;
-    }
-    else {
-      nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
-      cursor->mFiles.RemoveElementAt(0);
-      val = nsIFileToJsval(cursor->GetOwner(), file);
-      cursor->mOkToCallContinue = true;
-    }
-
-    mRequest->FireSuccess(val);
-    mRequest = nsnull;
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<DOMRequest> mRequest;
-};
-
+  mRequest->FireSuccess(val);
+  mRequest = nsnull;
+  return NS_OK;
+}
 
 class InitCursorEvent : public nsRunnable
 {
 public:
     InitCursorEvent(DOMRequest* aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
     , mRequest(aRequest)
   {
@@ -492,81 +591,25 @@ public:
     if (!check) {
       nsCOMPtr<PostErrorEvent> event = new PostErrorEvent(mRequest,
                                                           POST_ERROR_EVENT_FILE_NOT_ENUMERABLE,
                                                           mFile);
       NS_DispatchToMainThread(event);
       return NS_OK;
     }
 
-    collectFiles(mFile);
+    nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
+    mFile->CollectFiles(cursor->mFiles, cursor->mSince);
 
     nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(mRequest);
     NS_DispatchToMainThread(event);
 
     return NS_OK;
   }
 
-  void collectFiles(DeviceStorageFile* aFile)
-  {
-      // TODO - we may want to do this incrementally.
-    if (!aFile) {
-      return;
-    }
-
-    nsCOMPtr<nsISimpleEnumerator> e;
-    aFile->mFile->GetDirectoryEntries(getter_AddRefs(e));
-
-    if (!e) {
-      return;
-    }
-
-    nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
-    nsCOMPtr<nsIFile> f;
-
-    while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
-      nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
-
-      PRInt64 msecs;
-      f->GetLastModifiedTime(&msecs);
-
-      if (msecs < (PRInt64) cursor->mSince) {
-        continue;
-      }
-
-      bool isDir;
-      f->IsDirectory(&isDir);
-
-      bool isFile;
-      f->IsFile(&isFile);
-
-      nsString fullpath;
-      f->GetPath(fullpath);
-
-      nsString rootPath;
-      mFile->mFile->GetPath(rootPath);
-
-      if (!StringBeginsWith(fullpath, rootPath)) {
-	NS_WARNING("collectFiles returned a path that does not belong!");
-	continue;
-      }
-
-      nsAString::size_type len = rootPath.Length() + 1; // +1 for the trailing /
-      nsDependentSubstring newPath = Substring(fullpath, len);
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
-      dsf->setPath(newPath);
-
-      if (isDir) {
-        collectFiles(dsf);
-      }
-      else if (isFile) {
-        cursor->mFiles.AppendElement(dsf);
-      }
-    }
-  }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 DOMCI_DATA(DeviceStorageCursor, nsDOMDeviceStorageCursor)
 
@@ -582,19 +625,19 @@ NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStor
 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMRequest)
 
 nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsIDOMWindow* aWindow,
                                                    nsIURI* aURI,
                                                    DeviceStorageFile* aFile,
                                                    PRUint64 aSince)
   : DOMRequest(aWindow)
   , mOkToCallContinue(false)
+  , mSince(aSince)
   , mFile(aFile)
   , mURI(aURI)
-  , mSince(aSince)
 {
 }
 
 nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor()
 {
 }
 
 NS_IMETHODIMP
@@ -633,24 +676,35 @@ nsDOMDeviceStorageCursor::Cancel()
                                                       mFile);
   NS_DispatchToMainThread(event);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::Allow()
 {
-  if (!mFile->isSafePath()) {
+  if (!mFile->IsSafePath()) {
     nsCOMPtr<nsIRunnable> r = new PostErrorEvent(this,
                                                  POST_ERROR_EVENT_ILLEGAL_FILE_NAME,
                                                  mFile);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+
+    nsString fullpath;
+    mFile->mFile->GetPath(fullpath);
+
+    PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile);
+    DeviceStorageEnumerationParams params(fullpath, mSince);
+    ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   NS_ASSERTION(target, "Must have stream transport service");
 
   nsCOMPtr<InitCursorEvent> event = new InitCursorEvent(this, mFile);
   target->Dispatch(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
@@ -672,16 +726,33 @@ nsDOMDeviceStorageCursor::Continue()
 
   nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(this);
   NS_DispatchToMainThread(event);
 
   mOkToCallContinue = false;
   return NS_OK;
 }
 
+bool
+nsDOMDeviceStorageCursor::Recv__delete__(const bool& allow)
+{
+  if (allow) {
+    Allow();
+  }
+  else {
+    Cancel();
+  }
+  return true;
+}
+
+void
+nsDOMDeviceStorageCursor::IPDLRelease()
+{
+  Release();
+}
 
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
     {
       mRequest.swap(aRequest);
@@ -690,17 +761,17 @@ public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, const nsAString & aPath)
     : mPath(aPath)
     {
       mRequest.swap(aRequest);
     }
 
   ~PostResultEvent() {}
 
-  NS_IMETHOD Run() 
+  NS_IMETHOD Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     jsval result = JSVAL_NULL;
     if (mFile) {
       result = nsIFileToJsval(mRequest->GetOwner(), mFile);
     } else {
       result = StringToJsval(mRequest->GetOwner(), mPath);
@@ -715,96 +786,45 @@ private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsString mPath;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class WriteFileEvent : public nsRunnable
 {
 public:
-  WriteFileEvent(nsIDOMBlob *aBlob,
+  WriteFileEvent(nsIDOMBlob* aBlob,
                  DeviceStorageFile *aFile,
                  nsRefPtr<DOMRequest>& aRequest)
   : mBlob(aBlob)
   , mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
 
   ~WriteFileEvent() {}
 
-  void CleanupOnFail(const char* error)
-  {
-    if (mFile) {
-      mFile->mFile->Remove(false);
-    }
-
-    nsCOMPtr<PostErrorEvent> event = new PostErrorEvent(mRequest,
-                                                        error,
-                                                        mFile);
-    NS_DispatchToMainThread(event);
-  }
-
-  NS_IMETHOD Run() 
+  NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-    //TODO - this might be faster if we check to see if
-    //these are backed by OS-files, and if so, then just do
-    //a copy()
+    nsresult rv = mFile->Write(mBlob);
 
-    nsCOMPtr<nsIFile> f = mFile->mFile;
-
-    // This also creates all ancestors
-    nsresult rv = f->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
     if (NS_FAILED(rv)) {
-      CleanupOnFail(POST_ERROR_EVENT_UNKNOWN " 1 ");
-      return NS_OK;
-    }
-    
-    nsCOMPtr<nsIInputStream> stream;
-    mBlob->GetInternalStream(getter_AddRefs(stream));
+      mFile->mFile->Remove(false);
 
-    if (!stream) {
-      CleanupOnFail(POST_ERROR_EVENT_UNKNOWN " 2 ");
+      nsCOMPtr<PostErrorEvent> event = new PostErrorEvent(mRequest,
+							  POST_ERROR_EVENT_UNKNOWN,
+							  mFile);
+      NS_DispatchToMainThread(event);
       return NS_OK;
     }
 
-    PRUint32 bufSize;
-    stream->Available(&bufSize);
-
-    nsCOMPtr<nsIOutputStream> outputStream;
-    NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), f);
-
-    if (!outputStream) {
-      CleanupOnFail(POST_ERROR_EVENT_UNKNOWN " 3 ");
-      return NS_OK;
-    }
-
-    nsCOMPtr<nsIOutputStream> bufferedOutputStream;
-    NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
-                               outputStream,
-                               4096*4);
-
-    if (!bufferedOutputStream) {
-      CleanupOnFail(POST_ERROR_EVENT_UNKNOWN " 4" );
-      return NS_OK;
-    }
-
-    PRUint32 wrote;
-    bufferedOutputStream->WriteFrom(stream, bufSize, &wrote);
-    bufferedOutputStream->Close();
-    outputStream->Close();
-
-    if (bufSize != wrote) {
-      CleanupOnFail(POST_ERROR_EVENT_UNKNOWN " 5 " );
-      return NS_OK;
-    }
-
-    nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest, mFile->mPath);
+    nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest,
+							  mFile->mPath);
     NS_DispatchToMainThread(event);
 
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<DeviceStorageFile> mFile;
@@ -817,17 +837,17 @@ public:
                   nsRefPtr<DOMRequest>& aRequest)
   : mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
 
   ~ReadFileEvent() {}
 
-  NS_IMETHOD Run() 
+  NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
     nsRefPtr<nsRunnable> r;
 
     if (!mFile->mEditable) {
       bool check = false;
       mFile->mFile->Exists(&check);
@@ -854,18 +874,18 @@ public:
   DeleteFileEvent(DeviceStorageFile* aFile,
                   nsRefPtr<DOMRequest>& aRequest)
   : mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
 
   ~DeleteFileEvent() {}
-    
-  NS_IMETHOD Run() 
+
+  NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
     mFile->mFile->Remove(true);
 
     nsRefPtr<nsRunnable> r;
 
     bool check = false;
@@ -880,17 +900,20 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
-class DeviceStorageRequest MOZ_FINAL : public nsIContentPermissionRequest, public nsIRunnable
+class DeviceStorageRequest MOZ_FINAL
+  : public nsIContentPermissionRequest
+  , public nsIRunnable
+  , public PCOMContentPermissionRequestChild
 {
 public:
 
     enum {
         DEVICE_STORAGE_REQUEST_READ,
         DEVICE_STORAGE_REQUEST_WRITE,
         DEVICE_STORAGE_REQUEST_DELETE
     };
@@ -912,16 +935,35 @@ public:
 
   NS_IMETHOD Run() {
 
     if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
       Allow();
       return NS_OK;
     }
 
+    if (XRE_GetProcessType() == GeckoProcessType_Content) {
+
+      // because owner implements nsITabChild, we can assume that it is
+      // the one and only TabChild.
+      TabChild* child = GetTabChildFrom(mWindow->GetDocShell());
+      if (!child)
+	return false;
+
+      // Retain a reference so the object isn't deleted without IPDL's knowledge.
+      // Corresponding release occurs in DeallocPContentPermissionRequest.
+      AddRef();
+
+      nsCString type = NS_LITERAL_CSTRING("device-storage");
+      child->SendPContentPermissionRequestConstructor(this, type, IPC::URI(mURI));
+
+      Sendprompt();
+      return NS_OK;
+    }
+
     nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
     if (prompt) {
       prompt->Prompt(this);
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetType(nsACString & aType)
@@ -960,46 +1002,98 @@ public:
   NS_IMETHOD Allow()
   {
     nsCOMPtr<nsIRunnable> r;
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
+    nsString fullpath;
+    mFile->mFile->GetPath(fullpath);
+
     switch(mRequestType) {
       case DEVICE_STORAGE_REQUEST_WRITE:
       {
         if (!mBlob) {
           return NS_ERROR_FAILURE;
         }
 
-        r = new WriteFileEvent(mBlob, mFile, mRequest);
+	if (XRE_GetProcessType() != GeckoProcessType_Default) {
+	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+
+	  nsCOMPtr<nsIInputStream> stream;
+	  mBlob->GetInternalStream(getter_AddRefs(stream));
+
+	  InfallibleTArray<PRUint8> bits;
+	  PRUint32 bufSize, numRead;
+
+	  stream->Available(&bufSize);
+	  bits.SetCapacity(bufSize);
+
+	  void* buffer = (void*) bits.Elements();
+
+	  stream->Read((char*)buffer, bufSize, &numRead);
+
+	  DeviceStorageAddParams params(fullpath, bits);
+	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+	  return NS_OK;
+	}
+	r = new WriteFileEvent(mBlob, mFile, mRequest);
         break;
       }
       case DEVICE_STORAGE_REQUEST_READ:
       {
+	if (XRE_GetProcessType() != GeckoProcessType_Default) {
+	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+	  DeviceStorageGetParams params(fullpath);
+	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+	  return NS_OK;
+	}
+
         r = new ReadFileEvent(mFile, mRequest);
         break;
       }
       case DEVICE_STORAGE_REQUEST_DELETE:
       {
+	if (XRE_GetProcessType() != GeckoProcessType_Default) {
+	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+	  DeviceStorageDeleteParams params(fullpath);
+	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+	  return NS_OK;
+	}
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
     }
-    
+
     if (r) {
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
     return NS_OK;
   }
 
+  bool Recv__delete__(const bool& allow)
+  {
+    if (allow) {
+      Allow();
+    }
+    else {
+      Cancel();
+    }
+    return true;
+  }
+
+  void IPDLRelease()
+  {
+    Release();
+  }
+
 private:
   PRInt32 mRequestType;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIURI> mURI;
   nsRefPtr<DeviceStorageFile> mFile;
 
   nsRefPtr<DOMRequest> mRequest;
   nsCOMPtr<nsIDOMBlob> mBlob;
@@ -1061,17 +1155,16 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* 
   // Grab the uri of the document
   nsCOMPtr<nsIDOMDocument> domdoc;
   aWindow->GetDocument(getter_AddRefs(domdoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
   doc->NodePrincipal()->GetURI(getter_AddRefs(mURI));
-
   return NS_OK;
 }
 
 nsDOMDeviceStorage::~nsDOMDeviceStorage()
 {
 }
 
 void
@@ -1119,16 +1212,17 @@ nsDOMDeviceStorage::GetType(nsAString & 
       break;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval NS_OUTPARAM)
 {
+  // possible race here w/ unique filename
   char buffer[128];
   NS_MakeRandomString(buffer, 128);
 
   nsString path;
   path.AssignWithConversion(nsDependentCString(buffer));
 
   return AddNamed(aBlob, path, _retval);
 }
@@ -1141,25 +1235,25 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   // if the blob is null here, bail
   if (aBlob == nsnull)
     return NS_OK;
 
   nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mOwner);
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
-  
+
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, aPath);
 
-  if (!dsf->isSafePath()) {
+  if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   }
   else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WRITE,
                                  win, mURI, dsf, request, aBlob);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
@@ -1204,19 +1298,19 @@ nsDOMDeviceStorage::GetInternal(const JS
     r = new PostErrorEvent(request,
                            POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED,
                            dsf);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
-  dsf->setEditable(aEditable);
+  dsf->SetEditable(aEditable);
 
-  if (!dsf->isSafePath()) {
+  if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   } else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_READ,
                                  win, mURI, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -1240,17 +1334,17 @@ nsDOMDeviceStorage::Delete(const JS::Val
     nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
     r = new PostErrorEvent(request, POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED, dsf);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
 
-  if (!dsf->isSafePath()) {
+  if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   }
   else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_DELETE,
                                  win, mURI, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
@@ -1318,35 +1412,54 @@ nsDOMDeviceStorage::EnumerateInternal(co
       jspath.init(aCx, jsstr);
       path.Assign(jspath);
     } else if (!JSVAL_IS_PRIMITIVE(aName)) {
       // it also might be an options object
       since = ExtractDateFromOptions(aCx, aName);
     } else {
       return NS_ERROR_FAILURE;
     }
-      
+
     if (aArgc == 2 && (JSVAL_IS_VOID(aOptions) || aOptions.isNull() || !aOptions.isObject())) {
       return NS_ERROR_FAILURE;
     }
     since = ExtractDateFromOptions(aCx, aOptions);
   }
-  
+
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
-  dsf->setEditable(aEditable);
+  dsf->SetEditable(aEditable);
 
   nsRefPtr<nsDOMDeviceStorageCursor> cursor = new nsDOMDeviceStorageCursor(win, mURI, dsf, since);
   nsRefPtr<DeviceStorageCursorRequest> r = new DeviceStorageCursorRequest(cursor);
 
   NS_ADDREF(*aRetval = cursor);
 
   if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
     r->Allow();
     return NS_OK;
   }
 
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    // because owner implements nsITabChild, we can assume that it is
+    // the one and only TabChild.
+    TabChild* child = GetTabChildFrom(win->GetDocShell());
+    if (!child)
+      return false;
+
+    // Retain a reference so the object isn't deleted without IPDL's knowledge.
+    // Corresponding release occurs in DeallocPContentPermissionRequest.
+    r->AddRef();
+
+    nsCString type = NS_LITERAL_CSTRING("device-storage");
+    child->SendPContentPermissionRequestConstructor(r, type, IPC::URI(mURI));
+
+    r->Sendprompt();
+
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   if (prompt) {
     prompt->Prompt(r);
   }
 
   return NS_OK;
 }
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -2,29 +2,74 @@
  * 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 nsDeviceStorage_h
 #define nsDeviceStorage_h
 
 class nsPIDOMWindow;
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
+
+
+#include "DOMRequest.h"
+#include "PCOMContentPermissionRequestChild.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/PContentPermissionRequestChild.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
+#include "nsIContentPermissionPrompt.h"
 #include "nsIDOMDeviceStorage.h"
 #include "nsIDOMDeviceStorageCursor.h"
 #include "nsIDOMWindow.h"
 #include "nsIURI.h"
-
-#include "nsAutoPtr.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsDOMClassInfoID.h"
+#include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsWeakPtr.h"
-#include "nsInterfaceHashtable.h"
-#include "mozilla/Attributes.h"
+
+
+#define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "File location doesn't exists"
+#define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE         "File location is not enumerable"
+#define POST_ERROR_EVENT_PERMISSION_DENIED           "Permission Denied"
+#define POST_ERROR_EVENT_ILLEGAL_FILE_NAME           "Illegal file name"
+#define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
+#define POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED "Non-string type unsupported"
+
+using namespace mozilla::dom;
+
+class DeviceStorageFile MOZ_FINAL : public nsISupports {
+public:
+  nsCOMPtr<nsIFile> mFile;
+  nsString mPath;
+  bool mEditable;
+
+  DeviceStorageFile(nsIFile* aFile, const nsAString& aPath);
+  DeviceStorageFile(nsIFile* aFile);
+  void SetPath(const nsAString& aPath);
+  void SetEditable(bool aEditable);
+
+  NS_DECL_ISUPPORTS
+
+  // we want to make sure that the names of file can't reach
+  // outside of the type of storage the user asked for.
+  bool IsSafePath();
+  
+  nsresult Write(nsIDOMBlob* blob);
+  nsresult Write(InfallibleTArray<PRUint8>& bits);
+  void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince = 0);
+  void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince, nsAString& aRootPath);
+
+private:
+  void NormalizeFilePath();
+  void AppendRelativePath();
+};
 
 class nsDOMDeviceStorage MOZ_FINAL : public nsIDOMDeviceStorage
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMDEVICESTORAGE
 
   nsDOMDeviceStorage();
@@ -33,17 +78,16 @@ public:
 
   PRInt32 SetRootFileForType(const nsAString& aType, const PRInt32 aIndex);
 
   static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin, const nsAString &aType, nsIVariant** _retval);
 
 private:
   ~nsDOMDeviceStorage();
 
-
   nsresult GetInternal(const JS::Value & aName, JSContext* aCx, nsIDOMDOMRequest * *_retval NS_OUTPARAM, bool aEditable);
 
   nsresult EnumerateInternal(const JS::Value & aName, const JS::Value & aOptions, JSContext* aCx, PRUint8 aArgc, bool aEditable, nsIDOMDeviceStorageCursor** aRetval);
 
   PRInt32 mStorageType;
   nsCOMPtr<nsIFile> mFile;
 
   nsWeakPtr mOwner;
@@ -52,9 +96,57 @@ private:
   // nsIDOMDeviceStorage.type
   enum {
       DEVICE_STORAGE_TYPE_DEFAULT = 0,
       DEVICE_STORAGE_TYPE_SHARED,
       DEVICE_STORAGE_TYPE_EXTERNAL,
   };
 };
 
+class ContinueCursorEvent MOZ_FINAL: public nsRunnable
+{
+public:
+  ContinueCursorEvent(nsRefPtr<DOMRequest>& aRequest);
+  ContinueCursorEvent(DOMRequest* aRequest);
+  ~ContinueCursorEvent();
+  NS_IMETHOD Run();
+private:
+  nsRefPtr<DOMRequest> mRequest;
+};
+
+class nsDOMDeviceStorageCursor MOZ_FINAL
+  : public nsIDOMDeviceStorageCursor
+  , public DOMRequest
+  , public nsIContentPermissionRequest
+  , public PCOMContentPermissionRequestChild
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_NSIDOMDEVICESTORAGECURSOR
+
+  nsDOMDeviceStorageCursor(nsIDOMWindow* aWindow,
+                           nsIURI* aURI,
+                           DeviceStorageFile* aFile,
+                           PRUint64 aSince);
+
+
+  nsTArray<nsRefPtr<DeviceStorageFile> > mFiles;
+  bool mOkToCallContinue;
+  PRUint64 mSince;
+
+  virtual bool Recv__delete__(const bool& allow);
+  virtual void IPDLRelease();
+
+private:
+  ~nsDOMDeviceStorageCursor();
+
+  nsRefPtr<DeviceStorageFile> mFile;
+  nsCOMPtr<nsIURI> mURI;
+};
+
+//helpers
+jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString);
+jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile, bool aEditable);
+jsval BlobToJsval(nsPIDOMWindow* aWindow, nsIDOMBlob* aBlob);
+
+
 #endif
--- a/dom/devicestorage/test/test_overwrite.html
+++ b/dom/devicestorage/test/test_overwrite.html
@@ -34,16 +34,17 @@ function deleteSuccess(e) {
 
 function deleteError(e) {
   ok(false, "deleteError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addOverwritingSuccess(e) {
   ok(false, "addOverwritingSuccess was called.");
+  devicestorage_cleanup();
 }
 
 function addOverwritingError(e) {
   ok(true, "Adding to the same location should fail");
 
   var storage = navigator.getDeviceStorage("testing");
   request = storage[0].delete(filename)
   request.onsuccess = deleteSuccess;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -74,22 +74,24 @@
 #define getpid _getpid
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 
 #include "mozilla/dom/sms/SmsChild.h"
+#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::net;
 using namespace mozilla::places;
 using namespace mozilla::docshell;
+using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 
 namespace mozilla {
 namespace dom {
 
 class MemoryReportRequestChild : public PMemoryReportRequestChild
 {
@@ -481,16 +483,29 @@ ContentChild::DeallocPAudio(PAudioChild*
 {
 #if defined(MOZ_SYDNEYAUDIO)
     AudioChild *child = static_cast<AudioChild*>(doomed);
     NS_RELEASE(child);
 #endif
     return true;
 }
 
+PDeviceStorageRequestChild*
+ContentChild::AllocPDeviceStorageRequest(const DeviceStorageParams& aParams)
+{
+    return new DeviceStorageRequestChild();
+}
+
+bool
+ContentChild::DeallocPDeviceStorageRequest(PDeviceStorageRequestChild* aDeviceStorage)
+{
+    delete aDeviceStorage;
+    return true;
+}
+
 PNeckoChild* 
 ContentChild::AllocPNecko()
 {
     return new NeckoChild();
 }
 
 bool 
 ContentChild::DeallocPNecko(PNeckoChild* necko)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -54,16 +54,19 @@ public:
 
     /* if you remove this, please talk to cjones or dougt */
     virtual bool RecvDummy(Shmem& foo) { return true; }
 
     virtual PBrowserChild* AllocPBrowser(const PRUint32& aChromeFlags,
                                          const bool& aIsBrowserFrame);
     virtual bool DeallocPBrowser(PBrowserChild*);
 
+    virtual PDeviceStorageRequestChild* AllocPDeviceStorageRequest(const DeviceStorageParams&);
+    virtual bool DeallocPDeviceStorageRequest(PDeviceStorageRequestChild*);
+
     virtual PCrashReporterChild*
     AllocPCrashReporter(const mozilla::dom::NativeThreadId& id,
                         const PRUint32& processType);
     virtual bool
     DeallocPCrashReporter(PCrashReporterChild*);
 
     NS_OVERRIDE virtual PHalChild* AllocPHal();
     NS_OVERRIDE virtual bool DeallocPHal(PHalChild*);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -71,32 +71,38 @@
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 #include "nsIClipboard.h"
 #include "nsWidgetsCID.h"
 #include "nsISupportsPrimitives.h"
 #include "mozilla/dom/sms/SmsParent.h"
+#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "nsDebugImpl.h"
 
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "mozilla/Preferences.h"
+
 #include "IDBFactory.h"
 #include "IndexedDatabaseManager.h"
 #include "IndexedDBParent.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using mozilla::Preferences;
 using namespace mozilla::ipc;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::net;
 using namespace mozilla::places;
 using mozilla::unused; // heh
 using base::KillProcess;
+using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 
 namespace mozilla {
 namespace dom {
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
@@ -770,16 +776,29 @@ ContentParent::AllocPBrowser(const PRUin
 bool
 ContentParent::DeallocPBrowser(PBrowserParent* frame)
 {
   TabParent* parent = static_cast<TabParent*>(frame);
   NS_RELEASE(parent);
   return true;
 }
 
+PDeviceStorageRequestParent*
+ContentParent::AllocPDeviceStorageRequest(const DeviceStorageParams& aParams)
+{
+  return new DeviceStorageRequestParent(aParams);
+}
+
+bool
+ContentParent::DeallocPDeviceStorageRequest(PDeviceStorageRequestParent* doomed)
+{
+  delete doomed;
+  return true;
+}
+
 PCrashReporterParent*
 ContentParent::AllocPCrashReporter(const NativeThreadId& tid,
                                    const PRUint32& processType)
 {
 #ifdef MOZ_CRASHREPORTER
   return new CrashReporterParent();
 #else
   return nsnull;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -105,16 +105,19 @@ private:
     ContentParent(const nsAString& aAppManifestURL);
     virtual ~ContentParent();
 
     void Init();
 
     virtual PBrowserParent* AllocPBrowser(const PRUint32& aChromeFlags, const bool& aIsBrowserFrame);
     virtual bool DeallocPBrowser(PBrowserParent* frame);
 
+    virtual PDeviceStorageRequestParent* AllocPDeviceStorageRequest(const DeviceStorageParams&);
+    virtual bool DeallocPDeviceStorageRequest(PDeviceStorageRequestParent*);
+
     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);
 
     NS_OVERRIDE virtual PHalParent* AllocPHal();
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -72,16 +72,17 @@ LOCAL_INCLUDES += \
 	-I$(srcdir)/../../xpcom/base \
 	-I$(topsrcdir)/dom/indexedDB \
 	-I$(topsrcdir)/dom/indexedDB/ipc \
 	-I$(topsrcdir)/extensions/cookie \
 	-I$(topsrcdir)/dom/base \
 	-I$(topsrcdir)/toolkit/xre \
 	-I$(topsrcdir)/hal/sandbox \
 	-I$(topsrcdir)/dom/sms/src/ipc \
+	-I$(topsrcdir)/dom/devicestorage \
 	$(NULL)
 
 DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gtk2 gonk qt))
 DEFINES += -DMOZ_ENABLE_FREETYPE
 endif
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -2,16 +2,17 @@
 /* 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 PBrowser;
 include protocol PCrashReporter;
+include protocol PDeviceStorageRequest;
 include protocol PExternalHelperApp;
 include protocol PHal;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
 include protocol PNecko;
 include protocol PSms;
 include protocol PStorage;
 include protocol PTestShell;
@@ -60,16 +61,46 @@ struct FontListEntry {
     nsString  faceName;
     nsCString filepath;
     PRUint16  weight;
     PRInt16   stretch;
     PRUint8   italic;
     PRUint8   index;
 };
 
+struct DeviceStorageAddParams
+{
+  nsString fullpath;
+  PRUint8[] bits;
+};
+
+struct DeviceStorageGetParams
+{
+  nsString fullpath;
+};
+
+struct DeviceStorageDeleteParams
+{
+  nsString fullpath;
+};
+
+struct DeviceStorageEnumerationParams
+{
+  nsString fullpath;
+  PRUint32 since;
+};
+
+union DeviceStorageParams
+{
+  DeviceStorageAddParams;
+  DeviceStorageGetParams;
+  DeviceStorageDeleteParams;
+  DeviceStorageEnumerationParams;
+};
+
 rpc protocol PContent
 {
     manages PAudio;
     manages PBrowser;
     manages PCrashReporter;
     manages PExternalHelperApp;
     manages PHal;
     manages PIndexedDB;
@@ -127,16 +158,18 @@ child:
     SetID(PRUint64 id);
 
     // Notify child that last-pb-context-exited notification was observed
     LastPrivateDocShellDestroyed();
 
 parent:
     PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
 
+    PDeviceStorageRequest(DeviceStorageParams params);
+
     sync PCrashReporter(NativeThreadId tid, PRUint32 processType);
 
     PHal();
 
     PIndexedDB();
 
     PNecko();
 
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -18,16 +18,17 @@ FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 
 ##-----------------------------------------------------------------------------
 ## When you add IPDL files to a source directory, list the directory here.
 ##
 IPDLDIRS =  \
   uriloader/exthandler \
+  dom/devicestorage \
   dom/indexedDB/ipc \
   dom/plugins/ipc  \
   dom/ipc  \
   dom/sms/src/ipc \
   dom/src/storage \
   gfx/layers/ipc \
   hal/sandbox \
   ipc/testshell  \