Bug 777088 - device Storage - file stat API. r=bent
authorDoug Turner <dougt@dougt.org>
Tue, 31 Jul 2012 12:28:23 -0700
changeset 101988 ef06eb15d5208fe3b2b8b90f9d040dfb7af09c85
parent 101987 3664e5ab11b3981c3eb76cfe60438e64e8a22b93
child 101989 14c4b37f9a0ef49f8e7b708272033978ca29c8c3
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-esr52@fd72dbbd6920 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs777088
milestone17.0a1
Bug 777088 - device Storage - file stat API. r=bent
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/devicestorage/DeviceStorageRequestChild.cpp
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/DeviceStorageRequestParent.h
dom/devicestorage/PDeviceStorageRequest.ipdl
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/devicestorage/test/Makefile.in
dom/devicestorage/test/test_stat.html
dom/interfaces/devicestorage/Makefile.in
dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
dom/interfaces/devicestorage/nsIDOMDeviceStorageStat.idl
dom/ipc/PContent.ipdl
dom/tests/mochitest/general/test_interfaces.html
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -415,16 +415,17 @@
 #include "nsIImageDocument.h"
 
 // Storage includes
 #include "nsDOMStorage.h"
 
 // Device Storage
 #include "nsIDOMDeviceStorage.h"
 #include "nsIDOMDeviceStorageCursor.h"
+#include "nsIDOMDeviceStorageStat.h"
 
 // Drag and drop
 #include "nsIDOMDataTransfer.h"
 
 // Geolocation
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPosition.h"
 #include "nsIDOMGeoPositionCoords.h"
@@ -1436,16 +1437,19 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DeviceStorage, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DeviceStorageCursor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(DeviceStorageStat, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(GeoGeolocation, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   
   NS_DEFINE_CLASSINFO_DATA(GeoPosition, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS) 
   
   NS_DEFINE_CLASSINFO_DATA(GeoPositionCoords, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -4036,16 +4040,20 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DeviceStorageCursor, nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(DeviceStorageStat, nsIDOMDeviceStorageStat)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageStat)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(GeoGeolocation, nsIDOMGeoGeolocation)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoGeolocation)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(GeoPosition, nsIDOMGeoPosition)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoPosition)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -381,16 +381,17 @@ DOMCI_CLASS(ModalContentWindow)
 DOMCI_CLASS(DataContainerEvent)
 
 // event used for cross-domain message-passing and for server-sent events in
 // HTML5
 DOMCI_CLASS(MessageEvent)
 
 DOMCI_CLASS(DeviceStorage)
 DOMCI_CLASS(DeviceStorageCursor)
+DOMCI_CLASS(DeviceStorageStat)
 
 // Geolocation
 DOMCI_CLASS(GeoGeolocation)
 DOMCI_CLASS(GeoPosition)
 DOMCI_CLASS(GeoPositionCoords)
 DOMCI_CLASS(GeoPositionError)
 
 DOMCI_CLASS(BatteryManager)
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -50,30 +50,43 @@ DeviceStorageRequestChild::Recv__delete_
     }
 
     case DeviceStorageResponseValue::TBlobResponse:
     {
       BlobResponse r = aValue;
       BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
       nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
 
-      jsval result = BlobToJsval(mRequest->GetOwner(), blob);
+      jsval result = InterfaceToJsval(mRequest->GetOwner(), blob, &NS_GET_IID(nsIDOMBlob));
+      mRequest->FireSuccess(result);
+      break;
+    }
+
+    case DeviceStorageResponseValue::TStatStorageResponse:
+    {
+      StatStorageResponse r = aValue;
+
+      nsRefPtr<nsIDOMDeviceStorageStat> domstat = new nsDOMDeviceStorageStat(r.freeBytes(), r.totalBytes());
+      jsval result = InterfaceToJsval(mRequest->GetOwner(), domstat, &NS_GET_IID(nsIDOMDeviceStorageStat));
       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));
