CallbackRunnables.cpp (8113B)
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 "CallbackRunnables.h" 8 9 #include "../GetFileOrDirectoryTask.h" 10 #include "mozilla/dom/DOMException.h" 11 #include "mozilla/dom/Directory.h" 12 #include "mozilla/dom/DirectoryBinding.h" 13 #include "mozilla/dom/File.h" 14 #include "mozilla/dom/FileBinding.h" 15 #include "mozilla/dom/FileSystem.h" 16 #include "mozilla/dom/FileSystemDirectoryReaderBinding.h" 17 #include "mozilla/dom/FileSystemFileEntry.h" 18 #include "mozilla/dom/FileSystemUtils.h" 19 #include "mozilla/dom/Promise.h" 20 #include "nsIFile.h" 21 #include "nsIGlobalObject.h" 22 #include "nsPIDOMWindow.h" 23 24 namespace mozilla::dom { 25 26 EntryCallbackRunnable::EntryCallbackRunnable(FileSystemEntryCallback* aCallback, 27 FileSystemEntry* aEntry) 28 : Runnable("EntryCallbackRunnable"), mCallback(aCallback), mEntry(aEntry) { 29 MOZ_ASSERT(aCallback); 30 MOZ_ASSERT(aEntry); 31 } 32 33 NS_IMETHODIMP 34 EntryCallbackRunnable::Run() { 35 mCallback->Call(*mEntry); 36 return NS_OK; 37 } 38 39 ErrorCallbackRunnable::ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject, 40 ErrorCallback* aCallback, 41 nsresult aError) 42 : Runnable("ErrorCallbackRunnable"), 43 mGlobal(aGlobalObject), 44 mCallback(aCallback), 45 mError(aError) { 46 MOZ_ASSERT(aGlobalObject); 47 MOZ_ASSERT(aCallback); 48 MOZ_ASSERT(NS_FAILED(aError)); 49 } 50 51 NS_IMETHODIMP 52 ErrorCallbackRunnable::Run() { 53 RefPtr<DOMException> exception = DOMException::Create(mError); 54 mCallback->Call(*exception); 55 return NS_OK; 56 } 57 58 EmptyEntriesCallbackRunnable::EmptyEntriesCallbackRunnable( 59 FileSystemEntriesCallback* aCallback) 60 : Runnable("EmptyEntriesCallbackRunnable"), mCallback(aCallback) { 61 MOZ_ASSERT(aCallback); 62 } 63 64 NS_IMETHODIMP 65 EmptyEntriesCallbackRunnable::Run() { 66 Sequence<OwningNonNull<FileSystemEntry>> sequence; 67 mCallback->Call(sequence); 68 return NS_OK; 69 } 70 71 GetEntryHelper::GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, 72 Directory* aDirectory, 73 nsTArray<nsString>& aParts, 74 FileSystem* aFileSystem, 75 FileSystemEntryCallback* aSuccessCallback, 76 ErrorCallback* aErrorCallback, 77 FileSystemDirectoryEntry::GetInternalType aType) 78 : mParentEntry(aParentEntry), 79 mDirectory(aDirectory), 80 mParts(aParts.Clone()), 81 mFileSystem(aFileSystem), 82 mSuccessCallback(aSuccessCallback), 83 mErrorCallback(aErrorCallback), 84 mType(aType) { 85 MOZ_ASSERT(aParentEntry); 86 MOZ_ASSERT(aDirectory); 87 MOZ_ASSERT(!aParts.IsEmpty()); 88 MOZ_ASSERT(aFileSystem); 89 MOZ_ASSERT(aSuccessCallback || aErrorCallback); 90 } 91 92 GetEntryHelper::~GetEntryHelper() = default; 93 94 namespace { 95 96 nsresult DOMPathToRealPath(Directory* aDirectory, const nsAString& aPath, 97 nsIFile** aFile) { 98 nsString relativePath; 99 relativePath = aPath; 100 101 // Trim white spaces. 102 static const char kWhitespace[] = "\b\t\r\n "; 103 relativePath.Trim(kWhitespace); 104 105 nsTArray<nsString> parts; 106 if (!FileSystemUtils::IsValidRelativeDOMPath(relativePath, parts)) { 107 return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; 108 } 109 110 nsCOMPtr<nsIFile> file; 111 nsresult rv = aDirectory->GetInternalNsIFile()->Clone(getter_AddRefs(file)); 112 if (NS_WARN_IF(NS_FAILED(rv))) { 113 return rv; 114 } 115 116 for (uint32_t i = 0; i < parts.Length(); ++i) { 117 rv = file->AppendRelativePath(parts[i]); 118 if (NS_WARN_IF(NS_FAILED(rv))) { 119 return rv; 120 } 121 } 122 123 file.forget(aFile); 124 return NS_OK; 125 } 126 127 } // namespace 128 129 void GetEntryHelper::Run() { 130 MOZ_ASSERT(!mParts.IsEmpty()); 131 132 nsCOMPtr<nsIFile> realPath; 133 nsresult error = 134 DOMPathToRealPath(mDirectory, mParts[0], getter_AddRefs(realPath)); 135 136 ErrorResult rv; 137 RefPtr<FileSystemBase> fs = mDirectory->GetFileSystem(rv); 138 if (NS_WARN_IF(rv.Failed())) { 139 rv.SuppressException(); 140 Error(NS_ERROR_DOM_INVALID_STATE_ERR); 141 return; 142 } 143 144 RefPtr<GetFileOrDirectoryTaskChild> task = 145 GetFileOrDirectoryTaskChild::Create(fs, realPath, rv); 146 if (NS_WARN_IF(rv.Failed())) { 147 rv.SuppressException(); 148 Error(NS_ERROR_DOM_INVALID_STATE_ERR); 149 return; 150 } 151 152 task->SetError(error); 153 task->Start(); 154 155 RefPtr<Promise> promise = task->GetPromise(); 156 157 mParts.RemoveElementAt(0); 158 promise->AppendNativeHandler(this); 159 } 160 161 void GetEntryHelper::ResolvedCallback(JSContext* aCx, 162 JS::Handle<JS::Value> aValue, 163 ErrorResult& aRv) { 164 if (NS_WARN_IF(!aValue.isObject())) { 165 return; 166 } 167 168 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); 169 170 // This is not the last part of the path. 171 if (!mParts.IsEmpty()) { 172 ContinueRunning(obj); 173 return; 174 } 175 176 CompleteOperation(obj); 177 } 178 179 void GetEntryHelper::CompleteOperation(JSObject* aObj) { 180 MOZ_ASSERT(mParts.IsEmpty()); 181 182 if (mType == FileSystemDirectoryEntry::eGetFile) { 183 RefPtr<File> file; 184 if (NS_FAILED(UNWRAP_OBJECT(File, aObj, file))) { 185 Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); 186 return; 187 } 188 189 RefPtr<FileSystemFileEntry> entry = new FileSystemFileEntry( 190 mParentEntry->GetParentObject(), file, mParentEntry, mFileSystem); 191 mSuccessCallback->Call(*entry); 192 return; 193 } 194 195 MOZ_ASSERT(mType == FileSystemDirectoryEntry::eGetDirectory); 196 197 RefPtr<Directory> directory; 198 if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { 199 Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); 200 return; 201 } 202 203 RefPtr<FileSystemDirectoryEntry> entry = new FileSystemDirectoryEntry( 204 mParentEntry->GetParentObject(), directory, mParentEntry, mFileSystem); 205 mSuccessCallback->Call(*entry); 206 } 207 208 void GetEntryHelper::ContinueRunning(JSObject* aObj) { 209 MOZ_ASSERT(!mParts.IsEmpty()); 210 211 RefPtr<Directory> directory; 212 if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { 213 Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); 214 return; 215 } 216 217 RefPtr<FileSystemDirectoryEntry> entry = new FileSystemDirectoryEntry( 218 mParentEntry->GetParentObject(), directory, mParentEntry, mFileSystem); 219 220 // Update the internal values. 221 mParentEntry = entry; 222 mDirectory = directory; 223 224 Run(); 225 } 226 227 void GetEntryHelper::RejectedCallback(JSContext* aCx, 228 JS::Handle<JS::Value> aValue, 229 ErrorResult& aRv) { 230 Error(NS_ERROR_DOM_NOT_FOUND_ERR); 231 } 232 233 void GetEntryHelper::Error(nsresult aError) { 234 MOZ_ASSERT(NS_FAILED(aError)); 235 236 if (mErrorCallback) { 237 RefPtr<ErrorCallbackRunnable> runnable = new ErrorCallbackRunnable( 238 mParentEntry->GetParentObject(), mErrorCallback, aError); 239 240 FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(), 241 runnable.forget()); 242 } 243 } 244 245 NS_IMPL_ISUPPORTS0(GetEntryHelper); 246 247 /* static */ 248 void FileSystemEntryCallbackHelper::Call( 249 nsIGlobalObject* aGlobalObject, 250 const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback, 251 FileSystemEntry* aEntry) { 252 MOZ_ASSERT(aGlobalObject); 253 MOZ_ASSERT(aEntry); 254 255 if (aEntryCallback.WasPassed()) { 256 RefPtr<EntryCallbackRunnable> runnable = 257 new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry); 258 259 FileSystemUtils::DispatchRunnable(aGlobalObject, runnable.forget()); 260 } 261 } 262 263 /* static */ 264 void ErrorCallbackHelper::Call( 265 nsIGlobalObject* aGlobal, 266 const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback, 267 nsresult aError) { 268 MOZ_ASSERT(aGlobal); 269 MOZ_ASSERT(NS_FAILED(aError)); 270 271 if (aErrorCallback.WasPassed()) { 272 RefPtr<ErrorCallbackRunnable> runnable = 273 new ErrorCallbackRunnable(aGlobal, &aErrorCallback.Value(), aError); 274 275 FileSystemUtils::DispatchRunnable(aGlobal, runnable.forget()); 276 } 277 } 278 279 } // namespace mozilla::dom