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/FileSystemDirectoryReaderBinding.h"
14 #include "mozilla/dom/FileSystemFileEntry.h"
15 #include "mozilla/dom/FileSystemUtils.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/Unused.h"
18 #include "nsIGlobalObject.h"
19 #include "nsIFile.h"
20 #include "nsPIDOMWindow.h"
21
22 #include "../GetFileOrDirectoryTask.h"
23
24 namespace mozilla {
25 namespace 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->HandleEvent(*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->HandleEvent(*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->HandleEvent(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),
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
~GetEntryHelper()93 GetEntryHelper::~GetEntryHelper() {}
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->HandleEvent(*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->HandleEvent(*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
Call(nsIGlobalObject * aGlobalObject,const Optional<OwningNonNull<FileSystemEntryCallback>> & aEntryCallback,FileSystemEntry * aEntry)246 /* static */ void FileSystemEntryCallbackHelper::Call(
247 nsIGlobalObject* aGlobalObject,
248 const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
249 FileSystemEntry* aEntry) {
250 MOZ_ASSERT(aGlobalObject);
251 MOZ_ASSERT(aEntry);
252
253 if (aEntryCallback.WasPassed()) {
254 RefPtr<EntryCallbackRunnable> runnable =
255 new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry);
256
257 FileSystemUtils::DispatchRunnable(aGlobalObject, runnable.forget());
258 }
259 }
260
Call(nsIGlobalObject * aGlobal,const Optional<OwningNonNull<ErrorCallback>> & aErrorCallback,nsresult aError)261 /* static */ void ErrorCallbackHelper::Call(
262 nsIGlobalObject* aGlobal,
263 const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
264 nsresult aError) {
265 MOZ_ASSERT(aGlobal);
266 MOZ_ASSERT(NS_FAILED(aError));
267
268 if (aErrorCallback.WasPassed()) {
269 RefPtr<ErrorCallbackRunnable> runnable =
270 new ErrorCallbackRunnable(aGlobal, &aErrorCallback.Value(), aError);
271
272 FileSystemUtils::DispatchRunnable(aGlobal, runnable.forget());
273 }
274 }
275
276 } // namespace dom
277 } // namespace mozilla
278