Bug 786922 - Device Storage - use a properties file instead of the mime service. r=bent
authorDoug Turner <dougt@dougt.org>
Wed, 05 Sep 2012 14:30:36 -0700
changeset 104481 748af207351758c71a28903596fe4db36c874a27
parent 104480 96b314d423ab6306998b2a3dc5d9312dca7249b0
child 104482 e2fe8fa1f29b9b797143dc0a6c5086a2b95bec1e
child 104483 543a246b5539e4a687918d80253b7a3c12fd7149
push id23426
push userryanvm@gmail.com
push dateFri, 07 Sep 2012 00:38:20 +0000
treeherdermozilla-central@233441ff53af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs786922
milestone18.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 786922 - Device Storage - use a properties file instead of the mime service. r=bent
dom/devicestorage/DeviceStorage.h
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/devicestorage/test/Makefile.in
dom/devicestorage/test/test_add.html
dom/devicestorage/test/test_addCorrectType.html
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -45,16 +45,19 @@ private:
 
   nsresult EnumerateInternal(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              uint8_t aArgc, 
                              bool aEditable, 
                              nsIDOMDeviceStorageCursor** aRetval);
 
+  static bool IsMimeTypeCorrectForStorageType(nsAString& aType,
+					      nsIDOMBlob* aBlob);
+
   nsString mStorageType;
   nsCOMPtr<nsIFile> mRootDirectory;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -34,29 +34,27 @@
 #include "nsCRT.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "GeneratedEvents.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 
+#include "nsIStringBundle.h"
+
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #endif
 
-#define DEBUG_ISTYPE 1
-
-#ifdef DEBUG_ISTYPE
-#include "nsIConsoleService.h"
-#endif
+#define DEVICESTORAGE_PROPERTIES "chrome://global/content/devicestorage.properties"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
 class IOEventComplete : public nsRunnable
 {
@@ -79,17 +77,19 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
 
-DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath)
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
+				     nsIFile* aFile,
+				     const nsAString& aPath)
   : mPath(aPath)
   , mStorageType(aStorageType)
   , 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));
@@ -150,71 +150,48 @@ DeviceStorageFile::IsSafePath()
   return true;
 }
 
 bool
 DeviceStorageFile::IsType(nsAString& aType)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-#ifdef DEBUG_ISTYPE
-  nsCOMPtr<nsIConsoleService> svc = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-  char buffer[1024];
-  nsCString path;
-  mFile->GetNativePath(path);
+  // String bundles are cached by the bundle service.
+  nsCOMPtr<nsIStringBundleService> stringService = mozilla::services::GetStringBundleService();
+  if (!stringService) {
+    return false;
+  }
 
-  PRIntervalTime iStart = PR_IntervalNow();
-#endif
-
-  nsAutoCString mimeType;
-  nsCOMPtr<nsIMIMEService> mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID);
-  if (!mimeService) {
+  nsCOMPtr<nsIStringBundle> filterBundle;
+  if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
+					    getter_AddRefs(filterBundle)))) {
     return false;
   }
 
