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