+        nsresult rv = NS_NewLocalFile(r.paths()[i].fullpath(), false, getter_AddRefs(f));
+        if (NS_FAILED(rv)) {
+          continue;
+        }
 
         nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
         dsf->SetPath(r.paths()[i].name());
         cursor->mFiles.AppendElement(dsf);
       }
 
       nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(cursor);
       NS_DispatchToMainThread(event);
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -72,16 +72,32 @@ DeviceStorageRequestParent::DeviceStorag
       nsRefPtr<CancelableRunnable> 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::TDeviceStorageStatParams:
+    {
+      DeviceStorageStatParams p = aParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
+      nsRefPtr<StatFileEvent> r = new StatFileEvent(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);
@@ -252,17 +268,17 @@ DeviceStorageRequestParent::DeleteFileEv
 
 DeviceStorageRequestParent::DeleteFileEvent::~DeleteFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   mFile->mFile->Remove(true);
 
   nsRefPtr<nsRunnable> r;
 
   bool check = false;
   mFile->mFile->Exists(&check);
   if (check) {
@@ -271,16 +287,46 @@ DeviceStorageRequestParent::DeleteFileEv
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::StatFileEvent::StatFileEvent(DeviceStorageRequestParent* aParent,
+                                                         DeviceStorageFile* aFile)
+  : CancelableRunnable(aParent)
+  , mFile(aFile)
+{
+}
+
+DeviceStorageRequestParent::StatFileEvent::~StatFileEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::StatFileEvent::CancelableRun()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIRunnable> r;
+  PRUint64 diskUsage = DeviceStorageFile::DirectoryDiskUsage(mFile->mFile);
+  PRInt64 freeSpace = 0;
+  nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
+  if (NS_FAILED(rv)) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+  r = new PostStatResultEvent(mParent, diskUsage, freeSpace);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
 DeviceStorageRequestParent::ReadFileEvent::ReadFileEvent(DeviceStorageRequestParent* aParent,
                                                          DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
   nsCOMPtr<nsIMIMEService> mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID);
   if (mimeService) {
     nsresult rv = mimeService->GetTypeFromFile(mFile->mFile, mMimeType);
@@ -292,17 +338,17 @@ DeviceStorageRequestParent::ReadFileEven
 
 DeviceStorageRequestParent::ReadFileEvent::~ReadFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::ReadFileEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   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);
@@ -333,17 +379,17 @@ DeviceStorageRequestParent::EnumerateFil
 
 DeviceStorageRequestParent::EnumerateFileEvent::~EnumerateFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   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;
@@ -384,12 +430,35 @@ DeviceStorageRequestParent::PostPathResu
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
   unused <<  mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::PostStatResultEvent::PostStatResultEvent(DeviceStorageRequestParent* aParent,
+                                                                     PRInt64 aFreeBytes,
+                                                                     PRInt64 aTotalBytes)
+  : CancelableRunnable(aParent)
+  , mFreeBytes(aFreeBytes)
+  , mTotalBytes(aTotalBytes)
+{
+}
+
+DeviceStorageRequestParent::PostStatResultEvent::~PostStatResultEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::PostStatResultEvent::CancelableRun()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  StatStorageResponse response(mFreeBytes, mTotalBytes);
+  unused <<  mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
 
 } // namespace devicestorage
 } // namespace dom
 } // namespace mozilla
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -122,16 +122,26 @@ private:
     public:
       DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~DeleteFileEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
   };
 
+  class StatFileEvent : public CancelableRunnable
+  {
+    public:
+      StatFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      virtual ~StatFileEvent();
+      virtual nsresult CancelableRun();
+     private:
+       nsRefPtr<DeviceStorageFile> mFile;
+   };
+
   class ReadFileEvent : public CancelableRunnable
   {
     public:
       ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~ReadFileEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
@@ -155,16 +165,28 @@ private:
       PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
       virtual ~PostPathResultEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
       nsString mPath;
   };
 
+  class PostStatResultEvent : public CancelableRunnable
+ {
+    public:
+      PostStatResultEvent(DeviceStorageRequestParent* aParent,
+                          PRInt64 aFreeBytes,
+                          PRInt64 aTotalBytes);
+      virtual ~PostStatResultEvent();
+      virtual nsresult CancelableRun();
+    private:
+      PRInt64 mFreeBytes, mTotalBytes;
+   };
+
 protected:
   void AddRunnable(CancelableRunnable* aRunnable) {
     mRunnables.AppendElement(aRunnable);
   }
   void RemoveRunnable(CancelableRunnable* aRunnable) {
     mRunnables.RemoveElement(aRunnable);
   }
   nsTArray<nsRefPtr<CancelableRunnable> > mRunnables;
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -31,22 +31,29 @@ struct DeviceStorageFileValue
   nsString fullpath;
 };
 
 struct EnumerationResponse
 {
   DeviceStorageFileValue[] paths;
 };
 
