Bug 1072535 - Pt 2 - Make DeviceStorage notify all storage types that match a particular file extension. r=mikeh
authorDave Hylands <dhylands@mozilla.com>
Tue, 14 Oct 2014 13:50:55 -0700
changeset 210381 902dfed4c12076349367d7c05c62f0150e91b819
parent 210380 3eb80fee7a636e552b1bae56fb533e8700b995d7
child 210382 9d0bba1b36dd24bb2441ff05aa846268b66850e9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmikeh
bugs1072535
milestone36.0a1
Bug 1072535 - Pt 2 - Make DeviceStorage notify all storage types that match a particular file extension. r=mikeh
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -45,16 +45,17 @@
 #include "DeviceStorageFileDescriptor.h"
 #include "DeviceStorageRequestChild.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIPermissionManager.h"
 #include "nsIStringBundle.h"
+#include "nsISupportsPrimitives.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 #include <algorithm>
 #include "private/pprio.h"
 #include "nsContentPermissionHelper.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
@@ -76,16 +77,20 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::ipc;
 
 #include "nsDirectoryServiceDefs.h"
 
+const char* kFileWatcherUpdate = "file-watcher-update";
+const char* kFileWatcherNotify = "file-watcher-notify";
+const char *kDownloadWatcherNotify = "download-watcher-notify";
+
 namespace mozilla {
   MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
 }
 
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
@@ -299,34 +304,40 @@ DeviceStorageTypeChecker::Check(const ns
 
 bool
 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
 {
   if (!aFile) {
     return false;
   }
 
+  nsString path;
+  aFile->GetPath(path);
+
+  return Check(aType, path);
+}
+
+bool
+DeviceStorageTypeChecker::Check(const nsAString& aType, const nsString& aPath)
+{
   if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
       aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
       aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
     // Apps, crashes and sdcard have no restrictions on what file extensions used.
     return true;
   }
 
-  nsString path;
-  aFile->GetPath(path);
-
-  int32_t dotIdx = path.RFindChar(char16_t('.'));
+  int32_t dotIdx = aPath.RFindChar(char16_t('.'));
   if (dotIdx == kNotFound) {
     return false;
   }
 
   nsAutoString extensionMatch;
   extensionMatch.Assign('*');
-  extensionMatch.Append(Substring(path, dotIdx));
+  extensionMatch.Append(Substring(aPath, dotIdx));
   extensionMatch.Append(';');
 
   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
     return CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions);
   }
 
   if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
     return CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions);
@@ -423,71 +434,176 @@ DeviceStorageTypeChecker::GetAccessForRe
       aAccessResult.AssignLiteral("create");
       break;
     default:
       aAccessResult.AssignLiteral("undefined");
   }
   return NS_OK;
 }
 
+static bool IsMediaType(const nsAString& aType)
+{
+  return aType.EqualsLiteral(DEVICESTORAGE_PICTURES) ||
+         aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) ||
+         aType.EqualsLiteral(DEVICESTORAGE_MUSIC) ||
+         aType.EqualsLiteral(DEVICESTORAGE_SDCARD);
+}
+
 //static
 bool
 DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType)
 {
 #ifdef MOZ_WIDGET_GONK
   // The apps and crashes aren't stored in the same place as the media, so
   // we only ever return a single apps object, and not an array
   // with one per volume (as is the case for the remaining
   // storage types).
-  return !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
-         !aType.EqualsLiteral(DEVICESTORAGE_CRASHES);
+  return IsMediaType(aType);
 #else
   return false;
 #endif
 }
 
+//static
+bool
+DeviceStorageTypeChecker::IsSharedMediaRoot(const nsAString& aType)
+{
+  // This function determines if aType shares a root directory with the
+  // other media types (so only applies to music, videos, pictures and sdcard).
+#ifdef MOZ_WIDGET_GONK
+  return IsMediaType(aType);
+#else
+  // For desktop, if the directories have been overridden, then they share
+  // a common root.
+  return IsMediaType(aType) && sDirs->overrideRootDir;
+#endif
+}
+
 NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
 
 mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
 
 FileUpdateDispatcher*
 FileUpdateDispatcher::GetSingleton()
 {
   if (sSingleton) {
     return sSingleton;
   }
 
   sSingleton = new FileUpdateDispatcher();
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(sSingleton, "file-watcher-notify", false);
+  obs->AddObserver(sSingleton, kFileWatcherNotify, false);
+  obs->AddObserver(sSingleton, kDownloadWatcherNotify, false);
   ClearOnShutdown(&sSingleton);
 
   return sSingleton;
 }
 
 NS_IMETHODIMP
