Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/DeviceStorageFileSystem.h"
15 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
16 #include "mozilla/dom/Directory.h"
17 #include "mozilla/dom/FileSystemUtils.h"
18 #include "mozilla/dom/ipc/Blob.h"
19 #include "mozilla/dom/PBrowserChild.h"
20 #include "mozilla/dom/PContentPermissionRequestChild.h"
21 #include "mozilla/dom/PermissionMessageUtils.h"
22 #include "mozilla/dom/Promise.h"
23 #include "mozilla/EventDispatcher.h"
24 #include "mozilla/EventListenerManager.h"
25 #include "mozilla/LazyIdleThread.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/Scoped.h"
28 #include "mozilla/Services.h"
30 #include "nsAutoPtr.h"
31 #include "nsServiceManagerUtils.h"
32 #include "nsIFile.h"
33 #include "nsIDirectoryEnumerator.h"
34 #include "nsAppDirectoryServiceDefs.h"
35 #include "nsDirectoryServiceDefs.h"
36 #include "nsIDOMFile.h"
37 #include "nsDOMBlobBuilder.h"
38 #include "nsNetUtil.h"
39 #include "nsCycleCollectionParticipant.h"
40 #include "nsIPrincipal.h"
41 #include "nsJSUtils.h"
42 #include "nsContentUtils.h"
43 #include "nsCxPusher.h"
44 #include "nsXULAppAPI.h"
45 #include "TabChild.h"
46 #include "DeviceStorageFileDescriptor.h"
47 #include "DeviceStorageRequestChild.h"
48 #include "nsIDOMDeviceStorageChangeEvent.h"
49 #include "nsCRT.h"
50 #include "nsIObserverService.h"
51 #include "GeneratedEvents.h"
52 #include "nsIMIMEService.h"
53 #include "nsCExternalHandlerService.h"
54 #include "nsIPermissionManager.h"
55 #include "nsIStringBundle.h"
56 #include "nsIDocument.h"
57 #include "nsPrintfCString.h"
58 #include <algorithm>
59 #include "private/pprio.h"
60 #include "nsContentPermissionHelper.h"
62 #include "mozilla/dom/DeviceStorageBinding.h"
64 // Microsoft's API Name hackery sucks
65 #undef CreateEvent
67 #ifdef MOZ_WIDGET_GONK
68 #include "nsIVolume.h"
69 #include "nsIVolumeService.h"
70 #endif
72 #define DEVICESTORAGE_PROPERTIES \
73 "chrome://global/content/devicestorage.properties"
74 #define DEFAULT_THREAD_TIMEOUT_MS 30000
76 using namespace mozilla;
77 using namespace mozilla::dom;
78 using namespace mozilla::dom::devicestorage;
79 using namespace mozilla::ipc;
81 #include "nsDirectoryServiceDefs.h"
83 namespace mozilla {
84 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
85 }
87 StaticAutoPtr<DeviceStorageUsedSpaceCache>
88 DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
90 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
91 {
92 MOZ_ASSERT(NS_IsMainThread());
94 mIOThread = new LazyIdleThread(
95 DEFAULT_THREAD_TIMEOUT_MS,
96 NS_LITERAL_CSTRING("DeviceStorageUsedSpaceCache I/O"));
98 }
100 DeviceStorageUsedSpaceCache::~DeviceStorageUsedSpaceCache()
101 {
102 }
104 DeviceStorageUsedSpaceCache*
105 DeviceStorageUsedSpaceCache::CreateOrGet()
106 {
107 if (sDeviceStorageUsedSpaceCache) {
108 return sDeviceStorageUsedSpaceCache;
109 }
111 MOZ_ASSERT(NS_IsMainThread());
113 sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache();
114 ClearOnShutdown(&sDeviceStorageUsedSpaceCache);
115 return sDeviceStorageUsedSpaceCache;
116 }
118 already_AddRefed<DeviceStorageUsedSpaceCache::CacheEntry>
119 DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName)
120 {
121 nsTArray<nsRefPtr<CacheEntry>>::size_type numEntries = mCacheEntries.Length();
122 nsTArray<nsRefPtr<CacheEntry>>::index_type i;
123 for (i = 0; i < numEntries; i++) {
124 nsRefPtr<CacheEntry>& cacheEntry = mCacheEntries[i];
125 if (cacheEntry->mStorageName.Equals(aStorageName)) {
126 nsRefPtr<CacheEntry> addRefedCacheEntry = cacheEntry;
127 return addRefedCacheEntry.forget();
128 }
129 }
130 return nullptr;
131 }
133 static int64_t
134 GetFreeBytes(const nsAString& aStorageName)
135 {
136 // This function makes the assumption that the various types
137 // are all stored on the same filesystem. So we use pictures.
139 DeviceStorageFile dsf(NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
140 aStorageName);
141 int64_t freeBytes = 0;
142 dsf.GetDiskFreeSpace(&freeBytes);
143 return freeBytes;
144 }
146 nsresult
147 DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
148 uint64_t* aPicturesSoFar,
149 uint64_t* aVideosSoFar,
150 uint64_t* aMusicSoFar,
151 uint64_t* aTotalSoFar)
152 {
153 nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
154 if (!cacheEntry || cacheEntry->mDirty) {
155 return NS_ERROR_NOT_AVAILABLE;
156 }
157 int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName);
158 if (freeBytes != cacheEntry->mFreeBytes) {
159 // Free space changed, so our cached results are no longer valid.
160 return NS_ERROR_NOT_AVAILABLE;
161 }
163 *aPicturesSoFar += cacheEntry->mPicturesUsedSize;
164 *aVideosSoFar += cacheEntry->mVideosUsedSize;
165 *aMusicSoFar += cacheEntry->mMusicUsedSize;
166 *aTotalSoFar += cacheEntry->mTotalUsedSize;
168 return NS_OK;
169 }
171 void
172 DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName,
173 uint64_t aPictureSize,
174 uint64_t aVideosSize,
175 uint64_t aMusicSize,
176 uint64_t aTotalUsedSize)
177 {
178 nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
179 if (!cacheEntry) {
180 cacheEntry = new CacheEntry;
181 cacheEntry->mStorageName = aStorageName;
182 mCacheEntries.AppendElement(cacheEntry);
183 }
184 cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName);
186 cacheEntry->mPicturesUsedSize = aPictureSize;
187 cacheEntry->mVideosUsedSize = aVideosSize;
188 cacheEntry->mMusicUsedSize = aMusicSize;
189 cacheEntry->mTotalUsedSize = aTotalUsedSize;
190 cacheEntry->mDirty = false;
191 }
193 class GlobalDirs
194 {
195 public:
196 NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
197 #if !defined(MOZ_WIDGET_GONK)
198 nsCOMPtr<nsIFile> pictures;
199 nsCOMPtr<nsIFile> videos;
200 nsCOMPtr<nsIFile> music;
201 nsCOMPtr<nsIFile> sdcard;
202 #endif
203 nsCOMPtr<nsIFile> apps;
204 nsCOMPtr<nsIFile> crashes;
205 nsCOMPtr<nsIFile> overrideRootDir;
206 };
208 static StaticRefPtr<GlobalDirs> sDirs;
210 StaticAutoPtr<DeviceStorageTypeChecker>
211 DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
213 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
214 {
215 }
217 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
218 {
219 }
221 DeviceStorageTypeChecker*
222 DeviceStorageTypeChecker::CreateOrGet()
223 {
224 if (sDeviceStorageTypeChecker) {
225 return sDeviceStorageTypeChecker;
226 }
228 MOZ_ASSERT(NS_IsMainThread());
230 nsCOMPtr<nsIStringBundleService> stringService
231 = mozilla::services::GetStringBundleService();
232 if (!stringService) {
233 return nullptr;
234 }
236 nsCOMPtr<nsIStringBundle> filterBundle;
237 if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
238 getter_AddRefs(filterBundle)))) {
239 return nullptr;
240 }
242 DeviceStorageTypeChecker* result = new DeviceStorageTypeChecker();
243 result->InitFromBundle(filterBundle);
245 sDeviceStorageTypeChecker = result;
246 ClearOnShutdown(&sDeviceStorageTypeChecker);
247 return result;
248 }
250 void
251 DeviceStorageTypeChecker::InitFromBundle(nsIStringBundle* aBundle)
252 {
253 aBundle->GetStringFromName(
254 NS_ConvertASCIItoUTF16(DEVICESTORAGE_PICTURES).get(),
255 getter_Copies(mPicturesExtensions));
256 aBundle->GetStringFromName(
257 NS_ConvertASCIItoUTF16(DEVICESTORAGE_MUSIC).get(),
258 getter_Copies(mMusicExtensions));
259 aBundle->GetStringFromName(
260 NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(),
261 getter_Copies(mVideosExtensions));
262 }
265 bool
266 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
267 {
268 MOZ_ASSERT(aBlob);
270 nsString mimeType;
271 if (NS_FAILED(aBlob->GetType(mimeType))) {
272 return false;
273 }
275 if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
276 return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
277 }
279 if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
280 return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
281 }
283 if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
284 return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
285 }
287 if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
288 aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
289 aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
290 // Apps, crashes and sdcard have no restriction on mime types
291 return true;
292 }
294 return false;
295 }
297 bool
298 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
299 {
300 MOZ_ASSERT(aFile);
302 if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
303 aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
304 aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
305 // Apps, crashes and sdcard have no restrictions on what file extensions used.
306 return true;
307 }
309 nsString path;
310 aFile->GetPath(path);
312 int32_t dotIdx = path.RFindChar(char16_t('.'));
313 if (dotIdx == kNotFound) {
314 return false;
315 }
317 nsAutoString extensionMatch;
318 extensionMatch.AssignLiteral("*");
319 extensionMatch.Append(Substring(path, dotIdx));
320 extensionMatch.AppendLiteral(";");
322 if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
323 return CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions);
324 }
326 if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
327 return CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions);
328 }
330 if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
331 return CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions);
332 }
334 return false;
335 }
337 void
338 DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType)
339 {
340 MOZ_ASSERT(aFile);
342 nsString path;
343 aFile->GetPath(path);
345 GetTypeFromFileName(path, aType);
346 }
348 void
349 DeviceStorageTypeChecker::GetTypeFromFileName(const nsAString& aFileName,
350 nsAString& aType)
351 {
352 aType.AssignLiteral(DEVICESTORAGE_SDCARD);
354 nsString fileName(aFileName);
355 int32_t dotIdx = fileName.RFindChar(char16_t('.'));
356 if (dotIdx == kNotFound) {
357 return;
358 }
360 nsAutoString extensionMatch;
361 extensionMatch.AssignLiteral("*");
362 extensionMatch.Append(Substring(aFileName, dotIdx));
363 extensionMatch.AppendLiteral(";");
365 if (CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions)) {
366 aType.AssignLiteral(DEVICESTORAGE_PICTURES);
367 }
368 else if (CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions)) {
369 aType.AssignLiteral(DEVICESTORAGE_VIDEOS);
370 }
371 else if (CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions)) {
372 aType.AssignLiteral(DEVICESTORAGE_MUSIC);
373 }
374 }
376 nsresult
377 DeviceStorageTypeChecker::GetPermissionForType(const nsAString& aType,
378 nsACString& aPermissionResult)
379 {
380 if (!aType.EqualsLiteral(DEVICESTORAGE_PICTURES) &&
381 !aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) &&
382 !aType.EqualsLiteral(DEVICESTORAGE_MUSIC) &&
383 !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
384 !aType.EqualsLiteral(DEVICESTORAGE_SDCARD) &&
385 !aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
386 // unknown type
387 return NS_ERROR_FAILURE;
388 }
390 aPermissionResult.AssignLiteral("device-storage:");
391 aPermissionResult.Append(NS_ConvertUTF16toUTF8(aType));
392 return NS_OK;
393 }
395 nsresult
396 DeviceStorageTypeChecker::GetAccessForRequest(
397 const DeviceStorageRequestType aRequestType, nsACString& aAccessResult)
398 {
399 switch(aRequestType) {
400 case DEVICE_STORAGE_REQUEST_READ:
401 case DEVICE_STORAGE_REQUEST_WATCH:
402 case DEVICE_STORAGE_REQUEST_FREE_SPACE:
403 case DEVICE_STORAGE_REQUEST_USED_SPACE:
404 case DEVICE_STORAGE_REQUEST_AVAILABLE:
405 case DEVICE_STORAGE_REQUEST_STATUS:
406 aAccessResult.AssignLiteral("read");
407 break;
408 case DEVICE_STORAGE_REQUEST_WRITE:
409 case DEVICE_STORAGE_REQUEST_DELETE:
410 case DEVICE_STORAGE_REQUEST_FORMAT:
411 case DEVICE_STORAGE_REQUEST_MOUNT:
412 case DEVICE_STORAGE_REQUEST_UNMOUNT:
413 aAccessResult.AssignLiteral("write");
414 break;
415 case DEVICE_STORAGE_REQUEST_CREATE:
416 case DEVICE_STORAGE_REQUEST_CREATEFD:
417 aAccessResult.AssignLiteral("create");
418 break;
419 default:
420 aAccessResult.AssignLiteral("undefined");
421 }
422 return NS_OK;
423 }
425 //static
426 bool
427 DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType)
428 {
429 #ifdef MOZ_WIDGET_GONK
430 // The apps and crashes aren't stored in the same place as the media, so
431 // we only ever return a single apps object, and not an array
432 // with one per volume (as is the case for the remaining
433 // storage types).
434 return !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
435 !aType.EqualsLiteral(DEVICESTORAGE_CRASHES);
436 #else
437 return false;
438 #endif
439 }
441 NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
443 mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
445 FileUpdateDispatcher*
446 FileUpdateDispatcher::GetSingleton()
447 {
448 if (sSingleton) {
449 return sSingleton;
450 }
452 sSingleton = new FileUpdateDispatcher();
453 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
454 obs->AddObserver(sSingleton, "file-watcher-notify", false);
455 ClearOnShutdown(&sSingleton);
457 return sSingleton;
458 }
460 NS_IMETHODIMP
461 FileUpdateDispatcher::Observe(nsISupports *aSubject,
462 const char *aTopic,
463 const char16_t *aData)
464 {
465 if (XRE_GetProcessType() != GeckoProcessType_Default) {
467 DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
468 if (!file || !file->mFile) {
469 NS_WARNING("Device storage file looks invalid!");
470 return NS_OK;
471 }
472 ContentChild::GetSingleton()
473 ->SendFilePathUpdateNotify(file->mStorageType,
474 file->mStorageName,
475 file->mPath,
476 NS_ConvertUTF16toUTF8(aData));
477 } else {
478 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
479 obs->NotifyObservers(aSubject, "file-watcher-update", aData);
480 }
481 return NS_OK;
482 }
484 class IOEventComplete : public nsRunnable
485 {
486 public:
487 IOEventComplete(DeviceStorageFile *aFile, const char *aType)
488 : mFile(aFile)
489 , mType(aType)
490 {
491 }
493 ~IOEventComplete() {}
495 NS_IMETHOD Run()
496 {
497 MOZ_ASSERT(NS_IsMainThread());
498 nsString data;
499 CopyASCIItoUTF16(mType, data);
500 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
502 obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
504 DeviceStorageUsedSpaceCache* usedSpaceCache
505 = DeviceStorageUsedSpaceCache::CreateOrGet();
506 MOZ_ASSERT(usedSpaceCache);
507 usedSpaceCache->Invalidate(mFile->mStorageName);
508 return NS_OK;
509 }
511 private:
512 nsRefPtr<DeviceStorageFile> mFile;
513 nsCString mType;
514 };
516 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
517 const nsAString& aStorageName,
518 const nsAString& aRootDir,
519 const nsAString& aPath)
520 : mStorageType(aStorageType)
521 , mStorageName(aStorageName)
522 , mRootDir(aRootDir)
523 , mPath(aPath)
524 , mEditable(false)
525 , mLength(UINT64_MAX)
526 , mLastModifiedDate(UINT64_MAX)
527 {
528 Init();
529 AppendRelativePath(mRootDir);
530 if (!mPath.EqualsLiteral("")) {
531 AppendRelativePath(mPath);
532 }
533 NormalizeFilePath();
534 }
536 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
537 const nsAString& aStorageName,
538 const nsAString& aPath)
539 : mStorageType(aStorageType)
540 , mStorageName(aStorageName)
541 , mPath(aPath)
542 , mEditable(false)
543 , mLength(UINT64_MAX)
544 , mLastModifiedDate(UINT64_MAX)
545 {
546 Init();
547 AppendRelativePath(aPath);
548 NormalizeFilePath();
549 }
551 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
552 const nsAString& aStorageName)
553 : mStorageType(aStorageType)
554 , mStorageName(aStorageName)
555 , mEditable(false)
556 , mLength(UINT64_MAX)
557 , mLastModifiedDate(UINT64_MAX)
558 {
559 Init();
560 }
562 void
563 DeviceStorageFile::Dump(const char* label)
564 {
565 nsString path;
566 if (mFile) {
567 mFile->GetPath(path);
568 } else {
569 path = NS_LITERAL_STRING("(null)");
570 }
571 const char* ptStr;
572 if (XRE_GetProcessType() == GeckoProcessType_Default) {
573 ptStr = "parent";
574 } else {
575 ptStr = "child";
576 }
578 printf_stderr("DSF (%s) %s: mStorageType '%s' mStorageName '%s' "
579 "mRootDir '%s' mPath '%s' mFile->GetPath '%s'\n",
580 ptStr, label,
581 NS_LossyConvertUTF16toASCII(mStorageType).get(),
582 NS_LossyConvertUTF16toASCII(mStorageName).get(),
583 NS_LossyConvertUTF16toASCII(mRootDir).get(),
584 NS_LossyConvertUTF16toASCII(mPath).get(),
585 NS_LossyConvertUTF16toASCII(path).get());
586 }
588 void
589 DeviceStorageFile::Init()
590 {
591 DeviceStorageFile::GetRootDirectoryForType(mStorageType,
592 mStorageName,
593 getter_AddRefs(mFile));
595 DebugOnly<DeviceStorageTypeChecker*> typeChecker
596 = DeviceStorageTypeChecker::CreateOrGet();
597 MOZ_ASSERT(typeChecker);
598 }
600 // The OverrideRootDir is needed to facilitate testing of the
601 // device.storage.overrideRootDir preference. The preference is normally
602 // only read once during initialization, but since the test environment has
603 // no convenient way to restart, we use a pref watcher instead.
604 class OverrideRootDir MOZ_FINAL : public nsIObserver
605 {
606 public:
607 NS_DECL_ISUPPORTS
608 NS_DECL_NSIOBSERVER
610 static OverrideRootDir* GetSingleton();
611 ~OverrideRootDir();
612 void Init();
613 private:
614 static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
615 };
617 NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver)
619 mozilla::StaticRefPtr<OverrideRootDir>
620 OverrideRootDir::sSingleton;
622 OverrideRootDir*
623 OverrideRootDir::GetSingleton()
624 {
625 if (sSingleton) {
626 return sSingleton;
627 }
628 // Preference changes are automatically forwarded from parent to child
629 // in ContentParent::Observe, so we'll see the change in both the parent
630 // and the child process.
632 sSingleton = new OverrideRootDir();
633 Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
634 ClearOnShutdown(&sSingleton);
636 return sSingleton;
637 }
639 OverrideRootDir::~OverrideRootDir()
640 {
641 Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
642 }
644 NS_IMETHODIMP
645 OverrideRootDir::Observe(nsISupports *aSubject,
646 const char *aTopic,
647 const char16_t *aData)
648 {
649 MOZ_ASSERT(NS_IsMainThread());
651 if (sSingleton) {
652 sSingleton->Init();
653 }
654 return NS_OK;
655 }
657 void
658 OverrideRootDir::Init()
659 {
660 MOZ_ASSERT(NS_IsMainThread());
662 if (!sDirs) {
663 return;
664 }
666 if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
667 nsCOMPtr<nsIProperties> dirService
668 = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
669 MOZ_ASSERT(dirService);
670 dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
671 getter_AddRefs(sDirs->overrideRootDir));
672 if (sDirs->overrideRootDir) {
673 sDirs->overrideRootDir->AppendRelativeNativePath(
674 NS_LITERAL_CSTRING("device-storage-testing"));
675 }
676 } else {
677 // For users running on desktop, it's convenient to be able to override
678 // all of the directories to point to a single tree, much like what happens
679 // on a real device.
680 const nsAdoptingString& overrideRootDir =
681 mozilla::Preferences::GetString("device.storage.overrideRootDir");
682 if (overrideRootDir && overrideRootDir.Length() > 0) {
683 NS_NewLocalFile(overrideRootDir, false,
684 getter_AddRefs(sDirs->overrideRootDir));
685 } else {
686 sDirs->overrideRootDir = nullptr;
687 }
688 }
690 if (sDirs->overrideRootDir) {
691 if (XRE_GetProcessType() == GeckoProcessType_Default) {
692 // Only the parent process can create directories. In testing, because
693 // the preference is updated after startup, its entirely possible that
694 // the preference updated notification will be received by a child
695 // prior to the parent.
696 nsresult rv
697 = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
698 nsString path;
699 sDirs->overrideRootDir->GetPath(path);
700 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
701 nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
702 NS_LossyConvertUTF16toASCII(path).get());
703 NS_WARNING(msg.get());
704 }
705 }
706 sDirs->overrideRootDir->Normalize();
707 }
708 }
710 // Directories which don't depend on a volume should be calculated once
711 // here. Directories which depend on the root directory of a volume
712 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
713 static void
714 InitDirs()
715 {
716 if (sDirs) {
717 return;
718 }
719 MOZ_ASSERT(NS_IsMainThread());
720 sDirs = new GlobalDirs;
721 ClearOnShutdown(&sDirs);
723 nsCOMPtr<nsIProperties> dirService
724 = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
725 MOZ_ASSERT(dirService);
727 #if !defined(MOZ_WIDGET_GONK)
729 #if defined (MOZ_WIDGET_COCOA)
730 dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
731 NS_GET_IID(nsIFile),
732 getter_AddRefs(sDirs->pictures));
733 dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
734 NS_GET_IID(nsIFile),
735 getter_AddRefs(sDirs->videos));
736 dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
737 NS_GET_IID(nsIFile),
738 getter_AddRefs(sDirs->music));
739 #elif defined (XP_UNIX)
740 dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
741 NS_GET_IID(nsIFile),
742 getter_AddRefs(sDirs->pictures));
743 dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
744 NS_GET_IID(nsIFile),
745 getter_AddRefs(sDirs->videos));
746 dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
747 NS_GET_IID(nsIFile),
748 getter_AddRefs(sDirs->music));
749 #elif defined (XP_WIN)
750 dirService->Get(NS_WIN_PICTURES_DIR,
751 NS_GET_IID(nsIFile),
752 getter_AddRefs(sDirs->pictures));
753 dirService->Get(NS_WIN_VIDEOS_DIR,
754 NS_GET_IID(nsIFile),
755 getter_AddRefs(sDirs->videos));
756 dirService->Get(NS_WIN_MUSIC_DIR,
757 NS_GET_IID(nsIFile),
758 getter_AddRefs(sDirs->music));
759 #endif
761 // Eventually, on desktop, we want to do something smarter -- for example,
762 // detect when an sdcard is inserted, and use that instead of this.
763 dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
764 getter_AddRefs(sDirs->sdcard));
765 if (sDirs->sdcard) {
766 sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
767 }
768 #endif // !MOZ_WIDGET_GONK
770 #ifdef MOZ_WIDGET_GONK
771 NS_NewLocalFile(NS_LITERAL_STRING("/data"),
772 false,
773 getter_AddRefs(sDirs->apps));
774 #else
775 dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
776 getter_AddRefs(sDirs->apps));
777 if (sDirs->apps) {
778 sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
779 }
780 #endif
782 if (XRE_GetProcessType() == GeckoProcessType_Default) {
783 NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
784 if (sDirs->crashes) {
785 sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports"));
786 }
787 } else {
788 // NS_GetSpecialDirectory("UAppData") fails in content processes because
789 // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
790 #ifdef MOZ_WIDGET_GONK
791 NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
792 false,
793 getter_AddRefs(sDirs->crashes));
794 #endif
795 }
797 OverrideRootDir::GetSingleton()->Init();
798 }
800 void
801 DeviceStorageFile::GetFullPath(nsAString &aFullPath)
802 {
803 aFullPath.Truncate();
804 if (!mStorageName.EqualsLiteral("")) {
805 aFullPath.AppendLiteral("/");
806 aFullPath.Append(mStorageName);
807 aFullPath.AppendLiteral("/");
808 }
809 if (!mRootDir.EqualsLiteral("")) {
810 aFullPath.Append(mRootDir);
811 aFullPath.AppendLiteral("/");
812 }
813 aFullPath.Append(mPath);
814 }
817 // Directories which don't depend on a volume should be calculated once
818 // in InitDirs. Directories which depend on the root directory of a volume
819 // should be calculated in this method.
820 void
821 DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
822 const nsAString& aStorageName,
823 nsIFile** aFile)
824 {
825 nsCOMPtr<nsIFile> f;
826 *aFile = nullptr;
827 bool allowOverride = true;
829 InitDirs();
831 #ifdef MOZ_WIDGET_GONK
832 nsString volMountPoint;
833 if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
834 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
835 NS_ENSURE_TRUE_VOID(vs);
836 nsresult rv;
837 nsCOMPtr<nsIVolume> vol;
838 rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol));
839 NS_ENSURE_SUCCESS_VOID(rv);
840 vol->GetMountPoint(volMountPoint);
841 }
842 #endif
844 // Picture directory
845 if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
846 #ifdef MOZ_WIDGET_GONK
847 NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
848 #else
849 f = sDirs->pictures;
850 #endif
851 }
853 // Video directory
854 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
855 #ifdef MOZ_WIDGET_GONK
856 NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
857 #else
858 f = sDirs->videos;
859 #endif
860 }
862 // Music directory
863 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
864 #ifdef MOZ_WIDGET_GONK
865 NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
866 #else
867 f = sDirs->music;
868 #endif
869 }
871 // Apps directory
872 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
873 f = sDirs->apps;
874 allowOverride = false;
875 }
877 // default SDCard
878 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
879 #ifdef MOZ_WIDGET_GONK
880 NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
881 #else
882 f = sDirs->sdcard;
883 #endif
884 }
886 // crash reports directory.
887 else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
888 f = sDirs->crashes;
889 allowOverride = false;
890 } else {
891 // Not a storage type that we recognize. Return null
892 return;
893 }
895 // In testing, we default all device storage types to a temp directory.
896 // sDirs->overrideRootDir will only have been initialized (in InitDirs)
897 // if the preference device.storage.testing was set to true, or if
898 // device.storage.overrideRootDir is set. We can't test the preferences
899 // directly here, since we may not be on the main thread.
900 if (allowOverride && sDirs->overrideRootDir) {
901 f = sDirs->overrideRootDir;
902 }
904 if (f) {
905 f->Clone(aFile);
906 }
907 }
909 //static
910 already_AddRefed<DeviceStorageFile>
911 DeviceStorageFile::CreateUnique(nsAString& aFileName,
912 uint32_t aFileType,
913 uint32_t aFileAttributes)
914 {
915 DeviceStorageTypeChecker* typeChecker
916 = DeviceStorageTypeChecker::CreateOrGet();
917 MOZ_ASSERT(typeChecker);
919 nsString storageType;
920 typeChecker->GetTypeFromFileName(aFileName, storageType);
922 nsString storageName;
923 nsString storagePath;
924 if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) {
925 return nullptr;
926 }
927 if (storageName.IsEmpty()) {
928 nsDOMDeviceStorage::GetDefaultStorageName(storageType, storageName);
929 }
930 nsRefPtr<DeviceStorageFile> dsf =
931 new DeviceStorageFile(storageType, storageName, storagePath);
932 if (!dsf->mFile) {
933 return nullptr;
934 }
936 nsresult rv = dsf->mFile->CreateUnique(aFileType, aFileAttributes);
937 NS_ENSURE_SUCCESS(rv, nullptr);
939 // CreateUnique may cause the filename to change. So we need to update mPath
940 // to reflect that.
941 nsString leafName;
942 dsf->mFile->GetLeafName(leafName);
944 int32_t lastSlashIndex = dsf->mPath.RFindChar('/');
945 if (lastSlashIndex == kNotFound) {
946 dsf->mPath.Assign(leafName);
947 } else {
948 // Include the last '/'
949 dsf->mPath = Substring(dsf->mPath, 0, lastSlashIndex + 1);
950 dsf->mPath.Append(leafName);
951 }
953 return dsf.forget();
954 }
956 void
957 DeviceStorageFile::SetPath(const nsAString& aPath) {
958 mPath.Assign(aPath);
959 NormalizeFilePath();
960 }
962 void
963 DeviceStorageFile::SetEditable(bool aEditable) {
964 mEditable = aEditable;
965 }
967 // we want to make sure that the names of file can't reach
968 // outside of the type of storage the user asked for.
969 bool
970 DeviceStorageFile::IsSafePath()
971 {
972 return IsSafePath(mRootDir) && IsSafePath(mPath);
973 }
975 bool
976 DeviceStorageFile::IsSafePath(const nsAString& aPath)
977 {
978 nsAString::const_iterator start, end;
979 aPath.BeginReading(start);
980 aPath.EndReading(end);
982 // if the path is a '~' or starts with '~/', return false.
983 NS_NAMED_LITERAL_STRING(tilde, "~");
984 NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
985 if (aPath.Equals(tilde) ||
986 StringBeginsWith(aPath, tildeSlash)) {
987 NS_WARNING("Path name starts with tilde!");
988 return false;
989 }
990 // split on /. if any token is "", ., or .., return false.
991 NS_ConvertUTF16toUTF8 cname(aPath);
992 char* buffer = cname.BeginWriting();
993 const char* token;
995 while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
996 if (PL_strcmp(token, "") == 0 ||
997 PL_strcmp(token, ".") == 0 ||
998 PL_strcmp(token, "..") == 0 ) {
999 return false;
1000 }
1001 }
1002 return true;
1003 }
1005 void
1006 DeviceStorageFile::NormalizeFilePath() {
1007 FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
1008 }
1010 void
1011 DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
1012 if (!mFile) {
1013 return;
1014 }
1015 if (!IsSafePath(aPath)) {
1016 // All of the APIs (in the child) do checks to verify that the path is
1017 // valid and return PERMISSION_DENIED if a non-safe path is entered.
1018 // This check is done in the parent and prevents a compromised
1019 // child from bypassing the check. It shouldn't be possible for this
1020 // code path to be taken with a non-compromised child.
1021 NS_WARNING("Unsafe path detected - ignoring");
1022 NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
1023 return;
1024 }
1025 nsString localPath;
1026 FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
1027 mFile->AppendRelativePath(localPath);
1028 }
1030 nsresult
1031 DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
1032 {
1033 ScopedPRFileDesc fd;
1034 nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE,
1035 0660, &fd.rwget());
1036 NS_ENSURE_SUCCESS(rv, rv);
1038 // NOTE: The FileDescriptor::PlatformHandleType constructor returns a dup of
1039 // the file descriptor, so we don't need the original fd after this.
1040 // Our scoped file descriptor will automatically close fd.
1041 aFileDescriptor =
1042 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd));
1043 return NS_OK;
1044 }
1046 nsresult
1047 DeviceStorageFile::Write(nsIInputStream* aInputStream)
1048 {
1049 if (!aInputStream || !mFile) {
1050 return NS_ERROR_FAILURE;
1051 }
1053 nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
1054 if (NS_WARN_IF(NS_FAILED(rv))) {
1055 return rv;
1056 }
1058 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
1059 rv = NS_DispatchToMainThread(iocomplete);
1060 if (NS_WARN_IF(NS_FAILED(rv))) {
1061 return rv;
1062 }
1064 uint64_t bufSize = 0;
1065 aInputStream->Available(&bufSize);
1067 nsCOMPtr<nsIOutputStream> outputStream;
1068 NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
1070 if (!outputStream) {
1071 return NS_ERROR_FAILURE;
1072 }
1074 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
1075 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
1076 outputStream,
1077 4096*4);
1078 NS_ENSURE_SUCCESS(rv, rv);
1080 while (bufSize) {
1081 uint32_t wrote;
1082 rv = bufferedOutputStream->WriteFrom(
1083 aInputStream,
1084 static_cast<uint32_t>(std::min<uint64_t>(bufSize, UINT32_MAX)),
1085 &wrote);
1086 if (NS_FAILED(rv)) {
1087 break;
1088 }
1089 bufSize -= wrote;
1090 }
1092 iocomplete = new IOEventComplete(this, "modified");
1093 rv = NS_DispatchToMainThread(iocomplete);
1094 if (NS_WARN_IF(NS_FAILED(rv))) {
1095 return rv;
1096 }
1098 bufferedOutputStream->Close();
1099 outputStream->Close();
1100 if (NS_WARN_IF(NS_FAILED(rv))) {
1101 return rv;
1102 }
1103 return NS_OK;
1104 }
1106 nsresult
1107 DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
1108 {
1109 if (!mFile) {
1110 return NS_ERROR_FAILURE;
1111 }
1113 nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
1114 if (NS_WARN_IF(NS_FAILED(rv))) {
1115 return rv;
1116 }
1118 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
1119 rv = NS_DispatchToMainThread(iocomplete);
1120 if (NS_WARN_IF(NS_FAILED(rv))) {
1121 return rv;
1122 }
1124 nsCOMPtr<nsIOutputStream> outputStream;
1125 NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
1127 if (!outputStream) {
1128 return NS_ERROR_FAILURE;
1129 }
1131 uint32_t wrote;
1132 outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
1133 outputStream->Close();
1135 iocomplete = new IOEventComplete(this, "modified");
1136 rv = NS_DispatchToMainThread(iocomplete);
1137 if (NS_WARN_IF(NS_FAILED(rv))) {
1138 return rv;
1139 }
1141 if (aBits.Length() != wrote) {
1142 return NS_ERROR_FAILURE;
1143 }
1144 return NS_OK;
1145 }
1147 nsresult
1148 DeviceStorageFile::Remove()
1149 {
1150 MOZ_ASSERT(!NS_IsMainThread());
1152 if (!mFile) {
1153 return NS_ERROR_FAILURE;
1154 }
1156 bool check;
1157 nsresult rv = mFile->Exists(&check);
1158 if (NS_FAILED(rv)) {
1159 return rv;
1160 }
1162 if (!check) {
1163 return NS_OK;
1164 }
1166 rv = mFile->Remove(true);
1167 if (NS_WARN_IF(NS_FAILED(rv))) {
1168 return rv;
1169 }
1171 nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "deleted");
1172 return NS_DispatchToMainThread(iocomplete);
1173 }
1175 nsresult
1176 DeviceStorageFile::CalculateMimeType()
1177 {
1178 MOZ_ASSERT(NS_IsMainThread());
1180 nsAutoCString mimeType;
1181 nsCOMPtr<nsIMIMEService> mimeService =
1182 do_GetService(NS_MIMESERVICE_CONTRACTID);
1183 if (mimeService) {
1184 nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
1185 if (NS_FAILED(rv)) {
1186 mimeType.Truncate();
1187 return rv;
1188 }
1189 }
1191 mMimeType = NS_ConvertUTF8toUTF16(mimeType);
1192 return NS_OK;
1193 }
1195 nsresult
1196 DeviceStorageFile::CalculateSizeAndModifiedDate()
1197 {
1198 MOZ_ASSERT(!NS_IsMainThread());
1200 int64_t fileSize;
1201 nsresult rv = mFile->GetFileSize(&fileSize);
1202 NS_ENSURE_SUCCESS(rv, rv);
1204 mLength = fileSize;
1206 PRTime modDate;
1207 rv = mFile->GetLastModifiedTime(&modDate);
1208 NS_ENSURE_SUCCESS(rv, rv);
1210 mLastModifiedDate = modDate;
1211 return NS_OK;
1212 }
1214 void
1215 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
1216 PRTime aSince)
1217 {
1218 nsString fullRootPath;
1219 mFile->GetPath(fullRootPath);
1220 collectFilesInternal(aFiles, aSince, fullRootPath);
1221 }
1223 void
1224 DeviceStorageFile::collectFilesInternal(
1225 nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
1226 PRTime aSince,
1227 nsAString& aRootPath)
1228 {
1229 if (!mFile || !IsAvailable()) {
1230 return;
1231 }
1233 nsCOMPtr<nsISimpleEnumerator> e;
1234 mFile->GetDirectoryEntries(getter_AddRefs(e));
1236 if (!e) {
1237 return;
1238 }
1240 nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
1241 nsCOMPtr<nsIFile> f;
1243 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
1245 PRTime msecs;
1246 f->GetLastModifiedTime(&msecs);
1248 if (msecs < aSince) {
1249 continue;
1250 }
1252 bool isDir;
1253 f->IsDirectory(&isDir);
1255 bool isFile;
1256 f->IsFile(&isFile);
1258 nsString fullpath;
1259 nsresult rv = f->GetPath(fullpath);
1260 if (NS_FAILED(rv)) {
1261 continue;
1262 }
1264 if (!StringBeginsWith(fullpath, aRootPath)) {
1265 NS_ERROR("collectFiles returned a path that does not belong!");
1266 continue;
1267 }
1269 nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
1270 nsDependentSubstring newPath = Substring(fullpath, len);
1272 if (isDir) {
1273 DeviceStorageFile dsf(mStorageType, mStorageName, mRootDir, newPath);
1274 dsf.collectFilesInternal(aFiles, aSince, aRootPath);
1275 } else if (isFile) {
1276 nsRefPtr<DeviceStorageFile> dsf =
1277 new DeviceStorageFile(mStorageType, mStorageName, mRootDir, newPath);
1278 dsf->CalculateSizeAndModifiedDate();
1279 aFiles.AppendElement(dsf);
1280 }
1281 }
1282 }
1284 void
1285 DeviceStorageFile::AccumDiskUsage(uint64_t* aPicturesSoFar,
1286 uint64_t* aVideosSoFar,
1287 uint64_t* aMusicSoFar,
1288 uint64_t* aTotalSoFar)
1289 {
1290 if (!IsAvailable()) {
1291 return;
1292 }
1294 uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0;
1296 if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
1297 DeviceStorageUsedSpaceCache* usedSpaceCache =
1298 DeviceStorageUsedSpaceCache::CreateOrGet();
1299 MOZ_ASSERT(usedSpaceCache);
1300 nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName,
1301 aPicturesSoFar, aVideosSoFar,
1302 aMusicSoFar, aTotalSoFar);
1303 if (NS_SUCCEEDED(rv)) {
1304 return;
1305 }
1306 AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
1307 &musicUsage, &totalUsage);
1308 usedSpaceCache->SetUsedSizes(mStorageName, pictureUsage, videoUsage,
1309 musicUsage, totalUsage);
1310 } else {
1311 AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
1312 &musicUsage, &totalUsage);
1313 }
1315 *aPicturesSoFar += pictureUsage;
1316 *aVideosSoFar += videoUsage;
1317 *aMusicSoFar += musicUsage;
1318 *aTotalSoFar += totalUsage;
1319 }
1321 void
1322 DeviceStorageFile::AccumDirectoryUsage(nsIFile* aFile,
1323 uint64_t* aPicturesSoFar,
1324 uint64_t* aVideosSoFar,
1325 uint64_t* aMusicSoFar,
1326 uint64_t* aTotalSoFar)
1327 {
1328 if (!aFile) {
1329 return;
1330 }
1332 nsresult rv;
1333 nsCOMPtr<nsISimpleEnumerator> e;
1334 rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
1336 if (NS_FAILED(rv) || !e) {
1337 return;
1338 }
1340 nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
1341 MOZ_ASSERT(files);
1343 nsCOMPtr<nsIFile> f;
1344 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
1345 bool isDir;
1346 rv = f->IsDirectory(&isDir);
1347 if (NS_FAILED(rv)) {
1348 continue;
1349 }
1351 bool isFile;
1352 rv = f->IsFile(&isFile);
1353 if (NS_FAILED(rv)) {
1354 continue;
1355 }
1357 bool isLink;
1358 rv = f->IsSymlink(&isLink);
1359 if (NS_FAILED(rv)) {
1360 continue;
1361 }
1363 if (isLink) {
1364 // for now, lets just totally ignore symlinks.
1365 NS_WARNING("DirectoryDiskUsage ignores symlinks");
1366 } else if (isDir) {
1367 AccumDirectoryUsage(f, aPicturesSoFar, aVideosSoFar,
1368 aMusicSoFar, aTotalSoFar);
1369 } else if (isFile) {
1371 int64_t size;
1372 rv = f->GetFileSize(&size);
1373 if (NS_FAILED(rv)) {
1374 continue;
1375 }
1376 DeviceStorageTypeChecker* typeChecker
1377 = DeviceStorageTypeChecker::CreateOrGet();
1378 MOZ_ASSERT(typeChecker);
1379 nsString type;
1380 typeChecker->GetTypeFromFile(f, type);
1382 if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
1383 *aPicturesSoFar += size;
1384 }
1385 else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
1386 *aVideosSoFar += size;
1387 }
1388 else if (type.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
1389 *aMusicSoFar += size;
1390 }
1391 *aTotalSoFar += size;
1392 }
1393 }
1394 }
1396 void
1397 DeviceStorageFile::GetDiskFreeSpace(int64_t* aSoFar)
1398 {
1399 DeviceStorageTypeChecker* typeChecker
1400 = DeviceStorageTypeChecker::CreateOrGet();
1401 if (!typeChecker) {
1402 return;
1403 }
1404 if (!mFile || !IsAvailable()) {
1405 return;
1406 }
1408 int64_t storageAvail = 0;
1409 nsresult rv = mFile->GetDiskSpaceAvailable(&storageAvail);
1410 if (NS_SUCCEEDED(rv)) {
1411 *aSoFar += storageAvail;
1412 }
1413 }
1415 bool
1416 DeviceStorageFile::IsAvailable()
1417 {
1418 nsString status;
1419 GetStatus(status);
1420 return status.EqualsLiteral("available");
1421 }
1423 void
1424 DeviceStorageFile::DoFormat(nsAString& aStatus)
1425 {
1426 DeviceStorageTypeChecker* typeChecker
1427 = DeviceStorageTypeChecker::CreateOrGet();
1428 if (!typeChecker) {
1429 return;
1430 }
1431 if (!typeChecker->IsVolumeBased(mStorageType)) {
1432 aStatus.AssignLiteral("notVolume");
1433 return;
1434 }
1435 #ifdef MOZ_WIDGET_GONK
1436 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1437 NS_ENSURE_TRUE_VOID(vs);
1439 nsCOMPtr<nsIVolume> vol;
1440 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1441 NS_ENSURE_SUCCESS_VOID(rv);
1442 if (!vol) {
1443 return;
1444 }
1446 vol->Format();
1448 aStatus.AssignLiteral("formatting");
1449 #endif
1450 return;
1451 }
1453 void
1454 DeviceStorageFile::DoMount(nsAString& aStatus)
1455 {
1456 DeviceStorageTypeChecker* typeChecker
1457 = DeviceStorageTypeChecker::CreateOrGet();
1458 if (!typeChecker) {
1459 return;
1460 }
1461 if (!typeChecker->IsVolumeBased(mStorageType)) {
1462 aStatus.AssignLiteral("notVolume");
1463 return;
1464 }
1465 #ifdef MOZ_WIDGET_GONK
1466 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1467 NS_ENSURE_TRUE_VOID(vs);
1469 nsCOMPtr<nsIVolume> vol;
1470 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1471 NS_ENSURE_SUCCESS_VOID(rv);
1472 if (!vol) {
1473 return;
1474 }
1476 vol->Mount();
1478 aStatus.AssignLiteral("mounting");
1479 #endif
1480 return;
1481 }
1483 void
1484 DeviceStorageFile::DoUnmount(nsAString& aStatus)
1485 {
1486 DeviceStorageTypeChecker* typeChecker
1487 = DeviceStorageTypeChecker::CreateOrGet();
1488 if (!typeChecker) {
1489 return;
1490 }
1491 if (!typeChecker->IsVolumeBased(mStorageType)) {
1492 aStatus.AssignLiteral("notVolume");
1493 return;
1494 }
1495 #ifdef MOZ_WIDGET_GONK
1496 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1497 NS_ENSURE_TRUE_VOID(vs);
1499 nsCOMPtr<nsIVolume> vol;
1500 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1501 NS_ENSURE_SUCCESS_VOID(rv);
1502 if (!vol) {
1503 return;
1504 }
1506 vol->Unmount();
1508 aStatus.AssignLiteral("unmounting");
1509 #endif
1510 return;
1511 }
1513 void
1514 DeviceStorageFile::GetStatus(nsAString& aStatus)
1515 {
1516 aStatus.AssignLiteral("unavailable");
1518 DeviceStorageTypeChecker* typeChecker
1519 = DeviceStorageTypeChecker::CreateOrGet();
1520 if (!typeChecker) {
1521 return;
1522 }
1523 if (!typeChecker->IsVolumeBased(mStorageType)) {
1524 aStatus.AssignLiteral("available");
1525 return;
1526 }
1528 #ifdef MOZ_WIDGET_GONK
1529 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1530 NS_ENSURE_TRUE_VOID(vs);
1532 nsCOMPtr<nsIVolume> vol;
1533 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1534 NS_ENSURE_SUCCESS_VOID(rv);
1535 if (!vol) {
1536 return;
1537 }
1538 bool isMediaPresent;
1539 rv = vol->GetIsMediaPresent(&isMediaPresent);
1540 NS_ENSURE_SUCCESS_VOID(rv);
1541 if (!isMediaPresent) {
1542 return;
1543 }
1544 bool isSharing;
1545 rv = vol->GetIsSharing(&isSharing);
1546 NS_ENSURE_SUCCESS_VOID(rv);
1547 if (isSharing) {
1548 aStatus.AssignLiteral("shared");
1549 return;
1550 }
1551 bool isFormatting;
1552 rv = vol->GetIsFormatting(&isFormatting);
1553 NS_ENSURE_SUCCESS_VOID(rv);
1554 if (isFormatting) {
1555 aStatus.AssignLiteral("unavailable");
1556 return;
1557 }
1558 int32_t volState;
1559 rv = vol->GetState(&volState);
1560 NS_ENSURE_SUCCESS_VOID(rv);
1561 if (volState == nsIVolume::STATE_MOUNTED) {
1562 aStatus.AssignLiteral("available");
1563 }
1564 #endif
1565 }
1567 void
1568 DeviceStorageFile::GetStorageStatus(nsAString& aStatus)
1569 {
1570 aStatus.AssignLiteral("undefined");
1572 DeviceStorageTypeChecker* typeChecker
1573 = DeviceStorageTypeChecker::CreateOrGet();
1574 if (!typeChecker) {
1575 return;
1576 }
1577 if (!typeChecker->IsVolumeBased(mStorageType)) {
1578 aStatus.AssignLiteral("available");
1579 return;
1580 }
1582 #ifdef MOZ_WIDGET_GONK
1583 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1584 NS_ENSURE_TRUE_VOID(vs);
1586 nsCOMPtr<nsIVolume> vol;
1587 nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
1588 NS_ENSURE_SUCCESS_VOID(rv);
1589 if (!vol) {
1590 return;
1591 }
1593 int32_t volState;
1594 rv = vol->GetState(&volState);
1595 NS_ENSURE_SUCCESS_VOID(rv);
1596 aStatus.AssignASCII(mozilla::system::NS_VolumeStateStr(volState));
1597 #endif
1598 }
1600 NS_IMPL_ISUPPORTS0(DeviceStorageFile)
1602 static void
1603 RegisterForSDCardChanges(nsIObserver* aObserver)
1604 {
1605 #ifdef MOZ_WIDGET_GONK
1606 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1607 obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
1608 #endif
1609 }
1611 static void
1612 UnregisterForSDCardChanges(nsIObserver* aObserver)
1613 {
1614 #ifdef MOZ_WIDGET_GONK
1615 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1616 obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
1617 #endif
1618 }
1620 void
1621 nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
1622 const nsAString& aStorageName)
1623 {
1624 MOZ_ASSERT(NS_IsMainThread());
1626 nsCOMPtr<nsIFile> f;
1627 DeviceStorageFile::GetRootDirectoryForType(aStorageType,
1628 aStorageName,
1629 getter_AddRefs(f));
1630 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1631 obs->AddObserver(this, "file-watcher-update", false);
1632 obs->AddObserver(this, "disk-space-watcher", false);
1633 mRootDirectory = f;
1634 mStorageType = aStorageType;
1635 mStorageName = aStorageName;
1636 }
1638 JS::Value
1639 InterfaceToJsval(nsPIDOMWindow* aWindow,
1640 nsISupports* aObject,
1641 const nsIID* aIID)
1642 {
1643 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
1644 if (!sgo) {
1645 return JS::NullValue();
1646 }
1648 JSObject *unrootedScopeObj = sgo->GetGlobalJSObject();
1649 NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue());
1650 JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj);
1651 JS::Rooted<JS::Value> someJsVal(runtime);
1652 nsresult rv;
1654 { // Protect someJsVal from moving GC in ~JSAutoCompartment
1655 AutoJSContext cx;
1657 JS::Rooted<JSObject*> scopeObj(cx, unrootedScopeObj);
1658 JSAutoCompartment ac(cx, scopeObj);
1660 rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
1661 }
1662 if (NS_FAILED(rv)) {
1663 return JS::NullValue();
1664 }
1666 return someJsVal;
1667 }
1669 JS::Value
1670 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
1671 {
1672 MOZ_ASSERT(NS_IsMainThread());
1673 MOZ_ASSERT(aWindow);
1675 if (!aFile) {
1676 return JSVAL_NULL;
1677 }
1679 if (aFile->mEditable) {
1680 // TODO - needs janv's file handle support.
1681 return JSVAL_NULL;
1682 }
1684 nsString fullPath;
1685 aFile->GetFullPath(fullPath);
1687 // This check is useful to know if somewhere the DeviceStorageFile
1688 // has not been properly set. Mimetype is not checked because it can be
1689 // empty.
1690 MOZ_ASSERT(aFile->mLength != UINT64_MAX);
1691 MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX);
1693 nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, aFile->mMimeType,
1694 aFile->mLength, aFile->mFile,
1695 aFile->mLastModifiedDate);
1696 return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
1697 }
1699 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
1700 {
1701 MOZ_ASSERT(NS_IsMainThread());
1702 MOZ_ASSERT(aWindow);
1704 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
1705 if (!sgo) {
1706 return JSVAL_NULL;
1707 }
1709 nsIScriptContext *scriptContext = sgo->GetScriptContext();
1710 if (!scriptContext) {
1711 return JSVAL_NULL;
1712 }
1714 AutoPushJSContext cx(scriptContext->GetNativeContext());
1715 if (!cx) {
1716 return JSVAL_NULL;
1717 }
1719 JS::Rooted<JS::Value> result(cx);
1720 if (!xpc::StringToJsval(cx, aString, &result)) {
1721 return JSVAL_NULL;
1722 }
1724 return result;
1725 }
1727 class DeviceStorageCursorRequest MOZ_FINAL
1728 : public nsIContentPermissionRequest
1729 , public PCOMContentPermissionRequestChild
1730 {
1731 public:
1732 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1733 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest,
1734 nsIContentPermissionRequest)
1736 NS_FORWARD_NSICONTENTPERMISSIONREQUEST(mCursor->);
1738 DeviceStorageCursorRequest(nsDOMDeviceStorageCursor* aCursor)
1739 : mCursor(aCursor) { }
1741 ~DeviceStorageCursorRequest() {}
1743 bool Recv__delete__(const bool& allow,
1744 const InfallibleTArray<PermissionChoice>& choices)
1745 {
1746 MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice");
1747 if (allow) {
1748 Allow(JS::UndefinedHandleValue);
1749 }
1750 else {
1751 Cancel();
1752 }
1753 return true;
1754 }
1756 void IPDLRelease()
1757 {
1758 Release();
1759 }
1761 private:
1762 nsRefPtr<nsDOMDeviceStorageCursor> mCursor;
1763 };
1765 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest)
1766 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
1767 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
1768 NS_INTERFACE_MAP_END
1770 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest)
1771 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest)
1773 NS_IMPL_CYCLE_COLLECTION(DeviceStorageCursorRequest,
1774 mCursor)
1777 class PostErrorEvent : public nsRunnable
1778 {
1779 public:
1780 PostErrorEvent(already_AddRefed<DOMRequest> aRequest, const char* aMessage)
1781 : mRequest(aRequest)
1782 {
1783 CopyASCIItoUTF16(aMessage, mError);
1784 }
1786 PostErrorEvent(DOMRequest* aRequest, const char* aMessage)
1787 : mRequest(aRequest)
1788 {
1789 CopyASCIItoUTF16(aMessage, mError);
1790 }
1792 ~PostErrorEvent() {}
1794 NS_IMETHOD Run()
1795 {
1796 MOZ_ASSERT(NS_IsMainThread());
1797 if (!mRequest->GetOwner()) {
1798 return NS_OK;
1799 }
1800 mRequest->FireError(mError);
1801 mRequest = nullptr;
1802 return NS_OK;
1803 }
1805 private:
1806 nsRefPtr<DOMRequest> mRequest;
1807 nsString mError;
1808 };
1810 ContinueCursorEvent::ContinueCursorEvent(already_AddRefed<DOMRequest> aRequest)
1811 : mRequest(aRequest)
1812 {
1813 }
1815 ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest)
1816 : mRequest(aRequest)
1817 {
1818 }
1820 already_AddRefed<DeviceStorageFile>
1821 ContinueCursorEvent::GetNextFile()
1822 {
1823 MOZ_ASSERT(NS_IsMainThread());
1825 nsDOMDeviceStorageCursor* cursor
1826 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
1827 nsString cursorStorageType;
1828 cursor->GetStorageType(cursorStorageType);
1830 DeviceStorageTypeChecker* typeChecker
1831 = DeviceStorageTypeChecker::CreateOrGet();
1832 if (!typeChecker) {
1833 return nullptr;
1834 }
1836 while (cursor->mFiles.Length() > 0) {
1837 nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
1838 cursor->mFiles.RemoveElementAt(0);
1839 if (!typeChecker->Check(cursorStorageType, file->mFile)) {
1840 continue;
1841 }
1843 file->CalculateMimeType();
1844 return file.forget();
1845 }
1847 return nullptr;
1848 }
1850 ContinueCursorEvent::~ContinueCursorEvent() {}
1852 void
1853 ContinueCursorEvent::Continue()
1854 {
1855 if (XRE_GetProcessType() == GeckoProcessType_Default) {
1856 DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
1857 MOZ_ASSERT(NS_SUCCEEDED(rv));
1858 return;
1859 }
1861 nsRefPtr<DeviceStorageFile> file = GetNextFile();
1863 if (!file) {
1864 // done with enumeration.
1865 DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
1866 MOZ_ASSERT(NS_SUCCEEDED(rv));
1867 return;
1868 }
1870 nsDOMDeviceStorageCursor* cursor
1871 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
1872 nsString cursorStorageType;
1873 cursor->GetStorageType(cursorStorageType);
1875 DeviceStorageRequestChild* child
1876 = new DeviceStorageRequestChild(mRequest, file);
1877 child->SetCallback(cursor);
1878 DeviceStorageGetParams params(cursorStorageType,
1879 file->mStorageName,
1880 file->mRootDir,
1881 file->mPath);
1882 ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
1883 params);
1884 mRequest = nullptr;
1885 }
1887 NS_IMETHODIMP
1888 ContinueCursorEvent::Run()
1889 {
1890 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
1891 if (!window) {
1892 return NS_OK;
1893 }
1895 nsRefPtr<DeviceStorageFile> file = GetNextFile();
1897 nsDOMDeviceStorageCursor* cursor
1898 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
1900 AutoJSContext cx;
1901 JS::Rooted<JS::Value> val(cx, nsIFileToJsval(window, file));
1903 if (file) {
1904 cursor->mOkToCallContinue = true;
1905 cursor->FireSuccess(val);
1906 } else {
1907 cursor->FireDone();
1908 }
1909 mRequest = nullptr;
1910 return NS_OK;
1911 }
1913 class InitCursorEvent : public nsRunnable
1914 {
1915 public:
1916 InitCursorEvent(DOMRequest* aRequest, DeviceStorageFile* aFile)
1917 : mFile(aFile)
1918 , mRequest(aRequest)
1919 {
1920 }
1922 ~InitCursorEvent() {}
1924 NS_IMETHOD Run() {
1925 MOZ_ASSERT(!NS_IsMainThread());
1927 if (mFile->mFile) {
1928 bool check;
1929 mFile->mFile->IsDirectory(&check);
1930 if (!check) {
1931 nsCOMPtr<nsIRunnable> event =
1932 new PostErrorEvent(mRequest.forget(),
1933 POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
1934 return NS_DispatchToMainThread(event);
1935 }
1936 }
1938 nsDOMDeviceStorageCursor* cursor
1939 = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
1940 mFile->CollectFiles(cursor->mFiles, cursor->mSince);
1942 nsRefPtr<ContinueCursorEvent> event
1943 = new ContinueCursorEvent(mRequest.forget());
1944 event->Continue();
1946 return NS_OK;
1947 }
1950 private:
1951 nsRefPtr<DeviceStorageFile> mFile;
1952 nsRefPtr<DOMRequest> mRequest;
1953 };
1955 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorageCursor)
1956 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
1957 NS_INTERFACE_MAP_END_INHERITING(DOMCursor)
1959 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
1960 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
1962 nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsPIDOMWindow* aWindow,
1963 nsIPrincipal* aPrincipal,
1964 DeviceStorageFile* aFile,
1965 PRTime aSince)
1966 : DOMCursor(aWindow, nullptr)
1967 , mOkToCallContinue(false)
1968 , mSince(aSince)
1969 , mFile(aFile)
1970 , mPrincipal(aPrincipal)
1971 {
1972 }
1974 nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor()
1975 {
1976 }
1978 void
1979 nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
1980 {
1981 aType = mFile->mStorageType;
1982 }
1984 NS_IMETHODIMP
1985 nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
1986 {
1987 nsCString type;
1988 nsresult rv =
1989 DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
1990 NS_ENSURE_SUCCESS(rv, rv);
1992 nsTArray<nsString> emptyOptions;
1993 return CreatePermissionArray(type,
1994 NS_LITERAL_CSTRING("read"),
1995 emptyOptions,
1996 aTypes);
1997 }
1999 NS_IMETHODIMP
2000 nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
2001 {
2002 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
2003 return NS_OK;
2004 }
2006 NS_IMETHODIMP
2007 nsDOMDeviceStorageCursor::GetWindow(nsIDOMWindow * *aRequestingWindow)
2008 {
2009 NS_IF_ADDREF(*aRequestingWindow = GetOwner());
2010 return NS_OK;
2011 }
2013 NS_IMETHODIMP
2014 nsDOMDeviceStorageCursor::GetElement(nsIDOMElement * *aRequestingElement)
2015 {
2016 *aRequestingElement = nullptr;
2017 return NS_OK;
2018 }
2020 NS_IMETHODIMP
2021 nsDOMDeviceStorageCursor::Cancel()
2022 {
2023 nsCOMPtr<nsIRunnable> event
2024 = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
2025 return NS_DispatchToMainThread(event);
2026 }
2028 NS_IMETHODIMP
2029 nsDOMDeviceStorageCursor::Allow(JS::HandleValue aChoices)
2030 {
2031 MOZ_ASSERT(aChoices.isUndefined());
2033 if (!mFile->IsSafePath()) {
2034 nsCOMPtr<nsIRunnable> r
2035 = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
2036 return NS_DispatchToMainThread(r);
2037 }
2039 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2040 PDeviceStorageRequestChild* child
2041 = new DeviceStorageRequestChild(this, mFile);
2042 DeviceStorageEnumerationParams params(mFile->mStorageType,
2043 mFile->mStorageName,
2044 mFile->mRootDir,
2045 mSince);
2046 ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
2047 params);
2048 return NS_OK;
2049 }
2051 nsCOMPtr<nsIEventTarget> target
2052 = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
2053 MOZ_ASSERT(target);
2055 nsCOMPtr<nsIRunnable> event = new InitCursorEvent(this, mFile);
2056 target->Dispatch(event, NS_DISPATCH_NORMAL);
2057 return NS_OK;
2058 }
2060 void
2061 nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
2062 {
2063 if (!mOkToCallContinue) {
2064 aRv.Throw(NS_ERROR_UNEXPECTED);
2065 return;
2066 }
2068 if (mResult != JSVAL_VOID) {
2069 // We call onsuccess multiple times. Clear the last
2070 // result.
2071 mResult = JSVAL_VOID;
2072 mDone = false;
2073 }
2075 nsRefPtr<ContinueCursorEvent> event = new ContinueCursorEvent(this);
2076 event->Continue();
2078 mOkToCallContinue = false;
2079 }
2081 bool
2082 nsDOMDeviceStorageCursor::Recv__delete__(const bool& allow,
2083 const InfallibleTArray<PermissionChoice>& choices)
2084 {
2085 MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice");
2087 if (allow) {
2088 Allow(JS::UndefinedHandleValue);
2089 }
2090 else {
2091 Cancel();
2092 }
2093 return true;
2094 }
2096 void
2097 nsDOMDeviceStorageCursor::IPDLRelease()
2098 {
2099 Release();
2100 }
2102 void
2103 nsDOMDeviceStorageCursor::RequestComplete()
2104 {
2105 MOZ_ASSERT(!mOkToCallContinue);
2106 mOkToCallContinue = true;
2107 }
2109 class PostAvailableResultEvent : public nsRunnable
2110 {
2111 public:
2112 PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2113 : mFile(aFile)
2114 , mRequest(aRequest)
2115 {
2116 MOZ_ASSERT(mRequest);
2117 }
2119 ~PostAvailableResultEvent() {}
2121 NS_IMETHOD Run()
2122 {
2123 MOZ_ASSERT(NS_IsMainThread());
2124 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2125 if (!window) {
2126 return NS_OK;
2127 }
2129 nsString state = NS_LITERAL_STRING("unavailable");
2130 if (mFile) {
2131 mFile->GetStatus(state);
2132 }
2134 AutoJSContext cx;
2135 JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
2136 mRequest->FireSuccess(result);
2137 mRequest = nullptr;
2138 return NS_OK;
2139 }
2141 private:
2142 nsRefPtr<DeviceStorageFile> mFile;
2143 nsRefPtr<DOMRequest> mRequest;
2144 };
2146 class PostStatusResultEvent : public nsRunnable
2147 {
2148 public:
2149 PostStatusResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2150 : mFile(aFile)
2151 , mRequest(aRequest)
2152 {
2153 MOZ_ASSERT(mRequest);
2154 }
2156 ~PostStatusResultEvent() {}
2158 NS_IMETHOD Run()
2159 {
2160 MOZ_ASSERT(NS_IsMainThread());
2161 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2162 if (!window) {
2163 return NS_OK;
2164 }
2166 nsString state = NS_LITERAL_STRING("undefined");
2167 if (mFile) {
2168 mFile->GetStorageStatus(state);
2169 }
2171 AutoJSContext cx;
2172 JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
2173 mRequest->FireSuccess(result);
2174 mRequest = nullptr;
2175 return NS_OK;
2176 }
2178 private:
2179 nsRefPtr<DeviceStorageFile> mFile;
2180 nsRefPtr<DOMRequest> mRequest;
2181 };
2183 class PostFormatResultEvent : public nsRunnable
2184 {
2185 public:
2186 PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2187 : mFile(aFile)
2188 , mRequest(aRequest)
2189 {
2190 MOZ_ASSERT(mRequest);
2191 }
2193 ~PostFormatResultEvent() {}
2195 NS_IMETHOD Run()
2196 {
2197 MOZ_ASSERT(NS_IsMainThread());
2198 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2199 if (!window) {
2200 return NS_OK;
2201 }
2203 nsString state = NS_LITERAL_STRING("unavailable");
2204 if (mFile) {
2205 mFile->DoFormat(state);
2206 }
2208 AutoJSContext cx;
2209 JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
2210 mRequest->FireSuccess(result);
2211 mRequest = nullptr;
2212 return NS_OK;
2213 }
2215 private:
2216 nsRefPtr<DeviceStorageFile> mFile;
2217 nsRefPtr<DOMRequest> mRequest;
2218 };
2220 class PostMountResultEvent : public nsRunnable
2221 {
2222 public:
2223 PostMountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2224 : mFile(aFile)
2225 , mRequest(aRequest)
2226 {
2227 MOZ_ASSERT(mRequest);
2228 }
2230 ~PostMountResultEvent() {}
2232 NS_IMETHOD Run()
2233 {
2234 MOZ_ASSERT(NS_IsMainThread());
2235 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2236 if (!window) {
2237 return NS_OK;
2238 }
2240 nsString state = NS_LITERAL_STRING("unavailable");
2241 if (mFile) {
2242 mFile->DoMount(state);
2243 }
2245 AutoJSContext cx;
2246 JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
2247 mRequest->FireSuccess(result);
2248 mRequest = nullptr;
2249 return NS_OK;
2250 }
2252 private:
2253 nsRefPtr<DeviceStorageFile> mFile;
2254 nsRefPtr<DOMRequest> mRequest;
2255 };
2257 class PostUnmountResultEvent : public nsRunnable
2258 {
2259 public:
2260 PostUnmountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
2261 : mFile(aFile)
2262 , mRequest(aRequest)
2263 {
2264 MOZ_ASSERT(mRequest);
2265 }
2267 ~PostUnmountResultEvent() {}
2269 NS_IMETHOD Run()
2270 {
2271 MOZ_ASSERT(NS_IsMainThread());
2272 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2273 if (!window) {
2274 return NS_OK;
2275 }
2277 nsString state = NS_LITERAL_STRING("unavailable");
2278 if (mFile) {
2279 mFile->DoUnmount(state);
2280 }
2282 AutoJSContext cx;
2283 JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
2284 mRequest->FireSuccess(result);
2285 mRequest = nullptr;
2286 return NS_OK;
2287 }
2289 private:
2290 nsRefPtr<DeviceStorageFile> mFile;
2291 nsRefPtr<DOMRequest> mRequest;
2292 };
2294 class PostResultEvent : public nsRunnable
2295 {
2296 public:
2297 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2298 DeviceStorageFile* aFile)
2299 : mFile(aFile)
2300 , mRequest(aRequest)
2301 {
2302 MOZ_ASSERT(mRequest);
2303 }
2305 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2306 const nsAString & aPath)
2307 : mPath(aPath)
2308 , mRequest(aRequest)
2309 {
2310 MOZ_ASSERT(mRequest);
2311 }
2313 PostResultEvent(already_AddRefed<DOMRequest> aRequest,
2314 const uint64_t aValue)
2315 : mValue(aValue)
2316 , mRequest(aRequest)
2317 {
2318 MOZ_ASSERT(mRequest);
2319 }
2321 ~PostResultEvent() {}
2323 NS_IMETHOD Run()
2324 {
2325 MOZ_ASSERT(NS_IsMainThread());
2326 nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
2327 if (!window) {
2328 return NS_OK;
2329 }
2331 AutoJSContext cx;
2332 JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
2334 if (mFile) {
2335 result = nsIFileToJsval(window, mFile);
2336 } else if (mPath.Length()) {
2337 result = StringToJsval(window, mPath);
2338 }
2339 else {
2340 result = JS_NumberValue(double(mValue));
2341 }
2343 mRequest->FireSuccess(result);
2344 mRequest = nullptr;
2345 return NS_OK;
2346 }
2348 private:
2349 nsRefPtr<DeviceStorageFile> mFile;
2350 nsString mPath;
2351 uint64_t mValue;
2352 nsRefPtr<DOMRequest> mRequest;
2353 };
2355 class CreateFdEvent : public nsRunnable
2356 {
2357 public:
2358 CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor,
2359 already_AddRefed<DOMRequest> aRequest)
2360 : mDSFileDescriptor(aDSFileDescriptor)
2361 , mRequest(aRequest)
2362 {
2363 MOZ_ASSERT(mDSFileDescriptor);
2364 MOZ_ASSERT(mRequest);
2365 }
2367 NS_IMETHOD Run()
2368 {
2369 MOZ_ASSERT(!NS_IsMainThread());
2371 DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile;
2372 MOZ_ASSERT(dsFile);
2374 nsString fullPath;
2375 dsFile->GetFullPath(fullPath);
2376 MOZ_ASSERT(!fullPath.IsEmpty());
2378 bool check = false;
2379 dsFile->mFile->Exists(&check);
2380 if (check) {
2381 nsCOMPtr<nsIRunnable> event =
2382 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
2383 return NS_DispatchToMainThread(event);
2384 }
2386 nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor);
2388 if (NS_FAILED(rv)) {
2389 dsFile->mFile->Remove(false);
2391 nsCOMPtr<nsIRunnable> event =
2392 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2393 return NS_DispatchToMainThread(event);
2394 }
2396 nsCOMPtr<nsIRunnable> event =
2397 new PostResultEvent(mRequest.forget(), fullPath);
2398 return NS_DispatchToMainThread(event);
2399 }
2401 private:
2402 nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
2403 nsRefPtr<DOMRequest> mRequest;
2404 };
2406 class WriteFileEvent : public nsRunnable
2407 {
2408 public:
2409 WriteFileEvent(nsIDOMBlob* aBlob,
2410 DeviceStorageFile *aFile,
2411 already_AddRefed<DOMRequest> aRequest)
2412 : mBlob(aBlob)
2413 , mFile(aFile)
2414 , mRequest(aRequest)
2415 {
2416 MOZ_ASSERT(mFile);
2417 MOZ_ASSERT(mRequest);
2418 }
2420 ~WriteFileEvent() {}
2422 NS_IMETHOD Run()
2423 {
2424 MOZ_ASSERT(!NS_IsMainThread());
2426 nsCOMPtr<nsIInputStream> stream;
2427 mBlob->GetInternalStream(getter_AddRefs(stream));
2429 bool check = false;
2430 mFile->mFile->Exists(&check);
2431 if (check) {
2432 nsCOMPtr<nsIRunnable> event =
2433 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
2434 return NS_DispatchToMainThread(event);
2435 }
2437 nsresult rv = mFile->Write(stream);
2439 if (NS_FAILED(rv)) {
2440 mFile->mFile->Remove(false);
2442 nsCOMPtr<nsIRunnable> event =
2443 new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2444 return NS_DispatchToMainThread(event);
2445 }
2447 nsString fullPath;
2448 mFile->GetFullPath(fullPath);
2449 nsCOMPtr<nsIRunnable> event =
2450 new PostResultEvent(mRequest.forget(), fullPath);
2451 return NS_DispatchToMainThread(event);
2452 }
2454 private:
2455 nsCOMPtr<nsIDOMBlob> mBlob;
2456 nsRefPtr<DeviceStorageFile> mFile;
2457 nsRefPtr<DOMRequest> mRequest;
2458 };
2460 class ReadFileEvent : public nsRunnable
2461 {
2462 public:
2463 ReadFileEvent(DeviceStorageFile* aFile,
2464 already_AddRefed<DOMRequest> aRequest)
2465 : mFile(aFile)
2466 , mRequest(aRequest)
2467 {
2468 MOZ_ASSERT(mFile);
2469 MOZ_ASSERT(mRequest);
2470 mFile->CalculateMimeType();
2471 }
2473 ~ReadFileEvent() {}
2475 NS_IMETHOD Run()
2476 {
2477 MOZ_ASSERT(!NS_IsMainThread());
2479 nsCOMPtr<nsIRunnable> r;
2480 if (!mFile->mEditable) {
2481 bool check = false;
2482 mFile->mFile->Exists(&check);
2483 if (!check) {
2484 r = new PostErrorEvent(mRequest.forget(),
2485 POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
2486 }
2487 }
2489 if (!r) {
2490 nsresult rv = mFile->CalculateSizeAndModifiedDate();
2491 if (NS_FAILED(rv)) {
2492 r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
2493 }
2494 }
2496 if (!r) {
2497 r = new PostResultEvent(mRequest.forget(), mFile);
2498 }
2499 return NS_DispatchToMainThread(r);
2500 }
2502 private:
2503 nsRefPtr<DeviceStorageFile> mFile;
2504 nsRefPtr<DOMRequest> mRequest;
2505 };
2507 class DeleteFileEvent : public nsRunnable
2508 {
2509 public:
2510 DeleteFileEvent(DeviceStorageFile* aFile,
2511 already_AddRefed<DOMRequest> aRequest)
2512 : mFile(aFile)
2513 , mRequest(aRequest)
2514 {
2515 MOZ_ASSERT(mFile);
2516 MOZ_ASSERT(mRequest);
2517 }
2519 ~DeleteFileEvent() {}
2521 NS_IMETHOD Run()
2522 {
2523 MOZ_ASSERT(!NS_IsMainThread());
2524 mFile->Remove();
2526 nsCOMPtr<nsIRunnable> r;
2527 bool check = false;
2528 mFile->mFile->Exists(&check);
2529 if (check) {
2530 r = new PostErrorEvent(mRequest.forget(),
2531 POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
2532 }
2533 else {
2534 nsString fullPath;
2535 mFile->GetFullPath(fullPath);
2536 r = new PostResultEvent(mRequest.forget(), fullPath);
2537 }
2538 return NS_DispatchToMainThread(r);
2539 }
2541 private:
2542 nsRefPtr<DeviceStorageFile> mFile;
2543 nsRefPtr<DOMRequest> mRequest;
2544 };
2546 class UsedSpaceFileEvent : public nsRunnable
2547 {
2548 public:
2549 UsedSpaceFileEvent(DeviceStorageFile* aFile,
2550 already_AddRefed<DOMRequest> aRequest)
2551 : mFile(aFile)
2552 , mRequest(aRequest)
2553 {
2554 MOZ_ASSERT(mFile);
2555 MOZ_ASSERT(mRequest);
2556 }
2558 ~UsedSpaceFileEvent() {}
2560 NS_IMETHOD Run()
2561 {
2562 MOZ_ASSERT(!NS_IsMainThread());
2564 uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
2565 mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
2566 &musicUsage, &totalUsage);
2567 nsCOMPtr<nsIRunnable> r;
2568 if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
2569 r = new PostResultEvent(mRequest.forget(), picturesUsage);
2570 }
2571 else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
2572 r = new PostResultEvent(mRequest.forget(), videosUsage);
2573 }
2574 else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
2575 r = new PostResultEvent(mRequest.forget(), musicUsage);
2576 } else {
2577 r = new PostResultEvent(mRequest.forget(), totalUsage);
2578 }
2579 return NS_DispatchToMainThread(r);
2580 }
2582 private:
2583 nsRefPtr<DeviceStorageFile> mFile;
2584 nsRefPtr<DOMRequest> mRequest;
2585 };
2587 class FreeSpaceFileEvent : public nsRunnable
2588 {
2589 public:
2590 FreeSpaceFileEvent(DeviceStorageFile* aFile,
2591 already_AddRefed<DOMRequest> aRequest)
2592 : mFile(aFile)
2593 , mRequest(aRequest)
2594 {
2595 MOZ_ASSERT(mFile);
2596 MOZ_ASSERT(mRequest);
2597 }
2599 ~FreeSpaceFileEvent() {}
2601 NS_IMETHOD Run()
2602 {
2603 MOZ_ASSERT(!NS_IsMainThread());
2605 int64_t freeSpace = 0;
2606 if (mFile) {
2607 mFile->GetDiskFreeSpace(&freeSpace);
2608 }
2610 nsCOMPtr<nsIRunnable> r;
2611 r = new PostResultEvent(mRequest.forget(),
2612 static_cast<uint64_t>(freeSpace));
2613 return NS_DispatchToMainThread(r);
2614 }
2616 private:
2617 nsRefPtr<DeviceStorageFile> mFile;
2618 nsRefPtr<DOMRequest> mRequest;
2619 };
2621 class DeviceStorageRequest MOZ_FINAL
2622 : public nsIContentPermissionRequest
2623 , public nsIRunnable
2624 , public PCOMContentPermissionRequestChild
2625 {
2626 public:
2628 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2629 nsPIDOMWindow* aWindow,
2630 nsIPrincipal* aPrincipal,
2631 DeviceStorageFile* aFile,
2632 DOMRequest* aRequest,
2633 nsDOMDeviceStorage* aDeviceStorage)
2634 : mRequestType(aRequestType)
2635 , mWindow(aWindow)
2636 , mPrincipal(aPrincipal)
2637 , mFile(aFile)
2638 , mRequest(aRequest)
2639 , mDeviceStorage(aDeviceStorage)
2640 {
2641 MOZ_ASSERT(mWindow);
2642 MOZ_ASSERT(mPrincipal);
2643 MOZ_ASSERT(mFile);
2644 MOZ_ASSERT(mRequest);
2645 MOZ_ASSERT(mDeviceStorage);
2646 }
2648 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2649 nsPIDOMWindow* aWindow,
2650 nsIPrincipal* aPrincipal,
2651 DeviceStorageFile* aFile,
2652 DOMRequest* aRequest,
2653 nsIDOMBlob* aBlob = nullptr)
2654 : mRequestType(aRequestType)
2655 , mWindow(aWindow)
2656 , mPrincipal(aPrincipal)
2657 , mFile(aFile)
2658 , mRequest(aRequest)
2659 , mBlob(aBlob)
2660 {
2661 MOZ_ASSERT(mWindow);
2662 MOZ_ASSERT(mPrincipal);
2663 MOZ_ASSERT(mFile);
2664 MOZ_ASSERT(mRequest);
2665 }
2667 DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
2668 nsPIDOMWindow* aWindow,
2669 nsIPrincipal* aPrincipal,
2670 DeviceStorageFile* aFile,
2671 DOMRequest* aRequest,
2672 DeviceStorageFileDescriptor* aDSFileDescriptor)
2673 : mRequestType(aRequestType)
2674 , mWindow(aWindow)
2675 , mPrincipal(aPrincipal)
2676 , mFile(aFile)
2677 , mRequest(aRequest)
2678 , mDSFileDescriptor(aDSFileDescriptor)
2679 {
2680 MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD);
2681 MOZ_ASSERT(mWindow);
2682 MOZ_ASSERT(mPrincipal);
2683 MOZ_ASSERT(mFile);
2684 MOZ_ASSERT(mRequest);
2685 MOZ_ASSERT(mDSFileDescriptor);
2686 }
2688 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2689 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
2690 nsIContentPermissionRequest)
2692 NS_IMETHOD Run() {
2693 MOZ_ASSERT(NS_IsMainThread());
2695 if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
2696 Allow(JS::UndefinedHandleValue);
2697 return NS_OK;
2698 }
2700 if (XRE_GetProcessType() == GeckoProcessType_Content) {
2702 // because owner implements nsITabChild, we can assume that it is
2703 // the one and only TabChild.
2704 TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
2705 if (!child) {
2706 return NS_OK;
2707 }
2709 // Retain a reference so the object isn't deleted without IPDL's
2710 // knowledge. Corresponding release occurs in
2711 // DeallocPContentPermissionRequest.
2712 AddRef();
2714 nsCString type;
2715 nsresult rv = DeviceStorageTypeChecker::GetPermissionForType(
2716 mFile->mStorageType, type);
2717 if (NS_FAILED(rv)) {
2718 return rv;
2719 }
2720 nsCString access;
2721 rv = DeviceStorageTypeChecker::GetAccessForRequest(
2722 DeviceStorageRequestType(mRequestType), access);
2723 if (NS_FAILED(rv)) {
2724 return rv;
2725 }
2726 nsTArray<PermissionRequest> permArray;
2727 nsTArray<nsString> emptyOptions;
2728 permArray.AppendElement(PermissionRequest(type, access, emptyOptions));
2729 child->SendPContentPermissionRequestConstructor(
2730 this, permArray, IPC::Principal(mPrincipal));
2732 Sendprompt();
2733 return NS_OK;
2734 }
2736 nsCOMPtr<nsIContentPermissionPrompt> prompt
2737 = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
2738 if (prompt) {
2739 prompt->Prompt(this);
2740 }
2741 return NS_OK;
2742 }
2744 NS_IMETHODIMP GetTypes(nsIArray** aTypes)
2745 {
2746 nsCString type;
2747 nsresult rv =
2748 DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
2749 if (NS_FAILED(rv)) {
2750 return rv;
2751 }
2753 nsCString access;
2754 rv = DeviceStorageTypeChecker::GetAccessForRequest(
2755 DeviceStorageRequestType(mRequestType), access);
2756 if (NS_FAILED(rv)) {
2757 return rv;
2758 }
2760 nsTArray<nsString> emptyOptions;
2761 return CreatePermissionArray(type, access, emptyOptions, aTypes);
2762 }
2764 NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
2765 {
2766 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
2767 return NS_OK;
2768 }
2770 NS_IMETHOD GetWindow(nsIDOMWindow * *aRequestingWindow)
2771 {
2772 NS_IF_ADDREF(*aRequestingWindow = mWindow);
2773 return NS_OK;
2774 }
2776 NS_IMETHOD GetElement(nsIDOMElement * *aRequestingElement)
2777 {
2778 *aRequestingElement = nullptr;
2779 return NS_OK;
2780 }
2782 NS_IMETHOD Cancel()
2783 {
2784 nsCOMPtr<nsIRunnable> event
2785 = new PostErrorEvent(mRequest.forget(),
2786 POST_ERROR_EVENT_PERMISSION_DENIED);
2787 return NS_DispatchToMainThread(event);
2788 }
2790 NS_IMETHOD Allow(JS::HandleValue aChoices)
2791 {
2792 MOZ_ASSERT(NS_IsMainThread());
2793 MOZ_ASSERT(aChoices.isUndefined());
2795 if (!mRequest) {
2796 return NS_ERROR_FAILURE;
2797 }
2799 nsCOMPtr<nsIRunnable> r;
2801 switch(mRequestType) {
2802 case DEVICE_STORAGE_REQUEST_CREATEFD:
2803 {
2804 if (!mFile->mFile) {
2805 return NS_ERROR_FAILURE;
2806 }
2808 DeviceStorageTypeChecker* typeChecker
2809 = DeviceStorageTypeChecker::CreateOrGet();
2810 if (!typeChecker) {
2811 return NS_OK;
2812 }
2814 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
2815 r = new PostErrorEvent(mRequest.forget(),
2816 POST_ERROR_EVENT_ILLEGAL_TYPE);
2817 return NS_DispatchToCurrentThread(r);
2818 }
2820 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2822 DeviceStorageCreateFdParams params;
2823 params.type() = mFile->mStorageType;
2824 params.storageName() = mFile->mStorageName;
2825 params.relpath() = mFile->mPath;
2827 mFile->Dump("DeviceStorageCreateFdParams");
2829 PDeviceStorageRequestChild* child
2830 = new DeviceStorageRequestChild(mRequest, mFile,
2831 mDSFileDescriptor.get());
2832 ContentChild::GetSingleton()
2833 ->SendPDeviceStorageRequestConstructor(child, params);
2834 return NS_OK;
2835 }
2836 mDSFileDescriptor->mDSFile = mFile;
2837 r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget());
2838 break;
2839 }
2841 case DEVICE_STORAGE_REQUEST_CREATE:
2842 {
2843 if (!mBlob || !mFile->mFile) {
2844 return NS_ERROR_FAILURE;
2845 }
2847 DeviceStorageTypeChecker* typeChecker
2848 = DeviceStorageTypeChecker::CreateOrGet();
2849 if (!typeChecker) {
2850 return NS_OK;
2851 }
2853 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
2854 !typeChecker->Check(mFile->mStorageType, mBlob)) {
2855 r = new PostErrorEvent(mRequest.forget(),
2856 POST_ERROR_EVENT_ILLEGAL_TYPE);
2857 return NS_DispatchToCurrentThread(r);
2858 }
2860 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2861 BlobChild* actor
2862 = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
2863 if (!actor) {
2864 return NS_ERROR_FAILURE;
2865 }
2867 DeviceStorageAddParams params;
2868 params.blobChild() = actor;
2869 params.type() = mFile->mStorageType;
2870 params.storageName() = mFile->mStorageName;
2871 params.relpath() = mFile->mPath;
2873 PDeviceStorageRequestChild* child
2874 = new DeviceStorageRequestChild(mRequest, mFile);
2875 ContentChild::GetSingleton()
2876 ->SendPDeviceStorageRequestConstructor(child, params);
2877 return NS_OK;
2878 }
2879 r = new WriteFileEvent(mBlob, mFile, mRequest.forget());
2880 break;
2881 }
2883 case DEVICE_STORAGE_REQUEST_READ:
2884 case DEVICE_STORAGE_REQUEST_WRITE:
2885 {
2886 if (!mFile->mFile) {
2887 return NS_ERROR_FAILURE;
2888 }
2890 DeviceStorageTypeChecker* typeChecker
2891 = DeviceStorageTypeChecker::CreateOrGet();
2892 if (!typeChecker) {
2893 return NS_OK;
2894 }
2896 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
2897 r = new PostErrorEvent(mRequest.forget(),
2898 POST_ERROR_EVENT_ILLEGAL_TYPE);
2899 return NS_DispatchToCurrentThread(r);
2900 }
2902 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2903 PDeviceStorageRequestChild* child
2904 = new DeviceStorageRequestChild(mRequest, mFile);
2905 DeviceStorageGetParams params(mFile->mStorageType,
2906 mFile->mStorageName,
2907 mFile->mRootDir,
2908 mFile->mPath);
2909 ContentChild::GetSingleton()
2910 ->SendPDeviceStorageRequestConstructor(child, params);
2911 return NS_OK;
2912 }
2914 r = new ReadFileEvent(mFile, mRequest.forget());
2915 break;
2916 }
2918 case DEVICE_STORAGE_REQUEST_DELETE:
2919 {
2920 if (!mFile->mFile) {
2921 return NS_ERROR_FAILURE;
2922 }
2924 DeviceStorageTypeChecker* typeChecker
2925 = DeviceStorageTypeChecker::CreateOrGet();
2926 if (!typeChecker) {
2927 return NS_OK;
2928 }
2930 if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
2931 r = new PostErrorEvent(mRequest.forget(),
2932 POST_ERROR_EVENT_ILLEGAL_TYPE);
2933 return NS_DispatchToCurrentThread(r);
2934 }
2936 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2937 PDeviceStorageRequestChild* child
2938 = new DeviceStorageRequestChild(mRequest, mFile);
2939 DeviceStorageDeleteParams params(mFile->mStorageType,
2940 mFile->mStorageName,
2941 mFile->mPath);
2942 ContentChild::GetSingleton()
2943 ->SendPDeviceStorageRequestConstructor(child, params);
2944 return NS_OK;
2945 }
2946 r = new DeleteFileEvent(mFile, mRequest.forget());
2947 break;
2948 }
2950 case DEVICE_STORAGE_REQUEST_FREE_SPACE:
2951 {
2952 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2953 PDeviceStorageRequestChild* child
2954 = new DeviceStorageRequestChild(mRequest, mFile);
2955 DeviceStorageFreeSpaceParams params(mFile->mStorageType,
2956 mFile->mStorageName);
2957 ContentChild::GetSingleton()
2958 ->SendPDeviceStorageRequestConstructor(child, params);
2959 return NS_OK;
2960 }
2961 r = new FreeSpaceFileEvent(mFile, mRequest.forget());
2962 break;
2963 }
2965 case DEVICE_STORAGE_REQUEST_USED_SPACE:
2966 {
2967 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2968 PDeviceStorageRequestChild* child
2969 = new DeviceStorageRequestChild(mRequest, mFile);
2970 DeviceStorageUsedSpaceParams params(mFile->mStorageType,
2971 mFile->mStorageName);
2972 ContentChild::GetSingleton()
2973 ->SendPDeviceStorageRequestConstructor(child, params);
2974 return NS_OK;
2975 }
2976 // this needs to be dispatched to only one (1)
2977 // thread or we will do more work than required.
2978 DeviceStorageUsedSpaceCache* usedSpaceCache
2979 = DeviceStorageUsedSpaceCache::CreateOrGet();
2980 MOZ_ASSERT(usedSpaceCache);
2981 r = new UsedSpaceFileEvent(mFile, mRequest.forget());
2982 usedSpaceCache->Dispatch(r);
2983 return NS_OK;
2984 }
2986 case DEVICE_STORAGE_REQUEST_AVAILABLE:
2987 {
2988 if (XRE_GetProcessType() != GeckoProcessType_Default) {
2989 PDeviceStorageRequestChild* child
2990 = new DeviceStorageRequestChild(mRequest, mFile);
2991 DeviceStorageAvailableParams params(mFile->mStorageType,
2992 mFile->mStorageName);
2993 ContentChild::GetSingleton()
2994 ->SendPDeviceStorageRequestConstructor(child, params);
2995 return NS_OK;
2996 }
2997 r = new PostAvailableResultEvent(mFile, mRequest);
2998 return NS_DispatchToCurrentThread(r);
2999 }
3001 case DEVICE_STORAGE_REQUEST_STATUS:
3002 {
3003 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3004 PDeviceStorageRequestChild* child
3005 = new DeviceStorageRequestChild(mRequest, mFile);
3006 DeviceStorageStatusParams params(mFile->mStorageType,
3007 mFile->mStorageName);
3008 ContentChild::GetSingleton()
3009 ->SendPDeviceStorageRequestConstructor(child, params);
3010 return NS_OK;
3011 }
3012 r = new PostStatusResultEvent(mFile, mRequest);
3013 return NS_DispatchToCurrentThread(r);
3014 }
3016 case DEVICE_STORAGE_REQUEST_WATCH:
3017 {
3018 mDeviceStorage->mAllowedToWatchFile = true;
3019 return NS_OK;
3020 }
3022 case DEVICE_STORAGE_REQUEST_FORMAT:
3023 {
3024 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3025 PDeviceStorageRequestChild* child
3026 = new DeviceStorageRequestChild(mRequest, mFile);
3027 DeviceStorageFormatParams params(mFile->mStorageType,
3028 mFile->mStorageName);
3029 ContentChild::GetSingleton()
3030 ->SendPDeviceStorageRequestConstructor(child, params);
3031 return NS_OK;
3032 }
3033 r = new PostFormatResultEvent(mFile, mRequest);
3034 return NS_DispatchToCurrentThread(r);
3035 }
3037 case DEVICE_STORAGE_REQUEST_MOUNT:
3038 {
3039 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3040 PDeviceStorageRequestChild* child
3041 = new DeviceStorageRequestChild(mRequest, mFile);
3042 DeviceStorageMountParams params(mFile->mStorageType,
3043 mFile->mStorageName);
3044 ContentChild::GetSingleton()
3045 ->SendPDeviceStorageRequestConstructor(child, params);
3046 return NS_OK;
3047 }
3048 r = new PostMountResultEvent(mFile, mRequest);
3049 return NS_DispatchToCurrentThread(r);
3050 }
3052 case DEVICE_STORAGE_REQUEST_UNMOUNT:
3053 {
3054 if (XRE_GetProcessType() != GeckoProcessType_Default) {
3055 PDeviceStorageRequestChild* child
3056 = new DeviceStorageRequestChild(mRequest, mFile);
3057 DeviceStorageUnmountParams params(mFile->mStorageType,
3058 mFile->mStorageName);
3059 ContentChild::GetSingleton()
3060 ->SendPDeviceStorageRequestConstructor(child, params);
3061 return NS_OK;
3062 }
3063 r = new PostUnmountResultEvent(mFile, mRequest);
3064 return NS_DispatchToCurrentThread(r);
3065 }
3066 }
3068 if (r) {
3069 nsCOMPtr<nsIEventTarget> target
3070 = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
3071 MOZ_ASSERT(target);
3072 target->Dispatch(r, NS_DISPATCH_NORMAL);
3073 }
3075 return NS_OK;
3076 }
3078 bool Recv__delete__(const bool& allow,
3079 const InfallibleTArray<PermissionChoice>& choices)
3080 {
3081 MOZ_ASSERT(choices.IsEmpty(), "DeviceStorage doesn't support permission choice");
3083 if (allow) {
3084 Allow(JS::UndefinedHandleValue);
3085 }
3086 else {
3087 Cancel();
3088 }
3089 return true;
3090 }
3092 void IPDLRelease()
3093 {
3094 Release();
3095 }
3097 private:
3098 int32_t mRequestType;
3099 nsCOMPtr<nsPIDOMWindow> mWindow;
3100 nsCOMPtr<nsIPrincipal> mPrincipal;
3101 nsRefPtr<DeviceStorageFile> mFile;
3103 nsRefPtr<DOMRequest> mRequest;
3104 nsCOMPtr<nsIDOMBlob> mBlob;
3105 nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
3106 nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
3107 };
3109 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
3110 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
3111 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
3112 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
3113 NS_INTERFACE_MAP_END
3115 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest)
3116 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest)
3118 NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
3119 mRequest,
3120 mWindow,
3121 mBlob,
3122 mDeviceStorage)
3125 NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorage)
3126 NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
3127 NS_INTERFACE_MAP_ENTRY(nsIObserver)
3128 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
3130 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
3131 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
3133 nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow)
3134 : DOMEventTargetHelper(aWindow)
3135 , mIsWatchingFile(false)
3136 , mAllowedToWatchFile(false)
3137 {
3138 }
3140 /* virtual */ JSObject*
3141 nsDOMDeviceStorage::WrapObject(JSContext* aCx)
3142 {
3143 return DeviceStorageBinding::Wrap(aCx, this);
3144 }
3146 nsresult
3147 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
3148 const nsAString &aVolName)
3149 {
3150 DebugOnly<FileUpdateDispatcher*> observer
3151 = FileUpdateDispatcher::GetSingleton();
3152 MOZ_ASSERT(observer);
3154 MOZ_ASSERT(aWindow);
3156 SetRootDirectoryForType(aType, aVolName);
3157 if (!mRootDirectory) {
3158 return NS_ERROR_NOT_AVAILABLE;
3159 }
3160 if (!mStorageName.IsEmpty()) {
3161 RegisterForSDCardChanges(this);
3162 }
3164 // Grab the principal of the document
3165 nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
3166 if (!doc) {
3167 return NS_ERROR_FAILURE;
3168 }
3169 mPrincipal = doc->NodePrincipal();
3171 // the 'apps' type is special. We only want this exposed
3172 // if the caller has the "webapps-manage" permission.
3173 if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
3174 nsCOMPtr<nsIPermissionManager> permissionManager
3175 = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
3176 NS_ENSURE_TRUE(permissionManager, NS_ERROR_FAILURE);
3178 uint32_t permission;
3179 nsresult rv
3180 = permissionManager->TestPermissionFromPrincipal(mPrincipal,
3181 "webapps-manage",
3182 &permission);
3184 if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
3185 return NS_ERROR_NOT_AVAILABLE;
3186 }
3187 }
3189 return NS_OK;
3190 }
3192 nsDOMDeviceStorage::~nsDOMDeviceStorage()
3193 {
3194 }
3196 void
3197 nsDOMDeviceStorage::Shutdown()
3198 {
3199 MOZ_ASSERT(NS_IsMainThread());
3201 if (mFileSystem) {
3202 mFileSystem->Shutdown();
3203 mFileSystem = nullptr;
3204 }
3206 if (!mStorageName.IsEmpty()) {
3207 UnregisterForSDCardChanges(this);
3208 }
3210 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3211 obs->RemoveObserver(this, "file-watcher-update");
3212 obs->RemoveObserver(this, "disk-space-watcher");
3213 }
3215 StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
3217 // static
3218 void
3219 nsDOMDeviceStorage::GetOrderedVolumeNames(
3220 nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
3221 {
3222 if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
3223 aVolumeNames.AppendElements(*sVolumeNameCache);
3224 return;
3225 }
3226 #ifdef MOZ_WIDGET_GONK
3227 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
3228 if (vs) {
3229 vs->GetVolumeNames(aVolumeNames);
3231 // If the volume sdcard exists, then we want it to be first.
3233 VolumeNameArray::index_type sdcardIndex;
3234 sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard"));
3235 if (sdcardIndex != VolumeNameArray::NoIndex && sdcardIndex > 0) {
3236 aVolumeNames.RemoveElementAt(sdcardIndex);
3237 aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
3238 }
3239 }
3240 #endif
3241 if (aVolumeNames.IsEmpty()) {
3242 aVolumeNames.AppendElement(EmptyString());
3243 }
3244 sVolumeNameCache = new nsTArray<nsString>;
3245 sVolumeNameCache->AppendElements(aVolumeNames);
3246 }
3248 // static
3249 void
3250 nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
3251 const nsAString &aType,
3252 nsDOMDeviceStorage** aStore)
3253 {
3254 nsString storageName;
3255 if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
3256 // The storage name will be the empty string
3257 storageName.Truncate();
3258 } else {
3259 GetDefaultStorageName(aType, storageName);
3260 }
3262 nsRefPtr<nsDOMDeviceStorage> ds = new nsDOMDeviceStorage(aWin);
3263 if (NS_FAILED(ds->Init(aWin, aType, storageName))) {
3264 *aStore = nullptr;
3265 return;
3266 }
3267 NS_ADDREF(*aStore = ds.get());
3268 }
3270 // static
3271 void
3272 nsDOMDeviceStorage::CreateDeviceStoragesFor(
3273 nsPIDOMWindow* aWin,
3274 const nsAString &aType,
3275 nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
3276 {
3277 nsresult rv;
3279 if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
3280 nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
3281 rv = storage->Init(aWin, aType, EmptyString());
3282 if (NS_SUCCEEDED(rv)) {
3283 aStores.AppendElement(storage);
3284 }
3285 return;
3286 }
3287 VolumeNameArray volNames;
3288 GetOrderedVolumeNames(volNames);
3290 VolumeNameArray::size_type numVolumeNames = volNames.Length();
3291 for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) {
3292 nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
3293 rv = storage->Init(aWin, aType, volNames[i]);
3294 if (NS_FAILED(rv)) {
3295 break;
3296 }
3297 aStores.AppendElement(storage);
3298 }
3299 }
3301 // static
3302 bool
3303 nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
3304 nsAString& aOutStorageName,
3305 nsAString& aOutStoragePath)
3306 {
3307 aOutStorageName.Truncate();
3308 aOutStoragePath.Truncate();
3310 NS_NAMED_LITERAL_STRING(slash, "/");
3312 nsDependentSubstring storageName;
3314 if (StringBeginsWith(aFullPath, slash)) {
3315 int32_t slashIndex = aFullPath.FindChar('/', 1);
3316 if (slashIndex == kNotFound) {
3317 // names of the form /filename are illegal
3318 return false;
3319 }
3320 storageName.Rebind(aFullPath, 1, slashIndex - 1);
3321 aOutStoragePath = Substring(aFullPath, slashIndex + 1);
3322 } else {
3323 aOutStoragePath = aFullPath;
3324 }
3325 // If no volume name was specified in aFullPath, then aOutStorageName
3326 // will wind up being the empty string. It's up to the caller to figure
3327 // out which storage name to actually use.
3328 aOutStorageName = storageName;
3329 return true;
3330 }
3332 already_AddRefed<nsDOMDeviceStorage>
3333 nsDOMDeviceStorage::GetStorage(const nsAString& aFullPath,
3334 nsAString& aOutStoragePath)
3335 {
3336 nsString storageName;
3337 if (!ParseFullPath(aFullPath, storageName, aOutStoragePath)) {
3338 return nullptr;
3339 }
3340 nsRefPtr<nsDOMDeviceStorage> ds;
3341 if (storageName.IsEmpty()) {
3342 ds = this;
3343 } else {
3344 ds = GetStorageByName(storageName);
3345 }
3346 return ds.forget();
3347 }
3349 already_AddRefed<nsDOMDeviceStorage>
3350 nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
3351 {
3352 MOZ_ASSERT(NS_IsMainThread());
3354 nsRefPtr<nsDOMDeviceStorage> ds;
3356 if (mStorageName.Equals(aStorageName)) {
3357 ds = this;
3358 return ds.forget();
3359 }
3360 VolumeNameArray volNames;
3361 GetOrderedVolumeNames(volNames);
3362 VolumeNameArray::size_type numVolumes = volNames.Length();
3363 VolumeNameArray::index_type i;
3364 for (i = 0; i < numVolumes; i++) {
3365 if (volNames[i].Equals(aStorageName)) {
3366 ds = new nsDOMDeviceStorage(GetOwner());
3367 nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName);
3368 if (NS_FAILED(rv)) {
3369 return nullptr;
3370 }
3371 return ds.forget();
3372 }
3373 }
3374 return nullptr;
3375 }
3377 // static
3378 void
3379 nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
3380 nsAString& aStorageName)
3381 {
3382 // See if the preferred volume is available.
3383 nsRefPtr<nsDOMDeviceStorage> ds;
3384 nsAdoptingString prefStorageName =
3385 mozilla::Preferences::GetString("device.storage.writable.name");
3386 if (prefStorageName) {
3387 aStorageName = prefStorageName;
3388 return;
3389 }
3391 // No preferred storage, we'll use the first one (which should be sdcard).
3393 VolumeNameArray volNames;
3394 GetOrderedVolumeNames(volNames);
3395 if (volNames.Length() > 0) {
3396 aStorageName = volNames[0];
3397 return;
3398 }
3400 // No volumes available, return the empty string. This is normal for
3401 // b2g-desktop.
3402 aStorageName.Truncate();
3403 }
3405 bool
3406 nsDOMDeviceStorage::IsAvailable()
3407 {
3408 DeviceStorageFile dsf(mStorageType, mStorageName);
3409 return dsf.IsAvailable();
3410 }
3412 NS_IMETHODIMP
3413 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
3414 {
3415 ErrorResult rv;
3416 nsRefPtr<DOMRequest> request = Add(aBlob, rv);
3417 request.forget(_retval);
3418 return rv.ErrorCode();
3419 }
3421 already_AddRefed<DOMRequest>
3422 nsDOMDeviceStorage::Add(nsIDOMBlob* aBlob, ErrorResult& aRv)
3423 {
3424 if (!aBlob) {
3425 return nullptr;
3426 }
3428 nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
3429 if (!mimeSvc) {
3430 aRv.Throw(NS_ERROR_FAILURE);
3431 return nullptr;
3432 }
3434 // if mimeType isn't set, we will not get a correct
3435 // extension, and AddNamed() will fail. This will post an
3436 // onerror to the requestee.
3437 nsString mimeType;
3438 aBlob->GetType(mimeType);
3440 nsCString extension;
3441 mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
3442 EmptyCString(), extension);
3443 // if extension is null here, we will ignore it for now.
3444 // AddNamed() will check the file path and fail. This
3445 // will post an onerror to the requestee.
3447 // possible race here w/ unique filename
3448 char buffer[32];
3449 NS_MakeRandomString(buffer, ArrayLength(buffer) - 1);
3451 nsAutoCString path;
3452 path.Assign(nsDependentCString(buffer));
3453 path.Append(".");
3454 path.Append(extension);
3456 return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), aRv);
3457 }
3459 NS_IMETHODIMP
3460 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
3461 const nsAString & aPath,
3462 nsIDOMDOMRequest * *_retval)
3463 {
3464 ErrorResult rv;
3465 nsRefPtr<DOMRequest> request = AddNamed(aBlob, aPath, rv);
3466 request.forget(_retval);
3467 return rv.ErrorCode();
3468 }
3470 already_AddRefed<DOMRequest>
3471 nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
3472 ErrorResult& aRv)
3473 {
3474 MOZ_ASSERT(NS_IsMainThread());
3476 // if the blob is null here, bail
3477 if (!aBlob) {
3478 return nullptr;
3479 }
3481 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3482 if (!win) {
3483 aRv.Throw(NS_ERROR_UNEXPECTED);
3484 return nullptr;
3485 }
3487 DeviceStorageTypeChecker* typeChecker
3488 = DeviceStorageTypeChecker::CreateOrGet();
3489 if (!typeChecker) {
3490 aRv.Throw(NS_ERROR_FAILURE);
3491 return nullptr;
3492 }
3494 nsCOMPtr<nsIRunnable> r;
3495 nsresult rv;
3497 if (IsFullPath(aPath)) {
3498 nsString storagePath;
3499 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3500 if (!ds) {
3501 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3502 r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3503 rv = NS_DispatchToCurrentThread(r);
3504 if (NS_FAILED(rv)) {
3505 aRv.Throw(rv);
3506 }
3507 return request.forget();
3508 }
3509 return ds->AddNamed(aBlob, storagePath, aRv);
3510 }
3512 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3514 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3515 mStorageName,
3516 aPath);
3517 if (!dsf->IsSafePath()) {
3518 r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
3519 } else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
3520 !typeChecker->Check(mStorageType, aBlob)) {
3521 r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
3522 } else {
3523 r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE,
3524 win, mPrincipal, dsf, request, aBlob);
3525 }
3527 rv = NS_DispatchToCurrentThread(r);
3528 if (NS_FAILED(rv)) {
3529 aRv.Throw(rv);
3530 }
3531 return request.forget();
3532 }
3534 NS_IMETHODIMP
3535 nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
3536 {
3537 ErrorResult rv;
3538 nsRefPtr<DOMRequest> request = Get(aPath, rv);
3539 request.forget(aRetval);
3540 return rv.ErrorCode();
3541 }
3543 NS_IMETHODIMP
3544 nsDOMDeviceStorage::GetEditable(const nsAString& aPath,
3545 nsIDOMDOMRequest** aRetval)
3546 {
3547 ErrorResult rv;
3548 nsRefPtr<DOMRequest> request = GetEditable(aPath, rv);
3549 request.forget(aRetval);
3550 return rv.ErrorCode();
3551 }
3553 already_AddRefed<DOMRequest>
3554 nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable,
3555 ErrorResult& aRv)
3556 {
3557 MOZ_ASSERT(NS_IsMainThread());
3559 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3560 if (!win) {
3561 aRv.Throw(NS_ERROR_UNEXPECTED);
3562 return nullptr;
3563 }
3565 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3567 if (IsFullPath(aPath)) {
3568 nsString storagePath;
3569 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3570 if (!ds) {
3571 nsCOMPtr<nsIRunnable> r =
3572 new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3573 nsresult rv = NS_DispatchToCurrentThread(r);
3574 if (NS_FAILED(rv)) {
3575 aRv.Throw(rv);
3576 }
3577 return request.forget();
3578 }
3579 ds->GetInternal(win, storagePath, request, aEditable);
3580 return request.forget();
3581 }
3582 GetInternal(win, aPath, request, aEditable);
3583 return request.forget();
3584 }
3586 void
3587 nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin,
3588 const nsAString& aPath,
3589 DOMRequest* aRequest,
3590 bool aEditable)
3591 {
3592 MOZ_ASSERT(NS_IsMainThread());
3594 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3595 mStorageName,
3596 aPath);
3597 dsf->SetEditable(aEditable);
3599 nsCOMPtr<nsIRunnable> r;
3600 if (!dsf->IsSafePath()) {
3601 r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
3602 } else {
3603 r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE
3604 : DEVICE_STORAGE_REQUEST_READ,
3605 aWin, mPrincipal, dsf, aRequest);
3606 }
3607 DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
3608 MOZ_ASSERT(NS_SUCCEEDED(rv));
3609 }
3611 NS_IMETHODIMP
3612 nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
3613 {
3614 ErrorResult rv;
3615 nsRefPtr<DOMRequest> request = Delete(aPath, rv);
3616 request.forget(aRetval);
3617 return rv.ErrorCode();
3618 }
3620 already_AddRefed<DOMRequest>
3621 nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv)
3622 {
3623 MOZ_ASSERT(NS_IsMainThread());
3625 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3626 if (!win) {
3627 aRv.Throw(NS_ERROR_UNEXPECTED);
3628 return nullptr;
3629 }
3631 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3633 if (IsFullPath(aPath)) {
3634 nsString storagePath;
3635 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3636 if (!ds) {
3637 nsCOMPtr<nsIRunnable> r =
3638 new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3639 nsresult rv = NS_DispatchToCurrentThread(r);
3640 if (NS_FAILED(rv)) {
3641 aRv.Throw(rv);
3642 }
3643 return request.forget();
3644 }
3645 ds->DeleteInternal(win, storagePath, request);
3646 return request.forget();
3647 }
3648 DeleteInternal(win, aPath, request);
3649 return request.forget();
3650 }
3652 void
3653 nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin,
3654 const nsAString& aPath,
3655 DOMRequest* aRequest)
3656 {
3657 MOZ_ASSERT(NS_IsMainThread());
3659 nsCOMPtr<nsIRunnable> r;
3660 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3661 mStorageName,
3662 aPath);
3663 if (!dsf->IsSafePath()) {
3664 r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
3665 } else {
3666 r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
3667 aWin, mPrincipal, dsf, aRequest);
3668 }
3669 DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
3670 MOZ_ASSERT(NS_SUCCEEDED(rv));
3671 }
3673 NS_IMETHODIMP
3674 nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
3675 {
3676 ErrorResult rv;
3677 nsRefPtr<DOMRequest> request = FreeSpace(rv);
3678 request.forget(aRetval);
3679 return rv.ErrorCode();
3680 }
3682 already_AddRefed<DOMRequest>
3683 nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv)
3684 {
3685 MOZ_ASSERT(NS_IsMainThread());
3687 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3688 if (!win) {
3689 aRv.Throw(NS_ERROR_UNEXPECTED);
3690 return nullptr;
3691 }
3693 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3695 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3696 mStorageName);
3697 nsCOMPtr<nsIRunnable> r
3698 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
3699 win, mPrincipal, dsf, request);
3700 nsresult rv = NS_DispatchToCurrentThread(r);
3701 if (NS_FAILED(rv)) {
3702 aRv.Throw(rv);
3703 }
3704 return request.forget();
3705 }
3707 NS_IMETHODIMP
3708 nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
3709 {
3710 ErrorResult rv;
3711 nsRefPtr<DOMRequest> request = UsedSpace(rv);
3712 request.forget(aRetval);
3713 return rv.ErrorCode();
3714 }
3716 already_AddRefed<DOMRequest>
3717 nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv)
3718 {
3719 MOZ_ASSERT(NS_IsMainThread());
3721 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3722 if (!win) {
3723 aRv.Throw(NS_ERROR_UNEXPECTED);
3724 return nullptr;
3725 }
3727 DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
3728 = DeviceStorageUsedSpaceCache::CreateOrGet();
3729 MOZ_ASSERT(usedSpaceCache);
3731 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3733 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3734 mStorageName);
3735 nsCOMPtr<nsIRunnable> r
3736 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
3737 win, mPrincipal, dsf, request);
3738 nsresult rv = NS_DispatchToCurrentThread(r);
3739 if (NS_FAILED(rv)) {
3740 aRv.Throw(rv);
3741 }
3742 return request.forget();
3743 }
3745 NS_IMETHODIMP
3746 nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
3747 {
3748 ErrorResult rv;
3749 nsRefPtr<DOMRequest> request = Available(rv);
3750 request.forget(aRetval);
3751 return rv.ErrorCode();
3752 }
3754 already_AddRefed<DOMRequest>
3755 nsDOMDeviceStorage::Available(ErrorResult& aRv)
3756 {
3757 MOZ_ASSERT(NS_IsMainThread());
3759 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3760 if (!win) {
3761 aRv.Throw(NS_ERROR_UNEXPECTED);
3762 return nullptr;
3763 }
3765 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3767 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3768 mStorageName);
3769 nsCOMPtr<nsIRunnable> r
3770 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
3771 win, mPrincipal, dsf, request);
3772 nsresult rv = NS_DispatchToCurrentThread(r);
3773 if (NS_FAILED(rv)) {
3774 aRv.Throw(rv);
3775 }
3776 return request.forget();
3777 }
3779 already_AddRefed<DOMRequest>
3780 nsDOMDeviceStorage::StorageStatus(ErrorResult& aRv)
3781 {
3782 MOZ_ASSERT(NS_IsMainThread());
3784 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3785 if (!win) {
3786 aRv.Throw(NS_ERROR_UNEXPECTED);
3787 return nullptr;
3788 }
3790 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3792 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3793 mStorageName);
3794 nsCOMPtr<nsIRunnable> r
3795 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_STATUS,
3796 win, mPrincipal, dsf, request);
3797 nsresult rv = NS_DispatchToCurrentThread(r);
3798 if (NS_FAILED(rv)) {
3799 aRv.Throw(rv);
3800 }
3801 return request.forget();
3802 }
3804 already_AddRefed<DOMRequest>
3805 nsDOMDeviceStorage::Format(ErrorResult& aRv)
3806 {
3807 MOZ_ASSERT(NS_IsMainThread());
3809 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3810 if (!win) {
3811 aRv.Throw(NS_ERROR_UNEXPECTED);
3812 return nullptr;
3813 }
3815 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3817 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3818 mStorageName);
3819 nsCOMPtr<nsIRunnable> r
3820 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
3821 win, mPrincipal, dsf, request);
3822 nsresult rv = NS_DispatchToCurrentThread(r);
3823 if (NS_FAILED(rv)) {
3824 aRv.Throw(rv);
3825 }
3826 return request.forget();
3827 }
3829 already_AddRefed<DOMRequest>
3830 nsDOMDeviceStorage::Mount(ErrorResult& aRv)
3831 {
3832 MOZ_ASSERT(NS_IsMainThread());
3834 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3835 if (!win) {
3836 aRv.Throw(NS_ERROR_UNEXPECTED);
3837 return nullptr;
3838 }
3840 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3842 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3843 mStorageName);
3844 nsCOMPtr<nsIRunnable> r
3845 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_MOUNT,
3846 win, mPrincipal, dsf, request);
3847 nsresult rv = NS_DispatchToCurrentThread(r);
3848 if (NS_FAILED(rv)) {
3849 aRv.Throw(rv);
3850 }
3851 return request.forget();
3852 }
3854 already_AddRefed<DOMRequest>
3855 nsDOMDeviceStorage::Unmount(ErrorResult& aRv)
3856 {
3857 MOZ_ASSERT(NS_IsMainThread());
3859 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3860 if (!win) {
3861 aRv.Throw(NS_ERROR_UNEXPECTED);
3862 return nullptr;
3863 }
3865 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3867 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3868 mStorageName);
3869 nsCOMPtr<nsIRunnable> r
3870 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_UNMOUNT,
3871 win, mPrincipal, dsf, request);
3872 nsresult rv = NS_DispatchToCurrentThread(r);
3873 if (NS_FAILED(rv)) {
3874 aRv.Throw(rv);
3875 }
3876 return request.forget();
3877 }
3879 NS_IMETHODIMP
3880 nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath,
3881 DeviceStorageFileDescriptor* aDSFileDescriptor,
3882 nsIDOMDOMRequest** aRequest)
3883 {
3884 MOZ_ASSERT(NS_IsMainThread());
3885 MOZ_ASSERT(aDSFileDescriptor);
3887 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3888 if (!win) {
3889 return NS_ERROR_UNEXPECTED;
3890 }
3892 DeviceStorageTypeChecker* typeChecker
3893 = DeviceStorageTypeChecker::CreateOrGet();
3894 if (!typeChecker) {
3895 return NS_ERROR_FAILURE;
3896 }
3898 nsCOMPtr<nsIRunnable> r;
3899 nsresult rv;
3901 if (IsFullPath(aPath)) {
3902 nsString storagePath;
3903 nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
3904 if (!ds) {
3905 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3906 r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
3907 rv = NS_DispatchToCurrentThread(r);
3908 if (NS_FAILED(rv)) {
3909 return rv;
3910 }
3911 request.forget(aRequest);
3912 return NS_OK;
3913 }
3914 return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest);
3915 }
3917 nsRefPtr<DOMRequest> request = new DOMRequest(win);
3919 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
3920 mStorageName,
3921 aPath);
3922 if (!dsf->IsSafePath()) {
3923 r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
3924 } else if (!typeChecker->Check(mStorageType, dsf->mFile)) {
3925 r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
3926 } else {
3927 r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD,
3928 win, mPrincipal, dsf, request,
3929 aDSFileDescriptor);
3930 }
3932 rv = NS_DispatchToCurrentThread(r);
3933 if (NS_FAILED(rv)) {
3934 return rv;
3935 }
3936 request.forget(aRequest);
3937 return NS_OK;
3938 }
3940 bool
3941 nsDOMDeviceStorage::Default()
3942 {
3943 nsString defaultStorageName;
3944 GetDefaultStorageName(mStorageType, defaultStorageName);
3945 return mStorageName.Equals(defaultStorageName);
3946 }
3948 already_AddRefed<Promise>
3949 nsDOMDeviceStorage::GetRoot()
3950 {
3951 if (!mFileSystem) {
3952 mFileSystem = new DeviceStorageFileSystem(mStorageType, mStorageName);
3953 mFileSystem->Init(this);
3954 }
3955 return mozilla::dom::Directory::GetRoot(mFileSystem);
3956 }
3958 NS_IMETHODIMP
3959 nsDOMDeviceStorage::GetDefault(bool* aDefault)
3960 {
3961 *aDefault = Default();
3962 return NS_OK;
3963 }
3965 NS_IMETHODIMP
3966 nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName)
3967 {
3968 aStorageName = mStorageName;
3969 return NS_OK;
3970 }
3972 already_AddRefed<DOMCursor>
3973 nsDOMDeviceStorage::Enumerate(const nsAString& aPath,
3974 const EnumerationParameters& aOptions,
3975 ErrorResult& aRv)
3976 {
3977 return EnumerateInternal(aPath, aOptions, false, aRv);
3978 }
3980 already_AddRefed<DOMCursor>
3981 nsDOMDeviceStorage::EnumerateEditable(const nsAString& aPath,
3982 const EnumerationParameters& aOptions,
3983 ErrorResult& aRv)
3984 {
3985 return EnumerateInternal(aPath, aOptions, true, aRv);
3986 }
3989 already_AddRefed<DOMCursor>
3990 nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
3991 const EnumerationParameters& aOptions,
3992 bool aEditable, ErrorResult& aRv)
3993 {
3994 MOZ_ASSERT(NS_IsMainThread());
3996 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
3997 if (!win) {
3998 aRv.Throw(NS_ERROR_UNEXPECTED);
3999 return nullptr;
4000 }
4002 PRTime since = 0;
4003 if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
4004 since = PRTime(aOptions.mSince.Value().TimeStamp());
4005 }
4007 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4008 mStorageName,
4009 aPath,
4010 EmptyString());
4011 dsf->SetEditable(aEditable);
4013 nsRefPtr<nsDOMDeviceStorageCursor> cursor
4014 = new nsDOMDeviceStorageCursor(win, mPrincipal, dsf, since);
4015 nsRefPtr<DeviceStorageCursorRequest> r
4016 = new DeviceStorageCursorRequest(cursor);
4018 if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
4019 r->Allow(JS::UndefinedHandleValue);
4020 return cursor.forget();
4021 }
4023 if (XRE_GetProcessType() == GeckoProcessType_Content) {
4024 // because owner implements nsITabChild, we can assume that it is
4025 // the one and only TabChild.
4026 TabChild* child = TabChild::GetFrom(win->GetDocShell());
4027 if (!child) {
4028 return cursor.forget();
4029 }
4031 // Retain a reference so the object isn't deleted without IPDL's knowledge.
4032 // Corresponding release occurs in DeallocPContentPermissionRequest.
4033 r->AddRef();
4035 nsCString type;
4036 aRv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type);
4037 if (aRv.Failed()) {
4038 return nullptr;
4039 }
4040 nsTArray<PermissionRequest> permArray;
4041 nsTArray<nsString> emptyOptions;
4042 permArray.AppendElement(PermissionRequest(type,
4043 NS_LITERAL_CSTRING("read"),
4044 emptyOptions));
4045 child->SendPContentPermissionRequestConstructor(r,
4046 permArray,
4047 IPC::Principal(mPrincipal));
4049 r->Sendprompt();
4051 return cursor.forget();
4052 }
4054 nsCOMPtr<nsIContentPermissionPrompt> prompt
4055 = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
4056 if (prompt) {
4057 prompt->Prompt(r);
4058 }
4060 return cursor.forget();
4061 }
4063 #ifdef MOZ_WIDGET_GONK
4064 void
4065 nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeStatus)
4066 {
4067 if (aVolumeStatus == mLastStatus) {
4068 // We've already sent this status, don't bother sending it again.
4069 return;
4070 }
4071 mLastStatus = aVolumeStatus;
4073 nsCOMPtr<nsIDOMEvent> event;
4074 NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this,
4075 nullptr, nullptr);
4077 nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
4078 nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"),
4079 true, false,
4080 mStorageName,
4081 aVolumeStatus);
4082 if (NS_FAILED(rv)) {
4083 return;
4084 }
4086 bool ignore;
4087 DispatchEvent(ce, &ignore);
4088 }
4089 #endif
4091 NS_IMETHODIMP
4092 nsDOMDeviceStorage::Observe(nsISupports *aSubject,
4093 const char *aTopic,
4094 const char16_t *aData)
4095 {
4096 MOZ_ASSERT(NS_IsMainThread());
4098 if (!strcmp(aTopic, "file-watcher-update")) {
4100 DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
4101 Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
4102 return NS_OK;
4103 }
4104 if (!strcmp(aTopic, "disk-space-watcher")) {
4105 // 'disk-space-watcher' notifications are sent when there is a modification
4106 // of a file in a specific location while a low device storage situation
4107 // exists or after recovery of a low storage situation. For Firefox OS,
4108 // these notifications are specific for apps storage.
4109 nsRefPtr<DeviceStorageFile> file =
4110 new DeviceStorageFile(mStorageType, mStorageName);
4111 if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "full")) {
4112 Notify("low-disk-space", file);
4113 } else if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "free")) {
4114 Notify("available-disk-space", file);
4115 }
4116 return NS_OK;
4117 }
4119 #ifdef MOZ_WIDGET_GONK
4120 else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
4121 // We invalidate the used space cache for the volume that actually changed
4122 // state.
4123 nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
4124 if (!vol) {
4125 return NS_OK;
4126 }
4127 nsString volName;
4128 vol->GetName(volName);
4130 DeviceStorageUsedSpaceCache* usedSpaceCache
4131 = DeviceStorageUsedSpaceCache::CreateOrGet();
4132 MOZ_ASSERT(usedSpaceCache);
4133 usedSpaceCache->Invalidate(volName);
4135 if (!volName.Equals(mStorageName)) {
4136 // Not our volume - we can ignore.
4137 return NS_OK;
4138 }
4140 DeviceStorageFile dsf(mStorageType, mStorageName);
4141 nsString status;
4142 dsf.GetStatus(status);
4143 DispatchMountChangeEvent(status);
4144 return NS_OK;
4145 }
4146 #endif
4147 return NS_OK;
4148 }
4150 nsresult
4151 nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)
4152 {
4153 if (!mAllowedToWatchFile) {
4154 return NS_OK;
4155 }
4157 if (!mStorageType.Equals(aFile->mStorageType) ||
4158 !mStorageName.Equals(aFile->mStorageName)) {
4159 // Ignore this
4160 return NS_OK;
4161 }
4163 nsCOMPtr<nsIDOMEvent> event;
4164 NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this,
4165 nullptr, nullptr);
4167 nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
4169 nsString reason;
4170 reason.AssignWithConversion(aReason);
4172 nsString fullPath;
4173 aFile->GetFullPath(fullPath);
4174 nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"),
4175 true, false, fullPath,
4176 reason);
4177 NS_ENSURE_SUCCESS(rv, rv);
4179 bool ignore;
4180 DispatchEvent(ce, &ignore);
4181 return NS_OK;
4182 }
4184 NS_IMETHODIMP
4185 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
4186 nsIDOMEventListener *aListener,
4187 bool aUseCapture,
4188 bool aWantsUntrusted,
4189 uint8_t aArgc)
4190 {
4191 MOZ_ASSERT(NS_IsMainThread());
4193 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4194 if (!win) {
4195 return NS_ERROR_UNEXPECTED;
4196 }
4198 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4199 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4200 mStorageName);
4201 nsCOMPtr<nsIRunnable> r
4202 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
4203 win, mPrincipal, dsf, request, this);
4204 nsresult rv = NS_DispatchToCurrentThread(r);
4205 if (NS_WARN_IF(NS_FAILED(rv))) {
4206 return rv;
4207 }
4209 return DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
4210 aWantsUntrusted, aArgc);
4211 }
4213 void
4214 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
4215 EventListener *aListener,
4216 bool aUseCapture,
4217 const Nullable<bool>& aWantsUntrusted,
4218 ErrorResult& aRv)
4219 {
4220 MOZ_ASSERT(NS_IsMainThread());
4222 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
4223 if (!win) {
4224 aRv.Throw(NS_ERROR_UNEXPECTED);
4225 return;
4226 }
4228 nsRefPtr<DOMRequest> request = new DOMRequest(win);
4229 nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
4230 mStorageName);
4231 nsCOMPtr<nsIRunnable> r
4232 = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
4233 win, mPrincipal, dsf, request, this);
4234 nsresult rv = NS_DispatchToCurrentThread(r);
4235 if (NS_WARN_IF(NS_FAILED(rv))) {
4236 return;
4237 }
4238 DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
4239 aWantsUntrusted, aRv);
4240 }
4242 NS_IMETHODIMP
4243 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
4244 nsIDOMEventListener *aListener,
4245 bool aUseCapture,
4246 bool aWantsUntrusted,
4247 uint8_t aArgc)
4248 {
4249 if (!mIsWatchingFile) {
4250 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4251 obs->AddObserver(this, "file-watcher-update", false);
4252 mIsWatchingFile = true;
4253 }
4255 return nsDOMDeviceStorage::AddEventListener(aType, aListener, aUseCapture,
4256 aWantsUntrusted, aArgc);
4257 }
4259 NS_IMETHODIMP
4260 nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
4261 nsIDOMEventListener *aListener,
4262 bool aUseCapture)
4263 {
4264 DOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
4266 if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
4267 mIsWatchingFile = false;
4268 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4269 obs->RemoveObserver(this, "file-watcher-update");
4270 }
4271 return NS_OK;
4272 }
4274 void
4275 nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType,
4276 EventListener* aListener,
4277 bool aCapture,
4278 ErrorResult& aRv)
4279 {
4280 DOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv);
4282 if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
4283 mIsWatchingFile = false;
4284 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4285 obs->RemoveObserver(this, "file-watcher-update");
4286 }
4287 }
4289 NS_IMETHODIMP
4290 nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
4291 nsIDOMEventListener *aListener,
4292 bool aUseCapture)
4293 {
4294 return nsDOMDeviceStorage::RemoveEventListener(aType, aListener, aUseCapture);
4295 }
4297 NS_IMETHODIMP
4298 nsDOMDeviceStorage::DispatchEvent(nsIDOMEvent *aEvt,
4299 bool *aRetval)
4300 {
4301 return DOMEventTargetHelper::DispatchEvent(aEvt, aRetval);
4302 }
4304 EventTarget*
4305 nsDOMDeviceStorage::GetTargetForDOMEvent()
4306 {
4307 return DOMEventTargetHelper::GetTargetForDOMEvent();
4308 }
4310 EventTarget *
4311 nsDOMDeviceStorage::GetTargetForEventTargetChain()
4312 {
4313 return DOMEventTargetHelper::GetTargetForEventTargetChain();
4314 }
4316 nsresult
4317 nsDOMDeviceStorage::PreHandleEvent(EventChainPreVisitor& aVisitor)
4318 {
4319 return DOMEventTargetHelper::PreHandleEvent(aVisitor);
4320 }
4322 nsresult
4323 nsDOMDeviceStorage::WillHandleEvent(EventChainPostVisitor& aVisitor)
4324 {
4325 return DOMEventTargetHelper::WillHandleEvent(aVisitor);
4326 }
4328 nsresult
4329 nsDOMDeviceStorage::PostHandleEvent(EventChainPostVisitor& aVisitor)
4330 {
4331 return DOMEventTargetHelper::PostHandleEvent(aVisitor);
4332 }
4334 nsresult
4335 nsDOMDeviceStorage::DispatchDOMEvent(WidgetEvent* aEvent,
4336 nsIDOMEvent* aDOMEvent,
4337 nsPresContext* aPresContext,
4338 nsEventStatus* aEventStatus)
4339 {
4340 return DOMEventTargetHelper::DispatchDOMEvent(aEvent,
4341 aDOMEvent,
4342 aPresContext,
4343 aEventStatus);
4344 }
4346 EventListenerManager*
4347 nsDOMDeviceStorage::GetOrCreateListenerManager()
4348 {
4349 return DOMEventTargetHelper::GetOrCreateListenerManager();
4350 }
4352 EventListenerManager*
4353 nsDOMDeviceStorage::GetExistingListenerManager() const
4354 {
4355 return DOMEventTargetHelper::GetExistingListenerManager();
4356 }
4358 nsIScriptContext *
4359 nsDOMDeviceStorage::GetContextForEventHandlers(nsresult *aRv)
4360 {
4361 return DOMEventTargetHelper::GetContextForEventHandlers(aRv);
4362 }
4364 JSContext *
4365 nsDOMDeviceStorage::GetJSContextForEventHandlers()
4366 {
4367 return DOMEventTargetHelper::GetJSContextForEventHandlers();
4368 }
4370 NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)