Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / devicestorage / nsDeviceStorage.cpp
bloba445b5d91721bfc5c88dc913ac4a59b9729e00f9
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDeviceStorage.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/dom/ContentChild.h"
13 #include "mozilla/dom/DeviceStorageBinding.h"
14 #include "mozilla/dom/DeviceStorageChangeEvent.h"
15 #include "mozilla/dom/DeviceStorageFileSystem.h"
16 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
17 #include "mozilla/dom/Directory.h"
18 #include "mozilla/dom/FileSystemUtils.h"
19 #include "mozilla/dom/ipc/BlobChild.h"
20 #include "mozilla/dom/PBrowserChild.h"
21 #include "mozilla/dom/PermissionMessageUtils.h"
22 #include "mozilla/dom/Promise.h"
23 #include "mozilla/dom/ScriptSettings.h"
24 #include "mozilla/EventDispatcher.h"
25 #include "mozilla/EventListenerManager.h"
26 #include "mozilla/LazyIdleThread.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/Scoped.h"
29 #include "mozilla/Services.h"
31 #include "nsArrayUtils.h"
32 #include "nsAutoPtr.h"
33 #include "nsGlobalWindow.h"
34 #include "nsServiceManagerUtils.h"
35 #include "nsIFile.h"
36 #include "nsIDirectoryEnumerator.h"
37 #include "nsAppDirectoryServiceDefs.h"
38 #include "nsDirectoryServiceDefs.h"
39 #include "nsNetUtil.h"
40 #include "nsCycleCollectionParticipant.h"
41 #include "nsIPrincipal.h"
42 #include "nsJSUtils.h"
43 #include "nsContentUtils.h"
44 #include "nsXULAppAPI.h"
45 #include "DeviceStorageFileDescriptor.h"
46 #include "DeviceStorageRequestChild.h"
47 #include "nsCRT.h"
48 #include "nsIObserverService.h"
49 #include "nsIMIMEService.h"
50 #include "nsCExternalHandlerService.h"
51 #include "nsIPermissionManager.h"
52 #include "nsIStringBundle.h"
53 #include "nsISupportsPrimitives.h"
54 #include "nsIDocument.h"
55 #include "nsPrintfCString.h"
56 #include <algorithm>
57 #include "private/pprio.h"
58 #include "nsContentPermissionHelper.h"
60 #include "mozilla/dom/DeviceStorageBinding.h"
62 // Microsoft's API Name hackery sucks
63 #undef CreateEvent
65 #ifdef MOZ_WIDGET_ANDROID
66 #include "AndroidBridge.h"
67 #endif
69 #ifdef MOZ_WIDGET_GONK
70 #include "nsIVolume.h"
71 #include "nsIVolumeService.h"
72 #endif
74 #define DEVICESTORAGE_PROPERTIES \
75 "chrome://global/content/devicestorage.properties"
76 #define DEFAULT_THREAD_TIMEOUT_MS 30000
78 using namespace mozilla;
79 using namespace mozilla::dom;
80 using namespace mozilla::dom::devicestorage;
81 using namespace mozilla::ipc;
83 #include "nsDirectoryServiceDefs.h"
85 const char* kFileWatcherUpdate = "file-watcher-update";
86 const char* kFileWatcherNotify = "file-watcher-notify";
87 const char *kDownloadWatcherNotify = "download-watcher-notify";
89 namespace mozilla {
90 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
93 StaticAutoPtr<DeviceStorageUsedSpaceCache>
94 DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
96 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
98 MOZ_ASSERT(NS_IsMainThread());
100 mIOThread = new LazyIdleThread(
101 DEFAULT_THREAD_TIMEOUT_MS,
102 NS_LITERAL_CSTRING("DeviceStorageUsedSpaceCache I/O"));
106 DeviceStorageUsedSpaceCache::~DeviceStorageUsedSpaceCache()
110 DeviceStorageUsedSpaceCache*
111 DeviceStorageUsedSpaceCache::CreateOrGet()
113 if (sDeviceStorageUsedSpaceCache) {
114 return sDeviceStorageUsedSpaceCache;
117 MOZ_ASSERT(NS_IsMainThread());
119 sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache();
120 ClearOnShutdown(&sDeviceStorageUsedSpaceCache);
121 return sDeviceStorageUsedSpaceCache;
124 already_AddRefed<DeviceStorageUsedSpaceCache::CacheEntry>
125 DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName)
127 nsTArray<nsRefPtr<CacheEntry>>::size_type numEntries = mCacheEntries.Length();
128 nsTArray<nsRefPtr<CacheEntry>>::index_type i;
129 for (i = 0; i < numEntries; i++) {
130 nsRefPtr<CacheEntry>& cacheEntry = mCacheEntries[i];
131 if (cacheEntry->mStorageName.Equals(aStorageName)) {
132 nsRefPtr<CacheEntry> addRefedCacheEntry = cacheEntry;
133 return addRefedCacheEntry.forget();
136 return nullptr;
139 static int64_t
140 GetFreeBytes(const nsAString& aStorageName)
142 // This function makes the assumption that the various types
143 // are all stored on the same filesystem. So we use pictures.
145 nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
146 aStorageName));
147 int64_t freeBytes = 0;
148 dsf->GetDiskFreeSpace(&freeBytes);
149 return freeBytes;
152 nsresult
153 DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
154 uint64_t* aPicturesSoFar,
155 uint64_t* aVideosSoFar,
156 uint64_t* aMusicSoFar,
157 uint64_t* aTotalSoFar)
159 nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
160 if (!cacheEntry || cacheEntry->mDirty) {
161 return NS_ERROR_NOT_AVAILABLE;
163 int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName);
164 if (freeBytes != cacheEntry->mFreeBytes) {
165 // Free space changed, so our cached results are no longer valid.
166 return NS_ERROR_NOT_AVAILABLE;
169 *aPicturesSoFar += cacheEntry->mPicturesUsedSize;
170 *aVideosSoFar += cacheEntry->mVideosUsedSize;
171 *aMusicSoFar += cacheEntry->mMusicUsedSize;
172 *aTotalSoFar += cacheEntry->mTotalUsedSize;
174 return NS_OK;
177 void
178 DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName,
179 uint64_t aPictureSize,
180 uint64_t aVideosSize,
181 uint64_t aMusicSize,
182 uint64_t aTotalUsedSize)
184 nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
185 if (!cacheEntry) {
186 cacheEntry = new CacheEntry;
187 cacheEntry->mStorageName = aStorageName;
188 mCacheEntries.AppendElement(cacheEntry);
190 cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName);
192 cacheEntry->mPicturesUsedSize = aPictureSize;
193 cacheEntry->mVideosUsedSize = aVideosSize;
194 cacheEntry->mMusicUsedSize = aMusicSize;
195 cacheEntry->mTotalUsedSize = aTotalUsedSize;
196 cacheEntry->mDirty = false;
199 class GlobalDirs
201 private:
202 ~GlobalDirs() {}
203 public:
204 NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
205 #if !defined(MOZ_WIDGET_GONK)
206 nsCOMPtr<nsIFile> pictures;
207 nsCOMPtr<nsIFile> videos;
208 nsCOMPtr<nsIFile> music;
209 nsCOMPtr<nsIFile> sdcard;
210 #endif
211 nsCOMPtr<nsIFile> apps;
212 nsCOMPtr<nsIFile> crashes;
213 nsCOMPtr<nsIFile> overrideRootDir;
216 static StaticRefPtr<GlobalDirs> sDirs;
218 StaticAutoPtr<DeviceStorageTypeChecker>
219 DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
221 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
225 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
229 DeviceStorageTypeChecker*
230 DeviceStorageTypeChecker::CreateOrGet()
232 if (sDeviceStorageTypeChecker) {
233 return sDeviceStorageTypeChecker;
236 MOZ_ASSERT(NS_IsMainThread());
238 nsCOMPtr<nsIStringBundleService> stringService
239 = mozilla::services::GetStringBundleService();
240 if (!stringService) {
241 return nullptr;
244 nsCOMPtr<nsIStringBundle> filterBundle;
245 if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
246 getter_AddRefs(filterBundle)))) {
247 return nullptr;
250 DeviceStorageTypeChecker* result = new DeviceStorageTypeChecker();
251 result->InitFromBundle(filterBundle);
253 sDeviceStorageTypeChecker = result;
254 ClearOnShutdown(&sDeviceStorageTypeChecker);
255 return result;
258 void
259 DeviceStorageTypeChecker::InitFromBundle(nsIStringBundle* aBundle)
261 aBundle->GetStringFromName(
262 NS_ConvertASCIItoUTF16(DEVICESTORAGE_PICTURES).get(),
263 getter_Copies(mPicturesExtensions));
264 aBundle->GetStringFromName(
265 NS_ConvertASCIItoUTF16(DEVICESTORAGE_MUSIC).get(),
266 getter_Copies(mMusicExtensions));
267 aBundle->GetStringFromName(
268 NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(),
269 getter_Copies(mVideosExtensions));
273 bool
274 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
276 MOZ_ASSERT(aBlob);
278 nsString mimeType;
279 if (NS_FAILED(aBlob->GetType(mimeType))) {
280 return false;
283 if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
284 return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
287 if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
288 return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
291 if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
292 return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
295 if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
296 aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
297 aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
298 // Apps, crashes and sdcard have no restriction on mime types
299 return true;
302 return false;
305 bool
306 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
308 if (!aFile) {
309 return false;
312 nsString path;
313 aFile->GetPath(path);
315 return Check(aType, path);
318 bool
319 DeviceStorageTypeChecker::Check(const nsAString& aType, const nsString& aPath)
321 if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
322 aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
323 aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
324 // Apps, crashes and sdcard have no restrictions on what file extensions used.
325 return true;
328 int32_t dotIdx = aPath.RFindChar(char16_t('.'));
329 if (dotIdx == kNotFound) {
330 return false;
333 nsAutoString extensionMatch;
334 extensionMatch.Assign('*');
335 extensionMatch.Append(Substring(aPath, dotIdx));
336 extensionMatch.Append(';');
338 if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
339 return CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions);
342 if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
343 return CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions);
346 if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
347 return CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions);
350 return false;
353 void
354 DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType)
356 MOZ_ASSERT(aFile);
358 nsString path;
359 aFile->GetPath(path);
361 GetTypeFromFileName(path, aType);
364 void
365 DeviceStorageTypeChecker::GetTypeFromFileName(const nsAString& aFileName,
366 nsAString& aType)
368 aType.AssignLiteral(DEVICESTORAGE_SDCARD);
370 nsString fileName(aFileName);
371 int32_t dotIdx = fileName.RFindChar(char16_t('.'));
372 if (dotIdx == kNotFound) {
373 return;
376 nsAutoString extensionMatch;
377 extensionMatch.Assign('*');
378 extensionMatch.Append(Substring(aFileName, dotIdx));
379 extensionMatch.Append(';');
381 if (CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions)) {
382 aType.AssignLiteral(DEVICESTORAGE_PICTURES);
384 else if (CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions)) {
385 aType.AssignLiteral(DEVICESTORAGE_VIDEOS);
387 else if (CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions)) {
388 aType.AssignLiteral(DEVICESTORAGE_MUSIC);
392 nsresult
393 DeviceStorageTypeChecker::GetPermissionForType(const nsAString& aType,
394 nsACString& aPermissionResult)
396 if (!aType.EqualsLiteral(DEVICESTORAGE_PICTURES) &&
397 !aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) &&
398 !aType.EqualsLiteral(DEVICESTORAGE_MUSIC) &&
399 !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
400 !aType.EqualsLiteral(DEVICESTORAGE_SDCARD) &&
401 !aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
402 // unknown type
403 return NS_ERROR_FAILURE;
406 aPermissionResult.AssignLiteral("device-storage:");
407 aPermissionResult.Append(NS_ConvertUTF16toUTF8(aType));
408 return NS_OK;
411 nsresult
412 DeviceStorageTypeChecker::GetAccessForRequest(
413 const DeviceStorageRequestType aRequestType, nsACString& aAccessResult)
415 switch(aRequestType) {
416 case DEVICE_STORAGE_REQUEST_READ:
417 case DEVICE_STORAGE_REQUEST_WATCH:
418 case DEVICE_STORAGE_REQUEST_FREE_SPACE:
419 case DEVICE_STORAGE_REQUEST_USED_SPACE:
420 case DEVICE_STORAGE_REQUEST_AVAILABLE:
421 case DEVICE_STORAGE_REQUEST_STATUS:
422 aAccessResult.AssignLiteral("read");
423 break;
424 case DEVICE_STORAGE_REQUEST_WRITE:
425 case DEVICE_STORAGE_REQUEST_APPEND:
426 case DEVICE_STORAGE_REQUEST_DELETE:
427 case DEVICE_STORAGE_REQUEST_FORMAT:
428 case DEVICE_STORAGE_REQUEST_MOUNT:
429 case DEVICE_STORAGE_REQUEST_UNMOUNT:
430 aAccessResult.AssignLiteral("write");
431 break;
432 case DEVICE_STORAGE_REQUEST_CREATE:
433 case DEVICE_STORAGE_REQUEST_CREATEFD:
434 aAccessResult.AssignLiteral("create");
435 break;
436 default:
437 aAccessResult.AssignLiteral("undefined");
439 return NS_OK;
442 static bool IsMediaType(const nsAString& aType)
444 return aType.EqualsLiteral(DEVICESTORAGE_PICTURES) ||
445 aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) ||
446 aType.EqualsLiteral(DEVICESTORAGE_MUSIC) ||
447 aType.EqualsLiteral(DEVICESTORAGE_SDCARD);
450 //static
451 bool
452 DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType)
454 #ifdef MOZ_WIDGET_GONK
455 // The apps and crashes aren't stored in the same place as the media, so
456 // we only ever return a single apps object, and not an array
457 // with one per volume (as is the case for the remaining
458 // storage types).
459 return IsMediaType(aType);
460 #else
461 return false;
462 #endif
465 //static
466 bool
467 DeviceStorageTypeChecker::IsSharedMediaRoot(const nsAString& aType)
469 // This function determines if aType shares a root directory with the
470 // other media types (so only applies to music, videos, pictures and sdcard).
471 #ifdef MOZ_WIDGET_GONK
472 return IsMediaType(aType);
473 #else
474 // For desktop, if the directories have been overridden, then they share
475 // a common root.
476 return IsMediaType(aType) && sDirs->overrideRootDir;
477 #endif
480 NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
482 mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
484 FileUpdateDispatcher*
485 FileUpdateDispatcher::GetSingleton()
487 if (sSingleton) {
488 return sSingleton;
491 sSingleton = new FileUpdateDispatcher();
492 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
493 obs->AddObserver(sSingleton, kFileWatcherNotify, false);
494 obs->AddObserver(sSingleton, kDownloadWatcherNotify, false);
495 ClearOnShutdown(&sSingleton);
497 return sSingleton;
500 NS_IMETHODIMP
501 FileUpdateDispatcher::Observe(nsISupports* aSubject,
502 const char* aTopic,
503 const char16_t* aData)
505 nsRefPtr<DeviceStorageFile> dsf;
507 if (!strcmp(aTopic, kDownloadWatcherNotify)) {
508 // aSubject will be an nsISupportsString with the native path to the file
509 // in question.
511 nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
512 if (!supportsString) {
513 return NS_OK;
515 nsString path;
516 nsresult rv = supportsString->GetData(path);
517 if (NS_WARN_IF(NS_FAILED(rv))) {
518 return NS_OK;
521 // The downloader uses the sdcard storage type.
522 nsString volName;
523 #ifdef MOZ_WIDGET_GONK
524 if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
525 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
526 if (NS_WARN_IF(!vs)) {
527 return NS_OK;
529 nsCOMPtr<nsIVolume> vol;
530 rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
531 if (NS_WARN_IF(NS_FAILED(rv))) {
532 return NS_OK;
534 rv = vol->GetName(volName);
535 if (NS_WARN_IF(NS_FAILED(rv))) {
536 return NS_OK;
538 nsString mountPoint;
539 rv = vol->GetMountPoint(mountPoint);
540 if (NS_WARN_IF(NS_FAILED(rv))) {
541 return NS_OK;
543 if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
544 return NS_OK;
546 path = Substring(path, mountPoint.Length() + 1);
548 #endif
549 dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
551 } else if (!strcmp(aTopic, kFileWatcherNotify)) {
552 dsf = static_cast<DeviceStorageFile*>(aSubject);
553 } else {
554 NS_WARNING("FileUpdateDispatcher: Unrecognized topic");
555 return NS_OK;
558 if (!dsf || !dsf->mFile) {
559 NS_WARNING("FileUpdateDispatcher: Device storage file looks invalid!");
560 return NS_OK;
563 if (XRE_GetProcessType() != GeckoProcessType_Default) {
564 // Child process. Forward the notification to the parent.
565 ContentChild::GetSingleton()
566 ->SendFilePathUpdateNotify(dsf->mStorageType,
567 dsf->mStorageName,
568 dsf->mPath,
569 NS_ConvertUTF16toUTF8(aData));
570 return NS_OK;
573 // Multiple storage types may match the same files. So walk through each of
574 // the storage types, and if the extension matches, tell them about it.
575 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
576 if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
577 DeviceStorageTypeChecker* typeChecker
578 = DeviceStorageTypeChecker::CreateOrGet();
579 MOZ_ASSERT(typeChecker);
581 static const nsLiteralString kMediaTypes[] = {
582 NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
583 NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
584 NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
585 NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
588 for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
589 nsRefPtr<DeviceStorageFile> dsf2;
590 if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
591 if (dsf->mStorageType.Equals(kMediaTypes[i])) {
592 dsf2 = dsf;
593 } else {
594 dsf2 = new DeviceStorageFile(kMediaTypes[i],
595 dsf->mStorageName, dsf->mPath);
597 obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
600 } else {
601 obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
603 return NS_OK;
606 class IOEventComplete : public nsRunnable
608 public:
609 IOEventComplete(DeviceStorageFile *aFile, const char *aType)
610 : mFile(aFile)
611 , mType(aType)
615 ~IOEventComplete() {}
617 NS_IMETHOD Run()
619 MOZ_ASSERT(NS_IsMainThread());
620 nsString data;
621 CopyASCIItoUTF16(mType, data);
622 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
624 obs->NotifyObservers(mFile, kFileWatcherNotify, data.get());
626 DeviceStorageUsedSpaceCache* usedSpaceCache
627 = DeviceStorageUsedSpaceCache::CreateOrGet();
628 MOZ_ASSERT(usedSpaceCache);
629 usedSpaceCache->Invalidate(mFile->mStorageName);
630 return NS_OK;
633 private:
634 nsRefPtr<DeviceStorageFile> mFile;
635 nsCString mType;
638 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
639 const nsAString& aStorageName,
640 const nsAString& aRootDir,
641 const nsAString& aPath)
642 : mStorageType(aStorageType)
643 , mStorageName(aStorageName)
644 , mRootDir(aRootDir)
645 , mPath(aPath)
646 , mEditable(false)
647 , mLength(UINT64_MAX)
648 , mLastModifiedDate(UINT64_MAX)
650 Init();
651 AppendRelativePath(mRootDir);
652 if (!mPath.EqualsLiteral("")) {
653 AppendRelativePath(mPath);
655 NormalizeFilePath();
658 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
659 const nsAString& aStorageName,
660 const nsAString& aPath)
661 : mStorageType(aStorageType)
662 , mStorageName(aStorageName)
663 , mPath(aPath)
664 , mEditable(false)
665 , mLength(UINT64_MAX)
666 , mLastModifiedDate(UINT64_MAX)
668 Init();
669 AppendRelativePath(aPath);
670 NormalizeFilePath();
673 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
674 const nsAString& aStorageName)
675 : mStorageType(aStorageType)
676 , mStorageName(aStorageName)
677 , mEditable(false)
678 , mLength(UINT64_MAX)
679 , mLastModifiedDate(UINT64_MAX)
681 Init();
684 void
685 DeviceStorageFile::Dump(const char* label)
687 nsString path;
688 if (mFile) {
689 mFile->GetPath(path);
690 } else {
691 path = NS_LITERAL_STRING("(null)");
693 const char* ptStr;
694 if (XRE_GetProcessType() == GeckoProcessType_Default) {
695 ptStr = "parent";
696 } else {
697 ptStr = "child";
700 printf_stderr("DSF (%s) %s: mStorageType '%s' mStorageName '%s' "
701 "mRootDir '%s' mPath '%s' mFile->GetPath '%s'\n",
702 ptStr, label,
703 NS_LossyConvertUTF16toASCII(mStorageType).get(),
704 NS_LossyConvertUTF16toASCII(mStorageName).get(),
705 NS_LossyConvertUTF16toASCII(mRootDir).get(),
706 NS_LossyConvertUTF16toASCII(mPath).get(),
707 NS_LossyConvertUTF16toASCII(path).get());
710 void
711 DeviceStorageFile::Init()
713 DeviceStorageFile::GetRootDirectoryForType(mStorageType,
714 mStorageName,
715 getter_AddRefs(mFile));
717 DebugOnly<DeviceStorageTypeChecker*> typeChecker
718 = DeviceStorageTypeChecker::CreateOrGet();
719 MOZ_ASSERT(typeChecker);
722 // The OverrideRootDir is needed to facilitate testing of the
723 // device.storage.overrideRootDir preference. The preference is normally
724 // only read once during initialization, but since the test environment has
725 // no convenient way to restart, we use a pref watcher instead.
726 class OverrideRootDir MOZ_FINAL : public nsIObserver
728 ~OverrideRootDir();
730 public:
731 NS_DECL_ISUPPORTS
732 NS_DECL_NSIOBSERVER
734 static OverrideRootDir* GetSingleton();
735 void Init();
736 private:
737 static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
740 NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver)
742 mozilla::StaticRefPtr<OverrideRootDir>
743 OverrideRootDir::sSingleton;
745 OverrideRootDir*
746 OverrideRootDir::GetSingleton()
748 if (sSingleton) {
749 return sSingleton;
751 // Preference changes are automatically forwarded from parent to child
752 // in ContentParent::Observe, so we'll see the change in both the parent
753 // and the child process.
755 sSingleton = new OverrideRootDir();
756 Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
757 Preferences::AddStrongObserver(sSingleton, "device.storage.testing");
758 ClearOnShutdown(&sSingleton);
760 return sSingleton;
763 OverrideRootDir::~OverrideRootDir()
765 Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
766 Preferences::RemoveObserver(this, "device.storage.testing");
769 NS_IMETHODIMP
770 OverrideRootDir::Observe(nsISupports *aSubject,
771 const char *aTopic,
772 const char16_t *aData)
774 MOZ_ASSERT(NS_IsMainThread());
776 if (sSingleton) {
777 sSingleton->Init();
779 return NS_OK;
782 void
783 OverrideRootDir::Init()
785 MOZ_ASSERT(NS_IsMainThread());
787 if (!sDirs) {
788 return;
791 if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
792 nsCOMPtr<nsIProperties> dirService
793 = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
794 MOZ_ASSERT(dirService);
795 dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
796 getter_AddRefs(sDirs->overrideRootDir));
797 if (sDirs->overrideRootDir) {
798 sDirs->overrideRootDir->AppendRelativeNativePath(
799 NS_LITERAL_CSTRING("device-storage-testing"));
801 } else {
802 // For users running on desktop, it's convenient to be able to override
803 // all of the directories to point to a single tree, much like what happens
804 // on a real device.
805 const nsAdoptingString& overrideRootDir =
806 mozilla::Preferences::GetString("device.storage.overrideRootDir");
807 if (overrideRootDir && overrideRootDir.Length() > 0) {
808 NS_NewLocalFile(overrideRootDir, false,
809 getter_AddRefs(sDirs->overrideRootDir));
810 } else {
811 sDirs->overrideRootDir = nullptr;
815 if (sDirs->overrideRootDir) {
816 if (XRE_GetProcessType() == GeckoProcessType_Default) {
817 // Only the parent process can create directories. In testing, because
818 // the preference is updated after startup, its entirely possible that
819 // the preference updated notification will be received by a child
820 // prior to the parent.
821 nsresult rv
822 = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
823 nsString path;
824 sDirs->overrideRootDir->GetPath(path);
825 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
826 nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
827 NS_LossyConvertUTF16toASCII(path).get());
828 NS_WARNING(msg.get());
831 sDirs->overrideRootDir->Normalize();
835 // Directories which don't depend on a volume should be calculated once
836 // here. Directories which depend on the root directory of a volume
837 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
838 static void
839 InitDirs()
841 if (sDirs) {
842 return;
844 MOZ_ASSERT(NS_IsMainThread());
845 sDirs = new GlobalDirs;
846 ClearOnShutdown(&sDirs);
848 nsCOMPtr<nsIProperties> dirService
849 = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
850 MOZ_ASSERT(dirService);
852 #if !defined(MOZ_WIDGET_GONK)
854 // Keep MOZ_WIDGET_COCOA above XP_UNIX,
855 // because both are defined in Darwin builds.
856 #if defined (MOZ_WIDGET_COCOA)
857 dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
858 NS_GET_IID(nsIFile),
859 getter_AddRefs(sDirs->pictures));
860 dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
861 NS_GET_IID(nsIFile),
862 getter_AddRefs(sDirs->videos));
863 dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
864 NS_GET_IID(nsIFile),
865 getter_AddRefs(sDirs->music));
867 // Keep MOZ_WIDGET_ANDROID above XP_UNIX,
868 // because both are defined in Android builds.
869 #elif defined (MOZ_WIDGET_ANDROID)
870 nsAutoString path;
871 if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
872 NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
873 NS_NewLocalFile(path, /* aFollowLinks */ true,
874 getter_AddRefs(sDirs->pictures));
876 if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
877 NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
878 NS_NewLocalFile(path, /* aFollowLinks */ true,
879 getter_AddRefs(sDirs->videos));
881 if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
882 NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
883 NS_NewLocalFile(path, /* aFollowLinks */ true,
884 getter_AddRefs(sDirs->music));
886 if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
887 NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
888 NS_NewLocalFile(path, /* aFollowLinks */ true,
889 getter_AddRefs(sDirs->sdcard));
892 #elif defined (XP_UNIX)
893 dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
894 NS_GET_IID(nsIFile),
895 getter_AddRefs(sDirs->pictures));
896 dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
897 NS_GET_IID(nsIFile),
898 getter_AddRefs(sDirs->videos));
899 dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
900 NS_GET_IID(nsIFile),
901 getter_AddRefs(sDirs->music));
903 #elif defined (XP_WIN)
904 dirService->Get(NS_WIN_PICTURES_DIR,
905 NS_GET_IID(nsIFile),
906 getter_AddRefs(sDirs->pictures));
907 dirService->Get(NS_WIN_VIDEOS_DIR,
908 NS_GET_IID(nsIFile),
909 getter_AddRefs(sDirs->videos));
910 dirService->Get(NS_WIN_MUSIC_DIR,
911 NS_GET_IID(nsIFile),
912 getter_AddRefs(sDirs->music));
913 #endif
915 #ifndef MOZ_WIDGET_ANDROID
916 // Eventually, on desktop, we want to do something smarter -- for example,
917 // detect when an sdcard is inserted, and use that instead of this.
918 dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
919 getter_AddRefs(sDirs->sdcard));
920 if (sDirs->sdcard) {
921 sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
923 #endif // !MOZ_WIDGET_ANDROID
924 #endif // !MOZ_WIDGET_GONK
926 #ifdef MOZ_WIDGET_GONK
927 NS_NewLocalFile(NS_LITERAL_STRING("/data"),
928 false,
929 getter_AddRefs(sDirs->apps));
930 #else
931 dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
932 getter_AddRefs(sDirs->apps));
933 if (sDirs->apps) {
934 sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
936 #endif
938 if (XRE_GetProcessType() == GeckoProcessType_Default) {
939 NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
940 if (sDirs->crashes) {
941 sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports"));
943 } else {
944 // NS_GetSpecialDirectory("UAppData") fails in content processes because
945 // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
946 #ifdef MOZ_WIDGET_GONK
947 NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
948 false,
949 getter_AddRefs(sDirs->crashes));
950 #endif
953 OverrideRootDir::GetSingleton()->Init();
956 void
957 DeviceStorageFile::GetFullPath(nsAString &aFullPath)
959 aFullPath.Truncate();
960 if (!mStorageName.EqualsLiteral("")) {
961 aFullPath.Append('/');
962 aFullPath.Append(mStorageName);
963 aFullPath.Append('/');
965 if (!mRootDir.EqualsLiteral("")) {
966 aFullPath.Append(mRootDir);
967 aFullPath.Append('/');
969 aFullPath.Append(mPath);
973 // Directories which don't depend on a volume should be calculated once
974 // in InitDirs. Directories which depend on the root directory of a volume
975 // should be calculated in this method.
976 void
977 DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
978 const nsAString& aStorageName,
979 nsIFile** aFile)
981 nsCOMPtr<nsIFile> f;
982 *aFile = nullptr;
983 bool allowOverride = true;
985 InitDirs();
987 #ifdef MOZ_WIDGET_GONK
988 nsresult rv;
989 nsString volMountPoint;
990 if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
991 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
992 NS_ENSURE_TRUE_VOID(vs);
993 nsCOMPtr<nsIVolume> vol;
994 rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol));
995 if(NS_FAILED(rv)) {
996 printf_stderr("##### DeviceStorage: GetVolumeByName('%s') failed\n",
997 NS_LossyConvertUTF16toASCII(aStorageName).get());
999 NS_ENSURE_SUCCESS_VOID(rv);
1000 vol->GetMountPoint(volMountPoint);
1002 #endif
1004 // Picture directory
1005 if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
1006 #ifdef MOZ_WIDGET_GONK
1007 rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
1008 if(NS_FAILED(rv)) {
1009 printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
1010 NS_LossyConvertUTF16toASCII(volMountPoint).get(),
1011 NS_LossyConvertUTF16toASCII(aStorageType).get());
1013 #else
1014 f = sDirs->pictures;
1015 #endif
1018 // Video directory
1019 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
1020 #ifdef MOZ_WIDGET_GONK
1021 rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
1022 if(NS_FAILED(rv)) {
1023 printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
1024 NS_LossyConvertUTF16toASCII(volMountPoint).get(),
1025 NS_LossyConvertUTF16toASCII(aStorageType).get());
1027 #else
1028 f = sDirs->videos;
1029 #endif
1032 // Music directory
1033 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
1034 #ifdef MOZ_WIDGET_GONK
1035 rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
1036 if(NS_FAILED(rv)) {
1037 printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
1038 NS_LossyConvertUTF16toASCII(volMountPoint).get(),
1039 NS_LossyConvertUTF16toASCII(aStorageType).get());
1041 #else
1042 f = sDirs->music;
1043 #endif
1046 // Apps directory
1047 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
1048 f = sDirs->apps;
1049 allowOverride = false;
1052 // default SDCard
1053 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
1054 #ifdef MOZ_WIDGET_GONK
1055 rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
1056 if(NS_FAILED(rv)) {
1057 printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
1058 NS_LossyConvertUTF16toASCII(volMountPoint).get(),
1059 NS_LossyConvertUTF16toASCII(aStorageType).get());
1061 #else
1062 f = sDirs->sdcard;
1063 #endif
1066 // crash reports directory.
1067 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
1068 f = sDirs->crashes;
1069 allowOverride = false;
1070 } else {
1071 // Not a storage type that we recognize. Return null
1072 return;
1075 // In testing, we default all device storage types to a temp directory.
1076 // sDirs->overrideRootDir will only have been initialized (in InitDirs)
1077 // if the preference device.storage.testing was set to true, or if
1078 // device.storage.overrideRootDir is set. We can't test the preferences
1079 // directly here, since we may not be on the main thread.
1080 if (allowOverride && sDirs->overrideRootDir) {
1081 f = sDirs->overrideRootDir;
1084 if (f) {
1085 f->Clone(aFile);
1086 } else {
1087 // This should never happen unless something is severely wrong. So
1088 // scream a little.
1089 printf_stderr("##### GetRootDirectoryForType('%s', '%s') failed #####",
1090 NS_LossyConvertUTF16toASCII(aStorageType).get(),
1091 NS_LossyConvertUTF16toASCII(aStorageName).get());
1095 //static
1096 already_AddRefed<DeviceStorageFile>
1097 DeviceStorageFile::CreateUnique(nsAString& aFileName,
1098 uint32_t aFileType,
1099 uint32_t aFileAttributes)
1101 DeviceStorageTypeChecker* typeChecker
1102 = DeviceStorageTypeChecker::CreateOrGet();
1103 MOZ_ASSERT(typeChecker);
1105 nsString storageType;
1106 typeChecker->GetTypeFromFileName(aFileName, storageType);
1108 nsString storageName;
1109 nsString storagePath;
1110 if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) {
1111 return nullptr;
1113 if (storageName.IsEmpty()) {
1114 nsDOMDeviceStorage::GetDefaultStorageName(storageType, storageName);
1116 nsRefPtr<DeviceStorageFile> dsf =
1117 new DeviceStorageFile(storageType, storageName, storagePath);
1118 if (!dsf->mFile) {
1119 return nullptr;
1122 nsresult rv = dsf->mFile->CreateUnique(aFileType, aFileAttributes);
1123 NS_ENSURE_SUCCESS(rv, nullptr);
1125 // CreateUnique may cause the filename to change. So we need to update mPath
1126 // to reflect that.
1127 nsString leafName;
1128 dsf->mFile->GetLeafName(leafName);
1130 int32_t lastSlashIndex = dsf->mPath.RFindChar('/');
1131 if (lastSlashIndex == kNotFound) {
1132 dsf->mPath.Assign(leafName);
1133 } else {
1134 // Include the last '/'
1135 dsf->mPath = Substring(dsf->mPath, 0, lastSlashIndex + 1);
1136 dsf->mPath.Append(leafName);
1139 return dsf.forget();
1142 void
1143 DeviceStorageFile::SetPath(const nsAString& aPath) {
1144 mPath.Assign(aPath);
1145 NormalizeFilePath();
1148 void
1149 DeviceStorageFile::SetEditable(bool aEditable) {
1150 mEditable = aEditable;
1153 // we want to make sure that the names of file can't reach
1154 // outside of the type of storage the user asked for.
1155 bool
1156 DeviceStorageFile::IsSafePath()
1158 return IsSafePath(mRootDir) && IsSafePath(mPath);
1161 bool
1162 DeviceStorageFile::IsSafePath(const nsAString& aPath)
1164 nsAString::const_iterator start, end;
1165 aPath.BeginReading(start);
1166 aPath.EndReading(end);
1168 // if the path is a '~' or starts with '~/', return false.
1169 NS_NAMED_LITERAL_STRING(tilde, "~");
1170 NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
1171 if (aPath.Equals(tilde) ||
1172 StringBeginsWith(aPath, tildeSlash)) {
1173 NS_WARNING("Path name starts with tilde!");
1174 return false;
1176 // split on /. if any token is "", ., or .., return false.
1177 NS_ConvertUTF16toUTF8 cname(aPath);
1178 char* buffer = cname.BeginWriting();
1179 const char* token;
1181 while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
1182 if (PL_strcmp(token, "") == 0 ||
1183 PL_strcmp(token, ".") == 0 ||
1184 PL_strcmp(token, "..") == 0 ) {
1185 return false;
1188 return true;
1191 void
1192 DeviceStorageFile::NormalizeFilePath() {
1193 FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
1196 void
1197 DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
1198 if (!mFile) {
1199 return;
1201 if (!IsSafePath(aPath)) {
1202 // All of the APIs (in the child) do checks to verify that the path is
1203 // valid and return PERMISSION_DENIED if a non-safe path is entered.
1204 // This check is done in the parent and prevents a compromised
1205 // child from bypassing the check. It shouldn't be possible for this
1206 // code path to be taken with a non-compromised child.
1207 NS_WARNING("Unsafe path detected - ignoring");
1208 NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
1209 return;
1211 nsString localPath;
1212 FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
1213 mFile->AppendRelativePath(localPath);
1216 nsresult
1217 DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
1219 if (!mFile) {
1220 return NS_ERROR_FAILURE;
1222 ScopedPRFileDesc fd;
1223 nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE,
1224 0660, &fd.rwget());
1225 NS_ENSURE_SUCCESS(rv, rv);
1227 // NOTE: The FileDescriptor::PlatformHandleType constructor returns a dup of
1228 // the file descriptor, so we don't need the original fd after this.
1229 // Our scoped file descriptor will automatically close fd.
1230 aFileDescriptor = FileDescriptor(
1231 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd)));
1232 return NS_OK;
1235 nsresult
1236 DeviceStorageFile::Write(nsIInputStream* aInputStream)
1238 if (!aInputStream || !mFile) {
1239 return NS_ERROR_FAILURE;
1242 nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
1243 if (NS_WARN_IF(NS_FAILED(rv))) {
1244 return rv;
1247 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
1248 rv = NS_DispatchToMainThread(iocomplete);
1249 if (NS_WARN_IF(NS_FAILED(rv))) {
1250 return rv;
1253 nsCOMPtr<nsIOutputStream> outputStream;
1254 NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
1256 if (!outputStream) {
1257 return NS_ERROR_FAILURE;
1260 return Append(aInputStream, outputStream);
1263 nsresult
1264 DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
1266 if (!mFile) {
1267 return NS_ERROR_FAILURE;
1270 nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
1271 if (NS_WARN_IF(NS_FAILED(rv))) {
1272 return rv;
1275 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
1276 rv = NS_DispatchToMainThread(iocomplete);
1277 if (NS_WARN_IF(NS_FAILED(rv))) {
1278 return rv;
1281 nsCOMPtr<nsIOutputStream> outputStream;
1282 NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
1284 if (!outputStream) {
1285 return NS_ERROR_FAILURE;
1288 uint32_t wrote;
1289 outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
1290 outputStream->Close();
1292 iocomplete = new IOEventComplete(this, "modified");
1293 rv = NS_DispatchToMainThread(iocomplete);
1294 if (NS_WARN_IF(NS_FAILED(rv))) {
1295 return rv;
1298 if (aBits.Length() != wrote) {
1299 return NS_ERROR_FAILURE;
1301 return NS_OK;
1304 nsresult
1305 DeviceStorageFile::Append(nsIInputStream* aInputStream)
1307 if (!aInputStream || !mFile) {
1308 return NS_ERROR_FAILURE;
1311 nsCOMPtr<nsIOutputStream> outputStream;
1312 NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile, PR_WRONLY | PR_CREATE_FILE | PR_APPEND, -1, 0);
1314 if (!outputStream) {
1315 return NS_ERROR_FAILURE;
1318 return Append(aInputStream, outputStream);
1322 nsresult
1323 DeviceStorageFile::Append(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
1325 uint64_t bufSize = 0;
1326 aInputStream->Available(&bufSize);
1328 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
1329 nsresult rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
1330 aOutputStream,
1331 4096*4);
1332 NS_ENSURE_SUCCESS(rv, rv);
1334 while (bufSize) {
1335 uint32_t wrote;
1336 rv = bufferedOutputStream->WriteFrom(
1337 aInputStream,
1338 static_cast<uint32_t>(std::min<uint64_t>(bufSize, UINT32_MAX)),
1339 &wrote);
1340 if (NS_FAILED(rv)) {
1341 break;
1343 bufSize -= wrote;
1346 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "modified");
1347 rv = NS_DispatchToMainThread(iocomplete);
1348 if (NS_WARN_IF(NS_FAILED(rv))) {
1349 return rv;
1352 bufferedOutputStream->Close();
1353 aOutputStream->Close();
1354 if (NS_WARN_IF(NS_FAILED(rv))) {
1355 return rv;
1357 return NS_OK;
1360 nsresult
1361 DeviceStorageFile::Remove()
1363 MOZ_ASSERT(!NS_IsMainThread());
1365 if (!mFile) {
1366 return NS_ERROR_FAILURE;
1369 bool check;
1370 nsresult rv = mFile->Exists(&check);
1371 if (NS_FAILED(rv)) {
1372 return rv;
1375 if (!check) {
1376 return NS_OK;
1379 rv = mFile->Remove(true);
1380 if (NS_WARN_IF(NS_FAILED(rv))) {
1381 return rv;
1384 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "deleted");
1385 return NS_DispatchToMainThread(iocomplete);
1388 nsresult
1389 DeviceStorageFile::CalculateMimeType()
1391 MOZ_ASSERT(NS_IsMainThread());
1393 if (!mFile) {
1394 return NS_ERROR_FAILURE;
1396 nsAutoCString mimeType;
1397 nsCOMPtr<nsIMIMEService> mimeService =
1398 do_GetService(NS_MIMESERVICE_CONTRACTID);
1399 if (mimeService) {
1400 nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
1401 if (NS_FAILED(rv)) {
1402 mimeType.Truncate();
1403 return rv;
1407 mMimeType = NS_ConvertUTF8toUTF16(mimeType);
1408 return NS_OK;
1411 nsresult
1412 DeviceStorageFile::CalculateSizeAndModifiedDate()
1414 MOZ_ASSERT(!NS_IsMainThread());
1416 if (!mFile) {
1417 return NS_ERROR_FAILURE;
1420 int64_t fileSize;
1421 nsresult rv = mFile->GetFileSize(&fileSize);
1422 NS_ENSURE_SUCCESS(rv, rv);
1424 mLength = fileSize;
1426 PRTime modDate;
1427 rv = mFile->GetLastModifiedTime(&modDate);
1428 NS_ENSURE_SUCCESS(rv, rv);
1430 mLastModifiedDate = modDate;
1431 return NS_OK;
1434 void
1435 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
1436 PRTime aSince)
1438 if (!mFile) {
1439 return;
1441 nsString fullRootPath;
1442 mFile->GetPath(fullRootPath);
1443 collectFilesInternal(aFiles, aSince, fullRootPath);
1446 void
1447 DeviceStorageFile::collectFilesInternal(
1448 nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
1449 PRTime aSince,
1450 nsAString& aRootPath)
1452 if (!mFile || !IsAvailable()) {
1453 return;
1456 nsCOMPtr<nsISimpleEnumerator> e;
1457 mFile->GetDirectoryEntries(getter_AddRefs(e));
1459 if (!e) {
1460 return;
1463 nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
1464 nsCOMPtr<nsIFile> f;
1466 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
1468 bool isFile;
1469 f->IsFile(&isFile);
1471 if (isFile) {
1472 PRTime msecs;
1473 f->GetLastModifiedTime(&msecs);
1475 if (msecs < aSince) {
1476 continue;
1480 bool isDir;
1481 f->IsDirectory(&isDir);
1483 nsString fullpath;
1484 nsresult rv = f->GetPath(fullpath);
1485 if (NS_FAILED(rv)) {
1486 continue;
1489 if (!StringBeginsWith(fullpath, aRootPath)) {
1490 NS_ERROR("collectFiles returned a path that does not belong!");
1491 continue;
1494 nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
1495 nsDependentSubstring newPath = Substring(fullpath, len);
1497 if (isDir) {
1498 DeviceStorageFile dsf(mStorageType, mStorageName, mRootDir, newPath);
1499 dsf.collectFilesInternal(aFiles, aSince, aRootPath);
1500 } else if (isFile) {
1501 nsRefPtr<DeviceStorageFile> dsf =
1502 new DeviceStorageFile(mStorageType, mStorageName, mRootDir, newPath);
1503 dsf->CalculateSizeAndModifiedDate();
1504 aFiles.AppendElement(dsf);
1509 void
1510 DeviceStorageFile::AccumDiskUsage(uint64_t* aPicturesSoFar,
1511 uint64_t* aVideosSoFar,
1512 uint64_t* aMusicSoFar,
1513 uint64_t* aTotalSoFar)
1515 if (!IsAvailable()) {
1516 return;
1519 uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0;
1521 if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
1522 DeviceStorageUsedSpaceCache* usedSpaceCache =
1523 DeviceStorageUsedSpaceCache::CreateOrGet();
1524 MOZ_ASSERT(usedSpaceCache);
1525 nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName,
1526 aPicturesSoFar, aVideosSoFar,
1527 aMusicSoFar, aTotalSoFar);
1528 if (NS_SUCCEEDED(rv)) {
1529 return;
1531 AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
1532 &musicUsage, &totalUsage);
1533 usedSpaceCache->SetUsedSizes(mStorageName, pictureUsage, videoUsage,
1534 musicUsage, totalUsage);
1535 } else {
1536 AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
1537 &musicUsage, &totalUsage);
1540 *aPicturesSoFar += pictureUsage;
1541 *aVideosSoFar += videoUsage;
1542 *aMusicSoFar += musicUsage;
1543 *aTotalSoFar += totalUsage;
1546 void
1547 DeviceStorageFile::AccumDirectoryUsage(nsIFile* aFile,
1548 uint64_t* aPicturesSoFar,
1549 uint64_t* aVideosSoFar,
1550 uint64_t* aMusicSoFar,
1551 uint64_t* aTotalSoFar)
1553 if (!aFile) {
1554 return;
1557 nsresult rv;
1558 nsCOMPtr<nsISimpleEnumerator> e;
1559 rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
1561 if (NS_FAILED(rv) || !e) {
1562 return;
1565 nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
1566 MOZ_ASSERT(files);
1568 nsCOMPtr<nsIFile> f;
1569 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
1570 bool isDir;
1571 rv = f->IsDirectory(&isDir);
1572 if (NS_FAILED(rv)) {
1573 continue;
1576 bool isFile;
1577 rv = f->IsFile(&isFile);
1578 if (NS_FAILED(rv)) {
1579 continue;
1582 bool isLink;
1583 rv = f->IsSymlink(&isLink);
1584 if (NS_FAILED(rv)) {
1585 continue;
1588 if (isLink) {
1589 // for now, lets just totally ignore symlinks.
1590 NS_WARNING("DirectoryDiskUsage ignores symlinks");
1591 } else if (isDir) {
1592 AccumDirectoryUsage(f, aPicturesSoFar, aVideosSoFar,
1593 aMusicSoFar, aTotalSoFar);
1594 } else if (isFile) {
1596 int64_t size;
1597 rv = f->GetFileSize(&size);
1598 if (NS_FAILED(rv)) {
1599 continue;
1601 DeviceStorageTypeChecker* typeChecker
1602 = DeviceStorageTypeChecker::CreateOrGet();
1603 MOZ_ASSERT(typeChecker);
1604 nsString type;
1605 typeChecker->GetTypeFromFile(f, type);
1607 if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
1608 *aPicturesSoFar += size;
1610 else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
1611 *aVideosSoFar += size;
1613 else if (type.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
1614 *aMusicSoFar += size;
1616 *aTotalSoFar += size;
1621 void
1622 DeviceStorageFile::GetDiskFreeSpace(int64_t* aSoFar)
1624 DeviceStorageTypeChecker* typeChecker
1625 = DeviceStorageTypeChecker::CreateOrGet();
1626 if (!typeChecker) {
1627 return;
1629 if (!mFile || !IsAvailable()) {
1630 return;
1633 int64_t storageAvail = 0;
1634 nsresult rv = mFile->GetDiskSpaceAvailable(&storageAvail);
1635 if (NS_SUCCEEDED(rv)) {
1636 *aSoFar += storageAvail;
1640 bool
1641 DeviceStorageFile::IsAvailable()
1643 nsString status;
1644 GetStatus(status);
1645 return status.EqualsLiteral("available");
1648 void
1649 DeviceStorageFile::DoFormat(nsAString& aStatus)
1651 DeviceStorageTypeChecker* typeChecker
1652 = DeviceStorageTypeChecker::CreateOrGet();
1653 if (!typeChecker || !mFile) {
1654 return;
1656 if (!typeChecker->IsVolumeBased(mStorageType)) {
1657 aStatus.AssignLiteral("notVolume");
1658 return;
1660 #ifdef MOZ_WIDGET_GONK
1661 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1662 NS_ENSURE_TRUE_VOID(vs);
1664 nsCOMPtr<nsIVolume> vol;
1665 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1666 NS_ENSURE_SUCCESS_VOID(rv);
1667 if (!vol) {
1668 return;
1671 vol->Format();
1673 aStatus.AssignLiteral("formatting");
1674 #endif
1675 return;
1678 void
1679 DeviceStorageFile::DoMount(nsAString& aStatus)
1681 DeviceStorageTypeChecker* typeChecker
1682 = DeviceStorageTypeChecker::CreateOrGet();
1683 if (!typeChecker || !mFile) {
1684 return;
1686 if (!typeChecker->IsVolumeBased(mStorageType)) {
1687 aStatus.AssignLiteral("notVolume");
1688 return;
1690 #ifdef MOZ_WIDGET_GONK
1691 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1692 NS_ENSURE_TRUE_VOID(vs);
1694 nsCOMPtr<nsIVolume> vol;
1695 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1696 NS_ENSURE_SUCCESS_VOID(rv);
1697 if (!vol) {
1698 return;
1701 vol->Mount();
1703 aStatus.AssignLiteral("mounting");
1704 #endif
1705 return;
1708 void
1709 DeviceStorageFile::DoUnmount(nsAString& aStatus)
1711 DeviceStorageTypeChecker* typeChecker
1712 = DeviceStorageTypeChecker::CreateOrGet();
1713 if (!typeChecker || !mFile) {
1714 return;
1716 if (!typeChecker->IsVolumeBased(mStorageType)) {
1717 aStatus.AssignLiteral("notVolume");
1718 return;
1720 #ifdef MOZ_WIDGET_GONK
1721 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1722 NS_ENSURE_TRUE_VOID(vs);
1724 nsCOMPtr<nsIVolume> vol;
1725 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1726 NS_ENSURE_SUCCESS_VOID(rv);
1727 if (!vol) {
1728 return;
1731 vol->Unmount();
1733 aStatus.AssignLiteral("unmounting");
1734 #endif
1735 return;
1738 void
1739 DeviceStorageFile::GetStatus(nsAString& aStatus)
1741 aStatus.AssignLiteral("unavailable");
1743 DeviceStorageTypeChecker* typeChecker
1744 = DeviceStorageTypeChecker::CreateOrGet();
1745 if (!typeChecker || !mFile) {
1746 return;
1748 if (!typeChecker->IsVolumeBased(mStorageType)) {
1749 aStatus.AssignLiteral("available");
1750 return;
1753 #ifdef MOZ_WIDGET_GONK
1754 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1755 NS_ENSURE_TRUE_VOID(vs);
1757 nsCOMPtr<nsIVolume> vol;
1758 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1759 NS_ENSURE_SUCCESS_VOID(rv);
1760 if (!vol) {
1761 return;
1763 bool isMediaPresent;
1764 rv = vol->GetIsMediaPresent(&isMediaPresent);
1765 NS_ENSURE_SUCCESS_VOID(rv);
1766 if (!isMediaPresent) {
1767 return;
1769 bool isSharing;
1770 rv = vol->GetIsSharing(&isSharing);
1771 NS_ENSURE_SUCCESS_VOID(rv);
1772 if (isSharing) {
1773 aStatus.AssignLiteral("shared");
1774 return;
1776 bool isFormatting;
1777 rv = vol->GetIsFormatting(&isFormatting);
1778 NS_ENSURE_SUCCESS_VOID(rv);
1779 if (isFormatting) {
1780 aStatus.AssignLiteral("unavailable");
1781 return;
1783 bool isUnmounting;
1784 rv = vol->GetIsUnmounting(&isUnmounting);
1785 NS_ENSURE_SUCCESS_VOID(rv);
1786 if (isUnmounting) {
1787 aStatus.AssignLiteral("unavailable");
1788 return;
1790 int32_t volState;
1791 rv = vol->GetState(&volState);
1792 NS_ENSURE_SUCCESS_VOID(rv);
1793 if (volState == nsIVolume::STATE_MOUNTED) {
1794 aStatus.AssignLiteral("available");
1796 #endif
1799 void
1800 DeviceStorageFile::GetStorageStatus(nsAString& aStatus)
1802 aStatus.AssignLiteral("undefined");
1804 DeviceStorageTypeChecker* typeChecker
1805 = DeviceStorageTypeChecker::CreateOrGet();
1806 if (!typeChecker || !mFile) {
1807 return;
1809 if (!typeChecker->IsVolumeBased(mStorageType)) {
1810 aStatus.AssignLiteral("available");
1811 return;
1814 #ifdef MOZ_WIDGET_GONK
1815 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1816 NS_ENSURE_TRUE_VOID(vs);
1818 nsCOMPtr<nsIVolume> vol;
1819 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1820 NS_ENSURE_SUCCESS_VOID(rv);
1821 if (!vol) {
1822 return;
1825 int32_t volState;
1826 rv = vol->GetState(&volState);
1827 NS_ENSURE_SUCCESS_VOID(rv);
1828 aStatus.AssignASCII(mozilla::system::NS_VolumeStateStr(volState));
1829 #endif
1832 NS_IMPL_ISUPPORTS0(DeviceStorageFile)
1834 static void
1835 RegisterForSDCardChanges(nsIObserver* aObserver)
1837 #ifdef MOZ_WIDGET_GONK
1838 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1839 obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
1840 #endif
1843 static void
1844 UnregisterForSDCardChanges(nsIObserver* aObserver)
1846 #ifdef MOZ_WIDGET_GONK
1847 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1848 obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
1849 #endif
1852 void
1853 nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
1854 const nsAString& aStorageName)
1856 MOZ_ASSERT(NS_IsMainThread());
1858 nsCOMPtr<nsIFile> f;
1859 DeviceStorageFile::GetRootDirectoryForType(aStorageType,
1860 aStorageName,
1861 getter_AddRefs(f));
1862 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1863 obs->AddObserver(this, kFileWatcherUpdate, false);
1864 obs->AddObserver(this, "disk-space-watcher", false);
1865 mRootDirectory = f;
1866 mStorageType = aStorageType;
1867 mStorageName = aStorageName;
1870 JS::Value
1871 InterfaceToJsval(nsPIDOMWindow* aWindow,
1872 nsISupports* aObject,
1873 const nsIID* aIID)
1875 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
1876 if (!sgo) {
1877 return JS::NullValue();
1880 JSObject *unrootedScopeObj = sgo->GetGlobalJSObject();
1881 NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue());
1882 JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj);
1883 JS::Rooted<JS::Value> someJsVal(runtime);
1884 JS::Rooted<JSObject*> scopeObj(runtime, unrootedScopeObj);
1885 nsresult rv;
1887 { // Protect someJsVal from moving GC in ~JSAutoCompartment
1888 AutoJSContext cx;
1889 JSAutoCompartment ac(cx, scopeObj);
1891 rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
1893 if (NS_FAILED(rv)) {
1894 return JS::NullValue();
1897 return someJsVal;
1900 JS::Value
1901 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
1903 MOZ_ASSERT(NS_IsMainThread());
1904 MOZ_ASSERT(aWindow);
1906 if (!aFile) {
1907 return JSVAL_NULL;
1910 if (aFile->mEditable) {
1911 // TODO - needs janv's file handle support.
1912 return JSVAL_NULL;
1915 nsString fullPath;
1916 aFile->GetFullPath(fullPath);
1918 // This check is useful to know if somewhere the DeviceStorageFile
1919 // has not been properly set. Mimetype is not checked because it can be
1920 // empty.
1921 MOZ_ASSERT(aFile->mLength != UINT64_MAX);
1922 MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX);
1924 nsCOMPtr<nsIDOMBlob> blob = new File(aWindow,
1925 new FileImplFile(fullPath, aFile->mMimeType,
1926 aFile->mLength, aFile->mFile,
1927 aFile->mLastModifiedDate));
1928 return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
1931 bool
1932 StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString,
1933 JS::MutableHandle<JS::Value> result)
1935 MOZ_ASSERT(NS_IsMainThread());
1936 MOZ_ASSERT(aWindow);
1938 AutoJSAPI jsapi;
1939 if (NS_WARN_IF(!jsapi.Init(aWindow))) {
1940 return false;
1942 JSContext* cx = jsapi.cx();
1944 if (!xpc::StringToJsval(cx, aString, result)) {
1945 return false;
1948 return true;
1951 class DeviceStorageCursorRequest MOZ_FINAL
1952 : public nsIContentPermissionRequest
1954 public:
1955 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1956 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest,
1957 nsIContentPermissionRequest)
1959 NS_FORWARD_NSICONTENTPERMISSIONREQUEST(mCursor->);
1961 explicit DeviceStorageCursorRequest(nsDOMDeviceStorageCursor* aCursor)
1962 : mCursor(aCursor) { }
1964 private:
1965 ~DeviceStorageCursorRequest() {}
1967 nsRefPtr<nsDOMDeviceStorageCursor> mCursor;
1970 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest)
1971 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
1972 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
1973 NS_INTERFACE_MAP_END
1975 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest)
1976 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest)
1978 NS_IMPL_CYCLE_COLLECTION(DeviceStorageCursorRequest,
1979 mCursor)
1982 class PostErrorEvent : public nsRunnable
1984 public:
1985 PostErrorEvent(already_AddRefed<DOMRequest> aRequest, const char* aMessage)
1986 : mRequest(aRequest)
1988 CopyASCIItoUTF16(aMessage, mError);
1991 PostErrorEvent(DOMRequest* aRequest, const char* aMessage)
1992 : mRequest(aRequest)
1994 CopyASCIItoUTF16(aMessage, mError);
1997 ~PostErrorEvent() {}
1999 NS_IMETHOD Run()
2001 MOZ_ASSERT(NS_IsMainThread());
2002 if (!mRequest->GetOwner()) {
2003 return NS_OK;
2005 mRequest->FireError(mError);
2006 mRequest = nullptr;
2007 return NS_OK;
2010 private:
2011 nsRefPtr<DOMRequest> mRequest;
2012 nsString mError;
2015 ContinueCursorEvent::ContinueCursorEvent(already_AddRefed<DOMRequest> aRequest)
2016 : mRequest(aRequest)
2020 ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest)
2021 : mRequest(aRequest)
2025 already_AddRefed<DeviceStorageFile>
2026 ContinueCursorEvent::GetNextFile()
2028 MOZ_ASSERT(NS_IsMainThread());
2030 nsDOMDeviceStorageCursor* cursor
2031 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
2032 nsString cursorStorageType;
2033 cursor->GetStorageType(cursorStorageType);
2035 DeviceStorageTypeChecker* typeChecker
2036 = DeviceStorageTypeChecker::CreateOrGet();
2037 if (!typeChecker) {
2038 return nullptr;
2041 while (cursor->mFiles.Length() > 0) {
2042 nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
2043 cursor->mFiles.RemoveElementAt(0);
2044 if (!typeChecker->Check(cursorStorageType, file->mFile)) {
2045 continue;
2048 file->CalculateMimeType();
2049 return file.forget();
2052 return nullptr;
2055 ContinueCursorEvent::~ContinueCursorEvent() {}
2057 void
2058 ContinueCursorEvent::Continue()
2060 if (XRE_GetProcessType() == GeckoProcessType_Default) {
2061 DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
2062 MOZ_ASSERT(NS_SUCCEEDED(rv));
2063 return;
2066 nsRefPtr<DeviceStorageFile> file = GetNextFile();
2068 if (!file) {
2069 // done with enumeration.
2070 DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
2071 MOZ_ASSERT(NS_SUCCEEDED(rv));
2072 return;
2075 nsDOMDeviceStorageCursor* cursor
2076 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
2077 nsString cursorStorageType;
2078 cursor->GetStorageType(cursorStorageType);
2080 DeviceStorageRequestChild* child
2081 = new DeviceStorageRequestChild(mRequest, file);
2082 child->SetCallback(cursor);
2083 DeviceStorageGetParams params(cursorStorageType,
2084 file->mStorageName,
2085 file->mRootDir,
2086 file->mPath);
2087 ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
2088 params);
2089 mRequest = nullptr;
2092 NS_IMETHODIMP
2093 ContinueCursorEvent::Run()
2095 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2096 if (!window) {
2097 return NS_OK;
2100 nsRefPtr<DeviceStorageFile> file = GetNextFile();
2102 nsDOMDeviceStorageCursor* cursor
2103 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
2105 AutoJSContext cx;
2106 JS::Rooted<JS::Value> val(cx, nsIFileToJsval(window, file));
2108 if (file) {
2109 cursor->mOkToCallContinue = true;
2110 cursor->FireSuccess(val);
2111 } else {
2112 cursor->FireDone();
2114 mRequest = nullptr;
2115 return NS_OK;
2118 class InitCursorEvent : public nsRunnable
2120 public:
2121 InitCursorEvent(DOMRequest* aRequest, DeviceStorageFile* aFile)
2122 : mFile(aFile)
2123 , mRequest(aRequest)
2127 ~InitCursorEvent() {}
2129 NS_IMETHOD Run() {
2130 MOZ_ASSERT(!NS_IsMainThread());
2132 if (mFile->mFile) {
2133 bool check;
2134 mFile->mFile->IsDirectory(&check);
2135 if (!check) {
2136 nsCOMPtr<nsIRunnable> event =
2137 new PostErrorEvent(mRequest.forget(),
2138 POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
2139 return NS_DispatchToMainThread(event);
2143 nsDOMDeviceStorageCursor* cursor
2144 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
2145 mFile->CollectFiles(cursor->mFiles, cursor->mSince);
2147 nsRefPtr<ContinueCursorEvent> event
2148 = new ContinueCursorEvent(mRequest.forget());
2149 event->Continue();
2151 return NS_OK;
2155 private:
2156 nsRefPtr<DeviceStorageFile> mFile;
2157 nsRefPtr<DOMRequest> mRequest;
2160 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorageCursor)
2161 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
2162 NS_INTERFACE_MAP_END_INHERITING(DOMCursor)
2164 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
2165 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
2167 nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsPIDOMWindow* aWindow,
2168 nsIPrincipal* aPrincipal,
2169 DeviceStorageFile* aFile,
2170 PRTime aSince)
2171 : DOMCursor(aWindow, nullptr)
2172 , mOkToCallContinue(false)
2173 , mSince(aSince)
2174 , mFile(aFile)
2175 , mPrincipal(aPrincipal)
2179 nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor()
2183 void
2184 nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
2186 aType = mFile->mStorageType;
2189 NS_IMETHODIMP
2190 nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
2192 nsCString type;
2193 nsresult rv =
2194 DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
2195 NS_ENSURE_SUCCESS(rv, rv);
2197 nsTArray<nsString> emptyOptions;
2198 return nsContentPermissionUtils::CreatePermissionArray(type,
2199 NS_LITERAL_CSTRING("read"),
2200 emptyOptions,
2201 aTypes);
2204 NS_IMETHODIMP
2205 nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
2207 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
2208 return NS_OK;
2211 NS_IMETHODIMP
2212 nsDOMDeviceStorageCursor::GetWindow(nsIDOMWindow** aRequestingWindow)
2214 NS_IF_ADDREF(*aRequestingWindow = GetOwner());
2215 return NS_OK;
2218 NS_IMETHODIMP
2219 nsDOMDeviceStorageCursor::GetElement(nsIDOMElement** aRequestingElement)
2221 *aRequestingElement = nullptr;
2222 return NS_OK;
2225 NS_IMETHODIMP
2226 nsDOMDeviceStorageCursor::Cancel()
2228 nsCOMPtr<nsIRunnable> event
2229 = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
2230 return NS_DispatchToMainThread(event);
2233 NS_IMETHODIMP
2234 nsDOMDeviceStorageCursor::Allow(JS::HandleValue aChoices)
2236 MOZ_ASSERT(aChoices.isUndefined());
2238 if (!mFile->IsSafePath()) {
2239 nsCOMPtr<nsIRunnable> r
2240 = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
2241 return NS_DispatchToMainThread(r);
2244 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2245 PDeviceStorageRequestChild* child
2246 = new DeviceStorageRequestChild(this, mFile);
2247 DeviceStorageEnumerationParams params(mFile->mStorageType,
2248 mFile->mStorageName,
2249 mFile->mRootDir,
2250 mSince);
2251 ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
2252 params);
2253 return NS_OK;
2256 nsCOMPtr<nsIEventTarget> target
2257 = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
2258 MOZ_ASSERT(target);
2260 nsCOMPtr<nsIRunnable> event = new InitCursorEvent(this, mFile);
2261 target->Dispatch(event, NS_DISPATCH_NORMAL);
2262 return NS_OK;
2265 void
2266 nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
2268 if (!mOkToCallContinue) {
2269 aRv.Throw(NS_ERROR_UNEXPECTED);
2270 return;
2273 if (mResult != JSVAL_VOID) {
2274 // We call onsuccess multiple times. Clear the last
2275 // result.
2276 mResult = JSVAL_VOID;
2277 mDone = false;
2280 nsRefPtr<ContinueCursorEvent> event = new ContinueCursorEvent(this);
2281 event->Continue();
2283 mOkToCallContinue = false;
2286 void
2287 nsDOMDeviceStorageCursor::RequestComplete()
2289 MOZ_ASSERT(!mOkToCallContinue);
2290 mOkToCallContinue = true;
2293 class PostAvailableResultEvent : public nsRunnable
2295 public:
2296 PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2297 : mFile(aFile)
2298 , mRequest(aRequest)
2300 MOZ_ASSERT(mRequest);
2303 ~PostAvailableResultEvent() {}
2305 NS_IMETHOD Run()
2307 MOZ_ASSERT(NS_IsMainThread());
2308 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2309 if (!window) {
2310 return NS_OK;
2313 nsString state = NS_LITERAL_STRING("unavailable");
2314 if (mFile) {
2315 mFile->GetStatus(state);
2318 AutoJSContext cx;
2319 JS::Rooted<JS::Value> result(cx);
2320 StringToJsval(window, state, &result);
2321 mRequest->FireSuccess(result);
2322 mRequest = nullptr;
2323 return NS_OK;
2326 private:
2327 nsRefPtr<DeviceStorageFile> mFile;
2328 nsRefPtr<DOMRequest> mRequest;
2331 class PostStatusResultEvent : public nsRunnable
2333 public:
2334 PostStatusResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2335 : mFile(aFile)
2336 , mRequest(aRequest)
2338 MOZ_ASSERT(mRequest);
2341 ~PostStatusResultEvent() {}
2343 NS_IMETHOD Run()
2345 MOZ_ASSERT(NS_IsMainThread());
2346 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2347 if (!window) {
2348 return NS_OK;
2351 nsString state = NS_LITERAL_STRING("undefined");
2352 if (mFile) {
2353 mFile->GetStorageStatus(state);
2356 AutoJSContext cx;
2357 JS::Rooted<JS::Value> result(cx);
2358 StringToJsval(window, state, &result);
2359 mRequest->FireSuccess(result);
2360 mRequest = nullptr;
2361 return NS_OK;
2364 private:
2365 nsRefPtr<DeviceStorageFile> mFile;
2366 nsRefPtr<DOMRequest> mRequest;
2369 class PostFormatResultEvent : public nsRunnable
2371 public:
2372 PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2373 : mFile(aFile)
2374 , mRequest(aRequest)
2376 MOZ_ASSERT(mRequest);
2379 ~PostFormatResultEvent() {}
2381 NS_IMETHOD Run()
2383 MOZ_ASSERT(NS_IsMainThread());
2384 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2385 if (!window) {
2386 return NS_OK;
2389 nsString state = NS_LITERAL_STRING("unavailable");
2390 if (mFile) {
2391 mFile->DoFormat(state);
2394 AutoJSContext cx;
2395 JS::Rooted<JS::Value> result(cx);
2396 StringToJsval(window, state, &result);
2397 mRequest->FireSuccess(result);
2398 mRequest = nullptr;
2399 return NS_OK;
2402 private:
2403 nsRefPtr<DeviceStorageFile> mFile;
2404 nsRefPtr<DOMRequest> mRequest;
2407 class PostMountResultEvent : public nsRunnable
2409 public:
2410 PostMountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2411 : mFile(aFile)
2412 , mRequest(aRequest)
2414 MOZ_ASSERT(mRequest);
2417 ~PostMountResultEvent() {}
2419 NS_IMETHOD Run()
2421 MOZ_ASSERT(NS_IsMainThread());
2422 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2423 if (!window) {
2424 return NS_OK;
2427 nsString state = NS_LITERAL_STRING("unavailable");
2428 if (mFile) {
2429 mFile->DoMount(state);
2432 AutoJSContext cx;
2433 JS::Rooted<JS::Value> result(cx);
2434 StringToJsval(window, state, &result);
2435 mRequest->FireSuccess(result);
2436 mRequest = nullptr;
2437 return NS_OK;
2440 private:
2441 nsRefPtr<DeviceStorageFile> mFile;
2442 nsRefPtr<DOMRequest> mRequest;
2445 class PostUnmountResultEvent : public nsRunnable
2447 public:
2448 PostUnmountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2449 : mFile(aFile)
2450 , mRequest(aRequest)
2452 MOZ_ASSERT(mRequest);
2455 ~PostUnmountResultEvent() {}
2457 NS_IMETHOD Run()
2459 MOZ_ASSERT(NS_IsMainThread());
2460 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2461 if (!window) {
2462 return NS_OK;
2465 nsString state = NS_LITERAL_STRING("unavailable");
2466 if (mFile) {
2467 mFile->DoUnmount(state);
2470 AutoJSContext cx;
2471 JS::Rooted<JS::Value> result(cx);
2472 StringToJsval(window, state, &result);
2473 mRequest->FireSuccess(result);
2474 mRequest = nullptr;
2475 return NS_OK;
2478 private:
2479 nsRefPtr<DeviceStorageFile> mFile;
2480 nsRefPtr<DOMRequest> mRequest;
2483 class PostResultEvent : public nsRunnable
2485 public:
2486 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2487 DeviceStorageFile* aFile)
2488 : mFile(aFile)
2489 , mRequest(aRequest)
2491 MOZ_ASSERT(mRequest);
2494 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2495 const nsAString & aPath)
2496 : mPath(aPath)
2497 , mRequest(aRequest)
2499 MOZ_ASSERT(mRequest);
2502 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2503 const uint64_t aValue)
2504 : mValue(aValue)
2505 , mRequest(aRequest)
2507 MOZ_ASSERT(mRequest);
2510 ~PostResultEvent() {}
2512 NS_IMETHOD Run()
2514 MOZ_ASSERT(NS_IsMainThread());
2515 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2516 if (!window) {
2517 return NS_OK;
2520 AutoJSContext cx;
2521 JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
2523 if (mFile) {
2524 result = nsIFileToJsval(window, mFile);
2525 } else if (mPath.Length()) {
2526 StringToJsval(window, mPath, &result);
2528 else {
2529 result = JS_NumberValue(double(mValue));
2532 mRequest->FireSuccess(result);
2533 mRequest = nullptr;
2534 return NS_OK;
2537 private:
2538 nsRefPtr<DeviceStorageFile> mFile;
2539 nsString mPath;
2540 uint64_t mValue;
2541 nsRefPtr<DOMRequest> mRequest;
2544 class CreateFdEvent : public nsRunnable
2546 public:
2547 CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor,
2548 already_AddRefed<DOMRequest> aRequest)
2549 : mDSFileDescriptor(aDSFileDescriptor)
2550 , mRequest(aRequest)
2552 MOZ_ASSERT(mDSFileDescriptor);
2553 MOZ_ASSERT(mDSFileDescriptor->mDSFile);
2554 MOZ_ASSERT(mDSFileDescriptor->mDSFile->mFile);
2555 MOZ_ASSERT(mRequest);
2558 NS_IMETHOD Run()
2560 MOZ_ASSERT(!NS_IsMainThread());
2562 DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile;
2564 nsString fullPath;
2565 dsFile->GetFullPath(fullPath);
2566 MOZ_ASSERT(!fullPath.IsEmpty());
2568 bool check = false;
2569 dsFile->mFile->Exists(&check);
2570 if (check) {
2571 nsCOMPtr<nsIRunnable> event =
2572 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
2573 return NS_DispatchToMainThread(event);
2576 nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor);
2578 if (NS_FAILED(rv)) {
2579 dsFile->mFile->Remove(false);
2581 nsCOMPtr<nsIRunnable> event =
2582 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2583 return NS_DispatchToMainThread(event);
2586 nsCOMPtr<nsIRunnable> event =
2587 new PostResultEvent(mRequest.forget(), fullPath);
2588 return NS_DispatchToMainThread(event);
2591 private:
2592 nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
2593 nsRefPtr<DOMRequest> mRequest;
2596 class WriteFileEvent : public nsRunnable
2598 public:
2599 WriteFileEvent(FileImpl* aBlobImpl,
2600 DeviceStorageFile *aFile,
2601 already_AddRefed<DOMRequest> aRequest,
2602 int32_t aRequestType)
2603 : mBlobImpl(aBlobImpl)
2604 , mFile(aFile)
2605 , mRequest(aRequest)
2606 , mRequestType(aRequestType)
2608 MOZ_ASSERT(mFile);
2609 MOZ_ASSERT(mFile->mFile);
2610 MOZ_ASSERT(mRequest);
2611 MOZ_ASSERT(mRequestType);
2614 ~WriteFileEvent() {}
2616 NS_IMETHOD Run()
2618 MOZ_ASSERT(!NS_IsMainThread());
2620 nsCOMPtr<nsIInputStream> stream;
2621 mBlobImpl->GetInternalStream(getter_AddRefs(stream));
2623 bool check = false;
2624 mFile->mFile->Exists(&check);
2625 nsresult rv;
2627 if (mRequestType == DEVICE_STORAGE_REQUEST_APPEND) {
2628 if (!check) {
2629 nsCOMPtr<nsIRunnable> event =
2630 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
2631 return NS_DispatchToMainThread(event);
2633 rv = mFile->Append(stream);
2635 else if (mRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
2636 if (check) {
2637 nsCOMPtr<nsIRunnable> event =
2638 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
2639 return NS_DispatchToMainThread(event);
2641 rv = mFile->Write(stream);
2642 if (NS_FAILED(rv)) {
2643 mFile->mFile->Remove(false);
2645 } else {
2646 nsCOMPtr<nsIRunnable> event =
2647 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2648 return NS_DispatchToMainThread(event);
2651 if (NS_FAILED(rv)) {
2652 nsCOMPtr<nsIRunnable> event =
2653 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2654 return NS_DispatchToMainThread(event);
2657 nsString fullPath;
2658 mFile->GetFullPath(fullPath);
2659 nsCOMPtr<nsIRunnable> event =
2660 new PostResultEvent(mRequest.forget(), fullPath);
2661 return NS_DispatchToMainThread(event);
2664 private:
2665 nsRefPtr<FileImpl> mBlobImpl;
2666 nsRefPtr<DeviceStorageFile> mFile;
2667 nsRefPtr<DOMRequest> mRequest;
2668 int32_t mRequestType;
2672 class ReadFileEvent : public nsRunnable
2674 public:
2675 ReadFileEvent(DeviceStorageFile* aFile,
2676 already_AddRefed<DOMRequest> aRequest)
2677 : mFile(aFile)
2678 , mRequest(aRequest)
2680 MOZ_ASSERT(mFile);
2681 MOZ_ASSERT(mRequest);
2682 mFile->CalculateMimeType();
2685 ~ReadFileEvent() {}
2687 NS_IMETHOD Run()
2689 MOZ_ASSERT(!NS_IsMainThread());
2691 nsCOMPtr<nsIRunnable> r;
2692 if (!mFile->mEditable) {
2693 bool check = false;
2694 mFile->mFile->Exists(&check);
2695 if (!check) {
2696 r = new PostErrorEvent(mRequest.forget(),
2697 POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
2701 if (!r) {
2702 nsresult rv = mFile->CalculateSizeAndModifiedDate();
2703 if (NS_FAILED(rv)) {
2704 r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2708 if (!r) {
2709 r = new PostResultEvent(mRequest.forget(), mFile);
2711 return NS_DispatchToMainThread(r);
2714 private:
2715 nsRefPtr<DeviceStorageFile> mFile;
2716 nsRefPtr<DOMRequest> mRequest;
2719 class DeleteFileEvent : public nsRunnable
2721 public:
2722 DeleteFileEvent(DeviceStorageFile* aFile,
2723 already_AddRefed<DOMRequest> aRequest)
2724 : mFile(aFile)
2725 , mRequest(aRequest)
2727 MOZ_ASSERT(mFile);
2728 MOZ_ASSERT(mRequest);
2731 ~DeleteFileEvent() {}
2733 NS_IMETHOD Run()
2735 MOZ_ASSERT(!NS_IsMainThread());
2736 mFile->Remove();
2738 nsCOMPtr<nsIRunnable> r;
2739 bool check = false;
2740 mFile->mFile->Exists(&check);
2741 if (check) {
2742 r = new PostErrorEvent(mRequest.forget(),
2743 POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
2745 else {
2746 nsString fullPath;
2747 mFile->GetFullPath(fullPath);
2748 r = new PostResultEvent(mRequest.forget(), fullPath);
2750 return NS_DispatchToMainThread(r);
2753 private:
2754 nsRefPtr<DeviceStorageFile> mFile;
2755 nsRefPtr<DOMRequest> mRequest;
2758 class UsedSpaceFileEvent : public nsRunnable
2760 public:
2761 UsedSpaceFileEvent(DeviceStorageFile* aFile,
2762 already_AddRefed<DOMRequest> aRequest)
2763 : mFile(aFile)
2764 , mRequest(aRequest)
2766 MOZ_ASSERT(mFile);
2767 MOZ_ASSERT(mRequest);
2770 ~UsedSpaceFileEvent() {}
2772 NS_IMETHOD Run()
2774 MOZ_ASSERT(!NS_IsMainThread());
2776 uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
2777 mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
2778 &musicUsage, &totalUsage);
2779 nsCOMPtr<nsIRunnable> r;
2780 if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
2781 r = new PostResultEvent(mRequest.forget(), picturesUsage);
2783 else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
2784 r = new PostResultEvent(mRequest.forget(), videosUsage);
2786 else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
2787 r = new PostResultEvent(mRequest.forget(), musicUsage);
2788 } else {
2789 r = new PostResultEvent(mRequest.forget(), totalUsage);
2791 return NS_DispatchToMainThread(r);
2794 private:
2795 nsRefPtr<DeviceStorageFile> mFile;
2796 nsRefPtr<DOMRequest> mRequest;
2799 class FreeSpaceFileEvent : public nsRunnable
2801 public:
2802 FreeSpaceFileEvent(DeviceStorageFile* aFile,
2803 already_AddRefed<DOMRequest> aRequest)
2804 : mFile(aFile)
2805 , mRequest(aRequest)
2807 MOZ_ASSERT(mFile);
2808 MOZ_ASSERT(mRequest);
2811 ~FreeSpaceFileEvent() {}
2813 NS_IMETHOD Run()
2815 MOZ_ASSERT(!NS_IsMainThread());
2817 int64_t freeSpace = 0;
2818 if (mFile) {
2819 mFile->GetDiskFreeSpace(&freeSpace);
2822 nsCOMPtr<nsIRunnable> r;
2823 r = new PostResultEvent(mRequest.forget(),
2824 static_cast<uint64_t>(freeSpace));
2825 return NS_DispatchToMainThread(r);
2828 private:
2829 nsRefPtr<DeviceStorageFile> mFile;
2830 nsRefPtr<DOMRequest> mRequest;
2833 class DeviceStorageRequest MOZ_FINAL
2834 : public nsIContentPermissionRequest
2835 , public nsIRunnable
2837 public:
2839 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2840 nsPIDOMWindow* aWindow,
2841 nsIPrincipal* aPrincipal,
2842 DeviceStorageFile* aFile,
2843 DOMRequest* aRequest,
2844 nsDOMDeviceStorage* aDeviceStorage)
2845 : mRequestType(aRequestType)
2846 , mWindow(aWindow)
2847 , mPrincipal(aPrincipal)
2848 , mFile(aFile)
2849 , mRequest(aRequest)
2850 , mDeviceStorage(aDeviceStorage)
2852 MOZ_ASSERT(mWindow);
2853 MOZ_ASSERT(mPrincipal);
2854 MOZ_ASSERT(mFile);
2855 MOZ_ASSERT(mRequest);
2856 MOZ_ASSERT(mDeviceStorage);
2859 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2860 nsPIDOMWindow* aWindow,
2861 nsIPrincipal* aPrincipal,
2862 DeviceStorageFile* aFile,
2863 DOMRequest* aRequest,
2864 nsIDOMBlob* aBlob = nullptr)
2865 : mRequestType(aRequestType)
2866 , mWindow(aWindow)
2867 , mPrincipal(aPrincipal)
2868 , mFile(aFile)
2869 , mRequest(aRequest)
2870 , mBlob(aBlob)
2872 MOZ_ASSERT(mWindow);
2873 MOZ_ASSERT(mPrincipal);
2874 MOZ_ASSERT(mFile);
2875 MOZ_ASSERT(mRequest);
2878 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2879 nsPIDOMWindow* aWindow,
2880 nsIPrincipal* aPrincipal,
2881 DeviceStorageFile* aFile,
2882 DOMRequest* aRequest,
2883 DeviceStorageFileDescriptor* aDSFileDescriptor)
2884 : mRequestType(aRequestType)
2885 , mWindow(aWindow)
2886 , mPrincipal(aPrincipal)
2887 , mFile(aFile)
2888 , mRequest(aRequest)
2889 , mDSFileDescriptor(aDSFileDescriptor)
2891 MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD);
2892 MOZ_ASSERT(mWindow);
2893 MOZ_ASSERT(mPrincipal);
2894 MOZ_ASSERT(mFile);
2895 MOZ_ASSERT(mRequest);
2896 MOZ_ASSERT(mDSFileDescriptor);
2899 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2900 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
2901 nsIContentPermissionRequest)
2903 NS_IMETHOD Run() MOZ_OVERRIDE
2905 MOZ_ASSERT(NS_IsMainThread());
2907 if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
2908 Allow(JS::UndefinedHandleValue);
2909 return NS_OK;
2912 return nsContentPermissionUtils::AskPermission(this, mWindow);
2915 NS_IMETHODIMP GetTypes(nsIArray** aTypes) MOZ_OVERRIDE
2917 nsCString type;
2918 nsresult rv =
2919 DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
2920 if (NS_FAILED(rv)) {
2921 return rv;
2924 nsCString access;
2925 rv = DeviceStorageTypeChecker::GetAccessForRequest(
2926 DeviceStorageRequestType(mRequestType), access);
2927 if (NS_FAILED(rv)) {
2928 return rv;
2931 nsTArray<nsString> emptyOptions;
2932 return nsContentPermissionUtils::CreatePermissionArray(type, access, emptyOptions, aTypes);
2935 NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal) MOZ_OVERRIDE
2937 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
2938 return NS_OK;
2941 NS_IMETHOD GetWindow(nsIDOMWindow * *aRequestingWindow) MOZ_OVERRIDE
2943 NS_IF_ADDREF(*aRequestingWindow = mWindow);
2944 return NS_OK;
2947 NS_IMETHOD GetElement(nsIDOMElement * *aRequestingElement) MOZ_OVERRIDE
2949 *aRequestingElement = nullptr;
2950 return NS_OK;
2953 NS_IMETHOD Cancel() MOZ_OVERRIDE
2955 nsCOMPtr<nsIRunnable> event
2956 = new PostErrorEvent(mRequest.forget(),
2957 POST_ERROR_EVENT_PERMISSION_DENIED);
2958 return NS_DispatchToMainThread(event);
2961 NS_IMETHOD Allow(JS::HandleValue aChoices) MOZ_OVERRIDE
2963 MOZ_ASSERT(NS_IsMainThread());
2964 MOZ_ASSERT(aChoices.isUndefined());
2966 if (!mRequest) {
2967 return NS_ERROR_FAILURE;
2970 nsCOMPtr<nsIRunnable> r;
2972 switch(mRequestType) {
2973 case DEVICE_STORAGE_REQUEST_CREATEFD:
2975 if (!mFile->mFile) {
2976 return NS_ERROR_FAILURE;
2979 DeviceStorageTypeChecker* typeChecker
2980 = DeviceStorageTypeChecker::CreateOrGet();
2981 if (!typeChecker) {
2982 return NS_OK;
2985 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
2986 r = new PostErrorEvent(mRequest.forget(),
2987 POST_ERROR_EVENT_ILLEGAL_TYPE);
2988 return NS_DispatchToCurrentThread(r);
2991 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2993 DeviceStorageCreateFdParams params;
2994 params.type() = mFile->mStorageType;
2995 params.storageName() = mFile->mStorageName;
2996 params.relpath() = mFile->mPath;
2998 mFile->Dump("DeviceStorageCreateFdParams");
3000 PDeviceStorageRequestChild* child
3001 = new DeviceStorageRequestChild(mRequest, mFile,
3002 mDSFileDescriptor.get());
3003 ContentChild::GetSingleton()
3004 ->SendPDeviceStorageRequestConstructor(child, params);
3005 return NS_OK;
3007 mDSFileDescriptor->mDSFile = mFile;
3008 r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget());
3009 break;
3012 case DEVICE_STORAGE_REQUEST_CREATE:
3014 if (!mBlob || !mFile->mFile) {
3015 return NS_ERROR_FAILURE;
3018 DeviceStorageTypeChecker* typeChecker
3019 = DeviceStorageTypeChecker::CreateOrGet();
3020 if (!typeChecker) {
3021 return NS_OK;
3024 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
3025 !typeChecker->Check(mFile->mStorageType, mBlob)) {
3026 r = new PostErrorEvent(mRequest.forget(),
3027 POST_ERROR_EVENT_ILLEGAL_TYPE);
3028 return NS_DispatchToCurrentThread(r);
3031 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3032 BlobChild* actor
3033 = ContentChild::GetSingleton()->GetOrCreateActorForBlob(
3034 static_cast<File*>(mBlob.get()));
3035 if (!actor) {
3036 return NS_ERROR_FAILURE;
3039 DeviceStorageAddParams params;
3040 params.blobChild() = actor;
3041 params.type() = mFile->mStorageType;
3042 params.storageName() = mFile->mStorageName;
3043 params.relpath() = mFile->mPath;
3045 PDeviceStorageRequestChild* child
3046 = new DeviceStorageRequestChild(mRequest, mFile);
3047 ContentChild::GetSingleton()
3048 ->SendPDeviceStorageRequestConstructor(child, params);
3049 return NS_OK;
3052 File* blob = static_cast<File*>(mBlob.get());
3053 r = new WriteFileEvent(blob->Impl(), mFile, mRequest.forget(),
3054 mRequestType);
3055 break;
3058 case DEVICE_STORAGE_REQUEST_APPEND:
3060 if (!mBlob || !mFile->mFile) {
3061 return NS_ERROR_FAILURE;
3064 DeviceStorageTypeChecker* typeChecker
3065 = DeviceStorageTypeChecker::CreateOrGet();
3066 if (!typeChecker) {
3067 return NS_OK;
3070 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
3071 !typeChecker->Check(mFile->mStorageType, mBlob)) {
3072 r = new PostErrorEvent(mRequest.forget(),
3073 POST_ERROR_EVENT_ILLEGAL_TYPE);
3074 return NS_DispatchToCurrentThread(r);
3077 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3078 BlobChild* actor
3079 = ContentChild::GetSingleton()->GetOrCreateActorForBlob(
3080 static_cast<File*>(mBlob.get()));
3081 if (!actor) {
3082 return NS_ERROR_FAILURE;
3085 DeviceStorageAppendParams params;
3086 params.blobChild() = actor;
3087 params.type() = mFile->mStorageType;
3088 params.storageName() = mFile->mStorageName;
3089 params.relpath() = mFile->mPath;
3091 PDeviceStorageRequestChild* child
3092 = new DeviceStorageRequestChild(mRequest, mFile);
3093 ContentChild::GetSingleton()
3094 ->SendPDeviceStorageRequestConstructor(child, params);
3095 return NS_OK;
3098 File* blob = static_cast<File*>(mBlob.get());
3099 r = new WriteFileEvent(blob->Impl(), mFile, mRequest.forget(),
3100 mRequestType);
3101 break;
3104 case DEVICE_STORAGE_REQUEST_READ:
3105 case DEVICE_STORAGE_REQUEST_WRITE:
3107 if (!mFile->mFile) {
3108 return NS_ERROR_FAILURE;
3111 DeviceStorageTypeChecker* typeChecker
3112 = DeviceStorageTypeChecker::CreateOrGet();
3113 if (!typeChecker) {
3114 return NS_OK;
3117 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
3118 r = new PostErrorEvent(mRequest.forget(),
3119 POST_ERROR_EVENT_ILLEGAL_TYPE);
3120 return NS_DispatchToCurrentThread(r);
3123 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3124 PDeviceStorageRequestChild* child
3125 = new DeviceStorageRequestChild(mRequest, mFile);
3126 DeviceStorageGetParams params(mFile->mStorageType,
3127 mFile->mStorageName,
3128 mFile->mRootDir,
3129 mFile->mPath);
3130 ContentChild::GetSingleton()
3131 ->SendPDeviceStorageRequestConstructor(child, params);
3132 return NS_OK;
3135 r = new ReadFileEvent(mFile, mRequest.forget());
3136 break;
3139 case DEVICE_STORAGE_REQUEST_DELETE:
3141 if (!mFile->mFile) {
3142 return NS_ERROR_FAILURE;
3145 DeviceStorageTypeChecker* typeChecker
3146 = DeviceStorageTypeChecker::CreateOrGet();
3147 if (!typeChecker) {
3148 return NS_OK;
3151 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
3152 r = new PostErrorEvent(mRequest.forget(),
3153 POST_ERROR_EVENT_ILLEGAL_TYPE);
3154 return NS_DispatchToCurrentThread(r);
3157 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3158 PDeviceStorageRequestChild* child
3159 = new DeviceStorageRequestChild(mRequest, mFile);
3160 DeviceStorageDeleteParams params(mFile->mStorageType,
3161 mFile->mStorageName,
3162 mFile->mPath);
3163 ContentChild::GetSingleton()
3164 ->SendPDeviceStorageRequestConstructor(child, params);
3165 return NS_OK;
3167 r = new DeleteFileEvent(mFile, mRequest.forget());
3168 break;
3171 case DEVICE_STORAGE_REQUEST_FREE_SPACE:
3173 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3174 PDeviceStorageRequestChild* child
3175 = new DeviceStorageRequestChild(mRequest, mFile);
3176 DeviceStorageFreeSpaceParams params(mFile->mStorageType,
3177 mFile->mStorageName);
3178 ContentChild::GetSingleton()
3179 ->SendPDeviceStorageRequestConstructor(child, params);
3180 return NS_OK;
3182 r = new FreeSpaceFileEvent(mFile, mRequest.forget());
3183 break;
3186 case DEVICE_STORAGE_REQUEST_USED_SPACE:
3188 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3189 PDeviceStorageRequestChild* child
3190 = new DeviceStorageRequestChild(mRequest, mFile);
3191 DeviceStorageUsedSpaceParams params(mFile->mStorageType,
3192 mFile->mStorageName);
3193 ContentChild::GetSingleton()
3194 ->SendPDeviceStorageRequestConstructor(child, params);
3195 return NS_OK;
3197 // this needs to be dispatched to only one (1)
3198 // thread or we will do more work than required.
3199 DeviceStorageUsedSpaceCache* usedSpaceCache
3200 = DeviceStorageUsedSpaceCache::CreateOrGet();
3201 MOZ_ASSERT(usedSpaceCache);
3202 r = new UsedSpaceFileEvent(mFile, mRequest.forget());
3203 usedSpaceCache->Dispatch(r);
3204 return NS_OK;
3207 case DEVICE_STORAGE_REQUEST_AVAILABLE:
3209 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3210 PDeviceStorageRequestChild* child
3211 = new DeviceStorageRequestChild(mRequest, mFile);
3212 DeviceStorageAvailableParams params(mFile->mStorageType,
3213 mFile->mStorageName);
3214 ContentChild::GetSingleton()
3215 ->SendPDeviceStorageRequestConstructor(child, params);
3216 return NS_OK;
3218 r = new PostAvailableResultEvent(mFile, mRequest);
3219 return NS_DispatchToCurrentThread(r);
3222 case DEVICE_STORAGE_REQUEST_STATUS:
3224 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3225 PDeviceStorageRequestChild* child
3226 = new DeviceStorageRequestChild(mRequest, mFile);
3227 DeviceStorageStatusParams params(mFile->mStorageType,
3228 mFile->mStorageName);
3229 ContentChild::GetSingleton()
3230 ->SendPDeviceStorageRequestConstructor(child, params);
3231 return NS_OK;
3233 r = new PostStatusResultEvent(mFile, mRequest);
3234 return NS_DispatchToCurrentThread(r);
3237 case DEVICE_STORAGE_REQUEST_WATCH:
3239 mDeviceStorage->mAllowedToWatchFile = true;
3240 return NS_OK;
3243 case DEVICE_STORAGE_REQUEST_FORMAT:
3245 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3246 PDeviceStorageRequestChild* child
3247 = new DeviceStorageRequestChild(mRequest, mFile);
3248 DeviceStorageFormatParams params(mFile->mStorageType,
3249 mFile->mStorageName);
3250 ContentChild::GetSingleton()
3251 ->SendPDeviceStorageRequestConstructor(child, params);
3252 return NS_OK;
3254 r = new PostFormatResultEvent(mFile, mRequest);
3255 return NS_DispatchToCurrentThread(r);
3258 case DEVICE_STORAGE_REQUEST_MOUNT:
3260 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3261 PDeviceStorageRequestChild* child
3262 = new DeviceStorageRequestChild(mRequest, mFile);
3263 DeviceStorageMountParams params(mFile->mStorageType,
3264 mFile->mStorageName);
3265 ContentChild::GetSingleton()
3266 ->SendPDeviceStorageRequestConstructor(child, params);
3267 return NS_OK;
3269 r = new PostMountResultEvent(mFile, mRequest);
3270 return NS_DispatchToCurrentThread(r);
3273 case DEVICE_STORAGE_REQUEST_UNMOUNT:
3275 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3276 PDeviceStorageRequestChild* child
3277 = new DeviceStorageRequestChild(mRequest, mFile);
3278 DeviceStorageUnmountParams params(mFile->mStorageType,
3279 mFile->mStorageName);
3280 ContentChild::GetSingleton()
3281 ->SendPDeviceStorageRequestConstructor(child, params);
3282 return NS_OK;
3284 r = new PostUnmountResultEvent(mFile, mRequest);
3285 return NS_DispatchToCurrentThread(r);
3289 if (r) {
3290 nsCOMPtr<nsIEventTarget> target
3291 = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
3292 MOZ_ASSERT(target);
3293 target->Dispatch(r, NS_DISPATCH_NORMAL);
3296 return NS_OK;
3299 private:
3300 ~DeviceStorageRequest() {}
3302 int32_t mRequestType;
3303 nsCOMPtr<nsPIDOMWindow> mWindow;
3304 nsCOMPtr<nsIPrincipal> mPrincipal;
3305 nsRefPtr<DeviceStorageFile> mFile;
3307 nsRefPtr<DOMRequest> mRequest;
3308 nsCOMPtr<nsIDOMBlob> mBlob;
3309 nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
3310 nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
3313 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
3314 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
3315 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
3316 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
3317 NS_INTERFACE_MAP_END
3319 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest)
3320 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest)
3322 NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
3323 mRequest,
3324 mWindow,
3325 mBlob,
3326 mDeviceStorage)
3329 NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorage)
3330 NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
3331 NS_INTERFACE_MAP_ENTRY(nsIObserver)
3332 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
3334 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
3335 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
3337 nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow)
3338 : DOMEventTargetHelper(aWindow)
3339 , mIsShareable(false)
3340 , mIsRemovable(false)
3341 , mIsWatchingFile(false)
3342 , mAllowedToWatchFile(false)
3346 /* virtual */ JSObject*
3347 nsDOMDeviceStorage::WrapObject(JSContext* aCx)
3349 return DeviceStorageBinding::Wrap(aCx, this);
3352 nsresult
3353 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
3354 const nsAString &aVolName)
3356 DebugOnly<FileUpdateDispatcher*> observer
3357 = FileUpdateDispatcher::GetSingleton();
3358 MOZ_ASSERT(observer);
3360 MOZ_ASSERT(aWindow);
3362 SetRootDirectoryForType(aType, aVolName);
3363 if (!mRootDirectory) {
3364 return NS_ERROR_NOT_AVAILABLE;
3366 if (!mStorageName.IsEmpty()) {
3367 RegisterForSDCardChanges(this);
3369 #ifdef MOZ_WIDGET_GONK
3370 if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
3371 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
3372 if (NS_WARN_IF(!vs)) {
3373 return NS_ERROR_FAILURE;
3375 nsresult rv;
3376 nsCOMPtr<nsIVolume> vol;
3377 rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
3378 if (NS_WARN_IF(NS_FAILED(rv))) {
3379 return rv;
3381 bool isFake;
3382 rv = vol->GetIsFake(&isFake);
3383 if (NS_WARN_IF(NS_FAILED(rv))) {
3384 return rv;
3386 mIsShareable = !isFake;
3387 bool isRemovable;
3388 rv = vol->GetIsHotSwappable(&isRemovable);
3389 if (NS_WARN_IF(NS_FAILED(rv))) {
3390 return rv;
3392 mIsRemovable = isRemovable;
3394 #endif
3397 // Grab the principal of the document
3398 nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
3399 if (!doc) {
3400 return NS_ERROR_FAILURE;
3402 mPrincipal = doc->NodePrincipal();
3404 // the 'apps' type is special. We only want this exposed
3405 // if the caller has the "webapps-manage" permission.
3406 if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
3407 nsCOMPtr<nsIPermissionManager> permissionManager
3408 = services::GetPermissionManager();
3409 NS_ENSURE_TRUE(permissionManager, NS_ERROR_FAILURE);
3411 uint32_t permission;
3412 nsresult rv
3413 = permissionManager->TestPermissionFromPrincipal(mPrincipal,
3414 "webapps-manage",
3415 &permission);
3417 if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
3418 return NS_ERROR_NOT_AVAILABLE;
3422 return NS_OK;
3425 nsDOMDeviceStorage::~nsDOMDeviceStorage()
3429 void
3430 nsDOMDeviceStorage::Shutdown()
3432 MOZ_ASSERT(NS_IsMainThread());
3434 if (mFileSystem) {
3435 mFileSystem->Shutdown();
3436 mFileSystem = nullptr;
3439 if (!mStorageName.IsEmpty()) {
3440 UnregisterForSDCardChanges(this);
3443 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3444 obs->RemoveObserver(this, kFileWatcherUpdate);
3445 obs->RemoveObserver(this, "disk-space-watcher");
3448 StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
3450 // static
3451 void
3452 nsDOMDeviceStorage::GetOrderedVolumeNames(
3453 nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
3455 if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
3456 aVolumeNames.AppendElements(*sVolumeNameCache);
3457 return;
3459 #ifdef MOZ_WIDGET_GONK
3460 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
3461 if (vs) {
3462 nsCOMPtr<nsIArray> volNames;
3463 vs->GetVolumeNames(getter_AddRefs(volNames));
3464 uint32_t length = -1;
3465 volNames->GetLength(&length);
3466 for (uint32_t i = 0; i < length; i++) {
3467 nsCOMPtr<nsISupportsString> str = do_QueryElementAt(volNames, i);
3468 if (str) {
3469 nsAutoString s;
3470 if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
3471 aVolumeNames.AppendElement(s);
3476 // If the volume sdcard exists, then we want it to be first.
3478 VolumeNameArray::index_type sdcardIndex;
3479 sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard"));
3480 if (sdcardIndex != VolumeNameArray::NoIndex && sdcardIndex > 0) {
3481 aVolumeNames.RemoveElementAt(sdcardIndex);
3482 aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
3485 #endif
3486 if (aVolumeNames.IsEmpty()) {
3487 aVolumeNames.AppendElement(EmptyString());
3489 sVolumeNameCache = new nsTArray<nsString>;
3490 sVolumeNameCache->AppendElements(aVolumeNames);
3493 // static
3494 void
3495 nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
3496 const nsAString &aType,
3497 nsDOMDeviceStorage** aStore)
3499 nsString storageName;
3500 if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
3501 // The storage name will be the empty string
3502 storageName.Truncate();
3503 } else {
3504 GetDefaultStorageName(aType, storageName);
3507 nsRefPtr<nsDOMDeviceStorage> ds = new nsDOMDeviceStorage(aWin);
3508 if (NS_FAILED(ds->Init(aWin, aType, storageName))) {
3509 *aStore = nullptr;
3510 return;
3512 NS_ADDREF(*aStore = ds.get());
3515 // static
3516 void
3517 nsDOMDeviceStorage::CreateDeviceStoragesFor(
3518 nsPIDOMWindow* aWin,
3519 const nsAString &aType,
3520 nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
3522 nsresult rv;
3524 if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
3525 nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
3526 rv = storage->Init(aWin, aType, EmptyString());
3527 if (NS_SUCCEEDED(rv)) {
3528 aStores.AppendElement(storage);
3530 return;
3532 VolumeNameArray volNames;
3533 GetOrderedVolumeNames(volNames);
3535 VolumeNameArray::size_type numVolumeNames = volNames.Length();
3536 for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) {
3537 nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
3538 rv = storage->Init(aWin, aType, volNames[i]);
3539 if (NS_FAILED(rv)) {
3540 break;
3542 aStores.AppendElement(storage);
3546 // static
3547 bool
3548 nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
3549 nsAString& aOutStorageName,
3550 nsAString& aOutStoragePath)
3552 aOutStorageName.Truncate();
3553 aOutStoragePath.Truncate();
3555 NS_NAMED_LITERAL_STRING(slash, "/");
3557 nsDependentSubstring storageName;
3559 if (StringBeginsWith(aFullPath, slash)) {
3560 int32_t slashIndex = aFullPath.FindChar('/', 1);
3561 if (slashIndex == kNotFound) {
3562 // names of the form /filename are illegal
3563 return false;
3565 storageName.Rebind(aFullPath, 1, slashIndex - 1);
3566 aOutStoragePath = Substring(aFullPath, slashIndex + 1);
3567 } else {
3568 aOutStoragePath = aFullPath;
3570 // If no volume name was specified in aFullPath, then aOutStorageName
3571 // will wind up being the empty string. It's up to the caller to figure
3572 // out which storage name to actually use.
3573 aOutStorageName = storageName;
3574 return true;
3577 already_AddRefed<nsDOMDeviceStorage>
3578 nsDOMDeviceStorage::GetStorage(const nsAString& aFullPath,
3579 nsAString& aOutStoragePath)
3581 nsString storageName;
3582 if (!ParseFullPath(aFullPath, storageName, aOutStoragePath)) {
3583 return nullptr;
3585 nsRefPtr<nsDOMDeviceStorage> ds;
3586 if (storageName.IsEmpty()) {
3587 ds = this;
3588 } else {
3589 ds = GetStorageByName(storageName);
3591 return ds.forget();
3594 already_AddRefed<nsDOMDeviceStorage>
3595 nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
3597 MOZ_ASSERT(NS_IsMainThread());
3599 nsRefPtr<nsDOMDeviceStorage> ds;
3601 if (mStorageName.Equals(aStorageName)) {
3602 ds = this;
3603 return ds.forget();
3605 VolumeNameArray volNames;
3606 GetOrderedVolumeNames(volNames);
3607 VolumeNameArray::size_type numVolumes = volNames.Length();
3608 VolumeNameArray::index_type i;
3609 for (i = 0; i < numVolumes; i++) {
3610 if (volNames[i].Equals(aStorageName)) {
3611 ds = new nsDOMDeviceStorage(GetOwner());
3612 nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName);
3613 if (NS_FAILED(rv)) {
3614 return nullptr;
3616 return ds.forget();
3619 return nullptr;
3622 // static
3623 void
3624 nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
3625 nsAString& aStorageName)
3627 // See if the preferred volume is available.
3628 nsRefPtr<nsDOMDeviceStorage> ds;
3629 nsAdoptingString prefStorageName =
3630 mozilla::Preferences::GetString("device.storage.writable.name");
3631 if (prefStorageName) {
3632 aStorageName = prefStorageName;
3633 return;
3636 // No preferred storage, we'll use the first one (which should be sdcard).
3638 VolumeNameArray volNames;
3639 GetOrderedVolumeNames(volNames);
3640 if (volNames.Length() > 0) {
3641 aStorageName = volNames[0];
3642 return;
3645 // No volumes available, return the empty string. This is normal for
3646 // b2g-desktop.
3647 aStorageName.Truncate();
3650 bool
3651 nsDOMDeviceStorage::IsAvailable()
3653 nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
3654 return dsf->IsAvailable();
3657 NS_IMETHODIMP
3658 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
3660 ErrorResult rv;
3661 nsRefPtr<DOMRequest> request = Add(aBlob, rv);
3662 request.forget(_retval);
3663 return rv.ErrorCode();
3666 already_AddRefed<DOMRequest>
3667 nsDOMDeviceStorage::Add(nsIDOMBlob* aBlob, ErrorResult& aRv)
3669 if (!aBlob) {
3670 return nullptr;
3673 nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
3674 if (!mimeSvc) {
3675 aRv.Throw(NS_ERROR_FAILURE);
3676 return nullptr;
3679 // if mimeType isn't set, we will not get a correct
3680 // extension, and AddNamed() will fail. This will post an
3681 // onerror to the requestee.
3682 nsString mimeType;
3683 aBlob->GetType(mimeType);
3685 nsCString extension;
3686 mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
3687 EmptyCString(), extension);
3688 // if extension is null here, we will ignore it for now.
3689 // AddNamed() will check the file path and fail. This
3690 // will post an onerror to the requestee.
3692 // possible race here w/ unique filename
3693 char buffer[32];
3694 NS_MakeRandomString(buffer, ArrayLength(buffer) - 1);
3696 nsAutoCString path;
3697 path.Assign(nsDependentCString(buffer));
3698 path.Append('.');
3699 path.Append(extension);
3701 return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), aRv);
3704 NS_IMETHODIMP
3705 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
3706 const nsAString & aPath,
3707 nsIDOMDOMRequest * *_retval)
3709 ErrorResult rv;
3710 nsRefPtr<DOMRequest> request = AddNamed(aBlob, aPath, rv);
3711 request.forget(_retval);
3712 return rv.ErrorCode();
3715 already_AddRefed<DOMRequest>
3716 nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
3717 ErrorResult& aRv)
3719 return AddOrAppendNamed(aBlob, aPath,
3720 DEVICE_STORAGE_REQUEST_CREATE, aRv);
3723 already_AddRefed<DOMRequest>
3724 nsDOMDeviceStorage::AppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
3725 ErrorResult& aRv)
3727 return AddOrAppendNamed(aBlob, aPath,
3728 DEVICE_STORAGE_REQUEST_APPEND, aRv);
3732 already_AddRefed<DOMRequest>
3733 nsDOMDeviceStorage::AddOrAppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
3734 const int32_t aRequestType, ErrorResult& aRv)
3736 MOZ_ASSERT(NS_IsMainThread());
3738 // if the blob is null here, bail
3739 if (!aBlob) {
3740 return nullptr;
3743 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3744 if (!win) {
3745 aRv.Throw(NS_ERROR_UNEXPECTED);
3746 return nullptr;
3749 DeviceStorageTypeChecker* typeChecker
3750 = DeviceStorageTypeChecker::CreateOrGet();
3751 if (!typeChecker) {
3752 aRv.Throw(NS_ERROR_FAILURE);
3753 return nullptr;
3756 nsCOMPtr<nsIRunnable> r;
3757 nsresult rv;
3759 if (IsFullPath(aPath)) {
3760 nsString storagePath;
3761 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3762 if (!ds) {
3763 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3764 r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3765 rv = NS_DispatchToCurrentThread(r);
3766 if (NS_FAILED(rv)) {
3767 aRv.Throw(rv);
3769 return request.forget();
3772 return ds->AddOrAppendNamed(aBlob, storagePath,
3773 aRequestType, aRv);
3776 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3778 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3779 mStorageName,
3780 aPath);
3781 if (!dsf->IsSafePath()) {
3782 r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
3783 } else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
3784 !typeChecker->Check(mStorageType, aBlob)) {
3785 r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
3786 } else if (aRequestType == DEVICE_STORAGE_REQUEST_APPEND ||
3787 aRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
3788 r = new DeviceStorageRequest(DeviceStorageRequestType(aRequestType),
3789 win, mPrincipal, dsf, request, aBlob);
3790 } else {
3791 aRv.Throw(NS_ERROR_UNEXPECTED);
3792 return nullptr;
3795 rv = NS_DispatchToCurrentThread(r);
3796 if (NS_FAILED(rv)) {
3797 aRv.Throw(rv);
3799 return request.forget();
3802 NS_IMETHODIMP
3803 nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
3805 ErrorResult rv;
3806 nsRefPtr<DOMRequest> request = Get(aPath, rv);
3807 request.forget(aRetval);
3808 return rv.ErrorCode();
3811 NS_IMETHODIMP
3812 nsDOMDeviceStorage::GetEditable(const nsAString& aPath,
3813 nsIDOMDOMRequest** aRetval)
3815 ErrorResult rv;
3816 nsRefPtr<DOMRequest> request = GetEditable(aPath, rv);
3817 request.forget(aRetval);
3818 return rv.ErrorCode();
3821 already_AddRefed<DOMRequest>
3822 nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable,
3823 ErrorResult& aRv)
3825 MOZ_ASSERT(NS_IsMainThread());
3827 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3828 if (!win) {
3829 aRv.Throw(NS_ERROR_UNEXPECTED);
3830 return nullptr;
3833 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3835 if (IsFullPath(aPath)) {
3836 nsString storagePath;
3837 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3838 if (!ds) {
3839 nsCOMPtr<nsIRunnable> r =
3840 new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3841 nsresult rv = NS_DispatchToCurrentThread(r);
3842 if (NS_FAILED(rv)) {
3843 aRv.Throw(rv);
3845 return request.forget();
3847 ds->GetInternal(win, storagePath, request, aEditable);
3848 return request.forget();
3850 GetInternal(win, aPath, request, aEditable);
3851 return request.forget();
3854 void
3855 nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin,
3856 const nsAString& aPath,
3857 DOMRequest* aRequest,
3858 bool aEditable)
3860 MOZ_ASSERT(NS_IsMainThread());
3862 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3863 mStorageName,
3864 aPath);
3865 dsf->SetEditable(aEditable);
3867 nsCOMPtr<nsIRunnable> r;
3868 if (!dsf->IsSafePath()) {
3869 r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
3870 } else {
3871 r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE
3872 : DEVICE_STORAGE_REQUEST_READ,
3873 aWin, mPrincipal, dsf, aRequest);
3875 DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
3876 MOZ_ASSERT(NS_SUCCEEDED(rv));
3879 NS_IMETHODIMP
3880 nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
3882 ErrorResult rv;
3883 nsRefPtr<DOMRequest> request = Delete(aPath, rv);
3884 request.forget(aRetval);
3885 return rv.ErrorCode();
3888 already_AddRefed<DOMRequest>
3889 nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv)
3891 MOZ_ASSERT(NS_IsMainThread());
3893 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3894 if (!win) {
3895 aRv.Throw(NS_ERROR_UNEXPECTED);
3896 return nullptr;
3899 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3901 if (IsFullPath(aPath)) {
3902 nsString storagePath;
3903 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3904 if (!ds) {
3905 nsCOMPtr<nsIRunnable> r =
3906 new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3907 nsresult rv = NS_DispatchToCurrentThread(r);
3908 if (NS_FAILED(rv)) {
3909 aRv.Throw(rv);
3911 return request.forget();
3913 ds->DeleteInternal(win, storagePath, request);
3914 return request.forget();
3916 DeleteInternal(win, aPath, request);
3917 return request.forget();
3920 void
3921 nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin,
3922 const nsAString& aPath,
3923 DOMRequest* aRequest)
3925 MOZ_ASSERT(NS_IsMainThread());
3927 nsCOMPtr<nsIRunnable> r;
3928 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3929 mStorageName,
3930 aPath);
3931 if (!dsf->IsSafePath()) {
3932 r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
3933 } else {
3934 r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
3935 aWin, mPrincipal, dsf, aRequest);
3937 DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
3938 MOZ_ASSERT(NS_SUCCEEDED(rv));
3941 NS_IMETHODIMP
3942 nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
3944 ErrorResult rv;
3945 nsRefPtr<DOMRequest> request = FreeSpace(rv);
3946 request.forget(aRetval);
3947 return rv.ErrorCode();
3950 already_AddRefed<DOMRequest>
3951 nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv)
3953 MOZ_ASSERT(NS_IsMainThread());
3955 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3956 if (!win) {
3957 aRv.Throw(NS_ERROR_UNEXPECTED);
3958 return nullptr;
3961 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3963 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3964 mStorageName);
3965 nsCOMPtr<nsIRunnable> r
3966 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
3967 win, mPrincipal, dsf, request);
3968 nsresult rv = NS_DispatchToCurrentThread(r);
3969 if (NS_FAILED(rv)) {
3970 aRv.Throw(rv);
3972 return request.forget();
3975 NS_IMETHODIMP
3976 nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
3978 ErrorResult rv;
3979 nsRefPtr<DOMRequest> request = UsedSpace(rv);
3980 request.forget(aRetval);
3981 return rv.ErrorCode();
3984 already_AddRefed<DOMRequest>
3985 nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv)
3987 MOZ_ASSERT(NS_IsMainThread());
3989 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3990 if (!win) {
3991 aRv.Throw(NS_ERROR_UNEXPECTED);
3992 return nullptr;
3995 DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
3996 = DeviceStorageUsedSpaceCache::CreateOrGet();
3997 MOZ_ASSERT(usedSpaceCache);
3999 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4001 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4002 mStorageName);
4003 nsCOMPtr<nsIRunnable> r
4004 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
4005 win, mPrincipal, dsf, request);
4006 nsresult rv = NS_DispatchToCurrentThread(r);
4007 if (NS_FAILED(rv)) {
4008 aRv.Throw(rv);
4010 return request.forget();
4013 NS_IMETHODIMP
4014 nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
4016 ErrorResult rv;
4017 nsRefPtr<DOMRequest> request = Available(rv);
4018 request.forget(aRetval);
4019 return rv.ErrorCode();
4022 already_AddRefed<DOMRequest>
4023 nsDOMDeviceStorage::Available(ErrorResult& aRv)
4025 MOZ_ASSERT(NS_IsMainThread());
4027 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4028 if (!win) {
4029 aRv.Throw(NS_ERROR_UNEXPECTED);
4030 return nullptr;
4033 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4035 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4036 mStorageName);
4037 nsCOMPtr<nsIRunnable> r
4038 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
4039 win, mPrincipal, dsf, request);
4040 nsresult rv = NS_DispatchToCurrentThread(r);
4041 if (NS_FAILED(rv)) {
4042 aRv.Throw(rv);
4044 return request.forget();
4047 already_AddRefed<DOMRequest>
4048 nsDOMDeviceStorage::StorageStatus(ErrorResult& aRv)
4050 MOZ_ASSERT(NS_IsMainThread());
4052 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4053 if (!win) {
4054 aRv.Throw(NS_ERROR_UNEXPECTED);
4055 return nullptr;
4058 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4060 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4061 mStorageName);
4062 nsCOMPtr<nsIRunnable> r
4063 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_STATUS,
4064 win, mPrincipal, dsf, request);
4065 nsresult rv = NS_DispatchToCurrentThread(r);
4066 if (NS_FAILED(rv)) {
4067 aRv.Throw(rv);
4069 return request.forget();
4072 already_AddRefed<DOMRequest>
4073 nsDOMDeviceStorage::Format(ErrorResult& aRv)
4075 MOZ_ASSERT(NS_IsMainThread());
4077 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4078 if (!win) {
4079 aRv.Throw(NS_ERROR_UNEXPECTED);
4080 return nullptr;
4083 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4085 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4086 mStorageName);
4087 nsCOMPtr<nsIRunnable> r
4088 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
4089 win, mPrincipal, dsf, request);
4090 nsresult rv = NS_DispatchToCurrentThread(r);
4091 if (NS_FAILED(rv)) {
4092 aRv.Throw(rv);
4094 return request.forget();
4097 already_AddRefed<DOMRequest>
4098 nsDOMDeviceStorage::Mount(ErrorResult& aRv)
4100 MOZ_ASSERT(NS_IsMainThread());
4102 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4103 if (!win) {
4104 aRv.Throw(NS_ERROR_UNEXPECTED);
4105 return nullptr;
4108 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4110 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4111 mStorageName);
4112 nsCOMPtr<nsIRunnable> r
4113 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_MOUNT,
4114 win, mPrincipal, dsf, request);
4115 nsresult rv = NS_DispatchToCurrentThread(r);
4116 if (NS_FAILED(rv)) {
4117 aRv.Throw(rv);
4119 return request.forget();
4122 already_AddRefed<DOMRequest>
4123 nsDOMDeviceStorage::Unmount(ErrorResult& aRv)
4125 MOZ_ASSERT(NS_IsMainThread());
4127 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4128 if (!win) {
4129 aRv.Throw(NS_ERROR_UNEXPECTED);
4130 return nullptr;
4133 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4135 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4136 mStorageName);
4137 nsCOMPtr<nsIRunnable> r
4138 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_UNMOUNT,
4139 win, mPrincipal, dsf, request);
4140 nsresult rv = NS_DispatchToCurrentThread(r);
4141 if (NS_FAILED(rv)) {
4142 aRv.Throw(rv);
4144 return request.forget();
4147 NS_IMETHODIMP
4148 nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath,
4149 DeviceStorageFileDescriptor* aDSFileDescriptor,
4150 nsIDOMDOMRequest** aRequest)
4152 MOZ_ASSERT(NS_IsMainThread());
4153 MOZ_ASSERT(aDSFileDescriptor);
4155 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4156 if (!win) {
4157 return NS_ERROR_UNEXPECTED;
4160 DeviceStorageTypeChecker* typeChecker
4161 = DeviceStorageTypeChecker::CreateOrGet();
4162 if (!typeChecker) {
4163 return NS_ERROR_FAILURE;
4166 nsCOMPtr<nsIRunnable> r;
4167 nsresult rv;
4169 if (IsFullPath(aPath)) {
4170 nsString storagePath;
4171 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
4172 if (!ds) {
4173 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4174 r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
4175 rv = NS_DispatchToCurrentThread(r);
4176 if (NS_FAILED(rv)) {
4177 return rv;
4179 request.forget(aRequest);
4180 return NS_OK;
4182 return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest);
4185 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4187 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4188 mStorageName,
4189 aPath);
4190 if (!dsf->IsSafePath()) {
4191 r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
4192 } else if (!typeChecker->Check(mStorageType, dsf->mFile)) {
4193 r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
4194 } else {
4195 r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD,
4196 win, mPrincipal, dsf, request,
4197 aDSFileDescriptor);
4200 rv = NS_DispatchToCurrentThread(r);
4201 if (NS_FAILED(rv)) {
4202 return rv;
4204 request.forget(aRequest);
4205 return NS_OK;
4208 bool
4209 nsDOMDeviceStorage::Default()
4211 nsString defaultStorageName;
4212 GetDefaultStorageName(mStorageType, defaultStorageName);
4213 return mStorageName.Equals(defaultStorageName);
4216 bool
4217 nsDOMDeviceStorage::CanBeFormatted()
4219 // Currently, any volume which can be shared can also be formatted.
4220 return mIsShareable;
4223 bool
4224 nsDOMDeviceStorage::CanBeMounted()
4226 // Currently, any volume which can be shared can also be mounted/unmounted.
4227 return mIsShareable;
4230 bool
4231 nsDOMDeviceStorage::CanBeShared()
4233 return mIsShareable;
4236 bool
4237 nsDOMDeviceStorage::IsRemovable()
4239 return mIsRemovable;
4242 already_AddRefed<Promise>
4243 nsDOMDeviceStorage::GetRoot(ErrorResult& aRv)
4245 if (!mFileSystem) {
4246 mFileSystem = new DeviceStorageFileSystem(mStorageType, mStorageName);
4247 mFileSystem->Init(this);
4249 return mozilla::dom::Directory::GetRoot(mFileSystem, aRv);
4252 NS_IMETHODIMP
4253 nsDOMDeviceStorage::GetDefault(bool* aDefault)
4255 *aDefault = Default();
4256 return NS_OK;
4259 NS_IMETHODIMP
4260 nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName)
4262 aStorageName = mStorageName;
4263 return NS_OK;
4266 already_AddRefed<DOMCursor>
4267 nsDOMDeviceStorage::Enumerate(const nsAString& aPath,
4268 const EnumerationParameters& aOptions,
4269 ErrorResult& aRv)
4271 return EnumerateInternal(aPath, aOptions, false, aRv);
4274 already_AddRefed<DOMCursor>
4275 nsDOMDeviceStorage::EnumerateEditable(const nsAString& aPath,
4276 const EnumerationParameters& aOptions,
4277 ErrorResult& aRv)
4279 return EnumerateInternal(aPath, aOptions, true, aRv);
4283 already_AddRefed<DOMCursor>
4284 nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
4285 const EnumerationParameters& aOptions,
4286 bool aEditable, ErrorResult& aRv)
4288 MOZ_ASSERT(NS_IsMainThread());
4290 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4291 if (!win) {
4292 aRv.Throw(NS_ERROR_UNEXPECTED);
4293 return nullptr;
4296 PRTime since = 0;
4297 if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
4298 since = PRTime(aOptions.mSince.Value().TimeStamp());
4301 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4302 mStorageName,
4303 aPath,
4304 EmptyString());
4305 dsf->SetEditable(aEditable);
4307 nsRefPtr<nsDOMDeviceStorageCursor> cursor
4308 = new nsDOMDeviceStorageCursor(win, mPrincipal, dsf, since);
4309 nsRefPtr<DeviceStorageCursorRequest> r
4310 = new DeviceStorageCursorRequest(cursor);
4312 if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
4313 r->Allow(JS::UndefinedHandleValue);
4314 return cursor.forget();
4317 nsContentPermissionUtils::AskPermission(r, win);
4319 return cursor.forget();
4322 #ifdef MOZ_WIDGET_GONK
4323 void
4324 nsDOMDeviceStorage::DispatchStatusChangeEvent(nsAString& aStatus)
4326 if (aStatus == mLastStatus) {
4327 // We've already sent this status, don't bother sending it again.
4328 return;
4330 mLastStatus = aStatus;
4332 DeviceStorageChangeEventInit init;
4333 init.mBubbles = true;
4334 init.mCancelable = false;
4335 init.mPath = mStorageName;
4336 init.mReason = aStatus;
4338 nsRefPtr<DeviceStorageChangeEvent> event =
4339 DeviceStorageChangeEvent::Constructor(this, NS_LITERAL_STRING("change"),
4340 init);
4341 event->SetTrusted(true);
4343 bool ignore;
4344 DispatchEvent(event, &ignore);
4347 void
4348 nsDOMDeviceStorage::DispatchStorageStatusChangeEvent(nsAString& aStorageStatus)
4350 if (aStorageStatus == mLastStorageStatus) {
4351 // We've already sent this status, don't bother sending it again.
4352 return;
4354 mLastStorageStatus = aStorageStatus;
4356 DeviceStorageChangeEventInit init;
4357 init.mBubbles = true;
4358 init.mCancelable = false;
4359 init.mPath = mStorageName;
4360 init.mReason = aStorageStatus;
4362 nsRefPtr<DeviceStorageChangeEvent> event =
4363 DeviceStorageChangeEvent::Constructor(this, NS_LITERAL_STRING("storage-state-change"),
4364 init);
4365 event->SetTrusted(true);
4367 bool ignore;
4368 DispatchEvent(event, &ignore);
4370 #endif
4372 NS_IMETHODIMP
4373 nsDOMDeviceStorage::Observe(nsISupports *aSubject,
4374 const char *aTopic,
4375 const char16_t *aData)
4377 MOZ_ASSERT(NS_IsMainThread());
4379 if (!strcmp(aTopic, kFileWatcherUpdate)) {
4381 DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
4382 Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
4383 return NS_OK;
4385 if (!strcmp(aTopic, "disk-space-watcher")) {
4386 // 'disk-space-watcher' notifications are sent when there is a modification
4387 // of a file in a specific location while a low device storage situation
4388 // exists or after recovery of a low storage situation. For Firefox OS,
4389 // these notifications are specific for apps storage.
4390 nsRefPtr<DeviceStorageFile> file =
4391 new DeviceStorageFile(mStorageType, mStorageName);
4392 if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
4393 Notify("low-disk-space", file);
4394 } else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
4395 Notify("available-disk-space", file);
4397 return NS_OK;
4400 #ifdef MOZ_WIDGET_GONK
4401 else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
4402 // We invalidate the used space cache for the volume that actually changed
4403 // state.
4404 nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
4405 if (!vol) {
4406 return NS_OK;
4408 nsString volName;
4409 vol->GetName(volName);
4411 DeviceStorageUsedSpaceCache* usedSpaceCache
4412 = DeviceStorageUsedSpaceCache::CreateOrGet();
4413 MOZ_ASSERT(usedSpaceCache);
4414 usedSpaceCache->Invalidate(volName);
4416 if (!volName.Equals(mStorageName)) {
4417 // Not our volume - we can ignore.
4418 return NS_OK;
4421 nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
4422 nsString status, storageStatus;
4424 // Get Status (one of "available, unavailable, shared")
4425 dsf->GetStatus(status);
4426 DispatchStatusChangeEvent(status);
4428 // Get real volume status (defined in dom/system/gonk/nsIVolume.idl)
4429 dsf->GetStorageStatus(storageStatus);
4430 DispatchStorageStatusChangeEvent(storageStatus);
4431 return NS_OK;
4433 #endif
4434 return NS_OK;
4437 nsresult
4438 nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)
4440 if (!mAllowedToWatchFile) {
4441 return NS_OK;
4444 if (!mStorageType.Equals(aFile->mStorageType) ||
4445 !mStorageName.Equals(aFile->mStorageName)) {
4446 // Ignore this
4447 return NS_OK;
4450 DeviceStorageChangeEventInit init;
4451 init.mBubbles = true;
4452 init.mCancelable = false;
4453 aFile->GetFullPath(init.mPath);
4454 init.mReason.AssignWithConversion(aReason);
4456 nsRefPtr<DeviceStorageChangeEvent> event =
4457 DeviceStorageChangeEvent::Constructor(this, NS_LITERAL_STRING("change"),
4458 init);
4459 event->SetTrusted(true);
4461 bool ignore;
4462 DispatchEvent(event, &ignore);
4463 return NS_OK;
4466 NS_IMETHODIMP
4467 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
4468 nsIDOMEventListener *aListener,
4469 bool aUseCapture,
4470 bool aWantsUntrusted,
4471 uint8_t aArgc)
4473 MOZ_ASSERT(NS_IsMainThread());
4475 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4476 if (!win) {
4477 return NS_ERROR_UNEXPECTED;
4480 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4481 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4482 mStorageName);
4483 nsCOMPtr<nsIRunnable> r
4484 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
4485 win, mPrincipal, dsf, request, this);
4486 nsresult rv = NS_DispatchToCurrentThread(r);
4487 if (NS_WARN_IF(NS_FAILED(rv))) {
4488 return rv;
4491 return DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
4492 aWantsUntrusted, aArgc);
4495 void
4496 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
4497 EventListener *aListener,
4498 bool aUseCapture,
4499 const Nullable<bool>& aWantsUntrusted,
4500 ErrorResult& aRv)
4502 MOZ_ASSERT(NS_IsMainThread());
4504 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4505 if (!win) {
4506 aRv.Throw(NS_ERROR_UNEXPECTED);
4507 return;
4510 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4511 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4512 mStorageName);
4513 nsCOMPtr<nsIRunnable> r
4514 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
4515 win, mPrincipal, dsf, request, this);
4516 nsresult rv = NS_DispatchToCurrentThread(r);
4517 if (NS_WARN_IF(NS_FAILED(rv))) {
4518 return;
4520 DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
4521 aWantsUntrusted, aRv);
4524 NS_IMETHODIMP
4525 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
4526 nsIDOMEventListener *aListener,
4527 bool aUseCapture,
4528 bool aWantsUntrusted,
4529 uint8_t aArgc)
4531 if (!mIsWatchingFile) {
4532 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4533 obs->AddObserver(this, kFileWatcherUpdate, false);
4534 mIsWatchingFile = true;
4537 return nsDOMDeviceStorage::AddEventListener(aType, aListener, aUseCapture,
4538 aWantsUntrusted, aArgc);
4541 NS_IMETHODIMP
4542 nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
4543 nsIDOMEventListener *aListener,
4544 bool aUseCapture)
4546 DOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
4548 if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
4549 mIsWatchingFile = false;
4550 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4551 obs->RemoveObserver(this, kFileWatcherUpdate);
4553 return NS_OK;
4556 void
4557 nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType,
4558 EventListener* aListener,
4559 bool aCapture,
4560 ErrorResult& aRv)
4562 DOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv);
4564 if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
4565 mIsWatchingFile = false;
4566 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4567 obs->RemoveObserver(this, kFileWatcherUpdate);
4571 NS_IMETHODIMP
4572 nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
4573 nsIDOMEventListener *aListener,
4574 bool aUseCapture)
4576 return nsDOMDeviceStorage::RemoveEventListener(aType, aListener, aUseCapture);
4579 NS_IMETHODIMP
4580 nsDOMDeviceStorage::DispatchEvent(nsIDOMEvent *aEvt,
4581 bool *aRetval)
4583 return DOMEventTargetHelper::DispatchEvent(aEvt, aRetval);
4586 EventTarget*
4587 nsDOMDeviceStorage::GetTargetForDOMEvent()
4589 return DOMEventTargetHelper::GetTargetForDOMEvent();
4592 EventTarget *
4593 nsDOMDeviceStorage::GetTargetForEventTargetChain()
4595 return DOMEventTargetHelper::GetTargetForEventTargetChain();
4598 nsresult
4599 nsDOMDeviceStorage::PreHandleEvent(EventChainPreVisitor& aVisitor)
4601 return DOMEventTargetHelper::PreHandleEvent(aVisitor);
4604 nsresult
4605 nsDOMDeviceStorage::WillHandleEvent(EventChainPostVisitor& aVisitor)
4607 return DOMEventTargetHelper::WillHandleEvent(aVisitor);
4610 nsresult
4611 nsDOMDeviceStorage::PostHandleEvent(EventChainPostVisitor& aVisitor)
4613 return DOMEventTargetHelper::PostHandleEvent(aVisitor);
4616 nsresult
4617 nsDOMDeviceStorage::DispatchDOMEvent(WidgetEvent* aEvent,
4618 nsIDOMEvent* aDOMEvent,
4619 nsPresContext* aPresContext,
4620 nsEventStatus* aEventStatus)
4622 return DOMEventTargetHelper::DispatchDOMEvent(aEvent,
4623 aDOMEvent,
4624 aPresContext,
4625 aEventStatus);
4628 EventListenerManager*
4629 nsDOMDeviceStorage::GetOrCreateListenerManager()
4631 return DOMEventTargetHelper::GetOrCreateListenerManager();
4634 EventListenerManager*
4635 nsDOMDeviceStorage::GetExistingListenerManager() const
4637 return DOMEventTargetHelper::GetExistingListenerManager();
4640 nsIScriptContext *
4641 nsDOMDeviceStorage::GetContextForEventHandlers(nsresult *aRv)
4643 return DOMEventTargetHelper::GetContextForEventHandlers(aRv);
4646 JSContext *
4647 nsDOMDeviceStorage::GetJSContextForEventHandlers()
4649 return DOMEventTargetHelper::GetJSContextForEventHandlers();
4652 NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)