0

Mojo: Introduce GenericPendingAssociatedReceiver

This is analogous to GenericPendingReceiver but for associated
interfaces. It's needed for layering associated interfaces on
GpuChannel, but will also be useful for cleaning up legacy
Channel-associated interface support and for building whatever replaces
that in the future of browser-renderer IPC.

Bug: 1196476
Change-Id: I1380d1e47e3dc66c1364c82a938a7d30f4b4cf3d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2903727
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#889485}
This commit is contained in:
Ken Rockot
2021-06-04 22:16:50 +00:00
committed by Chromium LUCI CQ
parent 43ac907206
commit 493a59f3e2
24 changed files with 448 additions and 88 deletions

@ -192,7 +192,10 @@ mojom_component("mojom") {
output_prefix = "ipc_mojom"
macro_prefix = "IPC_MOJOM"
sources = [ "ipc.mojom" ]
public_deps = [ "//mojo/public/interfaces/bindings" ]
public_deps = [
"//mojo/public/interfaces/bindings",
"//mojo/public/mojom/base",
]
cpp_typemaps = [
{

@ -5,10 +5,7 @@
module IPC.mojom;
import "mojo/public/interfaces/bindings/native_struct.mojom";
// A placeholder interface type since we don't yet support generic associated
// message pipe handles.
interface GenericInterface {};
import "mojo/public/mojom/base/generic_pending_associated_receiver.mojom";
// Typemapped such that arbitrarily large IPC::Message objects can be sent and
// received with minimal copying.
@ -28,8 +25,7 @@ interface Channel {
// Requests a Channel-associated interface.
GetAssociatedInterface(
string name,
pending_associated_receiver<GenericInterface> receiver);
mojo_base.mojom.GenericPendingAssociatedReceiver receiver);
};
// A strictly nominal interface used to identify Channel bootstrap requests.

@ -24,6 +24,7 @@
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_sender.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
@ -104,9 +105,8 @@ class COMPONENT_EXPORT(IPC) Channel : public Sender {
const GenericAssociatedInterfaceFactory& factory) = 0;
// Requests an associated interface from the remote endpoint.
virtual void GetGenericRemoteAssociatedInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) = 0;
virtual void GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) = 0;
// Template helper to add an interface factory to this channel.
template <typename Interface>
@ -121,14 +121,6 @@ class COMPONENT_EXPORT(IPC) Channel : public Sender {
factory));
}
// Template helper to request a remote associated interface.
template <typename Interface>
void GetRemoteAssociatedInterface(
mojo::PendingAssociatedReceiver<Interface> receiver) {
GetGenericRemoteAssociatedInterface(Interface::Name_,
receiver.PassHandle());
}
private:
template <typename Interface>
static void BindPendingAssociatedReceiver(

@ -29,6 +29,7 @@
#include "ipc/trace_ipc_message.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/thread_safe_proxy.h"
@ -248,20 +249,22 @@ void ChannelMojo::OnPipeError() {
}
void ChannelMojo::OnAssociatedInterfaceRequest(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) {
mojo::GenericPendingAssociatedReceiver receiver) {
GenericAssociatedInterfaceFactory factory;
{
base::AutoLock locker(associated_interface_lock_);
auto iter = associated_interfaces_.find(name);
auto iter = associated_interfaces_.find(*receiver.interface_name());
if (iter != associated_interfaces_.end())
factory = iter->second;
}
if (!factory.is_null())
factory.Run(std::move(handle));
else
listener_->OnAssociatedInterfaceRequest(name, std::move(handle));
if (!factory.is_null()) {
factory.Run(receiver.PassHandle());
} else {
const std::string interface_name = *receiver.interface_name();
listener_->OnAssociatedInterfaceRequest(interface_name,
receiver.PassHandle());
}
}
bool ChannelMojo::Send(Message* message) {
@ -382,22 +385,20 @@ void ChannelMojo::AddGenericAssociatedInterface(
DCHECK(result.second);
}
void ChannelMojo::GetGenericRemoteAssociatedInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) {
void ChannelMojo::GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) {
if (message_reader_) {
if (!task_runner_->RunsTasksInCurrentSequence()) {
message_reader_->thread_safe_sender().GetAssociatedInterface(
name, mojo::PendingAssociatedReceiver<mojom::GenericInterface>(
std::move(handle)));
std::move(receiver));
return;
}
message_reader_->GetRemoteInterface(name, std::move(handle));
message_reader_->GetRemoteInterface(std::move(receiver));
} else {
// Attach the associated interface to a disconnected pipe, so that the
// associated interface pointer can be used to make calls (which are
// dropped).
mojo::AssociateWithDisconnectedPipe(std::move(handle));
mojo::AssociateWithDisconnectedPipe(receiver.PassHandle());
}
}

