diff options
Diffstat (limited to 'dom/filehandle/ActorsChild.cpp')
-rw-r--r-- | dom/filehandle/ActorsChild.cpp | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/dom/filehandle/ActorsChild.cpp b/dom/filehandle/ActorsChild.cpp new file mode 100644 index 000000000..7a0b622f8 --- /dev/null +++ b/dom/filehandle/ActorsChild.cpp @@ -0,0 +1,743 @@ +/* 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 "ActorsChild.h" + +#include "BackgroundChildImpl.h" +#include "FileHandleBase.h" +#include "FileRequestBase.h" +#include "js/Date.h" +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "MutableFileBase.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsString.h" +#include "xpcpublic.h" +#include "mozilla/dom/BindingUtils.h" + +namespace mozilla { +namespace dom { + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +namespace { + +class MOZ_STACK_CLASS AutoSetCurrentFileHandle final +{ + typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; + + FileHandleBase* const mFileHandle; + FileHandleBase* mPreviousFileHandle; + FileHandleBase** mThreadLocalSlot; + +public: + explicit AutoSetCurrentFileHandle(FileHandleBase* aFileHandle) + : mFileHandle(aFileHandle) + , mPreviousFileHandle(nullptr) + , mThreadLocalSlot(nullptr) + { + if (aFileHandle) { + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + // Hang onto this location for resetting later. + mThreadLocalSlot = &threadLocal->mCurrentFileHandle; + + // Save the current value. + mPreviousFileHandle = *mThreadLocalSlot; + + // Set the new value. + *mThreadLocalSlot = aFileHandle; + } + } + + ~AutoSetCurrentFileHandle() + { + MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle); + MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle); + + if (mThreadLocalSlot) { + // Reset old value. + *mThreadLocalSlot = mPreviousFileHandle; + } + } + + FileHandleBase* + FileHandle() const + { + return mFileHandle; + } +}; + +class MOZ_STACK_CLASS ResultHelper final + : public FileRequestBase::ResultCallback +{ + FileRequestBase* mFileRequest; + AutoSetCurrentFileHandle mAutoFileHandle; + + union + { + File* mFile; + const nsCString* mString; + const FileRequestMetadata* mMetadata; + const JS::Handle<JS::Value>* mJSValHandle; + } mResult; + + enum + { + ResultTypeFile, + ResultTypeString, + ResultTypeMetadata, + ResultTypeJSValHandle, + } mResultType; + +public: + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + File* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeFile) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mFile = aResult; + } + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const nsCString* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeString) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mString = aResult; + } + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const FileRequestMetadata* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeMetadata) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mMetadata = aResult; + } + + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const JS::Handle<JS::Value>* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeJSValHandle) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mJSValHandle = aResult; + } + + FileRequestBase* + FileRequest() const + { + return mFileRequest; + } + + FileHandleBase* + FileHandle() const + { + return mAutoFileHandle.FileHandle(); + } + + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) override + { + MOZ_ASSERT(aCx); + MOZ_ASSERT(mFileRequest); + + switch (mResultType) { + case ResultTypeFile: + return GetResult(aCx, mResult.mFile, aResult); + + case ResultTypeString: + return GetResult(aCx, mResult.mString, aResult); + + case ResultTypeMetadata: + return GetResult(aCx, mResult.mMetadata, aResult); + + case ResultTypeJSValHandle: + aResult.set(*mResult.mJSValHandle); + return NS_OK; + + default: + MOZ_CRASH("Unknown result type!"); + } + + MOZ_CRASH("Should never get here!"); + } + +private: + nsresult + GetResult(JSContext* aCx, + File* aFile, + JS::MutableHandle<JS::Value> aResult) + { + bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult); + if (NS_WARN_IF(!ok)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsCString* aString, + JS::MutableHandle<JS::Value> aResult) + { + const nsCString& data = *aString; + + nsresult rv; + + if (!mFileRequest->HasEncoding()) { + JS::Rooted<JSObject*> arrayBuffer(aCx); + rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + aResult.setObject(*arrayBuffer); + return NS_OK; + } + + nsAutoCString encoding; + // The BOM sniffing is baked into the "decode" part of the Encoding + // Standard, which the File API references. + if (!nsContentUtils::CheckForBOM( + reinterpret_cast<const unsigned char *>(data.get()), + data.Length(), + encoding)) { + // BOM sniffing failed. Try the API argument. + if (!EncodingUtils::FindEncodingForLabel(mFileRequest->GetEncoding(), + encoding)) { + // API argument failed. Since we are dealing with a file system file, + // we don't have a meaningful type attribute for the blob available, + // so proceeding to the next step, which is defaulting to UTF-8. + encoding.AssignLiteral("UTF-8"); + } + } + + nsString tmpString; + rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, tmpString); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const FileRequestMetadata* aMetadata, + JS::MutableHandle<JS::Value> aResult) + { + JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); + if (NS_WARN_IF(!obj)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + const FileRequestSize& size = aMetadata->size(); + if (size.type() != FileRequestSize::Tvoid_t) { + MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); + + JS::Rooted<JS::Value> number(aCx, JS_NumberValue(size.get_uint64_t())); + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } + + const FileRequestLastModified& lastModified = aMetadata->lastModified(); + if (lastModified.type() != FileRequestLastModified::Tvoid_t) { + MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); + + JS::Rooted<JSObject*> date(aCx, + JS::NewDateObject(aCx, JS::TimeClip(lastModified.get_int64_t()))); + if (NS_WARN_IF(!date)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } + + aResult.setObject(*obj); + return NS_OK; + } +}; + +already_AddRefed<File> +ConvertActorToFile(FileHandleBase* aFileHandle, + const FileRequestGetFileResponse& aResponse) +{ + auto* actor = static_cast<BlobChild*>(aResponse.fileChild()); + + MutableFileBase* mutableFile = aFileHandle->MutableFile(); + MOZ_ASSERT(mutableFile); + + const FileRequestMetadata& metadata = aResponse.metadata(); + + const FileRequestSize& size = metadata.size(); + MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); + + const FileRequestLastModified& lastModified = metadata.lastModified(); + MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); + + actor->SetMysteryBlobInfo(mutableFile->Name(), + mutableFile->Type(), + size.get_uint64_t(), + lastModified.get_int64_t()); + + RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + RefPtr<File> file = mutableFile->CreateFileFor(blobImpl, aFileHandle); + return file.forget(); +} + +void +HandleSuccess(ResultHelper* aResultHelper) +{ + MOZ_ASSERT(aResultHelper); + + RefPtr<FileRequestBase> fileRequest = aResultHelper->FileRequest(); + MOZ_ASSERT(fileRequest); + fileRequest->AssertIsOnOwningThread(); + + RefPtr<FileHandleBase> fileHandle = aResultHelper->FileHandle(); + MOZ_ASSERT(fileHandle); + + if (fileHandle->IsAborted()) { + fileRequest->SetError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); + return; + } + + MOZ_ASSERT(fileHandle->IsOpen()); + + fileRequest->SetResultCallback(aResultHelper); + + MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); +} + +void +HandleError(FileRequestBase* aFileRequest, + nsresult aErrorCode, + FileHandleBase* aFileHandle) +{ + MOZ_ASSERT(aFileRequest); + aFileRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE); + MOZ_ASSERT(aFileHandle); + + RefPtr<FileRequestBase> fileRequest = aFileRequest; + RefPtr<FileHandleBase> fileHandle = aFileHandle; + + AutoSetCurrentFileHandle ascfh(aFileHandle); + + fileRequest->SetError(aErrorCode); + + MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); +} + +} // anonymous namespace + +/******************************************************************************* + * BackgroundMutableFileChildBase + ******************************************************************************/ + +BackgroundMutableFileChildBase::BackgroundMutableFileChildBase( + DEBUGONLY(PRThread* aOwningThread)) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mMutableFile(nullptr) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundMutableFileChildBase); +} + +BackgroundMutableFileChildBase::~BackgroundMutableFileChildBase() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BackgroundMutableFileChildBase); +} + +void +BackgroundMutableFileChildBase::EnsureDOMObject() +{ + AssertIsOnOwningThread(); + + if (mTemporaryStrongMutableFile) { + return; + } + + mTemporaryStrongMutableFile = CreateMutableFile(); + + MOZ_ASSERT(mTemporaryStrongMutableFile); + mTemporaryStrongMutableFile->AssertIsOnOwningThread(); + + mMutableFile = mTemporaryStrongMutableFile; +} + +void +BackgroundMutableFileChildBase::ReleaseDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTemporaryStrongMutableFile); + mTemporaryStrongMutableFile->AssertIsOnOwningThread(); + MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile); + + mTemporaryStrongMutableFile = nullptr; +} + +void +BackgroundMutableFileChildBase::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongMutableFile); + + if (mMutableFile) { + mMutableFile->ClearBackgroundActor(); + mMutableFile = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe()); + } +} + +void +BackgroundMutableFileChildBase::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mMutableFile) { + mMutableFile->ClearBackgroundActor(); + DEBUGONLY(mMutableFile = nullptr;) + } +} + +PBackgroundFileHandleChild* +BackgroundMutableFileChildBase::AllocPBackgroundFileHandleChild( + const FileMode& aMode) +{ + MOZ_CRASH("PBackgroundFileHandleChild actors should be manually " + "constructed!"); +} + +bool +BackgroundMutableFileChildBase::DeallocPBackgroundFileHandleChild( + PBackgroundFileHandleChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundFileHandleChild*>(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFileHandleChild + ******************************************************************************/ + +BackgroundFileHandleChild::BackgroundFileHandleChild( + DEBUGONLY(PRThread* aOwningThread,) + FileHandleBase* aFileHandle) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mTemporaryStrongFileHandle(aFileHandle) + , mFileHandle(aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + aFileHandle->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundFileHandleChild); +} + +BackgroundFileHandleChild::~BackgroundFileHandleChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BackgroundFileHandleChild); +} + +void +BackgroundFileHandleChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mFileHandle) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundFileHandleChild::SendDeleteMe()); + } +} + +void +BackgroundFileHandleChild::NoteActorDestroyed() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTemporaryStrongFileHandle, mFileHandle); + + if (mFileHandle) { + mFileHandle->ClearBackgroundActor(); + + // Normally this would be DEBUG-only but NoteActorDestroyed is also called + // from SendDeleteMeInternal. In that case we're going to receive an actual + // ActorDestroy call later and we don't want to touch a dead object. + mTemporaryStrongFileHandle = nullptr; + mFileHandle = nullptr; + } +} + +void +BackgroundFileHandleChild::NoteComplete() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mFileHandle, mTemporaryStrongFileHandle); + + mTemporaryStrongFileHandle = nullptr; +} + +void +BackgroundFileHandleChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteActorDestroyed(); +} + +bool +BackgroundFileHandleChild::RecvComplete(const bool& aAborted) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + + mFileHandle->HandleCompleteOrAbort(aAborted); + + NoteComplete(); + return true; +} + +PBackgroundFileRequestChild* +BackgroundFileHandleChild::AllocPBackgroundFileRequestChild( + const FileRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundFileRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundFileHandleChild::DeallocPBackgroundFileRequestChild( + PBackgroundFileRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundFileRequestChild*>(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFileRequestChild + ******************************************************************************/ + +BackgroundFileRequestChild::BackgroundFileRequestChild( + DEBUGONLY(PRThread* aOwningThread,) + FileRequestBase* aFileRequest) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mFileRequest(aFileRequest) + , mFileHandle(aFileRequest->FileHandle()) + , mActorDestroyed(false) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileRequest); + aFileRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + mFileHandle->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundFileRequestChild); +} + +BackgroundFileRequestChild::~BackgroundFileRequestChild() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mFileHandle); + + MOZ_COUNT_DTOR(BackgroundFileRequestChild); +} + +void +BackgroundFileRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_FILEHANDLE); + MOZ_ASSERT(mFileHandle); + + HandleError(mFileRequest, aResponse, mFileHandle); +} + +void +BackgroundFileRequestChild::HandleResponse( + const FileRequestGetFileResponse& aResponse) +{ + AssertIsOnOwningThread(); + + RefPtr<File> file = ConvertActorToFile(mFileHandle, aResponse); + + ResultHelper helper(mFileRequest, mFileHandle, file); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + if (mFileHandle) { + mFileHandle->AssertIsOnOwningThread(); + + mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ + aWhy == Deletion); + + DEBUGONLY(mFileHandle = nullptr;) + } +} + +bool +BackgroundFileRequestChild::Recv__delete__(const FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileRequest); + MOZ_ASSERT(mFileHandle); + + if (mFileHandle->IsAborted()) { + // Always handle an "error" with ABORT_ERR if the file handle was aborted, + // even if the request succeeded or failed with another error. + HandleResponse(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); + } else { + switch (aResponse.type()) { + case FileRequestResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case FileRequestResponse::TFileRequestGetFileResponse: + HandleResponse(aResponse.get_FileRequestGetFileResponse()); + break; + + case FileRequestResponse::TFileRequestReadResponse: + HandleResponse(aResponse.get_FileRequestReadResponse().data()); + break; + + case FileRequestResponse::TFileRequestWriteResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestTruncateResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestFlushResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestGetMetadataResponse: + HandleResponse(aResponse.get_FileRequestGetMetadataResponse() + .metadata()); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + } + + mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true); + + // Null this out so that we don't try to call OnRequestFinished() again in + // ActorDestroy. + mFileHandle = nullptr; + + return true; +} + +bool +BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress, + const uint64_t& aProgressMax) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileRequest); + + mFileRequest->OnProgress(aProgress, aProgressMax); + + return true; +} + +} // namespace dom +} // namespace mozilla |