-  nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
-  if (NS_FAILED(rv)) {
-#ifdef DEBUG_ISTYPE
-    sprintf(buffer, "GetTypeFromFile failed for %s (took: %dms)\n",
-	    path.get(),
-	    PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
+  nsString path;
+  mFile->GetPath(path);
 
-    nsString data;
-    CopyASCIItoUTF16(buffer, data);
-    svc->LogStringMessage(data.get());
-    printf("%s\n", buffer);
-#endif
+  int32_t dotIdx = path.RFindChar(PRUnichar('.'));
+  if (dotIdx == kNotFound) {
     return false;
   }
 
-#ifdef DEBUG_ISTYPE
-  sprintf(buffer, "IsType of %s is %s (took: %dms)\n",
-	  path.get(),
-	  mimeType.get(),
-	  PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
+  nsAutoString extensionMatch;
+  extensionMatch.AssignASCII("*");
+  extensionMatch.Append(Substring(path, dotIdx));
+  extensionMatch.AppendASCII(";");
 
-  nsString data;
-  CopyASCIItoUTF16(buffer, data);
-  svc->LogStringMessage(data.get());
-  printf("%s\n", buffer);
-#endif
-
-  if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"));
+  nsString extensionListStr;
+  if (NS_FAILED(filterBundle->GetStringFromName(aType.BeginReading(),
+						getter_Copies(extensionListStr)))) {
+    return false;
   }
 
-  if (aType.Equals(NS_LITERAL_STRING("videos"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"));
-  }
-
-  if (aType.Equals(NS_LITERAL_STRING("music"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"));
-  }
-
-  return false;
+  return FindInReadable(extensionMatch, extensionListStr);
 }
 
 void
 DeviceStorageFile::NormalizeFilePath() {
 #if defined(XP_WIN)
   PRUnichar* cur = mPath.BeginWriting();
   PRUnichar* end = mPath.EndWriting();
   for (; cur < end; ++cur) {
@@ -338,17 +315,17 @@ DeviceStorageFile::Remove()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   bool check;
   nsresult rv = mFile->Exists(&check);
   if (NS_FAILED(rv)) {
     return rv;
   }
-  
+
   if (!check) {
     return NS_OK;
   }
 
   rv = mFile->Remove(true);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -1566,27 +1543,75 @@ nsDOMDeviceStorage::CreateDeviceStorages
                                             nsDOMDeviceStorage** aStore)
 {
   nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
   if (NS_SUCCEEDED(storage->Init(aWin, aType))) {
     NS_ADDREF(*aStore = storage);
   }
 }
 
+bool
+nsDOMDeviceStorage::IsMimeTypeCorrectForStorageType(nsAString& aType, nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(aBlob, "Calling IsMimeTypeCorrectForStorageType without a blob");
+
+  nsString mimeType;
+  if (NS_FAILED(aBlob->GetType(mimeType))) {
+    return false;
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("videos"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("music"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
+  }
+
+  return false;
+}
+
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
 {
+  if (!aBlob) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
+  if (!mimeSvc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // if mimeType isn't set, we will not get a correct
+  // extension, and AddNamed() will fail.  This will post an
+  // onerror to the requestee.
+  nsString mimeType;
+  aBlob->GetType(mimeType);
+
+  nsCString extension;
+  mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), EmptyCString(), extension);
+  // if extension is null here, we will ignore it for now.
+  // AddNamed() will check the file path and fail.  This
+  // will post an onerror to the requestee.
+
   // possible race here w/ unique filename
   char buffer[128];
-  NS_MakeRandomString(buffer, 128);
+  NS_MakeRandomString(buffer, ArrayLength(buffer));
 
-  nsString path;
-  path.AssignWithConversion(nsDependentCString(buffer));
+  nsAutoCString path;
+  path.Assign(nsDependentCString(buffer));
+  path.Append(".");
+  path.Append(extension);
 
-  return AddNamed(aBlob, path, _retval);
+  return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), _retval);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
                              const nsAString & aPath,
                              nsIDOMDOMRequest * *_retval)
 {
   // if the blob is null here, bail
@@ -1599,24 +1624,27 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath);
-
-  if (!dsf->IsSafePath()) {
+  if (!dsf->IsType(mStorageType) || !IsMimeTypeCorrectForStorageType(mStorageType, aBlob)) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE, dsf);
+  }
+  else if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   }
   else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WRITE,
-                                 win, mPrincipal, dsf, request, aBlob);
+				 win, mPrincipal, dsf, request, aBlob);
   }
+
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Get(const JS::Value & aPath,
                         JSContext* aCx,
                         nsIDOMDOMRequest * *_retval)
@@ -1656,17 +1684,16 @@ nsDOMDeviceStorage::GetInternal(const JS
                            POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED,
                            dsf);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
   dsf->SetEditable(aEditable);
-
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   } else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_READ,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -28,16 +28,17 @@ class nsPIDOMWindow;
 #include "mozilla/Mutex.h"
 #include "DeviceStorage.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_ILLEGAL_TYPE                "Illegal content type"
 #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 {
--- a/dom/devicestorage/test/Makefile.in
+++ b/dom/devicestorage/test/Makefile.in
@@ -5,18 +5,22 @@
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+#  man, our mime database sucks hard.  followup bug # 788273
+#		test_add.html \
+
 MOCHITEST_FILES	= \
 		test_sanity.html \
+		test_addCorrectType.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 \
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_add.html
@@ -0,0 +1,68 @@
+<!--
+  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=786922
+-->
+<head>
+  <title>Test for basic sanity of 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=786922">Mozilla Bug 786922</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+
+devicestorage_setup();
+
+function add(storage, mime) {
+  dump("adding: " + mime + "\n");
+  return navigator.getDeviceStorage(storage).add(createRandomBlob(mime));
+}
+
+var tests = [
+  function () { return add("pictures", "image/png")},
+  function () { return add("videos",   "video/webm")},
+  function () { return add("music",    "audio/wav")},
+];
+
+function fail(e) {
+  ok(false, "onerror was called");
+  devicestorage_cleanup();
+}
+
+function next(e) {
+
+  if (e != undefined)
+    ok(true, "addError was called");
+  
+  var f = tests.pop();
+
+  if (f == undefined) {
+    devicestorage_cleanup();
+    return;
+  }
+
+  request = f();
+  request.onsuccess = next;
+  request.onerror = fail;
+}
+
+next();
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_addCorrectType.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=786922
+-->
+<head>
+  <title>Test for basic sanity of 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=786922">Mozilla Bug 786922</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+
+devicestorage_setup();
+
+function addNamed(storage, mime, fileExtension) {
+  dump("adding: " + mime + " " + fileExtension + "\n");
+  return navigator.getDeviceStorage(storage).addNamed(createRandomBlob(mime), randomFilename(40) + "." + fileExtension);
+}
+
+// These tests must all fail
+var tests = [
+  function () { return addNamed("pictures", "kyle/smash", ".png")},
+  function () { return addNamed("pictures", "image/png",  ".poo")},
+  function () { return addNamed("music",    "kyle/smash", ".mp3")},
+  function () { return addNamed("music",    "music/mp3",  ".poo")},
+  function () { return addNamed("videos",   "kyle/smash", ".ogv")},
+  function () { return addNamed("videos",   "video/ogv",  ".poo")},
+];
+
+function fail(e) {
+  ok(false, "addSuccess was called");
+  devicestorage_cleanup();
+}
+
+function next(e) {
+
+  if (e != undefined)
+    ok(true, "addError was called");
+  
+  var f = tests.pop();
+
+  if (f == undefined) {
+    devicestorage_cleanup();
+    return;
+  }
+
+  request = f();
+  request.onsuccess = fail;
+  request.onerror = next;
+}
+
+next();
+
+</script>
+</pre>
+</body>
+</html>
+