/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DeviceStorage_h #define DeviceStorage_h #include "nsIFile.h" #include "nsIPrincipal.h" #include "nsIObserver.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" #include "mozilla/dom/DOMRequest.h" #define DEVICESTORAGE_PICTURES "pictures" #define DEVICESTORAGE_VIDEOS "videos" #define DEVICESTORAGE_MUSIC "music" #define DEVICESTORAGE_APPS "apps" #define DEVICESTORAGE_SDCARD "sdcard" #define DEVICESTORAGE_CRASHES "crashes" class nsIInputStream; class nsIOutputStream; struct DeviceStorageFileDescriptor; namespace mozilla { class EventListenerManager; namespace dom { class Blob; struct DeviceStorageEnumerationParameters; class DOMCursor; class DOMRequest; class Promise; class DeviceStorageFileSystem; } // namespace dom namespace ipc { class FileDescriptor; } } // namespace mozilla class DeviceStorageFile final : public nsISupports { public: nsCOMPtr mFile; nsString mStorageType; nsString mStorageName; nsString mRootDir; nsString mPath; bool mEditable; nsString mMimeType; uint64_t mLength; uint64_t mLastModifiedDate; // Used when the path will be set later via SetPath. DeviceStorageFile(const nsAString& aStorageType, const nsAString& aStorageName); // Used for non-enumeration purposes. DeviceStorageFile(const nsAString& aStorageType, const nsAString& aStorageName, const nsAString& aPath); // Used for enumerations. When you call Enumerate, you can pass in a // directory to enumerate and the results that are returned are relative to // that directory, files related to an enumeration need to know the "root of // the enumeration" directory. DeviceStorageFile(const nsAString& aStorageType, const nsAString& aStorageName, const nsAString& aRootDir, const nsAString& aPath); void SetPath(const nsAString& aPath); void SetEditable(bool aEditable); static already_AddRefed CreateUnique(nsAString& aFileName, uint32_t aFileType, uint32_t aFileAttributes); NS_DECL_THREADSAFE_ISUPPORTS bool IsAvailable(); void GetFullPath(nsAString& aFullPath); // we want to make sure that the names of file can't reach // outside of the type of storage the user asked for. bool IsSafePath(); bool IsSafePath(const nsAString& aPath); void Dump(const char* label); nsresult Remove(); nsresult Write(nsIInputStream* aInputStream); nsresult Write(InfallibleTArray& bits); nsresult Append(nsIInputStream* aInputStream); nsresult Append(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream); void CollectFiles(nsTArray >& aFiles, PRTime aSince = 0); void collectFilesInternal(nsTArray >& aFiles, PRTime aSince, nsAString& aRootPath); void AccumDiskUsage(uint64_t* aPicturesSoFar, uint64_t* aVideosSoFar, uint64_t* aMusicSoFar, uint64_t* aTotalSoFar); void GetStorageFreeSpace(int64_t* aSoFar); void GetStatus(nsAString& aStatus); void GetStorageStatus(nsAString& aStatus); void DoFormat(nsAString& aStatus); void DoMount(nsAString& aStatus); void DoUnmount(nsAString& aStatus); static void GetRootDirectoryForType(const nsAString& aStorageType, const nsAString& aStorageName, nsIFile** aFile); nsresult CalculateSizeAndModifiedDate(); nsresult CalculateMimeType(); nsresult CreateFileDescriptor(mozilla::ipc::FileDescriptor& aFileDescriptor); private: ~DeviceStorageFile() {} void Init(); void NormalizeFilePath(); void AppendRelativePath(const nsAString& aPath); void AccumDirectoryUsage(nsIFile* aFile, uint64_t* aPicturesSoFar, uint64_t* aVideosSoFar, uint64_t* aMusicSoFar, uint64_t* aTotalSoFar); }; /* The FileUpdateDispatcher converts file-watcher-notify observer events to file-watcher-update events. This is used to be able to broadcast events from one child to another child in B2G. (f.e., if one child decides to add a file, we want to be able to able to send a onchange notifications to every other child watching that device storage object). We create this object (via GetSingleton) in two places: * ContentParent::Init (for IPC) * nsDOMDeviceStorage::Init (for non-ipc) */ class FileUpdateDispatcher final : public nsIObserver { ~FileUpdateDispatcher() {} public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER static FileUpdateDispatcher* GetSingleton(); private: static mozilla::StaticRefPtr sSingleton; }; class nsDOMDeviceStorage final : public mozilla::DOMEventTargetHelper , public nsIObserver { typedef mozilla::ErrorResult ErrorResult; typedef mozilla::dom::DeviceStorageEnumerationParameters EnumerationParameters; typedef mozilla::dom::DOMCursor DOMCursor; typedef mozilla::dom::DOMRequest DOMRequest; typedef mozilla::dom::Promise Promise; typedef mozilla::dom::DeviceStorageFileSystem DeviceStorageFileSystem; public: typedef nsTArray VolumeNameArray; NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIOBSERVER NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper) void EventListenerWasAdded(const nsAString& aType, ErrorResult& aRv, JSCompartment* aCompartment) override; explicit nsDOMDeviceStorage(nsPIDOMWindow* aWindow); static int InstanceCount() { return sInstanceCount; } static void InvalidateVolumeCaches(); nsresult Init(nsPIDOMWindow* aWindow, const nsAString& aType, const nsAString& aVolName); bool IsAvailable(); bool IsFullPath(const nsAString& aPath) { return aPath.Length() > 0 && aPath.CharAt(0) == '/'; } void SetRootDirectoryForType(const nsAString& aType, const nsAString& aVolName); // WebIDL nsPIDOMWindow* GetParentObject() const { return GetOwner(); } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; IMPL_EVENT_HANDLER(change) already_AddRefed Add(mozilla::dom::Blob* aBlob, ErrorResult& aRv); already_AddRefed AddNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath, ErrorResult& aRv); already_AddRefed AppendNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath, ErrorResult& aRv); already_AddRefed AddOrAppendNamed(mozilla::dom::Blob* aBlob, const nsAString& aPath, const int32_t aRequestType, ErrorResult& aRv); already_AddRefed Get(const nsAString& aPath, ErrorResult& aRv) { return GetInternal(aPath, false, aRv); } already_AddRefed GetEditable(const nsAString& aPath, ErrorResult& aRv) { return GetInternal(aPath, true, aRv); } already_AddRefed Delete(const nsAString& aPath, ErrorResult& aRv); already_AddRefed Enumerate(const EnumerationParameters& aOptions, ErrorResult& aRv) { return Enumerate(NullString(), aOptions, aRv); } already_AddRefed Enumerate(const nsAString& aPath, const EnumerationParameters& aOptions, ErrorResult& aRv); already_AddRefed EnumerateEditable(const EnumerationParameters& aOptions, ErrorResult& aRv) { return EnumerateEditable(NullString(), aOptions, aRv); } already_AddRefed EnumerateEditable(const nsAString& aPath, const EnumerationParameters& aOptions, ErrorResult& aRv); already_AddRefed FreeSpace(ErrorResult& aRv); already_AddRefed UsedSpace(ErrorResult& aRv); already_AddRefed Available(ErrorResult& aRv); already_AddRefed Format(ErrorResult& aRv); already_AddRefed StorageStatus(ErrorResult& aRv); already_AddRefed Mount(ErrorResult& aRv); already_AddRefed Unmount(ErrorResult& aRv); already_AddRefed CreateFileDescriptor(const nsAString& aPath, DeviceStorageFileDescriptor* aDSFD, ErrorResult& aRv); bool CanBeMounted(); bool CanBeFormatted(); bool CanBeShared(); bool IsRemovable(); bool Default(); void GetStorageName(nsAString& aStorageName); already_AddRefed GetRoot(ErrorResult& aRv); static void CreateDeviceStorageFor(nsPIDOMWindow* aWin, const nsAString& aType, nsDOMDeviceStorage** aStore); static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin, const nsAString& aType, nsTArray >& aStores); static void CreateDeviceStorageByNameAndType(nsPIDOMWindow* aWin, const nsAString& aName, const nsAString& aType, nsDOMDeviceStorage** aStore); void Shutdown(); static void GetOrderedVolumeNames(nsTArray& aVolumeNames); static void GetDefaultStorageName(const nsAString& aStorageType, nsAString &aStorageName); static bool ParseFullPath(const nsAString& aFullPath, nsAString& aOutStorageName, nsAString& aOutStoragePath); private: ~nsDOMDeviceStorage(); already_AddRefed GetInternal(const nsAString& aPath, bool aEditable, ErrorResult& aRv); void GetInternal(nsPIDOMWindow* aWin, const nsAString& aPath, DOMRequest* aRequest, bool aEditable); void DeleteInternal(nsPIDOMWindow* aWin, const nsAString& aPath, DOMRequest* aRequest); already_AddRefed EnumerateInternal(const nsAString& aName, const EnumerationParameters& aOptions, bool aEditable, ErrorResult& aRv); static int sInstanceCount; nsString mStorageType; nsCOMPtr mRootDirectory; nsString mStorageName; bool mIsShareable; bool mIsRemovable; already_AddRefed GetStorage(const nsAString& aFullPath, nsAString& aOutStoragePath); already_AddRefed GetStorageByName(const nsAString &aStorageName); static already_AddRefed GetStorageByNameAndType(nsPIDOMWindow* aWin, const nsAString& aStorageName, const nsAString& aType); nsCOMPtr mPrincipal; bool mIsWatchingFile; bool mAllowedToWatchFile; bool mIsDefaultLocation; void DispatchDefaultChangeEvent(); nsresult Notify(const char* aReason, class DeviceStorageFile* aFile); friend class WatchFileEvent; friend class DeviceStorageRequest; static mozilla::StaticAutoPtr> sVolumeNameCache; #ifdef MOZ_WIDGET_GONK nsString mLastStatus; nsString mLastStorageStatus; void DispatchStatusChangeEvent(nsAString& aStatus); void DispatchStorageStatusChangeEvent(nsAString& aStorageStatus); #endif nsRefPtr mFileSystem; }; #endif