-FileUpdateDispatcher::Observe(nsISupports *aSubject,
-                              const char *aTopic,
-                              const char16_t *aData)
+FileUpdateDispatcher::Observe(nsISupports* aSubject,
+                              const char* aTopic,
+                              const char16_t* aData)
 {
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-
-    DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
-    if (!file || !file->mFile) {
-      NS_WARNING("Device storage file looks invalid!");
+  nsRefPtr<DeviceStorageFile> dsf;
+
+  if (!strcmp(aTopic, kDownloadWatcherNotify)) {
+    // aSubject will be an nsISupportsString with the native path to the file
+    // in question.
+
+    nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
+    if (!supportsString) {
+      return NS_OK;
+    }
+    nsString path;
+    nsresult rv = supportsString->GetData(path);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       return NS_OK;
     }
+
+    // The downloader uses the sdcard storage type.
+    nsString volName;
+#ifdef MOZ_WIDGET_GONK
+    if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
+      nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+      if (NS_WARN_IF(!vs)) {
+        return NS_OK;
+      }
+      nsCOMPtr<nsIVolume> vol;
+      rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      rv = vol->GetName(volName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      nsString mountPoint;
+      rv = vol->GetMountPoint(mountPoint);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
+        return NS_OK;
+      }
+      path = Substring(path, mountPoint.Length() + 1);
+    }
+#endif
+    dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
+
+  } else if (!strcmp(aTopic, kFileWatcherNotify)) {
+    dsf = static_cast<DeviceStorageFile*>(aSubject);
+  } else {
+    NS_WARNING("FileUpdateDispatcher: Unrecognized topic");
+    return NS_OK;
+  }
+
+  if (!dsf || !dsf->mFile) {
+    NS_WARNING("FileUpdateDispatcher: Device storage file looks invalid!");
+    return NS_OK;
+  }
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    // Child process. Forward the notification to the parent.
     ContentChild::GetSingleton()
-      ->SendFilePathUpdateNotify(file->mStorageType,
-                                 file->mStorageName,
-                                 file->mPath,
+      ->SendFilePathUpdateNotify(dsf->mStorageType,
+                                 dsf->mStorageName,
+                                 dsf->mPath,
                                  NS_ConvertUTF16toUTF8(aData));
+    return NS_OK;
+  }
+
+  // Multiple storage types may match the same files. So walk through each of
+  // the storage types, and if the extension matches, tell them about it.
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
+    DeviceStorageTypeChecker* typeChecker
+      = DeviceStorageTypeChecker::CreateOrGet();
+    MOZ_ASSERT(typeChecker);
+
+    static const nsLiteralString kMediaTypes[] = {
+      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
+      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
+      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
+      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
+    };
+
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
+      nsRefPtr<DeviceStorageFile> dsf2;
+      if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
+        if (dsf->mStorageType.Equals(kMediaTypes[i])) {
+          dsf2 = dsf;
+        } else {
+          dsf2 = new DeviceStorageFile(kMediaTypes[i],
+                                       dsf->mStorageName, dsf->mPath);
+        }
+        obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
+      }
+    }
   } else {
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    obs->NotifyObservers(aSubject, "file-watcher-update", aData);
+    obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
   }
   return NS_OK;
 }
 
 class IOEventComplete : public nsRunnable
 {
 public:
   IOEventComplete(DeviceStorageFile *aFile, const char *aType)
@@ -500,17 +616,17 @@ public:
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     nsString data;
     CopyASCIItoUTF16(mType, data);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 
-    obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
+    obs->NotifyObservers(mFile, kFileWatcherNotify, data.get());
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
     MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(mFile->mStorageName);
     return NS_OK;
   }
 