+struct StatStorageResponse
+{
+  PRInt64 totalBytes;
+  PRInt64 freeBytes;
+};
+
 union DeviceStorageResponseValue
 {
   ErrorResponse;
   SuccessResponse;
   BlobResponse;
   EnumerationResponse;
+  StatStorageResponse;
 };
 
 sync protocol PDeviceStorageRequest {
     manager PContent;
 child:
     __delete__(DeviceStorageResponseValue response);
 };
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -276,16 +276,70 @@ DeviceStorageFile::collectFilesInternal(
     } else if (isFile) {
       nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
       dsf->SetPath(newPath);
       aFiles.AppendElement(dsf);
     }
   }
 }
 
+PRUint64
+DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, PRUint64 aSoFar)
+{
+  if (!aFile) {
+    return aSoFar;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsISimpleEnumerator> e;
+  rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
+
+  if (NS_FAILED(rv) || !e) {
+    return aSoFar;
+  }
+
+  nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
+  NS_ASSERTION(files, "GetDirectoryEntries must return a nsIDirectoryEnumerator");
+
+  nsCOMPtr<nsIFile> f;
+  while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
+    bool isDir;
+    rv = f->IsDirectory(&isDir);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    bool isFile;
+    rv = f->IsFile(&isFile);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    bool isLink;
+    rv = f->IsSymlink(&isLink);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+      
+    if (isLink) {
+      // for now, lets just totally ignore symlinks.
+      NS_WARNING("DirectoryDiskUsage ignores symlinks");
+    } else if (isDir) {
+      aSoFar += DirectoryDiskUsage(f, aSoFar);
+    } else if (isFile) {
+      PRInt64 size;
+      rv = f->GetFileSize(&size);
+      if (NS_SUCCEEDED(rv)) {
+	aSoFar += size;
+      }
+    }
+  }
+  return aSoFar;
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
 
 #ifdef MOZ_WIDGET_GONK
 static void
 RegisterForSDCardChanges(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
@@ -354,65 +408,65 @@ nsDOMDeviceStorage::SetRootFileForType(c
   } 
 
 #ifdef MOZ_WIDGET_GONK
   RegisterForSDCardChanges(this);
 #endif
   mFile = f;
 }
 
-static jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWindow, "Null Window");
-
-  if (aFile->mEditable) {
-    // TODO - needs janv's file handle support.
-    return JSVAL_NULL;
-  }
-
-  if (aFile == nullptr) {
-    return JSVAL_NULL;
-  }
-
-  nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(aFile->mFile, aFile->mPath);
-  return BlobToJsval(aWindow, blob);
-}
-
-
-jsval BlobToJsval(nsPIDOMWindow* aWindow, nsIDOMBlob* aBlob)
+jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID)
 {
   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;
+  jsval someJsVal;
   nsresult rv = nsContentUtils::WrapNative(cx,
                                            JS_GetGlobalObject(cx),
-                                           aBlob,
-                                           &NS_GET_IID(nsIDOMFile),
-                                           &wrappedFile);
+                                           aObject,
+                                           aIID,
+                                           &someJsVal);
   if (NS_FAILED(rv)) {
     return JSVAL_NULL;
   }
 
-  return wrappedFile;
+  return someJsVal;
 }
 
+jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWindow, "Null Window");
+
+  if (aFile->mEditable) {
+    // TODO - needs janv's file handle support.
+    return JSVAL_NULL;
+  }
+
+  if (aFile == nullptr) {
+    return JSVAL_NULL;
+  }
+
+  nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(aFile->mFile, aFile->mPath);
+  return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
+ }
+
+
 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;
@@ -757,16 +811,49 @@ nsDOMDeviceStorageCursor::Recv__delete__
 }
 
 void
 nsDOMDeviceStorageCursor::IPDLRelease()
 {
   Release();
 }
 
