/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=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 "mozilla/dom/FileSystemTaskBase.h" #include "nsNetCID.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PContent.h" #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/unused.h" #include "nsProxyRelease.h" namespace mozilla { namespace dom { namespace { class FileSystemReleaseRunnable : public nsRunnable { public: explicit FileSystemReleaseRunnable(RefPtr& aDoomed) : mDoomed(nullptr) { aDoomed.swap(mDoomed); } NS_IMETHOD Run() { mDoomed->Release(); return NS_OK; } private: FileSystemBase* MOZ_OWNING_REF mDoomed; }; } // anonymous namespace FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem) : mErrorValue(NS_OK) , mFileSystem(aFileSystem) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); } FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem, const FileSystemParams& aParam, FileSystemRequestParent* aParent) : mErrorValue(NS_OK) , mFileSystem(aFileSystem) , mRequestParent(aParent) { MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); } FileSystemTaskBase::~FileSystemTaskBase() { if (!NS_IsMainThread()) { RefPtr runnable = new FileSystemReleaseRunnable(mFileSystem); MOZ_ASSERT(!mFileSystem); NS_DispatchToMainThread(runnable); } } FileSystemBase* FileSystemTaskBase::GetFileSystem() const { return mFileSystem.get(); } void FileSystemTaskBase::Start() { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (HasError()) { NS_DispatchToMainThread(this); return; } if (XRE_IsParentProcess()) { // Run in parent process. // Start worker thread. nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); NS_ASSERTION(target, "Must have stream transport service."); target->Dispatch(this, NS_DISPATCH_NORMAL); return; } // Run in child process. if (mFileSystem->IsShutdown()) { return; } // Retain a reference so the task object isn't deleted without IPDL's // knowledge. The reference will be released by // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild. NS_ADDREF_THIS(); ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this, GetRequestParams(mFileSystem->ToString())); } NS_IMETHODIMP FileSystemTaskBase::Run() { if (!NS_IsMainThread()) { // Run worker thread tasks nsresult rv = Work(); if (NS_FAILED(rv)) { SetError(rv); } // Dispatch itself to main thread NS_DispatchToMainThread(this); return NS_OK; } // Run main thread tasks HandleResult(); return NS_OK; } void FileSystemTaskBase::HandleResult() { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (mFileSystem->IsShutdown()) { return; } if (mRequestParent && mRequestParent->IsRunning()) { Unused << mRequestParent->Send__delete__(mRequestParent, GetRequestResult()); } else { HandlerCallback(); } } FileSystemResponseValue FileSystemTaskBase::GetRequestResult() const { MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (HasError()) { return FileSystemErrorResponse(mErrorValue); } else { return GetSuccessRequestResult(); } } void FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue) { MOZ_ASSERT(!XRE_IsParentProcess(), "Only call from child process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) { FileSystemErrorResponse r = aValue; mErrorValue = r.error(); } else { SetSuccessRequestResult(aValue); } } bool FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue) { SetRequestResult(aValue); HandlerCallback(); return true; } BlobParent* FileSystemTaskBase::GetBlobParent(BlobImpl* aFile) const { MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFile); // Load the lazy dom file data from the parent before sending to the child. nsString mimeType; aFile->GetType(mimeType); // We call GetSize and GetLastModified to prepopulate the value in the // BlobImpl. { ErrorResult rv; aFile->GetSize(rv); rv.SuppressException(); } { ErrorResult rv; aFile->GetLastModified(rv); rv.SuppressException(); } ContentParent* cp = static_cast(mRequestParent->Manager()); return cp->GetOrCreateActorForBlobImpl(aFile); } void FileSystemTaskBase::SetError(const nsresult& aErrorValue) { uint16_t module = NS_ERROR_GET_MODULE(aErrorValue); if (module == NS_ERROR_MODULE_DOM_FILESYSTEM || module == NS_ERROR_MODULE_DOM_FILE || module == NS_ERROR_MODULE_DOM) { mErrorValue = aErrorValue; return; } switch (aErrorValue) { case NS_OK: mErrorValue = NS_OK; return; case NS_ERROR_FILE_INVALID_PATH: case NS_ERROR_FILE_UNRECOGNIZED_PATH: mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; return; case NS_ERROR_FILE_DESTINATION_NOT_DIR: mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR; return; case NS_ERROR_FILE_ACCESS_DENIED: case NS_ERROR_FILE_DIR_NOT_EMPTY: mErrorValue = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; return; case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST: case NS_ERROR_NOT_AVAILABLE: mErrorValue = NS_ERROR_DOM_FILE_NOT_FOUND_ERR; return; case NS_ERROR_FILE_ALREADY_EXISTS: mErrorValue = NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; return; case NS_ERROR_FILE_NOT_DIRECTORY: mErrorValue = NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; return; case NS_ERROR_UNEXPECTED: default: mErrorValue = NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; return; } } } // namespace dom } // namespace mozilla