@@ -1737,17 +1853,17 @@ nsDOMDeviceStorage::SetRootDirectoryForT
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIFile> f;
   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
                                              aStorageName,
                                              getter_AddRefs(f));
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(this, "file-watcher-update", false);
+  obs->AddObserver(this, kFileWatcherUpdate, false);
   obs->AddObserver(this, "disk-space-watcher", false);
   mRootDirectory = f;
   mStorageType = aStorageType;
   mStorageName = aStorageName;
 }
 
 JS::Value
 InterfaceToJsval(nsPIDOMWindow* aWindow,
@@ -3311,17 +3427,17 @@ nsDOMDeviceStorage::Shutdown()
     mFileSystem = nullptr;
   }
 
   if (!mStorageName.IsEmpty()) {
     UnregisterForSDCardChanges(this);
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->RemoveObserver(this, "file-watcher-update");
+  obs->RemoveObserver(this, kFileWatcherUpdate);
   obs->RemoveObserver(this, "disk-space-watcher");
 }
 
 StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
 
 // static
 void
 nsDOMDeviceStorage::GetOrderedVolumeNames(
@@ -4240,17 +4356,17 @@ nsDOMDeviceStorage::DispatchStorageStatu
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Observe(nsISupports *aSubject,
                             const char *aTopic,
                             const char16_t *aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!strcmp(aTopic, "file-watcher-update")) {
+  if (!strcmp(aTopic, kFileWatcherUpdate)) {
 
     DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
     Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
     return NS_OK;
   }
   if (!strcmp(aTopic, "disk-space-watcher")) {
     // 'disk-space-watcher' notifications are sent when there is a modification
     // of a file in a specific location while a low device storage situation
@@ -4394,17 +4510,17 @@ NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
                                            nsIDOMEventListener *aListener,
                                            bool aUseCapture,
                                            bool aWantsUntrusted,
                                            uint8_t aArgc)
 {
   if (!mIsWatchingFile) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    obs->AddObserver(this, "file-watcher-update", false);
+    obs->AddObserver(this, kFileWatcherUpdate, false);
     mIsWatchingFile = true;
   }
 
   return nsDOMDeviceStorage::AddEventListener(aType, aListener, aUseCapture,
                                               aWantsUntrusted, aArgc);
 }
 
 NS_IMETHODIMP
@@ -4412,33 +4528,33 @@ nsDOMDeviceStorage::RemoveEventListener(
                                         nsIDOMEventListener *aListener,
                                         bool aUseCapture)
 {
   DOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
 
   if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
     mIsWatchingFile = false;
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    obs->RemoveObserver(this, "file-watcher-update");
+    obs->RemoveObserver(this, kFileWatcherUpdate);
   }
   return NS_OK;
 }
 
 void
 nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType,
                                         EventListener* aListener,
                                         bool aCapture,
                                         ErrorResult& aRv)
 {
   DOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv);
 
   if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
     mIsWatchingFile = false;
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    obs->RemoveObserver(this, "file-watcher-update");
+    obs->RemoveObserver(this, kFileWatcherUpdate);
   }
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
                                               nsIDOMEventListener *aListener,
                                               bool aUseCapture)
 {
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -153,22 +153,24 @@ public:
 
   DeviceStorageTypeChecker();
   ~DeviceStorageTypeChecker();
 
   void InitFromBundle(nsIStringBundle* aBundle);
 
   bool Check(const nsAString& aType, nsIDOMBlob* aBlob);
   bool Check(const nsAString& aType, nsIFile* aFile);
+  bool Check(const nsAString& aType, const nsString& aPath);
   void GetTypeFromFile(nsIFile* aFile, nsAString& aType);
   void GetTypeFromFileName(const nsAString& aFileName, nsAString& aType);
 
   static nsresult GetPermissionForType(const nsAString& aType, nsACString& aPermissionResult);
   static nsresult GetAccessForRequest(const DeviceStorageRequestType aRequestType, nsACString& aAccessResult);
   static bool IsVolumeBased(const nsAString& aType);
+  static bool IsSharedMediaRoot(const nsAString& aType);
 
 private:
   nsString mPicturesExtensions;
   nsString mVideosExtensions;
   nsString mMusicExtensions;
 
   static mozilla::StaticAutoPtr<DeviceStorageTypeChecker> sDeviceStorageTypeChecker;
 };