blob: 95d2da4ac3e1859a0abc21ea15d269758eed1681 [file] [log] [blame]
// 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 "build/build_config.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/modules/native_io/native_io_capacity_tracker.h"
#include "third_party/blink/renderer/modules/native_io/native_io_file_utils.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/mojo/heap_mojo_remote.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 ExecutionContext;
class ScriptPromiseResolver;
class ScriptState;
class NativeIOFile final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
NativeIOFile(base::File backing_file,
int64_t backing_file_length,
HeapMojoRemote<mojom::blink::NativeIOFileHost> backend_file,
NativeIOCapacityTracker* capacity_tracker,
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 getLength(ScriptState*, ExceptionState&);
ScriptPromise setLength(ScriptState*, uint64_t new_length, ExceptionState&);
ScriptPromise read(ScriptState*,
NotShared<DOMArrayBufferView> buffer,
uint64_t file_offset,
ExceptionState&);
ScriptPromise write(ScriptState*,
NotShared<DOMArrayBufferView> buffer,
uint64_t file_offset,
ExceptionState&);
ScriptPromise flush(ScriptState*, ExceptionState&);
// GarbageCollected
void Trace(Visitor* visitor) const override;
private:
class 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,
scoped_refptr<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 getLength(), off the main thread.
static void DoGetLength(
CrossThreadPersistent<NativeIOFile> native_io_file,
CrossThreadPersistent<ScriptPromiseResolver> resolver,
scoped_refptr<NativeIOFile::FileState> file_state,
scoped_refptr<base::SequencedTaskRunner> file_task_runner);
// Performs the post file I/O part of getLength(), on the main thread.
void DidGetLength(CrossThreadPersistent<ScriptPromiseResolver> resolver,
int64_t length,
base::File::Error get_length_error);
// Performs the file I/O part of getLength(), off the main thread.
static void DoSetLength(
CrossThreadPersistent<NativeIOFile> native_io_file,
CrossThreadPersistent<ScriptPromiseResolver> resolver,
scoped_refptr<NativeIOFile::FileState> file_state,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
int64_t expected_length);
// Performs the post file I/O part of setLength(), on the main thread.
//
// `actual_length` is negative if the I/O operation was unsuccessful and the
// correct length of the file could not be determined.
void DidSetLengthIo(CrossThreadPersistent<ScriptPromiseResolver> resolver,
int64_t actual_length,
base::File::Error set_length_result);
#if defined(OS_MAC)
// Performs the post IPC part of setLength(), on the main thread.
//
// `actual_length` is negative if the I/O operation was unsuccessful and the
// correct length of the file could not be determined.
void DidSetLengthIpc(ScriptPromiseResolver* resolver,
base::File backing_file,
int64_t actual_length,
mojom::blink::NativeIOErrorPtr set_length_result);
#endif // defined(OS_MAC)
// Performs the file I/O part of read(), off the main thread.
static void DoRead(CrossThreadPersistent<NativeIOFile> native_io_file,
CrossThreadPersistent<ScriptPromiseResolver> resolver,
scoped_refptr<NativeIOFile::FileState> file_state,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
std::unique_ptr<NativeIODataBuffer> result_buffer_data,
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,
std::unique_ptr<NativeIODataBuffer> result_buffer_data,
int read_bytes,
base::File::Error read_error);
// Performs the file I/O part of write(), off the main thread.
static void DoWrite(
CrossThreadPersistent<NativeIOFile> native_io_file,
CrossThreadPersistent<ScriptPromiseResolver> resolver,
scoped_refptr<NativeIOFile::FileState> file_state,
scoped_refptr<base::SequencedTaskRunner> resolver_task_runner,
std::unique_ptr<NativeIODataBuffer> result_buffer_data,
uint64_t file_offset,
int write_size);
// Performs the post file I/O part of write(), on the main thread.
//
// `actual_file_length_on_failure` is negative if the I/O operation was
// unsuccessful and the correct length of the file could not be determined.
void DidWrite(CrossThreadPersistent<ScriptPromiseResolver> resolver,
std::unique_ptr<NativeIODataBuffer> result_buffer_data,
int written_bytes,
base::File::Error write_error,
int write_size,
int64_t actual_file_length_on_failure);
// Performs the file I/O part of flush().
static void DoFlush(
CrossThreadPersistent<NativeIOFile> native_io_file,
CrossThreadPersistent<ScriptPromiseResolver> resolver,
scoped_refptr<NativeIOFile::FileState> file_state,
scoped_refptr<base::SequencedTaskRunner> file_task_runner);
// Performs the post file-I/O part of flush(), on the main thread.
void DidFlush(CrossThreadPersistent<ScriptPromiseResolver> resolver,
base::File::Error flush_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. |io_pending_| is meant to only be accessed on the main (JS)
// thread. At a high level, it's true when the mutex guarding the actual file
// object (within |file_state_|) is held, but it's also true during thread
// hops, when the mutex is not held.
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;
// The length of the file used in capacity accounting. Most of the time, this
// is the file's length. When `io_pending_` is true, `file_length_` may be
// larger than the actual length, to reflect capacity allocated for an
// in-progress I/O operation, or capacity that will be released by an
// in-progress I/O operation.
//
// Operations that increase the file's length must first allocate capacity,
// update `file_length_` to reflect the increased length, and then perform the
// I/O. If the I/O fails, GetLength() must be used to obtain the actual file
// length. The result must first be compared against `file_length_` to account
// for the unused capacity, then used to update `file_length_`.
//
// Operations that decrease the file's length must first perform the I/O, and
// then update `file_length_` and return freed up capacity. I/O failures can
// be handled using the same logic as above.
//
// TODO(rstz): Consider moving this variable into `file_state_`
int64_t file_length_ = 0;
// Points to a NativeIOFile::FileState while the underlying file is open.
//
// When the underlying file is closed, this pointer is nulled out, and the
// FileState instance is passed to a different thread, where the closing
// happens. This avoids having any I/O performed by the base::File::Close()
// jank the JavaScript thread that owns this NativeIOFile instance.
scoped_refptr<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.
HeapMojoRemote<mojom::blink::NativeIOFileHost> backend_file_;
// Tracks the capacity for this file's execution context.
Member<NativeIOCapacityTracker> capacity_tracker_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_H_