summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/native_io
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/native_io')
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/BUILD.gn20
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/README.md59
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/global_native_io.cc91
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/global_native_io.h27
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/idls.gni14
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file.cc403
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file.h150
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file.idl21
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.cc104
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.h69
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_file_sync.idl21
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_manager.cc269
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_manager.h67
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/native_io_manager.idl28
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/window_native_io.idl13
-rw-r--r--chromium/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl13
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;
+};