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