diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/filesystem/compat | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/filesystem/compat')
23 files changed, 2672 insertions, 0 deletions
diff --git a/dom/filesystem/compat/CallbackRunnables.cpp b/dom/filesystem/compat/CallbackRunnables.cpp new file mode 100644 index 000000000..efd14b03e --- /dev/null +++ b/dom/filesystem/compat/CallbackRunnables.cpp @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CallbackRunnables.h" +#include "mozilla/dom/Directory.h" +#include "mozilla/dom/DirectoryBinding.h" +#include "mozilla/dom/DOMException.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileBinding.h" +#include "mozilla/dom/FileSystemDirectoryReaderBinding.h" +#include "mozilla/dom/FileSystemFileEntry.h" +#include "mozilla/dom/FileSystemUtils.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/Unused.h" +#include "nsIGlobalObject.h" +#include "nsIFile.h" +#include "nsPIDOMWindow.h" + +#include "../GetFileOrDirectoryTask.h" + +namespace mozilla { +namespace dom { + +EntryCallbackRunnable::EntryCallbackRunnable(FileSystemEntryCallback* aCallback, + FileSystemEntry* aEntry) + : mCallback(aCallback) + , mEntry(aEntry) +{ + MOZ_ASSERT(aCallback); + MOZ_ASSERT(aEntry); +} + +NS_IMETHODIMP +EntryCallbackRunnable::Run() +{ + mCallback->HandleEvent(*mEntry); + return NS_OK; +} + +ErrorCallbackRunnable::ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject, + ErrorCallback* aCallback, + nsresult aError) + : mGlobal(aGlobalObject) + , mCallback(aCallback) + , mError(aError) +{ + MOZ_ASSERT(aGlobalObject); + MOZ_ASSERT(aCallback); + MOZ_ASSERT(NS_FAILED(aError)); +} + +NS_IMETHODIMP +ErrorCallbackRunnable::Run() +{ + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal); + if (NS_WARN_IF(!window)) { + return NS_ERROR_FAILURE; + } + + RefPtr<DOMException> exception = DOMException::Create(mError); + mCallback->HandleEvent(*exception); + return NS_OK; +} + +EmptyEntriesCallbackRunnable::EmptyEntriesCallbackRunnable(FileSystemEntriesCallback* aCallback) + : mCallback(aCallback) +{ + MOZ_ASSERT(aCallback); +} + +NS_IMETHODIMP +EmptyEntriesCallbackRunnable::Run() +{ + Sequence<OwningNonNull<FileSystemEntry>> sequence; + mCallback->HandleEvent(sequence); + return NS_OK; +} + +GetEntryHelper::GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, + Directory* aDirectory, + nsTArray<nsString>& aParts, + FileSystem* aFileSystem, + FileSystemEntryCallback* aSuccessCallback, + ErrorCallback* aErrorCallback, + FileSystemDirectoryEntry::GetInternalType aType) + : mParentEntry(aParentEntry) + , mDirectory(aDirectory) + , mParts(aParts) + , mFileSystem(aFileSystem) + , mSuccessCallback(aSuccessCallback) + , mErrorCallback(aErrorCallback) + , mType(aType) +{ + MOZ_ASSERT(aParentEntry); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(!aParts.IsEmpty()); + MOZ_ASSERT(aFileSystem); + MOZ_ASSERT(aSuccessCallback || aErrorCallback); +} + +GetEntryHelper::~GetEntryHelper() +{} + +namespace { + +nsresult +DOMPathToRealPath(Directory* aDirectory, const nsAString& aPath, + nsIFile** aFile) +{ + nsString relativePath; + relativePath = aPath; + + // Trim white spaces. + static const char kWhitespace[] = "\b\t\r\n "; + relativePath.Trim(kWhitespace); + + nsTArray<nsString> parts; + if (!FileSystemUtils::IsValidRelativeDOMPath(relativePath, parts)) { + return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; + } + + nsCOMPtr<nsIFile> file; + nsresult rv = aDirectory->GetInternalNsIFile()->Clone(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (uint32_t i = 0; i < parts.Length(); ++i) { + rv = file->AppendRelativePath(parts[i]); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + file.forget(aFile); + return NS_OK; +} + +} // anonymous + +void +GetEntryHelper::Run() +{ + MOZ_ASSERT(!mParts.IsEmpty()); + + nsCOMPtr<nsIFile> realPath; + nsresult error = DOMPathToRealPath(mDirectory, mParts[0], + getter_AddRefs(realPath)); + + ErrorResult rv; + RefPtr<FileSystemBase> fs = mDirectory->GetFileSystem(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + Error(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + RefPtr<GetFileOrDirectoryTaskChild> task = + GetFileOrDirectoryTaskChild::Create(fs, realPath, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + Error(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + task->SetError(error); + task->Start(); + + RefPtr<Promise> promise = task->GetPromise(); + + mParts.RemoveElementAt(0); + promise->AppendNativeHandler(this); +} + +void +GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) +{ + if(NS_WARN_IF(!aValue.isObject())) { + return; + } + + JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); + + // This is not the last part of the path. + if (!mParts.IsEmpty()) { + ContinueRunning(obj); + return; + } + + CompleteOperation(obj); +} + +void +GetEntryHelper::CompleteOperation(JSObject* aObj) +{ + MOZ_ASSERT(mParts.IsEmpty()); + + if (mType == FileSystemDirectoryEntry::eGetFile) { + RefPtr<File> file; + if (NS_FAILED(UNWRAP_OBJECT(File, aObj, file))) { + Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); + return; + } + + RefPtr<FileSystemFileEntry> entry = + new FileSystemFileEntry(mParentEntry->GetParentObject(), file, + mParentEntry, mFileSystem); + mSuccessCallback->HandleEvent(*entry); + return; + } + + MOZ_ASSERT(mType == FileSystemDirectoryEntry::eGetDirectory); + + RefPtr<Directory> directory; + if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { + Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); + return; + } + + RefPtr<FileSystemDirectoryEntry> entry = + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); + mSuccessCallback->HandleEvent(*entry); +} + +void +GetEntryHelper::ContinueRunning(JSObject* aObj) +{ + MOZ_ASSERT(!mParts.IsEmpty()); + + RefPtr<Directory> directory; + if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { + Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); + return; + } + + RefPtr<FileSystemDirectoryEntry> entry = + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); + + // Update the internal values. + mParentEntry = entry; + mDirectory = directory; + + Run(); +} + +void +GetEntryHelper::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) +{ + Error(NS_ERROR_DOM_NOT_FOUND_ERR); +} + +void +GetEntryHelper::Error(nsresult aError) +{ + MOZ_ASSERT(NS_FAILED(aError)); + + if (mErrorCallback) { + RefPtr<ErrorCallbackRunnable> runnable = + new ErrorCallbackRunnable(mParentEntry->GetParentObject(), + mErrorCallback, aError); + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } +} + +NS_IMPL_ISUPPORTS0(GetEntryHelper); + +/* static */ void +FileSystemEntryCallbackHelper::Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback, + FileSystemEntry* aEntry) +{ + MOZ_ASSERT(aEntry); + + if (aEntryCallback.WasPassed()) { + RefPtr<EntryCallbackRunnable> runnable = + new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry); + + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } +} + +/* static */ void +ErrorCallbackHelper::Call(nsIGlobalObject* aGlobal, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + nsresult aError) +{ + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(NS_FAILED(aError)); + + if (aErrorCallback.WasPassed()) { + RefPtr<ErrorCallbackRunnable> runnable = + new ErrorCallbackRunnable(aGlobal, &aErrorCallback.Value(), aError); + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } +} + +} // dom namespace +} // mozilla namespace + diff --git a/dom/filesystem/compat/CallbackRunnables.h b/dom/filesystem/compat/CallbackRunnables.h new file mode 100644 index 000000000..3ff77f1a9 --- /dev/null +++ b/dom/filesystem/compat/CallbackRunnables.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ErrorCallbackRunnable_h +#define mozilla_dom_ErrorCallbackRunnable_h + +#include "FileSystemDirectoryEntry.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/PromiseNativeHandler.h" + +class nsIGlobalObject; + +namespace mozilla { +namespace dom { + +class FileSystemEntriesCallback; + +class EntryCallbackRunnable final : public Runnable +{ +public: + EntryCallbackRunnable(FileSystemEntryCallback* aCallback, + FileSystemEntry* aEntry); + + NS_IMETHOD + Run() override; + +private: + RefPtr<FileSystemEntryCallback> mCallback; + RefPtr<FileSystemEntry> mEntry; +}; + +class ErrorCallbackRunnable final : public Runnable +{ +public: + ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject, + ErrorCallback* aCallback, + nsresult aError); + + NS_IMETHOD + Run() override; + +private: + nsCOMPtr<nsIGlobalObject> mGlobal; + RefPtr<ErrorCallback> mCallback; + nsresult mError; +}; + +class EmptyEntriesCallbackRunnable final : public Runnable +{ +public: + explicit EmptyEntriesCallbackRunnable(FileSystemEntriesCallback* aCallback); + + NS_IMETHOD + Run() override; + +private: + RefPtr<FileSystemEntriesCallback> mCallback; +}; + +class GetEntryHelper final : public PromiseNativeHandler +{ +public: + NS_DECL_ISUPPORTS + + GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, + Directory* aDirectory, + nsTArray<nsString>& aParts, + FileSystem* aFileSystem, + FileSystemEntryCallback* aSuccessCallback, + ErrorCallback* aErrorCallback, + FileSystemDirectoryEntry::GetInternalType aType); + + void + Run(); + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; + +private: + ~GetEntryHelper(); + + void + Error(nsresult aError); + + void + ContinueRunning(JSObject* aObj); + + void + CompleteOperation(JSObject* aObj); + + RefPtr<FileSystemDirectoryEntry> mParentEntry; + RefPtr<Directory> mDirectory; + nsTArray<nsString> mParts; + RefPtr<FileSystem> mFileSystem; + + RefPtr<FileSystemEntryCallback> mSuccessCallback; + RefPtr<ErrorCallback> mErrorCallback; + + FileSystemDirectoryEntry::GetInternalType mType; +}; + +class FileSystemEntryCallbackHelper +{ +public: + static void + Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback, + FileSystemEntry* aEntry); +}; + +class ErrorCallbackHelper +{ +public: + static void + Call(nsIGlobalObject* aGlobal, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + nsresult aError); +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_CallbackRunnables_h diff --git a/dom/filesystem/compat/FileSystem.cpp b/dom/filesystem/compat/FileSystem.cpp new file mode 100644 index 000000000..b45980fc6 --- /dev/null +++ b/dom/filesystem/compat/FileSystem.cpp @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystem.h" +#include "FileSystemRootDirectoryEntry.h" +#include "mozilla/dom/FileSystemBinding.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystem, mParent, mRoot) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystem) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystem) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystem) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* static */ already_AddRefed<FileSystem> +FileSystem::Create(nsIGlobalObject* aGlobalObject) + +{ + MOZ_ASSERT(aGlobalObject); + + + nsID id; + nsresult rv = nsContentUtils::GenerateUUIDInPlace(id); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + char chars[NSID_LENGTH]; + id.ToProvidedString(chars); + + // Any fileSystem has an unique ID. We use UUID, but our generator produces + // UUID in this format '{' + UUID + '}'. We remove them with these +1 and -2. + nsAutoCString name(Substring(chars + 1, chars + NSID_LENGTH - 2)); + + RefPtr<FileSystem> fs = + new FileSystem(aGlobalObject, NS_ConvertUTF8toUTF16(name)); + + return fs.forget(); +} + +FileSystem::FileSystem(nsIGlobalObject* aGlobal, const nsAString& aName) + : mParent(aGlobal) + , mName(aName) +{ + MOZ_ASSERT(aGlobal); +} + +FileSystem::~FileSystem() +{} + +JSObject* +FileSystem::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return FileSystemBinding::Wrap(aCx, this, aGivenProto); +} + +void +FileSystem::CreateRoot(const Sequence<RefPtr<FileSystemEntry>>& aEntries) +{ + MOZ_ASSERT(!mRoot); + mRoot = new FileSystemRootDirectoryEntry(mParent, aEntries, this); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystem.h b/dom/filesystem/compat/FileSystem.h new file mode 100644 index 000000000..43ce2ea38 --- /dev/null +++ b/dom/filesystem/compat/FileSystem.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystem_h +#define mozilla_dom_FileSystem_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +class nsIGlobalObject; + +namespace mozilla { +namespace dom { + +class FileSystemDirectoryEntry; +class FileSystemEntry; +class OwningFileOrDirectory; + +class FileSystem final + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystem) + + static already_AddRefed<FileSystem> + Create(nsIGlobalObject* aGlobalObject); + + nsIGlobalObject* + GetParentObject() const + { + return mParent; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + void + GetName(nsAString& aName) const + { + aName = mName; + } + + FileSystemDirectoryEntry* + Root() const + { + return mRoot; + } + + void + CreateRoot(const Sequence<RefPtr<FileSystemEntry>>& aEntries); + +private: + explicit FileSystem(nsIGlobalObject* aGlobalObject, + const nsAString& aName); + ~FileSystem(); + + nsCOMPtr<nsIGlobalObject> mParent; + RefPtr<FileSystemDirectoryEntry> mRoot; + nsString mName; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystem_h diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp new file mode 100644 index 000000000..3157ef654 --- /dev/null +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemDirectoryEntry.h" +#include "CallbackRunnables.h" +#include "FileSystemDirectoryReader.h" +#include "mozilla/dom/Directory.h" +#include "mozilla/dom/FileSystemDirectoryEntryBinding.h" +#include "mozilla/dom/FileSystemUtils.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemDirectoryEntry, FileSystemEntry, + mDirectory) + +NS_IMPL_ADDREF_INHERITED(FileSystemDirectoryEntry, FileSystemEntry) +NS_IMPL_RELEASE_INHERITED(FileSystemDirectoryEntry, FileSystemEntry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileSystemDirectoryEntry) +NS_INTERFACE_MAP_END_INHERITING(FileSystemEntry) + +FileSystemDirectoryEntry::FileSystemDirectoryEntry(nsIGlobalObject* aGlobal, + Directory* aDirectory, + FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem) + : FileSystemEntry(aGlobal, aParentEntry, aFileSystem) + , mDirectory(aDirectory) +{ + MOZ_ASSERT(aGlobal); +} + +FileSystemDirectoryEntry::~FileSystemDirectoryEntry() +{} + +JSObject* +FileSystemDirectoryEntry::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return FileSystemDirectoryEntryBinding::Wrap(aCx, this, aGivenProto); +} + +void +FileSystemDirectoryEntry::GetName(nsAString& aName, ErrorResult& aRv) const +{ + MOZ_ASSERT(mDirectory); + mDirectory->GetName(aName, aRv); +} + +void +FileSystemDirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const +{ + MOZ_ASSERT(mDirectory); + mDirectory->GetPath(aPath, aRv); +} + +already_AddRefed<FileSystemDirectoryReader> +FileSystemDirectoryEntry::CreateReader() +{ + MOZ_ASSERT(mDirectory); + + RefPtr<FileSystemDirectoryReader> reader = + new FileSystemDirectoryReader(this, Filesystem(), mDirectory); + return reader.forget(); +} + +void +FileSystemDirectoryEntry::GetInternal(const nsAString& aPath, + const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + GetInternalType aType) +{ + MOZ_ASSERT(mDirectory); + + if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) { + return; + } + + if (aFlag.mCreate) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsTArray<nsString> parts; + if (!FileSystemUtils::IsValidRelativeDOMPath(aPath, parts)) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + RefPtr<GetEntryHelper> helper = + new GetEntryHelper(this, mDirectory, parts, Filesystem(), + aSuccessCallback.WasPassed() + ? &aSuccessCallback.Value() : nullptr, + aErrorCallback.WasPassed() + ? &aErrorCallback.Value() : nullptr, + aType); + helper->Run(); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.h b/dom/filesystem/compat/FileSystemDirectoryEntry.h new file mode 100644 index 000000000..96dc21831 --- /dev/null +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemDirectoryEntry_h +#define mozilla_dom_FileSystemDirectoryEntry_h + +#include "mozilla/dom/FileSystemEntry.h" + +namespace mozilla { +namespace dom { + +class Directory; +class FileSystemDirectoryReader; + +class FileSystemDirectoryEntry : public FileSystemEntry +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemDirectoryEntry, + FileSystemEntry) + + FileSystemDirectoryEntry(nsIGlobalObject* aGlobalObject, + Directory* aDirectory, + FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem); + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + virtual bool + IsDirectory() const override + { + return true; + } + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; + + virtual already_AddRefed<FileSystemDirectoryReader> + CreateReader(); + + void + GetFile(const Optional<nsAString>& aPath, const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) + { + GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), + aFlag, aSuccessCallback, aErrorCallback, eGetFile); + } + + void + GetDirectory(const Optional<nsAString>& aPath, const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) + { + GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), + aFlag, aSuccessCallback, aErrorCallback, eGetDirectory); + } + + enum GetInternalType { eGetFile, eGetDirectory }; + + virtual void + GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + GetInternalType aType); + +protected: + virtual ~FileSystemDirectoryEntry(); + +private: + RefPtr<Directory> mDirectory; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemDirectoryEntry_h diff --git a/dom/filesystem/compat/FileSystemDirectoryReader.cpp b/dom/filesystem/compat/FileSystemDirectoryReader.cpp new file mode 100644 index 000000000..137437378 --- /dev/null +++ b/dom/filesystem/compat/FileSystemDirectoryReader.cpp @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemDirectoryReader.h" +#include "CallbackRunnables.h" +#include "FileSystemFileEntry.h" +#include "mozilla/dom/FileBinding.h" +#include "mozilla/dom/Directory.h" +#include "mozilla/dom/DirectoryBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" + +namespace mozilla { +namespace dom { + +namespace { + +class PromiseHandler final : public PromiseNativeHandler +{ +public: + NS_DECL_ISUPPORTS + + PromiseHandler(FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem, + FileSystemEntriesCallback* aSuccessCallback, + ErrorCallback* aErrorCallback) + : mParentEntry(aParentEntry) + , mFileSystem(aFileSystem) + , mSuccessCallback(aSuccessCallback) + , mErrorCallback(aErrorCallback) + { + MOZ_ASSERT(aParentEntry); + MOZ_ASSERT(aFileSystem); + MOZ_ASSERT(aSuccessCallback); + } + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override + { + if(NS_WARN_IF(!aValue.isObject())) { + return; + } + + JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); + + uint32_t length; + if (NS_WARN_IF(!JS_GetArrayLength(aCx, obj, &length))) { + return; + } + + Sequence<OwningNonNull<FileSystemEntry>> sequence; + if (NS_WARN_IF(!sequence.SetLength(length, fallible))) { + return; + } + + for (uint32_t i = 0; i < length; ++i) { + JS::Rooted<JS::Value> value(aCx); + if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &value))) { + return; + } + + if(NS_WARN_IF(!value.isObject())) { + return; + } + + JS::Rooted<JSObject*> valueObj(aCx, &value.toObject()); + + RefPtr<File> file; + if (NS_SUCCEEDED(UNWRAP_OBJECT(File, valueObj, file))) { + RefPtr<FileSystemFileEntry> entry = + new FileSystemFileEntry(mParentEntry->GetParentObject(), file, + mParentEntry, mFileSystem); + sequence[i] = entry; + continue; + } + + RefPtr<Directory> directory; + if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Directory, valueObj, + directory)))) { + return; + } + + RefPtr<FileSystemDirectoryEntry> entry = + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); + sequence[i] = entry; + } + + mSuccessCallback->HandleEvent(sequence); + } + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override + { + if (mErrorCallback) { + RefPtr<ErrorCallbackRunnable> runnable = + new ErrorCallbackRunnable(mParentEntry->GetParentObject(), + mErrorCallback, + NS_ERROR_DOM_INVALID_STATE_ERR); + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } + } + +private: + ~PromiseHandler() {} + + RefPtr<FileSystemDirectoryEntry> mParentEntry; + RefPtr<FileSystem> mFileSystem; + RefPtr<FileSystemEntriesCallback> mSuccessCallback; + RefPtr<ErrorCallback> mErrorCallback; +}; + +NS_IMPL_ISUPPORTS0(PromiseHandler); + +} // anonymous namespace + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemDirectoryReader, mParentEntry, + mDirectory, mFileSystem) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemDirectoryReader) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemDirectoryReader) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemDirectoryReader) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +FileSystemDirectoryReader::FileSystemDirectoryReader(FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem, + Directory* aDirectory) + : mParentEntry(aParentEntry) + , mFileSystem(aFileSystem) + , mDirectory(aDirectory) + , mAlreadyRead(false) +{ + MOZ_ASSERT(aParentEntry); + MOZ_ASSERT(aFileSystem); +} + +FileSystemDirectoryReader::~FileSystemDirectoryReader() +{} + +JSObject* +FileSystemDirectoryReader::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return FileSystemDirectoryReaderBinding::Wrap(aCx, this, aGivenProto); +} + +void +FileSystemDirectoryReader::ReadEntries(FileSystemEntriesCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + ErrorResult& aRv) +{ + MOZ_ASSERT(mDirectory); + + if (mAlreadyRead) { + RefPtr<EmptyEntriesCallbackRunnable> runnable = + new EmptyEntriesCallbackRunnable(&aSuccessCallback); + aRv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed"); + return; + } + + // This object can be used only once. + mAlreadyRead = true; + + ErrorResult rv; + RefPtr<Promise> promise = mDirectory->GetFilesAndDirectories(rv); + if (NS_WARN_IF(rv.Failed())) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + rv.StealNSResult()); + return; + } + + RefPtr<PromiseHandler> handler = + new PromiseHandler(mParentEntry, mFileSystem, &aSuccessCallback, + aErrorCallback.WasPassed() + ? &aErrorCallback.Value() : nullptr); + promise->AppendNativeHandler(handler); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemDirectoryReader.h b/dom/filesystem/compat/FileSystemDirectoryReader.h new file mode 100644 index 000000000..391a79329 --- /dev/null +++ b/dom/filesystem/compat/FileSystemDirectoryReader.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemDirectoryReader_h +#define mozilla_dom_FileSystemDirectoryReader_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/FileSystemDirectoryEntry.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace dom { + +class Directory; +class FileSystem; +class FileSystemEntriesCallback; + +class FileSystemDirectoryReader + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemDirectoryReader) + + explicit FileSystemDirectoryReader(FileSystemDirectoryEntry* aDirectoryEntry, + FileSystem* aFileSystem, + Directory* aDirectory); + + nsIGlobalObject* + GetParentObject() const + { + return mParentEntry->GetParentObject(); + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + virtual void + ReadEntries(FileSystemEntriesCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + ErrorResult& aRv); + +protected: + virtual ~FileSystemDirectoryReader(); + +private: + RefPtr<FileSystemDirectoryEntry> mParentEntry; + RefPtr<FileSystem> mFileSystem; + RefPtr<Directory> mDirectory; + + bool mAlreadyRead; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemDirectoryReader_h diff --git a/dom/filesystem/compat/FileSystemEntry.cpp b/dom/filesystem/compat/FileSystemEntry.cpp new file mode 100644 index 000000000..638c2c6db --- /dev/null +++ b/dom/filesystem/compat/FileSystemEntry.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemEntry.h" +#include "FileSystemDirectoryEntry.h" +#include "FileSystemFileEntry.h" +#include "mozilla/dom/FileSystemEntryBinding.h" +#include "mozilla/dom/UnionTypes.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemEntry, mParent, mParentEntry, + mFileSystem) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemEntry) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemEntry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemEntry) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* static */ already_AddRefed<FileSystemEntry> +FileSystemEntry::Create(nsIGlobalObject* aGlobalObject, + const OwningFileOrDirectory& aFileOrDirectory, + FileSystem* aFileSystem) +{ + MOZ_ASSERT(aGlobalObject); + MOZ_ASSERT(aFileSystem); + + RefPtr<FileSystemEntry> entry; + if (aFileOrDirectory.IsFile()) { + entry = new FileSystemFileEntry(aGlobalObject, + aFileOrDirectory.GetAsFile(), + nullptr, + aFileSystem); + } else { + MOZ_ASSERT(aFileOrDirectory.IsDirectory()); + entry = new FileSystemDirectoryEntry(aGlobalObject, + aFileOrDirectory.GetAsDirectory(), + nullptr, + aFileSystem); + } + + return entry.forget(); +} + +FileSystemEntry::FileSystemEntry(nsIGlobalObject* aGlobal, + FileSystemEntry* aParentEntry, + FileSystem* aFileSystem) + : mParent(aGlobal) + , mParentEntry(aParentEntry) + , mFileSystem(aFileSystem) +{ + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(aFileSystem); +} + +FileSystemEntry::~FileSystemEntry() +{} + +JSObject* +FileSystemEntry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return FileSystemEntryBinding::Wrap(aCx, this, aGivenProto); +} + +void +FileSystemEntry::GetParent(const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) +{ + if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) { + return; + } + + if (mParentEntry) { + FileSystemEntryCallbackHelper::Call(aSuccessCallback, mParentEntry); + return; + } + + FileSystemEntryCallbackHelper::Call(aSuccessCallback, this); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemEntry.h b/dom/filesystem/compat/FileSystemEntry.h new file mode 100644 index 000000000..769fb8f3f --- /dev/null +++ b/dom/filesystem/compat/FileSystemEntry.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemEntry_h +#define mozilla_dom_FileSystemEntry_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/FileSystemBinding.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIGlobalObject.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace dom { + +class FileSystem; +class OwningFileOrDirectory; + +class FileSystemEntry + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemEntry) + + static already_AddRefed<FileSystemEntry> + Create(nsIGlobalObject* aGlobalObject, + const OwningFileOrDirectory& aFileOrDirectory, + FileSystem* aFileSystem); + + nsIGlobalObject* + GetParentObject() const + { + return mParent; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + virtual bool + IsFile() const + { + return false; + } + + virtual bool + IsDirectory() const + { + return false; + } + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const = 0; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const = 0; + + void + GetParent(const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback); + + FileSystem* + Filesystem() const + { + return mFileSystem; + } + +protected: + FileSystemEntry(nsIGlobalObject* aGlobalObject, + FileSystemEntry* aParentEntry, + FileSystem* aFileSystem); + virtual ~FileSystemEntry(); + +private: + nsCOMPtr<nsIGlobalObject> mParent; + RefPtr<FileSystemEntry> mParentEntry; + RefPtr<FileSystem> mFileSystem; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemEntry_h diff --git a/dom/filesystem/compat/FileSystemFileEntry.cpp b/dom/filesystem/compat/FileSystemFileEntry.cpp new file mode 100644 index 000000000..6302df06d --- /dev/null +++ b/dom/filesystem/compat/FileSystemFileEntry.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemFileEntry.h" +#include "CallbackRunnables.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/MultipartBlobImpl.h" +#include "mozilla/dom/FileSystemFileEntryBinding.h" + +namespace mozilla { +namespace dom { + +namespace { + +class FileCallbackRunnable final : public Runnable +{ +public: + FileCallbackRunnable(FileCallback* aCallback, ErrorCallback* aErrorCallback, + File* aFile) + : mCallback(aCallback) + , mErrorCallback(aErrorCallback) + , mFile(aFile) + { + MOZ_ASSERT(aCallback); + MOZ_ASSERT(aFile); + } + + NS_IMETHOD + Run() override + { + // Here we clone the File object. + + nsAutoString name; + mFile->GetName(name); + + nsAutoString type; + mFile->GetType(type); + + nsTArray<RefPtr<BlobImpl>> blobImpls; + blobImpls.AppendElement(mFile->Impl()); + + ErrorResult rv; + RefPtr<BlobImpl> blobImpl = + MultipartBlobImpl::Create(Move(blobImpls), name, type, rv); + if (NS_WARN_IF(rv.Failed())) { + if (mErrorCallback) { + RefPtr<DOMException> exception = + DOMException::Create(rv.StealNSResult()); + mErrorCallback->HandleEvent(*exception); + } + + return NS_OK; + } + + RefPtr<File> file = File::Create(mFile->GetParentObject(), blobImpl); + MOZ_ASSERT(file); + + mCallback->HandleEvent(*file); + return NS_OK; + } + +private: + RefPtr<FileCallback> mCallback; + RefPtr<ErrorCallback> mErrorCallback; + RefPtr<File> mFile; +}; + +} // anonymous namespace + +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileEntry, FileSystemEntry, mFile) + +NS_IMPL_ADDREF_INHERITED(FileSystemFileEntry, FileSystemEntry) +NS_IMPL_RELEASE_INHERITED(FileSystemFileEntry, FileSystemEntry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileSystemFileEntry) +NS_INTERFACE_MAP_END_INHERITING(FileSystemEntry) + +FileSystemFileEntry::FileSystemFileEntry(nsIGlobalObject* aGlobal, + File* aFile, + FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem) + : FileSystemEntry(aGlobal, aParentEntry, aFileSystem) + , mFile(aFile) +{ + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(mFile); +} + +FileSystemFileEntry::~FileSystemFileEntry() +{} + +JSObject* +FileSystemFileEntry::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return FileSystemFileEntryBinding::Wrap(aCx, this, aGivenProto); +} + +void +FileSystemFileEntry::GetName(nsAString& aName, ErrorResult& aRv) const +{ + mFile->GetName(aName); +} + +void +FileSystemFileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const +{ + mFile->Impl()->GetDOMPath(aPath); + if (aPath.IsEmpty()) { + // We're under the root directory. webkitRelativePath + // (implemented as GetPath) is for cases when file is selected because its + // ancestor directory is selected. But that is not the case here, so need to + // manually prepend '/'. + nsAutoString name; + mFile->GetName(name); + aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + aPath.Append(name); + } +} + +void +FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const +{ + RefPtr<FileCallbackRunnable> runnable = + new FileCallbackRunnable(&aSuccessCallback, + aErrorCallback.WasPassed() + ? &aErrorCallback.Value() : nullptr, + mFile); + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemFileEntry.h b/dom/filesystem/compat/FileSystemFileEntry.h new file mode 100644 index 000000000..06b76e9f8 --- /dev/null +++ b/dom/filesystem/compat/FileSystemFileEntry.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemFileEntry_h +#define mozilla_dom_FileSystemFileEntry_h + +#include "mozilla/dom/FileSystemEntry.h" + +namespace mozilla { +namespace dom { + +class File; +class FileCallback; +class FileSystemDirectoryEntry; + +class FileSystemFileEntry final : public FileSystemEntry +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemFileEntry, FileSystemEntry) + + FileSystemFileEntry(nsIGlobalObject* aGlobalObject, File* aFile, + FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem); + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + virtual bool + IsFile() const override + { + return true; + } + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; + + void + GetFile(FileCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const; + +private: + ~FileSystemFileEntry(); + + RefPtr<File> mFile; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemFileEntry_h diff --git a/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp new file mode 100644 index 000000000..68ce62aa2 --- /dev/null +++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemRootDirectoryEntry.h" +#include "FileSystemRootDirectoryReader.h" +#include "mozilla/dom/FileSystemUtils.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemRootDirectoryEntry, + FileSystemDirectoryEntry, mEntries) + +NS_IMPL_ADDREF_INHERITED(FileSystemRootDirectoryEntry, FileSystemDirectoryEntry) +NS_IMPL_RELEASE_INHERITED(FileSystemRootDirectoryEntry, FileSystemDirectoryEntry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileSystemRootDirectoryEntry) +NS_INTERFACE_MAP_END_INHERITING(FileSystemDirectoryEntry) + +FileSystemRootDirectoryEntry::FileSystemRootDirectoryEntry(nsIGlobalObject* aGlobal, + const Sequence<RefPtr<FileSystemEntry>>& aEntries, + FileSystem* aFileSystem) + : FileSystemDirectoryEntry(aGlobal, nullptr, nullptr, aFileSystem) + , mEntries(aEntries) +{ + MOZ_ASSERT(aGlobal); +} + +FileSystemRootDirectoryEntry::~FileSystemRootDirectoryEntry() +{} + +void +FileSystemRootDirectoryEntry::GetName(nsAString& aName, ErrorResult& aRv) const +{ + aName.Truncate(); +} + +void +FileSystemRootDirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const +{ + aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); +} + +already_AddRefed<FileSystemDirectoryReader> +FileSystemRootDirectoryEntry::CreateReader() +{ + RefPtr<FileSystemDirectoryReader> reader = + new FileSystemRootDirectoryReader(this, Filesystem(), mEntries); + return reader.forget(); +} + +void +FileSystemRootDirectoryEntry::GetInternal(const nsAString& aPath, + const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + GetInternalType aType) +{ + if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) { + return; + } + + if (aFlag.mCreate) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsTArray<nsString> parts; + if (!FileSystemUtils::IsValidRelativeDOMPath(aPath, parts)) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + MOZ_ASSERT(!parts.IsEmpty()); + + RefPtr<FileSystemEntry> entry; + for (uint32_t i = 0; i < mEntries.Length(); ++i) { + ErrorResult rv; + nsAutoString name; + mEntries[i]->GetName(name, rv); + + if (NS_WARN_IF(rv.Failed())) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + rv.StealNSResult()); + return; + } + + if (name == parts[0]) { + entry = mEntries[i]; + break; + } + } + + // Not found. + if (!entry) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + // No subdirectory in the path. + if (parts.Length() == 1) { + if ((entry->IsFile() && aType == eGetDirectory) || + (entry->IsDirectory() && aType == eGetFile)) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_TYPE_MISMATCH_ERR); + return; + } + + if (aSuccessCallback.WasPassed()) { + RefPtr<EntryCallbackRunnable> runnable = + new EntryCallbackRunnable(&aSuccessCallback.Value(), entry); + DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } + return; + } + + // Subdirectories, but this is a file. + if (entry->IsFile()) { + ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, + NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + // Let's recreate a path without the first directory. + nsAutoString path; + for (uint32_t i = 1, len = parts.Length(); i < len; ++i) { + path.Append(parts[i]); + if (i < len - 1) { + path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + } + } + + auto* directoryEntry = static_cast<FileSystemDirectoryEntry*>(entry.get()); + directoryEntry->GetInternal(path, aFlag, aSuccessCallback, aErrorCallback, + aType); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemRootDirectoryEntry.h b/dom/filesystem/compat/FileSystemRootDirectoryEntry.h new file mode 100644 index 000000000..28c151ea2 --- /dev/null +++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemRootDirectoryEntry_h +#define mozilla_dom_FileSystemRootDirectoryEntry_h + +#include "mozilla/dom/FileSystemDirectoryEntry.h" + +namespace mozilla { +namespace dom { + +class FileSystemRootDirectoryEntry final : public FileSystemDirectoryEntry +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemRootDirectoryEntry, FileSystemDirectoryEntry) + + FileSystemRootDirectoryEntry(nsIGlobalObject* aGlobalObject, + const Sequence<RefPtr<FileSystemEntry>>& aEntries, + FileSystem* aFileSystem); + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; + + virtual already_AddRefed<FileSystemDirectoryReader> + CreateReader() override; + +private: + ~FileSystemRootDirectoryEntry(); + + virtual void + GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, + const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + GetInternalType aType) override; + + void + Error(const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + nsresult aError) const; + + Sequence<RefPtr<FileSystemEntry>> mEntries; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemRootDirectoryEntry_h diff --git a/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp new file mode 100644 index 000000000..5b4a41752 --- /dev/null +++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemRootDirectoryReader.h" +#include "CallbackRunnables.h" +#include "nsIGlobalObject.h" + +namespace mozilla { +namespace dom { + +namespace { + +class EntriesCallbackRunnable final : public Runnable +{ +public: + EntriesCallbackRunnable(FileSystemEntriesCallback* aCallback, + const Sequence<RefPtr<FileSystemEntry>>& aEntries) + : mCallback(aCallback) + , mEntries(aEntries) + { + MOZ_ASSERT(aCallback); + } + + NS_IMETHOD + Run() override + { + Sequence<OwningNonNull<FileSystemEntry>> entries; + for (uint32_t i = 0; i < mEntries.Length(); ++i) { + if (!entries.AppendElement(mEntries[i].forget(), fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + mCallback->HandleEvent(entries); + return NS_OK; + } + +private: + RefPtr<FileSystemEntriesCallback> mCallback; + Sequence<RefPtr<FileSystemEntry>> mEntries; +}; + +} // anonymous namespace + +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemRootDirectoryReader, + FileSystemDirectoryReader, mEntries) + +NS_IMPL_ADDREF_INHERITED(FileSystemRootDirectoryReader, + FileSystemDirectoryReader) +NS_IMPL_RELEASE_INHERITED(FileSystemRootDirectoryReader, + FileSystemDirectoryReader) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileSystemRootDirectoryReader) +NS_INTERFACE_MAP_END_INHERITING(FileSystemDirectoryReader) + +FileSystemRootDirectoryReader::FileSystemRootDirectoryReader(FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem, + const Sequence<RefPtr<FileSystemEntry>>& aEntries) + : FileSystemDirectoryReader(aParentEntry, aFileSystem, nullptr) + , mEntries(aEntries) + , mAlreadyRead(false) +{ + MOZ_ASSERT(aParentEntry); + MOZ_ASSERT(aFileSystem); +} + +FileSystemRootDirectoryReader::~FileSystemRootDirectoryReader() +{} + +void +FileSystemRootDirectoryReader::ReadEntries(FileSystemEntriesCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + ErrorResult& aRv) +{ + if (mAlreadyRead) { + RefPtr<EmptyEntriesCallbackRunnable> runnable = + new EmptyEntriesCallbackRunnable(&aSuccessCallback); + aRv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed"); + return; + } + + // This object can be used only once. + mAlreadyRead = true; + + RefPtr<EntriesCallbackRunnable> runnable = + new EntriesCallbackRunnable(&aSuccessCallback, mEntries); + aRv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed"); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemRootDirectoryReader.h b/dom/filesystem/compat/FileSystemRootDirectoryReader.h new file mode 100644 index 000000000..54bca7726 --- /dev/null +++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FileSystemRootDirectoryReader_h +#define mozilla_dom_FileSystemRootDirectoryReader_h + +#include "FileSystemDirectoryReader.h" + +namespace mozilla { +namespace dom { + +class FileSystemRootDirectoryReader final : public FileSystemDirectoryReader +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemRootDirectoryReader, + FileSystemDirectoryReader) + + explicit FileSystemRootDirectoryReader(FileSystemDirectoryEntry* aParentEntry, + FileSystem* aFileSystem, + const Sequence<RefPtr<FileSystemEntry>>& aEntries); + + virtual void + ReadEntries(FileSystemEntriesCallback& aSuccessCallback, + const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, + ErrorResult& aRv) override; + +private: + ~FileSystemRootDirectoryReader(); + + Sequence<RefPtr<FileSystemEntry>> mEntries; + bool mAlreadyRead; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileSystemRootDirectoryReader_h diff --git a/dom/filesystem/compat/moz.build b/dom/filesystem/compat/moz.build new file mode 100644 index 000000000..fc3f9482f --- /dev/null +++ b/dom/filesystem/compat/moz.build @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +TEST_DIRS += ['tests'] + +EXPORTS.mozilla.dom += [ + 'FileSystem.h', + 'FileSystemDirectoryEntry.h', + 'FileSystemDirectoryReader.h', + 'FileSystemEntry.h', + 'FileSystemFileEntry.h', +] + +UNIFIED_SOURCES += [ + 'CallbackRunnables.cpp', + 'FileSystem.cpp', + 'FileSystemDirectoryEntry.cpp', + 'FileSystemDirectoryReader.cpp', + 'FileSystemEntry.cpp', + 'FileSystemFileEntry.cpp', + 'FileSystemRootDirectoryEntry.cpp', + 'FileSystemRootDirectoryReader.cpp', +] + +FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/filesystem/compat/tests/mochitest.ini b/dom/filesystem/compat/tests/mochitest.ini new file mode 100644 index 000000000..2b6eafb55 --- /dev/null +++ b/dom/filesystem/compat/tests/mochitest.ini @@ -0,0 +1,8 @@ +[DEFAULT] +support-files = + script_entries.js + !/dom/html/test/form_submit_server.sjs + +[test_basic.html] +[test_no_dnd.html] +[test_formSubmission.html] diff --git a/dom/filesystem/compat/tests/moz.build b/dom/filesystem/compat/tests/moz.build new file mode 100644 index 000000000..3b13ba431 --- /dev/null +++ b/dom/filesystem/compat/tests/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/dom/filesystem/compat/tests/script_entries.js b/dom/filesystem/compat/tests/script_entries.js new file mode 100644 index 000000000..8083214c9 --- /dev/null +++ b/dom/filesystem/compat/tests/script_entries.js @@ -0,0 +1,47 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File", "Directory"]); + +var tmpFile, tmpDir; + +addMessageListener("entries.open", function (e) { + tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + tmpFile.append('file.txt'); + tmpFile.createUnique(Components.interfaces.nsIFile.FILE_TYPE, 0o600); + + tmpDir = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + + tmpDir.append('dir-test'); + tmpDir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file1 = tmpDir.clone(); + file1.append('foo.txt'); + file1.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var dir1 = tmpDir.clone(); + dir1.append('subdir'); + dir1.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file2 = dir1.clone(); + file2.append('bar..txt'); // Note the double .. + file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var dir2 = dir1.clone(); + dir2.append('subsubdir'); + dir2.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + sendAsyncMessage("entries.opened", { + data: [ new Directory(tmpDir.path), File.createFromNsIFile(tmpFile) ] + }); +}); + +addMessageListener("entries.delete", function(e) { + tmpFile.remove(true); + tmpDir.remove(true); + sendAsyncMessage("entries.deleted"); +}); diff --git a/dom/filesystem/compat/tests/test_basic.html b/dom/filesystem/compat/tests/test_basic.html new file mode 100644 index 000000000..85a7418d5 --- /dev/null +++ b/dom/filesystem/compat/tests/test_basic.html @@ -0,0 +1,494 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Blink FileSystem API - subset</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<input id="entries" type="file"></input> +<script type="application/javascript;version=1.7"> + +var fileEntry; +var directoryEntry; +var script; + +function setup_tests() { + SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true], + ["dom.filesystem.pathcheck.disabled", true], + ["dom.webkitBlink.filesystem.enabled", true]]}, next); +} + +function populate_entries() { + var url = SimpleTest.getTestFileURL("script_entries.js"); + script = SpecialPowers.loadChromeScript(url); + + function onOpened(message) { + var entries = document.getElementById('entries'); + SpecialPowers.wrap(entries).mozSetDndFilesAndDirectories(message.data); + next(); + } + + script.addMessageListener("entries.opened", onOpened); + script.sendAsyncMessage("entries.open"); +} + +function test_entries() { + var entries = document.getElementById('entries'); + ok("webkitEntries" in entries, "HTMLInputElement.webkitEntries"); + is(entries.webkitEntries.length, 2, "HTMLInputElement.webkitEntries.length == 2"); + is(entries.files.length, 1, "HTMLInputElement.files is still populated"); + + for (var i = 0; i < entries.webkitEntries.length; ++i) { + if (entries.webkitEntries[i].isFile) { + ok(!fileEntry, "We just want 1 fileEntry"); + fileEntry = entries.webkitEntries[i]; + } else { + ok(entries.webkitEntries[i].isDirectory, "If not a file, we have a directory."); + ok(!directoryEntry, "We just want 1 directoryEntry"); + directoryEntry = entries.webkitEntries[i]; + } + } + + next(); +} + +function test_fileEntry() { + ok("name" in fileEntry, "We have a name."); + ok("fullPath" in fileEntry, "We have a fullPath."); + ok("filesystem" in fileEntry, "We have a filesystem."); + + next(); +} + +function test_fileEntry_file() { + fileEntry.file(function(file) { + ok(file, "We have a file here!"); + is(file.name, fileEntry.name, "Same file name."); + next(); + }, function() { + ok(false, "Something when wrong!"); + }); +} + +function test_fileEntry_getParent() { + fileEntry.getParent(function(entry) { + is(fileEntry.fullPath, entry.fullPath, "Top level FileEntry should return itself as parent."); + next(); + }, function() { + ok(false, "This is wrong."); + }); +} + +function test_directoryEntry() { + ok("name" in directoryEntry, "We have a name."); + ok("fullPath" in directoryEntry, "We have a fullPath."); + ok("filesystem" in directoryEntry, "We have a filesystem."); + + next(); +} + +function test_directoryEntry_createReader() { + var reader = directoryEntry.createReader(); + ok(reader, "We have a DirectoryReader"); + + reader.readEntries(function(a) { + ok(Array.isArray(a), "We want an array."); + is(a.length, 2, "reader.readyEntries returns 2 elements."); + + for (var i = 0; i < 2; ++i) { + ok(a[i].name == "subdir" || a[i].name == "foo.txt", "Correct names"); + is(a[i].fullPath, directoryEntry.fullPath + "/" + a[i].name, "FullPath is correct"); + } + + // Called twice: + reader.readEntries(function(a) { + ok(Array.isArray(a), "We want an array."); + is(a.length, 0, "reader.readyEntries returns 0 elements."); + next(); + }, function() { + ok(false, "Something when wrong!"); + }); + + }, function() { + ok(false, "Something when wrong!"); + }); +} + +function test_directoryEntry_getParent() { + directoryEntry.getParent(function(entry) { + is(directoryEntry.fullPath, entry.fullPath, "Top level FileEntry should return itself as parent."); + next(); + }, function() { + ok(false, "This is wrong."); + }); +} + +function test_directoryEntry_getFile_securityError() { + directoryEntry.getFile("foo", { create: true }, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "SecurityError", "This must generate a SecurityError."); + next(); + }); +} + +function test_directoryEntry_getFile_typeMismatchError() { + directoryEntry.getFile("subdir", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "TypeMismatchError", "This must generate a TypeMismatchError."); + next(); + }); +} + +function test_directoryEntry_getFile_nonValidPath() { + directoryEntry.getFile("../../", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_directoryEntry_getFile_nonExistingPath() { + directoryEntry.getFile("foo_bar.txt", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_directoryEntry_getFile_simple() { + directoryEntry.getFile("foo.txt", {}, + function(e) { + is(e.name, "foo.txt", "We have the right FileEntry."); + test_getParent(e, directoryEntry, /* nested */ false); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_directoryEntry_getFile_deep() { + directoryEntry.getFile("subdir/bar..txt", {}, + function(e) { + is(e.name, "bar..txt", "We have the right FileEntry."); + test_getParent(e, directoryEntry, /* nested */ true); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_directoryEntry_getDirectory_securityError() { + directoryEntry.getDirectory("foo", { create: true }, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "SecurityError", "This must generate a SecurityError."); + next(); + }); +} + +function test_directoryEntry_getDirectory_typeMismatchError() { + directoryEntry.getDirectory("foo.txt", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "TypeMismatchError", "This must generate a TypeMismatchError."); + next(); + }); +} + +function test_directoryEntry_getDirectory_nonValidPath() { + directoryEntry.getDirectory("../../", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_directoryEntry_getDirectory_nonExistingPath() { + directoryEntry.getDirectory("non_existing_dir", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_directoryEntry_getDirectory_simple() { + directoryEntry.getDirectory("subdir", {}, + function(e) { + is(e.name, "subdir", "We have the right DirectoryEntry."); + test_getParent(e, directoryEntry, /* nested */ false); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_directoryEntry_getDirectory_deep() { + directoryEntry.getDirectory("subdir/subsubdir", {}, + function(e) { + is(e.name, "subsubdir", "We have the right DirectoryEntry."); + test_getParent(e, directoryEntry, /* nested */ true); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_filesystem() { + is(fileEntry.filesystem, directoryEntry.filesystem, "FileSystem object is shared."); + + var fs = fileEntry.filesystem; + ok(fs.name, "FileSystem.name exists."); + ok(fs.root, "FileSystem has a root."); + + is(fs.root.name, "", "FileSystem.root.name must be an empty string."); + is(fs.root.fullPath, "/", "FileSystem.root.fullPath must be '/'"); + + reader = fs.root.createReader(); + reader.readEntries(function(a) { + ok(Array.isArray(a), "We want an array."); + is(a.length, 2, "reader.readyEntries returns 2 elements."); + next(); + }, function() { + ok(false, "Something when wrong!"); + }); +} + +function test_root_getFile_securityError() { + fileEntry.filesystem.root.getFile("foo", { create: true }, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "SecurityError", "This must generate a SecurityError."); + next(); + }); +} + +function test_root_getFile_typeMismatchError() { + fileEntry.filesystem.root.getFile(directoryEntry.name, {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "TypeMismatchError", "This must generate a TypeMismatchError."); + next(); + }); +} + +function test_root_getFile_nonValidPath() { + fileEntry.filesystem.root.getFile("../../", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_root_getFile_nonExistingPath() { + fileEntry.filesystem.root.getFile("existing.txt", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_root_getFile_simple() { + fileEntry.filesystem.root.getFile(fileEntry.name, {}, + function(e) { + is(e.name, fileEntry.name, "We have the right FileEntry."); + next(); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_root_getFile_deep() { + fileEntry.filesystem.root.getFile(directoryEntry.name + "/subdir/bar..txt", {}, + function(e) { + is(e.name, "bar..txt", "We have the right FileEntry."); + next(); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_root_getDirectory_securityError() { + fileEntry.filesystem.root.getDirectory("foo", { create: true }, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "SecurityError", "This must generate a SecurityError."); + next(); + }); +} + +function test_root_getDirectory_typeMismatchError() { + fileEntry.filesystem.root.getDirectory(fileEntry.name, {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "TypeMismatchError", "This must generate a TypeMismatchError."); + next(); + }); +} + +function test_root_getDirectory_nonValidPath() { + fileEntry.filesystem.root.getDirectory("../../", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_root_getDirectory_nonExistingPath() { + fileEntry.filesystem.root.getDirectory("404", {}, + function() { + ok(false, "This should not happen."); + }, function(e) { + is(e.name, "NotFoundError", "This must generate a NotFoundError."); + next(); + }); +} + +function test_root_getDirectory_simple() { + fileEntry.filesystem.root.getDirectory(directoryEntry.name, {}, + function(e) { + is(e.name, directoryEntry.name, "We have the right DirectoryEntry."); + next(); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_root_getDirectory_deep() { + fileEntry.filesystem.root.getDirectory(directoryEntry.name + "/subdir/subsubdir", {}, + function(e) { + is(e.name, "subsubdir", "We have the right DirectoryEntry."); + next(); + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function cleanUpTestingFiles() { + script.addMessageListener("entries.deleted", function onDeleted() { + script.removeMessageListener("entries.deleted"); + script.destroy(); + next(); + }); + + script.sendAsyncMessage("entries.delete"); +} + +function test_getParent(entry, parentEntry, nested) { + entry.getParent(function(e) { + ok(e, "We have a parent Entry."); + if (!nested) { + is (e, parentEntry, "Parent entry matches"); + next(); + } else { + test_getParent(e, parentEntry, false); + } + }, function(e) { + ok(false, "This should not happen."); + }); +} + +function test_webkitRelativePath() { + fileEntry.file(function(file1) { + ok(file1, "We have a file here!"); + ok(!file1.webkitRelativePath, "webkitRelativePath is an empty string"); + + fileEntry.file(function(file2) { + ok(file2, "We have a file here!"); + ok(!file2.webkitRelativePath, "webkitRelativePath is an empty string"); + isnot(file1, file2, "The 2 files are not the same"); + + next(); + }, function() { + ok(false, "Something when wrong!"); + }); + }, function() { + ok(false, "Something when wrong!"); + }); +} + +var tests = [ + setup_tests, + populate_entries, + + test_entries, + + test_fileEntry, + test_fileEntry_file, + test_fileEntry_getParent, + + test_directoryEntry, + test_directoryEntry_createReader, + test_directoryEntry_getParent, + + test_directoryEntry_getFile_securityError, + test_directoryEntry_getFile_typeMismatchError, + test_directoryEntry_getFile_nonValidPath, + test_directoryEntry_getFile_nonExistingPath, + test_directoryEntry_getFile_simple, + test_directoryEntry_getFile_deep, + + test_directoryEntry_getDirectory_securityError, + test_directoryEntry_getDirectory_typeMismatchError, + test_directoryEntry_getDirectory_nonValidPath, + test_directoryEntry_getDirectory_nonExistingPath, + test_directoryEntry_getDirectory_simple, + test_directoryEntry_getDirectory_deep, + + test_filesystem, + + test_root_getFile_securityError, + test_root_getFile_typeMismatchError, + test_root_getFile_nonValidPath, + test_root_getFile_nonExistingPath, + test_root_getFile_simple, + test_root_getFile_deep, + + test_root_getDirectory_securityError, + test_root_getDirectory_typeMismatchError, + test_root_getDirectory_nonValidPath, + test_root_getDirectory_nonExistingPath, + test_root_getDirectory_simple, + test_root_getDirectory_deep, + + test_webkitRelativePath, + + cleanUpTestingFiles, +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +next(); +</script> +</body> +</html> diff --git a/dom/filesystem/compat/tests/test_formSubmission.html b/dom/filesystem/compat/tests/test_formSubmission.html new file mode 100644 index 000000000..0c04b8bf1 --- /dev/null +++ b/dom/filesystem/compat/tests/test_formSubmission.html @@ -0,0 +1,267 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Directory form submission</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> +</head> +<body onload="return next();"> + +<iframe name="target_iframe" id="target_iframe"></iframe> + +<form action="../../../html/test/form_submit_server.sjs" target="target_iframe" id="form" + method="POST" enctype="multipart/form-data"> +</form> + +<script class="testbody" type="text/javascript;version=1.8"> + +var form; +var iframe; +var input; +var xhr; + +function setup_tests() { + form = document.getElementById("form"); + + iframe = document.getElementById("target_iframe"); + iframe.onload = function() { + info("Frame loaded!"); + next(); + } + + SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true], + ["dom.webkitBlink.dirPicker.enabled", true], + ["dom.filesystem.pathcheck.disabled", true], + ["dom.webkitBlink.filesystem.enabled", true]]}, next); +} + +function populate_entries(webkitDirectory) { + if (input) { + form.removeChild(input); + } + + input = document.createElement('input'); + input.setAttribute('id', 'input'); + input.setAttribute('type', 'file'); + input.setAttribute('name', 'input'); + + if (webkitDirectory) { + input.setAttribute('webkitdirectory', 'true'); + } + + form.appendChild(input); + + var url = SimpleTest.getTestFileURL("script_entries.js"); + var script = SpecialPowers.loadChromeScript(url); + + function onOpened(message) { + input.addEventListener("change", function onChange() { + input.removeEventListener("change", onChange); + next(); + }); + + SpecialPowers.wrap(input).mozSetDndFilesAndDirectories([message.data[0]]); + script.destroy(); + } + + script.addMessageListener("entries.opened", onOpened); + script.sendAsyncMessage("entries.open"); +} + +function setup_plain() { + info("Preparing for a plain text submission..."); + form.action = "../../../html/test/form_submit_server.sjs?plain"; + form.method = "POST"; + form.enctype = "text/plain"; + form.submit(); +} + +function test_plain() { + var content = iframe.contentDocument.documentElement.textContent; + var submission = JSON.parse(content); + input.getFilesAndDirectories().then(function(array) { + is(submission, array.map(function(v) { + return "input=" + v.name + "\r\n"; + }).join(""), "Data match"); + + next(); + }); +} + +function setup_urlencoded() { + info("Preparing for a urlencoded submission..."); + form.action = "../../../html/test/form_submit_server.sjs?url"; + form.method = "POST"; + form.enctype = "application/x-www-form-urlencoded"; + form.submit(); +} + +function setup_urlencoded_get() { + info("Preparing for a urlencoded+GET submission..."); + form.action = "../../../html/test/form_submit_server.sjs?xxyy"; + form.method = "GET"; + form.enctype = ""; + form.submit(); +} + +function setup_urlencoded_empty() { + info("Preparing for a urlencoded+default values submission..."); + form.action = "../../../html/test/form_submit_server.sjs"; + form.method = ""; + form.enctype = ""; + form.submit(); +} + +function test_urlencoded() { + var content = iframe.contentDocument.documentElement.textContent; + var submission = JSON.parse(content); + input.getFilesAndDirectories().then(function(array) { + is(submission, array.map(function(v) { + return "input=" + v.name; + }).join("&"), "Data match"); + + next(); + }); +} + +function setup_formData() { + info("Preparing for a fromData submission..."); + + xhr = new XMLHttpRequest(); + xhr.onload = next; + xhr.open("POST", "../../../html/test/form_submit_server.sjs"); + xhr.send(new FormData(form)); +} + +function test_multipart() { + var submission = JSON.parse(xhr.responseText); + input.getFilesAndDirectories().then(function(array) { + is(submission.length, array.length, "Same length"); + + for (var i = 0; i < array.length; ++i) { + if (array[i] instanceof Directory) { + is(submission[i].headers["Content-Disposition"], + "form-data; name=\"input\"; filename=\"/" + array[i].name + "\"", + "Correct Content-Disposition"); + is(submission[i].headers["Content-Type"], "application/octet-stream", + "Correct Content-Type"); + is(submission[i].body, "", "Correct body"); + } else { + ok(array[i] instanceof File); + is(submission[i].headers["Content-Disposition"], + "form-data; name=\"input\"; filename=\"" + array[i].name + "\"", + "Correct Content-Disposition"); + is(submission[i].headers["Content-Type"], array[i].type, + "Correct Content-Type"); + is(submission[i].body, "", "Correct body"); + } + } + next(); + }); +} + +function test_webkit_plain() { + var content = iframe.contentDocument.documentElement.textContent; + var submission = JSON.parse(content); + + input.getFiles(true).then(function(array) { + is(submission, array.map(function(v) { + return "input=" + v.name + "\r\n"; + }).join(""), "Data match"); + + next(); + }); +} + +function test_webkit_urlencoded() { + var content = iframe.contentDocument.documentElement.textContent; + var submission = JSON.parse(content); + input.getFiles(true).then(function(array) { + is(submission, array.map(function(v) { + return "input=" + v.name; + }).join("&"), "Data match"); + + next(); + }); +} + +function test_webkit_multipart() { + var submission = JSON.parse(xhr.responseText); + input.getFiles(true).then(function(array) { + is(submission.length, array.length, "Same length"); + + for (var i = 0; i < array.length; ++i) { + if (array[i] instanceof Directory) { + is(submission[i].headers["Content-Disposition"], + "form-data; name=\"input\"; filename=\"/" + array[i].name + "\"", + "Correct Content-Disposition"); + is(submission[i].headers["Content-Type"], "application/octet-stream", + "Correct Content-Type"); + is(submission[i].body, "", "Correct body"); + } else { + ok(array[i] instanceof File); + is(submission[i].headers["Content-Disposition"], + "form-data; name=\"input\"; filename=\"" + array[i].webkitRelativePath + "\"", + "Correct Content-Disposition"); + is(submission[i].headers["Content-Type"], array[i].type, + "Correct Content-Type"); + is(submission[i].body, "", "Correct body"); + } + } + next(); + }); +} + +var tests = [ + setup_tests, + function() { populate_entries(false); }, + + setup_plain, + test_plain, + + setup_urlencoded, + test_urlencoded, + + setup_urlencoded_get, + test_urlencoded, + + setup_urlencoded_empty, + test_urlencoded, + + setup_formData, + test_multipart, + + function() { populate_entries(true); }, + + setup_plain, + test_webkit_plain, + + setup_urlencoded, + test_webkit_urlencoded, + + setup_urlencoded_get, + test_webkit_urlencoded, + + setup_urlencoded_empty, + test_webkit_urlencoded, + + setup_formData, + test_webkit_multipart, +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</body> +</html> diff --git a/dom/filesystem/compat/tests/test_no_dnd.html b/dom/filesystem/compat/tests/test_no_dnd.html new file mode 100644 index 000000000..a78ac108f --- /dev/null +++ b/dom/filesystem/compat/tests/test_no_dnd.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Blink FileSystem API - no DND == no webkitEntries</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<script type="application/javascript;version=1.7"> + +var fileEntry; +var directoryEntry; +var script; +var entries; + +function setup_tests() { + SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true], + ["dom.filesystem.pathcheck.disabled", true], + ["dom.webkitBlink.filesystem.enabled", true]]}, next); +} + +function populate_entries() { + entries = document.createElement('input'); + entries.setAttribute('type', 'file'); + document.body.appendChild(entries); + + var url = SimpleTest.getTestFileURL("script_entries.js"); + script = SpecialPowers.loadChromeScript(url); + + function onOpened(message) { + for (var i = 0 ; i < message.data.length; ++i) { + if (message.data[i] instanceof File) { + SpecialPowers.wrap(entries).mozSetFileArray([message.data[i]]); + next(); + } + } + } + + script.addMessageListener("entries.opened", onOpened); + script.sendAsyncMessage("entries.open"); +} + +function test_entries() { + ok("webkitEntries" in entries, "HTMLInputElement.webkitEntries"); + is(entries.webkitEntries.length, 0, "HTMLInputElement.webkitEntries.length == 0"); + is(entries.files.length, 1, "HTMLInputElement.files is still populated"); + + next(); +} + +function cleanUpTestingFiles() { + script.addMessageListener("entries.deleted", function onDeleted() { + script.removeMessageListener("entries.deleted"); + script.destroy(); + next(); + }); + + script.sendAsyncMessage("entries.delete"); +} + +var tests = [ + setup_tests, + populate_entries, + + test_entries, + + cleanUpTestingFiles, +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +next(); +</script> +</body> +</html> |