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/. */
7 #include "mozilla/dom/FileSystemTaskBase.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"
26 nsresult
FileSystemErrorFromNsError(const nsresult
& aErrorValue
) {
27 uint16_t module
= NS_ERROR_GET_MODULE(aErrorValue
);
28 if (module
== NS_ERROR_MODULE_DOM_FILESYSTEM
||
29 module
== NS_ERROR_MODULE_DOM_FILE
|| module
== NS_ERROR_MODULE_DOM
) {
33 switch (aErrorValue
) {
37 case NS_ERROR_FILE_INVALID_PATH
:
38 case NS_ERROR_FILE_UNRECOGNIZED_PATH
:
39 return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR
;
41 case NS_ERROR_FILE_DESTINATION_NOT_DIR
:
42 return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR
;
44 case NS_ERROR_FILE_ACCESS_DENIED
:
45 case NS_ERROR_FILE_DIR_NOT_EMPTY
:
46 return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR
;
48 case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
:
49 case NS_ERROR_NOT_AVAILABLE
:
50 return NS_ERROR_DOM_FILE_NOT_FOUND_ERR
;
52 case NS_ERROR_FILE_ALREADY_EXISTS
:
53 return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR
;
55 case NS_ERROR_FILE_NOT_DIRECTORY
:
56 return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR
;
58 case NS_ERROR_UNEXPECTED
:
60 return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR
;
64 nsresult
DispatchToIOThread(nsIRunnable
* aRunnable
) {
65 MOZ_ASSERT(aRunnable
);
67 nsCOMPtr
<nsIEventTarget
> target
=
68 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
71 return target
->Dispatch(aRunnable
, NS_DISPATCH_NORMAL
);
74 // This runnable is used when an error value is set before doing any real
75 // operation on the I/O thread. In this case we skip all and we directly
76 // communicate the error.
77 class ErrorRunnable final
: public CancelableRunnable
{
79 explicit ErrorRunnable(FileSystemTaskChildBase
* aTask
)
80 : CancelableRunnable("ErrorRunnable"), mTask(aTask
) {
86 MOZ_ASSERT(NS_IsMainThread());
87 MOZ_ASSERT(mTask
->HasError());
89 mTask
->HandlerCallback();
94 RefPtr
<FileSystemTaskChildBase
> mTask
;
97 } // anonymous namespace
100 * FileSystemTaskBase class
103 FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject
* aGlobalObject
,
104 FileSystemBase
* aFileSystem
)
105 : mErrorValue(NS_OK
),
106 mFileSystem(aFileSystem
),
107 mGlobalObject(aGlobalObject
) {
108 MOZ_ASSERT(aFileSystem
, "aFileSystem should not be null.");
109 aFileSystem
->AssertIsOnOwningThread();
110 MOZ_ASSERT(aGlobalObject
);
113 FileSystemTaskChildBase::~FileSystemTaskChildBase() {
114 mFileSystem
->AssertIsOnOwningThread();
117 FileSystemBase
* FileSystemTaskChildBase::GetFileSystem() const {
118 mFileSystem
->AssertIsOnOwningThread();
119 return mFileSystem
.get();
122 void FileSystemTaskChildBase::Start() {
123 mFileSystem
->AssertIsOnOwningThread();
125 mozilla::ipc::PBackgroundChild
* actor
=
126 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
127 if (NS_WARN_IF(!actor
)) {
128 // We are probably shutting down.
132 nsAutoString serialization
;
133 mFileSystem
->SerializeDOMPath(serialization
);
136 FileSystemParams params
= GetRequestParams(serialization
, rv
);
137 if (NS_WARN_IF(rv
.Failed())) {
138 rv
.SuppressException();
142 if (NS_IsMainThread()) {
143 nsIEventTarget
* target
= mGlobalObject
->EventTargetFor(TaskCategory::Other
);
146 actor
->SetEventTargetForActor(this, target
);
149 actor
->SendPFileSystemRequestConstructor(this, params
);
152 void FileSystemTaskChildBase::SetRequestResult(
153 const FileSystemResponseValue
& aValue
) {
154 mFileSystem
->AssertIsOnOwningThread();
156 if (aValue
.type() == FileSystemResponseValue::TFileSystemErrorResponse
) {
157 FileSystemErrorResponse r
= aValue
;
158 mErrorValue
= r
.error();
161 SetSuccessRequestResult(aValue
, rv
);
162 mErrorValue
= rv
.StealNSResult();
166 mozilla::ipc::IPCResult
FileSystemTaskChildBase::Recv__delete__(
167 const FileSystemResponseValue
& aValue
) {
168 mFileSystem
->AssertIsOnOwningThread();
170 SetRequestResult(aValue
);
175 void FileSystemTaskChildBase::SetError(const nsresult
& aErrorValue
) {
176 mErrorValue
= FileSystemErrorFromNsError(aErrorValue
);
180 * FileSystemTaskParentBase class
183 FileSystemTaskParentBase::FileSystemTaskParentBase(
184 FileSystemBase
* aFileSystem
, const FileSystemParams
& aParam
,
185 FileSystemRequestParent
* aParent
)
186 : Runnable("dom::FileSystemTaskParentBase"),
188 mFileSystem(aFileSystem
),
189 mRequestParent(aParent
),
190 mBackgroundEventTarget(GetCurrentThreadEventTarget()) {
191 MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
192 MOZ_ASSERT(aFileSystem
, "aFileSystem should not be null.");
194 MOZ_ASSERT(mBackgroundEventTarget
);
195 AssertIsOnBackgroundThread();
198 FileSystemTaskParentBase::~FileSystemTaskParentBase() {
199 // This task can be released on different threads because we dispatch it (as
200 // runnable) to main-thread, I/O and then back to the PBackground thread.
201 NS_ProxyRelease("FileSystemTaskParentBase::mFileSystem",
202 mBackgroundEventTarget
, mFileSystem
.forget());
203 NS_ProxyRelease("FileSystemTaskParentBase::mRequestParent",
204 mBackgroundEventTarget
, mRequestParent
.forget());
207 void FileSystemTaskParentBase::Start() {
208 AssertIsOnBackgroundThread();
209 mFileSystem
->AssertIsOnOwningThread();
211 DebugOnly
<nsresult
> rv
= DispatchToIOThread(this);
212 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "DispatchToIOThread failed");
215 void FileSystemTaskParentBase::HandleResult() {
216 AssertIsOnBackgroundThread();
217 mFileSystem
->AssertIsOnOwningThread();
219 if (mFileSystem
->IsShutdown()) {
223 MOZ_ASSERT(mRequestParent
);
224 Unused
<< mRequestParent
->Send__delete__(mRequestParent
, GetRequestResult());
227 FileSystemResponseValue
FileSystemTaskParentBase::GetRequestResult() const {
228 AssertIsOnBackgroundThread();
229 mFileSystem
->AssertIsOnOwningThread();
232 return FileSystemErrorResponse(mErrorValue
);
236 FileSystemResponseValue value
= GetSuccessRequestResult(rv
);
237 if (NS_WARN_IF(rv
.Failed())) {
238 return FileSystemErrorResponse(rv
.StealNSResult());
244 void FileSystemTaskParentBase::SetError(const nsresult
& aErrorValue
) {
245 mErrorValue
= FileSystemErrorFromNsError(aErrorValue
);
249 FileSystemTaskParentBase::Run() {
250 // This method can run in 2 different threads. Here why:
251 // 1. We are are on the I/O thread and we call IOWork().
252 // 2. After step 1, it returns back to the PBackground thread.
254 // Run I/O thread tasks
255 if (!IsOnBackgroundThread()) {
256 nsresult rv
= IOWork();
257 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 // Let's go back to PBackground thread to finish the work.
262 rv
= mBackgroundEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
263 if (NS_WARN_IF(NS_FAILED(rv
))) {
270 // If we are here, it's because the I/O work has been done and we have to
271 // handle the result back via IPC.
272 AssertIsOnBackgroundThread();
278 } // namespace mozilla