1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/filesystem/CreateFileTask.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,321 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "CreateFileTask.h" 1.11 + 1.12 +#include <algorithm> 1.13 + 1.14 +#include "DOMError.h" 1.15 +#include "mozilla/Preferences.h" 1.16 +#include "mozilla/dom/FileSystemBase.h" 1.17 +#include "mozilla/dom/FileSystemUtils.h" 1.18 +#include "mozilla/dom/Promise.h" 1.19 +#include "nsDOMFile.h" 1.20 +#include "nsIFile.h" 1.21 +#include "nsNetUtil.h" 1.22 +#include "nsStringGlue.h" 1.23 + 1.24 +namespace mozilla { 1.25 +namespace dom { 1.26 + 1.27 +uint32_t CreateFileTask::sOutputBufferSize = 0; 1.28 + 1.29 +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, 1.30 + const nsAString& aPath, 1.31 + nsIDOMBlob* aBlobData, 1.32 + InfallibleTArray<uint8_t>& aArrayData, 1.33 + bool replace) 1.34 + : FileSystemTaskBase(aFileSystem) 1.35 + , mTargetRealPath(aPath) 1.36 + , mBlobData(aBlobData) 1.37 + , mReplace(replace) 1.38 +{ 1.39 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.40 + MOZ_ASSERT(aFileSystem); 1.41 + GetOutputBufferSize(); 1.42 + if (mBlobData) { 1.43 + nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream)); 1.44 + NS_WARN_IF(NS_FAILED(rv)); 1.45 + } 1.46 + mArrayData.SwapElements(aArrayData); 1.47 + nsCOMPtr<nsIGlobalObject> globalObject = 1.48 + do_QueryInterface(aFileSystem->GetWindow()); 1.49 + if (!globalObject) { 1.50 + return; 1.51 + } 1.52 + mPromise = new Promise(globalObject); 1.53 +} 1.54 + 1.55 +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, 1.56 + const FileSystemCreateFileParams& aParam, 1.57 + FileSystemRequestParent* aParent) 1.58 + : FileSystemTaskBase(aFileSystem, aParam, aParent) 1.59 + , mReplace(false) 1.60 +{ 1.61 + MOZ_ASSERT(FileSystemUtils::IsParentProcess(), 1.62 + "Only call from parent process!"); 1.63 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.64 + MOZ_ASSERT(aFileSystem); 1.65 + GetOutputBufferSize(); 1.66 + 1.67 + mTargetRealPath = aParam.realPath(); 1.68 + 1.69 + mReplace = aParam.replace(); 1.70 + 1.71 + auto& data = aParam.data(); 1.72 + 1.73 + if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { 1.74 + mArrayData = data; 1.75 + return; 1.76 + } 1.77 + 1.78 + BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data)); 1.79 + mBlobData = bp->GetBlob(); 1.80 + MOZ_ASSERT(mBlobData, "mBlobData should not be null."); 1.81 + nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream)); 1.82 + NS_WARN_IF(NS_FAILED(rv)); 1.83 +} 1.84 + 1.85 +CreateFileTask::~CreateFileTask() 1.86 +{ 1.87 + MOZ_ASSERT(!mPromise || NS_IsMainThread(), 1.88 + "mPromise should be released on main thread!"); 1.89 + if (mBlobStream) { 1.90 + mBlobStream->Close(); 1.91 + } 1.92 +} 1.93 + 1.94 +already_AddRefed<Promise> 1.95 +CreateFileTask::GetPromise() 1.96 +{ 1.97 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.98 + return nsRefPtr<Promise>(mPromise).forget(); 1.99 +} 1.100 + 1.101 +FileSystemParams 1.102 +CreateFileTask::GetRequestParams(const nsString& aFileSystem) const 1.103 +{ 1.104 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.105 + FileSystemCreateFileParams param; 1.106 + param.filesystem() = aFileSystem; 1.107 + param.realPath() = mTargetRealPath; 1.108 + param.replace() = mReplace; 1.109 + if (mBlobData) { 1.110 + BlobChild* actor 1.111 + = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData); 1.112 + if (actor) { 1.113 + param.data() = actor; 1.114 + } 1.115 + } else { 1.116 + param.data() = mArrayData; 1.117 + } 1.118 + return param; 1.119 +} 1.120 + 1.121 +FileSystemResponseValue 1.122 +CreateFileTask::GetSuccessRequestResult() const 1.123 +{ 1.124 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.125 + BlobParent* actor = GetBlobParent(mTargetFile); 1.126 + if (!actor) { 1.127 + return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR); 1.128 + } 1.129 + FileSystemFileResponse response; 1.130 + response.blobParent() = actor; 1.131 + return response; 1.132 +} 1.133 + 1.134 +void 1.135 +CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) 1.136 +{ 1.137 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.138 + FileSystemFileResponse r = aValue; 1.139 + BlobChild* actor = static_cast<BlobChild*>(r.blobChild()); 1.140 + nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob(); 1.141 + mTargetFile = do_QueryInterface(blob); 1.142 +} 1.143 + 1.144 +nsresult 1.145 +CreateFileTask::Work() 1.146 +{ 1.147 + class AutoClose 1.148 + { 1.149 + public: 1.150 + AutoClose(nsIOutputStream* aStream) 1.151 + : mStream(aStream) 1.152 + { 1.153 + MOZ_ASSERT(aStream); 1.154 + } 1.155 + 1.156 + ~AutoClose() 1.157 + { 1.158 + mStream->Close(); 1.159 + } 1.160 + private: 1.161 + nsCOMPtr<nsIOutputStream> mStream; 1.162 + }; 1.163 + 1.164 + MOZ_ASSERT(FileSystemUtils::IsParentProcess(), 1.165 + "Only call from parent process!"); 1.166 + MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); 1.167 + 1.168 + if (mFileSystem->IsShutdown()) { 1.169 + return NS_ERROR_FAILURE; 1.170 + } 1.171 + 1.172 + nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath); 1.173 + if (!file) { 1.174 + return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; 1.175 + } 1.176 + 1.177 + if (!mFileSystem->IsSafeFile(file)) { 1.178 + return NS_ERROR_DOM_SECURITY_ERR; 1.179 + } 1.180 + 1.181 + bool exists = false; 1.182 + nsresult rv = file->Exists(&exists); 1.183 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.184 + return rv; 1.185 + } 1.186 + 1.187 + if (exists) { 1.188 + bool isFile = false; 1.189 + rv = file->IsFile(&isFile); 1.190 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.191 + return rv; 1.192 + } 1.193 + 1.194 + if (!isFile) { 1.195 + return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; 1.196 + } 1.197 + 1.198 + if (!mReplace) { 1.199 + return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; 1.200 + } 1.201 + 1.202 + // Remove the old file before creating. 1.203 + rv = file->Remove(false); 1.204 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.205 + return rv; 1.206 + } 1.207 + } 1.208 + 1.209 + rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600); 1.210 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.211 + return rv; 1.212 + } 1.213 + 1.214 + nsCOMPtr<nsIOutputStream> outputStream; 1.215 + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); 1.216 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.217 + return rv; 1.218 + } 1.219 + 1.220 + AutoClose acOutputStream(outputStream); 1.221 + 1.222 + nsCOMPtr<nsIOutputStream> bufferedOutputStream; 1.223 + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), 1.224 + outputStream, 1.225 + sOutputBufferSize); 1.226 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.227 + return rv; 1.228 + } 1.229 + 1.230 + AutoClose acBufferedOutputStream(bufferedOutputStream); 1.231 + 1.232 + if (mBlobStream) { 1.233 + // Write the file content from blob data. 1.234 + 1.235 + uint64_t bufSize = 0; 1.236 + rv = mBlobStream->Available(&bufSize); 1.237 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.238 + return rv; 1.239 + } 1.240 + 1.241 + while (bufSize && !mFileSystem->IsShutdown()) { 1.242 + uint32_t written = 0; 1.243 + uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX; 1.244 + rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written); 1.245 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.246 + return rv; 1.247 + } 1.248 + bufSize -= written; 1.249 + } 1.250 + 1.251 + mBlobStream->Close(); 1.252 + mBlobStream = nullptr; 1.253 + 1.254 + if (mFileSystem->IsShutdown()) { 1.255 + return NS_ERROR_FAILURE; 1.256 + } 1.257 + 1.258 + mTargetFile = new nsDOMFileFile(file); 1.259 + return NS_OK; 1.260 + } 1.261 + 1.262 + // Write file content from array data. 1.263 + 1.264 + uint32_t written; 1.265 + rv = bufferedOutputStream->Write( 1.266 + reinterpret_cast<char*>(mArrayData.Elements()), 1.267 + mArrayData.Length(), 1.268 + &written); 1.269 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.270 + return rv; 1.271 + } 1.272 + 1.273 + if (mArrayData.Length() != written) { 1.274 + return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; 1.275 + } 1.276 + 1.277 + mTargetFile = new nsDOMFileFile(file); 1.278 + return NS_OK; 1.279 +} 1.280 + 1.281 +void 1.282 +CreateFileTask::HandlerCallback() 1.283 +{ 1.284 + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); 1.285 + if (mFileSystem->IsShutdown()) { 1.286 + mPromise = nullptr; 1.287 + return; 1.288 + } 1.289 + 1.290 + if (HasError()) { 1.291 + nsRefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(), 1.292 + mErrorValue); 1.293 + mPromise->MaybeReject(domError); 1.294 + mPromise = nullptr; 1.295 + return; 1.296 + } 1.297 + 1.298 + mPromise->MaybeResolve(mTargetFile); 1.299 + mPromise = nullptr; 1.300 +} 1.301 + 1.302 +void 1.303 +CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const 1.304 +{ 1.305 + if (mReplace) { 1.306 + aAccess.AssignLiteral("write"); 1.307 + return; 1.308 + } 1.309 + 1.310 + aAccess.AssignLiteral("create"); 1.311 +} 1.312 + 1.313 +void 1.314 +CreateFileTask::GetOutputBufferSize() const 1.315 +{ 1.316 + if (sOutputBufferSize || !FileSystemUtils::IsParentProcess()) { 1.317 + return; 1.318 + } 1.319 + sOutputBufferSize = 1.320 + mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4); 1.321 +} 1.322 + 1.323 +} // namespace dom 1.324 +} // namespace mozilla