diff --git a/dom/base/File.cpp b/dom/base/File.cpp index a82995231559..c365912eb382 100644 --- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -227,12 +227,6 @@ Blob::IsFile() const return mImpl->IsFile(); } -bool -Blob::IsDirectory() const -{ - return mImpl->IsDirectory(); -} - const nsTArray>* Blob::GetSubBlobImpls() const { @@ -420,11 +414,10 @@ File::Create(nsISupports* aParent, BlobImpl* aImpl) /* static */ already_AddRefed File::Create(nsISupports* aParent, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, BlobDirState aDirState) + int64_t aLastModifiedDate) { RefPtr file = new File(aParent, - new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate, - aDirState)); + new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)); return file.forget(); } @@ -946,17 +939,6 @@ BlobImplFile::SetPath(const nsAString& aPath) mPath = aPath; } -void -BlobImplFile::LookupAndCacheIsDirectory() -{ - MOZ_ASSERT(mIsFile, - "This should only be called when this object has been created " - "from an nsIFile to note that the nsIFile is a directory"); - bool isDir; - mFile->IsDirectory(&isDir); - mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir; -} - //////////////////////////////////////////////////////////////////////////// // EmptyBlobImpl implementation diff --git a/dom/base/File.h b/dom/base/File.h index c60627b2a3e3..5f444b15ec28 100644 --- a/dom/base/File.h +++ b/dom/base/File.h @@ -44,18 +44,6 @@ class BlobImpl; class File; class OwningArrayBufferOrArrayBufferViewOrBlobOrString; -/** - * Used to indicate when a Blob/BlobImpl that was created from an nsIFile - * (when IsFile() will return true) was from an nsIFile for which - * nsIFile::IsDirectory() returned true. This is a tri-state to enable us to - * assert that the state is always set when callers request it. - */ -enum BlobDirState : uint32_t { - eIsDir, - eIsNotDir, - eUnknownIfDir -}; - class Blob : public nsIDOMBlob , public nsIXHRSendable , public nsIMutable @@ -101,12 +89,6 @@ public: bool IsFile() const; - /** - * This may return true if the Blob was created from an nsIFile that is a - * directory. - */ - bool IsDirectory() const; - const nsTArray>* GetSubBlobImpls() const; // This method returns null if this Blob is not a File; it returns @@ -114,9 +96,6 @@ public: // otherwise it returns a new File object with the same BlobImpl. already_AddRefed ToFile(); - // XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem - // object complicates that though. - // This method creates a new File object with the given name and the same // BlobImpl. already_AddRefed ToFile(const nsAString& aName, @@ -194,7 +173,7 @@ public: static already_AddRefed Create(nsISupports* aParent, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, BlobDirState aDirState); + int64_t aLastModifiedDate); // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be // freed by free so it must be allocated by malloc or something @@ -339,28 +318,6 @@ public: virtual bool IsFile() const = 0; - /** - * Called when this BlobImpl was created from an nsIFile in order to call - * nsIFile::IsDirectory() and cache the result so that when the BlobImpl is - * copied to another process that informaton is available. - * nsIFile::IsDirectory() does synchronous I/O, and BlobImpl objects may be - * created on the main thread or in a non-chrome process (where I/O is not - * allowed). Do not call this on a non-chrome process, and preferably do not - * call it on the main thread. - * - * Not all creators of BlobImplFile will call this method, in which case - * calling IsDirectory will MOZ_ASSERT. - */ - virtual void LookupAndCacheIsDirectory() = 0; - virtual void SetIsDirectory(bool aIsDir) = 0; - virtual bool IsDirectory() const = 0; - - /** - * Prefer IsDirectory(). This exists to help consumer code pass on state from - * one BlobImpl when creating another. - */ - virtual BlobDirState GetDirState() const = 0; - // True if this implementation can be sent to other threads. virtual bool MayBeClonedToOtherThreads() const { @@ -377,11 +334,9 @@ class BlobImplBase : public BlobImpl { public: BlobImplBase(const nsAString& aName, const nsAString& aContentType, - uint64_t aLength, int64_t aLastModifiedDate, - BlobDirState aDirState = BlobDirState::eUnknownIfDir) + uint64_t aLength, int64_t aLastModifiedDate) : mIsFile(true) , mImmutable(false) - , mDirState(aDirState) , mContentType(aContentType) , mName(aName) , mStart(0) @@ -397,7 +352,6 @@ public: uint64_t aLength) : mIsFile(true) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mName(aName) , mStart(0) @@ -412,7 +366,6 @@ public: BlobImplBase(const nsAString& aContentType, uint64_t aLength) : mIsFile(false) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mStart(0) , mLength(aLength) @@ -427,7 +380,6 @@ public: uint64_t aLength) : mIsFile(false) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mStart(aStart) , mLength(aLength) @@ -518,36 +470,6 @@ public: return mIsFile; } - virtual void LookupAndCacheIsDirectory() override - { - MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?"); - } - - virtual void SetIsDirectory(bool aIsDir) override - { - MOZ_ASSERT(mIsFile, - "This should only be called when this object has been created " - "from an nsIFile to note that the nsIFile is a directory"); - mDirState = aIsDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir; - } - - /** - * Returns true if the nsIFile that this object wraps is a directory. - */ - virtual bool IsDirectory() const override - { - MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir, - "Must only be used by callers for whom the code paths are " - "know to call LookupAndCacheIsDirectory() or " - "SetIsDirectory()"); - return mDirState == BlobDirState::eIsDir; - } - - virtual BlobDirState GetDirState() const override - { - return mDirState; - } - virtual bool IsSizeUnknown() const override { return mLength == UINT64_MAX; @@ -565,7 +487,6 @@ protected: bool mIsFile; bool mImmutable; - BlobDirState mDirState; nsString mContentType; nsString mName; @@ -590,8 +511,7 @@ public: BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate) - : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate, - BlobDirState::eIsNotDir) + : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate) , mDataOwner(new DataOwner(aMemoryBuffer, aLength)) { NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); @@ -792,8 +712,6 @@ public: void SetPath(const nsAString& aFullPath); - virtual void LookupAndCacheIsDirectory() override; - // We always have size and date for this kind of blob. virtual bool IsSizeUnknown() const override { return false; } virtual bool IsDateUnknown() const override { return false; } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index bfe977dd4a1e..5a199208fba5 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7625,7 +7625,6 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable, // has this data available to it when passed over: blobImpl->GetSize(rv); blobImpl->GetLastModified(rv); - blobImpl->LookupAndCacheIsDirectory(); } else { if (aInSyncMessage) { // Can't do anything. diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index b1ce9c9f2a4f..17fb01005aad 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -49,7 +49,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList) NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage) @@ -57,7 +57,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage) @@ -281,11 +281,12 @@ DataTransfer::GetMozUserCancelled(bool* aUserCancelled) FileList* DataTransfer::GetFiles(ErrorResult& aRv) { - return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal()); + return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal()); } FileList* -DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal) +DataTransfer::GetFileListInternal(ErrorResult& aRv, + nsIPrincipal* aSubjectPrincipal) { if (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop && @@ -293,14 +294,15 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal return nullptr; } - if (!mFiles) { - mFiles = new FileList(static_cast(this)); + if (!mFileList) { + mFileList = new FileList(static_cast(this)); uint32_t count = mItems.Length(); for (uint32_t i = 0; i < count; i++) { nsCOMPtr variant; - aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, aSubjectPrincipal, getter_AddRefs(variant)); + aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, + aSubjectPrincipal, getter_AddRefs(variant)); if (aRv.Failed()) { return nullptr; } @@ -338,21 +340,21 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal MOZ_ASSERT(domFile); } - if (!mFiles->Append(domFile)) { + if (!mFileList->Append(domFile)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } } - return mFiles; + return mFileList; } NS_IMETHODIMP DataTransfer::GetFiles(nsIDOMFileList** aFileList) { ErrorResult rv; - NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal())); + NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal())); return rv.StealNSResult(); } @@ -854,7 +856,7 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) return nullptr; } - if (!mFiles) { + if (!mFileList) { GetFiles(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; @@ -863,38 +865,14 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) Sequence filesAndDirsSeq; - if (mFiles && mFiles->Length()) { - if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) { + if (mFileList && mFileList->Length()) { + if (!filesAndDirsSeq.SetLength(mFileList->Length(), mozilla::fallible_t())) { p->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return p.forget(); } - for (uint32_t i = 0; i < mFiles->Length(); ++i) { - if (mFiles->Item(i)->Impl()->IsDirectory()) { -#if defined(ANDROID) || defined(MOZ_B2G) - MOZ_ASSERT(false, - "Directory picking should have been redirected to normal " - "file picking for platforms that don't have a directory " - "picker"); -#endif - nsAutoString path; - mFiles->Item(i)->GetMozFullPathInternal(path, aRv); - if (aRv.Failed()) { - return nullptr; - } - int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR); - nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex); - nsDependentSubstring basename = Substring(path, leafSeparatorIndex); - - RefPtr fs = new OSFileSystem(dirname); - fs->Init(parentNode->OwnerDoc()->GetInnerWindow()); - - RefPtr directory = new Directory(fs, basename); - directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); - filesAndDirsSeq[i].SetAsDirectory() = directory; - } else { - filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i); - } + for (uint32_t i = 0; i < mFileList->Length(); ++i) { + filesAndDirsSeq[i].SetAsFile() = mFileList->Item(i); } } diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h index 2b1f90a51ba5..f65a78d0c780 100644 --- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -252,7 +252,7 @@ protected: void FillInExternalData(TransferItem& aItem, uint32_t aIndex); - FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal); + FileList* GetFileListInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal); nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex, nsIPrincipal* aSubjectPrincipal, nsIVariant** aData); nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex, @@ -300,8 +300,9 @@ protected: // array of items, each containing an array of format->data pairs nsTArray > mItems; - // array of files, containing only the files present in the dataTransfer - RefPtr mFiles; + // array of files and directories, containing only the files present in the + // dataTransfer + RefPtr mFileList; // the target of the drag. The drag and dragend events will fire at this. nsCOMPtr mDragTarget; diff --git a/dom/filehandle/ActorsChild.cpp b/dom/filehandle/ActorsChild.cpp index 8ade9df479cf..20885683d643 100644 --- a/dom/filehandle/ActorsChild.cpp +++ b/dom/filehandle/ActorsChild.cpp @@ -318,8 +318,7 @@ ConvertActorToFile(FileHandleBase* aFileHandle, actor->SetMysteryBlobInfo(mutableFile->Name(), mutableFile->Type(), size.get_uint64_t(), - lastModified.get_int64_t(), - BlobDirState::eUnknownIfDir); + lastModified.get_int64_t()); RefPtr blobImpl = actor->GetBlobImpl(); MOZ_ASSERT(blobImpl); diff --git a/dom/filesystem/CreateDirectoryTask.cpp b/dom/filesystem/CreateDirectoryTask.cpp index b929c5038556..3cade3a232b0 100644 --- a/dom/filesystem/CreateDirectoryTask.cpp +++ b/dom/filesystem/CreateDirectoryTask.cpp @@ -16,33 +16,73 @@ namespace mozilla { namespace dom { -CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aPath) +/* static */ already_AddRefed +CreateDirectoryTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateDirectoryTask(aFileSystem, aTargetPath); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); } -CreateDirectoryTask::CreateDirectoryTask( - FileSystemBase* aFileSystem, - const FileSystemCreateDirectoryParams& aParam, - FileSystemRequestParent* aParent) - : FileSystemTaskBase(aFileSystem, aParam, aParent) +/* static */ already_AddRefed +CreateDirectoryTask::Create(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateDirectoryTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); +} + +CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent) + : FileSystemTaskBase(aFileSystem, aParam, aParent) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - mTargetRealPath = aParam.realPath(); } CreateDirectoryTask::~CreateDirectoryTask() @@ -59,25 +99,44 @@ CreateDirectoryTask::GetPromise() } FileSystemParams -CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const +CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemCreateDirectoryParams(); + } + + return FileSystemCreateDirectoryParams(aSerializedDOMPath, path); } FileSystemResponseValue -CreateDirectoryTask::GetSuccessRequestResult() const +CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemDirectoryResponse(mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); + } + + return FileSystemDirectoryResponse(path); } void -CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemDirectoryResponse r = aValue; - mTargetRealPath = r.realPath(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + NS_WARN_IF(aRv.Failed()); } nsresult @@ -91,13 +150,8 @@ CreateDirectoryTask::Work() return NS_ERROR_FAILURE; } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool fileExists; - nsresult rv = file->Exists(&fileExists); + nsresult rv = mTargetPath->Exists(&fileExists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -106,8 +160,12 @@ CreateDirectoryTask::Work() return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; } - rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770); - return rv; + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; } void @@ -124,7 +182,12 @@ CreateDirectoryTask::HandlerCallback() mPromise = nullptr; return; } - RefPtr dir = new Directory(mFileSystem, mTargetRealPath); + RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + mTargetPath, + Directory::eNotDOMRootDirectory, + mFileSystem); + MOZ_ASSERT(dir); + mPromise->MaybeResolve(dir); mPromise = nullptr; } diff --git a/dom/filesystem/CreateDirectoryTask.h b/dom/filesystem/CreateDirectoryTask.h index fb342c94bac1..837730629df3 100644 --- a/dom/filesystem/CreateDirectoryTask.h +++ b/dom/filesystem/CreateDirectoryTask.h @@ -16,16 +16,19 @@ namespace dom { class Promise; -class CreateDirectoryTask final - : public FileSystemTaskBase +class CreateDirectoryTask final : public FileSystemTaskBase { public: - CreateDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - ErrorResult& aRv); - CreateDirectoryTask(FileSystemBase* aFileSystem, - const FileSystemCreateDirectoryParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~CreateDirectoryTask(); @@ -38,13 +41,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -53,8 +58,15 @@ protected: HandlerCallback() override; private: + CreateDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath); + + CreateDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; }; } // namespace dom diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 871bf209d372..6a0bfd12d553 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -25,34 +25,104 @@ namespace dom { uint32_t CreateFileTask::sOutputBufferSize = 0; -CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - Blob* aBlobData, - InfallibleTArray& aArrayData, - bool replace, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aPath) - , mReplace(replace) +/* static */ already_AddRefed +CreateFileTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Blob* aBlobData, + InfallibleTArray& aArrayData, + bool aReplace, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - GetOutputBufferSize(); + + RefPtr task = + new CreateFileTask(aFileSystem, aTargetPath, aReplace); + + // aTargetPath can be null. In this case SetError will be called. + + task->GetOutputBufferSize(); + if (aBlobData) { if (XRE_IsParentProcess()) { - aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv); - NS_WARN_IF(aRv.Failed()); + aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } } else { - mBlobData = aBlobData; + task->mBlobData = aBlobData; } } - mArrayData.SwapElements(aArrayData); + + task->mArrayData.SwapElements(aArrayData); + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +CreateFileTask::Create(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateFileTask(aFileSystem, aParam, aParent); + + task->GetOutputBufferSize(); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mReplace = aParam.replace(); + + auto& data = aParam.data(); + + if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { + task->mArrayData = data; + return task.forget(); + } + + BlobParent* bp = static_cast(static_cast(data)); + RefPtr blobImpl = bp->GetBlobImpl(); + MOZ_ASSERT(blobImpl, "blobData should not be null."); + + ErrorResult rv; + blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + + return task.forget(); +} + +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aReplace) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mReplace(aReplace) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); } CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, @@ -61,32 +131,9 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, : FileSystemTaskBase(aFileSystem, aParam, aParent) , mReplace(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - GetOutputBufferSize(); - - mTargetRealPath = aParam.realPath(); - - mReplace = aParam.replace(); - - auto& data = aParam.data(); - - if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { - mArrayData = data; - return; - } - - BlobParent* bp = static_cast(static_cast(data)); - RefPtr blobImpl = bp->GetBlobImpl(); - MOZ_ASSERT(blobImpl, "blobData should not be null."); - - ErrorResult rv; - blobImpl->GetInternalStream(getter_AddRefs(mBlobStream), rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - } } CreateFileTask::~CreateFileTask() @@ -107,12 +154,18 @@ CreateFileTask::GetPromise() } FileSystemParams -CreateFileTask::GetRequestParams(const nsString& aFileSystem) const +CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemCreateFileParams param; - param.filesystem() = aFileSystem; - param.realPath() = mTargetRealPath; + param.filesystem() = aSerializedDOMPath; + + aRv = mTargetPath->GetPath(param.realPath()); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + param.replace() = mReplace; if (mBlobData) { BlobChild* actor @@ -127,7 +180,7 @@ CreateFileTask::GetRequestParams(const nsString& aFileSystem) const } FileSystemResponseValue -CreateFileTask::GetSuccessRequestResult() const +CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); BlobParent* actor = GetBlobParent(mTargetBlobImpl); @@ -140,7 +193,8 @@ CreateFileTask::GetSuccessRequestResult() const } void -CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemFileResponse r = aValue; @@ -176,24 +230,19 @@ CreateFileTask::Work() return NS_ERROR_FAILURE; } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - - if (!mFileSystem->IsSafeFile(file)) { + if (!mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } bool exists = false; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (exists) { bool isFile = false; - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -207,19 +256,19 @@ CreateFileTask::Work() } // Remove the old file before creating. - rv = file->Remove(false); + rv = mTargetPath->Remove(false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr outputStream; - rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -262,7 +311,7 @@ CreateFileTask::Work() return NS_ERROR_FAILURE; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -281,7 +330,7 @@ CreateFileTask::Work() return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } diff --git a/dom/filesystem/CreateFileTask.h b/dom/filesystem/CreateFileTask.h index 7adf49f14f58..fc0ba14ceb0b 100644 --- a/dom/filesystem/CreateFileTask.h +++ b/dom/filesystem/CreateFileTask.h @@ -20,19 +20,22 @@ class Blob; class BlobImpl; class Promise; -class CreateFileTask final - : public FileSystemTaskBase +class CreateFileTask final : public FileSystemTaskBase { public: - CreateFileTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - Blob* aBlobData, - InfallibleTArray& aArrayData, - bool replace, - ErrorResult& aRv); - CreateFileTask(FileSystemBase* aFileSystem, - const FileSystemCreateFileParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aFile, + Blob* aBlobData, + InfallibleTArray& aArrayData, + bool replace, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~CreateFileTask(); @@ -45,13 +48,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -60,12 +65,20 @@ protected: HandlerCallback() override; private: + CreateFileTask(FileSystemBase* aFileSystem, + nsIFile* aFile, + bool aReplace); + + CreateFileTask(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent); + void GetOutputBufferSize() const; static uint32_t sOutputBufferSize; RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; // Not thread-safe and should be released on main thread. RefPtr mBlobData; diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 86a86351c4b9..4d6053489cfd 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -46,17 +46,17 @@ DeviceStorageFileSystem::DeviceStorageFileSystem( NS_WARN_IF(NS_FAILED(rv)); // Get the local path of the file system root. - // Since the child process is not allowed to access the file system, we only - // do this from the parent process. - if (!XRE_IsParentProcess()) { - return; - } nsCOMPtr rootFile; DeviceStorageFile::GetRootDirectoryForType(aStorageType, aStorageName, getter_AddRefs(rootFile)); NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath))); + + if (!XRE_IsParentProcess()) { + return; + } + FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath, mNormalizedLocalRootPath); @@ -132,8 +132,14 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aDir); - RefPtr fs = aDir->GetFileSystem(); - MOZ_ASSERT(fs); + + ErrorResult rv; + RefPtr fs = aDir->GetFileSystem(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + // Check if the given directory is from this storage. return fs->ToString() == mString; } diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index 999e39302005..a3ff29b14e6d 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" +#include "mozilla/dom/OSFileSystem.h" // Resolve the name collision of Microsoft's API name with macros defined in // Windows header files. Undefine the macro of CreateDirectory to avoid @@ -37,12 +38,19 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(Directory) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory) - tmp->mFileSystem->Unlink(); + if (tmp->mFileSystem) { + tmp->mFileSystem->Unlink(); + tmp->mFileSystem = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory) - tmp->mFileSystem->Traverse(cb); + if (tmp->mFileSystem) { + tmp->mFileSystem->Traverse(cb); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -59,23 +67,57 @@ NS_INTERFACE_MAP_END already_AddRefed Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) { - RefPtr task = new GetFileOrDirectoryTask( - aFileSystem, EmptyString(), true, aRv); - if (aRv.Failed()) { + MOZ_ASSERT(aFileSystem); + + nsCOMPtr path; + aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()), + true, getter_AddRefs(path)); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + GetFileOrDirectoryTask::Create(aFileSystem, path, eDOMRootDirectory, true, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); } -Directory::Directory(FileSystemBase* aFileSystem, - const nsAString& aPath) - : mFileSystem(aFileSystem) - , mPath(aPath) +/* static */ already_AddRefed +Directory::Create(nsPIDOMWindowInner* aWindow, nsIFile* aFile, + DirectoryType aType, FileSystemBase* aFileSystem) { - MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); - // Remove the trailing "/". - mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aFile); + +#ifdef DEBUG + bool isDir; + nsresult rv = aFile->IsDirectory(&isDir); + MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir); +#endif + + RefPtr directory = + new Directory(aWindow, aFile, aType, aFileSystem); + return directory.forget(); +} + +Directory::Directory(nsPIDOMWindowInner* aWindow, + nsIFile* aFile, + DirectoryType aType, + FileSystemBase* aFileSystem) + : mWindow(aWindow) + , mFileSystem(aFileSystem) + , mFile(aFile) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFile); + + // aFileSystem can be null. In this case we create a OSFileSystem when needed. } Directory::~Directory() @@ -85,7 +127,7 @@ Directory::~Directory() nsPIDOMWindowInner* Directory::GetParentObject() const { - return mFileSystem->GetWindow(); + return mWindow; } JSObject* @@ -95,25 +137,28 @@ Directory::WrapObject(JSContext* aCx, JS::Handle aGivenProto) } void -Directory::GetName(nsAString& aRetval) const +Directory::GetName(nsAString& aRetval, ErrorResult& aRv) { aRetval.Truncate(); - if (mPath.IsEmpty()) { - mFileSystem->GetRootName(aRetval); + if (mType == eDOMRootDirectory) { + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + fs->GetRootName(aRetval); return; } - aRetval = Substring(mPath, - mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1); + aRv = mFile->GetLeafName(aRetval); + NS_WARN_IF(aRv.Failed()); } already_AddRefed Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; RefPtr blobData; InfallibleTArray arrayData; bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace); @@ -138,15 +183,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, } } - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } RefPtr task = - new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv); - if (aRv.Failed()) { + CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -155,16 +205,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, already_AddRefed Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - RefPtr task = new CreateDirectoryTask( - mFileSystem, realPath, aRv); - if (aRv.Failed()) { + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + CreateDirectoryTask::Create(fs, realPath, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -173,16 +227,21 @@ Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv) already_AddRefed Directory::Get(const nsAString& aPath, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - RefPtr task = new GetFileOrDirectoryTask( - mFileSystem, realPath, false, aRv); - if (aRv.Failed()) { + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + GetFileOrDirectoryTask::Create(fs, realPath, eNotDOMRootDirectory, false, + aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -205,30 +264,33 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, ErrorResult& aRv) { nsresult error = NS_OK; - nsAutoString realPath; + nsCOMPtr realPath; RefPtr blob; // Check and get the target path. + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + if (aPath.IsFile()) { blob = aPath.GetAsFile().Impl(); } else if (aPath.IsString()) { - if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - } else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) { + error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath)); + } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) { error = NS_ERROR_DOM_SECURITY_ERR; } else { - realPath = aPath.GetAsDirectory().mPath; + realPath = aPath.GetAsDirectory().mFile; // The target must be a descendant of this directory. - if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) { + if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) { error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; } } - RefPtr task = new RemoveTask(mFileSystem, mPath, blob, realPath, - aRecursive, aRv); - if (aRv.Failed()) { + RefPtr task = + RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } task->SetError(error); @@ -237,23 +299,38 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, } void -Directory::GetPath(nsAString& aRetval) const +Directory::GetPath(nsAString& aRetval, ErrorResult& aRv) { - if (mPath.IsEmpty()) { - // The Directory ctor removes any trailing '/'; this is the root directory. + if (mType == eDOMRootDirectory) { aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); } else { - aRetval = mPath; + // TODO: this should be a bit different... + GetName(aRetval, aRv); } } -already_AddRefed -Directory::GetFilesAndDirectories() +nsresult +Directory::GetFullRealPath(nsAString& aPath) { - ErrorResult rv; + nsresult rv = mFile->GetPath(aPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +already_AddRefed +Directory::GetFilesAndDirectories(ErrorResult& aRv) +{ + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + RefPtr task = - new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv); - if (NS_WARN_IF(rv.Failed())) { + GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -268,16 +345,38 @@ Directory::SetContentFilters(const nsAString& aFilters) } FileSystemBase* -Directory::GetFileSystem() const +Directory::GetFileSystem(ErrorResult& aRv) { - return mFileSystem.get(); + if (!mFileSystem) { + nsCOMPtr parent; + aRv = mFile->GetParent(getter_AddRefs(parent)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + // Parent can be null if mFile is pointing to the top directory. + if (!parent) { + parent = mFile; + } + + nsAutoString path; + aRv = parent->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr fs = new OSFileSystem(path); + fs->Init(mWindow); + + mFileSystem = fs; + } + + return mFileSystem; } -bool -Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const +nsresult +Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const { - aRealPath.Truncate(); - nsString relativePath; relativePath = aPath; @@ -286,13 +385,22 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const relativePath.Trim(kWhitespace); if (!IsValidRelativePath(relativePath)) { - return false; + return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; } - aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) + - relativePath; + nsCOMPtr file; + nsresult rv = mFile->Clone(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - return true; + rv = file->AppendRelativePath(relativePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + file.forget(aFile); + return NS_OK; } // static diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index f9c6e641294f..3d8a420723e5 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -41,14 +41,37 @@ class Directory final , public nsWrapperCache { public: + struct BlobImplOrDirectoryPath + { + RefPtr mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory) -public: static already_AddRefed GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv); - Directory(FileSystemBase* aFileSystem, const nsAString& aPath); + enum DirectoryType { + // When a directory is selected using a HTMLInputElement, that will be the + // DOM root directory and its name will be '/'. All the sub directory will + // be called with they real name. We use this enum to mark what we must + // consider the '/' of this DOM filesystem. + eDOMRootDirectory, + + // All the sub directories of the '/' will be marked using this other value. + eNotDOMRootDirectory + }; + + static already_AddRefed + Create(nsPIDOMWindowInner* aWindow, nsIFile* aDirectory, + DirectoryType aType, FileSystemBase* aFileSystem = 0); // ========= Begin WebIDL bindings. =========== @@ -59,7 +82,7 @@ public: WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; void - GetName(nsAString& aRetval) const; + GetName(nsAString& aRetval, ErrorResult& aRv); already_AddRefed CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, @@ -80,10 +103,13 @@ public: // From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface : void - GetPath(nsAString& aRetval) const; + GetPath(nsAString& aRetval, ErrorResult& aRv); + + nsresult + GetFullRealPath(nsAString& aPath); already_AddRefed - GetFilesAndDirectories(); + GetFilesAndDirectories(ErrorResult& aRv); // =========== End WebIDL bindings.============ @@ -113,8 +139,12 @@ public: SetContentFilters(const nsAString& aFilters); FileSystemBase* - GetFileSystem() const; + GetFileSystem(ErrorResult& aRv); + private: + Directory(nsPIDOMWindowInner* aWindow, + nsIFile* aFile, DirectoryType aType, + FileSystemBase* aFileSystem = nullptr); ~Directory(); static bool @@ -122,17 +152,19 @@ private: /* * Convert relative DOM path to the absolute real path. - * @return true if succeed. false if the DOM path is invalid. */ - bool - DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const; + nsresult + DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const; already_AddRefed RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, ErrorResult& aRv); + nsCOMPtr mWindow; RefPtr mFileSystem; - nsString mPath; + nsCOMPtr mFile; + DirectoryType mType; + nsString mFilters; }; diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index e62706b62b74..3f0046525f76 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -68,26 +68,41 @@ FileSystemBase::GetLocalFile(const nsAString& aRealPath) const { MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); + + // Let's convert the input path to /path nsAutoString localPath; - FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath); - localPath = mLocalRootPath + localPath; + if (!aRealPath.IsEmpty() && + !StringBeginsWith(aRealPath, + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR))) { + localPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); + } + localPath.Append(aRealPath); + + // We have to normalize the path string in order to follow the separator + // schema of this OS. + nsAutoString normalizedPath; + FileSystemUtils::NormalizedPathToLocalPath(localPath, normalizedPath); + + // The full path is mLocalRootPath + normalizedPath. + nsAutoString path(mLocalRootPath); + path.Append(normalizedPath); + nsCOMPtr file; - nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file)); + nsresult rv = NS_NewLocalFile(path, false, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } + return file.forget(); } bool -FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const +FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const { MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); MOZ_ASSERT(aFile, "aFile Should not be null."); - aRealPath.Truncate(); - nsAutoString filePath; ErrorResult rv; aFile->GetMozFullPathInternal(filePath, rv); @@ -95,7 +110,13 @@ FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const return false; } - return LocalPathToRealPath(filePath, aRealPath); + rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath), + true, aPath); + if (NS_WARN_IF(rv.Failed())) { + return false; + } + + return true; } bool @@ -120,6 +141,7 @@ FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath, aRealPath.Truncate(); return false; } + aRealPath = Substring(path, mNormalizedLocalRootPath.Length()); return true; } diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index e48380663939..5437e2357be3 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -73,13 +73,8 @@ public: virtual bool IsSafeDirectory(Directory* aDir) const; - /* - * Get the real path (absolute DOM path) of the DOM file in the file system. - * If succeeded, returns true. Otherwise, returns false and set aRealPath to - * empty string. - */ bool - GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const; + GetRealPath(BlobImpl* aFile, nsIFile** aPath) const; /* * Get the permission name required to access this file system. diff --git a/dom/filesystem/FileSystemRequestParent.cpp b/dom/filesystem/FileSystemRequestParent.cpp index b43c7bb1c012..c730a0dd4e93 100644 --- a/dom/filesystem/FileSystemRequestParent.cpp +++ b/dom/filesystem/FileSystemRequestParent.cpp @@ -29,7 +29,10 @@ FileSystemRequestParent::~FileSystemRequestParent() case FileSystemParams::TFileSystem##name##Params: { \ const FileSystem##name##Params& p = aParams; \ mFileSystem = FileSystemBase::FromString(p.filesystem()); \ - task = new name##Task(mFileSystem, p, this); \ + task = name##Task::Create(mFileSystem, p, this, rv); \ + if (NS_WARN_IF(rv.Failed())) { \ + return false; \ + } \ break; \ } @@ -39,6 +42,8 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent, { MOZ_ASSERT(aParent, "aParent should not be null."); RefPtr task; + ErrorResult rv; + switch (aParams.type()) { FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory) diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index e77cf251262a..b1bfdc6abc36 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -106,12 +106,19 @@ FileSystemTaskBase::Start() return; } + ErrorResult rv; + FileSystemParams params = GetRequestParams(mFileSystem->ToString(), rv); + if (NS_WARN_IF(rv.Failed())) { + return; + } + // Retain a reference so the task object isn't deleted without IPDL's // knowledge. The reference will be released by // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild. NS_ADDREF_THIS(); + ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this, - GetRequestParams(mFileSystem->ToString())); + params); } NS_IMETHODIMP @@ -154,11 +161,17 @@ FileSystemTaskBase::GetRequestResult() const MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - if (HasError()) { - return FileSystemErrorResponse(mErrorValue); - } else { - return GetSuccessRequestResult(); + if (!HasError()) { + ErrorResult rv; + FileSystemResponseValue value = GetSuccessRequestResult(rv); + if (NS_WARN_IF(rv.Failed())) { + return FileSystemErrorResponse(rv.StealNSResult()); + } + + return value; } + + return FileSystemErrorResponse(mErrorValue); } void @@ -171,7 +184,9 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue) FileSystemErrorResponse r = aValue; mErrorValue = r.error(); } else { - SetSuccessRequestResult(aValue); + ErrorResult rv; + SetSuccessRequestResult(aValue, rv); + mErrorValue = rv.StealNSResult(); } } diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index 43dcceb93056..3da16d9b251d 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -176,7 +176,8 @@ protected: * @param filesystem The string representation of the file system. */ virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const = 0; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const = 0; /* * Wrap the task success result to FileSystemResponseValue for sending it @@ -185,7 +186,7 @@ protected: * send the task success result back to the child process. */ virtual FileSystemResponseValue - GetSuccessRequestResult() const = 0; + GetSuccessRequestResult(ErrorResult& aRv) const = 0; /* * Unwrap the IPC message to get the task success result. @@ -194,7 +195,8 @@ protected: * success result. */ virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) = 0; bool HasError() const { return mErrorValue != NS_OK; } diff --git a/dom/filesystem/FileSystemUtils.cpp b/dom/filesystem/FileSystemUtils.cpp index cf175bcaa24c..1e62e4c56bce 100644 --- a/dom/filesystem/FileSystemUtils.cpp +++ b/dom/filesystem/FileSystemUtils.cpp @@ -65,5 +65,31 @@ FileSystemUtils::IsDescendantPath(const nsAString& aPath, return true; } +// static +bool +FileSystemUtils::IsDescendantPath(nsIFile* aFile, + nsIFile* aDescendantFile) +{ + nsAutoString path; + nsresult rv = aFile->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoString descendantPath; + rv = aDescendantFile->GetPath(descendantPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + // Check the sub-directory path to see if it has the parent path as prefix. + if (descendantPath.Length() <= path.Length() || + !StringBeginsWith(descendantPath, path)) { + return false; + } + + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/FileSystemUtils.h b/dom/filesystem/FileSystemUtils.h index abf30c3cf5f1..cadead05abea 100644 --- a/dom/filesystem/FileSystemUtils.h +++ b/dom/filesystem/FileSystemUtils.h @@ -34,6 +34,12 @@ public: static void NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal); + /* + * Return true if aDescendantPath is a descendant of aPath. + */ + static bool + IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath); + /* * Return true if aDescendantPath is a descendant of aPath. Both aPath and * aDescendantPath are absolute DOM path. diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 7a450753a3b0..153691506d93 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -8,7 +8,6 @@ #include "HTMLSplitOnSpacesTokenizer.h" #include "js/Value.h" -#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" @@ -21,33 +20,80 @@ namespace mozilla { namespace dom { -GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - const nsAString& aFilters, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aTargetPath) - , mFilters(aFilters) +/* static */ already_AddRefed +GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetDirectoryListingTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mType = aParam.isRoot() + ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + return task.forget(); +} + +GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mFilters(aFilters) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); } GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, const FileSystemGetDirectoryListingParams& aParam, FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) - , mTargetRealPath(aParam.realPath()) , mFilters(aParam.filters()) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); } @@ -66,43 +112,88 @@ GetDirectoryListingTask::GetPromise() } FileSystemParams -GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const +GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath, + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemGetDirectoryListingParams(); + } + + return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path, + mType == Directory::eDOMRootDirectory, mFilters); } +void +GetDirectoryListingTask::CreateNormalizedRelativePath(const nsAString& aPath, + nsAString& aRelativePath) const +{ + uint32_t rootPathLen = mFileSystem->GetLocalRootPath().Length(); + FileSystemUtils::LocalPathToNormalizedPath( + Substring(aPath, rootPathLen, aPath.Length() - rootPathLen), aRelativePath); +} + FileSystemResponseValue -GetDirectoryListingTask::GetSuccessRequestResult() const +GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); InfallibleTArray blobs; - for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) { - BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]); - if (blobParent) { - blobs.AppendElement(blobParent); + nsTArray inputs; + + for (unsigned i = 0; i < mTargetData.Length(); i++) { + if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { + BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl); + if (!blobParent) { + continue; + } + + FileSystemDirectoryListingResponseBlob blobData; + blobData.blobParent() = blobParent; + inputs.AppendElement(blobData); + } else { + MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + FileSystemDirectoryListingResponseDirectory directoryData; + directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath; + inputs.AppendElement(directoryData); } } + FileSystemDirectoryListingResponse response; - response.blobsParent().SwapElements(blobs); + response.data().SwapElements(inputs); return response; } void -GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aValue.type() == FileSystemResponseValue::TFileSystemDirectoryListingResponse); FileSystemDirectoryListingResponse r = aValue; - nsTArray& blobs = r.blobsChild(); + for (uint32_t i = 0; i < r.data().Length(); ++i) { + const FileSystemDirectoryListingResponseData& data = r.data()[i]; - for (unsigned i = 0; i < blobs.Length(); i++) { - mTargetBlobImpls.AppendElement(static_cast(blobs[i])->GetBlobImpl()); + if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) { + PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild(); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + element->mBlobImpl = static_cast(blob)->GetBlobImpl(); + } else { + MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + element->mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath(); + } } } @@ -117,27 +208,19 @@ GetDirectoryListingTask::Work() return NS_ERROR_FAILURE; } - // Whether we want to get the root directory. - bool getRoot = mTargetRealPath.IsEmpty(); - - nsCOMPtr dir = mFileSystem->GetLocalFile(mTargetRealPath); - if (!dir) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists; - nsresult rv = dir->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { - if (!getRoot) { + if (mType == Directory::eNotRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } // If the root directory doesn't exit, create it. - rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777); + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -145,7 +228,7 @@ GetDirectoryListingTask::Work() // Get isDirectory. bool isDir; - rv = dir->IsDirectory(&isDir); + rv = mTargetPath->IsDirectory(&isDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -155,7 +238,7 @@ GetDirectoryListingTask::Work() } nsCOMPtr entries; - rv = dir->GetDirectoryEntries(getter_AddRefs(entries)); + rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -186,7 +269,7 @@ GetDirectoryListingTask::Work() nsCOMPtr currFile = do_QueryInterface(supp); MOZ_ASSERT(currFile); - + bool isLink, isSpecial, isFile; if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) || NS_FAILED(currFile->IsSpecial(&isSpecial))) || @@ -213,9 +296,22 @@ GetDirectoryListingTask::Work() } } - BlobImplFile* impl = new BlobImplFile(currFile); - impl->LookupAndCacheIsDirectory(); - mTargetBlobImpls.AppendElement(impl); + if (isDir) { + nsAutoString path; + if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) { + continue; + } + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + element->mDirectoryPath = path; + } else { + BlobImplFile* impl = new BlobImplFile(currFile); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + element->mBlobImpl = impl; + } } return NS_OK; } @@ -235,7 +331,7 @@ GetDirectoryListingTask::HandlerCallback() return; } - size_t count = mTargetBlobImpls.Length(); + size_t count = mTargetData.Length(); Sequence listing; @@ -246,26 +342,65 @@ GetDirectoryListingTask::HandlerCallback() } for (unsigned i = 0; i < count; i++) { - if (mTargetBlobImpls[i]->IsDirectory()) { - nsAutoString name; - mTargetBlobImpls[i]->GetName(name); - nsAutoString path(mTargetRealPath); - path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); - path.Append(name); + if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) { #ifdef DEBUG if (XRE_IsParentProcess()) { - nsCOMPtr file = mFileSystem->GetLocalFile(path); + nsCOMPtr file; + nsresult rv = NS_NewLocalFile(mTargetData[i].mDirectoryPath, false, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + bool exist; - file->Exists(&exist); - MOZ_ASSERT(exist); + rv = file->Exists(&exist); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + + // We cannot assert here because the file can be done in the meantime. + NS_ASSERTION(exist, "The file doesn't exist anymore?!?"); + + nsAutoString normalizedLocalRootPath; + FileSystemUtils::NormalizedPathToLocalPath(mFileSystem->GetLocalRootPath(), + normalizedLocalRootPath); + + nsAutoString directoryPath; + FileSystemUtils::NormalizedPathToLocalPath(mTargetData[i].mDirectoryPath, + directoryPath); + + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(normalizedLocalRootPath, + directoryPath)); } #endif - RefPtr directory = new Directory(mFileSystem, path); + + nsCOMPtr directoryPath; + NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath); + nsresult rv = NS_NewNativeLocalFile(path, true, + getter_AddRefs(directoryPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + + RefPtr directory = Directory::Create(mFileSystem->GetWindow(), + directoryPath, + Directory::eNotRootDirectory, + mFileSystem); + MOZ_ASSERT(directory); + // Propogate mFilter onto sub-Directory object: directory->SetContentFilters(mFilters); listing[i].SetAsDirectory() = directory; } else { - listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]); + MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl); + listing[i].SetAsFile() = + File::Create(mFileSystem->GetWindow(), mTargetData[i].mBlobImpl); } } diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index 4a6dc7b045ab..ddf774e96350 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_GetDirectoryListing_h #define mozilla_dom_GetDirectoryListing_h +#include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemTaskBase.h" #include "mozilla/ErrorResult.h" #include "nsAutoPtr.h" @@ -16,18 +17,21 @@ namespace dom { class BlobImpl; -class GetDirectoryListingTask final - : public FileSystemTaskBase +class GetDirectoryListingTask final : public FileSystemTaskBase { public: - // If aDirectoryOnly is set, we should ensure that the target is a directory. - GetDirectoryListingTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - const nsAString& aFilters, - ErrorResult& aRv); - GetDirectoryListingTask(FileSystemBase* aFileSystem, - const FileSystemGetDirectoryListingParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~GetDirectoryListingTask(); @@ -37,15 +41,28 @@ public: virtual void GetPermissionAccessType(nsCString& aAccess) const override; -protected: + +private: + // If aDirectoryOnly is set, we should ensure that the target is a directory. + GetDirectoryListingTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters); + + GetDirectoryListingTask(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent); + virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -53,14 +70,18 @@ protected: virtual void HandlerCallback() override; -private: + void + CreateNormalizedRelativePath(const nsAString& aPath, + nsAString& aRelativePath) const; + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; nsString mFilters; + Directory::DirectoryType mType; // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. - nsTArray> mTargetBlobImpls; + nsTArray mTargetData; }; } // namespace dom diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 5d210243fcd7..4cbc443e5038 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -7,7 +7,6 @@ #include "GetFileOrDirectoryTask.h" #include "js/Value.h" -#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" @@ -20,37 +19,82 @@ namespace mozilla { namespace dom { -GetFileOrDirectoryTask::GetFileOrDirectoryTask( - FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - bool aDirectoryOnly, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aTargetPath) - , mIsDirectory(aDirectoryOnly) +/* static */ already_AddRefed +GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); } -GetFileOrDirectoryTask::GetFileOrDirectoryTask( - FileSystemBase* aFileSystem, - const FileSystemGetFileOrDirectoryParams& aParam, - FileSystemRequestParent* aParent) +/* static */ already_AddRefed +GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetFileOrDirectoryTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mType = aParam.isRoot() + ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + return task.forget(); +} + +GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mIsDirectory(aDirectoryOnly) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); +} + +GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) , mIsDirectory(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - mTargetRealPath = aParam.realPath(); } GetFileOrDirectoryTask::~GetFileOrDirectoryTask() @@ -67,18 +111,33 @@ GetFileOrDirectoryTask::GetPromise() } FileSystemParams -GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const +GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemGetFileOrDirectoryParams(); + } + + return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path, + mType == Directory::eDOMRootDirectory); } FileSystemResponseValue -GetFileOrDirectoryTask::GetSuccessRequestResult() const +GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (mIsDirectory) { - return FileSystemDirectoryResponse(mTargetRealPath); + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); + } + + return FileSystemDirectoryResponse(path); } BlobParent* actor = GetBlobParent(mTargetBlobImpl); @@ -91,7 +150,8 @@ GetFileOrDirectoryTask::GetSuccessRequestResult() const } void -GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); switch (aValue.type()) { @@ -104,7 +164,13 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a } case FileSystemResponseValue::TFileSystemDirectoryResponse: { FileSystemDirectoryResponse r = aValue; - mTargetRealPath = r.realPath(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + mIsDirectory = true; break; } @@ -127,33 +193,26 @@ GetFileOrDirectoryTask::Work() } // Whether we want to get the root directory. - bool getRoot = mTargetRealPath.IsEmpty(); - - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { - if (!getRoot) { + if (mType == Directory::eNotRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } // If the root directory doesn't exit, create it. - rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777); + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } // Get isDirectory. - rv = file->IsDirectory(&mIsDirectory); + rv = mTargetPath->IsDirectory(&mIsDirectory); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -163,13 +222,13 @@ GetFileOrDirectoryTask::Work() } // Check if the root is a directory. - if (getRoot) { + if (mType == Directory::eDOMRootDirectory) { return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; } bool isFile; // Get isFile - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -179,11 +238,11 @@ GetFileOrDirectoryTask::Work() return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; } - if (!mFileSystem->IsSafeFile(file)) { + if (!mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -204,7 +263,12 @@ GetFileOrDirectoryTask::HandlerCallback() } if (mIsDirectory) { - RefPtr dir = new Directory(mFileSystem, mTargetRealPath); + RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + mTargetPath, + mType, + mFileSystem); + MOZ_ASSERT(dir); + mPromise->MaybeResolve(dir); mPromise = nullptr; return; diff --git a/dom/filesystem/GetFileOrDirectoryTask.h b/dom/filesystem/GetFileOrDirectoryTask.h index 3d76bdca980b..4430e567d7e5 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.h +++ b/dom/filesystem/GetFileOrDirectoryTask.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_GetFileOrDirectory_h #define mozilla_dom_GetFileOrDirectory_h +#include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemTaskBase.h" #include "nsAutoPtr.h" #include "mozilla/ErrorResult.h" @@ -16,18 +17,21 @@ namespace dom { class BlobImpl; -class GetFileOrDirectoryTask final - : public FileSystemTaskBase +class GetFileOrDirectoryTask final : public FileSystemTaskBase { public: - // If aDirectoryOnly is set, we should ensure that the target is a directory. - GetFileOrDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - bool aDirectoryOnly, - ErrorResult& aRv); - GetFileOrDirectoryTask(FileSystemBase* aFileSystem, - const FileSystemGetFileOrDirectoryParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~GetFileOrDirectoryTask(); @@ -39,13 +43,15 @@ public: GetPermissionAccessType(nsCString& aAccess) const override; protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -54,10 +60,22 @@ protected: HandlerCallback() override; private: + // If aDirectoryOnly is set, we should ensure that the target is a directory. + GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly); + + GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; + // Whether we get a directory. bool mIsDirectory; + Directory::DirectoryType mType; // This cannot be a File bacause this object is created on a different // thread and File is not thread-safe. Let's use the BlobImpl instead. diff --git a/dom/filesystem/PFileSystemRequest.ipdl b/dom/filesystem/PFileSystemRequest.ipdl index 9e9a7c71c0ca..6d2e934930e1 100644 --- a/dom/filesystem/PFileSystemRequest.ipdl +++ b/dom/filesystem/PFileSystemRequest.ipdl @@ -20,9 +20,26 @@ struct FileSystemDirectoryResponse nsString realPath; }; +struct FileSystemDirectoryListingResponseBlob +{ + PBlob blob; +}; + +struct FileSystemDirectoryListingResponseDirectory +{ + // This is the full real path for the directory that we are sending via IPC. + nsString directoryRealPath; +}; + +union FileSystemDirectoryListingResponseData +{ + FileSystemDirectoryListingResponseBlob; + FileSystemDirectoryListingResponseDirectory; +}; + struct FileSystemDirectoryListingResponse { - PBlob[] blobs; + FileSystemDirectoryListingResponseData[] data; }; struct FileSystemErrorResponse diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 1e3df8f7bd28..8b806b72eee6 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -18,27 +18,94 @@ namespace mozilla { namespace dom { +/* static */ already_AddRefed +RemoveTask::Create(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + MOZ_ASSERT(aDirPath); + + RefPtr task = + new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive); + + // aTargetPath can be null. In this case SetError will be called. + + nsCOMPtr globalObject = + do_QueryInterface(aFileSystem->GetWindow()); + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +RemoveTask::Create(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new RemoveTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 directoryPath(aParam.directory()); + aRv = NS_NewNativeLocalFile(directoryPath, true, + getter_AddRefs(task->mDirPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mRecursive = aParam.recursive(); + + const FileSystemPathOrFileValue& target = aParam.target(); + + if (target.type() == FileSystemPathOrFileValue::TnsString) { + NS_ConvertUTF16toUTF8 path(target); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); + } + + BlobParent* bp = static_cast(static_cast(target)); + task->mTargetBlobImpl = bp->GetBlobImpl(); + MOZ_ASSERT(task->mTargetBlobImpl); + + return task.forget(); +} + RemoveTask::RemoveTask(FileSystemBase* aFileSystem, - const nsAString& aDirPath, + nsIFile* aDirPath, BlobImpl* aTargetBlob, - const nsAString& aTargetPath, - bool aRecursive, - ErrorResult& aRv) + nsIFile* aTargetPath, + bool aRecursive) : FileSystemTaskBase(aFileSystem) - , mDirRealPath(aDirPath) + , mDirPath(aDirPath) , mTargetBlobImpl(aTargetBlob) - , mTargetRealPath(aTargetPath) + , mTargetPath(aTargetPath) , mRecursive(aRecursive) , mReturnValue(false) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; - } - mPromise = Promise::Create(globalObject, aRv); + MOZ_ASSERT(aDirPath); } RemoveTask::RemoveTask(FileSystemBase* aFileSystem, @@ -48,25 +115,9 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem, , mRecursive(false) , mReturnValue(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - - mDirRealPath = aParam.directory(); - - mRecursive = aParam.recursive(); - - const FileSystemPathOrFileValue& target = aParam.target(); - - if (target.type() == FileSystemPathOrFileValue::TnsString) { - mTargetRealPath = target; - return; - } - - BlobParent* bp = static_cast(static_cast(target)); - mTargetBlobImpl = bp->GetBlobImpl(); - MOZ_ASSERT(mTargetBlobImpl); } RemoveTask::~RemoveTask() @@ -83,36 +134,49 @@ RemoveTask::GetPromise() } FileSystemParams -RemoveTask::GetRequestParams(const nsString& aFileSystem) const +RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemRemoveParams param; - param.filesystem() = aFileSystem; - param.directory() = mDirRealPath; + param.filesystem() = aSerializedDOMPath; + + aRv = mDirPath->GetPath(param.directory()); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + param.recursive() = mRecursive; if (mTargetBlobImpl) { RefPtr blob = Blob::Create(mFileSystem->GetWindow(), - mTargetBlobImpl); + mTargetBlobImpl); BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob); if (actor) { param.target() = actor; } } else { - param.target() = mTargetRealPath; + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + + param.target() = path; } return param; } FileSystemResponseValue -RemoveTask::GetSuccessRequestResult() const +RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); return FileSystemBooleanResponse(mReturnValue); } void -RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemBooleanResponse r = aValue; @@ -130,23 +194,19 @@ RemoveTask::Work() return NS_ERROR_FAILURE; } - // Get the DOM path if a File is passed as the target. + // Get the path if a File is passed as the target. if (mTargetBlobImpl) { - if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) { + if (!mFileSystem->GetRealPath(mTargetBlobImpl, + getter_AddRefs(mTargetPath))) { return NS_ERROR_DOM_SECURITY_ERR; } - if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) { + if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) { return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; } } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists = false; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -157,16 +217,16 @@ RemoveTask::Work() } bool isFile = false; - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (isFile && !mFileSystem->IsSafeFile(file)) { + if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } - rv = file->Remove(mRecursive); + rv = mTargetPath->Remove(mRecursive); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/filesystem/RemoveTask.h b/dom/filesystem/RemoveTask.h index 0f75d9f9eb3c..d3fbfd1ebbe7 100644 --- a/dom/filesystem/RemoveTask.h +++ b/dom/filesystem/RemoveTask.h @@ -17,19 +17,22 @@ namespace dom { class BlobImpl; class Promise; -class RemoveTask final - : public FileSystemTaskBase +class RemoveTask final : public FileSystemTaskBase { public: - RemoveTask(FileSystemBase* aFileSystem, - const nsAString& aDirPath, - BlobImpl* aTargetBlob, - const nsAString& aTargetPath, - bool aRecursive, - ErrorResult& aRv); - RemoveTask(FileSystemBase* aFileSystem, - const FileSystemRemoveParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~RemoveTask(); @@ -42,13 +45,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -57,12 +62,23 @@ protected: HandlerCallback() override; private: + RemoveTask(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive); + + RemoveTask(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mDirRealPath; + nsCOMPtr mDirPath; + // This cannot be a File because this object will be used on a different // thread and File is not thread-safe. Let's use the BlobImpl instead. RefPtr mTargetBlobImpl; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; bool mRecursive; bool mReturnValue; }; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 64bfebea7366..34f8dca5be1e 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -251,16 +251,63 @@ class HTMLInputElementState final : public nsISupports mValue = aValue; } - const nsTArray>& GetBlobImpls() + void + GetFilesOrDirectories(nsPIDOMWindowInner* aWindow, + nsTArray& aResult) const { - return mBlobImpls; + for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) { + if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { + RefPtr file = + File::Create(aWindow, + mBlobImplsOrDirectoryPaths[i].mBlobImpl); + MOZ_ASSERT(file); + + OwningFileOrDirectory* element = aResult.AppendElement(); + element->SetAsFile() = file; + } else { + MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + + nsCOMPtr file; + NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath); + nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + RefPtr directory = Directory::Create(aWindow, file, + Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + OwningFileOrDirectory* element = aResult.AppendElement(); + element->SetAsDirectory() = directory; + } + } } - void SetBlobImpls(const nsTArray>& aFile) + void SetFilesOrDirectories(const nsTArray& aArray) { - mBlobImpls.Clear(); - for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) { - mBlobImpls.AppendElement(aFile[i]->Impl()); + mBlobImplsOrDirectoryPaths.Clear(); + for (uint32_t i = 0; i < aArray.Length(); ++i) { + if (aArray[i].IsFile()) { + Directory::BlobImplOrDirectoryPath* data = + mBlobImplsOrDirectoryPaths.AppendElement(); + + data->mBlobImpl = aArray[i].GetAsFile()->Impl(); + data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + } else { + MOZ_ASSERT(aArray[i].IsDirectory()); + nsAutoString fullPath; + nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + Directory::BlobImplOrDirectoryPath* data = + mBlobImplsOrDirectoryPaths.AppendElement(); + + data->mDirectoryPath = fullPath; + data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + } } } @@ -274,7 +321,9 @@ class HTMLInputElementState final : public nsISupports ~HTMLInputElementState() {} nsString mValue; - nsTArray> mBlobImpls; + + nsTArray mBlobImplsOrDirectoryPaths; + bool mChecked; bool mCheckedSet; }; @@ -340,33 +389,65 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error) namespace { /** - * This may return nullptr if aDomFile's implementation of + * This may return nullptr if the DOM File's implementation of * File::mozFullPathInternal does not successfully return a non-empty * string that is a valid path. This can happen on Firefox OS, for example, * where the file picker can create Blobs. */ static already_AddRefed -DOMFileToLocalFile(File* aDomFile) +DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData) { nsString path; - ErrorResult rv; - aDomFile->GetMozFullPathInternal(path, rv); - if (rv.Failed() || path.IsEmpty()) { - rv.SuppressException(); - return nullptr; + + if (aData.IsFile()) { + ErrorResult rv; + aData.GetAsFile()->GetMozFullPathInternal(path, rv); + if (rv.Failed() || path.IsEmpty()) { + rv.SuppressException(); + return nullptr; + } + } else { + MOZ_ASSERT(aData.IsDirectory()); + aData.GetAsDirectory()->GetFullRealPath(path); } nsCOMPtr localFile; - rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, - getter_AddRefs(localFile)); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); + nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, + getter_AddRefs(localFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } return localFile.forget(); } +void +GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData, + nsAString& aName) +{ + if (aData.IsFile()) { + aData.GetAsFile()->GetName(aName); + } else { + MOZ_ASSERT(aData.IsDirectory()); + ErrorResult rv; + aData.GetAsDirectory()->GetName(aName, rv); + NS_WARN_IF(rv.Failed()); + } +} + +void +GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData, + nsAString& aPath, + ErrorResult& aRv) +{ + if (aData.IsFile()) { + aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv); + } else { + MOZ_ASSERT(aData.IsDirectory()); + aData.GetAsDirectory()->GetFullRealPath(aPath); + } +} + } // namespace @@ -383,7 +464,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) mFilePicker->GetMode(&mode); // Collect new selected filenames - nsTArray> newFiles; + nsTArray newFilesOrDirectories; if (mode == static_cast(nsIFilePicker::modeOpenMultiple)) { nsCOMPtr iter; nsresult rv = @@ -402,9 +483,12 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) nsCOMPtr domBlob = do_QueryInterface(tmp); MOZ_ASSERT(domBlob, "Null file object from FilePicker's file enumerator?"); - if (domBlob) { - newFiles.AppendElement(static_cast(domBlob.get())); + if (!domBlob) { + continue; } + + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsFile() = static_cast(domBlob.get()); } } else { MOZ_ASSERT(mode == static_cast(nsIFilePicker::modeOpen) || @@ -416,16 +500,25 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) nsCOMPtr blob = do_QueryInterface(tmp); if (blob) { RefPtr file = static_cast(blob.get())->ToFile(); - newFiles.AppendElement(file); + MOZ_ASSERT(file); + + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; + } else if (tmp) { + RefPtr directory = static_cast(tmp.get()); + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsDirectory() = directory; } } - if (newFiles.IsEmpty()) { + if (newFilesOrDirectories.IsEmpty()) { return NS_OK; } // Store the last used directory using the content pref service: - nsCOMPtr file = DOMFileToLocalFile(newFiles[0]); + nsCOMPtr file = + DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]); + if (file) { nsCOMPtr lastUsedDir; file->GetParent(getter_AddRefs(lastUsedDir)); @@ -436,7 +529,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) // The text control frame (if there is one) isn't going to send a change // event because it will think this is done by a script. // So, we can safely send one by ourself. - mInput->SetFiles(newFiles, true); + mInput->SetFilesOrDirectories(newFilesOrDirectories, true); return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(), static_cast(mInput.get()), NS_LITERAL_STRING("change"), true, @@ -672,7 +765,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) // Set default directry and filename nsAutoString defaultName; - const nsTArray>& oldFiles = GetFilesInternal(); + const nsTArray& oldFiles = + GetFilesOrDirectoriesInternal(); nsCOMPtr callback = new HTMLInputElement::nsFilePickerShownCallback(this, filePicker); @@ -681,18 +775,10 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) aType != FILE_PICKER_DIRECTORY) { nsString path; - ErrorResult error; - oldFiles[0]->GetMozFullPathInternal(path, error); - if (NS_WARN_IF(error.Failed())) { - return error.StealNSResult(); - } - - nsCOMPtr localFile; - rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile)); - - if (NS_SUCCEEDED(rv)) { + nsCOMPtr localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]); + if (localFile) { nsCOMPtr parentFile; - rv = localFile->GetParent(getter_AddRefs(parentFile)); + nsresult rv = localFile->GetParent(getter_AddRefs(parentFile)); if (NS_SUCCEEDED(rv)) { filePicker->SetDisplayDirectory(parentFile); } @@ -703,7 +789,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) // one file was selected before. if (oldFiles.Length() == 1) { nsAutoString leafName; - oldFiles[0]->GetName(leafName); + GetDOMFileOrDirectoryName(oldFiles[0], leafName); + if (!leafName.IsEmpty()) { filePicker->SetDefaultString(leafName); } @@ -756,7 +843,7 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc, NS_PRECONDITION(docURI, "docURI is null"); nsCOMPtr loadContext = aDoc->GetLoadContext(); - nsCOMPtr prefCallback = + nsCOMPtr prefCallback = new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback); #ifdef MOZ_B2G @@ -935,7 +1022,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement, if (tmp->IsSingleLineTextControl(false)) { tmp->mInputData.mState->Traverse(cb); } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -944,7 +1031,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement, nsGenericHTMLFormElementWithState) NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity) NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise) if (tmp->IsSingleLineTextControl(false)) { @@ -1003,8 +1090,8 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co // we can just grab the pretty string and use it as wallpaper GetDisplayFileName(it->mStaticDocFileList); } else { - it->mFiles.Clear(); - it->mFiles.AppendElements(mFiles); + it->mFilesOrDirectories.Clear(); + it->mFilesOrDirectories.AppendElements(mFilesOrDirectories); } break; case VALUE_MODE_DEFAULT_ON: @@ -1442,9 +1529,9 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const #else // XXX We'd love to assert that this can't happen, but some mochitests // use SpecialPowers to circumvent our more sane security model. - if (!mFiles.IsEmpty()) { + if (!mFilesOrDirectories.IsEmpty()) { ErrorResult rv; - mFiles[0]->GetMozFullPath(aValue, rv); + GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } @@ -1456,10 +1543,10 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const #endif } else { // Just return the leaf name - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { aValue.Truncate(); } else { - mFiles[0]->GetName(aValue); + GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue); } } @@ -1494,8 +1581,8 @@ HTMLInputElement::IsValueEmpty() const void HTMLInputElement::ClearFiles(bool aSetValueChanged) { - nsTArray> files; - SetFiles(files, aSetValueChanged); + nsTArray data; + SetFilesOrDirectories(data, aSetValueChanged); } /* static */ Decimal @@ -2065,9 +2152,9 @@ void HTMLInputElement::MozGetFileNameArray(nsTArray& aArray, ErrorResult& aRv) { - for (uint32_t i = 0; i < mFiles.Length(); i++) { - nsString str; - mFiles[i]->GetMozFullPathInternal(str, aRv); + for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) { + nsAutoString str; + GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -2117,25 +2204,29 @@ HTMLInputElement::MozSetFileArray(const Sequence>& aFiles) if (!global) { return; } - nsTArray> files; + + nsTArray files; for (uint32_t i = 0; i < aFiles.Length(); ++i) { RefPtr file = File::Create(global, aFiles[i].get()->Impl()); MOZ_ASSERT(file); - files.AppendElement(file); + OwningFileOrDirectory* element = files.AppendElement(); + element->SetAsFile() = file; } - SetFiles(files, true); + + SetFilesOrDirectories(files, true); } void -HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv) +HTMLInputElement::MozSetFileNameArray(const Sequence& aFileNames, + ErrorResult& aRv) { if (XRE_IsContentProcess()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } - nsTArray> files; + nsTArray files; for (uint32_t i = 0; i < aFileNames.Length(); ++i) { nsCOMPtr file; @@ -2152,21 +2243,28 @@ HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, Er NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file)); } - if (file) { - nsCOMPtr global = OwnerDoc()->GetScopeObject(); - RefPtr domFile = File::CreateFromFile(global, file); - files.AppendElement(domFile); - } else { + if (!file) { continue; // Not much we can do if the file doesn't exist } + nsCOMPtr global = OwnerDoc()->GetScopeObject(); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + RefPtr domFile = File::CreateFromFile(global, file); + + OwningFileOrDirectory* element = files.AppendElement(); + element->SetAsFile() = domFile; } - SetFiles(files, true); + SetFilesOrDirectories(files, true); } NS_IMETHODIMP -HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLength) +HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, + uint32_t aLength) { if (!nsContentUtils::IsCallerChrome()) { // setting the value of a "FILE" input widget requires chrome privilege @@ -2379,14 +2477,14 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const return; } - if (mFiles.Length() == 1) { - mFiles[0]->GetName(aValue); + if (mFilesOrDirectories.Length() == 1) { + GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue); return; } nsXPIDLString value; - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) { nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, "NoFilesSelected", value); @@ -2396,7 +2494,7 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } } else { nsString count; - count.AppendInt(int(mFiles.Length())); + count.AppendInt(int(mFilesOrDirectories.Length())); const char16_t* params[] = { count.get() }; nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES, @@ -2407,13 +2505,13 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } void -HTMLInputElement::SetFiles(const nsTArray>& aFiles, - bool aSetValueChanged) +HTMLInputElement::SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, + bool aSetValueChanged) { - mFiles.Clear(); - mFiles.AppendElements(aFiles); + mFilesOrDirectories.Clear(); + mFilesOrDirectories.AppendElements(aFilesOrDirectories); - AfterSetFiles(aSetValueChanged); + AfterSetFilesOrDirectories(aSetValueChanged); } void @@ -2421,22 +2519,24 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged) { RefPtr files = static_cast(aFiles); - mFiles.Clear(); + mFilesOrDirectories.Clear(); if (aFiles) { uint32_t listLength; aFiles->GetLength(&listLength); for (uint32_t i = 0; i < listLength; i++) { RefPtr file = files->Item(i); - mFiles.AppendElement(file); + + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; } } - AfterSetFiles(aSetValueChanged); + AfterSetFilesOrDirectories(aSetValueChanged); } void -HTMLInputElement::AfterSetFiles(bool aSetValueChanged) +HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged) { // No need to flush here, if there's no frame at this point we // don't need to force creation of one just to tell it about this @@ -2454,11 +2554,11 @@ HTMLInputElement::AfterSetFiles(bool aSetValueChanged) // call under GetMozFullPath won't be rejected for not being urgent. // XXX Protected by the ifndef because the blob code doesn't allow us to send // this message in b2g. - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { mFirstFilePath.Truncate(); } else { ErrorResult rv; - mFiles[0]->GetMozFullPath(mFirstFilePath, rv); + GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } @@ -2538,9 +2638,11 @@ HTMLInputElement::UpdateFileList() if (mFileList) { mFileList->Clear(); - const nsTArray>& files = GetFilesInternal(); - for (uint32_t i = 0; i < files.Length(); ++i) { - if (!mFileList->Append(files[i])) { + const nsTArray& array = + GetFilesOrDirectoriesInternal(); + + for (uint32_t i = 0; i < array.Length(); ++i) { + if (array[i].IsFile() && !mFileList->Append(array[i].GetAsFile())) { return NS_ERROR_FAILURE; } } @@ -3988,7 +4090,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); if (numberControlFrame) { - if (aVisitor.mEvent->mMessage == eMouseDown && + if (aVisitor.mEvent->mMessage == eMouseDown && IsMutable()) { switch (numberControlFrame->GetSpinButtonForPointerEvent( aVisitor.mEvent->AsMouseEvent())) { @@ -4539,7 +4641,7 @@ HTMLInputElement::GetValueAsDate(const nsAString& aValue, } uint32_t endOfYearOffset = aValue.Length() - 6; - + if (aValue[endOfYearOffset] != '-' || aValue[endOfYearOffset + 3] != '-') { return false; @@ -4907,45 +5009,38 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv) return nullptr; } - const nsTArray>& filesAndDirs = GetFilesInternal(); + const nsTArray& filesAndDirs = + GetFilesOrDirectoriesInternal(); Sequence filesAndDirsSeq; - if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) { + if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), + mozilla::fallible_t())) { p->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return p.forget(); } for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) { - if (filesAndDirs[i]->IsDirectory()) { + if (filesAndDirs[i].IsDirectory()) { #if defined(ANDROID) || defined(MOZ_B2G) MOZ_ASSERT(false, "Directory picking should have been redirected to normal " "file picking for platforms that don't have a directory " "picker"); #endif - nsAutoString path; - filesAndDirs[i]->GetMozFullPathInternal(path, aRv); - if (aRv.Failed()) { - return nullptr; - } - int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR); - nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex); - RefPtr fs = new OSFileSystem(dirname); - fs->Init(OwnerDoc()->GetInnerWindow()); + RefPtr directory = filesAndDirs[i].GetAsDirectory(); - nsAutoString dompath(NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR)); - dompath.Append(Substring(path, leafSeparatorIndex + 1)); - RefPtr directory = new Directory(fs, dompath); // In future we could refactor SetFilePickerFiltersFromAccept to return a // semicolon separated list of file extensions and include that in the // filter string passed here. directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); filesAndDirsSeq[i].SetAsDirectory() = directory; } else { + MOZ_ASSERT(filesAndDirs[i].IsFile()); + // This file was directly selected by the user, so don't filter it. - filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i]; + filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile(); } } @@ -5579,13 +5674,18 @@ HTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission) if (mType == NS_FORM_INPUT_FILE) { // Submit files - const nsTArray>& files = GetFilesInternal(); + const nsTArray& files = + GetFilesOrDirectoriesInternal(); + bool hasBlobs = false; for (uint32_t i = 0; i < files.Length(); ++i) { - aFormSubmission->AddNameBlobOrNullPair(name, files[i]); + if (files[i].IsFile()) { + hasBlobs = true; + aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile()); + } } - if (files.IsEmpty()) { + if (!hasBlobs) { aFormSubmission->AddNameBlobOrNullPair(name, nullptr); } @@ -5619,9 +5719,9 @@ HTMLInputElement::SaveState() } break; case VALUE_MODE_FILENAME: - if (!mFiles.IsEmpty()) { + if (!mFilesOrDirectories.IsEmpty()) { inputState = new HTMLInputElementState(); - inputState->SetBlobImpls(mFiles); + inputState->SetFilesOrDirectories(mFilesOrDirectories); } break; case VALUE_MODE_VALUE: @@ -5790,7 +5890,7 @@ HTMLInputElement::AddStates(EventStates aStates) } } } - nsGenericHTMLFormElementWithState::AddStates(aStates); + nsGenericHTMLFormElementWithState::AddStates(aStates); } void @@ -5827,20 +5927,13 @@ HTMLInputElement::RestoreState(nsPresState* aState) break; case VALUE_MODE_FILENAME: { - const nsTArray>& blobImpls = inputState->GetBlobImpls(); + nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow(); + if (window) { + nsTArray array; + inputState->GetFilesOrDirectories(window, array); - nsCOMPtr global = OwnerDoc()->GetScopeObject(); - MOZ_ASSERT(global); - - nsTArray> files; - for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) { - RefPtr file = File::Create(global, blobImpls[i]); - MOZ_ASSERT(file); - - files.AppendElement(file); + SetFilesOrDirectories(array, true); } - - SetFiles(files, true); } break; case VALUE_MODE_VALUE: @@ -6350,15 +6443,15 @@ HTMLInputElement::IsValueMissing() const switch (GetValueMode()) { case VALUE_MODE_VALUE: return IsValueEmpty(); + case VALUE_MODE_FILENAME: - { - const nsTArray>& files = GetFilesInternal(); - return files.IsEmpty(); - } + return GetFilesOrDirectoriesInternal().IsEmpty(); + case VALUE_MODE_DEFAULT_ON: // This should not be used for type radio. // See the MOZ_ASSERT at the beginning of the method. return !mChecked; + case VALUE_MODE_DEFAULT: default: return false; diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 98d35e64a045..1988ce4b21d2 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -20,6 +20,7 @@ #include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit() #include "mozilla/dom/HTMLInputElementBinding.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/UnionTypes.h" #include "nsIFilePicker.h" #include "nsIContentPrefService2.h" #include "mozilla/Decimal.h" @@ -220,12 +221,13 @@ public: void GetDisplayFileName(nsAString& aFileName) const; - const nsTArray>& GetFilesInternal() const + const nsTArray& GetFilesOrDirectoriesInternal() const { - return mFiles; + return mFilesOrDirectories; } - void SetFiles(const nsTArray>& aFiles, bool aSetValueChanged); + void SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, + bool aSetValueChanged); void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged); // Called when a nsIFilePicker or a nsIColorPicker terminate. @@ -925,9 +927,9 @@ protected: nsresult UpdateFileList(); /** - * Called after calling one of the SetFiles() functions. + * Called after calling one of the SetFilesOrDirectories() functions. */ - void AfterSetFiles(bool aSetValueChanged); + void AfterSetFilesOrDirectories(bool aSetValueChanged); /** * Determine whether the editor needs to be initialized explicitly for @@ -1266,16 +1268,16 @@ protected: } mInputData; /** - * The value of the input if it is a file input. This is the list of filenames - * used when uploading a file. It is vital that this is kept separate from - * mValue so that it won't be possible to 'leak' the value from a text-input - * to a file-input. Additionally, the logic for this value is kept as simple - * as possible to avoid accidental errors where the wrong filename is used. - * Therefor the list of filenames is always owned by this member, never by - * the frame. Whenever the frame wants to change the filename it has to call - * SetFileNames to update this member. + * The value of the input if it is a file input. This is the list of files or + * directories DOM objects used when uploading a file. It is vital that this + * is kept separate from mValue so that it won't be possible to 'leak' the + * value from a text-input to a file-input. Additionally, the logic for this + * value is kept as simple as possible to avoid accidental errors where the + * wrong filename is used. Therefor the list of filenames is always owned by + * this member, never by the frame. Whenever the frame wants to change the + * filename it has to call SetFilesOrDirectories to update this member. */ - nsTArray> mFiles; + nsTArray mFilesOrDirectories; #ifndef MOZ_CHILD_PERMISSIONS /** diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index d6a3dad00186..a5351d8d2b58 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -187,30 +187,6 @@ private: return mBlobImpl->IsFile(); } - virtual void - LookupAndCacheIsDirectory() override - { - mBlobImpl->LookupAndCacheIsDirectory(); - } - - virtual void - SetIsDirectory(bool aIsDir) override - { - return mBlobImpl->SetIsDirectory(aIsDir); - } - - virtual bool - IsDirectory() const override - { - return mBlobImpl->IsDirectory(); - } - - virtual BlobDirState - GetDirState() const override - { - return mBlobImpl->GetDirState(); - } - virtual bool MayBeClonedToOtherThreads() const override { diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 963676c0a44a..0b2a09f65add 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -444,8 +444,7 @@ ResolveMysteryFile(BlobImpl* aImpl, BlobChild* actor = ActorFromRemoteBlobImpl(aImpl); if (actor) { return actor->SetMysteryBlobInfo(aName, aContentType, - aSize, aLastModifiedDate, - BlobDirState::eUnknownIfDir); + aSize, aLastModifiedDate); } return true; } diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index ea59a50e8fdd..f3d7fbcdf919 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -670,8 +670,7 @@ public: EmptyBlobImpl(const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate) - : BlobImplBase(aName, aContentType, 0, aLastModifiedDate, - BlobDirState::eIsNotDir) + : BlobImplBase(aName, aContentType, 0, aLastModifiedDate) { mImmutable = true; } @@ -716,14 +715,12 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata final nsString mName; uint64_t mLength; int64_t mLastModifiedDate; - BlobDirState mDirState; bool mHasRecursed; const bool mIsSameProcessActor; explicit CreateBlobImplMetadata(bool aIsSameProcessActor) : mLength(0) , mLastModifiedDate(0) - , mDirState(BlobDirState::eUnknownIfDir) , mHasRecursed(false) , mIsSameProcessActor(aIsSameProcessActor) { @@ -965,7 +962,6 @@ CreateBlobImpl(const ParentBlobConstructorParams& aParams, metadata.mName = params.name(); metadata.mLength = params.length(); metadata.mLastModifiedDate = params.modDate(); - metadata.mDirState = BlobDirState(params.dirState()); } RefPtr blobImpl = @@ -1775,7 +1771,6 @@ public: const nsAString& aContentType, uint64_t aLength, int64_t aModDate, - BlobDirState aDirState, bool aIsSameProcessBlob); // For Blob. @@ -2053,18 +2048,6 @@ public: virtual bool IsFile() const override; - virtual void - LookupAndCacheIsDirectory() override; - - virtual void - SetIsDirectory(bool aIsDir) override; - - virtual bool - IsDirectory() const override; - - virtual BlobDirState - GetDirState() const override; - virtual bool MayBeClonedToOtherThreads() const override; @@ -2096,9 +2079,8 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, const nsAString& aContentType, uint64_t aLength, int64_t aModDate, - BlobDirState aDirState, bool aIsSameProcessBlob) - : BlobImplBase(aName, aContentType, aLength, aModDate, aDirState) + : BlobImplBase(aName, aContentType, aLength, aModDate) , mIsSlice(false) { if (aIsSameProcessBlob) { @@ -2814,34 +2796,6 @@ RemoteBlobImpl::IsFile() const return mBlobImpl->IsFile(); } -void -BlobParent:: -RemoteBlobImpl::LookupAndCacheIsDirectory() -{ - return mBlobImpl->LookupAndCacheIsDirectory(); -} - -void -BlobParent:: -RemoteBlobImpl::SetIsDirectory(bool aIsDir) -{ - return mBlobImpl->SetIsDirectory(aIsDir); -} - -bool -BlobParent:: -RemoteBlobImpl::IsDirectory() const -{ - return mBlobImpl->IsDirectory(); -} - -BlobDirState -BlobParent:: -RemoteBlobImpl::GetDirState() const -{ - return mBlobImpl->GetDirState(); -} - bool BlobParent:: RemoteBlobImpl::MayBeClonedToOtherThreads() const @@ -3033,8 +2987,7 @@ BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl) MOZ_ASSERT(!rv.Failed()); remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length, - modDate, otherImpl->GetDirState(), - false /* SameProcessBlobImpl */); + modDate, false /* SameProcessBlobImpl */); } else { remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length, false /* SameProcessBlobImpl */); @@ -3086,7 +3039,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) params.contentType(), params.length(), params.modDate(), - BlobDirState(params.dirState()), false /* SameProcessBlobImpl */); break; } @@ -3122,7 +3074,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) contentType, size, lastModifiedDate, - blobImpl->GetDirState(), true /* SameProcessBlobImpl */); } else { remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size, @@ -3303,8 +3254,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager, MOZ_ASSERT(!rv.Failed()); blobParams = - FileBlobConstructorParams(name, contentType, length, modDate, - aBlobImpl->GetDirState(), blobData); + FileBlobConstructorParams(name, contentType, length, modDate, blobData); } else { blobParams = NormalBlobConstructorParams(contentType, length, blobData); } @@ -3477,8 +3427,7 @@ bool BlobChild::SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, - BlobDirState aDirState) + int64_t aLastModifiedDate) { AssertIsOnOwningThread(); MOZ_ASSERT(mBlobImpl); @@ -3491,7 +3440,6 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName, aContentType, aLength, aLastModifiedDate, - aDirState, void_t() /* optionalBlobData */); return SendResolveMystery(params); } @@ -3854,7 +3802,7 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager, blobParams = FileBlobConstructorParams(name, contentType, length, modDate, - aBlobImpl->GetDirState(), void_t()); + void_t()); } else { blobParams = NormalBlobConstructorParams(contentType, length, void_t()); } diff --git a/dom/ipc/BlobChild.h b/dom/ipc/BlobChild.h index 35853f307dfb..affa9934aa85 100644 --- a/dom/ipc/BlobChild.h +++ b/dom/ipc/BlobChild.h @@ -31,8 +31,6 @@ class ContentChild; class nsIContentChild; class PBlobStreamChild; -enum BlobDirState : uint32_t; - class BlobChild final : public PBlobChild { @@ -117,8 +115,7 @@ public: SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, - BlobDirState aDirState); + int64_t aLastModifiedDate); // Use this for non-file blobs. bool diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index 49b4f3f24207..2778642ef9fa 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -73,7 +73,6 @@ struct FileBlobConstructorParams nsString contentType; uint64_t length; int64_t modDate; - uint32_t dirState; // This must be of type BlobData in a child->parent message, and will always // be of type void_t in a parent->child message. diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp index 114001ae104d..94c84709f11f 100644 --- a/dom/ipc/FilePickerParent.cpp +++ b/dom/ipc/FilePickerParent.cpp @@ -53,15 +53,18 @@ FilePickerParent::~FilePickerParent() // 2. The stream transport thread stat()s the file in Run() and then dispatches // the same runnable on the main thread. // 3. The main thread sends the results over IPC. -FilePickerParent::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent, - nsTArray>& aBlobs) +FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent, + nsTArray>& aFiles, + bool aIsDirectory) : mFilePickerParent(aFPParent) + , mIsDirectory(aIsDirectory) { - mBlobs.SwapElements(aBlobs); + mFiles.SwapElements(aFiles); + MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); } bool -FilePickerParent::FileSizeAndDateRunnable::Dispatch() +FilePickerParent::IORunnable::Dispatch() { MOZ_ASSERT(NS_IsMainThread()); @@ -75,23 +78,49 @@ FilePickerParent::FileSizeAndDateRunnable::Dispatch() } NS_IMETHODIMP -FilePickerParent::FileSizeAndDateRunnable::Run() +FilePickerParent::IORunnable::Run() { // If we're on the main thread, then that means we're done. Just send the // results. if (NS_IsMainThread()) { if (mFilePickerParent) { - mFilePickerParent->SendFiles(mBlobs); + mFilePickerParent->SendFilesOrDirectories(mResults); } return NS_OK; } - // We're not on the main thread, so do the stat(). - for (unsigned i = 0; i < mBlobs.Length(); i++) { - ErrorResult rv; - mBlobs[i]->GetSize(rv); - mBlobs[i]->GetLastModified(rv); - mBlobs[i]->LookupAndCacheIsDirectory(); + // We're not on the main thread, so do the IO. + + for (uint32_t i = 0; i < mFiles.Length(); ++i) { + if (mIsDirectory) { + nsAutoString path; + nsresult rv = mFiles[i]->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eDirectoryPath; + data->mDirectoryPath = path; + continue; + } + + RefPtr blobImpl = new BlobImplFile(mFiles[i]); + + ErrorResult error; + blobImpl->GetSize(error); + if (NS_WARN_IF(error.Failed())) { + continue; + } + + blobImpl->GetLastModified(error); + if (NS_WARN_IF(error.Failed())) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eBlobImpl; + data->mBlobImpl = blobImpl; } // Dispatch ourselves back on the main thread. @@ -101,29 +130,46 @@ FilePickerParent::FileSizeAndDateRunnable::Run() // thread. MOZ_CRASH(); } + return NS_OK; } void -FilePickerParent::FileSizeAndDateRunnable::Destroy() +FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; } void -FilePickerParent::SendFiles(const nsTArray>& aBlobs) +FilePickerParent::SendFilesOrDirectories(const nsTArray& aData) { + if (mMode == nsIFilePicker::modeGetFolder) { + MOZ_ASSERT(aData.Length() <= 1); + if (aData.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); + + InputDirectory input; + input.directoryPath() = aData[0].mDirectoryPath; + Unused << Send__delete__(this, input, mResult); + return; + } + nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager(); InfallibleTArray blobs; - for (unsigned i = 0; i < aBlobs.Length(); i++) { - BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]); + for (unsigned i = 0; i < aData.Length(); i++) { + MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); + BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl); if (blobParent) { blobs.AppendElement(blobParent); } } - InputFiles inblobs; + InputBlobs inblobs; inblobs.blobsParent().SwapElements(blobs); Unused << Send__delete__(this, inblobs, mResult); } @@ -138,7 +184,7 @@ FilePickerParent::Done(int16_t aResult) return; } - nsTArray> blobs; + nsTArray> files; if (mMode == nsIFilePicker::modeOpenMultiple) { nsCOMPtr iter; NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); @@ -149,22 +195,26 @@ FilePickerParent::Done(int16_t aResult) iter->GetNext(getter_AddRefs(supports)); if (supports) { nsCOMPtr file = do_QueryInterface(supports); - - RefPtr blobimpl = new BlobImplFile(file); - blobs.AppendElement(blobimpl); + MOZ_ASSERT(file); + files.AppendElement(file); } } } else { nsCOMPtr file; mFilePicker->GetFile(getter_AddRefs(file)); if (file) { - RefPtr blobimpl = new BlobImplFile(file); - blobs.AppendElement(blobimpl); + files.AppendElement(file); } } + if (files.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + MOZ_ASSERT(!mRunnable); - mRunnable = new FileSizeAndDateRunnable(this, blobs); + mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder); + // Dispatch to background thread to do I/O: if (!mRunnable->Dispatch()) { Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); diff --git a/dom/ipc/FilePickerParent.h b/dom/ipc/FilePickerParent.h index b27fa3822cc0..a11b89c7a343 100644 --- a/dom/ipc/FilePickerParent.h +++ b/dom/ipc/FilePickerParent.h @@ -14,6 +14,8 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/PFilePickerParent.h" +class nsIFile; + namespace mozilla { namespace dom { @@ -29,7 +31,19 @@ class FilePickerParent : public PFilePickerParent virtual ~FilePickerParent(); void Done(int16_t aResult); - void SendFiles(const nsTArray>& aDomBlobs); + + struct BlobImplOrString + { + RefPtr mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + + void SendFilesOrDirectories(const nsTArray& aData); virtual bool RecvOpen(const int16_t& aSelectedType, const bool& aAddToRecentDocs, @@ -61,21 +75,26 @@ class FilePickerParent : public PFilePickerParent private: bool CreateFilePicker(); - class FileSizeAndDateRunnable : public nsRunnable + // This runnable is used to do some I/O operation on a separate thread. + class IORunnable : public nsRunnable { FilePickerParent* mFilePickerParent; - nsTArray> mBlobs; + nsTArray> mFiles; + nsTArray mResults; nsCOMPtr mEventTarget; + bool mIsDirectory; public: - FileSizeAndDateRunnable(FilePickerParent *aFPParent, - nsTArray>& aBlobs); + IORunnable(FilePickerParent *aFPParent, + nsTArray>& aFiles, + bool aIsDirectory); + bool Dispatch(); NS_IMETHOD Run(); void Destroy(); }; - RefPtr mRunnable; + RefPtr mRunnable; RefPtr mCallback; nsCOMPtr mFilePicker; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 21d3879ba24a..0f9a0e7458c2 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -289,6 +289,7 @@ struct FileSystemGetDirectoryListingParams { nsString filesystem; nsString realPath; + bool isRoot; // 'filters' could be an array rather than a semicolon separated string // (we'd then use InfallibleTArray internally), but that is // wasteful. E10s requires us to pass the filters over as a string anyway, @@ -305,6 +306,7 @@ struct FileSystemGetFileOrDirectoryParams { nsString filesystem; nsString realPath; + bool isRoot; }; union FileSystemPathOrFileValue diff --git a/dom/ipc/PFilePicker.ipdl b/dom/ipc/PFilePicker.ipdl index aff58b011da8..6184c5b9d55f 100644 --- a/dom/ipc/PFilePicker.ipdl +++ b/dom/ipc/PFilePicker.ipdl @@ -12,14 +12,20 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; namespace mozilla { namespace dom { -struct InputFiles +struct InputBlobs { PBlob[] blobs; }; -union MaybeInputFiles +struct InputDirectory { - InputFiles; + nsString directoryPath; +}; + +union MaybeInputData +{ + InputBlobs; + InputDirectory; void_t; }; @@ -33,7 +39,7 @@ parent: nsString displayDirectory); child: - async __delete__(MaybeInputFiles files, int16_t result); + async __delete__(MaybeInputData data, int16_t result); }; } // namespace dom diff --git a/dom/webidl/Directory.webidl b/dom/webidl/Directory.webidl index 7ea8e02693cc..6504dd02e04f 100644 --- a/dom/webidl/Directory.webidl +++ b/dom/webidl/Directory.webidl @@ -20,6 +20,7 @@ interface Directory { /* * The leaf name of the directory. */ + [Throws] readonly attribute DOMString name; /* @@ -105,11 +106,13 @@ partial interface Directory { * to obtain this Directory. Full filesystem paths are not exposed to * unprivilaged content. */ + [Throws] readonly attribute DOMString path; /* * Getter for the immediate children of this directory. */ + [Throws] Promise> getFilesAndDirectories(); }; diff --git a/widget/nsBaseFilePicker.cpp b/widget/nsBaseFilePicker.cpp index 08935999d22d..71c00cbb1db3 100644 --- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -17,6 +17,7 @@ #include "nsCOMArray.h" #include "nsIFile.h" #include "nsEnumeratorUtils.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/Services.h" #include "WidgetUtils.h" @@ -30,6 +31,36 @@ using namespace mozilla::dom; #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties" #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties" +namespace { + +nsresult +LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow, + bool aIsDirectory, + nsIFile* aFile, + nsISupports** aResult) +{ + if (aIsDirectory) { +#ifdef DEBUG + bool isDir; + aFile->IsDirectory(&isDir); + MOZ_ASSERT(isDir); +#endif + + RefPtr directory = + Directory::Create(aWindow, aFile, Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + directory.forget(aResult); + return NS_OK; + } + + nsCOMPtr blob = File::CreateFromFile(aWindow, aFile); + blob.forget(aResult); + return NS_OK; +} + +} // anonymous namespace + /** * A runnable to dispatch from the main thread to the main thread to display * the file picker while letting the showAsync method return right away. @@ -74,9 +105,9 @@ class nsBaseFilePickerEnumerator : public nsISimpleEnumerator public: NS_DECL_ISUPPORTS - explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent, - nsISimpleEnumerator* iterator, - int16_t aMode) + nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent, + nsISimpleEnumerator* iterator, + int16_t aMode) : mIterator(iterator) , mParent(aParent->GetCurrentInnerWindow()) , mMode(aMode) @@ -98,32 +129,10 @@ public: return NS_ERROR_FAILURE; } - RefPtr domFile = File::CreateFromFile(mParent, localFile); - - // Right now we're on the main thread of the chrome process. We need - // to call SetIsDirectory on the BlobImpl, but it's preferable not to - // call nsIFile::IsDirectory to determine what argument to pass since - // IsDirectory does synchronous I/O. It's true that since we've just - // been called synchronously directly after nsIFilePicker::Show blocked - // the main thread while the picker was being shown and the OS did file - // system access, doing more I/O to stat the selected files probably - // wouldn't be the end of the world. However, we can simply check - // mMode and avoid calling IsDirectory. - // - // In future we may take advantage of OS X's ability to allow both - // files and directories to be picked at the same time, so we do assert - // in debug builds that the mMode trick produces the correct results. - // If we do add that support maybe it's better to use IsDirectory - // directly, but in an nsRunnable punted off to a background thread. -#ifdef DEBUG - bool isDir; - localFile->IsDirectory(&isDir); - MOZ_ASSERT(isDir == (mMode == nsIFilePicker::modeGetFolder)); -#endif - domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder); - - nsCOMPtr(domFile).forget(aResult); - return NS_OK; + return LocalFileToDirectoryOrBlob(mParent, + mMode == nsIFilePicker::modeGetFolder, + localFile, + aResult); } NS_IMETHOD @@ -349,10 +358,10 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr; - RefPtr domFile = File::CreateFromFile(innerParent, localFile); - domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder); - nsCOMPtr(domFile).forget(aValue); - return NS_OK; + return LocalFileToDirectoryOrBlob(innerParent, + mMode == nsIFilePicker::modeGetFolder, + localFile, + aValue); } NS_IMETHODIMP diff --git a/widget/nsFilePickerProxy.cpp b/widget/nsFilePickerProxy.cpp index 830159de1e42..1dcbcf7ee5c8 100644 --- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -7,6 +7,7 @@ #include "nsFilePickerProxy.h" #include "nsComponentManagerUtils.h" #include "nsIFile.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/ipc/BlobChild.h" @@ -142,11 +143,11 @@ nsFilePickerProxy::Open(nsIFilePickerShownCallback* aCallback) } bool -nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles, +nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) { - if (aFiles.type() == MaybeInputFiles::TInputFiles) { - const InfallibleTArray& blobs = aFiles.get_InputFiles().blobsChild(); + if (aData.type() == MaybeInputData::TInputBlobs) { + const InfallibleTArray& blobs = aData.get_InputBlobs().blobsChild(); for (uint32_t i = 0; i < blobs.Length(); ++i) { BlobChild* actor = static_cast(blobs[i]); RefPtr blobImpl = actor->GetBlobImpl(); @@ -161,8 +162,24 @@ nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles, RefPtr file = File::Create(inner, blobImpl); MOZ_ASSERT(file); - mFilesOrDirectories.AppendElement(file); + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; } + } else if (aData.type() == MaybeInputData::TInputDirectory) { + nsCOMPtr file; + NS_ConvertUTF16toUTF8 path(aData.get_InputDirectory().directoryPath()); + nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + RefPtr directory = + Directory::Create(mParent->GetCurrentInnerWindow(), file, + Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsDirectory() = directory; } if (mCallback) { @@ -182,8 +199,16 @@ nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue) } MOZ_ASSERT(mFilesOrDirectories.Length() == 1); - nsCOMPtr blob = mFilesOrDirectories[0].get(); - blob.forget(aValue); + + if (mFilesOrDirectories[0].IsFile()) { + nsCOMPtr blob = mFilesOrDirectories[0].GetAsFile().get(); + blob.forget(aValue); + return NS_OK; + } + + MOZ_ASSERT(mFilesOrDirectories[0].IsDirectory()); + RefPtr directory = mFilesOrDirectories[0].GetAsDirectory(); + directory.forget(aValue); return NS_OK; } @@ -194,8 +219,9 @@ class SimpleEnumerator final : public nsISimpleEnumerator public: NS_DECL_ISUPPORTS - explicit SimpleEnumerator(const nsTArray>& aFiles) - : mFiles(aFiles) + explicit + SimpleEnumerator(const nsTArray& aFilesOrDirectories) + : mFilesOrDirectories(aFilesOrDirectories) , mIndex(0) {} @@ -203,17 +229,26 @@ public: HasMoreElements(bool* aRetvalue) override { MOZ_ASSERT(aRetvalue); - *aRetvalue = mIndex < mFiles.Length(); + *aRetvalue = mIndex < mFilesOrDirectories.Length(); return NS_OK; } NS_IMETHOD - GetNext(nsISupports** aSupports) override + GetNext(nsISupports** aValue) override { - NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE); + NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE); - nsCOMPtr blob = mFiles[mIndex++].get(); - blob.forget(aSupports); + uint32_t index = mIndex++; + + if (mFilesOrDirectories[index].IsFile()) { + nsCOMPtr blob = mFilesOrDirectories[index].GetAsFile().get(); + blob.forget(aValue); + return NS_OK; + } + + MOZ_ASSERT(mFilesOrDirectories[index].IsDirectory()); + RefPtr directory = mFilesOrDirectories[index].GetAsDirectory(); + directory.forget(aValue); return NS_OK; } @@ -221,7 +256,7 @@ private: ~SimpleEnumerator() {} - nsTArray> mFiles; + nsTArray mFilesOrDirectories; uint32_t mIndex; }; @@ -232,7 +267,8 @@ NS_IMPL_ISUPPORTS(SimpleEnumerator, nsISimpleEnumerator) NS_IMETHODIMP nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aDomfiles) { - RefPtr enumerator = new SimpleEnumerator(mFilesOrDirectories); + RefPtr enumerator = + new SimpleEnumerator(mFilesOrDirectories); enumerator.forget(aDomfiles); return NS_OK; } diff --git a/widget/nsFilePickerProxy.h b/widget/nsFilePickerProxy.h index 393944d6c882..093678a698ed 100644 --- a/widget/nsFilePickerProxy.h +++ b/widget/nsFilePickerProxy.h @@ -13,17 +13,12 @@ #include "nsCOMArray.h" #include "mozilla/dom/PFilePickerChild.h" +#include "mozilla/dom/UnionTypes.h" class nsIWidget; class nsIFile; class nsPIDOMWindowInner; -namespace mozilla { -namespace dom { -class File; -} // namespace dom -} // namespace mozilla - /** This class creates a proxy file picker to be used in content processes. The file picker just collects the initialization data and when Show() is @@ -59,13 +54,13 @@ public: // PFilePickerChild virtual bool - Recv__delete__(const MaybeInputFiles& aFiles, const int16_t& aResult) override; + Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override; private: ~nsFilePickerProxy(); void InitNative(nsIWidget*, const nsAString&) override; - nsTArray> mFilesOrDirectories; + nsTArray mFilesOrDirectories; nsCOMPtr mCallback; int16_t mSelectedType;