| // Copyright 2017 The Chromium Authors |
| // 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/core/mojo/mojo_handle.h" |
| |
| #include "base/numerics/safe_math.h" |
| #include "mojo/public/c/system/message_pipe.h" |
| #include "mojo/public/cpp/bindings/message.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_create_shared_buffer_result.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_discard_data_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_duplicate_buffer_handle_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_map_buffer_result.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_read_data_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_read_data_result.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_read_message_flags.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_read_message_result.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_write_data_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mojo_write_data_result.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context.h" |
| #include "third_party/blink/renderer/core/mojo/mojo_watcher.h" |
| #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" |
| #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| |
| // Mojo messages typically do not contain many handles. In fact most |
| // messages do not contain any handle. An inline capacity of 4 should avoid |
| // heap allocation in vast majority of cases. |
| static const size_t kHandleVectorInlineCapacity = 4; |
| |
| namespace blink { |
| |
| mojo::ScopedHandle MojoHandle::TakeHandle() { |
| return std::move(handle_); |
| } |
| |
| MojoHandle::MojoHandle(mojo::ScopedHandle handle) |
| : handle_(std::move(handle)) {} |
| |
| void MojoHandle::close() { |
| handle_.reset(); |
| } |
| |
| MojoWatcher* MojoHandle::watch(ScriptState* script_state, |
| const MojoHandleSignals* signals, |
| V8MojoWatchCallback* callback) { |
| return MojoWatcher::Create(handle_.get(), signals, callback, |
| ExecutionContext::From(script_state)); |
| } |
| |
| MojoResult MojoHandle::writeMessage( |
| const V8BufferSource* buffer, |
| const HeapVector<Member<MojoHandle>>& handles) { |
| Vector<mojo::ScopedHandle, kHandleVectorInlineCapacity> scoped_handles; |
| scoped_handles.reserve(handles.size()); |
| bool has_invalid_handles = false; |
| for (auto& handle : handles) { |
| if (!handle->handle_.is_valid()) |
| has_invalid_handles = true; |
| else |
| scoped_handles.emplace_back(std::move(handle->handle_)); |
| } |
| if (has_invalid_handles) |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| |
| const void* bytes = nullptr; |
| size_t num_bytes = 0; |
| switch (buffer->GetContentType()) { |
| case V8BufferSource::ContentType::kArrayBuffer: { |
| DOMArrayBuffer* array = buffer->GetAsArrayBuffer(); |
| bytes = array->Data(); |
| num_bytes = array->ByteLength(); |
| break; |
| } |
| case V8BufferSource::ContentType::kArrayBufferView: { |
| const auto& view = buffer->GetAsArrayBufferView(); |
| bytes = view->BaseAddress(); |
| num_bytes = view->byteLength(); |
| break; |
| } |
| } |
| |
| auto message = mojo::Message( |
| base::make_span(static_cast<const uint8_t*>(bytes), num_bytes), |
| base::make_span(scoped_handles)); |
| DCHECK(!message.IsNull()); |
| return mojo::WriteMessageNew(mojo::MessagePipeHandle(handle_.get().value()), |
| message.TakeMojoMessage(), |
| MOJO_WRITE_MESSAGE_FLAG_NONE); |
| } |
| |
| MojoReadMessageResult* MojoHandle::readMessage( |
| const MojoReadMessageFlags* flags_dict) { |
| MojoReadMessageResult* result_dict = MojoReadMessageResult::Create(); |
| |
| mojo::ScopedMessageHandle message; |
| MojoResult result = |
| mojo::ReadMessageNew(mojo::MessagePipeHandle(handle_.get().value()), |
| &message, MOJO_READ_MESSAGE_FLAG_NONE); |
| if (result != MOJO_RESULT_OK) { |
| result_dict->setResult(result); |
| return result_dict; |
| } |
| |
| result = MojoSerializeMessage(message->value(), nullptr); |
| if (result != MOJO_RESULT_OK && result != MOJO_RESULT_FAILED_PRECONDITION) { |
| result_dict->setResult(MOJO_RESULT_ABORTED); |
| return result_dict; |
| } |
| |
| uint32_t num_bytes = 0, num_handles = 0; |
| void* bytes; |
| Vector<::MojoHandle, kHandleVectorInlineCapacity> raw_handles; |
| result = MojoGetMessageData(message->value(), nullptr, &bytes, &num_bytes, |
| nullptr, &num_handles); |
| if (result == MOJO_RESULT_RESOURCE_EXHAUSTED) { |
| raw_handles.resize(num_handles); |
| result = MojoGetMessageData(message->value(), nullptr, &bytes, &num_bytes, |
| raw_handles.data(), &num_handles); |
| } |
| |
| if (result != MOJO_RESULT_OK) { |
| result_dict->setResult(MOJO_RESULT_ABORTED); |
| return result_dict; |
| } |
| |
| DOMArrayBuffer* buffer = |
| DOMArrayBuffer::CreateUninitializedOrNull(num_bytes, 1); |
| if (num_bytes) { |
| CHECK(buffer); |
| memcpy(buffer->Data(), bytes, num_bytes); |
| } |
| result_dict->setBuffer(buffer); |
| |
| HeapVector<Member<MojoHandle>> handles(num_handles); |
| for (uint32_t i = 0; i < num_handles; ++i) { |
| handles[i] = MakeGarbageCollected<MojoHandle>( |
| mojo::MakeScopedHandle(mojo::Handle(raw_handles[i]))); |
| } |
| result_dict->setHandles(handles); |
| result_dict->setResult(result); |
| |
| return result_dict; |
| } |
| |
| MojoWriteDataResult* MojoHandle::writeData( |
| const V8BufferSource* buffer, |
| const MojoWriteDataOptions* options_dict) { |
| MojoWriteDataResult* result_dict = MojoWriteDataResult::Create(); |
| |
| MojoWriteDataFlags flags = MOJO_WRITE_DATA_FLAG_NONE; |
| if (options_dict->allOrNone()) |
| flags |= MOJO_WRITE_DATA_FLAG_ALL_OR_NONE; |
| |
| const void* elements = nullptr; |
| base::CheckedNumeric<uint32_t> checked_num_bytes; |
| switch (buffer->GetContentType()) { |
| case V8BufferSource::ContentType::kArrayBuffer: { |
| DOMArrayBuffer* array = buffer->GetAsArrayBuffer(); |
| elements = array->Data(); |
| checked_num_bytes = array->ByteLength(); |
| break; |
| } |
| case V8BufferSource::ContentType::kArrayBufferView: { |
| const auto& view = buffer->GetAsArrayBufferView(); |
| elements = view->BaseAddress(); |
| checked_num_bytes = view->byteLength(); |
| break; |
| } |
| } |
| |
| ::MojoWriteDataOptions options; |
| options.struct_size = sizeof(options); |
| options.flags = flags; |
| uint32_t num_bytes = 0; |
| MojoResult result = |
| checked_num_bytes.AssignIfValid(&num_bytes) |
| ? MojoWriteData(handle_.get().value(), elements, &num_bytes, &options) |
| : MOJO_RESULT_INVALID_ARGUMENT; |
| result_dict->setResult(result); |
| result_dict->setNumBytes(result == MOJO_RESULT_OK ? num_bytes : 0); |
| return result_dict; |
| } |
| |
| MojoReadDataResult* MojoHandle::queryData() const { |
| MojoReadDataResult* result_dict = MojoReadDataResult::Create(); |
| uint32_t num_bytes = 0; |
| ::MojoReadDataOptions options; |
| options.struct_size = sizeof(options); |
| options.flags = MOJO_READ_DATA_FLAG_QUERY; |
| MojoResult result = |
| MojoReadData(handle_.get().value(), &options, nullptr, &num_bytes); |
| result_dict->setResult(result); |
| result_dict->setNumBytes(num_bytes); |
| return result_dict; |
| } |
| |
| MojoReadDataResult* MojoHandle::discardData( |
| unsigned num_bytes, |
| const MojoDiscardDataOptions* options_dict) { |
| MojoReadDataResult* result_dict = MojoReadDataResult::Create(); |
| MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_DISCARD; |
| if (options_dict->allOrNone()) |
| flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE; |
| |
| ::MojoReadDataOptions options; |
| options.struct_size = sizeof(options); |
| options.flags = flags; |
| MojoResult result = |
| MojoReadData(handle_.get().value(), &options, nullptr, &num_bytes); |
| result_dict->setResult(result); |
| result_dict->setNumBytes(result == MOJO_RESULT_OK ? num_bytes : 0); |
| return result_dict; |
| } |
| |
| MojoReadDataResult* MojoHandle::readData( |
| const V8BufferSource* buffer, |
| const MojoReadDataOptions* options_dict) const { |
| MojoReadDataResult* result_dict = MojoReadDataResult::Create(); |
| MojoReadDataFlags flags = MOJO_READ_DATA_FLAG_NONE; |
| if (options_dict->allOrNone()) |
| flags |= MOJO_READ_DATA_FLAG_ALL_OR_NONE; |
| if (options_dict->peek()) |
| flags |= MOJO_READ_DATA_FLAG_PEEK; |
| |
| void* elements = nullptr; |
| base::CheckedNumeric<uint32_t> checked_num_bytes; |
| switch (buffer->GetContentType()) { |
| case V8BufferSource::ContentType::kArrayBuffer: { |
| DOMArrayBuffer* array = buffer->GetAsArrayBuffer(); |
| elements = array->Data(); |
| checked_num_bytes = array->ByteLength(); |
| break; |
| } |
| case V8BufferSource::ContentType::kArrayBufferView: { |
| const auto& view = buffer->GetAsArrayBufferView(); |
| elements = view->BaseAddress(); |
| checked_num_bytes = view->byteLength(); |
| break; |
| } |
| } |
| |
| ::MojoReadDataOptions options; |
| options.struct_size = sizeof(options); |
| options.flags = flags; |
| |
| uint32_t num_bytes; |
| MojoResult result = |
| checked_num_bytes.AssignIfValid(&num_bytes) |
| ? MojoReadData(handle_.get().value(), &options, elements, &num_bytes) |
| : MOJO_RESULT_INVALID_ARGUMENT; |
| result_dict->setResult(result); |
| result_dict->setNumBytes(result == MOJO_RESULT_OK ? num_bytes : 0); |
| return result_dict; |
| } |
| |
| MojoMapBufferResult* MojoHandle::mapBuffer(unsigned offset, |
| unsigned num_bytes) { |
| MojoMapBufferResult* result_dict = MojoMapBufferResult::Create(); |
| |
| // We need to extract the underlying shared memory region to map it as array |
| // buffer contents. However, as we don't know what kind of shared memory |
| // region is currently backing the buffer, we unwrap it to the underlying |
| // platform shared memory region. We also don't want to duplicate the region, |
| // and so need to perform a small dance here to first unwrap, and later |
| // re-wrap the MojoSharedBuffer to/from a //base shared memory region. |
| mojo::SharedBufferHandle buffer_handle(handle_.release().value()); |
| auto region = mojo::UnwrapPlatformSharedMemoryRegion( |
| mojo::ScopedSharedBufferHandle(buffer_handle)); |
| |
| if (region.IsValid()) { |
| ArrayBufferContents contents(region, offset, num_bytes); |
| result_dict->setResult(MOJO_RESULT_OK); |
| result_dict->setBuffer(DOMArrayBuffer::Create(contents)); |
| } else { |
| result_dict->setResult(MOJO_RESULT_UNKNOWN); |
| } |
| |
| // 2nd part of the dance: we now need to wrap the shared memory region into a |
| // mojo handle again. |
| mojo::ScopedSharedBufferHandle mojo_buffer = |
| mojo::WrapPlatformSharedMemoryRegion(std::move(region)); |
| handle_.reset(mojo::Handle(mojo_buffer.release().value())); |
| |
| return result_dict; |
| } |
| |
| MojoCreateSharedBufferResult* MojoHandle::duplicateBufferHandle( |
| const MojoDuplicateBufferHandleOptions* options_dict) { |
| MojoCreateSharedBufferResult* result_dict = |
| MojoCreateSharedBufferResult::Create(); |
| |
| ::MojoDuplicateBufferHandleOptions options = { |
| sizeof(options), MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_NONE}; |
| if (options_dict->readOnly()) |
| options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY; |
| |
| mojo::Handle handle; |
| MojoResult result = MojoDuplicateBufferHandle(handle_.get().value(), &options, |
| handle.mutable_value()); |
| result_dict->setResult(result); |
| if (result == MOJO_RESULT_OK) { |
| result_dict->setHandle( |
| MakeGarbageCollected<MojoHandle>(mojo::MakeScopedHandle(handle))); |
| } |
| return result_dict; |
| } |
| |
| } // namespace blink |