diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 10:22:43 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:36:28 +0000 |
commit | 271a6c3487a14599023a9106329505597638d793 (patch) | |
tree | e040d58ffc86c1480b79ca8528020ca9ec919bf8 /chromium/mojo | |
parent | 7b2ffa587235a47d4094787d72f38102089f402a (diff) | |
download | qtwebengine-chromium-271a6c3487a14599023a9106329505597638d793.tar.gz |
BASELINE: Update Chromium to 77.0.3865.59
Change-Id: I1e89a5f3b009a9519a6705102ad65c92fe736f21
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/mojo')
155 files changed, 3950 insertions, 2134 deletions
diff --git a/chromium/mojo/core/BUILD.gn b/chromium/mojo/core/BUILD.gn index 515972a9cf5..2d46dda4f9a 100644 --- a/chromium/mojo/core/BUILD.gn +++ b/chromium/mojo/core/BUILD.gn @@ -119,7 +119,7 @@ template("core_impl_source_set") { public_deps += [ "//third_party/fuchsia-sdk/sdk:fdio" ] } - if (is_posix) { + if (is_posix && !is_mac) { if (!is_nacl || is_nacl_nonsfi) { sources += [ "broker_posix.cc", @@ -128,12 +128,8 @@ template("core_impl_source_set") { } } - if (is_mac && !is_ios) { - sources += [ - "channel_mac.cc", - "mach_port_relay.cc", - "mach_port_relay.h", - ] + if (is_mac) { + sources += [ "channel_mac.cc" ] } if (!is_nacl || is_nacl_nonsfi) { diff --git a/chromium/mojo/core/README.md b/chromium/mojo/core/README.md new file mode 100644 index 00000000000..050f5e52fb1 --- /dev/null +++ b/chromium/mojo/core/README.md @@ -0,0 +1,309 @@ +# Mojo Core Overview + +[TOC] + +## Overview + +Mojo Core implements an IPC mechanism based on Message routing over a network +of "Nodes". A Node in typical usage corresponds to a system process. +"Messages" are exchanged between pairs of "Ports", where a pair of Ports +represent a "MessagePipe" from the outside. A MessagePipe provides +reliable in-order delivery of Messages. +Messages can be used to transport Ports and platform handles (file descriptors, +Mach ports, Windows handles, etc.) between Nodes. +Communication between Nodes is done through platform IPC channels, such as +AF_UNIX sockets, pipes or Fuchsia channels. A new Node has to be invited into +the network by giving it an IPC channel to an existing Node in the network, +or else provided with a way to establish one, such as e.g. the name/path to a +listening pipe or an AF_UNIX socket. + +Mojo Core also provides a shared memory buffer primitive and assists in the +creation and transport of shared memory buffers as required by the underlying +operating system and process permissions. + +A single Node in the network is termed the "Broker". +The Broker has some special responsibilities in the network, notably the Broker +is responsible for: + - Introducing a pair of Nodes in the network to one another to establish a + direct IPC link between them. + - Copying handles between Nodes, where they cannot themselves do so. This is + e.g. the case for sandboxed processes on Windows. + - Creating shared memory objects for processes that can't themselves do so. + +Mojo Core exposes two API sets: + 1. The [C system API](../public/c/system/README.md). + 1. The [embedder API](embedder/README.md). + +This document is to describe how these APIs are implemented by Mojo Core. + +## Layering + +This image provides an overview of how the Mojo Core implementation is layered: + +![Mojo Core Layering](doc/layering.png) + +The C system API is a stable, versioned API, exposed through a singleton +structure containing C function thunks. This allows multiple clients in the same +application to use a single embedder-provided instance of Mojo, and allows these +clients to be versioned and distributed independently of Mojo. +Using C thunks also makes it easier to provide compatibility with other +programming languages than C and C++. + +When clients invoke on the Mojo public API, they go through the dispatch +functions implemented in [dispatcher.cc](dispatcher.cc), which in turn forwards +the call to a global instance of [mojo::core::Core](core.h). +Core further dispatches the calls to the implementation instance, which is +either a [mojo::core::Dispatcher](dispatcher.h), or a +[mojo::core::UserMessageImpl](user_message_impl.h). +In the case of a Dispatcher, the incoming MojoHandle is looked up in the +[mojo::core::HandleTable](handle_table.h), which returns the Dispatcher +corresponding to the MojoHandle. + +The public API exposes the following concepts as MojoHandles: + 1. [DataPipeConsumer](data_pipe_consumer_dispatcher.h). + 1. [DataPipeProducer](data_pipe_producer_dispatcher.h). + 1. [Invitation](invitation_dispatcher.h). + 1. [MessagePipe](message_pipe_dispatcher.h). + 1. [PlatformHandle](platform_handle_dispatcher.h). + 1. [SharedBuffer](shared_buffer_dispatcher.h). + 1. [Watcher](watcher_dispatcher.h) - this is know as "Trap" in the public API. + +These are implemented as subclasses of [mojo::core::Dispatcher](dispatcher.h). +The Dispatcher class implements the union of all possible operations on a +MojoHandle, and the various subclasses implement the subset appropriate to their +type. + +Additionally the public API exposes Messages as MojoMessageHandles, which are +simply an instance of a [mojo::core::UserMessageImpl](user_message_impl.h). + +## Threading model + +Mojo Core is multi-threaded, so any API can be used from any thread in a +process. Receiving Messages from other Nodes is done on the "IO Thread", which +is an [embedder-supplied task runner](embedder/README.md#ipc-initialization). +Sending Messages to other Nodes is typically done on the sending thread. +However, if the native IPC channel's buffer is full, the outgoing Message will +be queued and subsequently sent on the "IO Thread". + +## Event Dispatching + +During the processing of an API call and during activity in the NodeController, +various entities may change states, which can lead to the generation of +[TrapEvents](../public/c/system/README.md#Signals-Traps) to the user. +The [mojo::core::RequestContext](request_context.h) takes care of aggregating +Watchers that need dispatching and issuing outstanding TrapEvents as the last +RequestContext in a thread goes out of scope. + +This avoids calling out to user code from deep within Core, and the resulting +reentrancy that might ensue. + +## Messaging implementation + +Mojo Core is implemented on top of "[Ports](ports)" and is an embedder of Ports. + +Ports provides the base abstractions of addressing, +[mojo::core::ports::Node](ports/node.h) and +[mojo::core::ports::Port](ports/port.h) as well as the messaging and event +handling for maintaining and transporting Port pairs across Nodes. + +All routing and IPC is however delegated to the embedder, Mojo Core, via +the [mojo::core::NodeController](node_controller.h) which owns an instance of +Node, and implements its delegate interface. + +### Naming and Addressing + +Nodes are named by a large (128 bit) random number. Ports are likewise named +by a large (128 bit) random number. +Messages are directed at a Port, and so are addressed by a (Node:Port) pair. + +Each Port has precisely one "conjugate" Port at any point in time. When a +Message is sent through a Port, its conjugate Port is the implicit destination, +and this is a symmetrical relationship. The address of the conjugate Port can +change as it is moved between nodes. +In practice a Port is renamed each time it's moved between Nodes, and so changes +in both components of the (Node:Port) address pair. + +Note that since each Port has a single conjugate Port at any time, en-route +Messages are addressed only with the destination Port address, as the source +is implicit. + +### Routing and Topology + +A process network initially starts with only the Broker process Node. +New Nodes must be introduced to the broker by some means. This can be done by +inheriting an IPC handle into a newly created process, or by providing them +the name of a listening IPC channel they can connect to. + +Each Node thus starts with only a direct IPC connection to a single other Node +in the network. The first time a Node tries to forward a Message to a Node it +doesn't have a direct IPC channel with, it will send a message to the broker to +request an introduction to the new peer Node. + +If the broker knows of the peer Node, it will construct a new native IPC +channel, then hand one end of it to the requesting Node and one end to the new +peer Node. This will result in a direct Node-to-Node link between the two, and +so the topology of a process network will trend towards a fully connected graph. + +### Messages + +A UserMessageImpl is the unit of transport in Mojo Core. +This is a collection of + 1. Handles, + 1. Ports, + 1. User data. + +Mojo Core takes care of transferring and serializing and deserializing Handles +and Ports. +The user data on the other hand has to be serialized and deserialized by the +user. Mojo Core optimizes this by allowing the user data to be serialized only +at need, e.g. when a Message is routed to another Node. + +### Port + +A Node maintains a set of Ports local to that Node. + +Logically, each Port has a conjugate Port where all its Messages are destined. +However, when a Port is transferred to a different Node, the newly created Port +on the destination Node will have the transferred Port as its next-hop "Proxy" +Port it sends Messages to. The Proxy Port is responsible for delivering +in-progress Messages across to the new destination Port, and may buffer Messages +to that end. Once a Proxy Port has delivered all in-progress Messages, it +is dissolved and Messages start flowing through the next Peer Port in turn, +which may be the conjugate Port or another Proxy Port. + +Each Port maintains state relating to its: + - Current Peer Port, which may be a Proxy Port, but in the stable state will + be its conjugate Port. + - Its current state of messaging, which changes as Ports move across Nodes. + - Its outgoing Message state, notably Message serial numbers. + - Its incoming Message queue. + +The unit of messaging at this level is the +[mojo::core::ports::Event](ports/event.h), which has several sub-types relating +to the business of maintaining and transporting Ports and keeping their +Peer Port address up to date. + +The UserMessageEvent subclass is the event type that carries all user messaging. +A UserMessageEvent owns a +[mojo::core::ports::UserMessage](ports/user_message.h), which is the interface +to the embedder's Message type. Mojo Core implements this in +[mojoe::core::UserMessageImpl](user_message_impl.h). Note that at the Ports +level, this is simply opaque user data. It's the Mojo Core embedder that is +aware of the handles and data attached to a UserMessageImpl. + +Note that if a Port is transferred across multiple Nodes, it may end up with a +multi-leg Peer relationship. As result, and because different Messages for the +same Port may take different routes through the process network, out of order +Message delivery is possible. The incoming +[mojo::core::ports::MessageQueue](ports/message_queue.h) takes care of +re-ordering incoming Messages. + +The Node does all Message handling and delivery for its local Ports, but +delegates all routing to its delegate through the +[mojo::core::ports::NodeDelegate](ports/node_delegate.h) interface. +Mojo Core implements this in [mojo::core::NodeController](node_controller.h). + +### Node to Node IPC + +The business of handling Node to Node IPC is primarily handled in +[mojo::core::NodeChannel](node_channel.h) as well as +[mojo::core::Channel](channel.h) and its OS-specific subclasses by exchanging +[mojo::core::Channel::Message](channel.h)s. + +Note that a Channel::Message always goes from a source Node to a destination +Node through a Channel (strictly speaking one on each side). As the Channels on +either side (or the OS-provided IPC mechanism) conspire to copy the handles +across, the processes involved in the handle transfer are always implicit. +This means that a low-privilege or sandboxed process can only send its own +handles in a Message, and it's not possible to abuse this to acquire another +process's handles. + +A NodeChannel wraps a Channel that communicates to and from another given Node. +It implements all messaging to another specific Node, both relating to general +messaging (see NodeChannel::OnEventMessage), as well as messaging relating to +invitations and Port management. +Since the NodeChannel is associated with a single given Node, the peer Node name +is an implicit given in any incoming Message from the Channel. + +The Channel takes care of maintaining and servicing a queue of Messages outgoing +to the peer node, as well as reading and parsing the next incoming Message from +the IPC channel. +Depending on the platform and the Node topology, it may also take care of moving +outgoing and incoming handles between the processes hosting the Nodes on either +side of the Channel. + +Note that on Windows, the Broker always takes care of handle copying, as the +Broker will typically be the only process with sufficient privilege to copy +handles. This means that any Message carrying handles is routed through the +Broker on Windows. + +### Packet Framing + +An in-transit Message on an IPC channel will be serialized as a +[mojo::core::Channel::Message](channel.h). + +For user Messages, this will contain: + 1. A [mojo::core::Channel::Message::Header](channel.h) containing: + - The length of the full Message + - The length of the header. + - The Message type. + - The number of handles. + 2. The Platform handles. + 3. The serialized [mojo::core::ports::Event](ports/event.h). Specifically for + a UserMessageEvent: + - [mojo::core::ports::SerializedHeader](ports/event.cc). + - [mojo::core::ports::UserMessageEventData](ports/event.cc). + - Ports; num_ports times core::Ports::PortDescriptor. + - The serialized user data. + +## Buffering + +Message buffering in Mojo Core primarily occurs at two levels: + - In the incoming Message queue of a receiving Port. + - In the outgoing queue for an IPC Channel to a peer Node. + +There is also transient buffering of Messages to a new Peer Node while +introductions are in-flight. +Finally, while a Port is in transport to different node, it buffers inbound data +until the destination Node accepts the new Peer, at which time any buffered data +is forwarded to the new Port in the destination Node. + +Mojo Core does implement quotas on Port receiving queue Message number and byte +size. If the receive length quota is exceeded on a Port, it signals an +over-quota TrapEvent on the receiving Port. This doesn't however limit buffering +as the caller needs to handle the TrapEvent in some way - most likely by simply +closing the Port. + +Since Mojo Core does not limit buffering, the producers and consumers in a +process network must be balanced. +If a producer runs wild, the producer's process may buffer an arbirarly large +number of Messages (and bytes) in the outgoing Message queue on the IPC channel +to the consumer's Node. +Alternatively the consumer's process may buffer an arbitrarily large +number of Messages (and bytes) in the incoming Message queue on the consumer's +Port. + +## Security + +Mojo can be viewed as a Capability system, where a Port is a Capability. +If a Node can name a Port, that Port can be considered a granted Capability +to that Node. Many Ports will grant other Ports upon request, and so the +transitive closure of those Ports can be considered in the set of granted +Capabilities to a Node. + +Native handles can also be viewed as Capabilities, and so any native handle +reachable from the set of Ports granted to a Node can also be considered in +the Node's set of granted Capabilities. + +There's however a significant difference between Ports and native handles, in +that native handles are kernel-mediated capabilities. This means there's no way +for a process that doesn't hold a handle to operate on it. + +Mojo Ports, in contrast, are a pure user-mode construct and any Node can send +any Message to any Port of any other Node so long as it has knowledge of the +Port and Node names. If it doesn't already have an IPC channel to that node, +it can either send the Message through the Broker, or request an invitation to +the destination Node from the Broker and then send the Message directly. + +It is therefore important not to leak Port names into Nodes that shouldn't be +granted the corresponding Capability. diff --git a/chromium/mojo/core/broker_host.cc b/chromium/mojo/core/broker_host.cc index 5e6b2250e69..a555bcc3ca9 100644 --- a/chromium/mojo/core/broker_host.cc +++ b/chromium/mojo/core/broker_host.cc @@ -108,29 +108,32 @@ void BrokerHost::OnBufferRequest(uint32_t num_bytes) { base::subtle::PlatformSharedMemoryRegion region = base::subtle::PlatformSharedMemoryRegion::CreateWritable(num_bytes); - std::vector<PlatformHandleInTransit> handles(2); + std::vector<PlatformHandleInTransit> handles; + handles.reserve(2); if (region.IsValid()) { PlatformHandle h[2]; ExtractPlatformHandlesFromSharedMemoryRegionHandle( region.PassPlatformHandle(), &h[0], &h[1]); - handles[0] = PlatformHandleInTransit(std::move(h[0])); - handles[1] = PlatformHandleInTransit(std::move(h[1])); + handles.emplace_back(std::move(h[0])); #if !defined(OS_POSIX) || defined(OS_ANDROID) || \ (defined(OS_MACOSX) && !defined(OS_IOS)) // Non-POSIX systems, as well as Android, and non-iOS Mac, only use a single // handle to represent a writable region. - DCHECK(!handles[1].handle().is_valid()); - handles.resize(1); + DCHECK(!h[1].is_valid()); #else - DCHECK(handles[1].handle().is_valid()); + DCHECK(h[1].is_valid()); + handles.emplace_back(std::move(h[1])); #endif } BufferResponseData* response; Channel::MessagePtr message = CreateBrokerMessage( BrokerMessageType::BUFFER_RESPONSE, handles.size(), 0, &response); - if (!handles.empty()) { - base::UnguessableToken guid = region.GetGUID(); + if (handles.empty()) { + response->guid_high = 0; + response->guid_low = 0; + } else { + const base::UnguessableToken& guid = region.GetGUID(); response->guid_high = guid.GetHighForSerialization(); response->guid_low = guid.GetLowForSerialization(); PrepareHandlesForClient(&handles); diff --git a/chromium/mojo/core/channel.cc b/chromium/mojo/core/channel.cc index 7daaaa13d22..6aa38d22c86 100644 --- a/chromium/mojo/core/channel.cc +++ b/chromium/mojo/core/channel.cc @@ -19,7 +19,6 @@ #include "build/build_config.h" #include "mojo/core/configuration.h" #include "mojo/core/core.h" -#include "mojo/public/cpp/platform/features.h" #if defined(OS_MACOSX) && !defined(OS_IOS) #include "base/mac/mach_logging.h" @@ -154,12 +153,7 @@ Channel::Message::Message(size_t capacity, mach_ports_header_->num_ports = 0; // Initialize all handles to invalid values. for (size_t i = 0; i < max_handles_; ++i) { - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - mach_ports_header_->entries[i].mach_entry.type = {0}; - } else { - mach_ports_header_->entries[i].posix_entry = { - 0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } + mach_ports_header_->entries[i] = {0}; } #endif } @@ -247,6 +241,11 @@ Channel::MessagePtr Channel::Message::Deserialize( sizeof(MachPortsEntry); #else const uint32_t max_handles = 0; + // No extra header expected. Fail if this is detected. + if (extra_header_size > 0) { + DLOG(ERROR) << "Decoding invalid message: unexpected extra_header_size > 0"; + return nullptr; + } #endif // defined(OS_WIN) const uint16_t num_handles = @@ -377,19 +376,6 @@ bool Channel::Message::has_handles() const { : header()->num_handles) > 0; } -#if defined(OS_MACOSX) && !defined(OS_IOS) -bool Channel::Message::has_mach_ports() const { - if (!has_handles()) - return false; - - for (const auto& handle : handle_vector_) { - if (handle.is_mach_port_name() || handle.handle().is_mach_port()) - return true; - } - return false; -} -#endif - bool Channel::Message::is_legacy_message() const { return legacy_header()->message_type == MessageType::NORMAL_LEGACY; } @@ -444,56 +430,20 @@ void Channel::Message::SetHandles( #endif // defined(OS_WIN) #if defined(OS_MACOSX) && !defined(OS_IOS) - size_t mach_port_index = 0; - const bool use_channel_mac = - base::FeatureList::IsEnabled(features::kMojoChannelMac); if (mach_ports_header_) { for (size_t i = 0; i < max_handles_; ++i) { - if (use_channel_mac) { - mach_ports_header_->entries[i].mach_entry.type = {0}; - } else { - mach_ports_header_->entries[i].posix_entry = { - 0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } + mach_ports_header_->entries[i] = {0}; } for (size_t i = 0; i < handle_vector_.size(); i++) { - if (use_channel_mac) { - mach_ports_header_->entries[i].mach_entry.type = - static_cast<uint8_t>(handle_vector_[i].handle().type()); - } else { - if (!handle_vector_[i].is_mach_port_name() && - !handle_vector_[i].handle().is_mach_port()) { - DCHECK(handle_vector_[i].handle().is_valid_fd()); - continue; - } - - mach_port_t port = handle_vector_[i].is_mach_port_name() - ? handle_vector_[i].mach_port_name() - : handle_vector_[i].handle().GetMachPort().get(); - mach_ports_header_->entries[mach_port_index].posix_entry.index = i; - mach_ports_header_->entries[mach_port_index].posix_entry.mach_port = - port; - mach_port_index++; - } + mach_ports_header_->entries[i].type = + static_cast<uint8_t>(handle_vector_[i].handle().type()); } - mach_ports_header_->num_ports = - use_channel_mac ? handle_vector_.size() - : static_cast<uint16_t>(mach_port_index); + mach_ports_header_->num_ports = handle_vector_.size(); } #endif } std::vector<PlatformHandleInTransit> Channel::Message::TakeHandles() { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (mach_ports_header_ && - !base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - for (size_t i = 0; i < max_handles_; ++i) { - mach_ports_header_->entries[i].posix_entry = { - 0, static_cast<uint32_t>(MACH_PORT_NULL)}; - } - mach_ports_header_->num_ports = 0; - } -#endif return std::move(handle_vector_); } @@ -503,23 +453,6 @@ Channel::Message::TakeHandlesForTransport() { // Not necessary on Windows. NOTREACHED(); return std::vector<PlatformHandleInTransit>(); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - return std::move(handle_vector_); - } else { - std::vector<PlatformHandleInTransit> non_mach_handles; - for (auto& handle : handle_vector_) { - if (handle.is_mach_port_name() || handle.handle().is_mach_port()) { - // Ownership is effectively transferred to the receiving process - // out-of-band via MachPortRelay. - handle.CompleteTransit(); - } else { - non_mach_handles.emplace_back(std::move(handle)); - } - } - handle_vector_.clear(); - return non_mach_handles; - } #else return std::move(handle_vector_); #endif diff --git a/chromium/mojo/core/channel.h b/chromium/mojo/core/channel.h index e06ce1af2b9..bf8e02820a8 100644 --- a/chromium/mojo/core/channel.h +++ b/chromium/mojo/core/channel.h @@ -66,7 +66,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // TODO(https://crbug.com/695645): remove legacy support when Arc++ has // updated to Mojo with normal versioned messages. NORMAL_LEGACY = 0, -#if defined(OS_MACOSX) +#if defined(OS_IOS) // A control message containing handles to echo back. HANDLES_SENT, // A control message containing handles that can now be closed. @@ -111,27 +111,12 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel }; #if defined(OS_MACOSX) && !defined(OS_IOS) - union MachPortsEntry { - // Used with ChannelPosix. - struct { - // Index of Mach port in the original vector of - // PlatformHandleInTransits. - uint16_t index; - - // Mach port name. - uint32_t mach_port; - } posix_entry; - - // Used with ChannelMac. - struct { - // The PlatformHandle::Type. - uint8_t type; - } mach_entry; - static_assert(sizeof(mach_port_t) <= sizeof(uint32_t), - "mach_port_t must be no larger than uint32_t"); + struct MachPortsEntry { + // The PlatformHandle::Type. + uint8_t type; }; - static_assert(sizeof(MachPortsEntry) == 6, - "sizeof(MachPortsEntry) must be 6 bytes"); + static_assert(sizeof(MachPortsEntry) == 1, + "sizeof(MachPortsEntry) must be 1 byte"); // Structure of the extra header field when present on OSX. struct MachPortsExtraHeader { @@ -205,9 +190,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel size_t num_handles() const; bool has_handles() const; -#if defined(OS_MACOSX) && !defined(OS_IOS) - bool has_mach_ports() const; -#endif bool is_legacy_message() const; LegacyHeader* legacy_header() const; diff --git a/chromium/mojo/core/channel_mac.cc b/chromium/mojo/core/channel_mac.cc index 8820afe0a0c..582b056d2d4 100644 --- a/chromium/mojo/core/channel_mac.cc +++ b/chromium/mojo/core/channel_mac.cc @@ -133,8 +133,8 @@ class ChannelMac : public Channel, } for (uint16_t i = 0; i < mach_ports_header->num_ports; ++i) { - auto type = static_cast<PlatformHandle::Type>( - mach_ports_header->entries[i].mach_entry.type); + auto type = + static_cast<PlatformHandle::Type>(mach_ports_header->entries[i].type); if (type == PlatformHandle::Type::kNone) { return false; } else if (type == PlatformHandle::Type::kFd && @@ -353,8 +353,8 @@ class ChannelMac : public Channel, sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) + (message->num_handles() * sizeof(mach_msg_port_descriptor_t)); const size_t expected_message_size = - round_msg(mach_header_size + message->data_num_bytes() + - sizeof(mach_msg_audit_trailer_t)); + round_msg(mach_header_size + sizeof(uint64_t) + + message->data_num_bytes() + sizeof(mach_msg_audit_trailer_t)); const bool transfer_message_ool = expected_message_size >= send_buffer_.size(); @@ -703,9 +703,8 @@ class ChannelMac : public Channel, } // namespace -// TODO(crbug.com/932175): This will be renamed Channel::Create. MOJO_SYSTEM_IMPL_EXPORT -scoped_refptr<Channel> ChannelMacCreate( +scoped_refptr<Channel> Channel::Create( Channel::Delegate* delegate, ConnectionParams connection_params, Channel::HandlePolicy handle_policy, diff --git a/chromium/mojo/core/channel_mac_fuzzer.cc b/chromium/mojo/core/channel_mac_fuzzer.cc index e45e6bbe5c2..87674baec92 100644 --- a/chromium/mojo/core/channel_mac_fuzzer.cc +++ b/chromium/mojo/core/channel_mac_fuzzer.cc @@ -6,13 +6,11 @@ #include "base/logging.h" #include "base/mac/mach_logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" -#include "base/test/scoped_feature_list.h" +#include "base/task/single_thread_task_executor.h" #include "mojo/core/channel.h" #include "mojo/core/entrypoints.h" #include "mojo/core/test/data/channel_mac/channel_mac.pb.h" -#include "mojo/public/cpp/platform/features.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "testing/libfuzzer/fuzzers/mach/mach_message_converter.h" #include "testing/libfuzzer/proto/lpm_interface.h" @@ -22,20 +20,17 @@ namespace { class ChannelMacFuzzer { public: ChannelMacFuzzer() { - feature_list_.InitAndEnableFeature(mojo::features::kMojoChannelMac); - mojo::core::InitializeCore(); logging::SetMinLogLevel(logging::LOG_FATAL); } scoped_refptr<base::TaskRunner> io_task_runner() { - return message_loop_.task_runner(); + return io_task_executor_.task_runner(); } private: - base::test::ScopedFeatureList feature_list_; - base::MessageLoopForIO message_loop_; + base::SingleThreadTaskExecutor io_task_executor_{base::MessagePump::Type::IO}; }; class FakeChannelDelegate : public mojo::core::Channel::Delegate { diff --git a/chromium/mojo/core/channel_posix.cc b/chromium/mojo/core/channel_posix.cc index eed78a3c7f4..24818bdf0f4 100644 --- a/chromium/mojo/core/channel_posix.cc +++ b/chromium/mojo/core/channel_posix.cc @@ -22,17 +22,12 @@ #include "base/task_runner.h" #include "build/build_config.h" #include "mojo/core/core.h" -#include "mojo/public/cpp/platform/features.h" #include "mojo/public/cpp/platform/socket_utils_posix.h" #if !defined(OS_NACL) #include <sys/uio.h> #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/core/mach_port_relay.h" -#endif - namespace mojo { namespace core { @@ -97,9 +92,6 @@ class MessageView { }; class ChannelPosix : public Channel, -#if defined(OS_MACOSX) && !defined(OS_IOS) - public MachPortRelay::Observer, -#endif public base::MessageLoopCurrent::DestructionObserver, public base::MessagePumpForIO::FdWatcher { public: @@ -119,15 +111,6 @@ class ChannelPosix : public Channel, } void Start() override { -#if defined(OS_MACOSX) && !defined(OS_IOS) - auto* relay = Core::Get()->GetMachPortRelay(); - if (relay) { - // We should only have a relay if we know the remote process handle, - // because that means we're in the broker process. - relay->AddObserver(this); - } -#endif - if (io_task_runner_->RunsTasksInCurrentSequence()) { StartOnIOThread(); } else { @@ -143,30 +126,6 @@ class ChannelPosix : public Channel, } void Write(MessagePtr message) override { -#if defined(OS_MACOSX) && !defined(OS_IOS) - // If this message has Mach ports and we have a MachPortRelay, use the relay - // to rewrite the ports as receive rights from which the send right can be - // read. See |MachPortRelay::SendPortsToProcess()|. - // - // Note that if we don't have a relay, the receiving process must, and they - // must also have the ability to extract a send right from the ports that - // are already attached. - MachPortRelay* relay = Core::Get()->GetMachPortRelay(); - if (relay && remote_process().is_valid() && message->has_mach_ports()) { - if (relay->port_provider()->TaskForPid(remote_process().get()) == - MACH_PORT_NULL) { - // We also need to have a task port for the remote process before we can - // send it any other ports. If we don't have one yet, queue the message - // until OnProcessReady() is invoked. - base::AutoLock lock(task_port_wait_lock_); - pending_outgoing_with_mach_ports_.emplace_back(std::move(message)); - return; - } - - relay->SendPortsToProcess(message.get(), remote_process().get()); - } -#endif - bool write_error = false; { base::AutoLock lock(write_lock_); @@ -202,82 +161,6 @@ class ChannelPosix : public Channel, bool* deferred) override { if (num_handles > std::numeric_limits<uint16_t>::max()) return false; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // On OSX, we can have mach ports which are located in the extra header - // section. - using MachPortsEntry = Channel::Message::MachPortsEntry; - using MachPortsExtraHeader = Channel::Message::MachPortsExtraHeader; - if (extra_header_size < - sizeof(MachPortsExtraHeader) + num_handles * sizeof(MachPortsEntry)) { - return false; - } - const MachPortsExtraHeader* mach_ports_header = - reinterpret_cast<const MachPortsExtraHeader*>(extra_header); - size_t num_mach_ports = mach_ports_header->num_ports; - if (num_mach_ports > num_handles) - return false; - if (incoming_fds_.size() + num_mach_ports < num_handles) - return true; - - std::vector<PlatformHandleInTransit> handles_in_transit(num_handles); - const MachPortsEntry* mach_ports = mach_ports_header->entries; - - // If we know the remote process handle, we assume all incoming Mach ports - // are send right references owned by the remote process. Otherwise they're - // receive ports we can use to read a send right. - const bool extract_send_rights = remote_process().is_valid(); - for (size_t i = 0, mach_port_index = 0; i < num_handles; ++i) { - if (mach_port_index < num_mach_ports && - mach_ports[mach_port_index].posix_entry.index == i) { - mach_port_t port_name = static_cast<mach_port_t>( - mach_ports[mach_port_index].posix_entry.mach_port); - if (extract_send_rights) { - handles_in_transit[i] = - PlatformHandleInTransit::CreateForMachPortName(port_name); - } else { - handles_in_transit[i] = PlatformHandleInTransit( - PlatformHandle(MachPortRelay::ReceiveSendRight( - base::mac::ScopedMachReceiveRight(port_name)))); - } - mach_port_index++; - } else { - if (incoming_fds_.empty()) - return false; - handles_in_transit[i] = PlatformHandleInTransit( - PlatformHandle(std::move(incoming_fds_.front()))); - incoming_fds_.pop_front(); - } - } - if (extract_send_rights && num_mach_ports) { - MachPortRelay* relay = Core::Get()->GetMachPortRelay(); - DCHECK(relay); - // Extracting send rights requires that we have a task port for the - // remote process, which we may not yet have. - if (relay->port_provider()->TaskForPid(remote_process().get()) != - MACH_PORT_NULL) { - // We do have a task port, so extract the send rights immediately. - for (auto& handle : handles_in_transit) { - if (handle.is_mach_port_name()) { - handle = PlatformHandleInTransit(PlatformHandle(relay->ExtractPort( - handle.mach_port_name(), remote_process().get()))); - } - } - } else { - // No task port, we have to defer this message. - *deferred = true; - base::AutoLock lock(task_port_wait_lock_); - std::vector<uint8_t> data(payload_size); - memcpy(data.data(), payload, payload_size); - pending_incoming_with_mach_ports_.emplace_back( - std::move(data), std::move(handles_in_transit)); - return true; - } - } - - handles->resize(handles_in_transit.size()); - for (size_t i = 0; i < handles->size(); ++i) - handles->at(i) = handles_in_transit[i].TakeHandle(); -#else if (incoming_fds_.size() < num_handles) return true; @@ -286,7 +169,6 @@ class ChannelPosix : public Channel, handles->at(i) = PlatformHandle(std::move(incoming_fds_.front())); incoming_fds_.pop_front(); } -#endif return true; } @@ -352,74 +234,14 @@ class ChannelPosix : public Channel, socket_.reset(); ignore_result(server_.TakePlatformHandle()); } -#if defined(OS_MACOSX) +#if defined(OS_IOS) fds_to_close_.clear(); #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) - auto* relay = Core::Get()->GetMachPortRelay(); - if (relay) - relay->RemoveObserver(this); -#endif - // May destroy the |this| if it was the last reference. self_ = nullptr; } -#if defined(OS_MACOSX) && !defined(OS_IOS) - // MachPortRelay::Observer: - void OnProcessReady(base::ProcessHandle process) override { - if (process != remote_process().get()) - return; - - io_task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - &ChannelPosix::FlushPendingMessagesWithMachPortsOnIOThread, this)); - } - - void FlushPendingMessagesWithMachPortsOnIOThread() { - // We have a task port for the remote process. Now we can send or accept - // any pending messages with Mach ports. - std::vector<RawIncomingMessage> incoming; - std::vector<MessagePtr> outgoing; - { - base::AutoLock lock(task_port_wait_lock_); - if (reject_incoming_messages_with_mach_ports_) - return; - std::swap(pending_incoming_with_mach_ports_, incoming); - std::swap(pending_outgoing_with_mach_ports_, outgoing); - } - - DCHECK(remote_process().is_valid()); - base::ProcessHandle process = remote_process().get(); - MachPortRelay* relay = Core::Get()->GetMachPortRelay(); - DCHECK(relay); - for (auto& message : incoming) { - Channel::Delegate* d = delegate(); - if (!d) - break; - std::vector<PlatformHandle> handles(message.handles.size()); - for (size_t i = 0; i < message.handles.size(); ++i) { - if (message.handles[i].is_mach_port_name()) { - handles[i] = PlatformHandle( - relay->ExtractPort(message.handles[i].mach_port_name(), process)); - } else { - DCHECK(!message.handles[i].owning_process().is_valid()); - handles[i] = message.handles[i].TakeHandle(); - } - } - d->OnChannelMessage(message.data.data(), message.data.size(), - std::move(handles)); - } - - for (auto& message : outgoing) { - relay->SendPortsToProcess(message.get(), process); - Write(std::move(message)); - } - } -#endif - // base::MessageLoopCurrent::DestructionObserver: void WillDestroyCurrentMessageLoop() override { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); @@ -535,8 +357,8 @@ class ChannelPosix : public Channel, // TODO: Handle lots of handles. result = SendmsgWithHandles(socket_.get(), &iov, 1, fds); if (result >= 0) { -#if defined(OS_MACOSX) - // There is a bug on OSX which makes it dangerous to close +#if defined(OS_IOS) + // There is a bug in XNU which makes it dangerous to close // a file descriptor while it is in transit. So instead we // store the file descriptor in a set and send a message to // the recipient, which is queued AFTER the message that @@ -555,7 +377,7 @@ class ChannelPosix : public Channel, for (auto& fd : fds) fds_to_close_.emplace_back(std::move(fd)); } -#endif // defined(OS_MACOSX) +#endif // defined(OS_IOS) handles_written += num_handles_to_send; DCHECK_LE(handles_written, num_handles); message_view.set_num_handles_sent(handles_written); @@ -575,8 +397,8 @@ class ChannelPosix : public Channel, if (result < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK -#if defined(OS_MACOSX) - // On OS X if sendmsg() is trying to send fds between processes and +#if defined(OS_IOS) + // On iOS if sendmsg() is trying to send fds between processes and // there isn't enough room in the output buffer to send the fd // structure over atomically then EMSGSIZE is returned. // @@ -591,7 +413,7 @@ class ChannelPosix : public Channel, // passing the FD over atomically. && errno != EMSGSIZE #endif - ) { + ) { return false; } message_view.SetHandles(std::move(handles)); @@ -634,7 +456,7 @@ class ChannelPosix : public Channel, return true; } -#if defined(OS_MACOSX) +#if defined(OS_IOS) bool OnControlMessage(Message::MessageType message_type, const void* payload, size_t payload_size, @@ -700,7 +522,7 @@ class ChannelPosix : public Channel, fds_to_close_.erase(start, it); return true; } -#endif // defined(OS_MACOSX) +#endif // defined(OS_IOS) void OnWriteError(Error error) { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); @@ -746,57 +568,22 @@ class ChannelPosix : public Channel, bool leak_handle_ = false; -#if defined(OS_MACOSX) +#if defined(OS_IOS) base::Lock fds_to_close_lock_; std::vector<base::ScopedFD> fds_to_close_; -#if !defined(OS_IOS) - // Guards access to the send/receive queues below. These are messages that - // can't be fully accepted from or dispatched to the Channel user yet because - // we're still waiting on a task port for the remote process. - struct RawIncomingMessage { - RawIncomingMessage(std::vector<uint8_t> data, - std::vector<PlatformHandleInTransit> handles) - : data(std::move(data)), handles(std::move(handles)) {} - RawIncomingMessage(RawIncomingMessage&&) = default; - ~RawIncomingMessage() = default; - - std::vector<uint8_t> data; - std::vector<PlatformHandleInTransit> handles; - }; - - base::Lock task_port_wait_lock_; - bool reject_incoming_messages_with_mach_ports_ = false; - std::vector<MessagePtr> pending_outgoing_with_mach_ports_; - std::vector<RawIncomingMessage> pending_incoming_with_mach_ports_; -#endif // !defined(OS_IOS) -#endif // defined(OS_MACOSX) +#endif // defined(OS_IOS) DISALLOW_COPY_AND_ASSIGN(ChannelPosix); }; } // namespace -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Forward declare from channel_mac.cc. -scoped_refptr<Channel> ChannelMacCreate( - Channel::Delegate* delegate, - ConnectionParams connection_params, - Channel::HandlePolicy handle_policy, - scoped_refptr<base::TaskRunner> io_task_runner); -#endif - // static scoped_refptr<Channel> Channel::Create( Delegate* delegate, ConnectionParams connection_params, HandlePolicy handle_policy, scoped_refptr<base::TaskRunner> io_task_runner) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - return ChannelMacCreate(delegate, std::move(connection_params), - handle_policy, io_task_runner); - } -#endif return new ChannelPosix(delegate, std::move(connection_params), handle_policy, io_task_runner); } diff --git a/chromium/mojo/core/channel_unittest.cc b/chromium/mojo/core/channel_unittest.cc index 14585e348ea..623ec6cd7be 100644 --- a/chromium/mojo/core/channel_unittest.cc +++ b/chromium/mojo/core/channel_unittest.cc @@ -11,8 +11,11 @@ #include "base/message_loop/message_loop.h" #include "base/optional.h" #include "base/process/process_handle.h" +#include "base/process/process_metrics.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "mojo/core/platform_handle_utils.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "testing/gmock/include/gmock/gmock.h" @@ -364,6 +367,28 @@ TEST(ChannelTest, DeserializeMessage_BadExtraHeaderSize) { base::kNullProcessHandle)); } +#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) +TEST(ChannelTest, DeserializeMessage_NonZeroExtraHeaderSize) { + // Verifies that a message payload is rejected when the extra header chunk + // size anything but zero on Linux, even if it's aligned. + constexpr uint16_t kTotalHeaderSize = + sizeof(Channel::Message::Header) + kChannelMessageAlignment; + constexpr uint32_t kEmptyPayloadSize = 8; + constexpr uint32_t kMessageSize = kTotalHeaderSize + kEmptyPayloadSize; + char message[kMessageSize]; + memset(message, 0, kMessageSize); + + Channel::Message::Header* header = + reinterpret_cast<Channel::Message::Header*>(&message[0]); + header->num_bytes = kMessageSize; + header->num_header_bytes = kTotalHeaderSize; + header->message_type = Channel::Message::MessageType::NORMAL; + header->num_handles = 0; + EXPECT_EQ(nullptr, Channel::Message::Deserialize(&message[0], kMessageSize, + base::kNullProcessHandle)); +} +#endif + class CountingChannelDelegate : public Channel::Delegate { public: explicit CountingChannelDelegate(base::OnceClosure on_final_message) @@ -485,6 +510,71 @@ TEST(ChannelTest, PeerStressTest) { EXPECT_EQ(0u, delegate_b.error_count_); } +class SingleMessageWaiterDelegate : public Channel::Delegate { + public: + SingleMessageWaiterDelegate() {} + + void OnChannelMessage(const void* payload, + size_t payload_size, + std::vector<PlatformHandle> handles) override { + message_received_ = true; + run_loop_->Quit(); + } + + void OnChannelError(Channel::Error error) override { + channel_error_ = true; + run_loop_->Quit(); + } + + void Reset(base::RunLoop* loop) { + run_loop_ = loop; + message_received_ = false; + channel_error_ = false; + } + + bool message_received() { return message_received_; } + bool channel_error() { return channel_error_; } + + private: + bool message_received_ = false; + bool channel_error_ = false; + base::RunLoop* run_loop_; + DISALLOW_COPY_AND_ASSIGN(SingleMessageWaiterDelegate); +}; + +TEST(ChannelTest, MessageSizeTest) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_IO); + PlatformChannel platform_channel; + + SingleMessageWaiterDelegate receiver_delegate; + scoped_refptr<Channel> receiver = Channel::Create( + &receiver_delegate, + ConnectionParams(platform_channel.TakeLocalEndpoint()), + Channel::HandlePolicy::kAcceptHandles, message_loop.task_runner()); + receiver->Start(); + + MockChannelDelegate sender_delegate; + scoped_refptr<Channel> sender = Channel::Create( + &sender_delegate, ConnectionParams(platform_channel.TakeRemoteEndpoint()), + Channel::HandlePolicy::kAcceptHandles, message_loop.task_runner()); + sender->Start(); + + for (uint32_t i = 0; i < base::GetPageSize() * 4; ++i) { + SCOPED_TRACE(base::StringPrintf("message size %d", i)); + + auto message = std::make_unique<Channel::Message>(i, 0); + memset(message->mutable_payload(), 0xAB, i); + sender->Write(std::move(message)); + + base::RunLoop loop; + receiver_delegate.Reset(&loop); + loop.Run(); + + EXPECT_TRUE(receiver_delegate.message_received()); + EXPECT_FALSE(receiver_delegate.channel_error()); + } +} + } // namespace } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/core.cc b/chromium/mojo/core/core.cc index 32ecea3eae5..bfd727a3992 100644 --- a/chromium/mojo/core/core.cc +++ b/chromium/mojo/core/core.cc @@ -213,18 +213,6 @@ void Core::ConnectIsolated(ConnectionParams connection_params, connection_name); } -void Core::SetMachPortProvider(base::PortProvider* port_provider) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - GetNodeController()->CreateMachPortRelay(port_provider); -#endif -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -MachPortRelay* Core::GetMachPortRelay() { - return GetNodeController()->GetMachPortRelay(); -} -#endif - MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { base::AutoLock lock(handles_->GetLock()); return handles_->AddDispatcher(dispatcher); diff --git a/chromium/mojo/core/core.h b/chromium/mojo/core/core.h index 2840bc5e680..b3f557b27de 100644 --- a/chromium/mojo/core/core.h +++ b/chromium/mojo/core/core.h @@ -30,14 +30,9 @@ #include "mojo/public/c/system/trap.h" #include "mojo/public/c/system/types.h" -namespace base { -class PortProvider; -} - namespace mojo { namespace core { -class MachPortRelay; class PlatformSharedMemoryMapping; // |Core| is an object that implements the Mojo system calls. All public methods @@ -112,13 +107,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const ports::PortRef& port, base::StringPiece connection_name); - // Sets the mach port provider for this process. - void SetMachPortProvider(base::PortProvider* port_provider); - -#if defined(OS_MACOSX) && !defined(OS_IOS) - MachPortRelay* GetMachPortRelay(); -#endif - MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher); // Adds new dispatchers for non-message-pipe handles received in a message. diff --git a/chromium/mojo/core/data_pipe_unittest.cc b/chromium/mojo/core/data_pipe_unittest.cc index 0ce3f1ed985..a46750795a4 100644 --- a/chromium/mojo/core/data_pipe_unittest.cc +++ b/chromium/mojo/core/data_pipe_unittest.cc @@ -10,9 +10,9 @@ #include "base/bind.h" #include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/stl_util.h" +#include "base/test/scoped_task_environment.h" #include "build/build_config.h" #include "mojo/core/embedder/embedder.h" #include "mojo/core/test/mojo_test_base.h" @@ -42,6 +42,9 @@ const size_t kMultiprocessCapacity = 37; const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes"; const int kMultiprocessMaxIter = 5; +// Capacity that will cause data pipe creation to fail. +constexpr size_t kOversizedCapacity = std::numeric_limits<uint32_t>::max(); + // TODO(rockot): There are many uses of ASSERT where EXPECT would be more // appropriate. Fix this. @@ -1727,6 +1730,17 @@ bool ReadAllData(MojoHandle consumer, return num_bytes == 0; } +TEST_F(DataPipeTest, CreateOversized) { + const MojoCreateDataPipeOptions options = { + kSizeOfOptions, // |struct_size|. + MOJO_CREATE_DATA_PIPE_FLAG_NONE, // |flags|. + 1, // |element_num_bytes|. + kOversizedCapacity, // |capacity_num_bytes|. + }; + + ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, Create(&options)); +} + #if !defined(OS_IOS) TEST_F(DataPipeTest, Multiprocess) { @@ -1972,7 +1986,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(DataPipeStatusChangeInTransitClient, EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - base::MessageLoop message_loop; + base::test::ScopedTaskEnvironment scoped_task_environment; // Wait on producer 1 and consumer 1 using SimpleWatchers. { @@ -2040,6 +2054,33 @@ TEST_F(DataPipeTest, StatusChangeInTransit) { }); } +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(CreateOversizedChild, DataPipeTest, h) { + const MojoCreateDataPipeOptions options = { + kSizeOfOptions, // |struct_size|. + MOJO_CREATE_DATA_PIPE_FLAG_NONE, // |flags|. + 1, // |element_num_bytes|. + kOversizedCapacity // |capacity_num_bytes|. + }; + + MojoHandle p, c; + ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, + MojoCreateDataPipe(&options, &p, &c)); + WriteMessage(h, "success"); + + // Wait for a quit message. + EXPECT_EQ("quit", ReadMessage(h)); +} + +TEST_F(DataPipeTest, CreateOversizedInChild) { + RunTestClient("CreateOversizedChild", [&](MojoHandle child) { + // Wait for the child to finish the test. + std::string expected_message = ReadMessage(child); + EXPECT_EQ("success", expected_message); + + WriteMessage(child, "quit"); + }); +} + #endif // !defined(OS_IOS) } // namespace diff --git a/chromium/mojo/core/doc/layering.png b/chromium/mojo/core/doc/layering.png Binary files differnew file mode 100644 index 00000000000..72c6d0bb0cd --- /dev/null +++ b/chromium/mojo/core/doc/layering.png diff --git a/chromium/mojo/core/embedder/BUILD.gn b/chromium/mojo/core/embedder/BUILD.gn index 774afe42938..47f1c390166 100644 --- a/chromium/mojo/core/embedder/BUILD.gn +++ b/chromium/mojo/core/embedder/BUILD.gn @@ -16,11 +16,6 @@ component("embedder") { "scoped_ipc_support.cc", ] - if (is_mac && !is_ios) { - public += [ "default_mach_broker.h" ] - sources += [ "default_mach_broker.cc" ] - } - defines = [ "IS_MOJO_CORE_EMBEDDER_IMPL" ] public_deps = [ diff --git a/chromium/mojo/core/embedder/default_mach_broker.cc b/chromium/mojo/core/embedder/default_mach_broker.cc deleted file mode 100644 index 436917a7536..00000000000 --- a/chromium/mojo/core/embedder/default_mach_broker.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2016 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 "mojo/core/embedder/default_mach_broker.h" - -#include "base/logging.h" - -namespace mojo { -namespace core { - -namespace { -const char kBootstrapPortName[] = "mojo_default_mach_broker"; -} - -// static -void DefaultMachBroker::SendTaskPortToParent() { - bool result = - base::MachPortBroker::ChildSendTaskPortToParent(kBootstrapPortName); - DCHECK(result); -} - -// static -DefaultMachBroker* DefaultMachBroker::Get() { - static DefaultMachBroker* broker = new DefaultMachBroker; - return broker; -} - -DefaultMachBroker::DefaultMachBroker() : broker_(kBootstrapPortName) { - bool result = broker_.Init(); - DCHECK(result); -} - -DefaultMachBroker::~DefaultMachBroker() {} - -void DefaultMachBroker::ExpectPid(base::ProcessHandle pid) { - broker_.AddPlaceholderForPid(pid); -} - -void DefaultMachBroker::RemovePid(base::ProcessHandle pid) { - broker_.InvalidatePid(pid); -} - -} // namespace core -} // namespace mojo diff --git a/chromium/mojo/core/embedder/default_mach_broker.h b/chromium/mojo/core/embedder/default_mach_broker.h deleted file mode 100644 index 2229ec12a44..00000000000 --- a/chromium/mojo/core/embedder/default_mach_broker.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 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 MOJO_CORE_EMBEDDER_DEFAULT_MACH_BROKER_H_ -#define MOJO_CORE_EMBEDDER_DEFAULT_MACH_BROKER_H_ - -#include "base/component_export.h" -#include "base/mac/mach_port_broker.h" -#include "base/macros.h" - -namespace mojo { -namespace core { - -// A singleton Mojo embedders can use to manage task port brokering among -// connected processes. -class COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) DefaultMachBroker { - public: - // Sends the task port of the current process to the parent over Mach IPC. - // For use in child processes. - static void SendTaskPortToParent(); - - // Returns the global |DefaultMachBroker|. - static DefaultMachBroker* Get(); - - // Registers |pid| with a MACH_PORT_NULL task port in the port provider. A - // child's pid must be registered before the broker will accept a task port - // from that child. - // - // Callers MUST have the lock acquired (see |GetLock()) while calling this. - void ExpectPid(base::ProcessHandle pid); - - // Removes |pid| from the port provider. - // - // Callers MUST have the lock acquired (see |GetLock()) while calling this. - void RemovePid(base::ProcessHandle pid); - - base::Lock& GetLock() { return broker_.GetLock(); } - base::PortProvider* port_provider() { return &broker_; } - - private: - DefaultMachBroker(); - ~DefaultMachBroker(); - - base::MachPortBroker broker_; - - DISALLOW_COPY_AND_ASSIGN(DefaultMachBroker); -}; - -} // namespace core -} // namespace mojo - -#endif // MOJO_CORE_EMBEDDER_DEFAULT_MACH_BROKER_H_ diff --git a/chromium/mojo/core/embedder/embedder.cc b/chromium/mojo/core/embedder/embedder.cc index 4044de083ee..a25d501386b 100644 --- a/chromium/mojo/core/embedder/embedder.cc +++ b/chromium/mojo/core/embedder/embedder.cc @@ -39,12 +39,5 @@ scoped_refptr<base::TaskRunner> GetIOTaskRunner() { return Core::Get()->GetNodeController()->io_task_runner(); } -#if defined(OS_MACOSX) && !defined(OS_IOS) -void SetMachPortProvider(base::PortProvider* port_provider) { - DCHECK(port_provider); - Core::Get()->SetMachPortProvider(port_provider); -} -#endif - } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/embedder/embedder.h b/chromium/mojo/core/embedder/embedder.h index feb7ca1be4b..2b7d7dbfbae 100644 --- a/chromium/mojo/core/embedder/embedder.h +++ b/chromium/mojo/core/embedder/embedder.h @@ -18,10 +18,6 @@ #include "build/build_config.h" #include "mojo/core/embedder/configuration.h" -namespace base { -class PortProvider; -} - namespace mojo { namespace core { @@ -50,15 +46,6 @@ void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback); COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) scoped_refptr<base::TaskRunner> GetIOTaskRunner(); -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Set the |base::PortProvider| for this process. Can be called on any thread, -// but must be set in the root process before any Mach ports can be transferred. -// -// If called at all, this must be called while a ScopedIPCSupport exists. -COMPONENT_EXPORT(MOJO_CORE_EMBEDDER) -void SetMachPortProvider(base::PortProvider* port_provider); -#endif - } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/invitation_unittest.cc b/chromium/mojo/core/invitation_unittest.cc index dae047ad6c9..674a4fe803c 100644 --- a/chromium/mojo/core/invitation_unittest.cc +++ b/chromium/mojo/core/invitation_unittest.cc @@ -22,7 +22,6 @@ #include "build/build_config.h" #include "mojo/core/test/mojo_test_base.h" #include "mojo/public/c/system/invitation.h" -#include "mojo/public/cpp/platform/features.h" #include "mojo/public/cpp/platform/named_platform_channel.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -80,18 +79,8 @@ void PrepareToPassRemoteEndpoint(PlatformChannel* channel, #if defined(OS_FUCHSIA) channel->PrepareToPassRemoteEndpoint(&options->handles_to_transfer, &value); #elif defined(OS_MACOSX) - PlatformChannel::HandlePassingInfo info; - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) - info = options->mach_ports_for_rendezvous; - else - info = options->fds_to_remap; - - channel->PrepareToPassRemoteEndpoint(&info, &value); - - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) - options->mach_ports_for_rendezvous = info; - else - options->fds_to_remap = info; + channel->PrepareToPassRemoteEndpoint(&options->mach_ports_for_rendezvous, + &value); #elif defined(OS_POSIX) channel->PrepareToPassRemoteEndpoint(&options->fds_to_remap, &value); #elif defined(OS_WIN) diff --git a/chromium/mojo/core/mach_port_relay.cc b/chromium/mojo/core/mach_port_relay.cc deleted file mode 100644 index 7d7a42510e4..00000000000 --- a/chromium/mojo/core/mach_port_relay.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2016 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 "mojo/core/mach_port_relay.h" - -#include <mach/mach.h> - -#include <utility> - -#include "base/logging.h" -#include "base/mac/mach_port_util.h" -#include "base/mac/scoped_mach_port.h" -#include "base/metrics/histogram_macros.h" -#include "base/process/process.h" - -namespace mojo { -namespace core { - -namespace { - -// Errors that can occur in the broker (privileged parent) process. -// These match tools/metrics/histograms.xml. -// This enum is append-only. -enum class BrokerUMAError : int { - SUCCESS = 0, - // Couldn't get a task port for the process with a given pid. - ERROR_TASK_FOR_PID = 1, - // Couldn't make a port with receive rights in the destination process. - ERROR_MAKE_RECEIVE_PORT = 2, - // Couldn't change the attributes of a Mach port. - ERROR_SET_ATTRIBUTES = 3, - // Couldn't extract a right from the destination. - ERROR_EXTRACT_DEST_RIGHT = 4, - // Couldn't send a Mach port in a call to mach_msg(). - ERROR_SEND_MACH_PORT = 5, - // Couldn't extract a right from the source. - ERROR_EXTRACT_SOURCE_RIGHT = 6, - ERROR_MAX -}; - -// Errors that can occur in a child process. -// These match tools/metrics/histograms.xml. -// This enum is append-only. -enum class ChildUMAError : int { - SUCCESS = 0, - // An error occurred while trying to receive a Mach port with mach_msg(). - ERROR_RECEIVE_MACH_MESSAGE = 1, - ERROR_MAX -}; - -void ReportBrokerError(BrokerUMAError error) { - UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.BrokerError", - static_cast<int>(error), - static_cast<int>(BrokerUMAError::ERROR_MAX)); -} - -void ReportChildError(ChildUMAError error) { - UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.ChildError", - static_cast<int>(error), - static_cast<int>(ChildUMAError::ERROR_MAX)); -} - -} // namespace - -// static -base::mac::ScopedMachSendRight MachPortRelay::ReceiveSendRight( - base::mac::ScopedMachReceiveRight port) { - // MACH_PORT_NULL doesn't need translation. - if (!port.is_valid()) - return base::mac::ScopedMachSendRight(); - - // Take ownership of the receive right. We only need it to read this single - // send right, then it can be closed. - base::mac::ScopedMachSendRight received_port( - base::ReceiveMachPort(port.get())); - if (!received_port.is_valid()) { - ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE); - DLOG(ERROR) << "Error receiving mach port"; - return base::mac::ScopedMachSendRight(); - } - - ReportChildError(ChildUMAError::SUCCESS); - return received_port; -} - -MachPortRelay::MachPortRelay(base::PortProvider* port_provider) - : port_provider_(port_provider) { - DCHECK(port_provider); - port_provider_->AddObserver(this); -} - -MachPortRelay::~MachPortRelay() { - port_provider_->RemoveObserver(this); -} - -void MachPortRelay::SendPortsToProcess(Channel::Message* message, - base::ProcessHandle process) { - DCHECK(message); - mach_port_t task_port = port_provider_->TaskForPid(process); - - std::vector<PlatformHandleInTransit> handles = message->TakeHandles(); - // Message should have handles, otherwise there's no point in calling this - // function. - DCHECK(!handles.empty()); - for (auto& handle : handles) { - if (!handle.handle().is_valid_mach_port()) - continue; - - if (task_port == MACH_PORT_NULL) { - // Callers check the port provider for the task port before calling this - // function, in order to queue pending messages. Therefore, if this fails, - // it should be considered a genuine, bona fide, electrified, six-car - // error. - ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID); - handle = PlatformHandleInTransit( - PlatformHandle(base::mac::ScopedMachSendRight())); - continue; - } - - mach_port_name_t intermediate_port; - base::MachCreateError error_code; - intermediate_port = base::CreateIntermediateMachPort( - task_port, handle.TakeHandle().TakeMachPort(), &error_code); - if (intermediate_port == MACH_PORT_NULL) { - BrokerUMAError uma_error; - switch (error_code) { - case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT: - uma_error = BrokerUMAError::ERROR_MAKE_RECEIVE_PORT; - break; - case base::MachCreateError::ERROR_SET_ATTRIBUTES: - uma_error = BrokerUMAError::ERROR_SET_ATTRIBUTES; - break; - case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT: - uma_error = BrokerUMAError::ERROR_EXTRACT_DEST_RIGHT; - break; - case base::MachCreateError::ERROR_SEND_MACH_PORT: - uma_error = BrokerUMAError::ERROR_SEND_MACH_PORT; - break; - } - ReportBrokerError(uma_error); - handle = PlatformHandleInTransit( - PlatformHandle(base::mac::ScopedMachSendRight())); - continue; - } - - handle = PlatformHandleInTransit::CreateForMachPortName(intermediate_port); - ReportBrokerError(BrokerUMAError::SUCCESS); - } - message->SetHandles(std::move(handles)); -} - -base::mac::ScopedMachSendRight MachPortRelay::ExtractPort( - mach_port_t port_name, - base::ProcessHandle process) { - // No extraction necessary for MACH_PORT_NULL. - if (port_name == MACH_PORT_NULL) - return base::mac::ScopedMachSendRight(); - - mach_port_t task_port = port_provider_->TaskForPid(process); - if (task_port == MACH_PORT_NULL) { - ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID); - return base::mac::ScopedMachSendRight(); - } - - mach_port_t extracted_right = MACH_PORT_NULL; - mach_msg_type_name_t extracted_right_type; - kern_return_t kr = - mach_port_extract_right(task_port, port_name, MACH_MSG_TYPE_MOVE_SEND, - &extracted_right, &extracted_right_type); - if (kr != KERN_SUCCESS) { - ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT); - return base::mac::ScopedMachSendRight(); - } - - ReportBrokerError(BrokerUMAError::SUCCESS); - DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), - extracted_right_type); - return base::mac::ScopedMachSendRight(extracted_right); -} - -void MachPortRelay::AddObserver(Observer* observer) { - base::AutoLock locker(observers_lock_); - bool inserted = observers_.insert(observer).second; - DCHECK(inserted); -} - -void MachPortRelay::RemoveObserver(Observer* observer) { - base::AutoLock locker(observers_lock_); - observers_.erase(observer); -} - -void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) { - base::AutoLock locker(observers_lock_); - for (auto* observer : observers_) - observer->OnProcessReady(process); -} - -} // namespace core -} // namespace mojo diff --git a/chromium/mojo/core/mach_port_relay.h b/chromium/mojo/core/mach_port_relay.h deleted file mode 100644 index 4cb6de4bd64..00000000000 --- a/chromium/mojo/core/mach_port_relay.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 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 MOJO_CORE_MACH_PORT_RELAY_H_ -#define MOJO_CORE_MACH_PORT_RELAY_H_ - -#include <set> -#include <vector> - -#include "base/macros.h" -#include "base/process/port_provider_mac.h" -#include "base/synchronization/lock.h" -#include "mojo/core/channel.h" - -namespace mojo { -namespace core { - -// The MachPortRelay is used by a privileged process, usually the root process, -// to manipulate Mach ports in a child process. Ports can be added to and -// extracted from a child process that has registered itself with the -// |base::PortProvider| used by this class. -class MachPortRelay : public base::PortProvider::Observer { - public: - class Observer { - public: - // Called by the MachPortRelay to notify observers that a new process is - // ready for Mach ports to be sent/received. There are no guarantees about - // the thread this is called on, including the presence of a MessageLoop. - // Implementations must not call AddObserver() or RemoveObserver() during - // this function, as doing so will deadlock. - virtual void OnProcessReady(base::ProcessHandle process) = 0; - }; - - // Used by a child process to receive Mach ports from a sender (privileged) - // process. The Mach port in |port| is interpreted as an intermediate Mach - // port. It replaces each Mach port with the final Mach port received from the - // intermediate port. This method takes ownership of the intermediate Mach - // port and gives ownership of the final Mach port to the caller. - // - // On failure, returns a null send right. - // - // See SendPortsToProcess() for the definition of intermediate and final Mach - // ports. - static base::mac::ScopedMachSendRight ReceiveSendRight( - base::mac::ScopedMachReceiveRight port); - - explicit MachPortRelay(base::PortProvider* port_provider); - ~MachPortRelay() override; - - // Sends the Mach ports attached to |message| to |process|. - // For each Mach port attached to |message|, a new Mach port, the intermediate - // port, is created in |process|. The message's Mach port is then sent over - // this intermediate port and the message is modified to refer to the name of - // the intermediate port. The Mach port received over the intermediate port in - // the child is referred to as the final Mach port. - // - // All ports in |message|'s set of handles are reset by this call, and all - // port names in the message's header are replaced with the new receive right - // ports. - void SendPortsToProcess(Channel::Message* message, - base::ProcessHandle process); - - // Given the name of a Mach send right within |process|, extracts an owned - // send right ref and returns it. May return a null port on failure. - base::mac::ScopedMachSendRight ExtractPort(mach_port_t port_name, - base::ProcessHandle process); - - // Observer interface. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - - base::PortProvider* port_provider() const { return port_provider_; } - - private: - // base::PortProvider::Observer implementation. - void OnReceivedTaskPort(base::ProcessHandle process) override; - - base::PortProvider* const port_provider_; - - base::Lock observers_lock_; - std::set<Observer*> observers_; - - DISALLOW_COPY_AND_ASSIGN(MachPortRelay); -}; - -} // namespace core -} // namespace mojo - -#endif // MOJO_CORE_MACH_PORT_RELAY_H_ diff --git a/chromium/mojo/core/message_pipe_dispatcher.cc b/chromium/mojo/core/message_pipe_dispatcher.cc index 00fc12e2aa0..482a685c0eb 100644 --- a/chromium/mojo/core/message_pipe_dispatcher.cc +++ b/chromium/mojo/core/message_pipe_dispatcher.cc @@ -158,6 +158,10 @@ MojoResult MessagePipeDispatcher::WriteMessage( return MOJO_RESULT_UNKNOWN; } + // We may need to update anyone watching our signals in case we just exceeded + // the unread message count quota. + base::AutoLock lock(signal_lock_); + watchers_.NotifyState(GetHandleSignalsStateNoLock()); return MOJO_RESULT_OK; } @@ -194,6 +198,8 @@ MojoResult MessagePipeDispatcher::ReadMessage( } MojoResult MessagePipeDispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { + base::AutoLock lock(signal_lock_); + switch (type) { case MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH: if (limit == MOJO_QUOTA_LIMIT_NONE) @@ -209,6 +215,23 @@ MojoResult MessagePipeDispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { receive_queue_memory_size_limit_ = limit; break; + case MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT: + if (limit == MOJO_QUOTA_LIMIT_NONE) { + unread_message_count_limit_.reset(); + node_controller_->node()->SetAcknowledgeRequestInterval(port_, 0); + } else { + unread_message_count_limit_ = limit; + // Setting the acknowledge request interval for the port to half the + // unread quota limit, means the ack roundtrip has half the window to + // catch up with sent messages. In other words, if the producer is + // producing messages at a steady rate of limit/2 packets per message + // round trip or lower, the quota limit won't be exceeded. This is + // assuming the consumer is consuming messages at the same rate. + node_controller_->node()->SetAcknowledgeRequestInterval( + port_, (limit + 1) / 2); + } + break; + default: return MOJO_RESULT_INVALID_ARGUMENT; } @@ -219,6 +242,8 @@ MojoResult MessagePipeDispatcher::SetQuota(MojoQuotaType type, uint64_t limit) { MojoResult MessagePipeDispatcher::QueryQuota(MojoQuotaType type, uint64_t* limit, uint64_t* usage) { + base::AutoLock lock(signal_lock_); + ports::PortStatus port_status; if (node_controller_->node()->GetStatus(port_, &port_status) != ports::OK) { CHECK(in_transit_ || port_transferred_ || port_closed_); @@ -236,6 +261,11 @@ MojoResult MessagePipeDispatcher::QueryQuota(MojoQuotaType type, *usage = port_status.queued_num_bytes; break; + case MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT: + *limit = unread_message_count_limit_.value_or(MOJO_QUOTA_LIMIT_NONE); + *usage = port_status.unacknowledged_message_count; + break; + default: return MOJO_RESULT_INVALID_ARGUMENT; } @@ -384,6 +414,10 @@ HandleSignalsState MessagePipeDispatcher::GetHandleSignalsStateNoLock() const { } else if (receive_queue_memory_size_limit_ && port_status.queued_num_bytes > *receive_queue_memory_size_limit_) { rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; + } else if (unread_message_count_limit_ && + port_status.unacknowledged_message_count > + *unread_message_count_limit_) { + rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; } rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; diff --git a/chromium/mojo/core/message_pipe_dispatcher.h b/chromium/mojo/core/message_pipe_dispatcher.h index 4fef7080997..231cb02162c 100644 --- a/chromium/mojo/core/message_pipe_dispatcher.h +++ b/chromium/mojo/core/message_pipe_dispatcher.h @@ -106,6 +106,7 @@ class MessagePipeDispatcher : public Dispatcher { WatcherSet watchers_; base::Optional<uint64_t> receive_queue_length_limit_; base::Optional<uint64_t> receive_queue_memory_size_limit_; + base::Optional<uint64_t> unread_message_count_limit_; DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher); }; diff --git a/chromium/mojo/core/multiprocess_message_pipe_unittest.cc b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc index ea06608fc21..60332c0716d 100644 --- a/chromium/mojo/core/multiprocess_message_pipe_unittest.cc +++ b/chromium/mojo/core/multiprocess_message_pipe_unittest.cc @@ -18,10 +18,10 @@ #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/string_split.h" +#include "base/test/scoped_task_environment.h" #include "build/build_config.h" #include "mojo/core/handle_signals_state.h" #include "mojo/core/test/mojo_test_base.h" @@ -1274,7 +1274,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient, EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - base::MessageLoop message_loop; + base::test::ScopedTaskEnvironment scoped_task_environment; // Wait on handle 1 using a SimpleWatcher. { diff --git a/chromium/mojo/core/node_channel.cc b/chromium/mojo/core/node_channel.cc index cabbe8809e6..c227a72f690 100644 --- a/chromium/mojo/core/node_channel.cc +++ b/chromium/mojo/core/node_channel.cc @@ -33,11 +33,11 @@ enum class MessageType : uint32_t { REQUEST_PORT_MERGE, REQUEST_INTRODUCTION, INTRODUCE, -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) RELAY_EVENT_MESSAGE, #endif BROADCAST_EVENT, -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) EVENT_MESSAGE_FROM_RELAY, #endif ACCEPT_PEER, @@ -110,7 +110,7 @@ struct IntroductionData { ports::NodeName name; }; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) // This struct is followed by the full payload of a message to be relayed. struct RelayEventMessageData { ports::NodeName destination; @@ -373,7 +373,7 @@ void NodeChannel::Broadcast(Channel::MessagePtr message) { WriteChannelMessage(std::move(broadcast_message)); } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) void NodeChannel::RelayEventMessage(const ports::NodeName& destination, Channel::MessagePtr message) { #if defined(OS_WIN) @@ -437,7 +437,7 @@ void NodeChannel::EventMessageFromRelay(const ports::NodeName& source, relayed_message->SetHandles(message->TakeHandles()); WriteChannelMessage(std::move(relayed_message)); } -#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#endif // defined(OS_WIN) NodeChannel::NodeChannel(Delegate* delegate, ConnectionParams connection_params, @@ -606,7 +606,7 @@ void NodeChannel::OnChannelMessage(const void* payload, break; } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) case MessageType::RELAY_EVENT_MESSAGE: { base::ProcessHandle from_process; { @@ -630,9 +630,6 @@ void NodeChannel::OnChannelMessage(const void* payload, DLOG(ERROR) << "Dropping invalid relay message."; break; } -#if defined(OS_MACOSX) && !defined(OS_IOS) - message->SetHandles(std::move(handles)); -#endif delegate_->OnRelayEventMessage(remote_node_name_, from_process, data->destination, std::move(message)); return; @@ -656,7 +653,7 @@ void NodeChannel::OnChannelMessage(const void* payload, return; } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) case MessageType::EVENT_MESSAGE_FROM_RELAY: const EventMessageFromRelayData* data; if (GetMessagePayload(payload, payload_size, &data)) { @@ -676,7 +673,7 @@ void NodeChannel::OnChannelMessage(const void* payload, } break; -#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#endif // defined(OS_WIN) case MessageType::ACCEPT_PEER: { const AcceptPeerData* data; diff --git a/chromium/mojo/core/node_channel.h b/chromium/mojo/core/node_channel.h index 55486007743..4297307847d 100644 --- a/chromium/mojo/core/node_channel.h +++ b/chromium/mojo/core/node_channel.h @@ -59,7 +59,7 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, PlatformHandle channel_handle) = 0; virtual void OnBroadcast(const ports::NodeName& from_node, Channel::MessagePtr message) = 0; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) virtual void OnRelayEventMessage(const ports::NodeName& from_node, base::ProcessHandle from_process, const ports::NodeName& destination, @@ -133,7 +133,7 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, void SendChannelMessage(Channel::MessagePtr message); void Broadcast(Channel::MessagePtr message); -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) // Relay the message to the specified node via this channel. This is used to // pass windows handles between two processes that do not have permission to // duplicate handles into the other's address space. The relay process is diff --git a/chromium/mojo/core/node_channel_fuzzer.cc b/chromium/mojo/core/node_channel_fuzzer.cc index 295f7a9e53e..4cce8445512 100644 --- a/chromium/mojo/core/node_channel_fuzzer.cc +++ b/chromium/mojo/core/node_channel_fuzzer.cc @@ -57,7 +57,7 @@ class FakeNodeChannelDelegate : public NodeChannel::Delegate { mojo::PlatformHandle channel_handle) override {} void OnBroadcast(const ports::NodeName& from_node, Channel::MessagePtr message) override {} -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) void OnRelayEventMessage(const ports::NodeName& from_node, base::ProcessHandle from_process, const ports::NodeName& destination, diff --git a/chromium/mojo/core/node_controller.cc b/chromium/mojo/core/node_controller.cc index 5a796e95b52..e919ccd0b75 100644 --- a/chromium/mojo/core/node_controller.cc +++ b/chromium/mojo/core/node_controller.cc @@ -32,10 +32,6 @@ #include <windows.h> #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include "mojo/core/mach_port_relay.h" -#endif - #if !defined(OS_NACL) #include "crypto/random.h" #endif @@ -158,14 +154,6 @@ NodeController::NodeController(Core* core) DVLOG(1) << "Initializing node " << name_; } -#if defined(OS_MACOSX) && !defined(OS_IOS) -void NodeController::CreateMachPortRelay(base::PortProvider* port_provider) { - base::AutoLock lock(mach_port_relay_lock_); - DCHECK(!mach_port_relay_); - mach_port_relay_.reset(new MachPortRelay(port_provider)); -} -#endif - void NodeController::SetIOTaskRunner( scoped_refptr<base::TaskRunner> task_runner) { io_task_runner_ = task_runner; @@ -612,28 +600,6 @@ void NodeController::SendPeerEvent(const ports::NodeName& name, return; } } -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (event_message->has_mach_ports()) { - // Messages containing Mach ports are always routed through the broker, even - // if the broker process is the intended recipient. - bool use_broker = false; - if (!GetConfiguration().is_broker_process) { - base::AutoLock lock(inviter_lock_); - use_broker = (bootstrap_inviter_channel_ || - inviter_name_ != ports::kInvalidNodeName); - } - - if (use_broker) { - scoped_refptr<NodeChannel> broker = GetBrokerChannel(); - if (broker) { - broker->RelayEventMessage(name, std::move(event_message)); - } else { - base::AutoLock lock(broker_lock_); - pending_relay_messages_[name].emplace(std::move(event_message)); - } - return; - } - } #endif // defined(OS_WIN) if (peer) { @@ -969,7 +935,7 @@ void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, pending_broker_clients.pop(); } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) // Have the broker relay any messages we have waiting. for (auto& entry : pending_relay_messages) { const ports::NodeName& destination = entry.first; @@ -1128,7 +1094,7 @@ void NodeController::OnBroadcast(const ports::NodeName& from_node, } } -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) void NodeController::OnRelayEventMessage(const ports::NodeName& from_node, base::ProcessHandle from_process, const ports::NodeName& destination, @@ -1221,20 +1187,6 @@ void NodeController::OnChannelError(const ports::NodeName& from_node, } } -#if defined(OS_MACOSX) && !defined(OS_IOS) -MachPortRelay* NodeController::GetMachPortRelay() { - { - base::AutoLock lock(inviter_lock_); - // Return null if we're not the root. - if (bootstrap_inviter_channel_ || inviter_name_ != ports::kInvalidNodeName) - return nullptr; - } - - base::AutoLock lock(mach_port_relay_lock_); - return mach_port_relay_.get(); -} -#endif - void NodeController::CancelPendingPortMerges() { std::vector<ports::PortRef> ports_to_close; diff --git a/chromium/mojo/core/node_controller.h b/chromium/mojo/core/node_controller.h index 0b44a6a2c4a..b0338f78696 100644 --- a/chromium/mojo/core/node_controller.h +++ b/chromium/mojo/core/node_controller.h @@ -31,16 +31,11 @@ #include "mojo/core/system_impl_export.h" #include "mojo/public/cpp/platform/platform_handle.h" -namespace base { -class PortProvider; -} - namespace mojo { namespace core { class Broker; class Core; -class MachPortRelay; // The owner of ports::Node which facilitates core EDK implementation. All // public interface methods are safe to call from any thread. @@ -66,11 +61,6 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, return io_task_runner_; } -#if defined(OS_MACOSX) && !defined(OS_IOS) - // Create the relay used to transfer mach ports between processes. - void CreateMachPortRelay(base::PortProvider* port_provider); -#endif - // Called exactly once, shortly after construction, and before any other // methods are called on this object. void SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner); @@ -217,7 +207,7 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, PlatformHandle channel_handle) override; void OnBroadcast(const ports::NodeName& from_node, Channel::MessagePtr message) override; -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) void OnRelayEventMessage(const ports::NodeName& from_node, base::ProcessHandle from_process, const ports::NodeName& destination, @@ -233,10 +223,6 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, void OnChannelError(const ports::NodeName& from_node, NodeChannel* channel) override; -#if defined(OS_MACOSX) && !defined(OS_IOS) - MachPortRelay* GetMachPortRelay(); -#endif - // Cancels all pending port merges. These are merges which are supposed to // be requested from the inviter ASAP, and they may be cancelled if the // connection to the inviter is broken or never established. @@ -335,12 +321,6 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, std::unique_ptr<Broker> broker_; #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) - base::Lock mach_port_relay_lock_; - // Relay for transferring mach ports to/from broker clients. - std::unique_ptr<MachPortRelay> mach_port_relay_; -#endif - DISALLOW_COPY_AND_ASSIGN(NodeController); }; diff --git a/chromium/mojo/core/platform_handle_in_transit.cc b/chromium/mojo/core/platform_handle_in_transit.cc index 9ee60d86c3d..4e75480a1bf 100644 --- a/chromium/mojo/core/platform_handle_in_transit.cc +++ b/chromium/mojo/core/platform_handle_in_transit.cc @@ -89,9 +89,6 @@ PlatformHandleInTransit& PlatformHandleInTransit::operator=( remote_handle_ = INVALID_HANDLE_VALUE; std::swap(remote_handle_, other.remote_handle_); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - mach_port_name_ = MACH_PORT_NULL; - std::swap(mach_port_name_, other.mach_port_name_); #endif handle_ = std::move(other.handle_); owning_process_ = std::move(other.owning_process_); @@ -152,20 +149,5 @@ PlatformHandle PlatformHandleInTransit::TakeIncomingRemoteHandle( } #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) -// static -PlatformHandleInTransit PlatformHandleInTransit::CreateForMachPortName( - mach_port_t name) { - if (name == MACH_PORT_NULL) { - return PlatformHandleInTransit( - PlatformHandle(base::mac::ScopedMachSendRight())); - } - - PlatformHandleInTransit handle; - handle.mach_port_name_ = name; - return handle; -} -#endif - } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/platform_handle_in_transit.h b/chromium/mojo/core/platform_handle_in_transit.h index ebe49e262e4..b69d3b6788c 100644 --- a/chromium/mojo/core/platform_handle_in_transit.h +++ b/chromium/mojo/core/platform_handle_in_transit.h @@ -10,10 +10,6 @@ #include "mojo/core/scoped_process_handle.h" #include "mojo/public/cpp/platform/platform_handle.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include <mach/mach.h> -#endif - #if defined(OS_WIN) #include <windows.h> #endif @@ -89,19 +85,6 @@ class PlatformHandleInTransit { base::ProcessHandle owning_process); #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) - // Creates a special wrapper holding an unowned Mach port name. This may refer - // to a send or receive right in a remote task (process), and is used for - // cases where message must retain such an object as one of its attached - // handles. We're OK for now with leaking in any scenario where a lack of - // strict ownership could cause leakage. See https://crbug.com/855930 for more - // details. - static PlatformHandleInTransit CreateForMachPortName(mach_port_t name); - - bool is_mach_port_name() const { return mach_port_name_ != MACH_PORT_NULL; } - mach_port_t mach_port_name() const { return mach_port_name_; } -#endif - private: #if defined(OS_WIN) // We don't use a ScopedHandle (or, by extension, PlatformHandle) here because @@ -114,10 +97,6 @@ class PlatformHandleInTransit { PlatformHandle handle_; ScopedProcessHandle owning_process_; -#if defined(OS_MACOSX) && !defined(OS_IOS) - mach_port_t mach_port_name_ = MACH_PORT_NULL; -#endif - DISALLOW_COPY_AND_ASSIGN(PlatformHandleInTransit); }; diff --git a/chromium/mojo/core/ports/event.cc b/chromium/mojo/core/ports/event.cc index f3cf74edbdd..a9bec670cb4 100644 --- a/chromium/mojo/core/ports/event.cc +++ b/chromium/mojo/core/ports/event.cc @@ -52,6 +52,14 @@ struct MergePortEventData { Event::PortDescriptor new_port_descriptor; }; +struct UserMessageReadAckRequestEventData { + uint64_t sequence_num_to_acknowledge; +}; + +struct UserMessageReadAckEventData { + uint64_t sequence_num_acknowledged; +}; + #pragma pack(pop) static_assert(sizeof(Event::PortDescriptor) % kPortsMessageAlignment == 0, @@ -75,6 +83,14 @@ static_assert(sizeof(ObserveClosureEventData) % kPortsMessageAlignment == 0, static_assert(sizeof(MergePortEventData) % kPortsMessageAlignment == 0, "Invalid MergePortEventData size."); +static_assert(sizeof(UserMessageReadAckRequestEventData) % + kPortsMessageAlignment == + 0, + "Invalid UserMessageReadAckRequestEventData size."); + +static_assert(sizeof(UserMessageReadAckEventData) % kPortsMessageAlignment == 0, + "Invalid UserMessageReadAckEventData size."); + } // namespace Event::PortDescriptor::PortDescriptor() { @@ -378,6 +394,70 @@ void MergePortEvent::SerializeData(void* buffer) const { data->new_port_descriptor = new_port_descriptor_; } +UserMessageReadAckRequestEvent::UserMessageReadAckRequestEvent( + const PortName& port_name, + uint64_t sequence_num_to_acknowledge) + : Event(Type::kUserMessageReadAckRequest, port_name), + sequence_num_to_acknowledge_(sequence_num_to_acknowledge) { + DCHECK(sequence_num_to_acknowledge); +} + +UserMessageReadAckRequestEvent::~UserMessageReadAckRequestEvent() = default; + +// static +ScopedEvent UserMessageReadAckRequestEvent::Deserialize( + const PortName& port_name, + const void* buffer, + size_t num_bytes) { + if (num_bytes < sizeof(UserMessageReadAckRequestEventData)) + return nullptr; + + const auto* data = + static_cast<const UserMessageReadAckRequestEventData*>(buffer); + return std::make_unique<UserMessageReadAckRequestEvent>( + port_name, data->sequence_num_to_acknowledge); +} + +size_t UserMessageReadAckRequestEvent::GetSerializedDataSize() const { + return sizeof(UserMessageReadAckRequestEventData); +} + +void UserMessageReadAckRequestEvent::SerializeData(void* buffer) const { + auto* data = static_cast<UserMessageReadAckRequestEventData*>(buffer); + data->sequence_num_to_acknowledge = sequence_num_to_acknowledge_; +} + +UserMessageReadAckEvent::UserMessageReadAckEvent( + const PortName& port_name, + uint64_t sequence_num_acknowledged) + : Event(Type::kUserMessageReadAck, port_name), + sequence_num_acknowledged_(sequence_num_acknowledged) { + DCHECK(sequence_num_acknowledged); +} + +UserMessageReadAckEvent::~UserMessageReadAckEvent() = default; + +// static +ScopedEvent UserMessageReadAckEvent::Deserialize(const PortName& port_name, + const void* buffer, + size_t num_bytes) { + if (num_bytes < sizeof(UserMessageReadAckEventData)) + return nullptr; + + const auto* data = static_cast<const UserMessageReadAckEventData*>(buffer); + return std::make_unique<UserMessageReadAckEvent>( + port_name, data->sequence_num_acknowledged); +} + +size_t UserMessageReadAckEvent::GetSerializedDataSize() const { + return sizeof(UserMessageReadAckEventData); +} + +void UserMessageReadAckEvent::SerializeData(void* buffer) const { + auto* data = static_cast<UserMessageReadAckEventData*>(buffer); + data->sequence_num_acknowledged = sequence_num_acknowledged_; +} + } // namespace ports } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/ports/event.h b/chromium/mojo/core/ports/event.h index c9a7d6a733a..55c081e3e81 100644 --- a/chromium/mojo/core/ports/event.h +++ b/chromium/mojo/core/ports/event.h @@ -56,6 +56,13 @@ class COMPONENT_EXPORT(MOJO_CORE_PORTS) Event { // Used to request the merging of two routes via two sacrificial receiving // ports, one from each route. kMergePort, + + // Used to request that the conjugate port acknowledges read messages by + // sending back a UserMessageReadAck. + kUserMessageReadAckRequest, + + // Used to acknowledge read messages to the conjugate. + kUserMessageReadAck, }; #pragma pack(push, 1) @@ -277,6 +284,49 @@ class COMPONENT_EXPORT(MOJO_CORE_PORTS) MergePortEvent : public Event { DISALLOW_COPY_AND_ASSIGN(MergePortEvent); }; +class COMPONENT_EXPORT(MOJO_CORE_PORTS) UserMessageReadAckRequestEvent + : public Event { + public: + UserMessageReadAckRequestEvent(const PortName& port_name, + uint64_t sequence_num_to_acknowledge); + ~UserMessageReadAckRequestEvent() override; + + uint64_t sequence_num_to_acknowledge() const { + return sequence_num_to_acknowledge_; + } + + static ScopedEvent Deserialize(const PortName& port_name, + const void* buffer, + size_t num_bytes); + + private: + size_t GetSerializedDataSize() const override; + void SerializeData(void* buffer) const override; + + uint64_t sequence_num_to_acknowledge_; +}; + +class COMPONENT_EXPORT(MOJO_CORE_PORTS) UserMessageReadAckEvent : public Event { + public: + UserMessageReadAckEvent(const PortName& port_name, + uint64_t sequence_num_acknowledged); + ~UserMessageReadAckEvent() override; + + uint64_t sequence_num_acknowledged() const { + return sequence_num_acknowledged_; + } + + static ScopedEvent Deserialize(const PortName& port_name, + const void* buffer, + size_t num_bytes); + + private: + size_t GetSerializedDataSize() const override; + void SerializeData(void* buffer) const override; + + uint64_t sequence_num_acknowledged_; +}; + } // namespace ports } // namespace core } // namespace mojo diff --git a/chromium/mojo/core/ports/node.cc b/chromium/mojo/core/ports/node.cc index 3f24c97defb..c895dee816d 100644 --- a/chromium/mojo/core/ports/node.cc +++ b/chromium/mojo/core/ports/node.cc @@ -313,6 +313,10 @@ int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) { port_status->queued_message_count = port->message_queue.queued_message_count(); port_status->queued_num_bytes = port->message_queue.queued_num_bytes(); + port_status->unacknowledged_message_count = + port->next_sequence_num_to_send - port->last_sequence_num_acknowledged - + 1; + return OK; } @@ -323,6 +327,8 @@ int Node::GetMessage(const PortRef& port_ref, DVLOG(4) << "GetMessage for " << port_ref.name() << "@" << name_; + NodeName peer_node_name; + ScopedEvent ack_event; { SinglePortLocker locker(&port_ref); auto* port = locker.port(); @@ -338,8 +344,17 @@ int Node::GetMessage(const PortRef& port_ref, return ERROR_PORT_PEER_CLOSED; port->message_queue.GetNextMessage(message, filter); + if (*message && + (*message)->sequence_num() == port->sequence_num_to_acknowledge) { + peer_node_name = port->peer_node_name; + ack_event = std::make_unique<UserMessageReadAckEvent>( + port->peer_port_name, port->sequence_num_to_acknowledge); + } } + if (ack_event) + delegate_->ForwardEvent(peer_node_name, std::move(ack_event)); + // Allow referenced ports to trigger PortStatusChanged calls. if (*message) { for (size_t i = 0; i < (*message)->num_ports(); ++i) { @@ -381,6 +396,35 @@ int Node::SendUserMessage(const PortRef& port_ref, return rv; } +int Node::SetAcknowledgeRequestInterval( + const PortRef& port_ref, + uint64_t sequence_num_acknowledge_interval) { + NodeName peer_node_name; + PortName peer_port_name; + uint64_t sequence_num_to_request_ack = 0; + { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + if (port->state != Port::kReceiving) + return ERROR_PORT_STATE_UNEXPECTED; + + port->sequence_num_acknowledge_interval = sequence_num_acknowledge_interval; + if (!sequence_num_acknowledge_interval) + return OK; + + peer_node_name = port->peer_node_name; + peer_port_name = port->peer_port_name; + + sequence_num_to_request_ack = port->last_sequence_num_acknowledged + + sequence_num_acknowledge_interval; + } + + delegate_->ForwardEvent(peer_node_name, + std::make_unique<UserMessageReadAckRequestEvent>( + peer_port_name, sequence_num_to_request_ack)); + return OK; +} + int Node::AcceptEvent(ScopedEvent event) { switch (event->type()) { case Event::Type::kUserMessage: @@ -395,6 +439,11 @@ int Node::AcceptEvent(ScopedEvent event) { return OnObserveClosure(Event::Cast<ObserveClosureEvent>(&event)); case Event::Type::kMergePort: return OnMergePort(Event::Cast<MergePortEvent>(&event)); + case Event::Type::kUserMessageReadAckRequest: + return OnUserMessageReadAckRequest( + Event::Cast<UserMessageReadAckRequestEvent>(&event)); + case Event::Type::kUserMessageReadAck: + return OnUserMessageReadAck(Event::Cast<UserMessageReadAckEvent>(&event)); } return OOPS(ERROR_NOT_IMPLEMENTED); } @@ -593,7 +642,7 @@ int Node::OnObserveProxy(std::unique_ptr<ObserveProxyEvent> event) { << event->proxy_target_port_name() << "@" << event->proxy_target_node_name(); - bool update_status = false; + bool peer_changed = false; ScopedEvent event_to_forward; NodeName event_target_node; { @@ -613,7 +662,7 @@ int Node::OnObserveProxy(std::unique_ptr<ObserveProxyEvent> event) { event_target_node = event->proxy_node_name(); event_to_forward = std::make_unique<ObserveProxyAckEvent>( event->proxy_port_name(), port->next_sequence_num_to_send - 1); - update_status = true; + peer_changed = true; DVLOG(2) << "Forwarding ObserveProxyAck from " << event->port_name() << "@" << name_ << " to " << event->proxy_port_name() << "@" << event_target_node; @@ -648,8 +697,14 @@ int Node::OnObserveProxy(std::unique_ptr<ObserveProxyEvent> event) { if (event_to_forward) delegate_->ForwardEvent(event_target_node, std::move(event_to_forward)); - if (update_status) + if (peer_changed) { + // Re-send ack and/or ack requests, as the previous peer proxy may not have + // forwarded the previous request before it died. + MaybeResendAck(port_ref); + MaybeResendAckRequest(port_ref); + delegate_->PortStatusChanged(port_ref); + } return OK; } @@ -732,6 +787,11 @@ int Node::OnObserveClosure(std::unique_ptr<ObserveClosureEvent> event) { // may be semantically confusing since the forwarding port is not actually // closed. Consider replacing this with a new event type. event->set_last_sequence_num(port->next_sequence_num_to_send - 1); + + // Treat the closure as an acknowledge that all sent messages have been + // read from the other end. + port->last_sequence_num_acknowledged = + port->next_sequence_num_to_send - 1; } else { // We haven't yet reached the receiving peer of the closed port, so we'll // forward the message along as-is. @@ -799,6 +859,114 @@ int Node::OnMergePort(std::unique_ptr<MergePortEvent> event) { false /* allow_close_on_bad_state */); } +int Node::OnUserMessageReadAckRequest( + std::unique_ptr<UserMessageReadAckRequestEvent> event) { + PortRef port_ref; + GetPort(event->port_name(), &port_ref); + + DVLOG(1) << "AckRequest " << port_ref.name() << "@" << name_ << " sequence " + << event->sequence_num_to_acknowledge(); + + if (!port_ref.is_valid()) + return ERROR_PORT_UNKNOWN; + + NodeName peer_node_name; + std::unique_ptr<Event> event_to_send; + { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + + peer_node_name = port->peer_node_name; + if (port->state == Port::kProxying) { + // Proxies simply forward the ack request to their peer. + event->set_port_name(port->peer_port_name); + event_to_send = std::move(event); + } else { + uint64_t current_sequence_num = + port->message_queue.next_sequence_num() - 1; + // Either this is requesting an ack for a sequence number already read, or + // else for a sequence number that is yet to be read. + if (current_sequence_num >= event->sequence_num_to_acknowledge()) { + // If the current sequence number to read already exceeds the ack + // request, send an ack immediately. + event_to_send = std::make_unique<UserMessageReadAckEvent>( + port->peer_port_name, current_sequence_num); + + // This might be a late or duplicate acknowledge request, that's + // requesting acknowledge for an already read message. There may already + // have been a request for future reads, so take care not to back up + // the requested acknowledge counter. + if (current_sequence_num > port->sequence_num_to_acknowledge) + port->sequence_num_to_acknowledge = current_sequence_num; + } else { + // This is request to ack a sequence number that hasn't been read yet. + // The state of the port can either be that it already has a + // future-requested ack, or not. Because ack requests aren't guaranteed + // to arrive in order, store the earlier of the current queued request + // and the new one, if one was already requested. + bool has_queued_ack_request = + port->sequence_num_to_acknowledge > current_sequence_num; + if (!has_queued_ack_request || + port->sequence_num_to_acknowledge > + event->sequence_num_to_acknowledge()) { + port->sequence_num_to_acknowledge = + event->sequence_num_to_acknowledge(); + } + return OK; + } + } + } + + delegate_->ForwardEvent(peer_node_name, std::move(event_to_send)); + + return OK; +} + +int Node::OnUserMessageReadAck(std::unique_ptr<UserMessageReadAckEvent> event) { + PortRef port_ref; + GetPort(event->port_name(), &port_ref); + + DVLOG(1) << "Acknowledge " << port_ref.name() << "@" << name_ << " sequence " + << event->sequence_num_acknowledged(); + + NodeName peer_node_name; + ScopedEvent ack_request_event; + if (port_ref.is_valid()) { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + + if (event->sequence_num_acknowledged() >= port->next_sequence_num_to_send) { + // TODO(http://crbug.com/980952): This is a malformed event. + // This could return a new error "ERROR_MALFORMED_EVENT" which the + // delegate could use as a signal to drop the peer node. + return OK; + } + + // Keep the largest acknowledge seen. + if (event->sequence_num_acknowledged() <= + port->last_sequence_num_acknowledged) { + // The acknowledge was late or a duplicate, it's safe to ignore it. + return OK; + } + + port->last_sequence_num_acknowledged = event->sequence_num_acknowledged(); + // Send another ack request if the interval is non-zero and the peer has + // not been closed. + if (port->sequence_num_acknowledge_interval && !port->peer_closed) { + peer_node_name = port->peer_node_name; + ack_request_event = std::make_unique<UserMessageReadAckRequestEvent>( + port->peer_port_name, port->last_sequence_num_acknowledged + + port->sequence_num_acknowledge_interval); + } + } + if (ack_request_event) + delegate_->ForwardEvent(peer_node_name, std::move(ack_request_event)); + + delegate_->PortStatusChanged(port_ref); + + return OK; +} + int Node::AddPortWithName(const PortName& port_name, scoped_refptr<Port> port) { PortLocker::AssertNoPortsLockedOnCurrentThread(); base::AutoLock lock(ports_lock_); @@ -1207,6 +1375,9 @@ int Node::BeginProxying(const PortRef& port_ref) { if (rv != OK) return rv; + // Forward any pending acknowledge request. + MaybeForwardAckRequest(port_ref); + bool try_remove_proxy_immediately; ScopedEvent closure_event; NodeName closure_target_node; @@ -1484,6 +1655,71 @@ void Node::SwapPortPeers(const PortName& port0_name, std::swap(port0->peer_port_name, port1->peer_port_name); } +void Node::MaybeResendAckRequest(const PortRef& port_ref) { + NodeName peer_node_name; + ScopedEvent ack_request_event; + { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + if (port->state != Port::kReceiving) + return; + + if (!port->sequence_num_acknowledge_interval) + return; + + peer_node_name = port->peer_node_name; + ack_request_event = std::make_unique<UserMessageReadAckRequestEvent>( + port->peer_port_name, port->last_sequence_num_acknowledged + + port->sequence_num_acknowledge_interval); + } + + delegate_->ForwardEvent(peer_node_name, std::move(ack_request_event)); +} + +void Node::MaybeForwardAckRequest(const PortRef& port_ref) { + NodeName peer_node_name; + ScopedEvent ack_request_event; + { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + if (port->state != Port::kProxying) + return; + + if (!port->sequence_num_to_acknowledge) + return; + + peer_node_name = port->peer_node_name; + ack_request_event = std::make_unique<UserMessageReadAckRequestEvent>( + port->peer_port_name, port->sequence_num_to_acknowledge); + + port->sequence_num_to_acknowledge = 0; + } + + delegate_->ForwardEvent(peer_node_name, std::move(ack_request_event)); +} + +void Node::MaybeResendAck(const PortRef& port_ref) { + NodeName peer_node_name; + ScopedEvent ack_event; + { + SinglePortLocker locker(&port_ref); + auto* port = locker.port(); + if (port->state != Port::kReceiving) + return; + + uint64_t last_sequence_num_read = + port->message_queue.next_sequence_num() - 1; + if (!port->sequence_num_to_acknowledge || !last_sequence_num_read) + return; + + peer_node_name = port->peer_node_name; + ack_event = std::make_unique<UserMessageReadAckEvent>( + port->peer_port_name, last_sequence_num_read); + } + + delegate_->ForwardEvent(peer_node_name, std::move(ack_event)); +} + Node::DelegateHolder::DelegateHolder(Node* node, NodeDelegate* delegate) : node_(node), delegate_(delegate) { DCHECK(node_); diff --git a/chromium/mojo/core/ports/node.h b/chromium/mojo/core/ports/node.h index 9c771eb2342..e806e1d918c 100644 --- a/chromium/mojo/core/ports/node.h +++ b/chromium/mojo/core/ports/node.h @@ -44,6 +44,7 @@ struct PortStatus { bool peer_remote; size_t queued_message_count; size_t queued_num_bytes; + size_t unacknowledged_message_count; }; class MessageFilter; @@ -140,6 +141,16 @@ class COMPONENT_EXPORT(MOJO_CORE_PORTS) Node { int SendUserMessage(const PortRef& port_ref, std::unique_ptr<UserMessageEvent> message); + // Makes the port send acknowledge requests to its conjugate to acknowledge + // at least every |sequence_number_acknowledge_interval| messages as they're + // read from the conjugate. The number of unacknowledged messages is exposed + // in the |unacknowledged_message_count| field of PortStatus. This allows + // bounding the number of unread and/or in-transit messages from this port + // to its conjugate between zero and |unacknowledged_message_count|. + int SetAcknowledgeRequestInterval( + const PortRef& port_ref, + uint64_t sequence_number_acknowledge_interval); + // Corresponding to NodeDelegate::ForwardEvent. int AcceptEvent(ScopedEvent event); @@ -200,6 +211,9 @@ class COMPONENT_EXPORT(MOJO_CORE_PORTS) Node { int OnObserveProxyAck(std::unique_ptr<ObserveProxyAckEvent> event); int OnObserveClosure(std::unique_ptr<ObserveClosureEvent> event); int OnMergePort(std::unique_ptr<MergePortEvent> event); + int OnUserMessageReadAckRequest( + std::unique_ptr<UserMessageReadAckRequestEvent> event); + int OnUserMessageReadAck(std::unique_ptr<UserMessageReadAckEvent> event); int AddPortWithName(const PortName& port_name, scoped_refptr<Port> port); void ErasePort(const PortName& port_name); @@ -249,6 +263,21 @@ class COMPONENT_EXPORT(MOJO_CORE_PORTS) Node { const PortName& port1_name, Port* port1); + // Sends an acknowledge request to the peer if the port has a non-zero + // |sequence_num_acknowledge_interval|. This needs to be done when the port's + // peer changes, as the previous peer proxy may not have forwarded any prior + // acknowledge request before deleting itself. + void MaybeResendAckRequest(const PortRef& port_ref); + + // Forwards a stored acknowledge request to the peer if the proxy has a + // non-zero |sequence_num_acknowledge_interval|. + void MaybeForwardAckRequest(const PortRef& port_ref); + + // Sends an acknowledge of the most recently read sequence number to the peer + // if any messages have been read, and the port has a non-zero + // |sequence_num_to_acknowledge|. + void MaybeResendAck(const PortRef& port_ref); + const NodeName name_; const DelegateHolder delegate_; diff --git a/chromium/mojo/core/ports/port.cc b/chromium/mojo/core/ports/port.cc index 71869790218..c3ead1ecadf 100644 --- a/chromium/mojo/core/ports/port.cc +++ b/chromium/mojo/core/ports/port.cc @@ -12,7 +12,10 @@ Port::Port(uint64_t next_sequence_num_to_send, uint64_t next_sequence_num_to_receive) : state(kUninitialized), next_sequence_num_to_send(next_sequence_num_to_send), + last_sequence_num_acknowledged(next_sequence_num_to_send - 1), + sequence_num_acknowledge_interval(0), last_sequence_num_to_receive(0), + sequence_num_to_acknowledge(0), message_queue(next_sequence_num_to_receive), remove_proxy_on_last_message(false), peer_closed(false) {} diff --git a/chromium/mojo/core/ports/port.h b/chromium/mojo/core/ports/port.h index d1a825e2c62..cc36568183a 100644 --- a/chromium/mojo/core/ports/port.h +++ b/chromium/mojo/core/ports/port.h @@ -110,12 +110,30 @@ class Port : public base::RefCountedThreadSafe<Port> { // originating from this port. uint64_t next_sequence_num_to_send; + // The largest acknowledged user message event sequence number. + uint64_t last_sequence_num_acknowledged; + + // The interval for which acknowledge requests will be sent. A value of N will + // cause an acknowledge request for |last_sequence_num_acknowledged| + N when + // initially set and on received acknowledge. This means that the lower bound + // for unread or in-transit messages is |next_sequence_num_to_send| - + // |last_sequence_num_acknowledged| + |sequence_number_acknowledge_interval|. + // If zero, no acknowledge requests are sent. + uint64_t sequence_num_acknowledge_interval; + // The sequence number of the last message this Port should ever expect to // receive in its lifetime. May be used to determine that a proxying port is // ready to be destroyed or that a receiving port's conjugate has been closed // and we know the sequence number of the last message it sent. uint64_t last_sequence_num_to_receive; + // The sequence number of the message for which this Port should send an + // acknowledge message. In the buffering state, holds the acknowledge request + // value that is forwarded to the peer on transition to proxying. + // This is zero in any port that's never received an acknowledge request, and + // in proxies that have forwarded a stored acknowledge. + uint64_t sequence_num_to_acknowledge; + // The queue of incoming user messages received by this Port. Only non-empty // for buffering or receiving Ports. When a buffering port enters the proxying // state, it flushes its queue and the proxy then bypasses the queue diff --git a/chromium/mojo/core/ports/ports_unittest.cc b/chromium/mojo/core/ports/ports_unittest.cc index e3a2a085723..40c278b3a8e 100644 --- a/chromium/mojo/core/ports/ports_unittest.cc +++ b/chromium/mojo/core/ports/ports_unittest.cc @@ -139,6 +139,15 @@ class TestNode : public NodeDelegate { return node_.SendUserMessage(port, NewUserMessageEvent(s, 0)); } + int SendMultipleMessages(const PortRef& port, size_t num_messages) { + for (size_t i = 0; i < num_messages; ++i) { + int result = SendStringMessage(port, ""); + if (result != OK) + return result; + } + return OK; + } + int SendStringMessageWithPort(const PortRef& port, const std::string& s, const PortName& sent_port_name) { @@ -167,6 +176,15 @@ class TestNode : public NodeDelegate { return node_.GetMessage(port, message, nullptr) == OK && *message; } + bool ReadMultipleMessages(const PortRef& port, size_t num_messages) { + for (size_t i = 0; i < num_messages; ++i) { + ScopedMessage message; + if (!ReadMessage(port, &message)) + return false; + } + return true; + } + bool GetSavedMessage(ScopedMessage* message) { base::AutoLock lock(lock_); if (saved_messages_.empty()) { @@ -240,6 +258,14 @@ class TestNode : public NodeDelegate { } } + uint64_t GetUnacknowledgedMessageCount(const PortRef& port_ref) { + PortStatus status; + if (node_.GetStatus(port_ref, &status) != OK) + return 0; + + return status.unacknowledged_message_count; + } + private: void ProcessEvents() { for (;;) { @@ -1638,6 +1664,72 @@ TEST_F(PortsTest, RetransmitUserMessageEvents) { EXPECT_EQ(OK, node0.node().ClosePort(b)); } +TEST_F(PortsTest, SetAcknowledgeRequestInterval) { + TestNode node0(0); + AddNode(&node0); + + PortRef a0, a1; + EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); + EXPECT_EQ(0u, node0.GetUnacknowledgedMessageCount(a0)); + + // Send a batch of messages. + EXPECT_EQ(OK, node0.SendMultipleMessages(a0, 15)); + EXPECT_EQ(15u, node0.GetUnacknowledgedMessageCount(a0)); + EXPECT_TRUE(node0.ReadMultipleMessages(a1, 5)); + WaitForIdle(); + EXPECT_EQ(15u, node0.GetUnacknowledgedMessageCount(a0)); + + // Set to acknowledge every read message, and validate that already-read + // messages are acknowledged. + EXPECT_EQ(OK, node0.node().SetAcknowledgeRequestInterval(a0, 1)); + WaitForIdle(); + EXPECT_EQ(10u, node0.GetUnacknowledgedMessageCount(a0)); + + // Read a third of the messages from the other end. + EXPECT_TRUE(node0.ReadMultipleMessages(a1, 5)); + WaitForIdle(); + + EXPECT_EQ(5u, node0.GetUnacknowledgedMessageCount(a0)); + + TestNode node1(1); + AddNode(&node1); + + // Transfer a1 across to node1. + PortRef x0, x1; + CreatePortPair(&node0, &x0, &node1, &x1); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a1)); + WaitForIdle(); + + ScopedMessage message; + ASSERT_TRUE(node1.ReadMessage(x1, &message)); + ASSERT_EQ(1u, message->num_ports()); + ASSERT_EQ(OK, node1.node().GetPort(message->ports()[0], &a1)); + + // Read the last third of the messages from the transferred node, and + // validate that the unacknowledge message count updates correctly. + EXPECT_TRUE(node1.ReadMultipleMessages(a1, 5)); + WaitForIdle(); + EXPECT_EQ(0u, node0.GetUnacknowledgedMessageCount(a0)); + + // Turn the acknowledges down and validate that they don't go on indefinitely. + EXPECT_EQ(OK, node0.node().SetAcknowledgeRequestInterval(a0, 0)); + EXPECT_EQ(OK, node0.SendMultipleMessages(a0, 10)); + WaitForIdle(); + EXPECT_TRUE(node1.ReadMultipleMessages(a1, 10)); + WaitForIdle(); + EXPECT_NE(0u, node0.GetUnacknowledgedMessageCount(a0)); + + // Close the far port and validate that the closure updates the unacknowledged + // count. + EXPECT_EQ(OK, node1.node().ClosePort(a1)); + WaitForIdle(); + EXPECT_EQ(0u, node0.GetUnacknowledgedMessageCount(a0)); + + EXPECT_EQ(OK, node0.node().ClosePort(a0)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); +} + } // namespace test } // namespace ports } // namespace core diff --git a/chromium/mojo/core/quota_unittest.cc b/chromium/mojo/core/quota_unittest.cc index bc26baf151a..e1d1a6ebd9e 100644 --- a/chromium/mojo/core/quota_unittest.cc +++ b/chromium/mojo/core/quota_unittest.cc @@ -256,6 +256,108 @@ TEST_F(QuotaTest, ReceiveQueueMemorySizeLimitExceeded) { MojoClose(b); } +TEST_F(QuotaTest, BasicUnreadMessageCount) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(MOJO_QUOTA_LIMIT_NONE, limit); + EXPECT_EQ(0u, usage); + + const uint64_t kTestLimit = 42; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, kTestLimit, + nullptr)); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(0u, usage); + + const std::string kTestMessage = "doot"; + WriteMessage(a, kTestMessage); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(kTestLimit, limit); + EXPECT_EQ(usage, 1u); + + MojoClose(a); + MojoClose(b); +} + +TEST_F(QuotaTest, UnreadMessageCountLimitExceeded) { + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + const uint64_t kMaxUnreadMessageCount = 4; + EXPECT_EQ(MOJO_RESULT_OK, + MojoSetQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, + kMaxUnreadMessageCount, nullptr)); + + MojoHandleSignalsState signals; + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + const std::string kTestMessage = "msg"; + WriteMessage(a, kTestMessage); + + uint64_t limit = 0; + uint64_t usage = 0; + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxUnreadMessageCount, limit); + EXPECT_EQ(1u, usage); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + // Push the endpoint over quota and ensure that it signals accordingly. + WriteMessage(a, kTestMessage); + WriteMessage(a, kTestMessage); + WriteMessage(a, kTestMessage); + WriteMessage(a, kTestMessage); + WaitForSignals(a, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &signals)); + EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxUnreadMessageCount, limit); + EXPECT_EQ(5u, usage); + + // Read the messages and wait for QUOTA_EXCEEDED on the other end to go back + // low. There's some hysteresis in the signaling, so it's not sufficient to + // read a single packet, but reading below the quota size should work. + EXPECT_EQ(kTestMessage, ReadMessage(b)); + EXPECT_EQ(kTestMessage, ReadMessage(b)); + EXPECT_EQ(kTestMessage, ReadMessage(b)); + EXPECT_EQ(kTestMessage, ReadMessage(b)); + WaitForSignals(a, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED, + MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &signals)); + EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED); + + EXPECT_EQ(MOJO_RESULT_OK, + MojoQueryQuota(a, MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, + &limit, &usage)); + EXPECT_EQ(kMaxUnreadMessageCount, limit); + EXPECT_LE(1u, usage); + + MojoClose(a); + MojoClose(b); +} + TEST_F(QuotaTest, TrapQuotaExceeded) { // Simple sanity check to verify that QUOTA_EXCEEDED signals can be trapped // like any other signals. diff --git a/chromium/mojo/core/trap_unittest.cc b/chromium/mojo/core/trap_unittest.cc index 309e5476ffd..b7ae4f3b02c 100644 --- a/chromium/mojo/core/trap_unittest.cc +++ b/chromium/mojo/core/trap_unittest.cc @@ -1486,6 +1486,10 @@ TEST_F(TrapTest, TriggersRemoveEachOtherWithinEventHandlers) { [](MojoHandle b) { WriteMessage(b, kTestMessageToA); }, b)); runner.Start(); + // To enforce that the two traps run concurrently, wait until the WriteMessage + // above has made a readable before firing the readable trap on b. + wait_for_a_to_notify.Wait(); + WriteMessage(a, kTestMessageToB); wait_for_a_to_cancel.Wait(); diff --git a/chromium/mojo/public/c/system/quota.h b/chromium/mojo/public/c/system/quota.h index 721c0e1bfdc..e817578e6d0 100644 --- a/chromium/mojo/public/c/system/quota.h +++ b/chromium/mojo/public/c/system/quota.h @@ -61,6 +61,11 @@ typedef uint32_t MojoQuotaType; // signal on that endpoint. May only be set on message pipe handles. #define MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE ((MojoQuotaType)1) +// Limits the number of sent, unread messages which can be queued on a message +// pipe endpoint before raising a |MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED| signal on +// that endpoint. May only be set on message pipe handles. +#define MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT ((MojoQuotaType)2) + #ifdef __cplusplus extern "C" { #endif diff --git a/chromium/mojo/public/cpp/base/BUILD.gn b/chromium/mojo/public/cpp/base/BUILD.gn index 934bb510161..5341d72fb00 100644 --- a/chromium/mojo/public/cpp/base/BUILD.gn +++ b/chromium/mojo/public/cpp/base/BUILD.gn @@ -38,6 +38,10 @@ component("shared_typemap_traits") { "file_mojom_traits.h", "file_path_mojom_traits.cc", "file_path_mojom_traits.h", + "generic_pending_receiver_mojom_traits.cc", + "generic_pending_receiver_mojom_traits.h", + "read_only_buffer_mojom_traits.cc", + "read_only_buffer_mojom_traits.h", "shared_memory_mojom_traits.cc", "shared_memory_mojom_traits.h", "time_mojom_traits.cc", @@ -88,7 +92,6 @@ source_set("tests") { "//base/test:test_support", "//mojo/public/cpp/test_support:test_utils", "//mojo/public/mojom/base", - "//mojo/public/mojom/base:read_only_buffer", "//testing/gtest", ] } diff --git a/chromium/mojo/public/cpp/base/generic_pending_receiver.typemap b/chromium/mojo/public/cpp/base/generic_pending_receiver.typemap new file mode 100644 index 00000000000..bb60f1569de --- /dev/null +++ b/chromium/mojo/public/cpp/base/generic_pending_receiver.typemap @@ -0,0 +1,15 @@ +# Copyright 2019 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. + +mojom = "//mojo/public/mojom/base/generic_pending_receiver.mojom" +public_headers = [ "//mojo/public/cpp/bindings/generic_pending_receiver.h" ] +traits_headers = + [ "//mojo/public/cpp/base/generic_pending_receiver_mojom_traits.h" ] +public_deps = [ + "//mojo/public/cpp/bindings", +] +deps = [ + "//mojo/public/cpp/base:shared_typemap_traits", +] +type_mappings = [ "mojo_base.mojom.GenericPendingReceiver=::mojo::GenericPendingReceiver[move_only]" ] diff --git a/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.cc b/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.cc new file mode 100644 index 00000000000..fb6137dfec6 --- /dev/null +++ b/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.cc @@ -0,0 +1,22 @@ +// Copyright 2019 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 "mojo/public/cpp/base/generic_pending_receiver_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits<mojo_base::mojom::GenericPendingReceiverDataView, + GenericPendingReceiver>:: + Read(mojo_base::mojom::GenericPendingReceiverDataView data, + GenericPendingReceiver* out) { + base::StringPiece interface_name; + if (!data.ReadInterfaceName(&interface_name)) + return false; + *out = GenericPendingReceiver(interface_name.as_string(), + data.TakeReceivingPipe()); + return true; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.h b/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.h new file mode 100644 index 00000000000..ad6e284d208 --- /dev/null +++ b/chromium/mojo/public/cpp/base/generic_pending_receiver_mojom_traits.h @@ -0,0 +1,37 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BASE_GENERIC_PENDING_RECEIVER_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BASE_GENERIC_PENDING_RECEIVER_MOJOM_TRAITS_H_ + +#include "base/component_export.h" +#include "base/strings/string_piece.h" +#include "mojo/public/cpp/bindings/generic_pending_receiver.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/mojom/base/generic_pending_receiver.mojom-shared.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) + StructTraits<mojo_base::mojom::GenericPendingReceiverDataView, + GenericPendingReceiver> { + static base::StringPiece interface_name( + const GenericPendingReceiver& receiver) { + DCHECK(receiver.interface_name().has_value()); + return receiver.interface_name().value(); + } + + static mojo::ScopedMessagePipeHandle receiving_pipe( + GenericPendingReceiver& receiver) { + return receiver.PassPipe(); + } + + static bool Read(mojo_base::mojom::GenericPendingReceiverDataView data, + GenericPendingReceiver* out); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BASE_GENERIC_PENDING_RECEIVER_MOJOM_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/base/message_loop_type.typemap b/chromium/mojo/public/cpp/base/message_loop_type.typemap new file mode 100644 index 00000000000..44d9fa57fba --- /dev/null +++ b/chromium/mojo/public/cpp/base/message_loop_type.typemap @@ -0,0 +1,15 @@ +# Copyright 2019 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. + +mojom = "//mojo/public/mojom/base/message_loop_type.mojom" +public_headers = [ "//base/message_loop/message_loop.h" ] +traits_headers = [ "//mojo/public/cpp/base/message_loop_type_mojom_traits.h" ] +sources = [ + "//mojo/public/cpp/base/message_loop_type_mojom_traits.cc", + "//mojo/public/cpp/base/message_loop_type_mojom_traits.h", +] +public_deps = [ + "//base", +] +type_mappings = [ "mojo_base.mojom.MessageLoopType=base::MessageLoop::Type" ] diff --git a/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.cc b/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.cc new file mode 100644 index 00000000000..2daaa609181 --- /dev/null +++ b/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.cc @@ -0,0 +1,76 @@ +// Copyright 2019 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 "mojo/public/cpp/base/message_loop_type_mojom_traits.h" +#include "build/build_config.h" + +namespace mojo { + +// static +mojo_base::mojom::MessageLoopType +EnumTraits<mojo_base::mojom::MessageLoopType, base::MessageLoop::Type>::ToMojom( + base::MessageLoop::Type input) { + switch (input) { + case base::MessageLoop::TYPE_DEFAULT: + return mojo_base::mojom::MessageLoopType::kDefault; + case base::MessageLoop::TYPE_UI: + return mojo_base::mojom::MessageLoopType::kUi; + case base::MessageLoop::TYPE_CUSTOM: + return mojo_base::mojom::MessageLoopType::kCustom; + case base::MessageLoop::TYPE_IO: + return mojo_base::mojom::MessageLoopType::kIo; +#if defined(OS_ANDROID) + case base::MessageLoop::TYPE_JAVA: + return mojo_base::mojom::MessageLoopType::kJava; +#endif +#if defined(OS_MACOSX) + case base::MessageLoop::Type::NS_RUNLOOP: + return mojo_base::mojom::MessageLoopType::kNsRunloop; +#endif +#if defined(OS_WIN) + case base::MessageLoop::Type::UI_WITH_WM_QUIT_SUPPORT: + return mojo_base::mojom::MessageLoopType::kUiWithWmQuitSupport; +#endif + } + NOTREACHED(); + return mojo_base::mojom::MessageLoopType::kDefault; +} + +// static +bool EnumTraits<mojo_base::mojom::MessageLoopType, base::MessageLoop::Type>:: + FromMojom(mojo_base::mojom::MessageLoopType input, + base::MessageLoop::Type* output) { + switch (input) { + case mojo_base::mojom::MessageLoopType::kDefault: + *output = base::MessageLoop::TYPE_DEFAULT; + return true; + case mojo_base::mojom::MessageLoopType::kUi: + *output = base::MessageLoop::TYPE_UI; + return true; + case mojo_base::mojom::MessageLoopType::kCustom: + *output = base::MessageLoop::TYPE_CUSTOM; + return true; + case mojo_base::mojom::MessageLoopType::kIo: + *output = base::MessageLoop::TYPE_IO; + return true; +#if defined(OS_ANDROID) + case mojo_base::mojom::MessageLoopType::kJava: + *output = base::MessageLoop::TYPE_JAVA; + return true; +#endif +#if defined(OS_MACOSX) + case mojo_base::mojom::MessageLoopType::kNsRunloop: + *output = base::MessageLoop::Type::NS_RUNLOOP; + return true; +#endif +#if defined(OS_WIN) + case mojo_base::mojom::MessageLoopType::kUiWithWmQuitSupport: + *output = base::MessageLoop::Type::UI_WITH_WM_QUIT_SUPPORT; + return true; +#endif + } + return false; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.h b/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.h new file mode 100644 index 00000000000..44860d0a465 --- /dev/null +++ b/chromium/mojo/public/cpp/base/message_loop_type_mojom_traits.h @@ -0,0 +1,26 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BASE_MESSAGE_LOOP_TYPE_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BASE_MESSAGE_LOOP_TYPE_MOJOM_TRAITS_H_ + +#include "base/component_export.h" +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/mojom/base/message_loop_type.mojom.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) + EnumTraits<mojo_base::mojom::MessageLoopType, base::MessageLoop::Type> { + static mojo_base::mojom::MessageLoopType ToMojom( + base::MessageLoop::Type input); + static bool FromMojom(mojo_base::mojom::MessageLoopType input, + base::MessageLoop::Type* output); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BASE_MESSAGE_LOOP_TYPE_MOJOM_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/base/read_only_buffer.typemap b/chromium/mojo/public/cpp/base/read_only_buffer.typemap index b772b729b67..08c764fe91a 100644 --- a/chromium/mojo/public/cpp/base/read_only_buffer.typemap +++ b/chromium/mojo/public/cpp/base/read_only_buffer.typemap @@ -5,9 +5,9 @@ mojom = "//mojo/public/mojom/base/read_only_buffer.mojom" public_headers = [ "//base/containers/span.h" ] traits_headers = [ "//mojo/public/cpp/base/read_only_buffer_mojom_traits.h" ] -sources = [ - "//mojo/public/cpp/base/read_only_buffer_mojom_traits.cc", - "//mojo/public/cpp/base/read_only_buffer_mojom_traits.h", +public_deps = [ + "//base", + "//mojo/public/cpp/base:shared_typemap_traits", ] type_mappings = [ "mojo_base.mojom.ReadOnlyBuffer=base::span<const uint8_t>[copyable_pass_by_value,force_serialize]" ] diff --git a/chromium/mojo/public/cpp/base/read_only_buffer_mojom_traits.h b/chromium/mojo/public/cpp/base/read_only_buffer_mojom_traits.h index ad96e91e078..09e06cf35cf 100644 --- a/chromium/mojo/public/cpp/base/read_only_buffer_mojom_traits.h +++ b/chromium/mojo/public/cpp/base/read_only_buffer_mojom_traits.h @@ -5,14 +5,16 @@ #ifndef MOJO_PUBLIC_CPP_BASE_READ_ONLY_BUFFER_MOJOM_TRAITS_H_ #define MOJO_PUBLIC_CPP_BASE_READ_ONLY_BUFFER_MOJOM_TRAITS_H_ +#include "base/component_export.h" #include "base/containers/span.h" #include "mojo/public/mojom/base/read_only_buffer.mojom-shared.h" namespace mojo { template <> -struct StructTraits<mojo_base::mojom::ReadOnlyBufferDataView, - base::span<const uint8_t>> { +struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS) + StructTraits<mojo_base::mojom::ReadOnlyBufferDataView, + base::span<const uint8_t>> { static base::span<const uint8_t> buffer(base::span<const uint8_t> input) { return input; } diff --git a/chromium/mojo/public/cpp/base/typemaps.gni b/chromium/mojo/public/cpp/base/typemaps.gni index 874e8c90e95..8d806254d13 100644 --- a/chromium/mojo/public/cpp/base/typemaps.gni +++ b/chromium/mojo/public/cpp/base/typemaps.gni @@ -10,9 +10,11 @@ typemaps = [ "//mojo/public/cpp/base/file_info.typemap", "//mojo/public/cpp/base/file_path.typemap", "//mojo/public/cpp/base/file.typemap", + "//mojo/public/cpp/base/generic_pending_receiver.typemap", "//mojo/public/cpp/base/read_only_buffer.typemap", "//mojo/public/cpp/base/memory_allocator_dump_cross_process_uid.typemap", "//mojo/public/cpp/base/memory_pressure_level.typemap", + "//mojo/public/cpp/base/message_loop_type.typemap", "//mojo/public/cpp/base/process_id.typemap", "//mojo/public/cpp/base/ref_counted_memory.typemap", "//mojo/public/cpp/base/shared_memory.typemap", diff --git a/chromium/mojo/public/cpp/bindings/BUILD.gn b/chromium/mojo/public/cpp/bindings/BUILD.gn index 7ba10d41f16..5667582bfd8 100644 --- a/chromium/mojo/public/cpp/bindings/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/BUILD.gn @@ -36,6 +36,8 @@ component("bindings_base") { "associated_group.h", "associated_group_controller.h", "clone_traits.h", + "connection_group.cc", + "connection_group.h", "disconnect_reason.h", "enum_traits.h", "equals_traits.h", @@ -62,6 +64,10 @@ component("bindings_base") { "lib/message_header_validator.cc", "lib/message_internal.cc", "lib/message_internal.h", + "lib/pending_receiver_state.cc", + "lib/pending_receiver_state.h", + "lib/pending_remote_state.cc", + "lib/pending_remote_state.h", "lib/scoped_interface_endpoint_handle.cc", "lib/serialization.h", "lib/serialization_context.cc", @@ -127,6 +133,8 @@ component("bindings") { "connection_error_callback.h", "connector.h", "filter_chain.h", + "generic_pending_receiver.cc", + "generic_pending_receiver.h", "interface_endpoint_client.h", "interface_endpoint_controller.h", "interface_ptr.h", @@ -176,6 +184,7 @@ component("bindings") { "receiver.h", "receiver_set.h", "remote.h", + "remote_set.h", "self_owned_receiver.h", "sequence_local_sync_event_watcher.h", "shared_associated_remote.h", @@ -188,6 +197,7 @@ component("bindings") { "sync_handle_registry.h", "sync_handle_watcher.h", "thread_safe_interface_ptr.h", + "unique_associated_receiver_set.h", "unique_ptr_impl_ref_traits.h", "unique_receiver_set.h", ] @@ -210,6 +220,7 @@ component("bindings") { ":bindings_base", ":struct_traits", "//base", + "//base/util/type_safety", "//ipc:message_support", "//ipc:param_traits", "//mojo/public/cpp/system", diff --git a/chromium/mojo/public/cpp/bindings/array_traits_stl.h b/chromium/mojo/public/cpp/bindings/array_traits_stl.h index 61688ed43bb..d579e5c2cdb 100644 --- a/chromium/mojo/public/cpp/bindings/array_traits_stl.h +++ b/chromium/mojo/public/cpp/bindings/array_traits_stl.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ +#include <array> #include <map> #include <set> #include <vector> @@ -145,6 +146,31 @@ struct ArrayTraits<MapValuesArrayView<K, V>> { static const V& GetValue(ConstIterator& iterator) { return iterator->second; } }; +// This ArrayTraits specialization is used for conversion between +// std::array<T, N> and array<T, N>. +template <typename T, size_t N> +struct ArrayTraits<std::array<T, N>> { + using Element = T; + + static bool IsNull(const std::array<T, N>& input) { return false; } + + static size_t GetSize(const std::array<T, N>& input) { return N; } + + static const T& GetAt(const std::array<T, N>& input, size_t index) { + return input[index]; + } + static T& GetAt(std::array<T, N>& input, size_t index) { + return input[index]; + } + + // std::array is fixed size but this is called during deserialization. + static bool Resize(std::array<T, N>& input, size_t size) { + if (size != N) + return false; + return true; + } +}; + } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ diff --git a/chromium/mojo/public/cpp/bindings/associated_remote.h b/chromium/mojo/public/cpp/bindings/associated_remote.h index 8ff75188c06..1feadc1611d 100644 --- a/chromium/mojo/public/cpp/bindings/associated_remote.h +++ b/chromium/mojo/public/cpp/bindings/associated_remote.h @@ -16,6 +16,7 @@ #include "base/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h" +#include "mojo/public/cpp/bindings/lib/multiplex_router.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" @@ -172,6 +173,36 @@ class AssociatedRemote { return PendingAssociatedReceiver<Interface>(std::move(receiver_handle)); } + // Like BindNewEndpointAndPassReceiver() above, but it creates a dedicated + // message pipe. The returned receiver can be bound directly to an + // implementation, without being first passed through a message pipe endpoint. + // + // For testing, where the returned request is bound to e.g. a mock and there + // are no other interfaces involved. + PendingAssociatedReceiver<Interface> + BindNewEndpointAndPassDedicatedReceiverForTesting() WARN_UNUSED_RESULT { + MessagePipe pipe; + scoped_refptr<internal::MultiplexRouter> router0 = + new internal::MultiplexRouter( + std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE, + false, base::SequencedTaskRunnerHandle::Get()); + scoped_refptr<internal::MultiplexRouter> router1 = + new internal::MultiplexRouter( + std::move(pipe.handle1), internal::MultiplexRouter::MULTI_INTERFACE, + true, base::SequencedTaskRunnerHandle::Get()); + + ScopedInterfaceEndpointHandle remote_handle; + ScopedInterfaceEndpointHandle receiver_handle; + ScopedInterfaceEndpointHandle::CreatePairPendingAssociation( + &remote_handle, &receiver_handle); + InterfaceId id = router1->AssociateInterface(std::move(remote_handle)); + remote_handle = router0->CreateLocalEndpointHandle(id); + + Bind(PendingAssociatedRemote<Interface>(std::move(remote_handle), 0), + nullptr); + return PendingAssociatedReceiver<Interface>(std::move(receiver_handle)); + } + // Binds this AssociatedRemote by consuming |pending_remote|, which must be // valid. The AssociatedRemote will schedule any response callbacks or // disconnection notifications on the default SequencedTaskRunner (i.e. diff --git a/chromium/mojo/public/cpp/bindings/binding.h b/chromium/mojo/public/cpp/bindings/binding.h index 1cd1677bd0b..d342c57208e 100644 --- a/chromium/mojo/public/cpp/bindings/binding.h +++ b/chromium/mojo/public/cpp/bindings/binding.h @@ -97,7 +97,7 @@ class Binding { // binding it to the previously specified implementation. void Bind(InterfaceRequest<Interface> request, scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { - internal_state_.Bind(request.PassMessagePipe(), std::move(runner)); + internal_state_.Bind(request.internal_state(), std::move(runner)); } // Adds a message filter to be notified of each incoming message before diff --git a/chromium/mojo/public/cpp/bindings/binding_set.h b/chromium/mojo/public/cpp/bindings/binding_set.h index 33872fa97a4..c31405d9be7 100644 --- a/chromium/mojo/public/cpp/bindings/binding_set.h +++ b/chromium/mojo/public/cpp/bindings/binding_set.h @@ -70,7 +70,7 @@ class BindingSetBase { using RequestType = typename Traits::RequestType; using ImplPointerType = typename Traits::ImplPointerType; - BindingSetBase() : weak_ptr_factory_(this) {} + BindingSetBase() {} void set_connection_error_handler(base::RepeatingClosure error_handler) { error_handler_ = std::move(error_handler); @@ -309,7 +309,7 @@ class BindingSetBase { bool is_flushing_ = false; const Context* dispatch_context_ = nullptr; BindingId dispatch_binding_; - base::WeakPtrFactory<BindingSetBase> weak_ptr_factory_; + base::WeakPtrFactory<BindingSetBase> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(BindingSetBase); }; diff --git a/chromium/mojo/public/cpp/bindings/connection_group.cc b/chromium/mojo/public/cpp/bindings/connection_group.cc new file mode 100644 index 00000000000..7dd255deed6 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/connection_group.cc @@ -0,0 +1,93 @@ +// Copyright 2019 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 "mojo/public/cpp/bindings/connection_group.h" + +#include "base/bind.h" + +namespace mojo { + +ConnectionGroup::Ref::Ref() = default; + +ConnectionGroup::Ref::Ref(const Ref& other) { + *this = other; +} + +ConnectionGroup::Ref::Ref(Ref&& other) noexcept { + *this = std::move(other); +} + +ConnectionGroup::Ref::~Ref() { + reset(); +} + +ConnectionGroup::Ref& ConnectionGroup::Ref::operator=(const Ref& other) { + reset(); + type_ = Type::kStrong; + group_ = other.group_; + group_->AddGroupRef(); + return *this; +} + +ConnectionGroup::Ref& ConnectionGroup::Ref::operator=(Ref&& other) noexcept { + reset(); + type_ = other.type_; + group_.swap(other.group_); + return *this; +} + +void ConnectionGroup::Ref::reset() { + if (type_ == Type::kStrong && group_) + group_->ReleaseGroupRef(); + type_ = Type::kWeak; + group_.reset(); +} + +ConnectionGroup::Ref ConnectionGroup::Ref::WeakCopy() const { + DCHECK(group_->notification_task_runner_->RunsTasksInCurrentSequence()); + return Ref(group_); +} + +bool ConnectionGroup::Ref::HasZeroRefs() const { + DCHECK(group_->notification_task_runner_->RunsTasksInCurrentSequence()); + return group_->num_refs_ == 0; +} + +ConnectionGroup::Ref::Ref(scoped_refptr<ConnectionGroup> group) + : group_(std::move(group)) {} + +// static +ConnectionGroup::Ref ConnectionGroup::Create( + base::RepeatingClosure callback, + scoped_refptr<base::TaskRunner> task_runner) { + return Ref(base::WrapRefCounted( + new ConnectionGroup(std::move(callback), std::move(task_runner)))); +} + +ConnectionGroup::ConnectionGroup(base::RepeatingClosure callback, + scoped_refptr<base::TaskRunner> task_runner) + : notification_callback_(std::move(callback)), + notification_task_runner_(std::move(task_runner)) {} + +ConnectionGroup::~ConnectionGroup() { + // Just a sanity check. This ref-count should always be adjusted before the + // internal RefCountedThreadSafe ref-count, so we should never hit the + // destructor with a non-zero |num_refs_|. + DCHECK_EQ(num_refs_, 0u); +} + +void ConnectionGroup::AddGroupRef() { + ++num_refs_; +} + +void ConnectionGroup::ReleaseGroupRef() { + DCHECK_GT(num_refs_, 0u); + --num_refs_; + if (num_refs_ == 0 && notification_task_runner_) { + notification_task_runner_->PostTask(FROM_HERE, + base::BindOnce(notification_callback_)); + } +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/connection_group.h b/chromium/mojo/public/cpp/bindings/connection_group.h new file mode 100644 index 00000000000..ba97769f02d --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/connection_group.h @@ -0,0 +1,110 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_GROUP_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_GROUP_H_ + +#include <atomic> + +#include "base/callback.h" +#include "base/component_export.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/task_runner.h" + +namespace mojo { + +// A ConnectionGroup is used to loosely track groups of related interface +// receivers. Any Receiver or PendingReceiver can reference a single +// ConnectionGroup by holding onto a corresponding Ref. +// +// Belonging to a connection group is a viral property: if a Receiver belongs to +// a connection group, any PendingReceivers arriving in inbound messages +// automatically inherit a Ref to the same group. Likewise if a PendingReceiver +// belongs to a group, any Receiver which consumes and binds that +// PendingReceiver inherits its group membership. +class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ConnectionGroup + : public base::RefCountedThreadSafe<ConnectionGroup> { + public: + // A single opaque reference to a ConnectionGroup. May be freely moved and + // copied. + class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Ref { + public: + Ref(); + Ref(const Ref&); + Ref(Ref&&) noexcept; + ~Ref(); + + Ref& operator=(const Ref&); + Ref& operator=(Ref&&) noexcept; + + explicit operator bool() const { return group_ != nullptr; } + void reset(); + + // Returns a real reference to the underlying ConnectionGroup. Should only + // be used in testing. + scoped_refptr<ConnectionGroup> GetGroupForTesting() { return group_; } + + // Returns a weak copy of this Ref. Does not increase ref-count. + Ref WeakCopy() const; + + // Indicates whether the underlying ConnectionGroup has zero strong + // references. Must ONLY be called from the sequence which owns the + // primordial weak Ref, since that sequence may increase the ref count at + // any time and otherwise this accessor would be unreliable. + bool HasZeroRefs() const; + + private: + friend class ConnectionGroup; + + enum class Type { + // A weak Ref does not influence the ref-count of its referenced group. + // Weak references always produce strong references when copied. + kWeak, + + // A strong Ref influences the ref-count of its reference group. + kStrong, + }; + + explicit Ref(scoped_refptr<ConnectionGroup> group); + + Type type_ = Type::kWeak; + scoped_refptr<ConnectionGroup> group_; + }; + + // Constructs a new ConnectionGroup and returns an initial Ref to it. This + // initial reference does *not* increase the group's ref-count. All other + // copies of Ref increase the ref-count. Any time the ref-count is decremented + // to zero, |callback| is invoked on |task_runner|. If |task_runner| is null + // (useless except perhaps in tests), |callback| is ignored. + static Ref Create(base::RepeatingClosure callback, + scoped_refptr<base::TaskRunner> task_runner); + + unsigned int GetNumRefsForTesting() const { return num_refs_; } + + private: + friend class base::RefCountedThreadSafe<ConnectionGroup>; + friend class Ref; + + ConnectionGroup(base::RepeatingClosure callback, + scoped_refptr<base::TaskRunner> task_runner); + virtual ~ConnectionGroup(); + + void AddGroupRef(); + void ReleaseGroupRef(); + + const base::RepeatingClosure notification_callback_; + const scoped_refptr<base::TaskRunner> notification_task_runner_; + + // We maintain our own ref count because we need to trigger behavior on + // release, and doing that in conjunction with the RefCountedThreadSafe's own + // lifetime-controlling ref count is not safely possible. + std::atomic<unsigned int> num_refs_{0}; + + DISALLOW_COPY_AND_ASSIGN(ConnectionGroup); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_GROUP_H_ diff --git a/chromium/mojo/public/cpp/bindings/connector.h b/chromium/mojo/public/cpp/bindings/connector.h index b05b747b1a2..c2cf962e87d 100644 --- a/chromium/mojo/public/cpp/bindings/connector.h +++ b/chromium/mojo/public/cpp/bindings/connector.h @@ -18,6 +18,7 @@ #include "base/optional.h" #include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" @@ -154,6 +155,11 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { return message_pipe_.is_valid(); } + // Adds this object to a ConnectionGroup identified by |ref|. All receiving + // pipe endpoints decoded from inbound messages on this MultiplexRouter will + // be added to the same group. + void SetConnectionGroup(ConnectionGroup::Ref ref); + // Waits for the next message on the pipe, blocking until one arrives, // |deadline| elapses, or an error happens. Returns |true| if a message has // been delivered, |false| otherwise. @@ -320,12 +326,15 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) Connector : public MessageReceiver { std::unique_ptr<MessageReceiver> message_dumper_; #endif + // A reference to the ConnectionGroup to which this Connector belongs, if any. + ConnectionGroup::Ref connection_group_; + // Create a single weak ptr and use it everywhere, to avoid the malloc/free // cost of creating a new weak ptr whenever it is needed. // NOTE: This weak pointer is invalidated when the message pipe is closed or // transferred (i.e., when |connected_| is set to false). base::WeakPtr<Connector> weak_self_; - base::WeakPtrFactory<Connector> weak_factory_; + base::WeakPtrFactory<Connector> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Connector); }; diff --git a/chromium/mojo/public/cpp/bindings/generic_pending_receiver.cc b/chromium/mojo/public/cpp/bindings/generic_pending_receiver.cc new file mode 100644 index 00000000000..1fb70944d17 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/generic_pending_receiver.cc @@ -0,0 +1,39 @@ +// Copyright 2019 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 "mojo/public/cpp/bindings/generic_pending_receiver.h" + +namespace mojo { + +GenericPendingReceiver::GenericPendingReceiver() = default; + +GenericPendingReceiver::GenericPendingReceiver( + base::StringPiece interface_name, + mojo::ScopedMessagePipeHandle receiving_pipe) + : interface_name_(interface_name.as_string()), + pipe_(std::move(receiving_pipe)) {} + +GenericPendingReceiver::GenericPendingReceiver(GenericPendingReceiver&&) = + default; + +GenericPendingReceiver::~GenericPendingReceiver() = default; + +GenericPendingReceiver& GenericPendingReceiver::operator=( + GenericPendingReceiver&&) = default; + +mojo::ScopedMessagePipeHandle GenericPendingReceiver::PassPipe() { + DCHECK(is_valid()); + interface_name_.reset(); + return std::move(pipe_); +} + +mojo::ScopedMessagePipeHandle GenericPendingReceiver::PassPipeIfNameIs( + const char* interface_name) { + DCHECK(is_valid()); + if (interface_name_ == interface_name) + return PassPipe(); + return mojo::ScopedMessagePipeHandle(); +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/generic_pending_receiver.h b/chromium/mojo/public/cpp/bindings/generic_pending_receiver.h new file mode 100644 index 00000000000..8a0a71abeef --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/generic_pending_receiver.h @@ -0,0 +1,72 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_GENERIC_PENDING_RECEIVER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_GENERIC_PENDING_RECEIVER_H_ + +#include <string> + +#include "base/component_export.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/strings/string_piece.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { + +// GenericPendingReceiver encapsulates a pairing of a receiving pipe endpoint +// with the name of the mojom interface assumed by the corresponding remote +// endpoint. +// +// This is used by mojom C++ bindings to represent +// |mojo_base.mojom.GenericPendingReceiver|, and it serves as a semi-safe +// wrapper for transporting arbitrary interface receivers in a generic object. +class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) GenericPendingReceiver { + public: + GenericPendingReceiver(); + GenericPendingReceiver(base::StringPiece interface_name, + mojo::ScopedMessagePipeHandle receiving_pipe); + + template <typename Interface> + GenericPendingReceiver(mojo::PendingReceiver<Interface> receiver) + : GenericPendingReceiver(Interface::Name_, receiver.PassPipe()) {} + + GenericPendingReceiver(GenericPendingReceiver&&); + ~GenericPendingReceiver(); + + GenericPendingReceiver& operator=(GenericPendingReceiver&&); + + bool is_valid() const { return pipe_.is_valid(); } + explicit operator bool() const { return is_valid(); } + + const base::Optional<std::string>& interface_name() const { + return interface_name_; + } + + mojo::MessagePipeHandle pipe() const { return pipe_.get(); } + + // Takes ownership of the receiving pipe, invalidating this + // GenericPendingReceiver. + mojo::ScopedMessagePipeHandle PassPipe(); + + // Takes ownership of the pipe, strongly typed as an |Interface| receiver, if + // and only if that interface's name matches the stored interface name. + template <typename Interface> + mojo::PendingReceiver<Interface> As() { + return mojo::PendingReceiver<Interface>(PassPipeIfNameIs(Interface::Name_)); + } + + private: + mojo::ScopedMessagePipeHandle PassPipeIfNameIs(const char* interface_name); + + base::Optional<std::string> interface_name_; + mojo::ScopedMessagePipeHandle pipe_; + + DISALLOW_COPY_AND_ASSIGN(GenericPendingReceiver); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_GENERIC_PENDING_RECEIVER_H_ diff --git a/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h b/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h index 48ba96437e6..363f97f9a10 100644 --- a/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/chromium/mojo/public/cpp/bindings/interface_endpoint_client.h @@ -22,7 +22,10 @@ #include "base/optional.h" #include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" +#include "base/time/time.h" +#include "base/timer/timer.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/filter_chain.h" #include "mojo/public/cpp/bindings/lib/control_message_handler.h" @@ -94,6 +97,12 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient void CloseWithReason(uint32_t custom_reason, const std::string& description); + // Used by ControlMessageProxy to send messages through this endpoint. + void SendControlMessage(Message* message); + void SendControlMessageWithResponder( + Message* message, + std::unique_ptr<MessageReceiver> responder); + // MessageReceiverWithResponder implementation: // They must only be called when the handle is not in pending association // state. @@ -102,6 +111,12 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient bool AcceptWithResponder(Message* message, std::unique_ptr<MessageReceiver> responder) override; + // Implementations used by both SendControlMessage* and Accept* above. + bool SendMessage(Message* message, bool is_control_message); + bool SendMessageWithResponder(Message* message, + bool is_control_message, + std::unique_ptr<MessageReceiver> responder); + // The following methods are called by the router. They must be called // outside of the router's lock. @@ -117,6 +132,36 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient void FlushForTesting(); void FlushAsyncForTesting(base::OnceClosure callback); + // Sets a callback to handle idle notifications. This callback will be invoked + // any time the peer endpoint sends a NotifyIdle control message AND + // |num_unacked_messages_| is zero. + // + // Configures the peer endpoint to ack incoming messages send NotifyIdle + // notifications only once it's been idle continuously for at least a duration + // of |timeout|. + void SetIdleHandler(base::TimeDelta timeout, base::RepeatingClosure handler); + + unsigned int GetNumUnackedMessagesForTesting() const { + return num_unacked_messages_; + } + + // Sets a callback to invoke whenever this endpoint receives an + // EnableIdleTracking message from its peer. The callback is invoked with a + // new ConnectionGroup Ref that is expected to be adopted by whatever owns + // this endpoint. + using IdleTrackingEnabledCallback = + base::OnceCallback<void(ConnectionGroup::Ref connection_group)>; + void SetIdleTrackingEnabledCallback(IdleTrackingEnabledCallback callback); + + // Called by the ControlMessageHandler when receiving corresponding control + // messages. + bool AcceptEnableIdleTracking(base::TimeDelta timeout); + bool AcceptMessageAck(); + bool AcceptNotifyIdle(); + + void MaybeStartIdleTimer(); + void MaybeSendNotifyIdle(); + const char* interface_name() const { return interface_name_; } #if DCHECK_IS_ON() @@ -172,12 +217,40 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient const bool expect_sync_requests_ = false; + // The callback to invoke when our peer endpoint sends us NotifyIdle and we + // have no outstanding unacked messages. If null, no callback has been set and + // we do not expect to receive NotifyIdle or MessageAck messages from the + // peer. + base::RepeatingClosure idle_handler_; + + // A callback to invoke if and when this endpoint receives an + // EnableIdleTracking control message. + IdleTrackingEnabledCallback idle_tracking_enabled_callback_; + + // The timeout to wait for continuous idling before notiftying our peer that + // we're idle. + base::Optional<base::TimeDelta> idle_timeout_; + + // The current idle timer, valid only while we're idle. If this fires, we send + // a NotifyIdle to our peer. + base::Optional<base::OneShotTimer> notify_idle_timer_; + + // A ref to a ConnectionGroup used to track the idle state of this endpoint, + // if any. Only non-null if an EnableIdleTracking message has been received. + // This is a weak ref to the group. + ConnectionGroup::Ref idle_tracking_connection_group_; + + // Indicates the number of unacked messages that have been sent so far. Only + // non-zero when |idle_handler_| has been set and some number of unacked + // messages remain in-flight. + unsigned int num_unacked_messages_ = 0; + ScopedInterfaceEndpointHandle handle_; std::unique_ptr<AssociatedGroup> associated_group_; InterfaceEndpointController* controller_ = nullptr; MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr; - HandleIncomingMessageThunk thunk_; + HandleIncomingMessageThunk thunk_{this}; FilterChain filters_; AsyncResponderMap async_responders_; @@ -191,7 +264,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient scoped_refptr<base::SequencedTaskRunner> task_runner_; - internal::ControlMessageProxy control_message_proxy_; + internal::ControlMessageProxy control_message_proxy_{this}; internal::ControlMessageHandler control_message_handler_; const char* interface_name_; @@ -204,7 +277,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient SEQUENCE_CHECKER(sequence_checker_); - base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_; + base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(InterfaceEndpointClient); }; diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr.h b/chromium/mojo/public/cpp/bindings/interface_ptr.h index f6fc4394391..af383ed1cd2 100644 --- a/chromium/mojo/public/cpp/bindings/interface_ptr.h +++ b/chromium/mojo/public/cpp/bindings/interface_ptr.h @@ -91,7 +91,7 @@ class InterfacePtr { scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { reset(); if (info.is_valid()) - internal_state_.Bind(std::move(info), std::move(runner)); + internal_state_.Bind(info.internal_state(), std::move(runner)); } // Returns whether or not this InterfacePtr is bound to a message pipe. diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr_info.h b/chromium/mojo/public/cpp/bindings/interface_ptr_info.h index 70030434178..a6e189ace27 100644 --- a/chromium/mojo/public/cpp/bindings/interface_ptr_info.h +++ b/chromium/mojo/public/cpp/bindings/interface_ptr_info.h @@ -10,6 +10,7 @@ #include <utility> #include "base/macros.h" +#include "mojo/public/cpp/bindings/lib/pending_remote_state.h" #include "mojo/public/cpp/system/message_pipe.h" namespace mojo { @@ -19,46 +20,36 @@ namespace mojo { template <typename Interface> class InterfacePtrInfo { public: - InterfacePtrInfo() : version_(0u) {} + InterfacePtrInfo() = default; InterfacePtrInfo(std::nullptr_t) : InterfacePtrInfo() {} InterfacePtrInfo(ScopedMessagePipeHandle handle, uint32_t version) - : handle_(std::move(handle)), version_(version) {} + : state_(std::move(handle), version) {} - InterfacePtrInfo(InterfacePtrInfo&& other) - : handle_(std::move(other.handle_)), version_(other.version_) { - other.version_ = 0u; - } + InterfacePtrInfo(InterfacePtrInfo&& other) = default; ~InterfacePtrInfo() {} - InterfacePtrInfo& operator=(InterfacePtrInfo&& other) { - if (this != &other) { - handle_ = std::move(other.handle_); - version_ = other.version_; - other.version_ = 0u; - } - - return *this; - } + InterfacePtrInfo& operator=(InterfacePtrInfo&& other) = default; - bool is_valid() const { return handle_.is_valid(); } + bool is_valid() const { return state_.pipe.is_valid(); } - ScopedMessagePipeHandle PassHandle() { return std::move(handle_); } - const ScopedMessagePipeHandle& handle() const { return handle_; } + ScopedMessagePipeHandle PassHandle() { return std::move(state_.pipe); } + const ScopedMessagePipeHandle& handle() const { return state_.pipe; } void set_handle(ScopedMessagePipeHandle handle) { - handle_ = std::move(handle); + state_.pipe = std::move(handle); } - uint32_t version() const { return version_; } - void set_version(uint32_t version) { version_ = version; } + uint32_t version() const { return state_.version; } + void set_version(uint32_t version) { state_.version = version; } // Allow InterfacePtrInfo<> to be used in boolean expressions. - explicit operator bool() const { return handle_.is_valid(); } + explicit operator bool() const { return state_.pipe.is_valid(); } + + internal::PendingRemoteState* internal_state() { return &state_; } private: - ScopedMessagePipeHandle handle_; - uint32_t version_; + internal::PendingRemoteState state_; DISALLOW_COPY_AND_ASSIGN(InterfacePtrInfo); }; diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr_set.h b/chromium/mojo/public/cpp/bindings/interface_ptr_set.h index 8130785bdae..faf91adbe2e 100644 --- a/chromium/mojo/public/cpp/bindings/interface_ptr_set.h +++ b/chromium/mojo/public/cpp/bindings/interface_ptr_set.h @@ -21,8 +21,8 @@ using InterfacePtrSetElementId = size_t; namespace internal { -// TODO(blundell): This class should be rewritten to be structured -// similarly to BindingSet if possible, with PtrSet owning its +// TODO(https://crbug.com/965668): This class should be rewritten to be +// structured similarly to BindingSet if possible, with PtrSet owning its // Elements and those Elements calling back into PtrSet on connection // error. template <typename Interface, template <typename> class Ptr> diff --git a/chromium/mojo/public/cpp/bindings/interface_request.h b/chromium/mojo/public/cpp/bindings/interface_request.h index 03372bc9755..44174347126 100644 --- a/chromium/mojo/public/cpp/bindings/interface_request.h +++ b/chromium/mojo/public/cpp/bindings/interface_request.h @@ -12,8 +12,12 @@ #include "base/macros.h" #include "base/optional.h" #include "base/sequenced_task_runner.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/pending_receiver_state.h" +#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/pipe_control_message_proxy.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -34,31 +38,27 @@ class InterfaceRequest { InterfaceRequest(std::nullptr_t) {} explicit InterfaceRequest(ScopedMessagePipeHandle handle) - : handle_(std::move(handle)) {} + : state_(std::move(handle)) {} // Takes the message pipe from another InterfaceRequest. - InterfaceRequest(InterfaceRequest&& other) { - handle_ = std::move(other.handle_); - } - InterfaceRequest& operator=(InterfaceRequest&& other) { - handle_ = std::move(other.handle_); - return *this; - } + InterfaceRequest(InterfaceRequest&& other) = default; + + InterfaceRequest& operator=(InterfaceRequest&& other) = default; // Assigning to nullptr resets the InterfaceRequest to an empty state, // closing the message pipe currently bound to it (if any). InterfaceRequest& operator=(std::nullptr_t) { - handle_.reset(); + state_.reset(); return *this; } // Indicates whether the request currently contains a valid message pipe. - bool is_pending() const { return handle_.is_valid(); } + bool is_pending() const { return state_.pipe.is_valid(); } - explicit operator bool() const { return handle_.is_valid(); } + explicit operator bool() const { return is_pending(); } // Removes the message pipe from the request and returns it. - ScopedMessagePipeHandle PassMessagePipe() { return std::move(handle_); } + ScopedMessagePipeHandle PassMessagePipe() { return std::move(state_.pipe); } bool Equals(const InterfaceRequest& other) const { if (this == &other) @@ -70,21 +70,41 @@ class InterfaceRequest { } void ResetWithReason(uint32_t custom_reason, const std::string& description) { - if (!handle_.is_valid()) + if (!is_pending()) return; Message message = PipeControlMessageProxy::ConstructPeerEndpointClosedMessage( kMasterInterfaceId, DisconnectReason(custom_reason, description)); - MojoResult result = WriteMessageNew( - handle_.get(), message.TakeMojoMessage(), MOJO_WRITE_MESSAGE_FLAG_NONE); + MojoResult result = + WriteMessageNew(state_.pipe.get(), message.TakeMojoMessage(), + MOJO_WRITE_MESSAGE_FLAG_NONE); DCHECK_EQ(MOJO_RESULT_OK, result); - handle_.reset(); + state_.reset(); + } + + // Assigns this InterfaceRequest to the ConnectionGroup referenced by |ref|. + // Any Receiver which binds this InterfaceRequest will inherit the Ref. + void set_connection_group(ConnectionGroup::Ref ref) { + state_.connection_group = std::move(ref); } + const ConnectionGroup::Ref& connection_group() const { + return state_.connection_group; + } + + // Passes ownership of this InterfaceRequest's ConnectionGroup Ref, removing + // it from its group. + ConnectionGroup::Ref PassConnectionGroupRef() { + return std::move(state_.connection_group); + } + + // For internal Mojo use only. + internal::PendingReceiverState* internal_state() { return &state_; } + private: - ScopedMessagePipeHandle handle_; + internal::PendingReceiverState state_; DISALLOW_COPY_AND_ASSIGN(InterfaceRequest); }; diff --git a/chromium/mojo/public/cpp/bindings/lib/binding_state.cc b/chromium/mojo/public/cpp/bindings/lib/binding_state.cc index 87560a19c32..b82a2c4ff66 100644 --- a/chromium/mojo/public/cpp/bindings/lib/binding_state.cc +++ b/chromium/mojo/public/cpp/bindings/lib/binding_state.cc @@ -13,7 +13,7 @@ namespace mojo { namespace internal { -BindingStateBase::BindingStateBase() : weak_ptr_factory_(this) {} +BindingStateBase::BindingStateBase() = default; BindingStateBase::~BindingStateBase() = default; @@ -90,7 +90,7 @@ scoped_refptr<internal::MultiplexRouter> BindingStateBase::RouterForTesting() { } void BindingStateBase::BindInternal( - ScopedMessagePipeHandle handle, + PendingReceiverState* receiver_state, scoped_refptr<base::SequencedTaskRunner> runner, const char* interface_name, std::unique_ptr<MessageReceiver> request_validator, @@ -110,14 +110,17 @@ void BindingStateBase::BindInternal( : (has_sync_methods ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS : MultiplexRouter::SINGLE_INTERFACE); - router_ = - new MultiplexRouter(std::move(handle), config, false, sequenced_runner); + router_ = new MultiplexRouter(std::move(receiver_state->pipe), config, false, + sequenced_runner); router_->SetMasterInterfaceName(interface_name); + router_->SetConnectionGroup(std::move(receiver_state->connection_group)); endpoint_client_.reset(new InterfaceEndpointClient( router_->CreateLocalEndpointHandle(kMasterInterfaceId), stub, std::move(request_validator), has_sync_methods, std::move(sequenced_runner), interface_version, interface_name)); + endpoint_client_->SetIdleTrackingEnabledCallback( + base::BindOnce(&MultiplexRouter::SetConnectionGroup, router_)); #if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED) MakeBindingRandomlyPaused(base::SequencedTaskRunnerHandle::Get(), diff --git a/chromium/mojo/public/cpp/bindings/lib/binding_state.h b/chromium/mojo/public/cpp/bindings/lib/binding_state.h index cc2ac0c3166..ced07dd4f73 100644 --- a/chromium/mojo/public/cpp/bindings/lib/binding_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/binding_state.h @@ -18,6 +18,7 @@ #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/filter_chain.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/interface_id.h" @@ -25,6 +26,7 @@ #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/lib/multiplex_router.h" +#include "mojo/public/cpp/bindings/lib/pending_receiver_state.h" #include "mojo/public/cpp/bindings/message_header_validator.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/core.h" @@ -83,7 +85,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) BindingStateBase { scoped_refptr<internal::MultiplexRouter> RouterForTesting(); protected: - void BindInternal(ScopedMessagePipeHandle handle, + void BindInternal(PendingReceiverState* receiver_state, scoped_refptr<base::SequencedTaskRunner> runner, const char* interface_name, std::unique_ptr<MessageReceiver> request_validator, @@ -95,7 +97,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) BindingStateBase { scoped_refptr<internal::MultiplexRouter> router_; std::unique_ptr<InterfaceEndpointClient> endpoint_client_; - base::WeakPtrFactory<BindingStateBase> weak_ptr_factory_; + base::WeakPtrFactory<BindingStateBase> weak_ptr_factory_{this}; }; template <typename Interface, typename ImplRefTraits> @@ -109,10 +111,10 @@ class BindingState : public BindingStateBase { ~BindingState() { Close(); } - void Bind(ScopedMessagePipeHandle handle, + void Bind(PendingReceiverState* receiver_state, scoped_refptr<base::SequencedTaskRunner> runner) { BindingStateBase::BindInternal( - std::move(handle), runner, Interface::Name_, + std::move(receiver_state), runner, Interface::Name_, std::make_unique<typename Interface::RequestValidator_>(), Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_, Interface::Version_); diff --git a/chromium/mojo/public/cpp/bindings/lib/connector.cc b/chromium/mojo/public/cpp/bindings/lib/connector.cc index 01c5c3b21b6..d0739ddbf1f 100644 --- a/chromium/mojo/public/cpp/bindings/lib/connector.cc +++ b/chromium/mojo/public/cpp/bindings/lib/connector.cc @@ -145,8 +145,7 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, force_immediate_dispatch_(!EnableTaskPerMessage()), outgoing_serialization_mode_(g_default_outgoing_serialization_mode), incoming_serialization_mode_(g_default_incoming_serialization_mode), - nesting_observer_(RunLoopNestingObserver::GetForThread()), - weak_factory_(this) { + nesting_observer_(RunLoopNestingObserver::GetForThread()) { if (config == MULTI_THREADED_SEND) lock_.emplace(); @@ -209,6 +208,10 @@ void Connector::RaiseError() { HandleError(true, true); } +void Connector::SetConnectionGroup(ConnectionGroup::Ref ref) { + connection_group_ = std::move(ref); +} + bool Connector::WaitForIncomingMessage(MojoDeadline deadline) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -505,6 +508,8 @@ bool Connector::DispatchMessage(Message message) { TRACE_EVENT0("mojom", heap_profiler_tag_); #endif + if (connection_group_) + message.set_receiver_connection_group(&connection_group_); bool receiver_result = incoming_receiver_ && incoming_receiver_->Accept(&message); if (!weak_self) diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_handler.cc b/chromium/mojo/public/cpp/bindings/lib/control_message_handler.cc index b87c11c874b..254f452835c 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_handler.cc +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_handler.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/lib/serialization.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/message.h" @@ -61,9 +62,9 @@ bool ControlMessageHandler::IsControlMessage(const Message* message) { message->header()->name == interface_control::kRunOrClosePipeMessageId; } -ControlMessageHandler::ControlMessageHandler(uint32_t interface_version) - : interface_version_(interface_version) { -} +ControlMessageHandler::ControlMessageHandler(InterfaceEndpointClient* owner, + uint32_t interface_version) + : owner_(owner), interface_version_(interface_version) {} ControlMessageHandler::~ControlMessageHandler() { } @@ -138,7 +139,14 @@ bool ControlMessageHandler::RunOrClosePipe(Message* message) { auto& input = *params_ptr->input; if (input.is_require_version()) return interface_version_ >= input.get_require_version()->version; - + if (input.is_enable_idle_tracking()) { + return owner_->AcceptEnableIdleTracking(base::TimeDelta::FromMicroseconds( + input.get_enable_idle_tracking()->timeout_in_microseconds)); + } + if (input.is_message_ack()) + return owner_->AcceptMessageAck(); + if (input.is_notify_idle()) + return owner_->AcceptNotifyIdle(); return false; } diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_handler.h b/chromium/mojo/public/cpp/bindings/lib/control_message_handler.h index 3594134c97e..cab631a72de 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_handler.h +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_handler.h @@ -7,13 +7,15 @@ #include <stdint.h> -#include "base/compiler_specific.h" #include "base/component_export.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { + +class InterfaceEndpointClient; + namespace internal { // Handlers for request messages defined in interface_control_messages.mojom. @@ -22,7 +24,8 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ControlMessageHandler public: static bool IsControlMessage(const Message* message); - explicit ControlMessageHandler(uint32_t interface_version); + ControlMessageHandler(InterfaceEndpointClient* owner, + uint32_t interface_version); ~ControlMessageHandler() override; // Call the following methods only if IsControlMessage() returned true. @@ -36,6 +39,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ControlMessageHandler std::unique_ptr<MessageReceiverWithStatus> responder); bool RunOrClosePipe(Message* message); + InterfaceEndpointClient* const owner_; uint32_t interface_version_; SerializationContext context_; diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc index c48261a6625..ed0e85f94fd 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.cc @@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/lib/serialization.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/message.h" @@ -72,7 +73,7 @@ bool RunResponseForwardToCallback::Accept(Message* message) { return true; } -void SendRunMessage(MessageReceiverWithResponder* receiver, +void SendRunMessage(InterfaceEndpointClient* endpoint, interface_control::RunInputPtr input_ptr, RunCallback callback) { auto params_ptr = interface_control::RunMessageParams::New(); @@ -86,7 +87,7 @@ void SendRunMessage(MessageReceiverWithResponder* receiver, params_ptr, message.payload_buffer(), ¶ms, &context); std::unique_ptr<MessageReceiver> responder = std::make_unique<RunResponseForwardToCallback>(std::move(callback)); - ignore_result(receiver->AcceptWithResponder(&message, std::move(responder))); + endpoint->SendControlMessageWithResponder(&message, std::move(responder)); } Message ConstructRunOrClosePipeMessage( @@ -105,11 +106,11 @@ Message ConstructRunOrClosePipeMessage( } void SendRunOrClosePipeMessage( - MessageReceiverWithResponder* receiver, + InterfaceEndpointClient* endpoint, interface_control::RunOrClosePipeInputPtr input_ptr) { Message message(ConstructRunOrClosePipeMessage(std::move(input_ptr))); message.set_heap_profiler_tag(kMessageTag); - ignore_result(receiver->Accept(&message)); + endpoint->SendControlMessage(&message); } void RunVersionCallback( @@ -128,9 +129,8 @@ void RunClosure(base::OnceClosure callback, } // namespace -ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver) - : receiver_(receiver) { -} +ControlMessageProxy::ControlMessageProxy(InterfaceEndpointClient* owner) + : owner_(owner) {} ControlMessageProxy::~ControlMessageProxy() { // If this is destroyed in the middle of a flush, make sure the callback is @@ -143,7 +143,7 @@ void ControlMessageProxy::QueryVersion( const base::Callback<void(uint32_t)>& callback) { auto input_ptr = interface_control::RunInput::New(); input_ptr->set_query_version(interface_control::QueryVersion::New()); - SendRunMessage(receiver_, std::move(input_ptr), + SendRunMessage(owner_, std::move(input_ptr), base::BindOnce(&RunVersionCallback, callback)); } @@ -152,7 +152,7 @@ void ControlMessageProxy::RequireVersion(uint32_t version) { require_version->version = version; auto input_ptr = interface_control::RunOrClosePipeInput::New(); input_ptr->set_require_version(std::move(require_version)); - SendRunOrClosePipeMessage(receiver_, std::move(input_ptr)); + SendRunOrClosePipeMessage(owner_, std::move(input_ptr)); } void ControlMessageProxy::FlushForTesting() { @@ -173,7 +173,7 @@ void ControlMessageProxy::FlushAsyncForTesting(base::OnceClosure callback) { DCHECK(!pending_flush_callback_); pending_flush_callback_ = std::move(callback); SendRunMessage( - receiver_, std::move(input_ptr), + owner_, std::move(input_ptr), base::BindOnce( &RunClosure, base::BindOnce(&ControlMessageProxy::RunFlushForTestingClosure, @@ -185,6 +185,25 @@ void ControlMessageProxy::RunFlushForTestingClosure() { std::move(pending_flush_callback_).Run(); } +void ControlMessageProxy::EnableIdleTracking(base::TimeDelta timeout) { + auto input = interface_control::RunOrClosePipeInput::New(); + input->set_enable_idle_tracking( + interface_control::EnableIdleTracking::New(timeout.InMicroseconds())); + SendRunOrClosePipeMessage(owner_, std::move(input)); +} + +void ControlMessageProxy::SendMessageAck() { + auto input = interface_control::RunOrClosePipeInput::New(); + input->set_message_ack(interface_control::MessageAck::New()); + SendRunOrClosePipeMessage(owner_, std::move(input)); +} + +void ControlMessageProxy::NotifyIdle() { + auto input = interface_control::RunOrClosePipeInput::New(); + input->set_notify_idle(interface_control::NotifyIdle::New()); + SendRunOrClosePipeMessage(owner_, std::move(input)); +} + void ControlMessageProxy::OnConnectionError() { encountered_error_ = true; if (!pending_flush_callback_.is_null()) diff --git a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h index 99f52cd4e65..533f5181157 100644 --- a/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h +++ b/chromium/mojo/public/cpp/bindings/lib/control_message_proxy.h @@ -14,15 +14,15 @@ namespace mojo { -class MessageReceiverWithResponder; +class InterfaceEndpointClient; namespace internal { // Proxy for request messages defined in interface_control_messages.mojom. class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ControlMessageProxy { public: - // Doesn't take ownership of |receiver|. It must outlive this object. - explicit ControlMessageProxy(MessageReceiverWithResponder* receiver); + // Doesn't take ownership of |owner|. It must outlive this object. + explicit ControlMessageProxy(InterfaceEndpointClient* owner); ~ControlMessageProxy(); void QueryVersion(const base::Callback<void(uint32_t)>& callback); @@ -30,13 +30,18 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ControlMessageProxy { void FlushForTesting(); void FlushAsyncForTesting(base::OnceClosure callback); + + void EnableIdleTracking(base::TimeDelta timeout); + void SendMessageAck(); + void NotifyIdle(); + void OnConnectionError(); private: void RunFlushForTestingClosure(); // Not owned. - MessageReceiverWithResponder* receiver_; + InterfaceEndpointClient* const owner_; bool encountered_error_ = false; base::OnceClosure pending_flush_callback_; diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index 51e4171ff67..d210a668b56 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc @@ -67,6 +67,12 @@ class ResponderThunk : public MessageReceiverWithStatus { } } + // Allows this thunk to be attached to a ConnectionGroup as a means of keeping + // the group from idling while the response is pending. + void set_connection_group(ConnectionGroup::Ref connection_group) { + connection_group_ = std::move(connection_group); + } + // MessageReceiver implementation: bool PrefersSerializedMessages() override { return endpoint_client_ && endpoint_client_->PrefersSerializedMessages(); @@ -105,6 +111,7 @@ class ResponderThunk : public MessageReceiverWithStatus { base::WeakPtr<InterfaceEndpointClient> endpoint_client_; bool accept_was_invoked_; scoped_refptr<base::SequencedTaskRunner> task_runner_; + ConnectionGroup::Ref connection_group_; DISALLOW_COPY_AND_ASSIGN(ResponderThunk); }; @@ -146,13 +153,10 @@ InterfaceEndpointClient::InterfaceEndpointClient( : expect_sync_requests_(expect_sync_requests), handle_(std::move(handle)), incoming_receiver_(receiver), - thunk_(this), filters_(&thunk_), task_runner_(std::move(runner)), - control_message_proxy_(this), - control_message_handler_(interface_version), - interface_name_(interface_name), - weak_ptr_factory_(this) { + control_message_handler_(this, interface_version), + interface_name_(interface_name) { DCHECK(handle_.is_valid()); // TODO(yzshen): the way to use validator (or message filter in general) @@ -223,7 +227,30 @@ bool InterfaceEndpointClient::PrefersSerializedMessages() { return controller && controller->PrefersSerializedMessages(); } +void InterfaceEndpointClient::SendControlMessage(Message* message) { + SendMessage(message, true /* is_control_message */); +} + +void InterfaceEndpointClient::SendControlMessageWithResponder( + Message* message, + std::unique_ptr<MessageReceiver> responder) { + SendMessageWithResponder(message, true /* is_control_message */, + std::move(responder)); +} + bool InterfaceEndpointClient::Accept(Message* message) { + return SendMessage(message, false /* is_control_message */); +} + +bool InterfaceEndpointClient::AcceptWithResponder( + Message* message, + std::unique_ptr<MessageReceiver> responder) { + return SendMessageWithResponder(message, false /* is_control_message */, + std::move(responder)); +} + +bool InterfaceEndpointClient::SendMessage(Message* message, + bool is_control_message) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!message->has_flag(Message::kFlagExpectsResponse)); DCHECK(!handle_.pending_association()); @@ -247,11 +274,18 @@ bool InterfaceEndpointClient::Accept(Message* message) { #endif message->set_heap_profiler_tag(interface_name_); - return controller_->SendMessage(message); + if (!controller_->SendMessage(message)) + return false; + + if (!is_control_message && idle_handler_) + ++num_unacked_messages_; + + return true; } -bool InterfaceEndpointClient::AcceptWithResponder( +bool InterfaceEndpointClient::SendMessageWithResponder( Message* message, + bool is_control_message, std::unique_ptr<MessageReceiver> responder) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(message->has_flag(Message::kFlagExpectsResponse)); @@ -283,6 +317,9 @@ bool InterfaceEndpointClient::AcceptWithResponder( if (!controller_->SendMessage(message)) return false; + if (!is_control_message && idle_handler_) + ++num_unacked_messages_; + if (!is_sync) { async_responders_[request_id] = std::move(responder); return true; @@ -299,7 +336,7 @@ bool InterfaceEndpointClient::AcceptWithResponder( controller_->SyncWatch(&response_received); // Make sure that this instance hasn't been destroyed. if (weak_self) { - DCHECK(base::ContainsKey(sync_responses_, request_id)); + DCHECK(base::Contains(sync_responses_, request_id)); auto iter = sync_responses_.find(request_id); DCHECK_EQ(&response_received, iter->second->response_received); if (response_received) { @@ -365,6 +402,81 @@ void InterfaceEndpointClient::FlushAsyncForTesting(base::OnceClosure callback) { control_message_proxy_.FlushAsyncForTesting(std::move(callback)); } +void InterfaceEndpointClient::SetIdleHandler(base::TimeDelta timeout, + base::RepeatingClosure handler) { + // We allow for idle handler replacement and changing the timeout duration. + control_message_proxy_.EnableIdleTracking(timeout); + idle_handler_ = std::move(handler); +} + +void InterfaceEndpointClient::SetIdleTrackingEnabledCallback( + IdleTrackingEnabledCallback callback) { + idle_tracking_enabled_callback_ = std::move(callback); +} + +bool InterfaceEndpointClient::AcceptEnableIdleTracking( + base::TimeDelta timeout) { + // If this is the first time EnableIdleTracking was received, set up the + // ConnectionGroup and give a ref to our owner. + if (idle_tracking_enabled_callback_) { + idle_tracking_connection_group_ = ConnectionGroup::Create( + base::BindRepeating(&InterfaceEndpointClient::MaybeStartIdleTimer, + weak_ptr_factory_.GetWeakPtr()), + task_runner_); + std::move(idle_tracking_enabled_callback_) + .Run(idle_tracking_connection_group_.WeakCopy()); + } + + idle_timeout_ = timeout; + return true; +} + +bool InterfaceEndpointClient::AcceptMessageAck() { + if (!idle_handler_ || num_unacked_messages_ == 0) + return false; + + --num_unacked_messages_; + return true; +} + +bool InterfaceEndpointClient::AcceptNotifyIdle() { + if (!idle_handler_) + return false; + + // We have outstanding unacked messages, so quietly ignore this NotifyIdle. + if (num_unacked_messages_ > 0) + return true; + + // With no outstanding unacked messages, a NotifyIdle received implies that + // the peer really is idle. We can invoke our idle handler. + idle_handler_.Run(); + return true; +} + +void InterfaceEndpointClient::MaybeStartIdleTimer() { + // Something has happened to interrupt the current idle state, if any. We + // either restart the idle timer (if idle again) or clear it so it doesn't + // fire. + if (idle_tracking_connection_group_ && + idle_tracking_connection_group_.HasZeroRefs()) { + DCHECK(idle_timeout_); + notify_idle_timer_.emplace(); + notify_idle_timer_->Start( + FROM_HERE, *idle_timeout_, + base::BindOnce(&InterfaceEndpointClient::MaybeSendNotifyIdle, + base::Unretained(this))); + } else { + notify_idle_timer_.reset(); + } +} + +void InterfaceEndpointClient::MaybeSendNotifyIdle() { + if (idle_tracking_connection_group_ && + idle_tracking_connection_group_.HasZeroRefs()) { + control_message_proxy_.NotifyIdle(); + } +} + void InterfaceEndpointClient::InitControllerIfNecessary() { if (controller_ || handle_.pending_association()) return; @@ -401,16 +513,21 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { return false; } + auto weak_self = weak_ptr_factory_.GetWeakPtr(); + bool accepted_interface_message = false; + bool has_response = false; if (message->has_flag(Message::kFlagExpectsResponse)) { - std::unique_ptr<MessageReceiverWithStatus> responder = - std::make_unique<ResponderThunk>(weak_ptr_factory_.GetWeakPtr(), - task_runner_); + has_response = true; + auto responder = std::make_unique<ResponderThunk>( + weak_ptr_factory_.GetWeakPtr(), task_runner_); if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) { return control_message_handler_.AcceptWithResponder(message, std::move(responder)); } else { - return incoming_receiver_->AcceptWithResponder(message, - std::move(responder)); + if (idle_tracking_connection_group_) + responder->set_connection_group(idle_tracking_connection_group_); + accepted_interface_message = incoming_receiver_->AcceptWithResponder( + message, std::move(responder)); } } else if (message->has_flag(Message::kFlagIsResponse)) { uint64_t request_id = message->request_id(); @@ -434,8 +551,17 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) return control_message_handler_.Accept(message); - return incoming_receiver_->Accept(message); + accepted_interface_message = incoming_receiver_->Accept(message); } + + if (weak_self && accepted_interface_message && + idle_tracking_connection_group_) { + control_message_proxy_.SendMessageAck(); + if (!has_response) + MaybeStartIdleTimer(); + } + + return accepted_interface_message; } } // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.cc b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.cc index 7d49fa3e544..6d31bab0c9d 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.cc +++ b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.cc @@ -44,17 +44,16 @@ void InterfacePtrStateBase::Swap(InterfacePtrStateBase* other) { } void InterfacePtrStateBase::Bind( - ScopedMessagePipeHandle handle, - uint32_t version, + PendingRemoteState* remote_state, scoped_refptr<base::SequencedTaskRunner> task_runner) { DCHECK(!router_); DCHECK(!endpoint_client_); DCHECK(!handle_.is_valid()); DCHECK_EQ(0u, version_); - DCHECK(handle.is_valid()); + DCHECK(remote_state->pipe.is_valid()); - handle_ = std::move(handle); - version_ = version; + handle_ = std::move(remote_state->pipe); + version_ = remote_state->version; runner_ = GetTaskRunnerToUseFromUserProvidedTaskRunner(std::move(task_runner)); } diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h index fa1e469b71d..8cd7886262b 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h @@ -21,6 +21,7 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" +#include "base/time/time.h" #include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/filter_chain.h" @@ -28,6 +29,7 @@ #include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/lib/multiplex_router.h" +#include "mojo/public/cpp/bindings/lib/pending_remote_state.h" #include "mojo/public/cpp/bindings/message_header_validator.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" @@ -75,8 +77,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfacePtrStateBase { void QueryVersion(base::OnceCallback<void(uint32_t)> callback); void RequireVersion(uint32_t version); void Swap(InterfacePtrStateBase* other); - void Bind(ScopedMessagePipeHandle handle, - uint32_t version, + void Bind(PendingRemoteState* remote_state, scoped_refptr<base::SequencedTaskRunner> task_runner); ScopedMessagePipeHandle PassMessagePipe() { @@ -166,11 +167,10 @@ class InterfacePtrState : public InterfacePtrStateBase { InterfacePtrStateBase::Swap(other); } - void Bind(InterfacePtrInfo<Interface> info, + void Bind(PendingRemoteState* remote_state, scoped_refptr<base::SequencedTaskRunner> runner) { DCHECK(!proxy_); - InterfacePtrStateBase::Bind(info.PassHandle(), info.version(), - std::move(runner)); + InterfacePtrStateBase::Bind(remote_state, std::move(runner)); } // After this method is called, the object is in an invalid state and @@ -196,6 +196,17 @@ class InterfacePtrState : public InterfacePtrStateBase { std::move(error_handler)); } + void set_idle_handler(base::TimeDelta timeout, + base::RepeatingClosure handler) { + ConfigureProxyIfNecessary(); + DCHECK(endpoint_client()); + endpoint_client()->SetIdleHandler(timeout, std::move(handler)); + } + + unsigned int GetNumUnackedMessagesForTesting() const { + return endpoint_client()->GetNumUnackedMessagesForTesting(); + } + AssociatedGroup* associated_group() { ConfigureProxyIfNecessary(); return endpoint_client()->associated_group(); diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_serialization.h b/chromium/mojo/public/cpp/bindings/lib/interface_serialization.h index 88e2f0118b6..858152bf522 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_serialization.h +++ b/chromium/mojo/public/cpp/bindings/lib/interface_serialization.h @@ -202,8 +202,7 @@ struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> { static bool Deserialize(Handle_Data* input, InterfaceRequest<T>* output, SerializationContext* context) { - *output = - InterfaceRequest<T>(context->TakeHandleAs<MessagePipeHandle>(*input)); + context->TakeHandleAsReceiver(*input, output->internal_state()); return true; } }; @@ -221,8 +220,7 @@ struct Serializer<InterfaceRequestDataView<Base>, PendingReceiver<T>> { static bool Deserialize(Handle_Data* input, PendingReceiver<T>* output, SerializationContext* context) { - *output = - PendingReceiver<T>(context->TakeHandleAs<MessagePipeHandle>(*input)); + context->TakeHandleAsReceiver(*input, output->internal_state()); return true; } }; diff --git a/chromium/mojo/public/cpp/bindings/lib/message.cc b/chromium/mojo/public/cpp/bindings/lib/message.cc index 29b4d3cc385..46b4d8b745a 100644 --- a/chromium/mojo/public/cpp/bindings/lib/message.cc +++ b/chromium/mojo/public/cpp/bindings/lib/message.cc @@ -218,10 +218,11 @@ Message::Message(Message&& other) associated_endpoint_handles_( std::move(other.associated_endpoint_handles_)), transferable_(other.transferable_), - serialized_(other.serialized_) { + serialized_(other.serialized_), + heap_profiler_tag_(other.heap_profiler_tag_), + receiver_connection_group_(other.receiver_connection_group_) { other.transferable_ = false; other.serialized_ = false; - heap_profiler_tag_ = other.heap_profiler_tag_; #if defined(ENABLE_IPC_FUZZER) interface_name_ = other.interface_name_; method_name_ = other.method_name_; @@ -243,6 +244,35 @@ Message::Message(uint32_t name, serialized_ = true; } +Message::Message(base::span<const uint8_t> payload, + base::span<ScopedHandle> handles) { + MojoResult rv = mojo::CreateMessage(&handle_); + DCHECK_EQ(MOJO_RESULT_OK, rv); + DCHECK(handle_.is_valid()); + + void* buffer; + uint32_t buffer_size; + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(payload.size())); + DCHECK(base::IsValueInRangeForNumericType<uint32_t>(handles.size())); + MojoAppendMessageDataOptions options; + options.struct_size = sizeof(options); + options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; + rv = MojoAppendMessageData( + handle_->value(), static_cast<uint32_t>(payload.size()), + reinterpret_cast<MojoHandle*>(handles.data()), + static_cast<uint32_t>(handles.size()), &options, &buffer, &buffer_size); + DCHECK_EQ(MOJO_RESULT_OK, rv); + // Handle ownership has been taken by MojoAppendMessageData. + for (auto& handle : handles) + ignore_result(handle.release()); + + payload_buffer_ = internal::Buffer(buffer, payload.size(), payload.size()); + std::copy(payload.begin(), payload.end(), + static_cast<uint8_t*>(payload_buffer_.data())); + transferable_ = true; + serialized_ = true; +} + // static Message Message::CreateFromMessageHandle(ScopedMessageHandle* message_handle) { DCHECK(message_handle); @@ -303,7 +333,8 @@ Message& Message::operator=(Message&& other) { other.transferable_ = false; serialized_ = other.serialized_; other.serialized_ = false; - other.heap_profiler_tag_ = heap_profiler_tag_; + heap_profiler_tag_ = other.heap_profiler_tag_; + receiver_connection_group_ = other.receiver_connection_group_; #if defined(ENABLE_IPC_FUZZER) interface_name_ = other.interface_name_; method_name_ = other.method_name_; @@ -319,6 +350,7 @@ void Message::Reset() { transferable_ = false; serialized_ = false; heap_profiler_tag_ = nullptr; + receiver_connection_group_ = nullptr; } const uint8_t* Message::payload() const { diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc index 3fc58449c54..9da1f0aa620 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc @@ -372,6 +372,10 @@ void MultiplexRouter::SetMasterInterfaceName(const char* name) { connector_.SetWatcherHeapProfilerTag(name); } +void MultiplexRouter::SetConnectionGroup(ConnectionGroup::Ref ref) { + connector_.SetConnectionGroup(std::move(ref)); +} + InterfaceId MultiplexRouter::AssociateInterface( ScopedInterfaceEndpointHandle handle_to_send) { if (!handle_to_send.pending_association()) @@ -386,7 +390,7 @@ InterfaceId MultiplexRouter::AssociateInterface( id = next_interface_id_value_++; if (set_interface_id_namespace_bit_) id |= kInterfaceIdNamespaceMask; - } while (base::ContainsKey(endpoints_, id)); + } while (base::Contains(endpoints_, id)); InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id); endpoints_[id] = endpoint; @@ -438,7 +442,7 @@ void MultiplexRouter::CloseEndpointHandle( return; MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + DCHECK(base::Contains(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); DCHECK(!endpoint->client()); DCHECK(!endpoint->closed()); @@ -462,7 +466,7 @@ InterfaceEndpointController* MultiplexRouter::AttachEndpointClient( DCHECK(client); MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + DCHECK(base::Contains(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->AttachClient(client, std::move(runner)); @@ -481,7 +485,7 @@ void MultiplexRouter::DetachEndpointClient( DCHECK(IsValidInterfaceId(id)); MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + DCHECK(base::Contains(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->DetachClient(); @@ -549,7 +553,7 @@ bool MultiplexRouter::HasAssociatedEndpoints() const { if (endpoints_.size() == 0) return false; - return !base::ContainsKey(endpoints_, kMasterInterfaceId); + return !base::Contains(endpoints_, kMasterInterfaceId); } void MultiplexRouter::EnableBatchDispatch() { diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h index cca9e1808e9..7152453f37c 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h @@ -24,6 +24,7 @@ #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" #include "mojo/public/cpp/bindings/associated_group_controller.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/connector.h" #include "mojo/public/cpp/bindings/filter_chain.h" #include "mojo/public/cpp/bindings/interface_id.h" @@ -87,6 +88,11 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) MultiplexRouter // |name| must be a string literal. void SetMasterInterfaceName(const char* name); + // Adds this object to a ConnectionGroup identified by |ref|. All receiving + // pipe endpoints decoded from inbound messages on this MultiplexRouter will + // be added to the same group. + void SetConnectionGroup(ConnectionGroup::Ref ref); + // --------------------------------------------------------------------------- // The following public methods are safe to call from any sequence. diff --git a/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.cc b/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.cc new file mode 100644 index 00000000000..c30d0764bf4 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.cc @@ -0,0 +1,29 @@ +// Copyright 2019 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 "mojo/public/cpp/bindings/lib/pending_receiver_state.h" + +namespace mojo { +namespace internal { + +PendingReceiverState::PendingReceiverState() = default; + +PendingReceiverState::PendingReceiverState(ScopedMessagePipeHandle pipe) + : pipe(std::move(pipe)) {} + +PendingReceiverState::PendingReceiverState(PendingReceiverState&&) noexcept = + default; + +PendingReceiverState::~PendingReceiverState() = default; + +PendingReceiverState& PendingReceiverState::operator=( + PendingReceiverState&&) noexcept = default; + +void PendingReceiverState::reset() { + pipe.reset(); + connection_group.reset(); +} + +} // namespace internal +} // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.h b/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.h new file mode 100644 index 00000000000..3bc34a94a61 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/lib/pending_receiver_state.h @@ -0,0 +1,37 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_RECEIVER_STATE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_RECEIVER_STATE_H_ + +#include "base/component_export.h" +#include "mojo/public/cpp/bindings/connection_group.h" +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { +namespace internal { + +// Generic state owned by every templated InterfaceRequest or PendingReceiver +// instance. +struct COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) PendingReceiverState { + public: + PendingReceiverState(); + explicit PendingReceiverState(ScopedMessagePipeHandle pipe); + PendingReceiverState(const PendingReceiverState&) = delete; + PendingReceiverState(PendingReceiverState&&) noexcept; + ~PendingReceiverState(); + + PendingReceiverState& operator=(const PendingReceiverState&) = delete; + PendingReceiverState& operator=(PendingReceiverState&&) noexcept; + + void reset(); + + ScopedMessagePipeHandle pipe; + ConnectionGroup::Ref connection_group; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_RECEIVER_STATE_H_ diff --git a/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.cc b/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.cc new file mode 100644 index 00000000000..cf99e42ecf8 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.cc @@ -0,0 +1,35 @@ +// Copyright 2019 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 "mojo/public/cpp/bindings/lib/pending_remote_state.h" + +namespace mojo { +namespace internal { + +PendingRemoteState::PendingRemoteState() = default; + +PendingRemoteState::PendingRemoteState(ScopedMessagePipeHandle pipe, + uint32_t version) + : pipe(std::move(pipe)), version(version) {} + +PendingRemoteState::PendingRemoteState(PendingRemoteState&&) noexcept = default; + +PendingRemoteState::~PendingRemoteState() = default; + +PendingRemoteState& PendingRemoteState::operator=( + PendingRemoteState&& other) noexcept { + reset(); + pipe = std::move(other.pipe); + version = other.version; + other.version = 0; + return *this; +} + +void PendingRemoteState::reset() { + pipe.reset(); + version = 0; +} + +} // namespace internal +} // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.h b/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.h new file mode 100644 index 00000000000..ad22f2243f4 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/lib/pending_remote_state.h @@ -0,0 +1,39 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_REMOTE_STATE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_REMOTE_STATE_H_ + +#include <stdint.h> + +#include "base/component_export.h" +#include "mojo/public/cpp/bindings/connection_group.h" +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { +namespace internal { + +// Generic state owned by every templated InterfacePtrInfo or PendingRemote +// instance. +struct COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) PendingRemoteState { + public: + PendingRemoteState(); + PendingRemoteState(ScopedMessagePipeHandle pipe, uint32_t version); + PendingRemoteState(const PendingRemoteState&) = delete; + PendingRemoteState(PendingRemoteState&&) noexcept; + ~PendingRemoteState(); + + PendingRemoteState& operator=(const PendingRemoteState&) = delete; + PendingRemoteState& operator=(PendingRemoteState&&) noexcept; + + void reset(); + + ScopedMessagePipeHandle pipe; + uint32_t version = 0; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_PENDING_REMOTE_STATE_H_ diff --git a/chromium/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc b/chromium/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc index 6543802d2e1..c443c65cf55 100644 --- a/chromium/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc +++ b/chromium/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc @@ -18,6 +18,7 @@ #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" #include "base/threading/sequence_local_storage_slot.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "mojo/public/cpp/bindings/sync_event_watcher.h" namespace mojo { @@ -60,8 +61,7 @@ class SequenceLocalSyncEventWatcher::SequenceLocalState { base::WaitableEvent::InitialState::NOT_SIGNALED), event_watcher_(&event_, base::BindRepeating(&SequenceLocalState::OnEventSignaled, - base::Unretained(this))), - weak_ptr_factory_(this) { + base::Unretained(this))) { // We always allow this event handler to be awoken during any sync event on // the sequence. Individual watchers still must opt into having such // wake-ups propagated to them. @@ -105,7 +105,11 @@ class SequenceLocalSyncEventWatcher::SequenceLocalState { if (registered_watchers_.empty()) { // If no more watchers are registered, clear our sequence-local storage. // Deletes |this|. - GetStorageSlot().reset(); + // Check if the current task runner is valid before doing this to avoid + // races at shutdown when other objects use SequenceLocalStorageSlot and + // indirectly call to here. + if (base::SequencedTaskRunnerHandle::IsSet()) + GetStorageSlot().reset(); } } @@ -190,7 +194,7 @@ class SequenceLocalSyncEventWatcher::SequenceLocalState { base::Lock ready_watchers_lock_; base::flat_set<const SequenceLocalSyncEventWatcher*> ready_watchers_; - base::WeakPtrFactory<SequenceLocalState> weak_ptr_factory_; + base::WeakPtrFactory<SequenceLocalState> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SequenceLocalState); }; diff --git a/chromium/mojo/public/cpp/bindings/lib/serialization_context.cc b/chromium/mojo/public/cpp/bindings/lib/serialization_context.cc index 267b54154bf..a804cfe3976 100644 --- a/chromium/mojo/public/cpp/bindings/lib/serialization_context.cc +++ b/chromium/mojo/public/cpp/bindings/lib/serialization_context.cc @@ -59,6 +59,7 @@ void SerializationContext::AddAssociatedInterfaceInfo( } void SerializationContext::TakeHandlesFromMessage(Message* message) { + receiver_connection_group_ = message->receiver_connection_group(); handles_.swap(*message->mutable_handles()); associated_endpoint_handles_.swap( *message->mutable_associated_endpoint_handles()); @@ -72,6 +73,14 @@ mojo::ScopedHandle SerializationContext::TakeHandle( return std::move(handles_[encoded_handle.value]); } +void SerializationContext::TakeHandleAsReceiver( + const Handle_Data& encoded_handle, + PendingReceiverState* receiver_state) { + receiver_state->pipe = TakeHandleAs<MessagePipeHandle>(encoded_handle); + if (receiver_connection_group_) + receiver_state->connection_group = *receiver_connection_group_; +} + mojo::ScopedInterfaceEndpointHandle SerializationContext::TakeAssociatedEndpointHandle( const AssociatedEndpointHandle_Data& encoded_handle) { diff --git a/chromium/mojo/public/cpp/bindings/lib/serialization_context.h b/chromium/mojo/public/cpp/bindings/lib/serialization_context.h index 0e3c0788dce..d8fecc4f5b4 100644 --- a/chromium/mojo/public/cpp/bindings/lib/serialization_context.h +++ b/chromium/mojo/public/cpp/bindings/lib/serialization_context.h @@ -13,7 +13,9 @@ #include "base/component_export.h" #include "base/containers/stack_container.h" #include "base/macros.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/pending_receiver_state.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/handle.h" @@ -51,6 +53,10 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) SerializationContext { uint32_t version, AssociatedInterface_Data* out_data); + const ConnectionGroup::Ref* receiver_connection_group() const { + return receiver_connection_group_; + } + const std::vector<mojo::ScopedHandle>* handles() { return &handles_; } std::vector<mojo::ScopedHandle>* mutable_handles() { return &handles_; } @@ -77,10 +83,19 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) SerializationContext { return ScopedHandleBase<T>::From(TakeHandle(encoded_handle)); } + // Takes a handle from the list of serialized handle data and stuffs it into + // the internal data of an InterfaceRequest or PendingReceiver. + void TakeHandleAsReceiver(const Handle_Data& encoded_handle, + PendingReceiverState* receiver_state); + mojo::ScopedInterfaceEndpointHandle TakeAssociatedEndpointHandle( const AssociatedEndpointHandle_Data& encoded_handle); private: + // The ConnectionGroup to which deserialized PendingReceivers should be added, + // if any. + const ConnectionGroup::Ref* receiver_connection_group_ = nullptr; + // Handles owned by this object. Used during serialization to hold onto // handles accumulated during pre-serialization, and used during // deserialization to hold onto handles extracted from a message. diff --git a/chromium/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/chromium/mojo/public/cpp/bindings/lib/sync_handle_registry.cc index 3278a76a676..b91d5f9c29c 100644 --- a/chromium/mojo/public/cpp/bindings/lib/sync_handle_registry.cc +++ b/chromium/mojo/public/cpp/bindings/lib/sync_handle_registry.cc @@ -36,7 +36,7 @@ bool SyncHandleRegistry::RegisterHandle(const Handle& handle, const HandleCallback& callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (base::ContainsKey(handles_, handle)) + if (base::Contains(handles_, handle)) return false; MojoResult result = wait_set_.AddHandle(handle, handle_signals); @@ -49,7 +49,7 @@ bool SyncHandleRegistry::RegisterHandle(const Handle& handle, void SyncHandleRegistry::UnregisterHandle(const Handle& handle) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!base::ContainsKey(handles_, handle)) + if (!base::Contains(handles_, handle)) return; MojoResult result = wait_set_.RemoveHandle(handle); @@ -85,17 +85,14 @@ void SyncHandleRegistry::UnregisterEvent(base::WaitableEvent* event, // Not safe to remove any elements from |callbacks| here since an outer // stack frame is currently iterating over it in Wait(). for (auto& cb : callbacks) { - if (cb.Equals(callback)) + if (cb == callback) cb.Reset(); else if (cb) has_valid_callbacks = true; } remove_invalid_event_callbacks_after_dispatch_ = true; } else { - callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(), - [&callback](const base::Closure& cb) { - return cb.Equals(callback); - }), + callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), callback), callbacks.end()); if (callbacks.empty()) events_.erase(it); diff --git a/chromium/mojo/public/cpp/bindings/message.h b/chromium/mojo/public/cpp/bindings/message.h index 4ff8a6b5e63..1d359526357 100644 --- a/chromium/mojo/public/cpp/bindings/message.h +++ b/chromium/mojo/public/cpp/bindings/message.h @@ -16,8 +16,10 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/component_export.h" +#include "base/containers/span.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/lib/buffer.h" #include "mojo/public/cpp/bindings/lib/message_internal.h" #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h" @@ -68,6 +70,14 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message { size_t payload_interface_id_count, std::vector<ScopedHandle>* handles); + // Constructs a new serialized Message object from a fully populated message + // payload (including a well-formed message header) and an optional set of + // handle attachments. This Message may not be extended with additional + // payload or handles once constructed, but its payload remains mutable as + // long as the Message is not moved and neither |Reset()| nor + // |TakeMojoMessage()| is called. + Message(base::span<const uint8_t> payload, base::span<ScopedHandle> handles); + // Constructs a new serialized Message object from an existing // ScopedMessageHandle; e.g., one read from a message pipe. // @@ -178,6 +188,17 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message { return &associated_endpoint_handles_; } + // Sets the ConnectionGroup to which this Message's local receiver belongs, if + // any. This is called immediately after a Message is read from a message pipe + // but before it's deserialized. If non-null, |ref| must point to a Ref that + // outlives this Message object. + void set_receiver_connection_group(const ConnectionGroup::Ref* ref) { + receiver_connection_group_ = ref; + } + const ConnectionGroup::Ref* receiver_connection_group() const { + return receiver_connection_group_; + } + // Takes ownership of any handles within |*context| and attaches them to this // Message. void AttachHandlesFromSerializationContext( @@ -268,6 +289,11 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message { const char* method_name_ = nullptr; #endif + // A reference to the ConnectionGroup to which the receiver of this Message + // belongs, if any. Only set if this Message was just read off of a message + // pipe and is about to be deserialized. + const ConnectionGroup::Ref* receiver_connection_group_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(Message); }; diff --git a/chromium/mojo/public/cpp/bindings/pending_receiver.h b/chromium/mojo/public/cpp/bindings/pending_receiver.h index 9d02333fe50..bffc6e90170 100644 --- a/chromium/mojo/public/cpp/bindings/pending_receiver.h +++ b/chromium/mojo/public/cpp/bindings/pending_receiver.h @@ -8,7 +8,11 @@ #include <utility> #include "base/macros.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/pending_receiver_state.h" +#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/system/message_pipe.h" namespace mojo { @@ -49,7 +53,7 @@ class PendingReceiver { // Constructs a valid PendingReceiver from a valid raw message pipe handle. explicit PendingReceiver(ScopedMessagePipeHandle pipe) - : pipe_(std::move(pipe)) {} + : state_(std::move(pipe)) {} ~PendingReceiver() = default; @@ -64,14 +68,14 @@ class PendingReceiver { // Indicates whether the PendingReceiver is valid, meaning it can ne used to // bind a Receiver that wants to begin dispatching method calls made by the // entangled Remote. - bool is_valid() const { return pipe_.is_valid(); } + bool is_valid() const { return state_.pipe.is_valid(); } explicit operator bool() const { return is_valid(); } // Resets this PendingReceiver to an invalid state. If it was entangled with a // Remote or PendingRemote, that object remains in a valid state and will // eventually detect that its receiver is gone. Any calls it makes will // effectively be dropped. - void reset() { pipe_.reset(); } + void reset() { state_.reset(); } // Like above but provides a reason for the disconnection. void ResetWithReason(uint32_t reason, const std::string& description) { @@ -83,11 +87,30 @@ class PendingReceiver { // call, the PendingReceiver is no longer in a valid state and can no longer // be used to bind a Receiver. ScopedMessagePipeHandle PassPipe() WARN_UNUSED_RESULT { - return std::move(pipe_); + return std::move(state_.pipe); } + // Assigns this PendingReceiver to the ConnectionGroup referenced by |ref|. + // Any Receiver which binds this PendingReceiver will inherit the Ref. + void set_connection_group(ConnectionGroup::Ref ref) { + state_.connection_group = std::move(ref); + } + + const ConnectionGroup::Ref& connection_group() const { + return state_.connection_group; + } + + // Passes ownership of this PendingReceiver's ConnectionGroup Ref, removing it + // from its group. + ConnectionGroup::Ref PassConnectionGroupRef() { + return std::move(state_.connection_group); + } + + // For internal Mojo use only. + internal::PendingReceiverState* internal_state() { return &state_; } + private: - ScopedMessagePipeHandle pipe_; + internal::PendingReceiverState state_; DISALLOW_COPY_AND_ASSIGN(PendingReceiver); }; diff --git a/chromium/mojo/public/cpp/bindings/pending_remote.h b/chromium/mojo/public/cpp/bindings/pending_remote.h index 4141633489b..e2788ce7c55 100644 --- a/chromium/mojo/public/cpp/bindings/pending_remote.h +++ b/chromium/mojo/public/cpp/bindings/pending_remote.h @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/bindings/lib/pending_remote_state.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -56,7 +57,7 @@ class PendingRemote { // Constructs a valid PendingRemote over a valid raw message pipe handle and // expected interface version number. PendingRemote(ScopedMessagePipeHandle pipe, uint32_t version) - : pipe_(std::move(pipe)), version_(version) {} + : state_(std::move(pipe), version) {} ~PendingRemote() = default; @@ -65,7 +66,7 @@ class PendingRemote { // Indicates whether the PendingRemote is valid, meaning it can be used to // bind a Remote that wants to begin issuing method calls to be dispatched by // the entangled Receiver. - bool is_valid() const { return pipe_.is_valid(); } + bool is_valid() const { return state_.pipe.is_valid(); } explicit operator bool() const { return is_valid(); } // Temporary helper for transitioning away from old bindings types. This is @@ -77,23 +78,20 @@ class PendingRemote { // Resets this PendingRemote to an invalid state. If it was entangled with a // Receiver or PendingReceiver, that object remains in a valid state and will // eventually detect that its remote caller is gone. - void reset() { - pipe_.reset(); - version_ = 0; - } + void reset() { state_.reset(); } // Takes ownership of this PendingRemote's message pipe handle. After this // call, the PendingRemote is no longer in a valid state and can no longer be // used to bind a Remote. ScopedMessagePipeHandle PassPipe() WARN_UNUSED_RESULT { - version_ = 0; - return std::move(pipe_); + state_.version = 0; + return std::move(state_.pipe); } // The version of the interface this Remote is assuming when making method // calls. For the most common case of unversioned mojom interfaces, this is // always zero. - uint32_t version() const { return version_; } + uint32_t version() const { return state_.version; } // Creates a new message pipe, retaining one end in the PendingRemote (making // it valid) and returning the other end as its entangled PendingReceiver. May @@ -102,13 +100,15 @@ class PendingRemote { WARN_UNUSED_RESULT { DCHECK(!is_valid()) << "PendingRemote already has a receiver"; MessagePipe pipe; - pipe_ = std::move(pipe.handle0); + state_.pipe = std::move(pipe.handle0); return PendingReceiver<Interface>(std::move(pipe.handle1)); } + // For internal Mojo use only. + internal::PendingRemoteState* internal_state() { return &state_; } + private: - ScopedMessagePipeHandle pipe_; - uint32_t version_ = 0; + internal::PendingRemoteState state_; DISALLOW_COPY_AND_ASSIGN(PendingRemote); }; diff --git a/chromium/mojo/public/cpp/bindings/receiver.h b/chromium/mojo/public/cpp/bindings/receiver.h index d30c5d15b54..0bac69338bc 100644 --- a/chromium/mojo/public/cpp/bindings/receiver.h +++ b/chromium/mojo/public/cpp/bindings/receiver.h @@ -14,6 +14,7 @@ #include "base/memory/scoped_refptr.h" #include "base/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/connection_error_callback.h" +#include "mojo/public/cpp/bindings/connection_group.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/lib/binding_state.h" #include "mojo/public/cpp/bindings/pending_remote.h" @@ -154,10 +155,12 @@ class Receiver { // Receiver. void Bind(PendingReceiver<Interface> pending_receiver, scoped_refptr<base::SequencedTaskRunner> task_runner) { - if (pending_receiver) - internal_state_.Bind(pending_receiver.PassPipe(), std::move(task_runner)); - else + if (pending_receiver) { + internal_state_.Bind(pending_receiver.internal_state(), + std::move(task_runner)); + } else { reset(); + } } // Unbinds this Receiver, preventing any further |impl| method calls or diff --git a/chromium/mojo/public/cpp/bindings/receiver_set.h b/chromium/mojo/public/cpp/bindings/receiver_set.h index 5019554903c..cf740d2695a 100644 --- a/chromium/mojo/public/cpp/bindings/receiver_set.h +++ b/chromium/mojo/public/cpp/bindings/receiver_set.h @@ -94,7 +94,7 @@ class ReceiverSetBase { using Context = typename ContextTraits::Type; using PreDispatchCallback = base::RepeatingCallback<void(const Context&)>; - ReceiverSetBase() : weak_ptr_factory_(this) {} + ReceiverSetBase() = default; // Sets a callback to be invoked any time a receiver in the set is // disconnected. The callback is invoked *after* the receiver in question @@ -230,6 +230,13 @@ class ReceiverSetBase { current_receiver()); } + void FlushForTesting() { + for (const auto& it : receivers_) { + if (it.second) + it.second->FlushForTesting(); + } + } + private: friend class Entry; @@ -252,6 +259,8 @@ class ReceiverSetBase { base::BindOnce(&Entry::OnDisconnect, base::Unretained(this))); } + void FlushForTesting() { receiver_.FlushForTesting(); } + private: class DispatchFilter : public MessageReceiver { public: @@ -329,7 +338,7 @@ class ReceiverSetBase { std::map<ReceiverId, std::unique_ptr<Entry>> receivers_; const Context* current_context_ = nullptr; ReceiverId current_receiver_; - base::WeakPtrFactory<ReceiverSetBase> weak_ptr_factory_; + base::WeakPtrFactory<ReceiverSetBase> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(ReceiverSetBase); }; @@ -338,14 +347,6 @@ class ReceiverSetBase { template <typename Interface, typename ContextType = void> using ReceiverSet = ReceiverSetBase<Receiver<Interface>, ContextType>; -// Helper for a set of Receivers where each bound Receiver is tied to an owned -// implementation. The |Add()| method takes a std::unique_ptr<Interface> for -// each bound implementation. -template <typename Interface, typename ContextType = void> -using OwnedReceiverSet = - ReceiverSetBase<Receiver<Interface, UniquePtrImplRefTraits<Interface>>, - ContextType>; - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_RECEIVER_SET_H_ diff --git a/chromium/mojo/public/cpp/bindings/remote.h b/chromium/mojo/public/cpp/bindings/remote.h index f709d03352c..60744438cbf 100644 --- a/chromium/mojo/public/cpp/bindings/remote.h +++ b/chromium/mojo/public/cpp/bindings/remote.h @@ -14,6 +14,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/sequenced_task_runner.h" +#include "base/time/time.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/lib/interface_ptr_state.h" #include "mojo/public/cpp/bindings/pending_remote.h" @@ -151,6 +152,54 @@ class Remote { std::move(handler)); } + // A convenient helper that resets this Remote on disconnect. Note that this + // replaces any previously set disconnection handler. + void reset_on_disconnect() { + if (!is_connected()) { + reset(); + return; + } + set_disconnect_handler( + base::BindOnce(&Remote::reset, base::Unretained(this))); + } + + // Sets a Closure to be invoked if the receiving endpoint reports itself as + // idle and there are no in-flight messages it has yet to acknowledge, and + // this state occurs continuously for a duration of at least |timeout|. The + // first time this is called, it must be called BEFORE sending any interface + // messages to the receiver. It may be called any number of times after that + // to reconfigure the idle timeout period or assign a new idle handler. + // + // Once called, the interface connection incurs some permanent additional + // per-message overhead to help track idle state across the interface + // boundary. + // + // Whenever this callback is invoked, the following conditions are guaranteed + // to hold: + // + // - There are no messages sent on this Remote that have not already been + // dispatched by the receiver. + // - The receiver has explicitly notified us that it considers itself to be + // "idle." + // - The receiver has not dispatched any additional messages since sending + // this idle notification + // - The Remote does not have any outstanding reply callbacks that haven't + // been called yet + // - All of the above has been true continuously for a duration of at least + // |timeout|. + // + void set_idle_handler(base::TimeDelta timeout, + base::RepeatingClosure handler) { + internal_state_.set_idle_handler(timeout, std::move(handler)); + } + + // A convenient helper for common idle timeout behavior. This is equivalent to + // calling |set_idle_handler| with a handler that only resets this Remote. + void reset_on_idle_timeout(base::TimeDelta timeout) { + set_idle_handler( + timeout, base::BindRepeating(&Remote::reset, base::Unretained(this))); + } + // Resets this Remote to an unbound state. To reset the Remote and recover an // PendingRemote that can be bound again later, use |Unbind()| instead. void reset() { @@ -213,8 +262,7 @@ class Remote { return; } - internal_state_.Bind(InterfacePtrInfo<Interface>(pending_remote.PassPipe(), - pending_remote.version()), + internal_state_.Bind(pending_remote.internal_state(), std::move(task_runner)); // Force the internal state to configure its proxy. Unlike InterfacePtr we @@ -270,6 +318,12 @@ class Remote { internal_state_.FlushAsyncForTesting(std::move(callback)); } + // Returns the number of unacknowledged messages sent by this Remote. Only + // non-zero when |set_idle_handler()| has been called. + unsigned int GetNumUnackedMessagesForTesting() const { + return internal_state_.GetNumUnackedMessagesForTesting(); + } + // DO NOT USE. Exposed only for internal use and for testing. internal::InterfacePtrState<Interface>* internal_state() { return &internal_state_; diff --git a/chromium/mojo/public/cpp/bindings/remote_set.h b/chromium/mojo/public/cpp/bindings/remote_set.h new file mode 100644 index 00000000000..82a00999c23 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/remote_set.h @@ -0,0 +1,162 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_ + +#include <iterator> +#include <set> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "base/util/type_safety/id_type.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace mojo { + +namespace internal { +struct RemoteSetElementIdTypeTag {}; +} // namespace internal + +using RemoteSetElementId = util::IdTypeU32<internal::RemoteSetElementIdTypeTag>; + +// Shared implementation of a set of remotes, used by both RemoteSet and +// AssociatedRemoteSet aliases (see below). +// +// A RemoteSet or AssociatedRemoteSet is a collection of remote interface +// endpoints whose lifetime is conveniently managed by the set, i.e., remotes +// are removed from the set automatically when losing a connection). +template <typename Interface, + template <typename> + class RemoteType, + template <typename> + class PendingRemoteType> +class RemoteSetImpl { + public: + using Storage = std::map<RemoteSetElementId, RemoteType<Interface>>; + + // An iterator definition to support range-for iteration over RemoteSet + // objects. An iterator can be dereferenced to get at the Remote, and |id()| + // can be called to get the element's ID (for e.g. later removal). + struct Iterator { + using self_type = Iterator; + using value_type = RemoteType<Interface>; + using reference = const value_type&; + using pointer = const value_type*; + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + + explicit Iterator(typename Storage::const_iterator it) : it_(it) {} + + self_type& operator++() { + ++it_; + return *this; + } + self_type operator++(int) { + self_type result(*this); + ++(*this); + return result; + } + self_type& operator--() { + --it_; + return *this; + } + self_type operator--(int) { + self_type result(*this); + --(*this); + return result; + } + + RemoteSetElementId id() const { return it_.first; } + + reference operator*() const { return it_->second; } + pointer operator->() const { return &it_->second; } + bool operator==(const self_type& rhs) { return it_ == rhs.it_; } + bool operator!=(const self_type& rhs) { return it_ != rhs.it_; } + + private: + typename Storage::const_iterator it_; + }; + + RemoteSetImpl() = default; + ~RemoteSetImpl() = default; + + // Adds a new remote to this set and returns a unique ID that can be used to + // identify the remote later. + RemoteSetElementId Add(RemoteType<Interface> remote) { + auto id = GenerateNextElementId(); + remote.set_disconnect_handler(base::BindOnce(&RemoteSetImpl::OnDisconnect, + base::Unretained(this), id)); + auto result = storage_.emplace(id, std::move(remote)); + DCHECK(result.second); + return id; + } + + // Same as above but for the equivalent pending remote type, for convenience. + RemoteSetElementId Add(PendingRemoteType<Interface> remote) { + return Add(RemoteType<Interface>(std::move(remote))); + } + + // Removes a remote from the set given |id|, if present. + void Remove(RemoteSetElementId id) { storage_.erase(id); } + + // Indicates whether a remote with the given ID is present in the set. + bool Contains(RemoteSetElementId id) { return base::Contains(storage_, id); } + + // Sets a callback to invoke any time a remote in the set is disconnected. + // Note that the remote in question is already removed from the set by the + // time the callback is run for its disconnection. + using DisconnectHandler = base::RepeatingCallback<void(RemoteSetElementId)>; + void set_disconnect_handler(DisconnectHandler handler) { + disconnect_handler_ = std::move(handler); + } + + void Clear() { storage_.clear(); } + + bool empty() const { return storage_.empty(); } + Iterator begin() { return Iterator(storage_.begin()); } + Iterator begin() const { return Iterator(storage_.begin()); } + Iterator end() { return Iterator(storage_.end()); } + Iterator end() const { return Iterator(storage_.end()); } + + void FlushForTesting() { + for (auto& it : storage_) { + it.second.FlushForTesting(); + } + } + + private: + RemoteSetElementId GenerateNextElementId() { + return RemoteSetElementId::FromUnsafeValue(next_element_id_++); + } + + void OnDisconnect(RemoteSetElementId id) { + Remove(id); + if (disconnect_handler_) + disconnect_handler_.Run(id); + } + + uint32_t next_element_id_ = 1; + Storage storage_; + DisconnectHandler disconnect_handler_; + + DISALLOW_COPY_AND_ASSIGN(RemoteSetImpl); +}; + +template <typename Interface> +using RemoteSet = RemoteSetImpl<Interface, Remote, PendingRemote>; + +template <typename Interface> +using AssociatedRemoteSet = + RemoteSetImpl<Interface, AssociatedRemote, PendingAssociatedRemote>; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_ diff --git a/chromium/mojo/public/cpp/bindings/strong_associated_binding.h b/chromium/mojo/public/cpp/bindings/strong_associated_binding.h index dceb21f025e..f017956f334 100644 --- a/chromium/mojo/public/cpp/bindings/strong_associated_binding.h +++ b/chromium/mojo/public/cpp/bindings/strong_associated_binding.h @@ -43,9 +43,6 @@ using StrongAssociatedBindingPtr = template <typename Interface> class StrongAssociatedBinding { public: - using ImplPointerType = - typename AssociatedBinding<Interface>::ImplPointerType; - // Create a new StrongAssociatedBinding instance. The instance owns itself, // cleaning up only in the event of a pipe connection error. Returns a WeakPtr // to the new StrongAssociatedBinding instance. @@ -87,8 +84,11 @@ class StrongAssociatedBinding { void FlushForTesting() { binding_.FlushForTesting(); } // Allows test code to swap the interface implementation. - ImplPointerType SwapImplForTesting(ImplPointerType new_impl) { - return binding_.SwapImplForTesting(new_impl); + std::unique_ptr<Interface> SwapImplForTesting( + std::unique_ptr<Interface> new_impl) { + binding_.SwapImplForTesting(new_impl.get()); + impl_.swap(new_impl); + return new_impl; } private: diff --git a/chromium/mojo/public/cpp/bindings/strong_binding.h b/chromium/mojo/public/cpp/bindings/strong_binding.h index 441ce9baf22..a0fe6214772 100644 --- a/chromium/mojo/public/cpp/bindings/strong_binding.h +++ b/chromium/mojo/public/cpp/bindings/strong_binding.h @@ -104,8 +104,7 @@ class StrongBinding { InterfaceRequest<Interface> request, scoped_refptr<base::SequencedTaskRunner> task_runner) : impl_(std::move(impl)), - binding_(impl_.get(), std::move(request), std::move(task_runner)), - weak_factory_(this) { + binding_(impl_.get(), std::move(request), std::move(task_runner)) { binding_.set_connection_error_with_reason_handler( base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this))); } @@ -127,7 +126,7 @@ class StrongBinding { base::OnceClosure connection_error_handler_; ConnectionErrorWithReasonCallback connection_error_with_reason_handler_; Binding<Interface> binding_; - base::WeakPtrFactory<StrongBinding> weak_factory_; + base::WeakPtrFactory<StrongBinding> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(StrongBinding); }; diff --git a/chromium/mojo/public/cpp/bindings/struct_ptr.h b/chromium/mojo/public/cpp/bindings/struct_ptr.h index 4dcddd15f55..50533e9a300 100644 --- a/chromium/mojo/public/cpp/bindings/struct_ptr.h +++ b/chromium/mojo/public/cpp/bindings/struct_ptr.h @@ -99,10 +99,6 @@ class StructPtr { explicit operator bool() const { return !is_null(); } - bool operator<(const StructPtr& other) const { - return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed); - } - private: friend class internal::StructPtrWTFHelper<Struct>; void Take(StructPtr* other) { @@ -115,32 +111,23 @@ class StructPtr { DISALLOW_COPY_AND_ASSIGN(StructPtr); }; -template <typename T> -bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) { - return lhs.Equals(rhs); -} -template <typename T> -bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) { - return !(lhs == rhs); -} - // Designed to be used when Struct is small and copyable. template <typename S> class InlinedStructPtr { public: using Struct = S; - InlinedStructPtr() : state_(NIL) {} - InlinedStructPtr(std::nullptr_t) : state_(NIL) {} + InlinedStructPtr() = default; + InlinedStructPtr(std::nullptr_t) {} - ~InlinedStructPtr() {} + ~InlinedStructPtr() = default; InlinedStructPtr& operator=(std::nullptr_t) { reset(); return *this; } - InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); } + InlinedStructPtr(InlinedStructPtr&& other) { Take(&other); } InlinedStructPtr& operator=(InlinedStructPtr&& other) { Take(&other); return *this; @@ -198,10 +185,6 @@ class InlinedStructPtr { explicit operator bool() const { return !is_null(); } - bool operator<(const InlinedStructPtr& other) const { - return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed); - } - private: friend class internal::InlinedStructPtrWTFHelper<Struct>; void Take(InlinedStructPtr* other) { @@ -210,28 +193,17 @@ class InlinedStructPtr { } enum State { - VALID, NIL, + VALID, DELETED, // For use in WTF::HashMap only }; mutable Struct value_; - State state_; + State state_ = NIL; DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr); }; -template <typename T> -bool operator==(const InlinedStructPtr<T>& lhs, - const InlinedStructPtr<T>& rhs) { - return lhs.Equals(rhs); -} -template <typename T> -bool operator!=(const InlinedStructPtr<T>& lhs, - const InlinedStructPtr<T>& rhs) { - return !(lhs == rhs); -} - namespace internal { template <typename Struct> @@ -269,7 +241,56 @@ class InlinedStructPtrWTFHelper { } }; +// Convenience type trait so that we can get away with defining the comparison +// operators only once. +template <typename T> +struct IsStructPtrImpl : std::false_type {}; + +template <typename S> +struct IsStructPtrImpl<StructPtr<S>> : std::true_type {}; + +template <typename S> +struct IsStructPtrImpl<InlinedStructPtr<S>> : std::true_type {}; + } // namespace internal + +template <typename T> +constexpr bool IsStructPtrV = internal::IsStructPtrImpl<std::decay_t<T>>::value; + +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator==(const Ptr& lhs, const Ptr& rhs) { + return lhs.Equals(rhs); +} + +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator!=(const Ptr& lhs, const Ptr& rhs) { + return !(lhs == rhs); +} + +// Perform a deep comparison if possible. Otherwise treat null pointers less +// than valid pointers. +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator<(const Ptr& lhs, const Ptr& rhs) { + if (!lhs || !rhs) + return bool{lhs} < bool{rhs}; + return *lhs < *rhs; +} + +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator<=(const Ptr& lhs, const Ptr& rhs) { + return !(rhs < lhs); +} + +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator>(const Ptr& lhs, const Ptr& rhs) { + return rhs < lhs; +} + +template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr> +bool operator>=(const Ptr& lhs, const Ptr& rhs) { + return !(lhs < rhs); +} + } // namespace mojo namespace std { diff --git a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn index 73c5ba421b9..709abe86c68 100644 --- a/chromium/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/tests/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/public/tools/bindings/mojom.gni") + source_set("tests") { testonly = true @@ -12,6 +14,7 @@ source_set("tests") { "bindings_test_base.h", "buffer_unittest.cc", "callback_helpers_unittest.cc", + "connection_group_unittest.cc", "connector_unittest.cc", "constant_unittest.cc", "container_test_util.cc", @@ -20,10 +23,12 @@ source_set("tests") { "equals_unittest.cc", "handle_passing_unittest.cc", "hash_unittest.cc", + "idle_tracking_unittest.cc", "lazy_serialization_unittest.cc", "map_unittest.cc", "message_queue.cc", "message_queue.h", + "message_unittest.cc", "multiplex_router_unittest.cc", "native_struct_unittest.cc", "new_endpoint_types_unittest.cc", @@ -50,6 +55,7 @@ source_set("tests") { deps = [ ":mojo_public_bindings_test_utils", + ":test_mojom", "//base/test:test_support", "//mojo/core/embedder", "//mojo/public/cpp/bindings", @@ -98,6 +104,7 @@ if (!is_ios) { ] deps = [ + "//base/test:test_support", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//mojo/public/interfaces/bindings/tests:test_export_blink_component", @@ -123,6 +130,16 @@ source_set("struct_with_traits_impl") { ] } +mojom("test_mojom") { + testonly = true + sources = [ + "connection_group_unittest.test-mojom", + "idle_tracking_unittest.test-mojom", + ] + + support_lazy_serialization = true +} + source_set("perftests") { testonly = true diff --git a/chromium/mojo/public/cpp/bindings/unique_associated_receiver_set.h b/chromium/mojo/public/cpp/bindings/unique_associated_receiver_set.h new file mode 100644 index 00000000000..cabffa441f2 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/unique_associated_receiver_set.h @@ -0,0 +1,39 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_ + +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h" + +namespace mojo { + +template <typename Interface, typename ImplRefTraits> +struct ReceiverSetTraits<AssociatedReceiver<Interface, ImplRefTraits>> { + using InterfaceType = Interface; + using PendingType = PendingAssociatedReceiver<Interface>; + using ImplPointerType = typename ImplRefTraits::PointerType; +}; + +// This class manages a set of associated receiving endpoints where each +// endpoint is bound to a unique implementation of the interface owned by this +// object. That is to say, for every bound AssociatedReceiver<T> in the set, +// there is a dedicated unique_ptr<T> owned by the set and receiving messages. +// +// Each owned implementation of T has its lifetime automatically managed by the +// UniqueAssociatedReceiverSet, destroying an instance whenever its receiver is +// disconnected because the remote endpoint intentionally hung up, crashed, or +// sent malformed message. +template <typename Interface, + typename ContextType = void, + typename Deleter = std::default_delete<Interface>> +using UniqueAssociatedReceiverSet = ReceiverSetBase< + AssociatedReceiver<Interface, UniquePtrImplRefTraits<Interface, Deleter>>, + ContextType>; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_ASSOCIATED_RECEIVER_SET_H_ diff --git a/chromium/mojo/public/cpp/platform/BUILD.gn b/chromium/mojo/public/cpp/platform/BUILD.gn index 79c017608d8..8e1e014ede7 100644 --- a/chromium/mojo/public/cpp/platform/BUILD.gn +++ b/chromium/mojo/public/cpp/platform/BUILD.gn @@ -8,7 +8,6 @@ component("platform") { output_name = "mojo_cpp_platform" public = [ - "features.h", "platform_channel.h", "platform_channel_endpoint.h", "platform_channel_server_endpoint.h", @@ -16,7 +15,6 @@ component("platform") { ] sources = [ - "features.cc", "named_platform_channel_win.cc", "platform_channel.cc", "platform_channel_endpoint.cc", @@ -24,7 +22,7 @@ component("platform") { "platform_handle.cc", ] - if (is_posix && (!is_nacl || is_nacl_nonsfi)) { + if (is_posix && !is_mac && (!is_nacl || is_nacl_nonsfi)) { public += [ "socket_utils_posix.h" ] sources += [ "socket_utils_posix.cc" ] } @@ -34,7 +32,7 @@ component("platform") { "//mojo/public/c/system:headers", ] - if (is_posix && !is_nacl) { + if (is_posix && !is_nacl && !is_mac) { sources += [ "named_platform_channel_posix.cc" ] } diff --git a/chromium/mojo/public/cpp/platform/features.cc b/chromium/mojo/public/cpp/platform/features.cc deleted file mode 100644 index 79bdd62c41f..00000000000 --- a/chromium/mojo/public/cpp/platform/features.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019 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 "mojo/public/cpp/platform/features.h" - -namespace mojo { -namespace features { - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Enables the ChannelMac implementation, which uses Mach IPC as the underlying -// transport mechanism for PlatformChannel. Otherwise, macOS defaults to using -// ChannelPosix. -const base::Feature kMojoChannelMac{"MojoChannelMac", - base::FEATURE_DISABLED_BY_DEFAULT}; -#endif - -} // namespace features -} // namespace mojo diff --git a/chromium/mojo/public/cpp/platform/features.h b/chromium/mojo/public/cpp/platform/features.h deleted file mode 100644 index 0ab2980e588..00000000000 --- a/chromium/mojo/public/cpp/platform/features.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 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 MOJO_PUBLIC_CPP_PLATFORM_FEATURES_H_ -#define MOJO_PUBLIC_CPP_PLATFORM_FEATURES_H_ - -#include "base/component_export.h" -#include "base/feature_list.h" -#include "build/build_config.h" - -namespace mojo { -namespace features { - -#if defined(OS_MACOSX) && !defined(OS_IOS) -COMPONENT_EXPORT(MOJO_CPP_PLATFORM) -extern const base::Feature kMojoChannelMac; -#endif - -} // namespace features -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_PLATFORM_FEATURES_H_ diff --git a/chromium/mojo/public/cpp/platform/named_platform_channel_mac.cc b/chromium/mojo/public/cpp/platform/named_platform_channel_mac.cc index 0a2df95360d..08d2d51f2bb 100644 --- a/chromium/mojo/public/cpp/platform/named_platform_channel_mac.cc +++ b/chromium/mojo/public/cpp/platform/named_platform_channel_mac.cc @@ -28,13 +28,8 @@ std::string GetBootstrapName(const std::string& name) { } // namespace -// TODO(crbug.com/932175): This namespace will go away and these methods -// will be defined direclty on NamedPlatformChannel. -namespace NamedPlatformChannelMac { - // static -COMPONENT_EXPORT(MOJO_CPP_PLATFORM) -PlatformChannelServerEndpoint CreateServerEndpoint( +PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint( const NamedPlatformChannel::Options& options, NamedPlatformChannel::ServerName* server_name) { const std::string bootstrap_name = GetBootstrapName(options.server_name); @@ -64,8 +59,7 @@ PlatformChannelServerEndpoint CreateServerEndpoint( } // static -COMPONENT_EXPORT(MOJO_CPP_PLATFORM) -PlatformChannelEndpoint CreateClientEndpoint( +PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint( const NamedPlatformChannel::ServerName& server_name) { base::mac::ScopedMachSendRight send_right; kern_return_t kr = bootstrap_look_up( @@ -79,5 +73,4 @@ PlatformChannelEndpoint CreateClientEndpoint( return PlatformChannelEndpoint(PlatformHandle(std::move(send_right))); } -} // namespace NamedPlatformChannelMac } // namespace mojo diff --git a/chromium/mojo/public/cpp/platform/named_platform_channel_posix.cc b/chromium/mojo/public/cpp/platform/named_platform_channel_posix.cc index d5f82d86430..9082ac4da59 100644 --- a/chromium/mojo/public/cpp/platform/named_platform_channel_posix.cc +++ b/chromium/mojo/public/cpp/platform/named_platform_channel_posix.cc @@ -15,8 +15,6 @@ #include "base/posix/eintr_wrapper.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" -#include "build/build_config.h" -#include "mojo/public/cpp/platform/features.h" namespace mojo { @@ -79,26 +77,10 @@ PlatformHandle CreateUnixDomainSocket() { } // namespace -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Temporarily forward declare named_platform_channel_mac.cc symbols. -namespace NamedPlatformChannelMac { -PlatformChannelServerEndpoint CreateServerEndpoint( - const NamedPlatformChannel::Options& options, - NamedPlatformChannel::ServerName* server_name); -PlatformChannelEndpoint CreateClientEndpoint( - const NamedPlatformChannel::ServerName& server_name); -} // namespace NamedPlatformChannelMac -#endif - // static PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint( const Options& options, ServerName* server_name) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - return NamedPlatformChannelMac::CreateServerEndpoint(options, server_name); - } -#endif ServerName name = options.server_name; if (name.empty()) name = GenerateRandomServerName(options); @@ -146,11 +128,6 @@ PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint( // static PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint( const ServerName& server_name) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - return NamedPlatformChannelMac::CreateClientEndpoint(server_name); - } -#endif DCHECK(!server_name.empty()); struct sockaddr_un unix_addr; diff --git a/chromium/mojo/public/cpp/platform/platform_channel.cc b/chromium/mojo/public/cpp/platform/platform_channel.cc index 8502ae35c95..3320bcce7eb 100644 --- a/chromium/mojo/public/cpp/platform/platform_channel.cc +++ b/chromium/mojo/public/cpp/platform/platform_channel.cc @@ -16,7 +16,6 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "mojo/public/cpp/platform/features.h" #if defined(OS_WIN) #include <windows.h> @@ -102,12 +101,9 @@ void CreateChannel(PlatformHandle* local_endpoint, DCHECK(local_endpoint->is_valid()); DCHECK(remote_endpoint->is_valid()); } -#elif defined(OS_POSIX) - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// TODO(crbug.com/932175): This will be renamed CreateChannel(). -void CreateMachChannel(PlatformHandle* local_endpoint, - PlatformHandle* remote_endpoint) { +#elif defined(OS_MACOSX) && !defined(OS_IOS) +void CreateChannel(PlatformHandle* local_endpoint, + PlatformHandle* remote_endpoint) { // Mach messaging is simplex; and in order to enable full-duplex // communication, the Mojo channel implementation performs an internal // handshake with its peer to establish two sets of Mach receive and send @@ -124,7 +120,7 @@ void CreateMachChannel(PlatformHandle* local_endpoint, *local_endpoint = PlatformHandle(std::move(send)); *remote_endpoint = PlatformHandle(std::move(receive)); } -#endif // defined(OS_MACOSX) && !defined(OS_IOS) +#elif defined(OS_POSIX) #if defined(OS_ANDROID) // Leave room for any other descriptors defined in content for example. @@ -145,11 +141,6 @@ bool IsTargetDescriptorUsed(const base::FileHandleMappingVector& mapping, void CreateChannel(PlatformHandle* local_endpoint, PlatformHandle* remote_endpoint) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - return CreateMachChannel(local_endpoint, remote_endpoint); - } -#endif int fds[2]; #if defined(OS_NACL_SFI) PCHECK(imc_socketpair(fds) == 0); @@ -199,12 +190,8 @@ PlatformChannel::~PlatformChannel() = default; PlatformChannel& PlatformChannel::operator=(PlatformChannel&& other) = default; -void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info_input, +void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info, std::string* value) { -#if !defined(OS_MACOSX) || defined(OS_IOS) - auto* info = info_input; - DCHECK(info); -#endif DCHECK(value); DCHECK(remote_endpoint_.is_valid()); @@ -221,27 +208,19 @@ void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info_input, int mapped_fd = kAndroidClientHandleDescriptor + info->size(); info->emplace_back(fd, mapped_fd); *value = base::NumberToString(mapped_fd); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + DCHECK(remote_endpoint_.platform_handle().is_mach_receive()); + base::mac::ScopedMachReceiveRight receive_right = + remote_endpoint_.TakePlatformHandle().TakeMachReceiveRight(); + base::MachPortsForRendezvous::key_type rendezvous_key = 0; + do { + rendezvous_key = static_cast<decltype(rendezvous_key)>(base::RandUint64()); + } while (info->find(rendezvous_key) != info->end()); + auto it = info->insert(std::make_pair( + rendezvous_key, base::MachRendezvousPort(std::move(receive_right)))); + DCHECK(it.second) << "Failed to insert port for rendezvous."; + *value = base::NumberToString(rendezvous_key); #elif defined(OS_POSIX) -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - auto* info = static_cast<base::MachPortsForRendezvous*>(info_input); - DCHECK(remote_endpoint_.platform_handle().is_mach_receive()); - base::mac::ScopedMachReceiveRight receive_right = - remote_endpoint_.TakePlatformHandle().TakeMachReceiveRight(); - base::MachPortsForRendezvous::key_type rendezvous_key = 0; - do { - rendezvous_key = - static_cast<decltype(rendezvous_key)>(base::RandUint64()); - } while (info->find(rendezvous_key) != info->end()); - auto it = info->insert(std::make_pair( - rendezvous_key, base::MachRendezvousPort(std::move(receive_right)))); - DCHECK(it.second) << "Failed to insert port for rendezvous."; - *value = base::NumberToString(rendezvous_key); - return; - } - - auto* info = static_cast<base::FileHandleMappingVector*>(info_input); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) // Arbitrary sanity check to ensure the loop below terminates reasonably // quickly. CHECK_LT(info->size(), 1000u); @@ -275,18 +254,8 @@ void PlatformChannel::PrepareToPassRemoteEndpoint( #elif defined(OS_FUCHSIA) PrepareToPassRemoteEndpoint(&options->handles_to_transfer, command_line); #elif defined(OS_MACOSX) && !defined(OS_IOS) - HandlePassingInfo info; - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) - info = options->mach_ports_for_rendezvous; - else - info = options->fds_to_remap; - - PrepareToPassRemoteEndpoint(&info, command_line); - - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) - options->mach_ports_for_rendezvous = info; - else - options->fds_to_remap = info; + PrepareToPassRemoteEndpoint(&options->mach_ports_for_rendezvous, + command_line); #elif defined(OS_POSIX) PrepareToPassRemoteEndpoint(&options->fds_to_remap, command_line); #else @@ -333,27 +302,24 @@ PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromString( } return PlatformChannelEndpoint(PlatformHandle( base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(key)))); -#elif defined(OS_POSIX) -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::FeatureList::IsEnabled(features::kMojoChannelMac)) { - auto* client = base::MachPortRendezvousClient::GetInstance(); - if (!client) { - DLOG(ERROR) << "Mach rendezvous failed."; - return PlatformChannelEndpoint(); - } - uint32_t rendezvous_key = 0; - if (value.empty() || !base::StringToUint(value, &rendezvous_key)) { - DLOG(ERROR) << "Invalid PlatformChannel rendezvous key."; - return PlatformChannelEndpoint(); - } - auto receive = client->TakeReceiveRight(rendezvous_key); - if (!receive.is_valid()) { - DLOG(ERROR) << "Invalid PlatformChannel receive right."; - return PlatformChannelEndpoint(); - } - return PlatformChannelEndpoint(PlatformHandle(std::move(receive))); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + auto* client = base::MachPortRendezvousClient::GetInstance(); + if (!client) { + DLOG(ERROR) << "Mach rendezvous failed."; + return PlatformChannelEndpoint(); + } + uint32_t rendezvous_key = 0; + if (value.empty() || !base::StringToUint(value, &rendezvous_key)) { + DLOG(ERROR) << "Invalid PlatformChannel rendezvous key."; + return PlatformChannelEndpoint(); } -#endif // defined(OS_MACOSX) && !defined(OS_IOS) + auto receive = client->TakeReceiveRight(rendezvous_key); + if (!receive.is_valid()) { + DLOG(ERROR) << "Invalid PlatformChannel receive right."; + return PlatformChannelEndpoint(); + } + return PlatformChannelEndpoint(PlatformHandle(std::move(receive))); +#elif defined(OS_POSIX) int fd = -1; if (value.empty() || !base::StringToInt(value, &fd) || fd < base::GlobalDescriptors::kBaseDescriptor) { diff --git a/chromium/mojo/public/cpp/platform/platform_channel.h b/chromium/mojo/public/cpp/platform/platform_channel.h index d31668a3286..74a060dfaf7 100644 --- a/chromium/mojo/public/cpp/platform/platform_channel.h +++ b/chromium/mojo/public/cpp/platform/platform_channel.h @@ -42,17 +42,7 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel { #elif defined(OS_FUCHSIA) using HandlePassingInfo = base::HandlesToTransferVector; #elif defined(OS_MACOSX) && !defined(OS_IOS) - // This type represents a union between MachPortsForRendezvous and - // FileHandleMappingVector, so that the type to use can be determined at run- - // time. - // TODO(crbug.com/932175): This will become a typedef to - // base::MachPortsForRendezvous in the future. - class HandlePassingInfo : public base::MachPortsForRendezvous, - public base::FileHandleMappingVector { - public: - using base::MachPortsForRendezvous::operator=; - using base::FileHandleMappingVector::operator=; - }; + using HandlePassingInfo = base::MachPortsForRendezvous; #elif defined(OS_POSIX) using HandlePassingInfo = base::FileHandleMappingVector; #else diff --git a/chromium/mojo/public/cpp/system/BUILD.gn b/chromium/mojo/public/cpp/system/BUILD.gn index 01b62788fbe..235c65d3288 100644 --- a/chromium/mojo/public/cpp/system/BUILD.gn +++ b/chromium/mojo/public/cpp/system/BUILD.gn @@ -30,10 +30,14 @@ component("system") { "data_pipe.h", "data_pipe_drainer.cc", "data_pipe_drainer.h", + "data_pipe_producer.cc", + "data_pipe_producer.h", "data_pipe_utils.cc", "data_pipe_utils.h", - "file_data_pipe_producer.cc", - "file_data_pipe_producer.h", + "file_data_source.cc", + "file_data_source.h", + "filtered_data_source.cc", + "filtered_data_source.h", "functions.h", "handle.h", "handle_signal_tracker.cc", @@ -52,8 +56,8 @@ component("system") { "scope_to_message_pipe.h", "simple_watcher.cc", "simple_watcher.h", - "string_data_pipe_producer.cc", - "string_data_pipe_producer.h", + "string_data_source.cc", + "string_data_source.h", "system_export.h", "trap.cc", "trap.h", @@ -67,7 +71,7 @@ component("system") { # This file refers to a the base::File::File(path, flags) # constructor which does not exist in nacl builds, and the code # here is unused in nacl builds anyway. - sources -= [ "file_data_pipe_producer.cc" ] + sources -= [ "file_data_source.cc" ] } public_deps = [ diff --git a/chromium/mojo/public/cpp/system/README.md b/chromium/mojo/public/cpp/system/README.md index 60ef3f7a356..07bc7d3b9a2 100644 --- a/chromium/mojo/public/cpp/system/README.md +++ b/chromium/mojo/public/cpp/system/README.md @@ -145,10 +145,10 @@ memory: ``` cpp mojo::ScopedSharedBufferMapping mapping = buffer->Map(64); -static_cast<int*>(mapping.get()) = 42; +static_cast<int*>(mapping.get())[0] = 42; mojo::ScopedSharedBufferMapping another_mapping = buffer->MapAtOffset(64, 4); -static_cast<int*>(mapping.get()) = 43; +static_cast<int*>(mapping.get())[0] = 43; ``` When `mapping` and `another_mapping` are destroyed, they automatically unmap diff --git a/chromium/mojo/public/cpp/system/data_pipe_drainer.cc b/chromium/mojo/public/cpp/system/data_pipe_drainer.cc index 27b995a2a77..8b8d823fb75 100644 --- a/chromium/mojo/public/cpp/system/data_pipe_drainer.cc +++ b/chromium/mojo/public/cpp/system/data_pipe_drainer.cc @@ -18,8 +18,7 @@ DataPipeDrainer::DataPipeDrainer(Client* client, source_(std::move(source)), handle_watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, - base::SequencedTaskRunnerHandle::Get()), - weak_factory_(this) { + base::SequencedTaskRunnerHandle::Get()) { DCHECK(client_); handle_watcher_.Watch( source_.get(), MOJO_HANDLE_SIGNAL_READABLE, diff --git a/chromium/mojo/public/cpp/system/data_pipe_drainer.h b/chromium/mojo/public/cpp/system/data_pipe_drainer.h index 209d93d705b..949c9d13dd2 100644 --- a/chromium/mojo/public/cpp/system/data_pipe_drainer.h +++ b/chromium/mojo/public/cpp/system/data_pipe_drainer.h @@ -37,7 +37,7 @@ class MOJO_CPP_SYSTEM_EXPORT DataPipeDrainer { mojo::ScopedDataPipeConsumerHandle source_; mojo::SimpleWatcher handle_watcher_; - base::WeakPtrFactory<DataPipeDrainer> weak_factory_; + base::WeakPtrFactory<DataPipeDrainer> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DataPipeDrainer); }; diff --git a/chromium/mojo/public/cpp/system/data_pipe_producer.cc b/chromium/mojo/public/cpp/system/data_pipe_producer.cc new file mode 100644 index 00000000000..228b58f4ddf --- /dev/null +++ b/chromium/mojo/public/cpp/system/data_pipe_producer.cc @@ -0,0 +1,208 @@ +// Copyright 2017 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 "mojo/public/cpp/system/data_pipe_producer.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/memory/ref_counted_delete_on_sequence.h" +#include "base/numerics/safe_conversions.h" +#include "base/sequenced_task_runner.h" +#include "base/synchronization/lock.h" +#include "base/task/post_task.h" +#include "base/thread_annotations.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "mojo/public/cpp/system/simple_watcher.h" + +namespace mojo { + +namespace { + +// No good reason not to attempt very large pipe transactions in case the data +// pipe in use has a very large capacity available, so we default to trying +// 64 MB chunks whenever a producer is writable. +constexpr uint32_t kDefaultMaxReadSize = 64 * 1024 * 1024; + +} // namespace + +class DataPipeProducer::SequenceState + : public base::RefCountedDeleteOnSequence<SequenceState> { + public: + using CompletionCallback = + base::OnceCallback<void(ScopedDataPipeProducerHandle producer, + MojoResult result)>; + + SequenceState(ScopedDataPipeProducerHandle producer_handle, + scoped_refptr<base::SequencedTaskRunner> file_task_runner, + CompletionCallback callback, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner) + : base::RefCountedDeleteOnSequence<SequenceState>( + std::move(file_task_runner)), + callback_task_runner_(std::move(callback_task_runner)), + producer_handle_(std::move(producer_handle)), + callback_(std::move(callback)) {} + + void Cancel() { + base::AutoLock lock(lock_); + is_cancelled_ = true; + } + + void Start(std::unique_ptr<DataSource> data_source) { + owning_task_runner()->PostTask( + FROM_HERE, base::BindOnce(&SequenceState::StartOnSequence, this, + std::move(data_source))); + } + + private: + friend class base::DeleteHelper<SequenceState>; + friend class base::RefCountedDeleteOnSequence<SequenceState>; + + ~SequenceState() = default; + + void StartOnSequence(std::unique_ptr<DataSource> data_source) { + data_source_ = std::move(data_source); + if (!data_source_->IsValid()) { + Finish(MOJO_RESULT_UNKNOWN); + return; + } + TransferSomeBytes(); + if (producer_handle_.is_valid()) { + // If we didn't nail it all on the first transaction attempt, setup a + // watcher and complete the read asynchronously. + watcher_ = std::make_unique<SimpleWatcher>( + FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, + base::SequencedTaskRunnerHandle::Get()); + watcher_->Watch(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_WATCH_CONDITION_SATISFIED, + base::Bind(&SequenceState::OnHandleReady, this)); + } + } + + void OnHandleReady(MojoResult result, const HandleSignalsState& state) { + { + // Stop ourselves from doing redundant work if we've been cancelled from + // another thread. Note that we do not rely on this for any kind of thread + // safety concerns. + base::AutoLock lock(lock_); + if (is_cancelled_) + return; + } + + if (result != MOJO_RESULT_OK) { + // Either the consumer pipe has been closed or something terrible + // happened. In any case, we'll never be able to write more data. + Finish(result); + return; + } + + TransferSomeBytes(); + } + + void TransferSomeBytes() { + while (true) { + // Lock as much of the pipe as we can. + void* pipe_buffer; + uint32_t size = kDefaultMaxReadSize; + int64_t max_data_size = data_source_->GetLength(); + if (static_cast<int64_t>(size) > max_data_size) + size = static_cast<uint32_t>(max_data_size); + + MojoResult mojo_result = producer_handle_->BeginWriteData( + &pipe_buffer, &size, MOJO_WRITE_DATA_FLAG_NONE); + if (mojo_result == MOJO_RESULT_SHOULD_WAIT) + return; + if (mojo_result != MOJO_RESULT_OK) { + Finish(mojo_result); + return; + } + base::span<char> read_buffer(static_cast<char*>(pipe_buffer), size); + + DataSource::ReadResult result = + data_source_->Read(bytes_transferred_, read_buffer); + producer_handle_->EndWriteData(result.bytes_read); + + if (result.result != MOJO_RESULT_OK) { + Finish(result.result); + return; + } + + bytes_transferred_ += result.bytes_read; + + if (result.bytes_read < read_buffer.size()) { + // DataSource::Read makes a best effort to read all requested bytes. We + // reasonably assume if it fails to read what we ask for, we've hit EOF. + Finish(MOJO_RESULT_OK); + return; + } + } + } + + void Finish(MojoResult result) { + watcher_.reset(); + if (result != MOJO_RESULT_OK) + data_source_->Abort(); + data_source_.reset(); + callback_task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(callback_), + std::move(producer_handle_), result)); + } + + const scoped_refptr<base::SequencedTaskRunner> callback_task_runner_; + + // State which is effectively owned and used only on the file sequence. + ScopedDataPipeProducerHandle producer_handle_; + std::unique_ptr<DataPipeProducer::DataSource> data_source_; + size_t bytes_transferred_ = 0; + CompletionCallback callback_; + std::unique_ptr<SimpleWatcher> watcher_; + + base::Lock lock_; + bool is_cancelled_ GUARDED_BY(lock_) = false; + + DISALLOW_COPY_AND_ASSIGN(SequenceState); +}; + +DataPipeProducer::DataPipeProducer(ScopedDataPipeProducerHandle producer) + : producer_(std::move(producer)) {} + +DataPipeProducer::~DataPipeProducer() { + if (sequence_state_) + sequence_state_->Cancel(); +} + +void DataPipeProducer::Write(std::unique_ptr<DataSource> data_source, + CompletionCallback callback) { + InitializeNewRequest(std::move(callback)); + sequence_state_->Start(std::move(data_source)); +} + +void DataPipeProducer::InitializeNewRequest(CompletionCallback callback) { + DCHECK(!sequence_state_); + // TODO(crbug.com/924416): Re-evaluate how TaskPriority is set here and in + // other file URL-loading-related code. Some callers require USER_VISIBLE + // (i.e., BEST_EFFORT is not enough). + auto file_task_runner = base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE}); + sequence_state_ = new SequenceState( + std::move(producer_), file_task_runner, + base::BindOnce(&DataPipeProducer::OnWriteComplete, + weak_factory_.GetWeakPtr(), std::move(callback)), + base::SequencedTaskRunnerHandle::Get()); +} + +void DataPipeProducer::OnWriteComplete(CompletionCallback callback, + ScopedDataPipeProducerHandle producer, + MojoResult ready_result) { + producer_ = std::move(producer); + sequence_state_ = nullptr; + std::move(callback).Run(ready_result); +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/data_pipe_producer.h b/chromium/mojo/public/cpp/system/data_pipe_producer.h new file mode 100644 index 00000000000..b10f9ebb3c6 --- /dev/null +++ b/chromium/mojo/public/cpp/system/data_pipe_producer.h @@ -0,0 +1,110 @@ +// Copyright 2017 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 MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_PRODUCER_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_PRODUCER_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/containers/span.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// Helper class which takes ownership of a ScopedDataPipeProducerHandle and +// assumes responsibility for feeding it the contents of a DataSource. This +// takes care of waiting for pipe capacity as needed, and can notify callers +// asynchronously when the operation is complete. +// +// Note that the DataPipeProducer must be kept alive until notified of +// completion to ensure that all of the intended contents are written to the +// pipe. Premature destruction may result in partial or total truncation of data +// made available to the consumer. +class MOJO_CPP_SYSTEM_EXPORT DataPipeProducer { + public: + using CompletionCallback = base::OnceCallback<void(MojoResult result)>; + // Interface definition of abstracted content reader that has minimum + // base::File equivalent interface to read content. + class DataSource { + public: + // Used as a return value for Read(). + struct ReadResult { + // The number of bytes read. If returned |bytes_read| is less than + // requested size, it means EOF is reached. + size_t bytes_read = 0; + + // MojoResult resulting from this call. + MojoResult result = MOJO_RESULT_OK; + }; + virtual ~DataSource() {} + + // Checks if this Reader instance owns a valid content to read. + virtual bool IsValid() const = 0; + + // Returns maximum data size. Actual size may be smaller. This should return + // a positive value. + // TODO(crbug.com/983023): Return size_t. + virtual int64_t GetLength() const = 0; + + // Similar to base::File::Read(), reads the given number of bytes (or until + // EOF is reached) starting with the given offset. Returns ReadResult to + // represent the number of bytes read and errors. + virtual ReadResult Read(int64_t offset, base::span<char> buffer) = 0; + + // Notifies DataPipeProducer aborts read operations. + virtual void Abort() {} + }; + + // Constructs a new DataPipeProducer which will write data to |producer|. + explicit DataPipeProducer(ScopedDataPipeProducerHandle producer); + ~DataPipeProducer(); + + // Attempts to eventually write all of |data_source|'s contents to the pipe. + // Invokes |callback| asynchronously when done. Note that |callback| IS + // allowed to delete this DataPipeProducer. + // + // If the write is successful |result| will be |MOJO_RESULT_OK|. Otherwise + // (e.g. if the producer detects the consumer is closed and the pipe has no + // remaining capacity, or if file open/reads fail for any reason) |result| + // will be one of the following: + // + // |MOJO_RESULT_ABORTED| + // |MOJO_RESULT_NOT_FOUND| + // |MOJO_RESULT_PERMISSION_DENIED| + // |MOJO_RESULT_RESOURCE_EXHAUSTED| + // |MOJO_RESULT_UNKNOWN| + // + // Note that if the DataPipeProducer is destroyed before |callback| can be + // invoked, |callback| is *never* invoked, and the write will be permanently + // interrupted (and the producer handle closed) after making potentially only + // partial progress. + // + // Multiple writes may be performed in sequence (each one after the last + // completes), but Write() must not be called before the |callback| for the + // previous call to Write() (if any) has returned. + void Write(std::unique_ptr<DataSource> reader, CompletionCallback callback); + + private: + class SequenceState; + + void InitializeNewRequest(CompletionCallback callback); + void OnWriteComplete(CompletionCallback callback, + ScopedDataPipeProducerHandle producer, + MojoResult result); + + ScopedDataPipeProducerHandle producer_; + scoped_refptr<SequenceState> sequence_state_; + base::WeakPtrFactory<DataPipeProducer> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(DataPipeProducer); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_PRODUCER_H_ diff --git a/chromium/mojo/public/cpp/system/file_data_pipe_producer.cc b/chromium/mojo/public/cpp/system/file_data_pipe_producer.cc deleted file mode 100644 index 3c928db92ab..00000000000 --- a/chromium/mojo/public/cpp/system/file_data_pipe_producer.cc +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2017 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 "mojo/public/cpp/system/file_data_pipe_producer.h" - -#include <algorithm> -#include <limits> -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/location.h" -#include "base/memory/ref_counted_delete_on_sequence.h" -#include "base/numerics/safe_conversions.h" -#include "base/sequenced_task_runner.h" -#include "base/synchronization/lock.h" -#include "base/task/post_task.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "mojo/public/cpp/system/simple_watcher.h" - -namespace mojo { - -namespace { - -// No good reason not to attempt very large pipe transactions in case the data -// pipe in use has a very large capacity available, so we default to trying -// 64 MB chunks whenever a producer is writable. -constexpr uint32_t kDefaultMaxReadSize = 64 * 1024 * 1024; - -MojoResult FileErrorToMojoResult(base::File::Error error) { - switch (error) { - case base::File::FILE_OK: - return MOJO_RESULT_OK; - case base::File::FILE_ERROR_NOT_FOUND: - return MOJO_RESULT_NOT_FOUND; - case base::File::FILE_ERROR_SECURITY: - case base::File::FILE_ERROR_ACCESS_DENIED: - return MOJO_RESULT_PERMISSION_DENIED; - case base::File::FILE_ERROR_TOO_MANY_OPENED: - case base::File::FILE_ERROR_NO_MEMORY: - return MOJO_RESULT_RESOURCE_EXHAUSTED; - case base::File::FILE_ERROR_ABORT: - return MOJO_RESULT_ABORTED; - default: - return MOJO_RESULT_UNKNOWN; - } -} - -} // namespace - -class FileDataPipeProducer::FileSequenceState - : public base::RefCountedDeleteOnSequence<FileSequenceState> { - public: - using CompletionCallback = - base::OnceCallback<void(ScopedDataPipeProducerHandle producer, - MojoResult result)>; - - FileSequenceState( - ScopedDataPipeProducerHandle producer_handle, - scoped_refptr<base::SequencedTaskRunner> file_task_runner, - CompletionCallback callback, - scoped_refptr<base::SequencedTaskRunner> callback_task_runner, - std::unique_ptr<Observer> observer) - : base::RefCountedDeleteOnSequence<FileSequenceState>( - std::move(file_task_runner)), - callback_task_runner_(std::move(callback_task_runner)), - producer_handle_(std::move(producer_handle)), - callback_(std::move(callback)), - observer_(std::move(observer)) {} - - void Cancel() { - base::AutoLock lock(lock_); - is_cancelled_ = true; - } - - void StartFromFile(base::File file, size_t max_bytes) { - owning_task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&FileSequenceState::StartFromFileOnFileSequence, this, - std::move(file), max_bytes)); - } - - void StartFromPath(const base::FilePath& path) { - owning_task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&FileSequenceState::StartFromPathOnFileSequence, this, - path)); - } - - private: - friend class base::DeleteHelper<FileSequenceState>; - friend class base::RefCountedDeleteOnSequence<FileSequenceState>; - - ~FileSequenceState() = default; - - void StartFromFileOnFileSequence(base::File file, size_t max_bytes) { - if (file.error_details() != base::File::FILE_OK) { - Finish(FileErrorToMojoResult(file.error_details())); - return; - } - file_ = std::move(file); - max_bytes_ = max_bytes; - TransferSomeBytes(); - if (producer_handle_.is_valid()) { - // If we didn't nail it all on the first transaction attempt, setup a - // watcher and complete the read asynchronously. - watcher_ = std::make_unique<SimpleWatcher>( - FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, - base::SequencedTaskRunnerHandle::Get()); - watcher_->Watch(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_WATCH_CONDITION_SATISFIED, - base::Bind(&FileSequenceState::OnHandleReady, this)); - } - } - - void StartFromPathOnFileSequence(const base::FilePath& path) { - StartFromFileOnFileSequence( - base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ), - std::numeric_limits<size_t>::max()); - } - - void OnHandleReady(MojoResult result, const HandleSignalsState& state) { - { - // Stop ourselves from doing redundant work if we've been cancelled from - // another thread. Note that we do not rely on this for any kind of thread - // safety concerns. - base::AutoLock lock(lock_); - if (is_cancelled_) - return; - } - - if (result != MOJO_RESULT_OK) { - // Either the consumer pipe has been closed or something terrible - // happened. In any case, we'll never be able to write more data. - Finish(result); - return; - } - - TransferSomeBytes(); - } - - void TransferSomeBytes() { - while (true) { - // Lock as much of the pipe as we can. - void* pipe_buffer; - uint32_t size = kDefaultMaxReadSize; - MojoResult result = producer_handle_->BeginWriteData( - &pipe_buffer, &size, MOJO_WRITE_DATA_FLAG_NONE); - if (result == MOJO_RESULT_SHOULD_WAIT) - return; - if (result != MOJO_RESULT_OK) { - Finish(result); - return; - } - - // Attempt to read that many bytes from the file, directly into the data - // pipe. Note that while |max_bytes_remaining| may be very large, the - // length we attempt read is bounded by the much smaller - // |kDefaultMaxReadSize| via |size|. - DCHECK(base::IsValueInRangeForNumericType<int>(size)); - const size_t max_bytes_remaining = max_bytes_ - bytes_transferred_; - int attempted_read_size = static_cast<int>( - std::min(static_cast<size_t>(size), max_bytes_remaining)); - int read_size = file_.ReadAtCurrentPos(static_cast<char*>(pipe_buffer), - attempted_read_size); - base::File::Error read_error; - if (read_size < 0) { - read_error = base::File::GetLastFileError(); - DCHECK_NE(base::File::FILE_OK, read_error); - if (observer_) - observer_->OnBytesRead(pipe_buffer, 0u, read_error); - } else { - read_error = base::File::FILE_OK; - if (observer_) { - observer_->OnBytesRead(pipe_buffer, static_cast<size_t>(read_size), - base::File::FILE_OK); - } - } - producer_handle_->EndWriteData( - read_size >= 0 ? static_cast<uint32_t>(read_size) : 0); - - if (read_size < 0) { - Finish(FileErrorToMojoResult(read_error)); - return; - } - - bytes_transferred_ += read_size; - DCHECK_LE(bytes_transferred_, max_bytes_); - - if (read_size < attempted_read_size) { - // ReadAtCurrentPos makes a best effort to read all requested bytes. We - // reasonably assume if it fails to read what we ask for, we've hit EOF. - Finish(MOJO_RESULT_OK); - return; - } - - if (bytes_transferred_ == max_bytes_) { - // We've read as much as we were asked to read. - Finish(MOJO_RESULT_OK); - return; - } - } - } - - void Finish(MojoResult result) { - if (observer_) { - if (result != MOJO_RESULT_OK) - observer_->OnBytesRead(nullptr, 0u, base::File::FILE_ERROR_ABORT); - observer_->OnDoneReading(); - observer_ = nullptr; - } - watcher_.reset(); - callback_task_runner_->PostTask( - FROM_HERE, base::BindOnce(std::move(callback_), - std::move(producer_handle_), result)); - } - - const scoped_refptr<base::SequencedTaskRunner> callback_task_runner_; - - // State which is effectively owned and used only on the file sequence. - ScopedDataPipeProducerHandle producer_handle_; - base::File file_; - size_t max_bytes_ = 0; - size_t bytes_transferred_ = 0; - CompletionCallback callback_; - std::unique_ptr<SimpleWatcher> watcher_; - - // Guards |is_cancelled_|. - base::Lock lock_; - bool is_cancelled_ = false; - std::unique_ptr<Observer> observer_; - - DISALLOW_COPY_AND_ASSIGN(FileSequenceState); -}; - -FileDataPipeProducer::FileDataPipeProducer( - ScopedDataPipeProducerHandle producer, - std::unique_ptr<Observer> observer) - : producer_(std::move(producer)), - observer_(std::move(observer)), - weak_factory_(this) {} - -FileDataPipeProducer::~FileDataPipeProducer() { - if (file_sequence_state_) - file_sequence_state_->Cancel(); -} - -void FileDataPipeProducer::WriteFromFile(base::File file, - CompletionCallback callback) { - WriteFromFile(std::move(file), std::numeric_limits<size_t>::max(), - std::move(callback)); -} - -void FileDataPipeProducer::WriteFromFile(base::File file, - size_t max_bytes, - CompletionCallback callback) { - InitializeNewRequest(std::move(callback)); - file_sequence_state_->StartFromFile(std::move(file), max_bytes); -} - -void FileDataPipeProducer::WriteFromPath(const base::FilePath& path, - CompletionCallback callback) { - InitializeNewRequest(std::move(callback)); - file_sequence_state_->StartFromPath(path); -} - -void FileDataPipeProducer::InitializeNewRequest(CompletionCallback callback) { - DCHECK(!file_sequence_state_); - // TODO(crbug.com/924416): Re-evaluate how TaskPriority is set here and in - // other file URL-loading-related code. Some callers require USER_VISIBLE - // (i.e., BEST_EFFORT is not enough). - auto file_task_runner = base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE}); - file_sequence_state_ = new FileSequenceState( - std::move(producer_), file_task_runner, - base::BindOnce(&FileDataPipeProducer::OnWriteComplete, - weak_factory_.GetWeakPtr(), std::move(callback)), - base::SequencedTaskRunnerHandle::Get(), std::move(observer_)); -} - -void FileDataPipeProducer::OnWriteComplete( - CompletionCallback callback, - ScopedDataPipeProducerHandle producer, - MojoResult ready_result) { - producer_ = std::move(producer); - file_sequence_state_ = nullptr; - std::move(callback).Run(ready_result); -} - -} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/file_data_pipe_producer.h b/chromium/mojo/public/cpp/system/file_data_pipe_producer.h deleted file mode 100644 index c3479f75bfe..00000000000 --- a/chromium/mojo/public/cpp/system/file_data_pipe_producer.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 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 MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_PIPE_PRODUCER_H_ -#define MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_PIPE_PRODUCER_H_ - -#include <memory> - -#include "base/callback_forward.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "mojo/public/cpp/system/data_pipe.h" -#include "mojo/public/cpp/system/system_export.h" - -namespace mojo { - -// Helper class which takes ownership of a ScopedDataPipeProducerHandle and -// assumes responsibility for feeding it the contents of a given file. This -// takes care of waiting for pipe capacity as needed, and can notify callers -// asynchronously when the operation is complete. -// -// Note that the FileDataPipeProducer must be kept alive until notified of -// completion to ensure that all of the intended contents are written to the -// pipe. Premature destruction may result in partial or total truncation of data -// made available to the consumer. -class MOJO_CPP_SYSTEM_EXPORT FileDataPipeProducer { - public: - using CompletionCallback = base::OnceCallback<void(MojoResult result)>; - - // Interface definition of an optional object that may be supplied to the - // FileDataPipeProducer so that the data being read from the consumer can be - // observed. - class Observer { - public: - virtual ~Observer() {} - - // Called once per read attempt. |data| contains the read data (if any). - // |num_bytes_read| is the number of read bytes, 0 indicates EOF. Both - // parameters may only be used when |read_result| is base::File::FILE_OK. - // Can be called on any sequence. - virtual void OnBytesRead(const void* data, - size_t num_bytes_read, - base::File::Error read_result) = 0; - - // Called when the FileDataPipeProducer has finished reading all data. Will - // be called even if there was an error opening the file or reading the - // data. Can be called on any sequence. - virtual void OnDoneReading() = 0; - }; - - // Constructs a new FileDataPipeProducer which will write data to |producer|. - // Caller may supply an optional |observer| if observation of the read file - // data is desired. - FileDataPipeProducer(ScopedDataPipeProducerHandle producer, - std::unique_ptr<Observer> observer); - ~FileDataPipeProducer(); - - // Attempts to eventually write all of |file|'s contents to the pipe. Invokes - // |callback| asynchronously when done. Note that |callback| IS allowed to - // delete this FileDataPipeProducer. - // - // If the write is successful |result| will be |MOJO_RESULT_OK|. Otherwise - // (e.g. if the producer detects the consumer is closed and the pipe has no - // remaining capacity, or if file open/reads fail for any reason) |result| - // will be one of the following: - // - // |MOJO_RESULT_ABORTED| - // |MOJO_RESULT_NOT_FOUND| - // |MOJO_RESULT_PERMISSION_DENIED| - // |MOJO_RESULT_RESOURCE_EXHAUSTED| - // |MOJO_RESULT_UNKNOWN| - // - // Note that if the FileDataPipeProducer is destroyed before |callback| can be - // invoked, |callback| is *never* invoked, and the write will be permanently - // interrupted (and the producer handle closed) after making potentially only - // partial progress. - // - // Multiple writes may be performed in sequence (each one after the last - // completes), but Write() must not be called before the |callback| for the - // previous call to Write() (if any) has returned. - void WriteFromFile(base::File file, CompletionCallback callback); - - // Like above, but writes at most |max_bytes| bytes from the file to the pipe. - void WriteFromFile(base::File file, - size_t max_bytes, - CompletionCallback callback); - - // Same as above but takes a FilePath instead of an opened File. Opens the - // file on an appropriate sequence and then proceeds as WriteFromFile() would. - void WriteFromPath(const base::FilePath& path, CompletionCallback callback); - - private: - class FileSequenceState; - - void InitializeNewRequest(CompletionCallback callback); - void OnWriteComplete(CompletionCallback callback, - ScopedDataPipeProducerHandle producer, - MojoResult result); - - ScopedDataPipeProducerHandle producer_; - scoped_refptr<FileSequenceState> file_sequence_state_; - std::unique_ptr<Observer> observer_; - base::WeakPtrFactory<FileDataPipeProducer> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FileDataPipeProducer); -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_PIPE_PRODUCER_H_ diff --git a/chromium/mojo/public/cpp/system/file_data_source.cc b/chromium/mojo/public/cpp/system/file_data_source.cc new file mode 100644 index 00000000000..642021e5530 --- /dev/null +++ b/chromium/mojo/public/cpp/system/file_data_source.cc @@ -0,0 +1,87 @@ +// Copyright 2019 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 "mojo/public/cpp/system/file_data_source.h" + +#include <limits> + +#include "base/numerics/checked_math.h" + +namespace { + +int64_t CalculateBaseOffset(base::File* file) { + if (file->IsValid()) + return file->Seek(base::File::FROM_CURRENT, 0); + return 0; +} + +size_t CalculateMaxBytes(base::File* file, + int64_t base_offset, + base::Optional<int64_t> max_bytes) { + if (max_bytes) + return *max_bytes; + if (file->IsValid()) + return file->GetLength() - base_offset; + return std::numeric_limits<size_t>::max(); +} + +} // namespace + +namespace mojo { + +// static +MojoResult FileDataSource::ConvertFileErrorToMojoResult( + base::File::Error error) { + switch (error) { + case base::File::FILE_OK: + return MOJO_RESULT_OK; + case base::File::FILE_ERROR_NOT_FOUND: + return MOJO_RESULT_NOT_FOUND; + case base::File::FILE_ERROR_SECURITY: + case base::File::FILE_ERROR_ACCESS_DENIED: + return MOJO_RESULT_PERMISSION_DENIED; + case base::File::FILE_ERROR_TOO_MANY_OPENED: + case base::File::FILE_ERROR_NO_MEMORY: + return MOJO_RESULT_RESOURCE_EXHAUSTED; + case base::File::FILE_ERROR_ABORT: + return MOJO_RESULT_ABORTED; + default: + return MOJO_RESULT_UNKNOWN; + } +} + +FileDataSource::FileDataSource(base::File file, + base::Optional<int64_t> max_bytes) + : file_(std::move(file)), + base_offset_(CalculateBaseOffset(&file_)), + max_bytes_(CalculateMaxBytes(&file_, base_offset_, max_bytes)) {} + +FileDataSource::~FileDataSource() = default; + +bool FileDataSource::IsValid() const { + return file_.IsValid(); +} + +int64_t FileDataSource::GetLength() const { + return max_bytes_ - base_offset_; +} + +DataPipeProducer::DataSource::ReadResult FileDataSource::Read( + int64_t offset, + base::span<char> buffer) { + ReadResult result; + size_t readable_size = max_bytes_ - offset; + size_t read_size = std::min(readable_size, buffer.size()); + DCHECK(base::IsValueInRangeForNumericType<int>(read_size)); + int bytes_read = file_.Read(base_offset_ + offset, buffer.data(), read_size); + if (bytes_read < 0) { + result.bytes_read = 0; + result.result = ConvertFileErrorToMojoResult(file_.GetLastFileError()); + } else { + result.bytes_read = bytes_read; + } + return result; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/file_data_source.h b/chromium/mojo/public/cpp/system/file_data_source.h new file mode 100644 index 00000000000..a28755a0ae4 --- /dev/null +++ b/chromium/mojo/public/cpp/system/file_data_source.h @@ -0,0 +1,43 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_SOURCE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_SOURCE_H_ + +#include "base/containers/span.h" +#include "base/files/file.h" +#include "base/macros.h" +#include "base/optional.h" +#include "mojo/public/cpp/system/data_pipe_producer.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// A class to wrap base::File as DataPipeProducer::DataSource class. Reads at +// most |max_bytes| bytes from the file. +class MOJO_CPP_SYSTEM_EXPORT FileDataSource final + : public DataPipeProducer::DataSource { + public: + static MojoResult ConvertFileErrorToMojoResult(base::File::Error error); + + FileDataSource(base::File file, + base::Optional<int64_t> max_bytes = base::nullopt); + ~FileDataSource() override; + + private: + // DataPipeProducer::DataSource: + bool IsValid() const override; + int64_t GetLength() const override; + ReadResult Read(int64_t offset, base::span<char> buffer) override; + + base::File file_; + const int64_t base_offset_; + const int64_t max_bytes_; + + DISALLOW_COPY_AND_ASSIGN(FileDataSource); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_FILE_DATA_SOURCE_H_ diff --git a/chromium/mojo/public/cpp/system/filtered_data_source.cc b/chromium/mojo/public/cpp/system/filtered_data_source.cc new file mode 100644 index 00000000000..5916393bb2c --- /dev/null +++ b/chromium/mojo/public/cpp/system/filtered_data_source.cc @@ -0,0 +1,46 @@ +// Copyright 2019 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 "mojo/public/cpp/system/filtered_data_source.h" + +namespace mojo { + +FilteredDataSource::FilteredDataSource( + std::unique_ptr<DataPipeProducer::DataSource> source, + std::unique_ptr<Filter> filter) + : source_(std::move(source)), filter_(std::move(filter)) { + DCHECK(source_); +} + +FilteredDataSource::~FilteredDataSource() { + if (filter_) + filter_->OnDone(); +} + +bool FilteredDataSource::IsValid() const { + return source_->IsValid(); +} + +int64_t FilteredDataSource::GetLength() const { + return source_->GetLength(); +} + +FilteredDataSource::ReadResult FilteredDataSource::Read( + int64_t offset, + base::span<char> buffer) { + ReadResult result = source_->Read(offset, buffer); + if (filter_) + filter_->OnRead(buffer, &result); + return result; +} + +void FilteredDataSource::Abort() { + if (filter_) { + ReadResult result; + result.result = MOJO_RESULT_ABORTED; + filter_->OnRead(base::span<char>(), &result); + } +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/filtered_data_source.h b/chromium/mojo/public/cpp/system/filtered_data_source.h new file mode 100644 index 00000000000..69f58c989d5 --- /dev/null +++ b/chromium/mojo/public/cpp/system/filtered_data_source.h @@ -0,0 +1,64 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_SYSTEM_FILTERED_DATA_SOURCE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_FILTERED_DATA_SOURCE_H_ + +#include <limits> +#include <memory> + +#include "base/containers/span.h" +#include "base/macros.h" +#include "mojo/public/cpp/system/data_pipe_producer.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// A class wraps any other DataPipeProducer::DataSource interface and provides +// the Filter interface to monitor read manipulations. +class MOJO_CPP_SYSTEM_EXPORT FilteredDataSource final + : public DataPipeProducer::DataSource { + public: + class Filter { + public: + virtual ~Filter() {} + + // Called once after each |source|'s read attempt. Filter instance can use + // this interface both to monitor read payloads and to modify read results. + // |buffer| contains the read buffer. |result->bytes_read| is the actual + // bytes number that |source| read that should be less than buffer.size(). + // 0 indicates EOF. Both parameters are only valid when |result->error| is + // MOJO_RESULT_OK. + // Filter can modify contents of |buffer| and |result|, but it could not + // expand |buffer|'s size. |result| should be updated to be aligned with + // modified result and size. + // Can be called on any sequence. + virtual void OnRead(base::span<char> buffer, ReadResult* result) = 0; + + // Called when the DataPipeProducer has finished reading all data. Will be + // called even if there was an error opening the file or reading the data. + // Can be called on any sequence. + virtual void OnDone() = 0; + }; + + FilteredDataSource(std::unique_ptr<DataPipeProducer::DataSource> source, + std::unique_ptr<Filter> filter); + ~FilteredDataSource() override; + + private: + // DataPipeProducer::DataSource: + bool IsValid() const override; + int64_t GetLength() const override; + ReadResult Read(int64_t offset, base::span<char> buffer) override; + void Abort() override; + + std::unique_ptr<DataPipeProducer::DataSource> source_; + std::unique_ptr<Filter> filter_; + + DISALLOW_COPY_AND_ASSIGN(FilteredDataSource); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_FILTERED_DATA_SOURCE_H_ diff --git a/chromium/mojo/public/cpp/system/simple_watcher.cc b/chromium/mojo/public/cpp/system/simple_watcher.cc index 5905b02c247..c2ed7efd39a 100644 --- a/chromium/mojo/public/cpp/system/simple_watcher.cc +++ b/chromium/mojo/public/cpp/system/simple_watcher.cc @@ -142,8 +142,7 @@ SimpleWatcher::SimpleWatcher(const base::Location& from_here, is_default_task_runner_(base::ThreadTaskRunnerHandle::IsSet() && task_runner_ == base::ThreadTaskRunnerHandle::Get()), - heap_profiler_tag_(from_here.file_name()), - weak_factory_(this) { + heap_profiler_tag_(from_here.file_name()) { MojoResult rv = CreateTrap(&Context::CallNotify, &trap_handle_); DCHECK_EQ(MOJO_RESULT_OK, rv); DCHECK(task_runner_->RunsTasksInCurrentSequence()); diff --git a/chromium/mojo/public/cpp/system/simple_watcher.h b/chromium/mojo/public/cpp/system/simple_watcher.h index cc916c88019..e34d7ddb210 100644 --- a/chromium/mojo/public/cpp/system/simple_watcher.h +++ b/chromium/mojo/public/cpp/system/simple_watcher.h @@ -233,7 +233,7 @@ class MOJO_CPP_SYSTEM_EXPORT SimpleWatcher { // this watcher. const char* heap_profiler_tag_ = nullptr; - base::WeakPtrFactory<SimpleWatcher> weak_factory_; + base::WeakPtrFactory<SimpleWatcher> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SimpleWatcher); }; diff --git a/chromium/mojo/public/cpp/system/string_data_pipe_producer.cc b/chromium/mojo/public/cpp/system/string_data_pipe_producer.cc deleted file mode 100644 index 81f7e7d5e29..00000000000 --- a/chromium/mojo/public/cpp/system/string_data_pipe_producer.cc +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2017 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 "mojo/public/cpp/system/string_data_pipe_producer.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/location.h" -#include "base/task/post_task.h" - -namespace mojo { - -namespace { - -// Attempts to write data to a producer handle. Outputs the actual number of -// bytes written in |*size|, and returns a result indicating the status of the -// last attempted write operation. -MojoResult WriteDataToProducerHandle(DataPipeProducerHandle producer, - const char* data, - size_t* size) { - void* dest; - uint32_t bytes_left = static_cast<uint32_t>(*size); - - // We loop here since the pipe's available capacity may be larger than its - // *contiguous* capacity, and hence two independent consecutive two-phase - // writes may succeed. The goal here is to write as much of |data| as possible - // until we either run out of data or run out of capacity. - MojoResult result; - do { - uint32_t capacity = bytes_left; - result = - producer.BeginWriteData(&dest, &capacity, MOJO_WRITE_DATA_FLAG_NONE); - if (result == MOJO_RESULT_SHOULD_WAIT) { - result = MOJO_RESULT_OK; - break; - } else if (result != MOJO_RESULT_OK) { - break; - } - - capacity = std::min(capacity, bytes_left); - memcpy(dest, data, capacity); - MojoResult end_result = producer.EndWriteData(capacity); - DCHECK_EQ(MOJO_RESULT_OK, end_result); - - data += capacity; - bytes_left -= capacity; - } while (bytes_left); - - *size -= bytes_left; - return result; -} - -} // namespace - -StringDataPipeProducer::StringDataPipeProducer( - ScopedDataPipeProducerHandle producer) - : producer_(std::move(producer)), - watcher_(FROM_HERE, - SimpleWatcher::ArmingPolicy::AUTOMATIC, - base::SequencedTaskRunnerHandle::Get()), - weak_factory_(this) {} - -StringDataPipeProducer::~StringDataPipeProducer() = default; - -void StringDataPipeProducer::Write(const base::StringPiece& data, - AsyncWritingMode mode, - CompletionCallback callback) { - DCHECK(!callback_); - callback_ = std::move(callback); - - // Immediately attempt to write data without making an extra copy. If we can - // get it all in one shot, we're done aleady. - size_t size = data.size(); - MojoResult result = - WriteDataToProducerHandle(producer_.get(), data.data(), &size); - if (result == MOJO_RESULT_OK && size == data.size()) { - base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&StringDataPipeProducer::InvokeCallback, - weak_factory_.GetWeakPtr(), MOJO_RESULT_OK)); - } else { - // Copy whatever data didn't make the cut and try again when the pipe has - // some more capacity. - if (mode == AsyncWritingMode::STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION) { - data_ = std::string(data.data() + size, data.size() - size); - data_view_ = data_; - } else { - data_view_ = base::StringPiece(data.data() + size, data.size() - size); - } - watcher_.Watch(producer_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_WATCH_CONDITION_SATISFIED, - base::Bind(&StringDataPipeProducer::OnProducerHandleReady, - base::Unretained(this))); - } -} - -void StringDataPipeProducer::InvokeCallback(MojoResult result) { - // May delete |this|. - std::move(callback_).Run(result); -} - -void StringDataPipeProducer::OnProducerHandleReady( - MojoResult ready_result, - const HandleSignalsState& state) { - bool failed = false; - size_t size = data_view_.size(); - if (ready_result == MOJO_RESULT_OK) { - MojoResult write_result = - WriteDataToProducerHandle(producer_.get(), data_view_.data(), &size); - if (write_result != MOJO_RESULT_OK) - failed = true; - } else { - failed = true; - } - - if (failed) { - watcher_.Cancel(); - - // May delete |this|. - std::move(callback_).Run(MOJO_RESULT_ABORTED); - return; - } - - if (size == data_view_.size()) { - watcher_.Cancel(); - - // May delete |this|. - std::move(callback_).Run(MOJO_RESULT_OK); - return; - } - - data_view_ = - base::StringPiece(data_view_.data() + size, data_view_.size() - size); -} - -} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/string_data_pipe_producer.h b/chromium/mojo/public/cpp/system/string_data_pipe_producer.h deleted file mode 100644 index 0e66f2bcfaa..00000000000 --- a/chromium/mojo/public/cpp/system/string_data_pipe_producer.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 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 MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_PIPE_PRODUCER_H_ -#define MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_PIPE_PRODUCER_H_ - -#include <string> - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/strings/string_piece.h" -#include "mojo/public/cpp/system/data_pipe.h" -#include "mojo/public/cpp/system/simple_watcher.h" -#include "mojo/public/cpp/system/system_export.h" - -namespace mojo { - -// Helper class which takes ownership of a ScopedDataPipeProducerHandle and -// assumes responsibility for feeding it the contents of a given string. This -// takes care of waiting for pipe capacity as needed, and can notify callers -// asynchronously when the operation is complete. -// -// Note that the StringDataPipeProducer must be kept alive until notified of -// completion to ensure that all of the string's data is written to the pipe. -// Premature destruction may result in partial or total truncation of data made -// available to the consumer. -class MOJO_CPP_SYSTEM_EXPORT StringDataPipeProducer { - public: - using CompletionCallback = base::OnceCallback<void(MojoResult result)>; - - // Constructs a new StringDataPipeProducer which will write data to - // |producer|. - explicit StringDataPipeProducer(ScopedDataPipeProducerHandle producer); - ~StringDataPipeProducer(); - - // Describes what happens to the data when an async writing situation occurs - // (where the pipe cannot immediately accept all of the data). - enum class AsyncWritingMode { - // The |data| given to Write() may be invalidated before completion - // |callback| is called. The pending |data| is copied and owned by this - // class until all bytes are written. - STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION, - // The |data| given to Write() stays valid until the completion |callback| - // is called. - STRING_STAYS_VALID_UNTIL_COMPLETION - }; - - // Attempts to eventually write all of |data|. Invokes |callback| - // asynchronously when done. Note that |callback| IS allowed to delete this - // StringDataPipeProducer. - // - // If the data cannot be entirely written synchronously, then the |mode| - // determines how this class holds the pending data. - // - // If the write is successful |result| will be |MOJO_RESULT_OK|. Otherwise - // (e.g. if the producer detects the consumer is closed and the pipe has no - // remaining capacity) |result| will be |MOJO_RESULT_ABORTED|. - // - // Note that if the StringDataPipeProducer is destroyed before |callback| can - // be invoked, |callback| is *never* invoked, and the write will be - // permanently interrupted (and the producer handle closed) after making - // potentially only partial progress. - // - // Multiple writes may be performed in sequence (each one after the last - // completes), but Write() must not be called before the |callback| for the - // previous call to Write() (if any) has returned. - void Write(const base::StringPiece& data, - AsyncWritingMode mode, - CompletionCallback callback); - - private: - void InvokeCallback(MojoResult result); - void OnProducerHandleReady(MojoResult ready_result, - const HandleSignalsState& state); - - ScopedDataPipeProducerHandle producer_; - std::string data_; - base::StringPiece data_view_; - CompletionCallback callback_; - SimpleWatcher watcher_; - base::WeakPtrFactory<StringDataPipeProducer> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(StringDataPipeProducer); -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_PIPE_PRODUCER_H_ diff --git a/chromium/mojo/public/cpp/system/string_data_source.cc b/chromium/mojo/public/cpp/system/string_data_source.cc new file mode 100644 index 00000000000..788195b4b3d --- /dev/null +++ b/chromium/mojo/public/cpp/system/string_data_source.cc @@ -0,0 +1,52 @@ +// Copyright 2019 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 "mojo/public/cpp/system/string_data_source.h" + +#include <algorithm> + +namespace mojo { + +StringDataSource::StringDataSource(base::StringPiece data, + AsyncWritingMode mode) { + switch (mode) { + case AsyncWritingMode::STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION: + data_ = std::string(data.data(), data.size()); + data_view_ = base::span<const char>(data_.data(), data_.size()); + break; + case AsyncWritingMode::STRING_STAYS_VALID_UNTIL_COMPLETION: + data_view_ = base::span<const char>(data.data(), data.size()); + break; + } +} + +StringDataSource::~StringDataSource() = default; + +bool StringDataSource::IsValid() const { + return true; +} + +int64_t StringDataSource::GetLength() const { + return data_view_.size(); +} + +DataPipeProducer::DataSource::ReadResult StringDataSource::Read( + int64_t offset, + base::span<char> buffer) { + ReadResult result; + if (static_cast<size_t>(offset) <= data_view_.size()) { + size_t readable_size = data_view_.size() - offset; + size_t writable_size = buffer.size(); + size_t copyable_size = std::min(readable_size, writable_size); + for (size_t copied_size = 0; copied_size < copyable_size; ++copied_size) + buffer[copied_size] = data_view_[offset + copied_size]; + result.bytes_read = copyable_size; + } else { + NOTREACHED(); + result.result = MOJO_RESULT_OUT_OF_RANGE; + } + return result; +} + +} // namespace mojo diff --git a/chromium/mojo/public/cpp/system/string_data_source.h b/chromium/mojo/public/cpp/system/string_data_source.h new file mode 100644 index 00000000000..8c88109528b --- /dev/null +++ b/chromium/mojo/public/cpp/system/string_data_source.h @@ -0,0 +1,49 @@ +// Copyright 2019 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 MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_SOURCE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_SOURCE_H_ + +#include <string> + +#include "base/containers/span.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "mojo/public/cpp/system/data_pipe_producer.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// A class to wrap base::StringPiece as DataPipeProducer::DataSource class. +class MOJO_CPP_SYSTEM_EXPORT StringDataSource final + : public DataPipeProducer::DataSource { + public: + enum class AsyncWritingMode { + // The |data| given to the constructor may be invalidated before completion + // |callback| is called. The pending |data| is copied and owned by this + // class until all bytes are written. + STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION, + // The |data| given to the constructor stays valid until this instance is + // moved to DataPipeProducer and the completion callback is called. + STRING_STAYS_VALID_UNTIL_COMPLETION + }; + + StringDataSource(base::StringPiece data, AsyncWritingMode mode); + ~StringDataSource() override; + + private: + // DataPipeProducer::DataSource: + bool IsValid() const override; + int64_t GetLength() const override; + ReadResult Read(int64_t offset, base::span<char> buffer) override; + + std::string data_; + base::span<const char> data_view_; + + DISALLOW_COPY_AND_ASSIGN(StringDataSource); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_STRING_DATA_SOURCE_H_ diff --git a/chromium/mojo/public/cpp/system/tests/BUILD.gn b/chromium/mojo/public/cpp/system/tests/BUILD.gn index c08f3c1099e..0dabd992cc7 100644 --- a/chromium/mojo/public/cpp/system/tests/BUILD.gn +++ b/chromium/mojo/public/cpp/system/tests/BUILD.gn @@ -8,12 +8,12 @@ source_set("tests") { sources = [ "core_unittest.cc", "data_pipe_drainer_unittest.cc", - "file_data_pipe_producer_unittest.cc", + "data_pipe_producer_unittest.cc", "handle_signal_tracker_unittest.cc", "handle_signals_state_unittest.cc", "scope_to_message_pipe_unittest.cc", "simple_watcher_unittest.cc", - "string_data_pipe_producer_unittest.cc", + "string_data_source_unittest.cc", "wait_set_unittest.cc", "wait_unittest.cc", ] diff --git a/chromium/mojo/public/interfaces/bindings/interface_control_messages.mojom b/chromium/mojo/public/interfaces/bindings/interface_control_messages.mojom index 0a1904206a5..f9f24103876 100644 --- a/chromium/mojo/public/interfaces/bindings/interface_control_messages.mojom +++ b/chromium/mojo/public/interfaces/bindings/interface_control_messages.mojom @@ -57,6 +57,9 @@ struct RunOrClosePipeMessageParams { }; union RunOrClosePipeInput { RequireVersion require_version; + EnableIdleTracking enable_idle_tracking; + MessageAck message_ack; + NotifyIdle notify_idle; }; // If the specified version of the user-defined interface is not supported, the @@ -65,3 +68,24 @@ union RunOrClosePipeInput { struct RequireVersion { uint32 version; }; + +// Sent by a remote caller endpoint to a receiving endpoint to enable idle state +// tracking. Once a receiving endpoint receives this message, it will begin +// acknowledging all received interface messages with a MessageAck, and may send +// arbitrarily many NotifyIdle messages any time it believes itself to have been +// idle for a continuous period at least as long as |timeout_in_microseconds|. +struct EnableIdleTracking { + // NOTE: It would be nice to use mojo_base.mojom.TimeDelta here, but currently + // that results in a tricky dependency cycle for JS bindings. + int64 timeout_in_microseconds; +}; + +// Sent by a receiving endpoint to its client to acknowledge message receipt. +// Should only be sent by endpoints which have previously received an +// EnableIdleTracking message. +struct MessageAck {}; + +// Sent by a receiving endpoint to its client to indicate that the receiver is +// currently idle. Should only be sent by endpoints which have previously +// received and EnableIdleTracking message. +struct NotifyIdle {}; diff --git a/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn b/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn index a5efd42d93c..97f241a74aa 100644 --- a/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/chromium/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -301,6 +301,7 @@ mojom("test_interfaces") { ":echo", ":test_mojom_import", ":test_mojom_import2", + "//mojo/public/mojom/base", ] support_lazy_serialization = true diff --git a/chromium/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/chromium/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom index 5960d756657..1bcd7502950 100644 --- a/chromium/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom +++ b/chromium/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom @@ -7,6 +7,8 @@ Foo = "hello world"] module sample; +import "mojo/public/mojom/base/generic_pending_receiver.mojom"; + const uint64 kLong = 4405; enum Enum { @@ -30,3 +32,7 @@ interface IntegerAccessor { [MinVersion=1] SetInteger(int64 data, [MinVersion=3] Enum type); }; + +interface InterfaceFactory { + BindInterface(mojo_base.mojom.GenericPendingReceiver receiver); +}; diff --git a/chromium/mojo/public/java/system/BUILD.gn b/chromium/mojo/public/java/system/BUILD.gn index 49b2dbcaebf..c493a9dbd36 100644 --- a/chromium/mojo/public/java/system/BUILD.gn +++ b/chromium/mojo/public/java/system/BUILD.gn @@ -21,8 +21,6 @@ generate_jni("jni_headers") { public_deps = [ ":system_impl_java_jni_headers", ] - - jni_package = "mojo" } generate_jni("system_impl_java_jni_headers") { @@ -31,8 +29,6 @@ generate_jni("system_impl_java_jni_headers") { "src/org/chromium/mojo/system/impl/CoreImpl.java", "src/org/chromium/mojo/system/impl/WatcherImpl.java", ] - - jni_package = "mojo" } source_set("native_support") { @@ -174,5 +170,6 @@ instrumentation_test_apk("mojo_test_apk") { shared_libraries = [ ":mojo_java_unittests" ] apk_name = "MojoTest" android_manifest = "javatests/AndroidManifest.xml" + target_sdk_version = 21 enable_multidex = false } diff --git a/chromium/mojo/public/java/system/base_run_loop.cc b/chromium/mojo/public/java/system/base_run_loop.cc index 5b14852b130..012ce48c187 100644 --- a/chromium/mojo/public/java/system/base_run_loop.cc +++ b/chromium/mojo/public/java/system/base_run_loop.cc @@ -8,10 +8,10 @@ #include "base/android/scoped_java_ref.h" #include "base/bind.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" -#include "jni/BaseRunLoop_jni.h" +#include "base/task/single_thread_task_executor.h" +#include "mojo/public/java/system/system_impl_java_jni_headers/BaseRunLoop_jni.h" using base::android::JavaParamRef; @@ -21,8 +21,9 @@ namespace android { static jlong JNI_BaseRunLoop_CreateBaseRunLoop( JNIEnv* env, const JavaParamRef<jobject>& jcaller) { - base::MessageLoop* message_loop = new base::MessageLoop; - return reinterpret_cast<uintptr_t>(message_loop); + base::SingleThreadTaskExecutor* task_executor = + new base::SingleThreadTaskExecutor; + return reinterpret_cast<uintptr_t>(task_executor); } static void JNI_BaseRunLoop_Run(JNIEnv* env, @@ -57,7 +58,7 @@ static void JNI_BaseRunLoop_PostDelayedTask( // use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before // running the Runnable. runnable_ref.Reset(env, runnable); - reinterpret_cast<base::MessageLoop*>(runLoopID) + reinterpret_cast<base::SingleThreadTaskExecutor*>(runLoopID) ->task_runner() ->PostDelayedTask(FROM_HERE, base::BindOnce(&RunJavaRunnable, runnable_ref), @@ -68,9 +69,9 @@ static void JNI_BaseRunLoop_DeleteMessageLoop( JNIEnv* env, const JavaParamRef<jobject>& jcaller, jlong runLoopID) { - base::MessageLoop* message_loop = - reinterpret_cast<base::MessageLoop*>(runLoopID); - delete message_loop; + base::SingleThreadTaskExecutor* task_executor = + reinterpret_cast<base::SingleThreadTaskExecutor*>(runLoopID); + delete task_executor; } } // namespace android diff --git a/chromium/mojo/public/java/system/core_impl.cc b/chromium/mojo/public/java/system/core_impl.cc index 33f0576a4c4..97278a0d78a 100644 --- a/chromium/mojo/public/java/system/core_impl.cc +++ b/chromium/mojo/public/java/system/core_impl.cc @@ -8,9 +8,9 @@ #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/scoped_java_ref.h" -#include "jni/CoreImpl_jni.h" #include "mojo/public/c/system/core.h" #include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/java/system/system_impl_java_jni_headers/CoreImpl_jni.h" namespace mojo { namespace android { diff --git a/chromium/mojo/public/java/system/javatests/AndroidManifest.xml b/chromium/mojo/public/java/system/javatests/AndroidManifest.xml index 84b217bdcfc..04abc64e684 100644 --- a/chromium/mojo/public/java/system/javatests/AndroidManifest.xml +++ b/chromium/mojo/public/java/system/javatests/AndroidManifest.xml @@ -8,8 +8,6 @@ xmlns:tools="http://schemas.android.com/tools" package="org.chromium.mojo.tests"> - <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="21" /> - <uses-permission android:name="android.permission.INJECT_EVENTS" tools:ignore="ProtectedPermissions"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> diff --git a/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc b/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc index be37af1bb6e..52e2b047bc4 100644 --- a/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc +++ b/chromium/mojo/public/java/system/javatests/mojo_test_rule.cc @@ -8,13 +8,13 @@ #include "base/bind.h" #include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/single_thread_task_executor.h" #include "base/test/test_support_android.h" #include "base/threading/thread_task_runner_handle.h" -#include "jni/MojoTestRule_jni.h" #include "mojo/core/embedder/embedder.h" +#include "mojo/public/java/system/jni_headers/MojoTestRule_jni.h" using base::android::JavaParamRef; @@ -24,7 +24,7 @@ struct TestEnvironment { TestEnvironment() {} base::ShadowingAtExitManager at_exit; - base::MessageLoop message_loop; + base::SingleThreadTaskExecutor main_task_executor; }; } // namespace diff --git a/chromium/mojo/public/java/system/javatests/validation_test_util.cc b/chromium/mojo/public/java/system/javatests/validation_test_util.cc index 7aa1098ead2..7c3c0a64edd 100644 --- a/chromium/mojo/public/java/system/javatests/validation_test_util.cc +++ b/chromium/mojo/public/java/system/javatests/validation_test_util.cc @@ -9,8 +9,8 @@ #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/test/test_support_android.h" -#include "jni/ValidationTestUtil_jni.h" #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" +#include "mojo/public/java/system/jni_headers/ValidationTestUtil_jni.h" using base::android::JavaParamRef; using base::android::ScopedJavaLocalRef; diff --git a/chromium/mojo/public/java/system/watcher_impl.cc b/chromium/mojo/public/java/system/watcher_impl.cc index 36196487ebc..e251d2e6cbb 100644 --- a/chromium/mojo/public/java/system/watcher_impl.cc +++ b/chromium/mojo/public/java/system/watcher_impl.cc @@ -8,9 +8,9 @@ #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/bind.h" -#include "jni/WatcherImpl_jni.h" #include "mojo/public/cpp/system/handle.h" #include "mojo/public/cpp/system/simple_watcher.h" +#include "mojo/public/java/system/system_impl_java_jni_headers/WatcherImpl_jni.h" namespace mojo { namespace android { diff --git a/chromium/mojo/public/js/interface_support.js b/chromium/mojo/public/js/interface_support.js index 77ab0f966c3..36ec5dd7f0c 100644 --- a/chromium/mojo/public/js/interface_support.js +++ b/chromium/mojo/public/js/interface_support.js @@ -11,7 +11,7 @@ goog.provide('mojo.internal.interfaceSupport'); /** - * Handles incoming interface control messages on a proxy or router endpoint. + * Handles incoming interface control messages on a remote or router endpoint. */ mojo.internal.interfaceSupport.ControlMessageHandler = class { /** @param {!MojoHandle} handle */ @@ -72,8 +72,8 @@ mojo.internal.interfaceSupport.ControlMessageHandler = class { }; /** - * Captures metadata about a request which was sent by a local proxy, for which - * a response is expected. + * Captures metadata about a request which was sent by a remote, for which a + * response is expected. */ mojo.internal.interfaceSupport.PendingResponse = class { /** @@ -150,16 +150,16 @@ mojo.internal.interfaceSupport.ConnectionErrorEventRouter = class { }; /** - * Generic helper used to implement all generated proxy classes. Knows how to + * Generic helper used to implement all generated remote classes. Knows how to * serialize requests and deserialize their replies, both according to * declarative message structure specs. * @template T * @export */ -mojo.internal.interfaceSupport.InterfaceProxyBase = class { +mojo.internal.interfaceSupport.InterfaceRemoteBase = class { /** * @param {!function(new:T, !MojoHandle)} requestType - * @param {MojoHandle=} opt_handle The message pipe handle to use as a proxy + * @param {MojoHandle=} opt_handle The message pipe handle to use as a remote * endpoint. If null, this object must be bound with bindHandle before * it can be used to send any messages. * @public @@ -196,7 +196,7 @@ mojo.internal.interfaceSupport.InterfaceProxyBase = class { /** * @return {!T} */ - createRequest() { + bindNewPipeAndPassReceiver() { let {handle0, handle1} = Mojo.createMessagePipe(); this.bindHandle(handle0); return new this.requestType_(handle1); @@ -208,7 +208,7 @@ mojo.internal.interfaceSupport.InterfaceProxyBase = class { */ bindHandle(handle) { if (this.handle) - throw new Error('Proxy already bound.'); + throw new Error('Remote already bound.'); this.handle = handle; const reader = new mojo.internal.interfaceSupport.HandleReader(handle); @@ -253,7 +253,8 @@ mojo.internal.interfaceSupport.InterfaceProxyBase = class { sendMessage(ordinal, paramStruct, responseStruct, args) { if (!this.handle) { throw new Error( - 'Attempting to use an unbound proxy. Try $.createRequest() first.') + 'Attempting to use an unbound remote. Try ' + + '$.bindNewPipeAndPassReceiver() first.') } // The pipe has already been closed, so just drop the message. @@ -344,33 +345,42 @@ mojo.internal.interfaceSupport.InterfaceProxyBase = class { }; /** - * Wrapper around mojo.internal.interfaceSupport.InterfaceProxyBase that - * exposes the subset of InterfaceProxyBase's method that users are allowed + * Wrapper around mojo.internal.interfaceSupport.InterfaceRemoteBase that + * exposes the subset of InterfaceRemoteBase's method that users are allowed * to use. * @template T * @export */ -mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper = class { +mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper = class { /** - * @param {!mojo.internal.interfaceSupport.InterfaceProxyBase<T>} proxy + * @param {!mojo.internal.interfaceSupport.InterfaceRemoteBase<T>} remote * @public */ - constructor(proxy) { - /** @private {!mojo.internal.interfaceSupport.InterfaceProxyBase<T>} */ - this.proxy_ = proxy; + constructor(remote) { + /** @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<T>} */ + this.remote_ = remote; } + // TODO(ortuno): Remove once new names are used in the exposed interfaces. /** * @return {!T} * @export */ createRequest() { - return this.proxy_.createRequest(); + return this.remote_.bindNewPipeAndPassReceiver(); + } + + /** + * @return {!T} + * @export + */ + bindNewPipeAndPassReceiver() { + return this.remote_.bindNewPipeAndPassReceiver(); } /** @export */ close() { - this.proxy_.close(); + this.remote_.close(); } /** @@ -378,7 +388,7 @@ mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper = class { * @export */ flushForTesting() { - return this.proxy_.flushForTesting(); + return this.remote_.flushForTesting(); } } @@ -417,7 +427,7 @@ mojo.internal.interfaceSupport.CallbackRouter = class { * messages to listeners. * @export */ -mojo.internal.interfaceSupport.InterfaceCallbackTarget = class { +mojo.internal.interfaceSupport.InterfaceCallbackReceiver = class { /** * @public * @param {!mojo.internal.interfaceSupport.CallbackRouter} callbackRouter @@ -449,7 +459,7 @@ mojo.internal.interfaceSupport.InterfaceCallbackTarget = class { * @return {!Function} * @export */ - createTargetHandler(expectsResponse) { + createReceiverHandler(expectsResponse) { if (expectsResponse) return this.dispatchWithResponse_.bind(this); return this.dispatch_.bind(this); @@ -488,7 +498,7 @@ mojo.internal.interfaceSupport.InterfaceCallbackTarget = class { }; /** - * Wraps message handlers attached to an InterfaceTarget. + * Wraps message handlers attached to an InterfaceReceiver. */ mojo.internal.interfaceSupport.MessageHandler = class { /** @@ -510,20 +520,28 @@ mojo.internal.interfaceSupport.MessageHandler = class { }; /** - * Listens for incoming request messages on a message pipe, dispatching them to - * any registered handlers. Handlers are registered against a specific ordinal - * message number. + * Generic helper that listens for incoming request messages on a message pipe, + * dispatching them to any registered handlers. Handlers are registered against + * a specific ordinal message number. It has methods to perform operations + * related to the interface pipe e.g. bind the pipe, close it, etc. Should only + * be used by the generated receiver classes. + * @template T * @export */ -mojo.internal.interfaceSupport.InterfaceTarget = class { - /** @public */ - constructor() { +mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal = class { + /** + * @param {!function(new:T)} remoteType + * @public + */ + constructor(remoteType) { /** * @private {!Map<MojoHandle, * !mojo.internal.interfaceSupport.HandleReader>} */ this.readers_ = new Map; + /** @private {!function(new:T)} */ + this.remoteType_ = remoteType; /** * @private {!Map<number, !mojo.internal.interfaceSupport.MessageHandler>} */ @@ -565,6 +583,16 @@ mojo.internal.interfaceSupport.InterfaceTarget = class { new mojo.internal.interfaceSupport.ControlMessageHandler(handle); } + /** + * @return {!T} + * @export + */ + bindNewPipeAndPassRemote() { + let remote = new this.remoteType_; + this.bindHandle(remote.$.bindNewPipeAndPassReceiver().handle); + return remote; + } + /** @export */ closeBindings() { for (const reader of this.readers_.values()) @@ -592,7 +620,7 @@ mojo.internal.interfaceSupport.InterfaceTarget = class { if (this.controlMessageHandler_.maybeHandleControlMessage(header, buffer)) return; if (header.flags & mojo.internal.kMessageFlagIsResponse) - throw new Error('Received unexpected response on interface target'); + throw new Error('Received unexpected response on interface receiver'); const handler = this.messageHandlers_.get(header.ordinal); if (!handler) throw new Error('Received unknown message'); @@ -653,9 +681,68 @@ mojo.internal.interfaceSupport.InterfaceTarget = class { }; /** + * Generic helper used to perform operations related to the interface pipe e.g. + * bind the pipe, close it, flush it for testing, etc. Wraps + * mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal and exposes a + * subset of methods that meant to be used by users of a receiver class. + * + * @template T + * @export + */ +mojo.internal.interfaceSupport.InterfaceReceiverHelper = class { + /** + * @param {!mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal<T>} + * helper_internal + * @public + */ + constructor(helper_internal) { + /** + * @private {!mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal<T>} + */ + this.helper_internal_ = helper_internal; + } + + /** + * Binds a new handle to this object. Messages which arrive on the handle will + * be read and dispatched to this object. + * + * @param {!MojoHandle} handle + * @export + */ + bindHandle(handle) { + this.helper_internal_.bindHandle(handle); + } + + // TODO(ortuno): Remove once new names are used in the exposed interfaces. + /** + * Returns a remote for this interface which sends messages directly to this + * object. Any number of proxies may be created to the same object. + * + * @return {!T} + * @export + */ + createProxy() { + return this.helper_internal_.bindNewPipeAndPassRemote(); + } + + /** + * @return {!T} + * @export + */ + bindNewPipeAndPassRemote() { + return this.helper_internal_.bindNewPipeAndPassRemote(); + } + + /** @export */ + close() { + this.helper_internal_.closeBindings(); + } +} + +/** * Watches a MojoHandle for readability or peer closure, forwarding either event - * to one of two callbacks on the reader. Used by both InterfaceProxyBase and - * InterfaceTarget to watch for incoming messages. + * to one of two callbacks on the reader. Used by both InterfaceRemoteBase and + * InterfaceReceiverHelperInternal to watch for incoming messages. */ mojo.internal.interfaceSupport.HandleReader = class { /** diff --git a/chromium/mojo/public/js/test/BUILD.gn b/chromium/mojo/public/js/test/BUILD.gn index 8816cc14c29..00fb5213d01 100644 --- a/chromium/mojo/public/js/test/BUILD.gn +++ b/chromium/mojo/public/js/test/BUILD.gn @@ -13,6 +13,19 @@ mojom("test_mojom") { "module_b_1.test-mojom", "module_b_2.test-mojom", ] + + use_old_js_lite_bindings_names = false +} + +mojom("test_old_names_mojom") { + testonly = true + sources = [ + "module_a_old_names.test-mojom", + "module_b_1_old_names.test-mojom", + "module_b_2_old_names.test-mojom", + ] + + use_old_js_lite_bindings_names = true } if (enable_mojom_closure_compile || closure_compile) { @@ -22,6 +35,7 @@ if (enable_mojom_closure_compile || closure_compile) { ] deps = [ ":test_mojom_js_library_for_compile", + ":test_old_names_mojom_js_library_for_compile", ] } @@ -32,6 +46,7 @@ if (enable_mojom_closure_compile || closure_compile) { deps = [ ":compile_test_sources", ":test_mojom_js_library_for_compile", + ":test_old_names_mojom_js_library_for_compile", ] closure_flags = strict_error_checking_closure_args + [ "compilation_level=ADVANCED_OPTIMIZATIONS", diff --git a/chromium/mojo/public/mojom/base/BUILD.gn b/chromium/mojo/public/mojom/base/BUILD.gn index e6c8e1a76c4..c1a5d9e6c73 100644 --- a/chromium/mojo/public/mojom/base/BUILD.gn +++ b/chromium/mojo/public/mojom/base/BUILD.gn @@ -13,9 +13,12 @@ mojom_component("base") { "file_error.mojom", "file_info.mojom", "file_path.mojom", + "generic_pending_receiver.mojom", "memory_allocator_dump_cross_process_uid.mojom", "memory_pressure_level.mojom", + "message_loop_type.mojom", "process_id.mojom", + "read_only_buffer.mojom", "ref_counted_memory.mojom", "shared_memory.mojom", "string16.mojom", @@ -40,9 +43,3 @@ mojom_component("base") { output_prefix = "mojo_base_mojom" macro_prefix = "MOJO_BASE_MOJOM" } - -mojom("read_only_buffer") { - sources = [ - "read_only_buffer.mojom", - ] -} diff --git a/chromium/mojo/public/mojom/base/generic_pending_receiver.mojom b/chromium/mojo/public/mojom/base/generic_pending_receiver.mojom new file mode 100644 index 00000000000..67dda272651 --- /dev/null +++ b/chromium/mojo/public/mojom/base/generic_pending_receiver.mojom @@ -0,0 +1,16 @@ +// Copyright 2019 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. + +module mojo_base.mojom; + +// Convenience helper to wrap the pairing of a receiving pipe endpoint and the +// name of the interface expected by the remote endpoint. +// +// This should be used sparingly, in cases where APIs need to dynamically pass +// different types of receivers that cannot or should not be known at compile +// time. +struct GenericPendingReceiver { + string interface_name; + handle<message_pipe> receiving_pipe; +}; diff --git a/chromium/mojo/public/mojom/base/message_loop_type.mojom b/chromium/mojo/public/mojom/base/message_loop_type.mojom new file mode 100644 index 00000000000..d0d87757d5f --- /dev/null +++ b/chromium/mojo/public/mojom/base/message_loop_type.mojom @@ -0,0 +1,21 @@ +// Copyright 2019 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. + +module mojo_base.mojom; + +// Mirror of base::MessageLoop::Type. +enum MessageLoopType { + kDefault, + kUi, + kCustom, + kIo, + [EnableIf=is_android] + kJava, + [EnableIf=is_mac] + kNsRunloop, + [EnableIf=is_ios] + kNsRunloop, + [EnableIf=is_win] + kUiWithWmQuitSupport, +}; diff --git a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni index d70eb62df58..25384ccd0ad 100644 --- a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni @@ -3,8 +3,7 @@ # found in the LICENSE file. _typemap_imports = [ - "//ash/public/interfaces/typemaps.gni", - "//chrome/chrome_cleaner/interfaces/typemaps/typemaps.gni", + "//chrome/chrome_cleaner/mojom/typemaps/typemaps.gni", "//chrome/common/importer/typemaps.gni", "//chrome/common/media_router/mojo/typemaps.gni", "//chrome/typemaps.gni", @@ -15,14 +14,15 @@ _typemap_imports = [ "//chromeos/services/secure_channel/public/mojom/typemaps.gni", "//components/arc/common/typemaps.gni", "//components/chromeos_camera/common/typemaps.gni", - "//components/sync/mojo/typemaps.gni", + "//components/sync/mojom/typemaps.gni", "//components/typemaps.gni", - "//content/common/bluetooth/typemaps.gni", + "//content/browser/typemaps.gni", "//content/common/typemaps.gni", "//content/public/common/typemaps.gni", "//device/bluetooth/public/mojom/typemaps.gni", "//device/bluetooth/public/mojom/test/typemaps.gni", "//device/gamepad/public/cpp/typemaps.gni", + "//fuchsia/mojo/test_typemaps.gni", "//gpu/ipc/common/typemaps.gni", "//ipc/typemaps.gni", "//media/capture/mojom/typemaps.gni", @@ -51,13 +51,11 @@ _typemap_imports = [ "//ui/accessibility/mojom/typemaps.gni", "//ui/base/accelerators/mojo/typemaps.gni", "//ui/base/ime/mojo/typemaps.gni", - "//ui/base/mojo/typemaps.gni", - "//ui/display/mojo/typemaps.gni", - "//ui/events/devices/mojo/typemaps.gni", + "//ui/base/mojom/typemaps.gni", + "//ui/display/mojom/typemaps.gni", "//ui/events/mojo/typemaps.gni", "//ui/gfx/typemaps.gni", "//ui/latency/mojo/typemaps.gni", - "//ui/message_center/public/mojo/typemaps.gni", "//ui/ozone/public/interfaces/typemaps.gni", "//url/mojom/typemaps.gni", ] diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 304098680d8..8ad699bad15 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -291,7 +291,7 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( message->SerializeIfNecessary(); } else { if (!callback_.is_null()) - context->Dispatch(&callback_); + context->Dispatch(message, &callback_); return true; } } @@ -358,9 +358,9 @@ bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept( message->SerializeIfNecessary(); } else { context->HandleSyncResponse( -{%- for param in method.response_parameters %} + message +{%- for param in method.response_parameters %}, out_{{param.name}}_ -{%- if not loop.last -%}, {% endif -%} {%- endfor %}); *result_ = true; mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage( @@ -421,7 +421,7 @@ bool {{class_name}}StubDispatch::Accept( // Force serialization before dispatch in this case. message->SerializeIfNecessary(); } else { - context->Dispatch(impl); + context->Dispatch(message, impl); return true; } } @@ -484,7 +484,7 @@ bool {{class_name}}StubDispatch::AcceptWithResponder( message->request_id(), message->has_flag(mojo::Message::kFlagIsSync), std::move(responder)); - context->Dispatch(impl, std::move(callback)); + context->Dispatch(message, impl, std::move(callback)); return true; } } diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl index e2ca7eafe2a..67940021e20 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl @@ -120,10 +120,21 @@ class {{message_typename}} } {% if not is_response %} - void Dispatch({{interface.name}}* impl + void Dispatch( + mojo::Message* message, + {{interface.name}}* impl {%- if method.response_parameters != None -%} , {{interface.name}}::{{method.name}}Callback callback {%- endif -%}) { + if (message->receiver_connection_group()) { +{%- for param in parameters -%} +{%- if param.kind|is_receiver_kind %} + param_{{param.name}}_.set_connection_group( + *message->receiver_connection_group()); +{%- endif %} +{%- endfor %} + } + impl->{{method.name}}( {%- for param in parameters -%} {%- if param.kind|is_interface_kind %} @@ -138,7 +149,17 @@ class {{message_typename}} {%- endif -%}); } {%- else %} - void Dispatch({{interface.name}}::{{method.name}}Callback* callback) { + void Dispatch(mojo::Message* message, + {{interface.name}}::{{method.name}}Callback* callback) { + if (message->receiver_connection_group()) { +{%- for param in parameters -%} +{%- if param.kind|is_receiver_kind %} + param_{{param.name}}_.set_connection_group( + *message->receiver_connection_group()); +{%- endif %} +{%- endfor %} + } + std::move(*callback).Run( {%- for param in parameters -%} {%- if param.kind|is_interface_kind %} @@ -152,10 +173,20 @@ class {{message_typename}} {% if method.sync %} void HandleSyncResponse( -{% for param in parameters %} + mojo::Message* message +{% for param in parameters %}, {{param.kind|cpp_wrapper_call_type}}* out_{{param.name}} - {%- if not loop.last -%}, {% endif -%} {%- endfor -%}) { + + if (message->receiver_connection_group()) { +{%- for param in parameters -%} +{%- if param.kind|is_receiver_kind %} + param_{{param.name}}_.set_connection_group( + *message->receiver_connection_group()); +{%- endif %} +{%- endfor %} + } + {% for param in parameters -%} {%- if param.kind|is_interface_kind %} out_{{param.name}}->Bind(std::move(param_{{param.name}}_)); diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl index 007e89e34e6..5df13c5127e 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl @@ -190,17 +190,21 @@ typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; {#--- Interface Forward Declarations -#} {% for interface in interfaces %} class {{interface.name}}; +{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }} using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>; using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>; using ThreadSafe{{interface.name}}Ptr = mojo::ThreadSafeInterfacePtr<{{interface.name}}>; +{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }} using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>; +{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }} using {{interface.name}}AssociatedPtr = mojo::AssociatedInterfacePtr<{{interface.name}}>; using ThreadSafe{{interface.name}}AssociatedPtr = mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>; using {{interface.name}}AssociatedPtrInfo = mojo::AssociatedInterfacePtrInfo<{{interface.name}}>; +{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }} using {{interface.name}}AssociatedRequest = mojo::AssociatedInterfaceRequest<{{interface.name}}>; {% endfor %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl index 801c01523bd..0679323afd5 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -3,6 +3,8 @@ {{ kythe_annotation(struct_prefix) }} class {{export_attribute}} {{struct.name}} { public: + template <typename T> + using EnableIfSame = std::enable_if_t<std::is_same<{{struct.name}}, T>::value>; using DataView = {{struct.name}}DataView; using Data_ = internal::{{struct.name}}_Data; @@ -54,9 +56,7 @@ class {{export_attribute}} {{struct.name}} { // Equals() is a template so it is only instantiated if it is used. Thus, the // bindings generator does not need to know whether Equals() or == operator // are available for members. - template <typename T, - typename std::enable_if<std::is_same< - T, {{struct.name}}>::value>::type* = nullptr> + template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> bool Equals(const T& other) const; {%- if struct|is_hashable %} @@ -136,3 +136,24 @@ class {{export_attribute}} {{struct.name}} { DISALLOW_COPY_AND_ASSIGN({{struct.name}}); {%- endif %} }; + +// The comparison operators are templates, so they are only instantiated if they +// are used. Thus, the bindings generator does not need to know whether +// comparison operators are available for members. +template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> +bool operator<(const T& lhs, const T& rhs); + +template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> +bool operator<=(const T& lhs, const T& rhs) { + return !(rhs < lhs); +} + +template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> +bool operator>(const T& lhs, const T& rhs) { + return rhs < lhs; +} + +template <typename T, {{struct.name}}::EnableIfSame<T>* = nullptr> +bool operator>=(const T& lhs, const T& rhs) { + return !(lhs < rhs); +} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl index fab27cd8d00..32bf674804c 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl @@ -8,9 +8,7 @@ template <typename StructPtrType> ); } -template <typename T, - typename std::enable_if<std::is_same< - T, {{struct.name}}>::value>::type*> +template <typename T, {{struct.name}}::EnableIfSame<T>*> bool {{struct.name}}::Equals(const T& other_struct) const { {%- for field in struct.fields %} if (!mojo::Equals(this->{{field.name}}, other_struct.{{field.name}})) @@ -18,3 +16,14 @@ bool {{struct.name}}::Equals(const T& other_struct) const { {%- endfor %} return true; } + +template <typename T, {{struct.name}}::EnableIfSame<T>*> +bool operator<(const T& lhs, const T& rhs) { +{%- for field in struct.fields %} + if (lhs.{{field.name}} < rhs.{{field.name}}) + return true; + if (rhs.{{field.name}} < lhs.{{field.name}}) + return false; +{%- endfor %} + return false; +} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl index 094f0dd80e2..295987a6882 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl @@ -18,15 +18,18 @@ {% endmacro %} {% if generate_closure_exports -%} +{% if not use_old_names -%} goog.provide('{{module.namespace}}.{{interface.name}}'); +{% endif %} +goog.provide('{{module.namespace}}.{{interface.name}}{{primitives_names.receiver}}'); goog.provide('{{module.namespace}}.{{interface.name}}CallbackRouter'); goog.provide('{{module.namespace}}.{{interface.name}}Interface'); -goog.provide('{{module.namespace}}.{{interface.name}}Proxy'); -goog.provide('{{module.namespace}}.{{interface.name}}Request'); +goog.provide('{{module.namespace}}.{{interface.name}}{{primitives_names.remote}}'); +goog.provide('{{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}}'); {% endif %} /** @export */ -{{module.namespace}}.{{interface.name}}Request = class { +{{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}} = class { /** @param {!MojoHandle} handle */ constructor(handle) { /** @public {!MojoHandle} */ @@ -52,21 +55,21 @@ goog.provide('{{module.namespace}}.{{interface.name}}Request'); * @export * @implements { {{module.namespace}}.{{interface.name}}Interface } */ -{{module.namespace}}.{{interface.name}}Proxy = class { +{{module.namespace}}.{{interface.name}}{{primitives_names.remote}} = class { /** @param {MojoHandle=} opt_handle */ constructor(opt_handle) { /** - * @private {!mojo.internal.interfaceSupport.InterfaceProxyBase<!{{module.namespace}}.{{interface.name}}Request>} + * @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<!{{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}}>} */ this.proxy = - new mojo.internal.interfaceSupport.InterfaceProxyBase( - {{module.namespace}}.{{interface.name}}Request, + new mojo.internal.interfaceSupport.InterfaceRemoteBase( + {{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}}, opt_handle); /** - * @public {!mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper<!{{module.namespace}}.{{interface.name}}Request>} + * @public {!mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper<!{{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}}>} */ - this.$ = new mojo.internal.interfaceSupport.InterfaceProxyBaseWrapper(this.proxy); + this.$ = new mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper(this.proxy); /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ this.onConnectionError = this.proxy.getConnectionErrorEventRouter(); @@ -109,16 +112,24 @@ goog.provide('{{module.namespace}}.{{interface.name}}Request'); * * @export */ -{{module.namespace}}.{{interface.name}} = class { +{{module.namespace}}.{{interface.name}}{{primitives_names.receiver}} = class { /** * @param {!{{module.namespace}}.{{interface.name}}Interface } impl */ constructor(impl) { - this.target_ = new mojo.internal.interfaceSupport.InterfaceTarget; + /** @private {!mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal<!{{module.namespace}}.{{interface.name}}{{primitives_names.remote}}>} */ + this.helper_internal_ = new mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal( + {{module.namespace}}.{{interface.name}}{{primitives_names.remote}}); + + /** + * @public {!mojo.internal.interfaceSupport.InterfaceReceiverHelper<!{{module.namespace}}.{{interface.name}}{{primitives_names.remote}}>} + */ + this.$ = new mojo.internal.interfaceSupport.InterfaceReceiverHelper(this.helper_internal_); + {% for method in interface.methods %} {%- set interface_message_id = interface.mojom_name ~ "_" ~ method.mojom_name %} - this.target_.registerHandler( + this.helper_internal_.registerHandler( {{method.ordinal}}, {{module.namespace}}.{{interface_message_id}}_ParamsSpec.$, {%- if method.response_parameters != None %} @@ -129,29 +140,10 @@ goog.provide('{{module.namespace}}.{{interface.name}}Request'); impl.{{method.name}}.bind(impl)); {%- endfor %} /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ - this.onConnectionError = this.target_.getConnectionErrorEventRouter(); - } - - /** - * Binds a new handle to this object. Messages which arrive on the handle will - * be read and dispatched to this object. - * - * @param {!MojoHandle} handle - * @export - */ - bindHandle(handle) { - this.target_.bindHandle(handle); - } - - /** - * Closes all bindings bound to this target. - * - * @export - */ - closeBindings() { - this.target_.closeBindings(); + this.onConnectionError = this.helper_internal_.getConnectionErrorEventRouter(); } + {% if use_old_names -%} /** * Returns a proxy for this interface which sends messages to the browser. * The browser must have an interface request binder registered for this @@ -163,22 +155,17 @@ goog.provide('{{module.namespace}}.{{interface.name}}Request'); static getProxy() { let proxy = new {{module.namespace}}.{{interface.name}}Proxy; Mojo.bindInterface('{{mojom_namespace}}.{{interface.name}}', - proxy.$.createRequest().handle); + proxy.$.bindNewPipeAndPassReceiver().handle); return proxy; } /** - * Returns a proxy for this interface which sends messages directly to this - * object. Any number of proxies may be created to the same object. - * - * @return {!{{module.namespace}}.{{interface.name}}Proxy} - * @export + * @return {!string} */ - createProxy() { - let proxy = new {{module.namespace}}.{{interface.name}}Proxy; - this.target_.bindHandle(proxy.$.createRequest().handle); - return proxy; + static get $interfaceName() { + return "{{mojom_namespace}}.{{interface.name}}"; } + {% endif %} }; {#--- Enums #} @@ -189,80 +176,79 @@ goog.provide('{{module.namespace}}.{{interface.name}}Request'); enum) }} {%- endfor %} +{% if not use_old_names -%} + /** - * @const {string} - * @export + * @export */ -{{module.namespace}}.{{interface.name}}.$interfaceName = - '{{mojom_namespace}}.{{interface.name}}'; +{{module.namespace}}.{{interface.name}} = class { + /** + * @return {!string} + */ + static get $interfaceName() { + return "{{mojom_namespace}}.{{interface.name}}"; + } + + /** + * Returns a remote for this interface which sends messages to the browser. + * The browser must have an interface request binder registered for this + * interface and accessible to the calling document's frame. + * + * @return {!{{module.namespace}}.{{interface.name}}Remote} + * @export + */ + static getRemote() { + let remote = new {{module.namespace}}.{{interface.name}}Remote; + Mojo.bindInterface(this.$interfaceName, + remote.$.bindNewPipeAndPassReceiver().handle); + return remote; + } +}; + +{% endif %} /** * An object which receives request messages for the {{interface.name}} - * mojom interface and dispatches them as callbacks. One callback target exists + * mojom interface and dispatches them as callbacks. One callback receiver exists * on this object for each message defined in the mojom interface, and each - * target can have any number of listeners added to it. + * receiver can have any number of listeners added to it. * * @export */ {{module.namespace}}.{{interface.name}}CallbackRouter = class { constructor() { - this.target_ = new mojo.internal.interfaceSupport.InterfaceTarget; + this.helper_internal_ = new mojo.internal.interfaceSupport.InterfaceReceiverHelperInternal( + {{module.namespace}}.{{interface.name}}{{primitives_names.remote}}); + + /** + * @public {!mojo.internal.interfaceSupport.InterfaceReceiverHelper<!{{module.namespace}}.{{interface.name}}{{primitives_names.remote}}>} + */ + this.$ = new mojo.internal.interfaceSupport.InterfaceReceiverHelper(this.helper_internal_); + this.router_ = new mojo.internal.interfaceSupport.CallbackRouter; {% for method in interface.methods %} {%- set interface_message_id = interface.mojom_name ~ "_" ~ method.mojom_name %} /** - * @public {!mojo.internal.interfaceSupport.InterfaceCallbackTarget} + * @public {!mojo.internal.interfaceSupport.InterfaceCallbackReceiver} */ this.{{method.name}} = - new mojo.internal.interfaceSupport.InterfaceCallbackTarget( + new mojo.internal.interfaceSupport.InterfaceCallbackReceiver( this.router_); - this.target_.registerHandler( + this.helper_internal_.registerHandler( {{method.ordinal}}, {{module.namespace}}.{{interface_message_id}}_ParamsSpec.$, {%- if method.response_parameters != None %} {{module.namespace}}.{{interface_message_id}}_ResponseParamsSpec.$, - this.{{method.name}}.createTargetHandler(true /* expectsResponse */)); + this.{{method.name}}.createReceiverHandler(true /* expectsResponse */)); {%- else %} null, - this.{{method.name}}.createTargetHandler(false /* expectsResponse */)); + this.{{method.name}}.createReceiverHandler(false /* expectsResponse */)); {%- endif %} {%- endfor %} /** @public {!mojo.internal.interfaceSupport.ConnectionErrorEventRouter} */ - this.onConnectionError = this.target_.getConnectionErrorEventRouter(); - } - - /** - * Binds a new handle to this object. Messages which arrive on the handle will - * be read and dispatched as callbacks on this object. - * - * @param {!MojoHandle} handle - * @export - */ - bindHandle(handle) { - this.target_.bindHandle(handle); - } - - /** - * Closes all bindings bound to this target. The target will not receive any - * further message message events unless rebound to one or more handles. - */ - closeBindings() { - this.target_.closeBindings(); - } - - /** - * Returns a proxy for this interface which sends messages directly to this - * object. Any number of proxies may be created to the same object. - * - * @return {!{{module.namespace}}.{{interface.name}}Proxy} - * @export - */ - createProxy() { - let proxy = new {{module.namespace}}.{{interface.name}}Proxy; - this.target_.bindHandle(proxy.$.createRequest().handle); - return proxy; + this.onConnectionError = this.helper_internal_.getConnectionErrorEventRouter(); } /** diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/test/BUILD.gn b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/test/BUILD.gn index ec085c5f674..d3c94dbc139 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/lite/test/BUILD.gn +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/lite/test/BUILD.gn @@ -12,8 +12,17 @@ mojom("mojo_bindings") { ] } +mojom("mojo_old_names_bindings") { + testonly = true + sources = [ + "test_old_names.test-mojom", + ] + use_old_js_lite_bindings_names = true +} + js_type_check("closure_compile") { deps = [ + ":old_names_test", ":test", ] } @@ -21,5 +30,12 @@ js_type_check("closure_compile") { js_library("test") { deps = [ ":mojo_bindings_js_library_for_compile", + ":mojo_old_names_bindings_js_library_for_compile", + ] +} + +js_library("old_names_test") { + deps = [ + ":mojo_old_names_bindings_js_library_for_compile", ] } diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index 8bb05221397..fb6e0827b4a 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -353,6 +353,7 @@ class Generator(generator.Generator): "is_enum_kind": mojom.IsEnumKind, "is_integral_kind": mojom.IsIntegralKind, "is_interface_kind": mojom.IsInterfaceKind, + "is_receiver_kind": self._IsReceiverKind, "is_native_only_kind": IsNativeOnlyKind, "is_any_handle_kind": mojom.IsAnyHandleKind, "is_any_interface_kind": mojom.IsAnyInterfaceKind, @@ -643,6 +644,10 @@ class Generator(generator.Generator): return True return False + def _IsReceiverKind(self, kind): + return (mojom.IsPendingReceiverKind(kind) or + mojom.IsInterfaceRequestKind(kind)) + def _IsCopyablePassByValue(self, kind): if not self._IsTypemappedKind(kind): return False diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py index fcef5453cba..60ae83cf859 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py @@ -152,7 +152,7 @@ def GetNameForElement(element): raise Exception('Unexpected element: %s' % element) def GetInterfaceResponseName(method): - return UpperCamelCase(method.name + 'Response') + return UpperCamelCase(method.name + '_Response') def ParseStringAttribute(attribute): assert isinstance(attribute, basestring) diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py index 19a94f273ba..eb8d3b6f44a 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -264,7 +264,9 @@ class Generator(generator.Generator): "unions": self.module.unions, "generate_fuzzing": self.generate_fuzzing, "generate_closure_exports": for_compile, - } + "use_old_names": self.use_old_js_lite_bindings_names, + "primitives_names": self._GetPrimitivesNames(), + } @staticmethod def GetTemplatePrefix(): @@ -339,15 +341,15 @@ class Generator(generator.Generator): def _GenerateExterns(self): return self._GetParameters() - @UseJinja("lite/mojom-lite.js.tmpl") - def _GenerateLiteBindings(self): - return self._GetParameters() - @UseJinja("lite/mojom.html.tmpl") def _GenerateLiteHtml(self): return self._GetParameters() @UseJinja("lite/mojom-lite.js.tmpl") + def _GenerateLiteBindings(self): + return self._GetParameters() + + @UseJinja("lite/mojom-lite.js.tmpl") def _GenerateLiteBindingsForCompile(self): return self._GetParameters(for_compile=True) @@ -368,7 +370,7 @@ class Generator(generator.Generator): self.Write(self._GenerateLiteHtml(), "%s.html" % self.module.path) self.Write(self._GenerateLiteBindings(), "%s-lite.js" % self.module.path) self.Write(self._GenerateLiteBindingsForCompile(), - "%s-lite-for-compile.js" % self.module.path) + "%s-lite-for-compile.js" % self.module.path) def _SetUniqueNameForImports(self): used_names = set() @@ -464,9 +466,9 @@ class Generator(generator.Generator): mojom.IsEnumKind(kind)): return name if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): - return name + "Proxy" + return name + self._GetPrimitivesNames()["remote"] if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return name + "Request" + return name + self._GetPrimitivesNames()["pending_receiver"] # TODO(calamity): Support associated interfaces properly. if (mojom.IsAssociatedInterfaceKind(kind) or mojom.IsPendingAssociatedRemoteKind(kind)): @@ -561,17 +563,21 @@ class Generator(generator.Generator): mojom.IsEnumKind(kind)): return "%sSpec.$" % name if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind): - return "mojo.internal.InterfaceProxy(%sProxy)" % name + remote_name = name + self._GetPrimitivesNames()["remote"] + return "mojo.internal.InterfaceProxy(%s)" % remote_name if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind): - return "mojo.internal.InterfaceRequest(%sRequest)" % name + request_name = name + self._GetPrimitivesNames()["pending_receiver"] + return "mojo.internal.InterfaceRequest(%s)" % request_name if (mojom.IsAssociatedInterfaceKind(kind) or mojom.IsPendingAssociatedRemoteKind(kind)): + remote_name = name + self._GetPrimitivesNames()["remote"] # TODO(rockot): Implement associated interfaces. - return "mojo.internal.AssociatedInterfaceProxy(%sProxy)" % ( - name) + return "mojo.internal.AssociatedInterfaceProxy(%s)" % ( + remote_name) if (mojom.IsAssociatedInterfaceRequestKind(kind) or mojom.IsPendingAssociatedReceiverKind(kind)): - return "mojo.internal.AssociatedInterfaceRequest(%s)" % name + request_name = name + self._GetPrimitivesNames()["pending_receiver"] + return "mojo.internal.AssociatedInterfaceRequest(%s)" % request_name return name @@ -810,6 +816,20 @@ class Generator(generator.Generator): return self._ExpressionToText(token) + def _GetPrimitivesNames(self): + if self.use_old_js_lite_bindings_names: + return { + "remote": "Proxy", + "receiver": "", + "pending_receiver": "Request", + } + else: + return { + "remote": "Remote", + "receiver": "Receiver", + "pending_receiver": "PendingReceiver", + } + def _GenerateHtmlImports(self): result = [] for full_import in self.module.imports: diff --git a/chromium/mojo/public/tools/bindings/mojom.gni b/chromium/mojo/public/tools/bindings/mojom.gni index 8310a1bc1fe..8cdf5931304 100644 --- a/chromium/mojo/public/tools/bindings/mojom.gni +++ b/chromium/mojo/public/tools/bindings/mojom.gni @@ -282,6 +282,10 @@ if (enable_mojom_typemapping) { # correct dependency order. Note that this only has an effect if # the |enable_mojom_closure_compile| global arg is set to |true| as well. # +# use_old_js_lite_bindings_names (optional) +# Use old names i.e. FooProxy, Foo, getProxy(), etc. instead of the new +# names i.e. FooRemote, FooReceiver, getRemote(), etc. +# # The following parameters are used to support the component build. They are # needed so that bindings which are linked with a component can use the same # export settings for classes. The first three are for the chromium variant, and @@ -1267,6 +1271,11 @@ template("mojom") { if (generate_fuzzing) { args += [ "--generate_fuzzing" ] } + + if (defined(invoker.use_old_js_lite_bindings_names) && + invoker.use_old_js_lite_bindings_names) { + args += [ "--use_old_js_lite_bindings_names" ] + } } } diff --git a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py index 72f23d86fa9..d8a2118eb2b 100755 --- a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py +++ b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py @@ -214,6 +214,7 @@ class MojomProcessor(object): variant=args.variant, bytecode_path=args.bytecode_path, for_blink=args.for_blink, js_bindings_mode=args.js_bindings_mode, + use_old_js_lite_bindings_names=args.use_old_js_lite_bindings_names, export_attribute=args.export_attribute, export_header=args.export_header, generate_non_variant_code=args.generate_non_variant_code, @@ -466,6 +467,11 @@ def main(): "be \"new\" to generate new-style lite JS bindings in addition to the " "old, or \"old\" to only generate old bindings.") generate_parser.add_argument( + "--use_old_js_lite_bindings_names", action="store_true", + help="This option only affects the JavaScript bindings. Specifying this " + "argument causes the generated new-style lite JS bindings to use the old" + "names for primitives e.g. Foo, FooProxy, getProxy(), etc.") + generate_parser.add_argument( "--export_attribute", default="", help="Optional attribute to specify on class declaration to export it " "for the component build.") diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py index fe4fce20ec4..6487635ed43 100644 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py +++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py @@ -165,7 +165,9 @@ class Generator(object): # files to stdout. def __init__(self, module, output_dir=None, typemap=None, variant=None, bytecode_path=None, for_blink=False, - js_bindings_mode="new", export_attribute=None, + js_bindings_mode="new", + use_old_js_lite_bindings_names=False, + export_attribute=None, export_header=None, generate_non_variant_code=False, support_lazy_serialization=False, disallow_native_types=False, disallow_interfaces=False, generate_message_ids=False, @@ -177,6 +179,7 @@ class Generator(object): self.bytecode_path = bytecode_path self.for_blink = for_blink self.js_bindings_mode = js_bindings_mode + self.use_old_js_lite_bindings_names = use_old_js_lite_bindings_names self.export_attribute = export_attribute self.export_header = export_header self.generate_non_variant_code = generate_non_variant_code |