+class PostStatResultEvent : public nsRunnable
+{
+public:
+  PostStatResultEvent(nsRefPtr<DOMRequest>& aRequest, PRInt64 aFreeBytes, PRInt64 aTotalBytes)
+    : mFreeBytes(aFreeBytes)
+    , mTotalBytes(aTotalBytes)
+    {
+      mRequest.swap(aRequest);
+    }
+
+  ~PostStatResultEvent() {}
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    nsRefPtr<nsIDOMDeviceStorageStat> domstat = new nsDOMDeviceStorageStat(mFreeBytes, mTotalBytes);
+
+    jsval result = InterfaceToJsval(mRequest->GetOwner(),
+				    domstat,
+				    &NS_GET_IID(nsIDOMDeviceStorageStat));
+
+    mRequest->FireSuccess(result);
+    mRequest = nsnull;
+    return NS_OK;
+  }
+
+private:
+  PRInt64 mFreeBytes, mTotalBytes;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
+
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
@@ -915,28 +1002,63 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
+class StatFileEvent : public nsRunnable
+{
+public:
+  StatFileEvent(DeviceStorageFile* aFile, nsRefPtr<DOMRequest>& aRequest)
+  : mFile(aFile)
+    {
+      mRequest.swap(aRequest);
+    }
+
+  ~StatFileEvent() {}
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    nsCOMPtr<nsIRunnable> r;
+    PRUint64 diskUsage = DeviceStorageFile::DirectoryDiskUsage(mFile->mFile);
+    PRInt64 freeSpace = 0;
+    nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
+    if (NS_FAILED(rv)) {
+      r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_UNKNOWN, mFile);
+      NS_DispatchToMainThread(r);
+      return NS_OK;
+    }
+
+    r = new PostStatResultEvent(mRequest, diskUsage, freeSpace);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<DeviceStorageFile> mFile;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
 class DeviceStorageRequest MOZ_FINAL
   : public nsIContentPermissionRequest
   , public nsIRunnable
   , public PCOMContentPermissionRequestChild
 {
 public:
 
     enum DeviceStorageRequestType {
         DEVICE_STORAGE_REQUEST_READ,
         DEVICE_STORAGE_REQUEST_WRITE,
         DEVICE_STORAGE_REQUEST_DELETE,
-        DEVICE_STORAGE_REQUEST_WATCH
+        DEVICE_STORAGE_REQUEST_WATCH,
+        DEVICE_STORAGE_REQUEST_STAT
     };
 
     DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
                          nsPIDOMWindow *aWindow,
                          nsIPrincipal *aPrincipal,
                          DeviceStorageFile *aFile,
                          DOMRequest* aRequest,
                          nsDOMDeviceStorage *aDeviceStorage,
@@ -1095,16 +1217,28 @@ public:
           DeviceStorageDeleteParams params(fullpath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
 
+      case DEVICE_STORAGE_REQUEST_STAT:
+      {
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+          DeviceStorageStatParams params(fullpath);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+	  return NS_OK;
+        }
+        r = new StatFileEvent(mFile, mRequest);
+        break;
+      }
+
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
          if (XRE_GetProcessType() != GeckoProcessType_Default) {
            nsString fullpath;
            mFile->mFile->GetPath(fullpath);
            nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
            obs->AddObserver(mDeviceStorage, "file-watcher-update", false);
            ContentChild::GetSingleton()->SendAddFileWatch(fullpath);
@@ -1406,16 +1540,37 @@ nsDOMDeviceStorage::Delete(const JS::Val
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_DELETE,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMDeviceStorage::Stat(nsIDOMDOMRequest** aRetval)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+  NS_ADDREF(*aRetval = request);
+
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
+  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_STAT,
+						     win,
+						     mPrincipal,
+						     dsf,
+						     request);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMDeviceStorage::Enumerate(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              PRUint8 aArgc,
                              nsIDOMDeviceStorageCursor** aRetval)
 {
   return EnumerateInternal(aName, aOptions, aCx, aArgc, false, aRetval);
 }
@@ -1542,16 +1697,51 @@ nsDOMDeviceStorage::DispatchMountChangeE
     return;
   }
 
   bool ignore;
   DispatchEvent(ce, &ignore);
 }
 #endif
 
