Bug 761930 - Multi-process support for Device Storage. r=sicking
authorDoug Turner <dougt@dougt.org>
Tue, 19 Jun 2012 16:14:39 -0700
changeset 99278 8349edd9f8a1658ce5aed5b77a541c2ed5fc1f0d
parent 99277 ebaadef4058056f94dc40ae79c7769d27a371858
child 99279 9f3534c54fa38db04f783b218c8f9d310a779900
push id23116
push userryanvm@gmail.com
push dateSat, 14 Jul 2012 16:58:48 +0000
treeherdermozilla-central@9046ecf4db8f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs761930
milestone16.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 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 "IDBFactory.h"
 #include "IndexedDatabaseManager.h"
 #include "IndexedDBParent.h"
 
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "mozilla/Preferences.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
@@ -3,16 +3,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/. */
 
 include protocol PAudio;
 include protocol PBrowser;
 include protocol PCrashReporter;
 include protocol PExternalHelperApp;
+include protocol PDeviceStorageRequest;
 include protocol PHal;
 include protocol PIndexedDB;
 include protocol PMemoryReportRequest;
 include protocol PNecko;
 include protocol PSms;
 include protocol PStorage;
 include protocol PTestShell;
 
@@ -60,21 +61,51 @@ 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 PDeviceStorageRequest;
     manages PExternalHelperApp;
     manages PHal;
     manages PIndexedDB;
     manages PMemoryReportRequest;
     manages PNecko;
     manages PSms;
     manages PStorage;
     manages PTestShell;
@@ -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  \