diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/native_io')
18 files changed, 1376 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/modules/native_io/BUILD.gn b/chromium/third_party/blink/renderer/modules/native_io/BUILD.gn new file mode 100644 index 00000000000..61ed8d843f9 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/blink/renderer/modules/modules.gni") + +blink_modules_sources("native_io") { + sources = [ + "global_native_io.cc", + "global_native_io.h", + "native_io_file.cc", + "native_io_file.h", + "native_io_file_sync.cc", + "native_io_file_sync.h", + "native_io_manager.cc", + "native_io_manager.h", + ] + + deps = [ "//third_party/blink/renderer/platform" ] +} diff --git a/chromium/third_party/blink/renderer/modules/native_io/DEPS b/chromium/third_party/blink/renderer/modules/native_io/DEPS new file mode 100644 index 00000000000..8c20ba39153 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+base/files/file.h", +] diff --git a/chromium/third_party/blink/renderer/modules/native_io/OWNERS b/chromium/third_party/blink/renderer/modules/native_io/OWNERS new file mode 100644 index 00000000000..271647c4a24 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/OWNERS @@ -0,0 +1,4 @@ +file://content/browser/native_io/OWNERS + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>NativeIO diff --git a/chromium/third_party/blink/renderer/modules/native_io/README.md b/chromium/third_party/blink/renderer/modules/native_io/README.md new file mode 100644 index 00000000000..89abc128180 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/README.md @@ -0,0 +1,59 @@ +# NativeIO + +[NativeIO](https://github.com/fivedots/nativeio-explainer) is a proposal for +a low-level storage mechanism that will open a path for developers to use +their favorite database in their web applications. This API is expected to be +used heavily from WebAssembly, and the most popular ported database is +expected to be SQLite. + +Most of the Blink implementation should be unsurprising, given the WebIDL for +the feature. This README goes over the parts that may be surprising to a storage +engineer. + +## Synchronous storage APIs + +The current prototype implements both synchronous and asynchronous versions of +NativeIO. This allows us to collaborate with web developers to understand the +overhead of an asynchronous API, compared to the fastest synchronous API we +could possibly build. + +The synchronous API is limited to dedicated workers. + +## SharedArrayBuffer + +The current NativeIO design assumes that allocating and copying memory buffers +will result in significant overhead. To test this hypothesis, the prototype +includes zero-copy APIs, where the caller passes in a view into a caller-owned +I/O buffer. + +This approach introduces an interesting design challenge for the asynchronous +API, because the web application can run code that interacts with the I/O buffer +while the operation is in progress, and observe side-effects. For example, the +application can read the buffer in a tight loop, and observe how the OS is +filling it with data from disk. + +The current prototype requires that I/O buffers passed to the asynchronous API +are backed by SharedArrayBuffers, because these are intended to be modified by +multiple threads at the same time. + +This aspect of the prototype is fairly unstable. Benchmarking may reveal that +we don't need zero-copy, and WebAssembly interfacing concerns may nudge us +towards a different design. + +# Locking + +The current prototype enforces that there is at most one open handle to a +NativeIO file. In other words, opening a file only succeeds if it can get an +exclusive lock on the file, and closing the file releases the lock. + +This approach is different from other storage APIs, which wait until they can +grab a lock. The main reason for the difference is this is the first API built +after +[Web Locks](https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API), so +we can ask developers to take advantage of Web Locks instead of building a lock +manager with associated tests into each new storage API. + +The main reason for enforcing locking early in the API design is to avoid +exposing the details of OS-level file descriptor sharing to the Web Platform. +This aspect of the prototype may also change, based on research and developer +feedback. diff --git a/chromium/third_party/blink/renderer/modules/native_io/global_native_io.cc b/chromium/third_party/blink/renderer/modules/native_io/global_native_io.cc new file mode 100644 index 00000000000..59d95769add --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/global_native_io.cc @@ -0,0 +1,91 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/native_io/global_native_io.h" + +#include <utility> + +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/common/browser_interface_broker_proxy.h" +#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h" +#include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/workers/worker_global_scope.h" +#include "third_party/blink/renderer/modules/native_io/native_io_manager.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/supplementable.h" + +namespace blink { + +namespace { + +template <typename T> +class GlobalNativeIOImpl final : public GarbageCollected<GlobalNativeIOImpl<T>>, + public Supplement<T> { + USING_GARBAGE_COLLECTED_MIXIN(GlobalNativeIOImpl); + + public: + static const char kSupplementName[]; + + static GlobalNativeIOImpl& From(T& supplementable) { + GlobalNativeIOImpl* supplement = + Supplement<T>::template From<GlobalNativeIOImpl>(supplementable); + if (!supplement) { + supplement = MakeGarbageCollected<GlobalNativeIOImpl>(supplementable); + Supplement<T>::ProvideTo(supplementable, supplement); + } + return *supplement; + } + + explicit GlobalNativeIOImpl(T& supplementable) + : Supplement<T>(supplementable) {} + + NativeIOManager* GetNativeIOManager(T& scope) { + if (!native_io_manager_) { + ExecutionContext* execution_context = scope.GetExecutionContext(); + if (&execution_context->GetBrowserInterfaceBroker() == + &GetEmptyBrowserInterfaceBroker()) { + return nullptr; + } + + mojo::Remote<mojom::blink::NativeIOHost> backend; + execution_context->GetBrowserInterfaceBroker().GetInterface( + backend.BindNewPipeAndPassReceiver( + execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI))); + native_io_manager_ = MakeGarbageCollected<NativeIOManager>( + execution_context, std::move(backend)); + } + return native_io_manager_; + } + + void Trace(Visitor* visitor) override { + visitor->Trace(native_io_manager_); + Supplement<T>::Trace(visitor); + } + + private: + Member<NativeIOManager> native_io_manager_; +}; + +// static +template <typename T> +const char GlobalNativeIOImpl<T>::kSupplementName[] = "GlobalNativeIOImpl"; + +} // namespace + +// static +NativeIOManager* GlobalNativeIO::nativeIO(LocalDOMWindow& window) { + return GlobalNativeIOImpl<LocalDOMWindow>::From(window).GetNativeIOManager( + window); +} + +// static +NativeIOManager* GlobalNativeIO::nativeIO(WorkerGlobalScope& worker) { + return GlobalNativeIOImpl<WorkerGlobalScope>::From(worker).GetNativeIOManager( + worker); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/native_io/global_native_io.h b/chromium/third_party/blink/renderer/modules/native_io/global_native_io.h new file mode 100644 index 00000000000..bb9b05b9399 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/global_native_io.h @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_GLOBAL_NATIVE_IO_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_GLOBAL_NATIVE_IO_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { + +class LocalDOMWindow; +class NativeIOManager; +class WorkerGlobalScope; + +// The "nativeIO" attribute on the Window global and Worker global scope. +class GlobalNativeIO { + STATIC_ONLY(GlobalNativeIO); + + public: + static NativeIOManager* nativeIO(LocalDOMWindow&); + static NativeIOManager* nativeIO(WorkerGlobalScope&); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_GLOBAL_NATIVE_IO_H_ diff --git a/chromium/third_party/blink/renderer/modules/native_io/idls.gni b/chromium/third_party/blink/renderer/modules/native_io/idls.gni new file mode 100644 index 00000000000..694bfa928fd --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/idls.gni @@ -0,0 +1,14 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +modules_idl_files = [ + "native_io_file.idl", + "native_io_file_sync.idl", + "native_io_manager.idl", +] + +modules_dependency_idl_files = [ + "window_native_io.idl", + "worker_global_scope_native_io.idl", +] diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file.cc b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.cc new file mode 100644 index 00000000000..465f519e77f --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.cc @@ -0,0 +1,403 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/native_io/native_io_file.h" + +#include <utility> + +#include "base/files/file.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/sequenced_task_runner.h" +#include "base/task/thread_pool.h" +#include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/blink/renderer/platform/wtf/threading_primitives.h" +#include "third_party/blink/renderer/platform/wtf/wtf.h" + +namespace blink { + +namespace { + +// Extracts the read/write operation size from the buffer size. +int OperationSize(const DOMArrayBufferView& buffer) { + // On 32-bit platforms, clamp operation sizes to 2^31-1. + return base::saturated_cast<int>(buffer.byteLengthAsSizeT()); +} + +} // namespace + +struct NativeIOFile::FileState { + explicit FileState(base::File file) : file(std::move(file)) {} + + FileState(const FileState&) = delete; + FileState& operator=(const FileState&) = delete; + + ~FileState() = default; + + // Lock coordinating cross-thread access to the state. + WTF::Mutex mutex; + // The file on disk backing this NativeIOFile. + base::File file GUARDED_BY(mutex); +}; + +NativeIOFile::NativeIOFile( + base::File backing_file, + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file, + ExecutionContext* execution_context) + : ExecutionContextLifecycleObserver(execution_context), + file_state_(std::make_unique<FileState>(std::move(backing_file))), + // TODO(pwnall): Get a dedicated queue when the specification matures. + resolver_task_runner_( + execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI)), + backend_file_(std::move(backend_file)) { + backend_file_.set_disconnect_handler( + WTF::Bind(&NativeIOFile::OnBackendDisconnect, WrapWeakPersistent(this))); +} + +NativeIOFile::~NativeIOFile() { + // Needed to avoid having the base::File destructor close the file descriptor + // synchronously on the main thread. + CloseBackingFile(); +} + +ScriptPromise NativeIOFile::close(ScriptState* script_state) { + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + + if (closed_) { + // close() is idempotent. + resolver->Resolve(); + return resolver->Promise(); + } + + closed_ = true; + + DCHECK(!queued_close_resolver_) << "Close logic kicked off twice"; + queued_close_resolver_ = resolver; + + if (!io_pending_) { + // Pretend that a close() promise was queued behind an I/O operation, and + // the operation just finished. This is less logic than handling the + // non-queued case separately. + DispatchQueuedClose(); + } + + return resolver->Promise(); +} + +ScriptPromise NativeIOFile::read(ScriptState* script_state, + MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState& exception_state) { + if (!buffer.View()->IsShared()) { + exception_state.ThrowTypeError( + "The I/O buffer must be backed by a SharedArrayBuffer"); + return ScriptPromise(); + } + + if (io_pending_) { + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + "Another I/O operation is in progress on the same file"); + return ScriptPromise(); + } + if (closed_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "The file was already closed"); + return ScriptPromise(); + } + io_pending_ = true; + + int read_size = OperationSize(*buffer.View()); + char* read_buffer = + static_cast<char*>(buffer.View()->BaseAddressMaybeShared()); + DOMSharedArrayBuffer* read_buffer_keepalive = buffer.View()->BufferShared(); + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + // The first CrossThreadUnretained() is safe here because the + // NativeIOFile::FileState instance is owned by this NativeIOFile, which is + // also passed to the task via WrapCrossThreadPersistent. Therefore, the + // FileState instance is guaranteed to remain alive during the task's + // execution. + // + // The second CrossThreadUnretained() is safe here because the read buffer is + // backed by a DOMSharedArrayBuffer that is also passed to the task via + // WrapCrossThreadPersistent. Therefore, the buffer is guaranteed to remain + // alive during the task's execution. + worker_pool::PostTask( + FROM_HERE, {base::MayBlock(), base::ThreadPool()}, + CrossThreadBindOnce( + &DoRead, WrapCrossThreadPersistent(this), + WrapCrossThreadPersistent(resolver), + WrapCrossThreadPersistent(read_buffer_keepalive), + CrossThreadUnretained(file_state_.get()), resolver_task_runner_, + CrossThreadUnretained(read_buffer), file_offset, read_size)); + return resolver->Promise(); +} + +ScriptPromise NativeIOFile::write(ScriptState* script_state, + MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState& exception_state) { + if (!buffer.View()->IsShared()) { + exception_state.ThrowTypeError( + "The I/O buffer must be backed by a SharedArrayBuffer"); + return ScriptPromise(); + } + + if (io_pending_) { + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + "Another I/O operation is in progress on the same file"); + return ScriptPromise(); + } + if (closed_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "The file was already closed"); + return ScriptPromise(); + } + io_pending_ = true; + + int write_size = OperationSize(*buffer.View()); + const char* write_data = + static_cast<const char*>(buffer.View()->BaseAddressMaybeShared()); + DOMSharedArrayBuffer* read_buffer_keepalive = buffer.View()->BufferShared(); + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + // The first CrossThreadUnretained() is safe here because the + // NativeIOFile::FileState instance is owned by this NativeIOFile, which is + // also passed to the task via WrapCrossThreadPersistent. Therefore, the + // FileState instance is guaranteed to remain alive during the task's + // execution. + // + // The second CrossThreadUnretained() is safe here because the write data is + // backed by a DOMSharedArrayBuffer that is also passed to the task via + // WrapCrossThreadPersistent. Therefore, the data is guaranteed to remain + // alive during the task's execution. + worker_pool::PostTask( + FROM_HERE, {base::MayBlock(), base::ThreadPool()}, + CrossThreadBindOnce( + &DoWrite, WrapCrossThreadPersistent(this), + WrapCrossThreadPersistent(resolver), + WrapCrossThreadPersistent(read_buffer_keepalive), + CrossThreadUnretained(file_state_.get()), resolver_task_runner_, + CrossThreadUnretained(write_data), file_offset, write_size)); + return resolver->Promise(); +} + +void NativeIOFile::Trace(Visitor* visitor) { + ScriptWrappable::Trace(visitor); + ExecutionContextLifecycleObserver::Trace(visitor); + visitor->Trace(queued_close_resolver_); +} + +void NativeIOFile::ContextDestroyed() { + backend_file_.reset(); +} + +void NativeIOFile::OnBackendDisconnect() { + backend_file_.reset(); + CloseBackingFile(); +} + +void NativeIOFile::DispatchQueuedClose() { + DCHECK(!io_pending_) + << "Dispatching close() concurrently with other I/O operations is racy"; + + if (!queued_close_resolver_) + return; + + DCHECK(closed_) << "close() resolver queued without setting closed_"; + ScriptPromiseResolver* resolver = queued_close_resolver_; + queued_close_resolver_ = nullptr; + + worker_pool::PostTask( + FROM_HERE, {base::MayBlock(), base::ThreadPool()}, + CrossThreadBindOnce(&DoClose, WrapCrossThreadPersistent(this), + WrapCrossThreadPersistent(resolver), + CrossThreadUnretained(file_state_.get()), + resolver_task_runner_)); +} + +// static +void NativeIOFile::DoClose( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) { + DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread"; + + { + WTF::MutexLocker locker(file_state->mutex); + DCHECK(file_state->file.IsValid()) + << "file I/O operation queued after file closed"; + file_state->file.Close(); + } + + PostCrossThreadTask( + *resolver_task_runner, FROM_HERE, + CrossThreadBindOnce(&NativeIOFile::DidClose, std::move(native_io_file), + std::move(resolver))); +} + +void NativeIOFile::DidClose( + CrossThreadPersistent<ScriptPromiseResolver> resolver) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) { + // If the context was torn down, the backend is disconnecting or + // disconnected. No need to report that the file is closing. + return; + } + + if (!backend_file_) { + // If the backend went away, no need to tell it that the file was closed. + resolver->Resolve(); + return; + } + + backend_file_->Close( + WTF::Bind([](ScriptPromiseResolver* resolver) { resolver->Resolve(); }, + WrapPersistent(resolver.Get()))); +} + +// static +void NativeIOFile::DoRead( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + CrossThreadPersistent<DOMSharedArrayBuffer> read_buffer_keepalive, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> resolver_task_runner, + char* read_buffer, + uint64_t file_offset, + int read_size) { + DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread"; + + int read_bytes; + base::File::Error read_error; + { + WTF::MutexLocker mutex_locker(file_state->mutex); + DCHECK(file_state->file.IsValid()) + << "file I/O operation queued after file closed"; + read_bytes = file_state->file.Read(file_offset, read_buffer, read_size); + read_error = (read_bytes < 0) ? file_state->file.GetLastFileError() + : base::File::FILE_OK; + } + + PostCrossThreadTask( + *resolver_task_runner, FROM_HERE, + CrossThreadBindOnce(&NativeIOFile::DidRead, std::move(native_io_file), + std::move(resolver), read_bytes, read_error)); +} + +void NativeIOFile::DidRead( + CrossThreadPersistent<ScriptPromiseResolver> resolver, + int read_bytes, + base::File::Error read_error) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) + return; + ScriptState::Scope scope(script_state); + + DCHECK(io_pending_) << "I/O operation performed without io_pending_ set"; + io_pending_ = false; + + DispatchQueuedClose(); + + if (read_bytes < 0) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError, + "read() failed")); + return; + } + resolver->Resolve(read_bytes); +} + +// static +void NativeIOFile::DoWrite( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + CrossThreadPersistent<DOMSharedArrayBuffer> write_data_keepalive, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> resolver_task_runner, + const char* write_data, + uint64_t file_offset, + int write_size) { + DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread"; + + int written_bytes; + base::File::Error write_error; + { + WTF::MutexLocker mutex_locker(file_state->mutex); + DCHECK(file_state->file.IsValid()) + << "file I/O operation queued after file closed"; + written_bytes = file_state->file.Write(file_offset, write_data, write_size); + write_error = (written_bytes < 0) ? file_state->file.GetLastFileError() + : base::File::FILE_OK; + } + + PostCrossThreadTask( + *resolver_task_runner, FROM_HERE, + CrossThreadBindOnce(&NativeIOFile::DidRead, std::move(native_io_file), + std::move(resolver), written_bytes, write_error)); +} + +void NativeIOFile::DidWrite( + CrossThreadPersistent<ScriptPromiseResolver> resolver, + int written_bytes, + base::File::Error write_error) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) + return; + ScriptState::Scope scope(script_state); + + DCHECK(io_pending_) << "I/O operation performed without io_pending_ set"; + io_pending_ = false; + + DispatchQueuedClose(); + + if (written_bytes < 0) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kDataError, + "write() failed")); + return; + } + resolver->Resolve(written_bytes); +} + +void NativeIOFile::CloseBackingFile() { + closed_ = true; + file_state_->mutex.lock(); + base::File backing_file = std::move(file_state_->file); + file_state_->mutex.unlock(); + + if (!backing_file.IsValid()) { + // Avoid posting a cross-thread task if the file is already closed. This is + // the expected path. + return; + } + + worker_pool::PostTask( + FROM_HERE, {base::MayBlock(), base::ThreadPool()}, + CrossThreadBindOnce([](base::File file) { file.Close(); }, + std::move(backing_file))); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file.h b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.h new file mode 100644 index 00000000000..270c43f111d --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.h @@ -0,0 +1,150 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_H_ + +#include <memory> + +#include "base/files/file.h" +#include "base/memory/scoped_refptr.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace base { +class SequencedTaskRunner; +} // namespace base + +namespace blink { + +class DOMSharedArrayBuffer; +class ExecutionContext; +class ScriptPromiseResolver; +class ScriptState; + +class NativeIOFile final : public ScriptWrappable, + public ExecutionContextLifecycleObserver { + DEFINE_WRAPPERTYPEINFO(); + USING_GARBAGE_COLLECTED_MIXIN(NativeIOFile); + + public: + NativeIOFile(base::File backing_file, + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file, + ExecutionContext*); + + NativeIOFile(const NativeIOFile&) = delete; + NativeIOFile& operator=(const NativeIOFile&) = delete; + + // Needed because of the mojo::Remote<mojom::blink::NativeIOFile>. + ~NativeIOFile() override; + + ScriptPromise close(ScriptState*); + ScriptPromise read(ScriptState*, + MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState&); + ScriptPromise write(ScriptState*, + MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState&); + + // GarbageCollected + void Trace(Visitor* visitor) override; + + // ExecutionContextLifecycleObserver + void ContextDestroyed() override; + + private: + // Data accessed on the threads that do file I/O. + // + // Instances are allocated on the PartitionAlloc heap. Instances are initially + // constructed on Blink's main thread, or on a worker thread. Afterwards, + // instances are only accessed on dedicated threads that do blocking file I/O. + struct FileState; + + // Called when the mojo backend disconnects. + void OnBackendDisconnect(); + + // Runs any queued close operation. + void DispatchQueuedClose(); + + // Performs the file I/O part of close(). + static void DoClose( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> file_task_runner); + // Performs the post file-I/O part of close(), on the main thread. + void DidClose(CrossThreadPersistent<ScriptPromiseResolver> resolver); + + // Performs the file I/O part of read(). + static void DoRead( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + CrossThreadPersistent<DOMSharedArrayBuffer> read_buffer_keepalive, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> file_task_runner, + char* read_buffer, + uint64_t file_offset, + int read_size); + // Performs the post file-I/O part of read(), on the main thread. + void DidRead(CrossThreadPersistent<ScriptPromiseResolver> resolver, + int read_bytes, + base::File::Error read_error); + + // Performs the file I/O part of write(). + static void DoWrite( + CrossThreadPersistent<NativeIOFile> native_io_file, + CrossThreadPersistent<ScriptPromiseResolver> resolver, + CrossThreadPersistent<DOMSharedArrayBuffer> write_data_keepalive, + NativeIOFile::FileState* file_state, + scoped_refptr<base::SequencedTaskRunner> file_task_runner, + const char* write_data, + uint64_t file_offset, + int write_size); + // Performs the post file-I/O part of write(), on the main thread. + void DidWrite(CrossThreadPersistent<ScriptPromiseResolver> resolver, + int written_bytes, + base::File::Error write_error); + + // Kicks off closing the file from the main thread. + void CloseBackingFile(); + + // True when an I/O operation other than close is underway. + // + // Checked before kicking off any I/O operation except for close(). This + // ensures that at most one I/O operation is underway at any given time. + bool io_pending_ = false; + + // Non-null when a close() I/O is queued behind another I/O operation. + // + // Set when close() is called while another I/O operation is underway. Cleared + // when the queued close() operation is queued. + Member<ScriptPromiseResolver> queued_close_resolver_; + + // Set to true when close() is called, or when the backend goes away. + bool closed_ = false; + + // See NativeIO::FileState, declared above. + const std::unique_ptr<FileState> file_state_; + + // Schedules resolving Promises with file I/O results. + const scoped_refptr<base::SequencedTaskRunner> resolver_task_runner_; + + // Mojo pipe that holds the renderer's lock on the file. + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_H_ diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file.idl b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.idl new file mode 100644 index 00000000000..fce545fa24b --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file.idl @@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/fivedots/nativeio-explainer + +[ + Exposed=(Window, Worker), + RuntimeEnabled=NativeIO, + SecureContext +] interface NativeIOFile { + [CallWith=ScriptState] Promise<void> close(); + [ + CallWith=ScriptState, RaisesException + ] Promise<unsigned long long> read([AllowShared] ArrayBufferView buffer, + unsigned long long file_offset); + [ + CallWith=ScriptState, RaisesException + ] Promise<unsigned long long> write([AllowShared] ArrayBufferView buffer, + unsigned long long file_offset); +}; diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.cc b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.cc new file mode 100644 index 00000000000..466c4d75b43 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.cc @@ -0,0 +1,104 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/native_io/native_io_file_sync.h" + +#include <limits> + +#include "base/numerics/safe_conversions.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" +#include "third_party/blink/renderer/modules/native_io/native_io_file.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" + +namespace blink { + +// Extracts the read/write operation size from the buffer size. +int OperationSize(const DOMArrayBufferView& buffer) { + // On 32-bit platforms, clamp operation sizes to 2^31-1. + return base::saturated_cast<int>(buffer.byteLengthAsSizeT()); +} + +NativeIOFileSync::NativeIOFileSync( + base::File backing_file, + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file, + ExecutionContext* execution_context) + : ExecutionContextLifecycleObserver(execution_context), + backing_file_(std::move(backing_file)), + backend_file_(std::move(backend_file)) { + backend_file_.set_disconnect_handler(WTF::Bind( + &NativeIOFileSync::OnBackendDisconnect, WrapWeakPersistent(this))); +} + +NativeIOFileSync::~NativeIOFileSync() = default; + +void NativeIOFileSync::close() { + backing_file_.Close(); + + if (!backend_file_) { + // If the backend went away, it already considers the file closed. Nothing + // to report here. + return; + } + backend_file_->Close(); +} + +int NativeIOFileSync::read(MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState& exception_state) { + int read_size = OperationSize(*buffer.View()); + char* read_data = static_cast<char*>(buffer.View()->BaseAddressMaybeShared()); + if (!backing_file_.IsValid()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "File already closed"); + return 0; + } + int read_bytes = backing_file_.Read(file_offset, read_data, read_size); + if (read_bytes < 0) { + exception_state.ThrowDOMException(DOMExceptionCode::kDataError, + "read() failed"); + } + return read_bytes; +} + +int NativeIOFileSync::write(MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState& exception_state) { + int write_size = OperationSize(*buffer.View()); + char* write_data = + static_cast<char*>(buffer.View()->BaseAddressMaybeShared()); + if (!backing_file_.IsValid()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "File already closed"); + return 0; + } + int written_bytes = backing_file_.Write(file_offset, write_data, write_size); + if (written_bytes < 0) { + exception_state.ThrowDOMException(DOMExceptionCode::kDataError, + "write() failed"); + } + return written_bytes; +} + +void NativeIOFileSync::Trace(Visitor* visitor) { + ScriptWrappable::Trace(visitor); + ExecutionContextLifecycleObserver::Trace(visitor); +} + +void NativeIOFileSync::ContextDestroyed() { + backend_file_.reset(); +} + +void NativeIOFileSync::OnBackendDisconnect() { + backend_file_.reset(); + backing_file_.Close(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.h b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.h new file mode 100644 index 00000000000..d36815e6c93 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.h @@ -0,0 +1,69 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_SYNC_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_SYNC_H_ + +#include <stdint.h> + +#include "base/files/file.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class ExceptionState; +class ExecutionContext; + +class NativeIOFileSync final : public ScriptWrappable, + public ExecutionContextLifecycleObserver { + DEFINE_WRAPPERTYPEINFO(); + USING_GARBAGE_COLLECTED_MIXIN(NativeIOFileSync); + + public: + NativeIOFileSync(base::File backing_file, + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file, + ExecutionContext*); + + NativeIOFileSync(const NativeIOFileSync&) = delete; + NativeIOFileSync& operator=(const NativeIOFileSync&) = delete; + + // Needed because of the mojo::Remote<mojom::blink::NativeIOFile>. + ~NativeIOFileSync() override; + + void close(); + int read(MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState&); + int write(MaybeShared<DOMArrayBufferView> buffer, + uint64_t file_offset, + ExceptionState&); + + // GarbageCollected + void Trace(Visitor* visitor) override; + + // ExecutionContextLifecycleObserver + void ContextDestroyed() override; + + private: + // Called when the mojo backend disconnects. + void OnBackendDisconnect(); + + // The file on disk backing this NativeIOFile. + base::File backing_file_; + + // Mojo pipe that holds the renderer's lock on the file. + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_SYNC_H_ diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.idl b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.idl new file mode 100644 index 00000000000..001d5487dc6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.idl @@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/fivedots/nativeio-explainer + +[ + Exposed=DedicatedWorker, + RuntimeEnabled=NativeIO, + SecureContext +] interface NativeIOFileSync { + void close(); + [ + RaisesException + ] unsigned long long read([AllowShared] ArrayBufferView buffer, + unsigned long long file_offset); + [ + RaisesException + ] unsigned long long write([AllowShared] ArrayBufferView buffer, + unsigned long long file_offset); +}; diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.cc b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.cc new file mode 100644 index 00000000000..0fad4c7a7a6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.cc @@ -0,0 +1,269 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/native_io/native_io_manager.h" + +#include <algorithm> +#include <utility> + +#include "base/files/file.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h" +#include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/modules/native_io/native_io_file.h" +#include "third_party/blink/renderer/modules/native_io/native_io_file_sync.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" + +namespace blink { + +namespace { + +bool IsValidNativeIONameCharacter(int name_char) { + return (name_char >= 'a' && name_char <= 'z') || + (name_char >= '0' && name_char <= '9') || name_char == '_'; +} + +bool IsValidNativeIOName(const String& name) { + if (name.IsEmpty()) + return false; + + if (name.Is8Bit()) { + return std::all_of(name.Span8().begin(), name.Span8().end(), + &IsValidNativeIONameCharacter); + } + return std::all_of(name.Span16().begin(), name.Span16().end(), + &IsValidNativeIONameCharacter); +} + +void OnOpenResult(ScriptPromiseResolver* resolver, + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file, + base::File backing_file) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) + return; + ScriptState::Scope scope(script_state); + + if (!backing_file.IsValid()) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kUnknownError, + "open() failed")); + return; + } + + NativeIOFile* file = MakeGarbageCollected<NativeIOFile>( + std::move(backing_file), std::move(backend_file), + ExecutionContext::From(script_state)); + resolver->Resolve(file); +} + +void OnDeleteResult(ScriptPromiseResolver* resolver, bool backend_success) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) + return; + ScriptState::Scope scope(script_state); + + if (!backend_success) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kUnknownError, + "delete() failed")); + return; + } + + resolver->Resolve(); +} + +void OnGetAllResult(ScriptPromiseResolver* resolver, + bool backend_success, + const Vector<String>& file_names) { + ScriptState* script_state = resolver->GetScriptState(); + if (!script_state->ContextIsValid()) + return; + ScriptState::Scope scope(script_state); + + if (!backend_success) { + resolver->Reject(V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kUnknownError, + "getAll() failed")); + return; + } + + resolver->Resolve(file_names); +} + +} // namespace + +NativeIOManager::NativeIOManager( + ExecutionContext* execution_context, + mojo::Remote<mojom::blink::NativeIOHost> backend) + : ExecutionContextLifecycleObserver(execution_context), + // TODO(pwnall): Get a dedicated queue when the specification matures. + receiver_task_runner_( + execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI)), + backend_(std::move(backend)) { + backend_.set_disconnect_handler(WTF::Bind( + &NativeIOManager::OnBackendDisconnect, WrapWeakPersistent(this))); +} + +NativeIOManager::~NativeIOManager() = default; + +ScriptPromise NativeIOManager::open(ScriptState* script_state, + String name, + ExceptionState& exception_state) { + if (!IsValidNativeIOName(name)) { + exception_state.ThrowTypeError("Invalid file name"); + return ScriptPromise(); + } + + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return ScriptPromise(); + } + + ExecutionContext* execution_context = GetExecutionContext(); + DCHECK(execution_context); + + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file; + mojo::PendingReceiver<mojom::blink::NativeIOFileHost> backend_file_receiver = + backend_file.BindNewPipeAndPassReceiver(receiver_task_runner_); + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + backend_->OpenFile(name, std::move(backend_file_receiver), + WTF::Bind(&OnOpenResult, WrapPersistent(resolver), + std::move(backend_file))); + return resolver->Promise(); +} + +ScriptPromise NativeIOManager::Delete(ScriptState* script_state, + String name, + ExceptionState& exception_state) { + if (!IsValidNativeIOName(name)) { + exception_state.ThrowTypeError("Invalid file name"); + return ScriptPromise(); + } + + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return ScriptPromise(); + } + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + backend_->DeleteFile(name, + WTF::Bind(&OnDeleteResult, WrapPersistent(resolver))); + return resolver->Promise(); +} + +ScriptPromise NativeIOManager::getAll(ScriptState* script_state, + ExceptionState& exception_state) { + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return ScriptPromise(); + } + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + backend_->GetAllFileNames( + WTF::Bind(&OnGetAllResult, WrapPersistent(resolver))); + return resolver->Promise(); +} + +NativeIOFileSync* NativeIOManager::openSync(String name, + ExceptionState& exception_state) { + if (!IsValidNativeIOName(name)) { + exception_state.ThrowTypeError("Invalid file name"); + return nullptr; + } + + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return nullptr; + } + + ExecutionContext* execution_context = GetExecutionContext(); + DCHECK(execution_context); + + mojo::Remote<mojom::blink::NativeIOFileHost> backend_file; + mojo::PendingReceiver<mojom::blink::NativeIOFileHost> backend_file_receiver = + backend_file.BindNewPipeAndPassReceiver(receiver_task_runner_); + + base::File backing_file; + bool call_succeeded = + backend_->OpenFile(name, std::move(backend_file_receiver), &backing_file); + + if (!call_succeeded || !backing_file.IsValid()) { + exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError, + "openSync() failed"); + return nullptr; + } + + return MakeGarbageCollected<NativeIOFileSync>( + std::move(backing_file), std::move(backend_file), execution_context); +} + +void NativeIOManager::deleteSync(String name, ExceptionState& exception_state) { + if (!IsValidNativeIOName(name)) { + exception_state.ThrowTypeError("Invalid file name"); + return; + } + + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return; + } + + bool backend_success = false; + bool call_succeeded = backend_->DeleteFile(name, &backend_success); + + if (!call_succeeded || !backend_success) { + exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError, + "deleteSync() failed"); + } +} + +Vector<String> NativeIOManager::getAllSync(ExceptionState& exception_state) { + Vector<String> result; + if (!backend_) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "NativeIOHost backend went away"); + return result; + } + + bool backend_success = false; + bool call_succeeded = backend_->GetAllFileNames(&backend_success, &result); + + if (!call_succeeded || !backend_success) { + exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError, + "getAllSync() failed"); + } + return result; +} + +void NativeIOManager::Trace(Visitor* visitor) { + ScriptWrappable::Trace(visitor); + ExecutionContextLifecycleObserver::Trace(visitor); +} + +void NativeIOManager::ContextDestroyed() { + backend_.reset(); +} + +void NativeIOManager::OnBackendDisconnect() { + backend_.reset(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.h b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.h new file mode 100644 index 00000000000..c028e7ba4e0 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.h @@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_MANAGER_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_MANAGER_H_ + +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class ExecutionContext; +class ExceptionState; +class NativeIOFileSync; +class ScriptState; + +class NativeIOManager final : public ScriptWrappable, + public ExecutionContextLifecycleObserver { + DEFINE_WRAPPERTYPEINFO(); + USING_GARBAGE_COLLECTED_MIXIN(NativeIOManager); + + public: + explicit NativeIOManager(ExecutionContext*, + mojo::Remote<mojom::blink::NativeIOHost> backend); + + NativeIOManager(const NativeIOManager&) = delete; + NativeIOManager& operator=(const NativeIOManager&) = delete; + + // Needed because of the + // mojo::Remote<blink::mojom::NativeIOHost> + ~NativeIOManager() override; + + ScriptPromise open(ScriptState*, String name, ExceptionState&); + ScriptPromise Delete(ScriptState*, String name, ExceptionState&); + ScriptPromise getAll(ScriptState*, ExceptionState&); + + NativeIOFileSync* openSync(String name, ExceptionState&); + void deleteSync(String name, ExceptionState&); + Vector<String> getAllSync(ExceptionState&); + + // GarbageCollected + void Trace(Visitor* visitor) override; + + // ExecutionContextLifecycleObserver + void ContextDestroyed() override; + + private: + // Called when the mojo backend disconnects. + void OnBackendDisconnect(); + + // Task runner used by NativeIOFile mojo receivers generated by this API. + const scoped_refptr<base::SequencedTaskRunner> receiver_task_runner_; + + // Wraps an always-on Mojo pipe for sending requests to the storage backend. + mojo::Remote<mojom::blink::NativeIOHost> backend_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_MANAGER_H_ diff --git a/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.idl b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.idl new file mode 100644 index 00000000000..c2317ff73d5 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/native_io_manager.idl @@ -0,0 +1,28 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/fivedots/nativeio-explainer + +[ + Exposed=(Window,Worker), + RuntimeEnabled=NativeIO, + SecureContext +] interface NativeIOManager { + [ + CallWith=ScriptState, RaisesException + ] Promise<NativeIOFile> open(DOMString name); + [ + Exposed=DedicatedWorker, RaisesException + ] NativeIOFileSync openSync(DOMString name); + + [ + CallWith=ScriptState, ImplementedAs=Delete, RaisesException + ] Promise<void> delete(DOMString name); + [Exposed=DedicatedWorker, RaisesException] void deleteSync(DOMString name); + + [ + CallWith=ScriptState, RaisesException + ] Promise<sequence<DOMString>> getAll(); + [Exposed=DedicatedWorker, RaisesException] sequence<DOMString> getAllSync(); +}; diff --git a/chromium/third_party/blink/renderer/modules/native_io/window_native_io.idl b/chromium/third_party/blink/renderer/modules/native_io/window_native_io.idl new file mode 100644 index 00000000000..1b78cc42f38 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/window_native_io.idl @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/fivedots/nativeio-explainer + +[ + ImplementedAs=GlobalNativeIO, + RuntimeEnabled=NativeIO, + SecureContext +] partial interface Window { + readonly attribute NativeIOManager nativeIO; +}; diff --git a/chromium/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl b/chromium/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl new file mode 100644 index 00000000000..aa03a6ca7c5 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://github.com/fivedots/nativeio-explainer + +[ + ImplementedAs=GlobalNativeIO, + RuntimeEnabled=NativeIO, + SecureContext +] partial interface WorkerGlobalScope { + readonly attribute NativeIOManager nativeIO; +}; |