+DOMCI_DATA(DeviceStorageStat, nsDOMDeviceStorageStat)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorageStat)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorageStat)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorageStat)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsDOMDeviceStorageStat)
+NS_IMPL_RELEASE(nsDOMDeviceStorageStat)
+
+nsDOMDeviceStorageStat::nsDOMDeviceStorageStat(PRUint64 aFreeBytes, PRUint64 aTotalBytes)
+  : mFreeBytes(aFreeBytes)
+  , mTotalBytes(aTotalBytes)
+{
+}
+
+nsDOMDeviceStorageStat::~nsDOMDeviceStorageStat()
+{
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorageStat::GetTotalBytes(PRUint64 *aTotalBytes)
+{
+  *aTotalBytes = mTotalBytes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorageStat::GetFreeBytes(PRUint64 *aFreeBytes)
+{
+  *aFreeBytes = mFreeBytes;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
 {
   if (!strcmp(aTopic, "file-watcher-update")) {
 
     // data strings will have the format of
     //  reason:path
     nsDependentString data(aData);
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -10,16 +10,17 @@ class nsPIDOMWindow;
 
 #include "DOMRequest.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIDOMDeviceStorageCursor.h"
+#include "nsIDOMDeviceStorageStat.h"
 #include "nsIDOMWindow.h"
 #include "nsIURI.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsWeakPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
@@ -29,20 +30,22 @@ class nsPIDOMWindow;
 
 
 #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"
+#define POST_ERROR_EVENT_NOT_IMPLEMENTED             "Not implemented"
 
 using namespace mozilla::dom;
 
-class DeviceStorageFile MOZ_FINAL : public nsISupports {
+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);
@@ -54,16 +57,18 @@ public:
   // outside of the type of storage the user asked for.
   bool IsSafePath();
 
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<PRUint8>& bits);
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince, nsAString& aRootPath);
 
+  static PRUint64 DirectoryDiskUsage(nsIFile* aFile, PRUint64 aSoFar = 0);
+
 private:
   void NormalizeFilePath();
   void AppendRelativePath();
 };
 
 class ContinueCursorEvent MOZ_FINAL: public nsRunnable
 {
 public:
@@ -101,15 +106,29 @@ public:
 
 private:
   ~nsDOMDeviceStorageCursor();
 
   nsRefPtr<DeviceStorageFile> mFile;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
+class nsDOMDeviceStorageStat MOZ_FINAL
+  : public nsIDOMDeviceStorageStat
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMDEVICESTORAGESTAT
+
+  nsDOMDeviceStorageStat(PRUint64 aFreeBytes, PRUint64 aTotalBytes);
+
+private:
+  ~nsDOMDeviceStorageStat();
+  PRUint64 mFreeBytes, mTotalBytes;
+};
+
 //helpers
 jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString);
-jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile, bool aEditable);
-jsval BlobToJsval(nsPIDOMWindow* aWindow, nsIDOMBlob* aBlob);
+jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile);
+jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID);
 
 
 #endif
--- a/dom/devicestorage/test/Makefile.in
+++ b/dom/devicestorage/test/Makefile.in
@@ -14,12 +14,13 @@ MOCHITEST_FILES	= \
 		test_sanity.html \
 		test_basic.html \
 		test_enumerate.html \
 		test_enumerateMultipleContinue.html \
 		test_overwrite.html \
 		test_dotdot.html \
 		test_enumerateOptions.html \
 		test_lastModificationFilter.html \
+		test_stat.html \
 		devicestorage_common.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_stat.html
