dom/devicestorage/nsDeviceStorage.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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.

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

mercurial