diff -r 000000000000 -r 6474c204b198 dom/filesystem/CreateFileTask.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/filesystem/CreateFileTask.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,321 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CreateFileTask.h" + +#include + +#include "DOMError.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/FileSystemBase.h" +#include "mozilla/dom/FileSystemUtils.h" +#include "mozilla/dom/Promise.h" +#include "nsDOMFile.h" +#include "nsIFile.h" +#include "nsNetUtil.h" +#include "nsStringGlue.h" + +namespace mozilla { +namespace dom { + +uint32_t CreateFileTask::sOutputBufferSize = 0; + +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, + const nsAString& aPath, + nsIDOMBlob* aBlobData, + InfallibleTArray& aArrayData, + bool replace) + : FileSystemTaskBase(aFileSystem) + , mTargetRealPath(aPath) + , mBlobData(aBlobData) + , mReplace(replace) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + GetOutputBufferSize(); + if (mBlobData) { + nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream)); + NS_WARN_IF(NS_FAILED(rv)); + } + mArrayData.SwapElements(aArrayData); + nsCOMPtr globalObject = + do_QueryInterface(aFileSystem->GetWindow()); + if (!globalObject) { + return; + } + mPromise = new Promise(globalObject); +} + +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent) + : FileSystemTaskBase(aFileSystem, aParam, aParent) + , mReplace(false) +{ + MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + GetOutputBufferSize(); + + mTargetRealPath = aParam.realPath(); + + mReplace = aParam.replace(); + + auto& data = aParam.data(); + + if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { + mArrayData = data; + return; + } + + BlobParent* bp = static_cast(static_cast(data)); + mBlobData = bp->GetBlob(); + MOZ_ASSERT(mBlobData, "mBlobData should not be null."); + nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream)); + NS_WARN_IF(NS_FAILED(rv)); +} + +CreateFileTask::~CreateFileTask() +{ + MOZ_ASSERT(!mPromise || NS_IsMainThread(), + "mPromise should be released on main thread!"); + if (mBlobStream) { + mBlobStream->Close(); + } +} + +already_AddRefed +CreateFileTask::GetPromise() +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + return nsRefPtr(mPromise).forget(); +} + +FileSystemParams +CreateFileTask::GetRequestParams(const nsString& aFileSystem) const +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + FileSystemCreateFileParams param; + param.filesystem() = aFileSystem; + param.realPath() = mTargetRealPath; + param.replace() = mReplace; + if (mBlobData) { + BlobChild* actor + = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData); + if (actor) { + param.data() = actor; + } + } else { + param.data() = mArrayData; + } + return param; +} + +FileSystemResponseValue +CreateFileTask::GetSuccessRequestResult() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + BlobParent* actor = GetBlobParent(mTargetFile); + if (!actor) { + return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR); + } + FileSystemFileResponse response; + response.blobParent() = actor; + return response; +} + +void +CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + FileSystemFileResponse r = aValue; + BlobChild* actor = static_cast(r.blobChild()); + nsCOMPtr blob = actor->GetBlob(); + mTargetFile = do_QueryInterface(blob); +} + +nsresult +CreateFileTask::Work() +{ + class AutoClose + { + public: + AutoClose(nsIOutputStream* aStream) + : mStream(aStream) + { + MOZ_ASSERT(aStream); + } + + ~AutoClose() + { + mStream->Close(); + } + private: + nsCOMPtr mStream; + }; + + MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + "Only call from parent process!"); + MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); + + if (mFileSystem->IsShutdown()) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); + if (!file) { + return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; + } + + if (!mFileSystem->IsSafeFile(file)) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + bool exists = false; + nsresult rv = file->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + bool isFile = false; + rv = file->IsFile(&isFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!isFile) { + return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; + } + + if (!mReplace) { + return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; + } + + // Remove the old file before creating. + rv = file->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr outputStream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AutoClose acOutputStream(outputStream); + + nsCOMPtr bufferedOutputStream; + rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), + outputStream, + sOutputBufferSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AutoClose acBufferedOutputStream(bufferedOutputStream); + + if (mBlobStream) { + // Write the file content from blob data. + + uint64_t bufSize = 0; + rv = mBlobStream->Available(&bufSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + while (bufSize && !mFileSystem->IsShutdown()) { + uint32_t written = 0; + uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX; + rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + bufSize -= written; + } + + mBlobStream->Close(); + mBlobStream = nullptr; + + if (mFileSystem->IsShutdown()) { + return NS_ERROR_FAILURE; + } + + mTargetFile = new nsDOMFileFile(file); + return NS_OK; + } + + // Write file content from array data. + + uint32_t written; + rv = bufferedOutputStream->Write( + reinterpret_cast(mArrayData.Elements()), + mArrayData.Length(), + &written); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mArrayData.Length() != written) { + return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; + } + + mTargetFile = new nsDOMFileFile(file); + return NS_OK; +} + +void +CreateFileTask::HandlerCallback() +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + if (mFileSystem->IsShutdown()) { + mPromise = nullptr; + return; + } + + if (HasError()) { + nsRefPtr domError = new DOMError(mFileSystem->GetWindow(), + mErrorValue); + mPromise->MaybeReject(domError); + mPromise = nullptr; + return; + } + + mPromise->MaybeResolve(mTargetFile); + mPromise = nullptr; +} + +void +CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const +{ + if (mReplace) { + aAccess.AssignLiteral("write"); + return; + } + + aAccess.AssignLiteral("create"); +} + +void +CreateFileTask::GetOutputBufferSize() const +{ + if (sOutputBufferSize || !FileSystemUtils::IsParentProcess()) { + return; + } + sOutputBufferSize = + mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4); +} + +} // namespace dom +} // namespace mozilla