@@ -0,0 +1,72 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html> <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717103
+-->
+<head>
+  <title>Test for the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+devicestorage_setup();
+
+function statSuccess(e) {
+  ok(e.target.result.freeBytes  > 0, "free bytes should exist and be greater than zero");
+  ok(e.target.result.totalBytes > 0, "total bytes should exist and be greater than zero");
+  devicestorage_cleanup();
+}
+
+function statError(e) {
+  ok(false, "statError was called");
+  devicestorage_cleanup();
+}
+
+var isMac = /Mac/.test(navigator.platform);
+var isWin = /Win/.test(navigator.platform);
+
+if (isMac || isWin) {
+  todo(false, "stat is not available on mac or windows yet. see bug xxxx");
+  devicestorage_cleanup();
+} else {
+  var storage = navigator.getDeviceStorage("testing");
+  ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
+
+
+  function addError(e) {
+    ok(false, "addError was called : " + e.target.error.name);
+    devicestorage_cleanup();
+  }
+
+  function addSuccess(e) {
+    request = storage.stat();
+    ok(request, "Should have a non-null request");
+
+    request.onsuccess = statSuccess;
+    request.onerror = statError;
+  }
+
+  request = storage.addNamed(createRandomBlob(), "a/b");
+  request.onsuccess = addSuccess;
+  request.onerror = addError;
+
+}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/interfaces/devicestorage/Makefile.in
+++ b/dom/interfaces/devicestorage/Makefile.in
@@ -14,14 +14,15 @@ LIBRARY_NAME     = domdevicestorage_s
 XPIDL_MODULE     = dom_devicestorage
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
 	nsIDOMDeviceStorage.idl \
+	nsIDOMDeviceStorageStat.idl \
 	nsIDOMDeviceStorageCursor.idl \
 	nsIDOMNavigatorDeviceStorage.idl \
 	nsIDOMDeviceStorageChangeEvent.idl
 
 include $(topsrcdir)/config/rules.mk
 
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -10,17 +10,17 @@ interface nsIDOMDeviceStorageCursor;
 interface nsIDOMDeviceStorageChangeEvent;
 interface nsIDOMEventListener;
 
 dictionary DeviceStorageEnumerationParameters
 {
   jsval since;
 };
 
-[scriptable, uuid(3dbe0137-ca73-44c5-bcde-25c297bf7c65), builtinclass]
+[scriptable, uuid(36f3b16b-a398-4b19-944e-ce299b714725), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     attribute nsIDOMEventListener onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     [implicit_jscontext]
     nsIDOMDOMRequest get(in jsval aName);
@@ -31,9 +31,11 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     [implicit_jscontext]
     nsIDOMDOMRequest delete(in jsval aName);
 
     [optional_argc, implicit_jscontext]
     nsIDOMDeviceStorageCursor enumerate([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
 
     [optional_argc, implicit_jscontext]
     nsIDOMDeviceStorageCursor enumerateEditable([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
+
+    nsIDOMDOMRequest stat();
 };
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorageStat.idl
@@ -0,0 +1,12 @@
+/* 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 "domstubs.idl"
+
+[scriptable, uuid(b951ec07-d5db-42fc-bf4c-4eded202f7f5)]
+interface nsIDOMDeviceStorageStat : nsISupports
+{
+  readonly attribute PRUint64 totalBytes;
+  readonly attribute PRUint64 freeBytes;
+};
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -66,16 +66,21 @@ struct FontListEntry {
     nsString  faceName;
     nsCString filepath;
     PRUint16  weight;
     PRInt16   stretch;
     PRUint8   italic;
     PRUint8   index;
 };
 
+struct DeviceStorageStatParams
+{
+  nsString fullpath;
+};
+
 struct DeviceStorageAddParams
 {
   PBlob blob;
   nsString name;
   nsString fullpath;
 };
 
 struct DeviceStorageGetParams
@@ -96,16 +101,17 @@ struct DeviceStorageEnumerationParams
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
   DeviceStorageEnumerationParams;
+  DeviceStorageStatParams;
 };
 
 struct SlicedBlobConstructorParams
 {
   PBlob source;
   uint64_t begin;
   uint64_t end;
   nsString contentType;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -65,16 +65,17 @@ var interfaceNamesInGlobalScope =
     "Pkcs11",
     "NotifyAudioAvailableEvent",
     "Array",
     "SVGZoomAndPan",
     "XULPopupElement",
     "MediaError",
     "DeviceStorageCursor",
     "DeviceStorageChangeEvent",
+    "DeviceStorageStat",
     "PageTransitionEvent",
     "DataContainerEvent",
     "MozCSSKeyframesRule",
     "SVGAnimatedInteger",
     "TouchEvent",
     "OpenWindowEventDetail",
     "IDBIndex",
     "EventListener",