@ -92,8 +92,7 @@ class COMPONENT_EXPORT(IPC) ChannelMojo
void OnBrokenDataReceived() override;
void OnPipeError() override;
void OnAssociatedInterfaceRequest(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) override;
mojo::GenericPendingAssociatedReceiver receiver) override;
private:
ChannelMojo(
@ -112,9 +111,8 @@ class COMPONENT_EXPORT(IPC) ChannelMojo
void AddGenericAssociatedInterface(
const std::string& name,
const GenericAssociatedInterfaceFactory& factory) override;
void GetGenericRemoteAssociatedInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) override;
void GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) override;
void FinishConnectOnIOThread();

@ -980,7 +980,8 @@ DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
// Send a bunch of interleaved messages, alternating between the associated
// interface and a legacy IPC::Message.
mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> driver;
proxy()->GetRemoteAssociatedInterface(&driver);
proxy()->GetRemoteAssociatedInterface(
driver.BindNewEndpointAndPassReceiver());
for (int i = 0; i < ListenerWithSimpleProxyAssociatedInterface::kNumMessages;
++i) {
driver->ExpectValue(i);
@ -1073,7 +1074,8 @@ DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
// processed on the IO thread.
mojo::AssociatedRemote<IPC::mojom::IndirectTestDriver> driver;
mojo::AssociatedRemote<IPC::mojom::PingReceiver> ping_receiver;
proxy()->GetRemoteAssociatedInterface(&driver);
proxy()->GetRemoteAssociatedInterface(
driver.BindNewEndpointAndPassReceiver());
driver->GetPingReceiver(ping_receiver.BindNewEndpointAndPassReceiver());
base::RunLoop loop;
@ -1194,7 +1196,8 @@ TEST_F(IPCChannelProxyMojoTest, SyncAssociatedInterface) {
// while waiting on it
listener.set_response_value(42);
mojo::AssociatedRemote<IPC::mojom::SimpleTestClient> client;
proxy()->GetRemoteAssociatedInterface(&client);
proxy()->GetRemoteAssociatedInterface(
client.BindNewEndpointAndPassReceiver());
int32_t received_value;
EXPECT_TRUE(client->RequestValue(&received_value));
EXPECT_EQ(42, received_value);
@ -1300,7 +1303,8 @@ DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(SyncAssociatedInterface,
RunProxy();
mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> driver;
proxy()->GetRemoteAssociatedInterface(&driver);
proxy()->GetRemoteAssociatedInterface(
driver.BindNewEndpointAndPassReceiver());
client_impl.set_driver(driver.get());
// Simple sync message sanity check.
@ -1432,7 +1436,8 @@ TEST_F(IPCChannelProxyMojoTest, AssociatedRequestClose) {
RunProxy();
mojo::AssociatedRemote<IPC::mojom::AssociatedInterfaceVendor> vendor;
proxy()->GetRemoteAssociatedInterface(&vendor);
proxy()->GetRemoteAssociatedInterface(
vendor.BindNewEndpointAndPassReceiver());
mojo::AssociatedRemote<IPC::mojom::SimpleTestDriver> tester;
vendor->GetTestInterface(tester.BindNewEndpointAndPassReceiver());
base::RunLoop run_loop;
@ -1440,7 +1445,8 @@ TEST_F(IPCChannelProxyMojoTest, AssociatedRequestClose) {
run_loop.Run();
tester.reset();
proxy()->GetRemoteAssociatedInterface(&tester);
proxy()->GetRemoteAssociatedInterface(
tester.BindNewEndpointAndPassReceiver());
EXPECT_TRUE(WaitForClientShutdown());
DestroyProxy();
}

@ -602,13 +602,10 @@ void ChannelProxy::AddGenericAssociatedInterfaceForIOThread(
context()->AddGenericAssociatedInterfaceForIOThread(name, factory);
}
void ChannelProxy::GetGenericRemoteAssociatedInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) {
void ChannelProxy::GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) {
DCHECK(did_init_);
context()->thread_safe_channel().GetAssociatedInterface(
name, mojo::PendingAssociatedReceiver<mojom::GenericInterface>(
std::move(handle)));
context()->thread_safe_channel().GetAssociatedInterface(std::move(receiver));
}
void ChannelProxy::ClearIPCTaskRunner() {

@ -25,6 +25,7 @@
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
@ -195,15 +196,13 @@ class COMPONENT_EXPORT(IPC) ChannelProxy : public Sender {
}
// Requests an associated interface from the remote endpoint.
void GetGenericRemoteAssociatedInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle);
void GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver);
// Template helper to receive associated interfaces from the remote endpoint.
template <typename Interface>
void GetRemoteAssociatedInterface(mojo::AssociatedRemote<Interface>* proxy) {
GetGenericRemoteAssociatedInterface(
Interface::Name_, proxy->BindNewEndpointAndPassReceiver().PassHandle());
GetRemoteAssociatedInterface(proxy->BindNewEndpointAndPassReceiver());
}
#if defined(ENABLE_IPC_FUZZER)

@ -126,13 +126,10 @@ bool MessagePipeReader::Send(std::unique_ptr<Message> message) {
}
void MessagePipeReader::GetRemoteInterface(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) {
mojo::GenericPendingAssociatedReceiver receiver) {
if (!sender_.is_bound())
return;
sender_->GetAssociatedInterface(
name, mojo::PendingAssociatedReceiver<mojom::GenericInterface>(
std::move(handle)));
sender_->GetAssociatedInterface(std::move(receiver));
}
void MessagePipeReader::SetPeerPid(int32_t peer_pid) {
@ -165,11 +162,10 @@ void MessagePipeReader::Receive(MessageView message_view) {
}
void MessagePipeReader::GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<mojom::GenericInterface> receiver) {
mojo::GenericPendingAssociatedReceiver receiver) {
DCHECK(thread_checker_.CalledOnValidThread());
if (delegate_)
delegate_->OnAssociatedInterfaceRequest(name, receiver.PassHandle());
delegate_->OnAssociatedInterfaceRequest(std::move(receiver));
}
void MessagePipeReader::OnPipeError(MojoResult error) {

@ -21,6 +21,7 @@
#include "ipc/ipc_message.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
@ -55,8 +56,7 @@ class COMPONENT_EXPORT(IPC) MessagePipeReader : public mojom::Channel {
virtual void OnBrokenDataReceived() = 0;
virtual void OnPipeError() = 0;
virtual void OnAssociatedInterfaceRequest(
const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle) = 0;
mojo::GenericPendingAssociatedReceiver receiver) = 0;
};
// Builds a reader that reads messages from |receive_handle| and lets
@ -89,8 +89,7 @@ class COMPONENT_EXPORT(IPC) MessagePipeReader : public mojom::Channel {
bool Send(std::unique_ptr<Message> message);
// Requests an associated interface from the other end of the pipe.
void GetRemoteInterface(const std::string& name,
mojo::ScopedInterfaceEndpointHandle handle);
void GetRemoteInterface(mojo::GenericPendingAssociatedReceiver receiver);
mojo::AssociatedRemote<mojom::Channel>& sender() { return sender_; }
mojom::Channel& thread_safe_sender() { return thread_safe_sender_->proxy(); }
@ -104,9 +103,7 @@ class COMPONENT_EXPORT(IPC) MessagePipeReader : public mojom::Channel {
void SetPeerPid(int32_t peer_pid) override;
void Receive(MessageView message_view) override;
void GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<mojom::GenericInterface> receiver)
override;
mojo::GenericPendingAssociatedReceiver receiver) override;
void ForwardMessage(mojo::Message message);

@ -93,9 +93,7 @@ class PeerPidReceiver : public IPC::mojom::Channel {
}
void GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<IPC::mojom::GenericInterface> receiver)
override {}
mojo::GenericPendingAssociatedReceiver receiver) override {}
int32_t peer_pid() const { return peer_pid_; }

