FileSystemDirectoryReader.cpp (5804B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FileSystemDirectoryReader.h" 8 9 #include "CallbackRunnables.h" 10 #include "FileSystemFileEntry.h" 11 #include "js/Array.h" // JS::NewArrayObject 12 #include "js/PropertyAndElement.h" // JS_GetElement 13 #include "mozilla/dom/Directory.h" 14 #include "mozilla/dom/DirectoryBinding.h" 15 #include "mozilla/dom/FileBinding.h" 16 #include "mozilla/dom/FileSystem.h" 17 #include "mozilla/dom/FileSystemDirectoryReaderBinding.h" 18 #include "mozilla/dom/FileSystemUtils.h" 19 #include "mozilla/dom/Promise.h" 20 #include "mozilla/dom/PromiseNativeHandler.h" 21 22 namespace mozilla::dom { 23 24 namespace { 25 26 class PromiseHandler final : public PromiseNativeHandler { 27 public: 28 NS_DECL_ISUPPORTS 29 30 PromiseHandler(FileSystemDirectoryEntry* aParentEntry, 31 FileSystem* aFileSystem, 32 FileSystemEntriesCallback* aSuccessCallback, 33 ErrorCallback* aErrorCallback) 34 : mParentEntry(aParentEntry), 35 mFileSystem(aFileSystem), 36 mSuccessCallback(aSuccessCallback), 37 mErrorCallback(aErrorCallback) { 38 MOZ_ASSERT(aParentEntry); 39 MOZ_ASSERT(aFileSystem); 40 MOZ_ASSERT(aSuccessCallback); 41 } 42 43 MOZ_CAN_RUN_SCRIPT 44 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 45 ErrorResult& aRv) override { 46 if (NS_WARN_IF(!aValue.isObject())) { 47 return; 48 } 49 50 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); 51 52 uint32_t length; 53 if (NS_WARN_IF(!JS::GetArrayLength(aCx, obj, &length))) { 54 return; 55 } 56 57 Sequence<OwningNonNull<FileSystemEntry>> sequence; 58 if (NS_WARN_IF(!sequence.SetLength(length, fallible))) { 59 return; 60 } 61 62 for (uint32_t i = 0; i < length; ++i) { 63 JS::Rooted<JS::Value> value(aCx); 64 if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &value))) { 65 return; 66 } 67 68 if (NS_WARN_IF(!value.isObject())) { 69 return; 70 } 71 72 JS::Rooted<JSObject*> valueObj(aCx, &value.toObject()); 73 74 RefPtr<File> file; 75 if (NS_SUCCEEDED(UNWRAP_OBJECT(File, valueObj, file))) { 76 RefPtr<FileSystemFileEntry> entry = new FileSystemFileEntry( 77 mParentEntry->GetParentObject(), file, mParentEntry, mFileSystem); 78 sequence[i] = entry; 79 continue; 80 } 81 82 RefPtr<Directory> directory; 83 if (NS_WARN_IF( 84 NS_FAILED(UNWRAP_OBJECT(Directory, valueObj, directory)))) { 85 return; 86 } 87 88 RefPtr<FileSystemDirectoryEntry> entry = 89 new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), 90 directory, mParentEntry, mFileSystem); 91 sequence[i] = entry; 92 } 93 94 mSuccessCallback->Call(sequence); 95 } 96 97 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 98 ErrorResult& aRv) override { 99 if (mErrorCallback) { 100 RefPtr<ErrorCallbackRunnable> runnable = new ErrorCallbackRunnable( 101 mParentEntry->GetParentObject(), mErrorCallback, 102 NS_ERROR_DOM_INVALID_STATE_ERR); 103 104 FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(), 105 runnable.forget()); 106 } 107 } 108 109 private: 110 ~PromiseHandler() = default; 111 112 RefPtr<FileSystemDirectoryEntry> mParentEntry; 113 RefPtr<FileSystem> mFileSystem; 114 const RefPtr<FileSystemEntriesCallback> mSuccessCallback; 115 RefPtr<ErrorCallback> mErrorCallback; 116 }; 117 118 NS_IMPL_ISUPPORTS0(PromiseHandler); 119 120 } // anonymous namespace 121 122 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemDirectoryReader, mParentEntry, 123 mDirectory, mFileSystem) 124 125 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemDirectoryReader) 126 NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemDirectoryReader) 127 128 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemDirectoryReader) 129 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 130 NS_INTERFACE_MAP_ENTRY(nsISupports) 131 NS_INTERFACE_MAP_END 132 133 FileSystemDirectoryReader::FileSystemDirectoryReader( 134 FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, 135 Directory* aDirectory) 136 : mParentEntry(aParentEntry), 137 mFileSystem(aFileSystem), 138 mDirectory(aDirectory), 139 mAlreadyRead(false) { 140 MOZ_ASSERT(aParentEntry); 141 MOZ_ASSERT(aFileSystem); 142 } 143 144 FileSystemDirectoryReader::~FileSystemDirectoryReader() = default; 145 146 JSObject* FileSystemDirectoryReader::WrapObject( 147 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 148 return FileSystemDirectoryReader_Binding::Wrap(aCx, this, aGivenProto); 149 } 150 151 void FileSystemDirectoryReader::ReadEntries( 152 FileSystemEntriesCallback& aSuccessCallback, 153 const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, 154 ErrorResult& aRv) { 155 MOZ_ASSERT(mDirectory); 156 157 if (mAlreadyRead) { 158 RefPtr<EmptyEntriesCallbackRunnable> runnable = 159 new EmptyEntriesCallbackRunnable(&aSuccessCallback); 160 161 FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget()); 162 return; 163 } 164 165 // This object can be used only once. 166 mAlreadyRead = true; 167 168 ErrorResult rv; 169 RefPtr<Promise> promise = mDirectory->GetFilesAndDirectories(rv); 170 if (NS_WARN_IF(rv.Failed())) { 171 ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, 172 rv.StealNSResult()); 173 return; 174 } 175 176 RefPtr<PromiseHandler> handler = new PromiseHandler( 177 mParentEntry, mFileSystem, &aSuccessCallback, 178 aErrorCallback.WasPassed() ? &aErrorCallback.Value() : nullptr); 179 promise->AppendNativeHandler(handler); 180 } 181 182 } // namespace mozilla::dom