This patch also moves a similar call for SyncAccessHandle. Differential Revision: https://phabricator.services.mozilla.com/D161274
399 lines
12 KiB
C++
399 lines
12 KiB
C++
/* -*- 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 "FileSystemSyncAccessHandle.h"
|
|
|
|
#include "fs/FileSystemRequestHandler.h"
|
|
#include "mozilla/CheckedInt.h"
|
|
#include "mozilla/ErrorResult.h"
|
|
#include "mozilla/FixedBufferOutputStream.h"
|
|
#include "mozilla/MozPromise.h"
|
|
#include "mozilla/dom/FileSystemAccessHandleChild.h"
|
|
#include "mozilla/dom/FileSystemHandleBinding.h"
|
|
#include "mozilla/dom/FileSystemManager.h"
|
|
#include "mozilla/dom/FileSystemSyncAccessHandleBinding.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/UnionTypes.h"
|
|
#include "mozilla/dom/WorkerCommon.h"
|
|
#include "mozilla/dom/WorkerPrivate.h"
|
|
#include "mozilla/dom/quota/QuotaCommon.h"
|
|
#include "mozilla/dom/quota/ResultExtensions.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsStringStream.h"
|
|
|
|
namespace mozilla {
|
|
|
|
LazyLogModule gOPFSLog("OPFS");
|
|
|
|
}
|
|
#define LOG(args) MOZ_LOG(mozilla::gOPFSLog, mozilla::LogLevel::Verbose, args)
|
|
|
|
#define LOG_DEBUG(args) \
|
|
MOZ_LOG(mozilla::gOPFSLog, mozilla::LogLevel::Debug, args)
|
|
|
|
namespace mozilla::dom {
|
|
|
|
namespace {
|
|
|
|
const uint32_t kStreamCopyBlockSize = 1024 * 1024;
|
|
|
|
nsresult AsyncCopy(nsIInputStream* aSource, nsIOutputStream* aSink,
|
|
const nsAsyncCopyMode aMode, const bool aCloseSource,
|
|
const bool aCloseSink,
|
|
std::function<void(uint32_t)>&& aProgressCallback,
|
|
std::function<void(nsresult)>&& aCompleteCallback) {
|
|
QM_TRY_INSPECT(const auto& ioTarget,
|
|
MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>,
|
|
MOZ_SELECT_OVERLOAD(do_GetService),
|
|
NS_STREAMTRANSPORTSERVICE_CONTRACTID));
|
|
|
|
struct CallbackClosure {
|
|
CallbackClosure(std::function<void(uint32_t)>&& aProgressCallback,
|
|
std::function<void(nsresult)>&& aCompleteCallback) {
|
|
mProgressCallbackWrapper = MakeUnique<std::function<void(uint32_t)>>(
|
|
[progressCallback = std::move(aProgressCallback)](uint32_t count) {
|
|
progressCallback(count);
|
|
});
|
|
|
|
mCompleteCallbackWrapper = MakeUnique<std::function<void(nsresult)>>(
|
|
[completeCallback = std::move(aCompleteCallback)](nsresult rv) {
|
|
completeCallback(rv);
|
|
});
|
|
}
|
|
|
|
UniquePtr<std::function<void(uint32_t)>> mProgressCallbackWrapper;
|
|
UniquePtr<std::function<void(nsresult)>> mCompleteCallbackWrapper;
|
|
};
|
|
|
|
auto* callbackClosure = new CallbackClosure(std::move(aProgressCallback),
|
|
std::move(aCompleteCallback));
|
|
|
|
QM_TRY(
|
|
MOZ_TO_RESULT(NS_AsyncCopy(
|
|
aSource, aSink, ioTarget, aMode, kStreamCopyBlockSize,
|
|
[](void* aClosure, nsresult aRv) {
|
|
auto* callbackClosure = static_cast<CallbackClosure*>(aClosure);
|
|
(*callbackClosure->mCompleteCallbackWrapper)(aRv);
|
|
delete callbackClosure;
|
|
},
|
|
callbackClosure, aCloseSource, aCloseSink, /* aCopierCtx */ nullptr,
|
|
[](void* aClosure, uint32_t aCount) {
|
|
auto* callbackClosure = static_cast<CallbackClosure*>(aClosure);
|
|
(*callbackClosure->mProgressCallbackWrapper)(aCount);
|
|
})),
|
|
[callbackClosure](nsresult rv) {
|
|
delete callbackClosure;
|
|
return rv;
|
|
});
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
FileSystemSyncAccessHandle::FileSystemSyncAccessHandle(
|
|
nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
|
|
RefPtr<FileSystemAccessHandleChild> aActor,
|
|
nsCOMPtr<nsIRandomAccessStream> aStream,
|
|
const fs::FileSystemEntryMetadata& aMetadata)
|
|
: mGlobal(aGlobal),
|
|
mManager(aManager),
|
|
mActor(std::move(aActor)),
|
|
mStream(std::move(aStream)),
|
|
mMetadata(aMetadata),
|
|
mClosed(false) {
|
|
LOG(("Created SyncAccessHandle %p for stream %p", this, mStream.get()));
|
|
}
|
|
|
|
FileSystemSyncAccessHandle::~FileSystemSyncAccessHandle() {
|
|
MOZ_ASSERT(!mActor);
|
|
MOZ_ASSERT(mClosed);
|
|
}
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemSyncAccessHandle)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemSyncAccessHandle)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FileSystemSyncAccessHandle,
|
|
LastRelease())
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemSyncAccessHandle)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemSyncAccessHandle)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
|
|
// Don't unlink mManager!
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
tmp->Close();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemSyncAccessHandle)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
void FileSystemSyncAccessHandle::LastRelease() {
|
|
Close();
|
|
|
|
if (mActor) {
|
|
PFileSystemAccessHandleChild::Send__delete__(mActor);
|
|
MOZ_ASSERT(!mActor);
|
|
}
|
|
}
|
|
|
|
void FileSystemSyncAccessHandle::ClearActor() {
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mActor = nullptr;
|
|
}
|
|
|
|
void FileSystemSyncAccessHandle::Close() {
|
|
if (mClosed) {
|
|
return;
|
|
}
|
|
|
|
LOG(("%p: Closing", mStream.get()));
|
|
|
|
mClosed = true;
|
|
|
|
mStream->OutputStream()->Close();
|
|
mStream = nullptr;
|
|
|
|
if (mActor) {
|
|
mActor->SendClose();
|
|
}
|
|
}
|
|
|
|
// WebIDL Boilerplate
|
|
|
|
nsIGlobalObject* FileSystemSyncAccessHandle::GetParentObject() const {
|
|
return mGlobal;
|
|
}
|
|
|
|
JSObject* FileSystemSyncAccessHandle::WrapObject(
|
|
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
|
return FileSystemSyncAccessHandle_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
// WebIDL Interface
|
|
|
|
uint64_t FileSystemSyncAccessHandle::Read(
|
|
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer,
|
|
const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) {
|
|
return ReadOrWrite(aBuffer, aOptions, /* aRead */ true, aRv);
|
|
}
|
|
|
|
uint64_t FileSystemSyncAccessHandle::Write(
|
|
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer,
|
|
const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) {
|
|
return ReadOrWrite(aBuffer, aOptions, /* aRead */ false, aRv);
|
|
}
|
|
|
|
already_AddRefed<Promise> FileSystemSyncAccessHandle::Truncate(
|
|
uint64_t aSize, ErrorResult& aError) {
|
|
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mClosed) {
|
|
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return promise.forget();
|
|
}
|
|
|
|
auto rejectAndReturn = [&promise](const nsresult rv) {
|
|
promise->MaybeReject(rv);
|
|
return promise.forget();
|
|
};
|
|
|
|
LOG_DEBUG(("%p: Truncate to %" PRIu64, mStream.get(), aSize));
|
|
|
|
QM_TRY(MOZ_TO_RESULT(mStream->Seek(nsISeekableStream::NS_SEEK_SET, aSize)),
|
|
rejectAndReturn);
|
|
|
|
// XXX FileQuotaStream::SetEOF needs to be updated to support extension of
|
|
// files. See bug 1797913.
|
|
|
|
QM_TRY(MOZ_TO_RESULT(mStream->SetEOF()), rejectAndReturn);
|
|
|
|
promise->MaybeResolveWithUndefined();
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> FileSystemSyncAccessHandle::GetSize(
|
|
ErrorResult& aError) {
|
|
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mClosed) {
|
|
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return promise.forget();
|
|
}
|
|
|
|
auto rejectAndReturn = [&promise](const nsresult rv) {
|
|
promise->MaybeReject(rv);
|
|
return promise.forget();
|
|
};
|
|
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mStream);
|
|
MOZ_ASSERT(fileMetadata);
|
|
|
|
QM_TRY_INSPECT(const auto& size,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(fileMetadata, GetSize),
|
|
rejectAndReturn);
|
|
|
|
LOG_DEBUG(("%p: GetSize %" PRIu64, mStream.get(), size));
|
|
|
|
promise->MaybeResolve(size);
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> FileSystemSyncAccessHandle::Flush(
|
|
ErrorResult& aError) {
|
|
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mClosed) {
|
|
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return promise.forget();
|
|
}
|
|
|
|
auto rejectAndReturn = [&promise](const nsresult rv) {
|
|
promise->MaybeReject(rv);
|
|
return promise.forget();
|
|
};
|
|
|
|
LOG_DEBUG(("%p: Flush", mStream.get()));
|
|
|
|
QM_TRY(MOZ_TO_RESULT(mStream->OutputStream()->Flush()), rejectAndReturn);
|
|
|
|
promise->MaybeResolveWithUndefined();
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> FileSystemSyncAccessHandle::Close(
|
|
ErrorResult& aError) {
|
|
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
Close();
|
|
|
|
promise->MaybeResolveWithUndefined();
|
|
return promise.forget();
|
|
}
|
|
|
|
uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
|
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer,
|
|
const FileSystemReadWriteOptions& aOptions, const bool aRead,
|
|
ErrorResult& aRv) {
|
|
if (mClosed) {
|
|
aRv.ThrowInvalidStateError("SyncAccessHandle is closed");
|
|
return 0;
|
|
}
|
|
|
|
auto throwAndReturn = [&aRv](const nsresult rv) {
|
|
aRv.Throw(rv);
|
|
return 0;
|
|
};
|
|
|
|
// Handle seek before read ('at')
|
|
const auto at = [&aOptions]() -> uint64_t {
|
|
if (aOptions.mAt.WasPassed()) {
|
|
return aOptions.mAt.Value();
|
|
}
|
|
// Spec says default for at is 0 (2.6)
|
|
return 0;
|
|
}();
|
|
|
|
const auto offset = CheckedInt<int64_t>(at);
|
|
QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn);
|
|
|
|
LOG(("%p: Seeking to %" PRIu64, mStream.get(), offset.value()));
|
|
|
|
QM_TRY(MOZ_TO_RESULT(
|
|
mStream->Seek(nsISeekableStream::NS_SEEK_SET, offset.value())),
|
|
throwAndReturn);
|
|
|
|
const auto dataSpan = [&aBuffer]() {
|
|
if (aBuffer.IsArrayBuffer()) {
|
|
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
|
buffer.ComputeState();
|
|
return Span{buffer.Data(), buffer.Length()};
|
|
}
|
|
MOZ_ASSERT(aBuffer.IsArrayBufferView());
|
|
const ArrayBufferView& buffer = aBuffer.GetAsArrayBufferView();
|
|
buffer.ComputeState();
|
|
return Span{buffer.Data(), buffer.Length()};
|
|
}();
|
|
|
|
nsCOMPtr<nsIInputStream> inputStream;
|
|
nsCOMPtr<nsIOutputStream> outputStream;
|
|
|
|
if (aRead) {
|
|
LOG(("%p: Reading %zu bytes", mStream.get(), dataSpan.Length()));
|
|
|
|
inputStream = mStream->InputStream();
|
|
|
|
outputStream = FixedBufferOutputStream::Create(AsWritableChars(dataSpan));
|
|
} else {
|
|
LOG(("%p: Writing %zu bytes", mStream.get(), dataSpan.Length()));
|
|
|
|
QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream(getter_AddRefs(inputStream),
|
|
AsChars(dataSpan),
|
|
NS_ASSIGNMENT_DEPEND)),
|
|
throwAndReturn);
|
|
|
|
outputStream = mStream->OutputStream();
|
|
}
|
|
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
AutoSyncLoopHolder syncLoop(workerPrivate, Canceling);
|
|
|
|
nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
|
|
syncLoop.GetSerialEventTarget();
|
|
|
|
QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aRv](nsresult) {
|
|
aRv.ThrowInvalidStateError("Worker is shutting down");
|
|
return 0;
|
|
});
|
|
|
|
uint64_t totalCount = 0;
|
|
|
|
QM_TRY(MOZ_TO_RESULT(AsyncCopy(
|
|
inputStream, outputStream,
|
|
aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS
|
|
: NS_ASYNCCOPY_VIA_READSEGMENTS,
|
|
/* aCloseSource */ !aRead, /* aCloseSink */ aRead,
|
|
[&totalCount](uint32_t count) { totalCount += count; },
|
|
[syncLoopTarget](nsresult rv) {
|
|
InvokeAsync(syncLoopTarget, __func__, [syncLoopTarget]() {
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
workerPrivate->AssertIsOnWorkerThread();
|
|
|
|
workerPrivate->StopSyncLoop(syncLoopTarget, true);
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
});
|
|
})),
|
|
throwAndReturn);
|
|
|
|
MOZ_ALWAYS_TRUE(syncLoop.Run());
|
|
|
|
return totalCount;
|
|
}
|
|
|
|
} // namespace mozilla::dom
|