@ -179,23 +179,21 @@ void SyncMessageFilter::SignalAllEvents() {
}
}
void SyncMessageFilter::GetGenericRemoteAssociatedInterface(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
void SyncMessageFilter::GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) {
base::AutoLock auto_lock(lock_);
DCHECK(io_task_runner_ && io_task_runner_->BelongsToCurrentThread());
if (!channel_) {
// Attach the associated interface to a disconnected pipe, so that the
// associated interface pointer can be used to make calls (which are
// dropped).
mojo::AssociateWithDisconnectedPipe(std::move(handle));
mojo::AssociateWithDisconnectedPipe(receiver.PassHandle());
return;
}
Channel::AssociatedInterfaceSupport* support =
channel_->GetAssociatedInterfaceSupport();
support->GetGenericRemoteAssociatedInterface(
interface_name, std::move(handle));
support->GetRemoteAssociatedInterface(std::move(receiver));
}
} // namespace IPC

@ -15,8 +15,7 @@
#include "ipc/ipc_sender.h"
#include "ipc/ipc_sync_message.h"
#include "ipc/message_filter.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
namespace base {
@ -50,12 +49,13 @@ class COMPONENT_EXPORT(IPC) SyncMessageFilter : public MessageFilter,
//
// NOTE: This must ONLY be called on the Channel's thread, after
// OnFilterAdded.
void GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver);
template <typename Interface>
void GetRemoteAssociatedInterface(
mojo::PendingAssociatedRemote<Interface>* proxy) {
auto receiver = proxy->InitWithNewEndpointAndPassReceiver();
GetGenericRemoteAssociatedInterface(Interface::Name_,
receiver.PassHandle());
GetRemoteAssociatedInterface(proxy->InitWithNewEndpointAndPassReceiver());
}
protected:
@ -69,11 +69,6 @@ class COMPONENT_EXPORT(IPC) SyncMessageFilter : public MessageFilter,
// Signal all the pending sends as done, used in an error condition.
void SignalAllEvents();
// NOTE: This must ONLY be called on the Channel's thread.
void GetGenericRemoteAssociatedInterface(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle);
// The channel to which this filter was added.
Channel* channel_;

