/* -*- 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 "js/Array.h" // JS::NewArrayObject #include "js/PropertyAndElement.h" // JS_GetElement #include "mozilla/dom/FileBinding.h" #include "mozilla/dom/FileSystem.h" #include "mozilla/dom/FileSystemDirectoryReaderBinding.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" namespace mozilla::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); } MOZ_CAN_RUN_SCRIPT virtual void ResolvedCallback(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) override { if (NS_WARN_IF(!aValue.isObject())) { return; } JS::Rooted obj(aCx, &aValue.toObject()); uint32_t length; if (NS_WARN_IF(!JS::GetArrayLength(aCx, obj, &length))) { return; } Sequence> sequence; if (NS_WARN_IF(!sequence.SetLength(length, fallible))) { return; } for (uint32_t i = 0; i < length; ++i) { JS::Rooted value(aCx); if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &value))) { return; } if (NS_WARN_IF(!value.isObject())) { return; } JS::Rooted valueObj(aCx, &value.toObject()); RefPtr file; if (NS_SUCCEEDED(UNWRAP_OBJECT(File, valueObj, file))) { RefPtr entry = new FileSystemFileEntry( mParentEntry->GetParentObject(), file, mParentEntry, mFileSystem); sequence[i] = entry; continue; } RefPtr directory; if (NS_WARN_IF( NS_FAILED(UNWRAP_OBJECT(Directory, valueObj, directory)))) { return; } RefPtr entry = new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, mParentEntry, mFileSystem); sequence[i] = entry; } mSuccessCallback->Call(sequence); } virtual void RejectedCallback(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) override { if (mErrorCallback) { RefPtr runnable = new ErrorCallbackRunnable( mParentEntry->GetParentObject(), mErrorCallback, NS_ERROR_DOM_INVALID_STATE_ERR); FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(), runnable.forget()); } } private: ~PromiseHandler() = default; RefPtr mParentEntry; RefPtr mFileSystem; const RefPtr mSuccessCallback; RefPtr 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() = default; JSObject* FileSystemDirectoryReader::WrapObject( JSContext* aCx, JS::Handle aGivenProto) { return FileSystemDirectoryReader_Binding::Wrap(aCx, this, aGivenProto); } void FileSystemDirectoryReader::ReadEntries( FileSystemEntriesCallback& aSuccessCallback, const Optional>& aErrorCallback, ErrorResult& aRv) { MOZ_ASSERT(mDirectory); if (mAlreadyRead) { RefPtr runnable = new EmptyEntriesCallbackRunnable(&aSuccessCallback); FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget()); return; } // This object can be used only once. mAlreadyRead = true; ErrorResult rv; RefPtr promise = mDirectory->GetFilesAndDirectories(rv); if (NS_WARN_IF(rv.Failed())) { ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, rv.StealNSResult()); return; } RefPtr handler = new PromiseHandler( mParentEntry, mFileSystem, &aSuccessCallback, aErrorCallback.WasPassed() ? &aErrorCallback.Value() : nullptr); promise->AppendNativeHandler(handler); } } // namespace mozilla::dom