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