@ -88,6 +88,8 @@ component("shared_typemap_traits") {
"file_mojom_traits.h",
"file_path_mojom_traits.cc",
"file_path_mojom_traits.h",
"generic_pending_associated_receiver_mojom_traits.cc",
"generic_pending_associated_receiver_mojom_traits.h",
"generic_pending_receiver_mojom_traits.cc",
"generic_pending_receiver_mojom_traits.h",
"read_only_buffer_mojom_traits.cc",

@ -0,0 +1,38 @@
// Copyright 2021 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_associated_receiver_mojom_traits.h"
#include "base/strings/string_piece.h"
namespace mojo {
// static
bool StructTraits<mojo_base::mojom::GenericPendingAssociatedReceiverDataView,
GenericPendingAssociatedReceiver>::
IsNull(const GenericPendingAssociatedReceiver& receiver) {
return !receiver.is_valid();
}
// static
void StructTraits<mojo_base::mojom::GenericPendingAssociatedReceiverDataView,
GenericPendingAssociatedReceiver>::
SetToNull(GenericPendingAssociatedReceiver* receiver) {
receiver->reset();
}
// static
bool StructTraits<mojo_base::mojom::GenericPendingAssociatedReceiverDataView,
GenericPendingAssociatedReceiver>::
Read(mojo_base::mojom::GenericPendingAssociatedReceiverDataView data,
GenericPendingAssociatedReceiver* out) {
base::StringPiece interface_name;
if (!data.ReadInterfaceName(&interface_name))
return false;
*out = GenericPendingAssociatedReceiver(
interface_name, data.TakeReceiver<ScopedInterfaceEndpointHandle>());
return true;
}
} // namespace mojo

@ -0,0 +1,42 @@
// Copyright 2021 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_ASSOCIATED_RECEIVER_MOJOM_TRAITS_H_
#define MOJO_PUBLIC_CPP_BASE_GENERIC_PENDING_ASSOCIATED_RECEIVER_MOJOM_TRAITS_H_
#include "base/component_export.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
#include "mojo/public/mojom/base/generic_pending_associated_receiver.mojom-shared.h"
namespace mojo {
template <>
struct COMPONENT_EXPORT(MOJO_BASE_SHARED_TRAITS)
StructTraits<mojo_base::mojom::GenericPendingAssociatedReceiverDataView,
GenericPendingAssociatedReceiver> {
static bool IsNull(const GenericPendingAssociatedReceiver& receiver);
static void SetToNull(GenericPendingAssociatedReceiver* receiver);
static base::StringPiece interface_name(
const GenericPendingAssociatedReceiver& receiver) {
DCHECK(receiver.interface_name().has_value());
return receiver.interface_name().value();
}
static mojo::ScopedInterfaceEndpointHandle receiver(
GenericPendingAssociatedReceiver& receiver) {
return receiver.PassHandle();
}
static bool Read(
mojo_base::mojom::GenericPendingAssociatedReceiverDataView data,
GenericPendingAssociatedReceiver* out);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BASE_GENERIC_PENDING_ASSOCIATED_RECEIVER_MOJOM_TRAITS_H_

@ -138,6 +138,8 @@ component("bindings") {
"callback_helpers.h",
"connection_error_callback.h",
"connector.h",
"generic_pending_associated_receiver.cc",
"generic_pending_associated_receiver.h",
"generic_pending_receiver.cc",
"generic_pending_receiver.h",
"interface_endpoint_client.h",

@ -0,0 +1,51 @@
// Copyright 2021 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_associated_receiver.h"
#include <utility>
#include "base/check.h"
#include "base/strings/string_piece.h"
namespace mojo {
GenericPendingAssociatedReceiver::GenericPendingAssociatedReceiver() = default;
GenericPendingAssociatedReceiver::GenericPendingAssociatedReceiver(
base::StringPiece interface_name,
mojo::ScopedInterfaceEndpointHandle handle)
: interface_name_(std::string(interface_name)),
handle_(std::move(handle)) {}
GenericPendingAssociatedReceiver::GenericPendingAssociatedReceiver(
GenericPendingAssociatedReceiver&&) = default;
GenericPendingAssociatedReceiver& GenericPendingAssociatedReceiver::operator=(
GenericPendingAssociatedReceiver&&) = default;
GenericPendingAssociatedReceiver::~GenericPendingAssociatedReceiver() = default;
void GenericPendingAssociatedReceiver::reset() {
interface_name_.reset();
handle_.reset();
}
mojo::ScopedInterfaceEndpointHandle
GenericPendingAssociatedReceiver::PassHandle() {
DCHECK(is_valid());
interface_name_.reset();
return std::move(handle_);
}
mojo::ScopedInterfaceEndpointHandle
GenericPendingAssociatedReceiver::PassHandleIfNameIs(
const char* interface_name) {
DCHECK(is_valid());
if (interface_name_ == interface_name)
return PassHandle();
return mojo::ScopedInterfaceEndpointHandle();
}
} // namespace mojo

@ -0,0 +1,82 @@
// Copyright 2021 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_ASSOCIATED_RECEIVER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_GENERIC_PENDING_ASSOCIATED_RECEIVER_H_
#include <string>
#include "base/component_export.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace mojo {
// GenericPendingAssociatedReceiver encapsulates a pairing of a receiving
// associated interface endponit 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.GenericAssociatedPendingReceiver|, and it serves as a
// semi-safe wrapper for transporting arbitrary associated interface receivers
// in a generic object.
//
// It is intended to be used in the (relatively rare) scenario where an
// interface needs to support sharing its message ordering with interfaces
// defined at higher application layers, such that knowledge of those associated
// interface(s) would constitute a layering violation.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) GenericPendingAssociatedReceiver {
public:
GenericPendingAssociatedReceiver();
GenericPendingAssociatedReceiver(base::StringPiece interface_name,
mojo::ScopedInterfaceEndpointHandle handle);
template <typename Interface>
GenericPendingAssociatedReceiver(
mojo::PendingAssociatedReceiver<Interface> receiver)
: GenericPendingAssociatedReceiver(Interface::Name_,
receiver.PassHandle()) {}
GenericPendingAssociatedReceiver(const GenericPendingAssociatedReceiver&) =
delete;
GenericPendingAssociatedReceiver(GenericPendingAssociatedReceiver&&);
GenericPendingAssociatedReceiver& operator=(
const GenericPendingAssociatedReceiver&) = delete;
GenericPendingAssociatedReceiver& operator=(
GenericPendingAssociatedReceiver&&);
~GenericPendingAssociatedReceiver();
bool is_valid() const { return handle_.is_valid(); }
explicit operator bool() const { return is_valid(); }
void reset();
const absl::optional<std::string>& interface_name() const {
return interface_name_;
}
// Takes ownership of the endpoint, invalidating this object.
mojo::ScopedInterfaceEndpointHandle PassHandle();
// Takes ownership of the endpoint, strongly typed as an `Interface` receiver,
// if and only if that interface's name matches the stored interface name.
template <typename Interface>
mojo::PendingAssociatedReceiver<Interface> As() {
return mojo::PendingAssociatedReceiver<Interface>(
PassHandleIfNameIs(Interface::Name_));
}
private:
mojo::ScopedInterfaceEndpointHandle PassHandleIfNameIs(
const char* interface_name);
absl::optional<std::string> interface_name_;
mojo::ScopedInterfaceEndpointHandle handle_;
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_GENERIC_PENDING_ASSOCIATED_RECEIVER_H_

@ -131,6 +131,24 @@ struct Serializer<AssociatedInterfaceRequestDataView<Base>,
}
};
template <typename T>
struct Serializer<AssociatedInterfaceRequestDataView<T>,
ScopedInterfaceEndpointHandle> {
static void Serialize(ScopedInterfaceEndpointHandle& input,
AssociatedEndpointHandle_Data* output,
Message* message) {
DCHECK(!input.is_valid() || input.pending_association());
SerializeAssociatedEndpoint(std::move(input), *message, *output);
}
static bool Deserialize(AssociatedEndpointHandle_Data* input,
ScopedInterfaceEndpointHandle* output,
Message* message) {
*output = DeserializeAssociatedEndpointHandle(*input, *message);
return true;
}
};
template <typename Base, typename T>
struct Serializer<InterfacePtrDataView<Base>, InterfacePtr<T>> {
static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");

@ -16,6 +16,7 @@
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/threading/thread.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@ -584,6 +585,26 @@ TEST_P(ReceiverTest, GenericPendingReceiver) {
EXPECT_FALSE(receiver.is_valid());
}
TEST_P(ReceiverTest, GenericPendingAssociatedReceiver) {
AssociatedRemote<sample::Service> remote;
GenericPendingAssociatedReceiver receiver;
EXPECT_FALSE(receiver.is_valid());
EXPECT_FALSE(receiver.interface_name().has_value());
receiver =
GenericPendingAssociatedReceiver(remote.BindNewEndpointAndPassReceiver());
ASSERT_TRUE(receiver.is_valid());
EXPECT_EQ(sample::Service::Name_, receiver.interface_name());
auto ping_receiver = receiver.As<test::PingService>();
EXPECT_FALSE(ping_receiver.is_valid());
EXPECT_TRUE(receiver.is_valid());
auto sample_receiver = receiver.As<sample::Service>();
EXPECT_TRUE(sample_receiver.is_valid());
EXPECT_FALSE(receiver.is_valid());
}
class RebindTestImpl : public mojom::RebindTestInterface {
public:
explicit RebindTestImpl(base::WaitableEvent* event) : event_(event) {
@ -660,6 +681,13 @@ class TestGenericBinderImpl : public mojom::TestGenericBinder {
wait_loop_->Run();
}
void WaitForNextAssociatedReceiver(
GenericPendingAssociatedReceiver* storage) {
wait_loop_.emplace();
next_associated_receiver_storage_ = storage;
wait_loop_->Run();
}
// mojom::TestGenericBinder:
void BindOptionalReceiver(GenericPendingReceiver receiver) override {
if (next_receiver_storage_) {
@ -679,6 +707,26 @@ class TestGenericBinderImpl : public mojom::TestGenericBinder {
wait_loop_->Quit();
}
void BindOptionalAssociatedReceiver(
GenericPendingAssociatedReceiver receiver) override {
if (next_associated_receiver_storage_) {
*next_associated_receiver_storage_ = std::move(receiver);
next_associated_receiver_storage_ = nullptr;
}
if (wait_loop_)
wait_loop_->Quit();
}
void BindAssociatedReceiver(
GenericPendingAssociatedReceiver receiver) override {
if (next_associated_receiver_storage_) {
*next_associated_receiver_storage_ = std::move(receiver);
next_associated_receiver_storage_ = nullptr;
}
if (wait_loop_)
wait_loop_->Quit();
}
private:
void OnDisconnect() {
if (wait_loop_)
@ -690,6 +738,7 @@ class TestGenericBinderImpl : public mojom::TestGenericBinder {
bool connected_ = true;
absl::optional<base::RunLoop> wait_loop_;
GenericPendingReceiver* next_receiver_storage_ = nullptr;
GenericPendingAssociatedReceiver* next_associated_receiver_storage_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestGenericBinderImpl);
};
@ -711,12 +760,14 @@ TEST_P(ReceiverSerializationTest, NullGenericPendingReceiver) {
mojo::Remote<mojom::TestInterface1>().BindNewPipeAndPassReceiver());
binder.WaitForNextReceiver(&receiver);
EXPECT_TRUE(receiver.is_valid());
EXPECT_FALSE(receiver.As<mojom::TestInterface2>());
EXPECT_TRUE(receiver.As<mojom::TestInterface1>());
remote->BindReceiver(
mojo::Remote<mojom::TestInterface2>().BindNewPipeAndPassReceiver());
binder.WaitForNextReceiver(&receiver);
EXPECT_TRUE(receiver.is_valid());
EXPECT_FALSE(receiver.As<mojom::TestInterface1>());
EXPECT_TRUE(receiver.As<mojom::TestInterface2>());
mojo::internal::SerializationWarningObserverForTesting observer;
@ -745,6 +796,58 @@ TEST_P(ReceiverSerializationTest, NullGenericPendingReceiver) {
EXPECT_FALSE(binder.connected());
}
TEST_P(ReceiverSerializationTest, NullGenericPendingAssociatedReceiver) {
Remote<mojom::TestGenericBinder> remote;
TestGenericBinderImpl binder(remote.BindNewPipeAndPassReceiver());
// Bind a null, nullable associated receiver.
remote->BindOptionalAssociatedReceiver(GenericPendingAssociatedReceiver());
GenericPendingAssociatedReceiver receiver;
binder.WaitForNextAssociatedReceiver(&receiver);
EXPECT_FALSE(receiver.is_valid());
// Bind some valid non-null, non-nullable associated receivers.
remote->BindAssociatedReceiver(mojo::AssociatedRemote<mojom::TestInterface1>()
.BindNewEndpointAndPassReceiver());
binder.WaitForNextAssociatedReceiver(&receiver);
EXPECT_TRUE(receiver.is_valid());
EXPECT_FALSE(receiver.As<mojom::TestInterface2>());
EXPECT_TRUE(receiver.As<mojom::TestInterface1>());
remote->BindAssociatedReceiver(mojo::AssociatedRemote<mojom::TestInterface2>()
.BindNewEndpointAndPassReceiver());
binder.WaitForNextAssociatedReceiver(&receiver);
EXPECT_TRUE(receiver.is_valid());
EXPECT_FALSE(receiver.As<mojom::TestInterface1>());
EXPECT_TRUE(receiver.As<mojom::TestInterface2>());
mojo::internal::SerializationWarningObserverForTesting observer;
// Now attempt to send a null associated receiver for a non-nullable argument.
EXPECT_TRUE(binder.connected());
remote->BindAssociatedReceiver(GenericPendingAssociatedReceiver());
// We should see a validation warning at serialization time. Normally this
// results in a DCHECK, but it's suppressed by the testing observer we have on
// the stack. Note that this only works for DCHECK-enabled builds. For
// non-DCHECK-enabled builds, serialization will succeed above with no errors,
// but the receiver below will still reject the message and disconnect.
#if DCHECK_IS_ON()
EXPECT_EQ(mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
observer.last_warning());
#endif
// `receiver` should not be modified again by the implementation in `binder`,
// because the it must never receive the invalid request. Instead the Wait
// should be terminated by disconnection.
receiver = mojo::AssociatedRemote<mojom::TestInterface1>()
.BindNewEndpointAndPassReceiver();
binder.WaitForNextAssociatedReceiver(&receiver);
EXPECT_TRUE(receiver.is_valid());
EXPECT_TRUE(receiver.As<mojom::TestInterface1>());
EXPECT_FALSE(binder.connected());
}
using SelfOwnedReceiverTest = BindingsTestBase;
TEST_P(SelfOwnedReceiverTest, CloseDestroysImplAndPipe) {

@ -4,11 +4,16 @@
module mojo.test.receiver_unittest.mojom;
import "mojo/public/mojom/base/generic_pending_associated_receiver.mojom";
import "mojo/public/mojom/base/generic_pending_receiver.mojom";
interface TestGenericBinder {
BindOptionalReceiver(mojo_base.mojom.GenericPendingReceiver? receiver);
BindReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
BindOptionalAssociatedReceiver(
mojo_base.mojom.GenericPendingAssociatedReceiver? receiver);
BindAssociatedReceiver(
mojo_base.mojom.GenericPendingAssociatedReceiver receiver);
};
interface TestInterface1 {};

@ -16,6 +16,7 @@ mojom_component("base") {
"file_error.mojom",
"file_info.mojom",
"file_path.mojom",
"generic_pending_associated_receiver.mojom",
"generic_pending_receiver.mojom",
"memory_allocator_dump_cross_process_uid.mojom",
"memory_pressure_level.mojom",
@ -150,6 +151,21 @@ mojom_component("base") {
"//mojo/public/cpp/base:shared_typemap_traits",
]
},
{
types = [
{
mojom = "mojo_base.mojom.GenericPendingAssociatedReceiver"
cpp = "::mojo::GenericPendingAssociatedReceiver"
move_only = true
nullable_is_same_type = true
},
]
traits_headers = [ "//mojo/public/cpp/base/generic_pending_associated_receiver_mojom_traits.h" ]
traits_public_deps = [
"//mojo/public/cpp/base:shared_typemap_traits",
"//mojo/public/cpp/bindings",
]
},
{
types = [
{

@ -0,0 +1,25 @@
// Copyright 2021 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 associated interface
// 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 asspcoated receivers that cannot or should not be known at
// compile time.
struct GenericPendingAssociatedReceiver {
// The name of the interface which defines the messages to be received by
// `receiver`.
string interface_name;
// A generic associated interface receiver which is actually expected to
// receive messages defined by the interface named by `interface_name` above.
pending_associated_receiver<GenericAssociatedInterface> receiver;
};
// A generic placeholder interface for the associated endpoint to be passed by a
// GenericPendingAssociatedReceiver.
interface GenericAssociatedInterface {};