/src/mozilla-central/dom/filesystem/FileSystemTaskBase.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/dom/FileSystemTaskBase.h" |
8 | | |
9 | | #include "nsNetCID.h" |
10 | | #include "mozilla/dom/File.h" |
11 | | #include "mozilla/dom/FileSystemBase.h" |
12 | | #include "mozilla/dom/FileSystemRequestParent.h" |
13 | | #include "mozilla/dom/FileSystemUtils.h" |
14 | | #include "mozilla/dom/Promise.h" |
15 | | #include "mozilla/ipc/BackgroundChild.h" |
16 | | #include "mozilla/ipc/BackgroundParent.h" |
17 | | #include "mozilla/ipc/PBackgroundChild.h" |
18 | | #include "mozilla/Unused.h" |
19 | | #include "nsProxyRelease.h" |
20 | | |
21 | | namespace mozilla { |
22 | | namespace dom { |
23 | | |
24 | | namespace { |
25 | | |
26 | | nsresult |
27 | | FileSystemErrorFromNsError(const nsresult& aErrorValue) |
28 | 0 | { |
29 | 0 | uint16_t module = NS_ERROR_GET_MODULE(aErrorValue); |
30 | 0 | if (module == NS_ERROR_MODULE_DOM_FILESYSTEM || |
31 | 0 | module == NS_ERROR_MODULE_DOM_FILE || |
32 | 0 | module == NS_ERROR_MODULE_DOM) { |
33 | 0 | return aErrorValue; |
34 | 0 | } |
35 | 0 | |
36 | 0 | switch (aErrorValue) { |
37 | 0 | case NS_OK: |
38 | 0 | return NS_OK; |
39 | 0 |
|
40 | 0 | case NS_ERROR_FILE_INVALID_PATH: |
41 | 0 | case NS_ERROR_FILE_UNRECOGNIZED_PATH: |
42 | 0 | return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; |
43 | 0 |
|
44 | 0 | case NS_ERROR_FILE_DESTINATION_NOT_DIR: |
45 | 0 | return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR; |
46 | 0 |
|
47 | 0 | case NS_ERROR_FILE_ACCESS_DENIED: |
48 | 0 | case NS_ERROR_FILE_DIR_NOT_EMPTY: |
49 | 0 | return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; |
50 | 0 |
|
51 | 0 | case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST: |
52 | 0 | case NS_ERROR_NOT_AVAILABLE: |
53 | 0 | return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; |
54 | 0 |
|
55 | 0 | case NS_ERROR_FILE_ALREADY_EXISTS: |
56 | 0 | return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; |
57 | 0 |
|
58 | 0 | case NS_ERROR_FILE_NOT_DIRECTORY: |
59 | 0 | return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; |
60 | 0 |
|
61 | 0 | case NS_ERROR_UNEXPECTED: |
62 | 0 | default: |
63 | 0 | return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | nsresult |
68 | | DispatchToIOThread(nsIRunnable* aRunnable) |
69 | 0 | { |
70 | 0 | MOZ_ASSERT(aRunnable); |
71 | 0 |
|
72 | 0 | nsCOMPtr<nsIEventTarget> target |
73 | 0 | = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); |
74 | 0 | MOZ_ASSERT(target); |
75 | 0 |
|
76 | 0 | return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL); |
77 | 0 | } |
78 | | |
79 | | // This runnable is used when an error value is set before doing any real |
80 | | // operation on the I/O thread. In this case we skip all and we directly |
81 | | // communicate the error. |
82 | | class ErrorRunnable final : public CancelableRunnable |
83 | | { |
84 | | public: |
85 | | explicit ErrorRunnable(FileSystemTaskChildBase* aTask) |
86 | | : CancelableRunnable("ErrorRunnable") |
87 | | , mTask(aTask) |
88 | 0 | { |
89 | 0 | MOZ_ASSERT(aTask); |
90 | 0 | } |
91 | | |
92 | | NS_IMETHOD |
93 | | Run() override |
94 | 0 | { |
95 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
96 | 0 | MOZ_ASSERT(mTask->HasError()); |
97 | 0 |
|
98 | 0 | mTask->HandlerCallback(); |
99 | 0 | return NS_OK; |
100 | 0 | } |
101 | | |
102 | | private: |
103 | | RefPtr<FileSystemTaskChildBase> mTask; |
104 | | }; |
105 | | |
106 | | } // anonymous namespace |
107 | | |
108 | | /** |
109 | | * FileSystemTaskBase class |
110 | | */ |
111 | | |
112 | | FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject, |
113 | | FileSystemBase* aFileSystem) |
114 | | : mErrorValue(NS_OK) |
115 | | , mFileSystem(aFileSystem) |
116 | | , mGlobalObject(aGlobalObject) |
117 | 0 | { |
118 | 0 | MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); |
119 | 0 | aFileSystem->AssertIsOnOwningThread(); |
120 | 0 | MOZ_ASSERT(aGlobalObject); |
121 | 0 | } |
122 | | |
123 | | FileSystemTaskChildBase::~FileSystemTaskChildBase() |
124 | 0 | { |
125 | 0 | mFileSystem->AssertIsOnOwningThread(); |
126 | 0 | } |
127 | | |
128 | | FileSystemBase* |
129 | | FileSystemTaskChildBase::GetFileSystem() const |
130 | 0 | { |
131 | 0 | mFileSystem->AssertIsOnOwningThread(); |
132 | 0 | return mFileSystem.get(); |
133 | 0 | } |
134 | | |
135 | | void |
136 | | FileSystemTaskChildBase::Start() |
137 | 0 | { |
138 | 0 | mFileSystem->AssertIsOnOwningThread(); |
139 | 0 |
|
140 | 0 | mozilla::ipc::PBackgroundChild* actor = |
141 | 0 | mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); |
142 | 0 | if (NS_WARN_IF(!actor)) { |
143 | 0 | // We are probably shutting down. |
144 | 0 | return; |
145 | 0 | } |
146 | 0 | |
147 | 0 | nsAutoString serialization; |
148 | 0 | mFileSystem->SerializeDOMPath(serialization); |
149 | 0 |
|
150 | 0 | ErrorResult rv; |
151 | 0 | FileSystemParams params = GetRequestParams(serialization, rv); |
152 | 0 | if (NS_WARN_IF(rv.Failed())) { |
153 | 0 | rv.SuppressException(); |
154 | 0 | return; |
155 | 0 | } |
156 | 0 | |
157 | 0 | // Retain a reference so the task object isn't deleted without IPDL's |
158 | 0 | // knowledge. The reference will be released by |
159 | 0 | // mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild. |
160 | 0 | NS_ADDREF_THIS(); |
161 | 0 |
|
162 | 0 | if (NS_IsMainThread()) { |
163 | 0 | nsIEventTarget* target = mGlobalObject->EventTargetFor(TaskCategory::Other); |
164 | 0 | MOZ_ASSERT(target); |
165 | 0 |
|
166 | 0 | actor->SetEventTargetForActor(this, target); |
167 | 0 | } |
168 | 0 |
|
169 | 0 | actor->SendPFileSystemRequestConstructor(this, params); |
170 | 0 | } |
171 | | |
172 | | void |
173 | | FileSystemTaskChildBase::SetRequestResult(const FileSystemResponseValue& aValue) |
174 | 0 | { |
175 | 0 | mFileSystem->AssertIsOnOwningThread(); |
176 | 0 |
|
177 | 0 | if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) { |
178 | 0 | FileSystemErrorResponse r = aValue; |
179 | 0 | mErrorValue = r.error(); |
180 | 0 | } else { |
181 | 0 | ErrorResult rv; |
182 | 0 | SetSuccessRequestResult(aValue, rv); |
183 | 0 | mErrorValue = rv.StealNSResult(); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | mozilla::ipc::IPCResult |
188 | | FileSystemTaskChildBase::Recv__delete__(const FileSystemResponseValue& aValue) |
189 | 0 | { |
190 | 0 | mFileSystem->AssertIsOnOwningThread(); |
191 | 0 |
|
192 | 0 | SetRequestResult(aValue); |
193 | 0 | HandlerCallback(); |
194 | 0 | return IPC_OK(); |
195 | 0 | } |
196 | | |
197 | | void |
198 | | FileSystemTaskChildBase::SetError(const nsresult& aErrorValue) |
199 | 0 | { |
200 | 0 | mErrorValue = FileSystemErrorFromNsError(aErrorValue); |
201 | 0 | } |
202 | | |
203 | | /** |
204 | | * FileSystemTaskParentBase class |
205 | | */ |
206 | | |
207 | | FileSystemTaskParentBase::FileSystemTaskParentBase( |
208 | | FileSystemBase* aFileSystem, |
209 | | const FileSystemParams& aParam, |
210 | | FileSystemRequestParent* aParent) |
211 | | : Runnable("dom::FileSystemTaskParentBase") |
212 | | , mErrorValue(NS_OK) |
213 | | , mFileSystem(aFileSystem) |
214 | | , mRequestParent(aParent) |
215 | | , mBackgroundEventTarget(GetCurrentThreadEventTarget()) |
216 | 0 | { |
217 | 0 | MOZ_ASSERT(XRE_IsParentProcess(), |
218 | 0 | "Only call from parent process!"); |
219 | 0 | MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); |
220 | 0 | MOZ_ASSERT(aParent); |
221 | 0 | MOZ_ASSERT(mBackgroundEventTarget); |
222 | 0 | AssertIsOnBackgroundThread(); |
223 | 0 | } |
224 | | |
225 | | FileSystemTaskParentBase::~FileSystemTaskParentBase() |
226 | 0 | { |
227 | 0 | // This task can be released on different threads because we dispatch it (as |
228 | 0 | // runnable) to main-thread, I/O and then back to the PBackground thread. |
229 | 0 | NS_ProxyRelease( |
230 | 0 | "FileSystemTaskParentBase::mFileSystem", |
231 | 0 | mBackgroundEventTarget, mFileSystem.forget()); |
232 | 0 | NS_ProxyRelease( |
233 | 0 | "FileSystemTaskParentBase::mRequestParent", |
234 | 0 | mBackgroundEventTarget, mRequestParent.forget()); |
235 | 0 | } |
236 | | |
237 | | void |
238 | | FileSystemTaskParentBase::Start() |
239 | 0 | { |
240 | 0 | AssertIsOnBackgroundThread(); |
241 | 0 | mFileSystem->AssertIsOnOwningThread(); |
242 | 0 |
|
243 | 0 | DebugOnly<nsresult> rv = DispatchToIOThread(this); |
244 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchToIOThread failed"); |
245 | 0 | } |
246 | | |
247 | | void |
248 | | FileSystemTaskParentBase::HandleResult() |
249 | 0 | { |
250 | 0 | AssertIsOnBackgroundThread(); |
251 | 0 | mFileSystem->AssertIsOnOwningThread(); |
252 | 0 |
|
253 | 0 | if (mFileSystem->IsShutdown()) { |
254 | 0 | return; |
255 | 0 | } |
256 | 0 | |
257 | 0 | MOZ_ASSERT(mRequestParent); |
258 | 0 | Unused << mRequestParent->Send__delete__(mRequestParent, GetRequestResult()); |
259 | 0 | } |
260 | | |
261 | | FileSystemResponseValue |
262 | | FileSystemTaskParentBase::GetRequestResult() const |
263 | 0 | { |
264 | 0 | AssertIsOnBackgroundThread(); |
265 | 0 | mFileSystem->AssertIsOnOwningThread(); |
266 | 0 |
|
267 | 0 | if (HasError()) { |
268 | 0 | return FileSystemErrorResponse(mErrorValue); |
269 | 0 | } |
270 | 0 | |
271 | 0 | ErrorResult rv; |
272 | 0 | FileSystemResponseValue value = GetSuccessRequestResult(rv); |
273 | 0 | if (NS_WARN_IF(rv.Failed())) { |
274 | 0 | return FileSystemErrorResponse(rv.StealNSResult()); |
275 | 0 | } |
276 | 0 | |
277 | 0 | return value; |
278 | 0 | } |
279 | | |
280 | | void |
281 | | FileSystemTaskParentBase::SetError(const nsresult& aErrorValue) |
282 | 0 | { |
283 | 0 | mErrorValue = FileSystemErrorFromNsError(aErrorValue); |
284 | 0 | } |
285 | | |
286 | | NS_IMETHODIMP |
287 | | FileSystemTaskParentBase::Run() |
288 | 0 | { |
289 | 0 | // This method can run in 2 different threads. Here why: |
290 | 0 | // 1. We are are on the I/O thread and we call IOWork(). |
291 | 0 | // 2. After step 1, it returns back to the PBackground thread. |
292 | 0 |
|
293 | 0 | // Run I/O thread tasks |
294 | 0 | if (!IsOnBackgroundThread()) { |
295 | 0 | nsresult rv = IOWork(); |
296 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
297 | 0 | SetError(rv); |
298 | 0 | } |
299 | 0 |
|
300 | 0 | // Let's go back to PBackground thread to finish the work. |
301 | 0 | rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
302 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
303 | 0 | return rv; |
304 | 0 | } |
305 | 0 | |
306 | 0 | return NS_OK; |
307 | 0 | } |
308 | 0 | |
309 | 0 | // If we are here, it's because the I/O work has been done and we have to |
310 | 0 | // handle the result back via IPC. |
311 | 0 | AssertIsOnBackgroundThread(); |
312 | 0 | HandleResult(); |
313 | 0 | return NS_OK; |
314 | 0 | } |
315 | | |
316 | | } // namespace dom |
317 | | } // namespace mozilla |