Commit efcb6efe authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Introduce new Mojo bindings types

Introduces new bindings types described by
https://docs.google.com/document/d/18pOsJOTuLVH-V7s9xwR-em9t-R4icyPjk7i8I8bfjjg/edit

In summary:

- Remote will replace InterfacePtr
- PendingRemote will replace InterfacePtrInfo
- PendingReceiver will replace InterfaceRequest
- Receiver will replace Binding
- mojom IDL will use pending_remote<T> and pending_receiver<T>
  to replace T and T& syntax.

See the lone unittest.cc and .test-mojom files in this patch for working
example usage.

Bug: 875030
Change-Id: Id4b76599d4d6973661c80fa438b37b14d4b1e93a
Reviewed-on: https://chromium-review.googlesource.com/c/1474844
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarAlbert J. Wong <ajwong@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarOksana Zhuravlova <oksamyt@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636616}
parent 1732cc5b
......@@ -120,6 +120,8 @@ component("bindings") {
"associated_interface_request.h",
"binding.h",
"binding_set.h",
"call_internal.cc",
"call_internal.h",
"callback_helpers.h",
"connection_error_callback.h",
"connector.h",
......@@ -162,10 +164,14 @@ component("bindings") {
"lib/task_runner_helper.cc",
"lib/task_runner_helper.h",
"native_enum.h",
"pending_receiver.h",
"pending_remote.h",
"pipe_control_message_handler.h",
"pipe_control_message_handler_delegate.h",
"pipe_control_message_proxy.h",
"raw_ptr_impl_ref_traits.h",
"receiver.h",
"remote.h",
"sequence_local_sync_event_watcher.h",
"strong_associated_binding.h",
"strong_binding.h",
......
// 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/call_internal.h"
namespace mojo {
namespace internal {
CallProxyWrapperBase::CallProxyWrapperBase(void* proxy) : proxy_(proxy) {}
CallProxyWrapperBase::CallProxyWrapperBase(CallProxyWrapperBase&& other)
: proxy_(other.proxy_) {}
CallProxyWrapperBase::~CallProxyWrapperBase() = default;
} // namespace internal
} // namespace mojo
// 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_CALL_INTERNAL_H_
#define MOJO_PUBLIC_CPP_BINDINGS_CALL_INTERNAL_H_
#include <utility>
#include "base/component_export.h"
#include "base/macros.h"
namespace mojo {
namespace internal {
// Base class for temporary objects returned by |Remote<T>::rpc()|. This
// provides pass-through access to the passed calling proxy.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) CallProxyWrapperBase {
public:
explicit CallProxyWrapperBase(void* proxy);
CallProxyWrapperBase(CallProxyWrapperBase&& other);
~CallProxyWrapperBase();
void* proxy() const { return proxy_; }
private:
void* proxy_;
DISALLOW_COPY_AND_ASSIGN(CallProxyWrapperBase);
};
template <typename T>
class CallProxyWrapper : public CallProxyWrapperBase {
public:
explicit CallProxyWrapper(T* proxy) : CallProxyWrapperBase(proxy) {}
CallProxyWrapper(CallProxyWrapper&& other)
: CallProxyWrapperBase(std::move(other)) {}
T* operator->() const { return static_cast<T*>(proxy()); }
private:
DISALLOW_COPY_AND_ASSIGN(CallProxyWrapper);
};
} // namespace internal
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_CALL_INTERNAL_H_
......@@ -14,6 +14,7 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
......@@ -115,6 +116,12 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient
void FlushForTesting();
void FlushAsyncForTesting(base::OnceClosure callback);
#if DCHECK_IS_ON()
void SetNextCallLocation(const base::Location& location) {
next_call_location_ = location;
}
#endif
private:
// Maps from the id of a response to the MessageReceiver that handles the
// response.
......@@ -184,6 +191,13 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient
internal::ControlMessageProxy control_message_proxy_;
internal::ControlMessageHandler control_message_handler_;
#if DCHECK_IS_ON()
// The code location of the the most recent call into a method on this
// interface endpoint. This is set *after* the call but *before* any message
// is actually transmitted for it.
base::Location next_call_location_;
#endif
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_;
......
......@@ -239,6 +239,11 @@ bool InterfaceEndpointClient::Accept(Message* message) {
InitControllerIfNecessary();
#if DCHECK_IS_ON()
// TODO(https://crbug.com/695289): Send |next_call_location_| in a control
// message before calling |SendMessage()| below.
#endif
return controller_->SendMessage(message);
}
......@@ -265,6 +270,11 @@ bool InterfaceEndpointClient::AcceptWithResponder(
message->set_request_id(request_id);
#if DCHECK_IS_ON()
// TODO(https://crbug.com/695289): Send |next_call_location_| in a control
// message before calling |SendMessage()| below.
#endif
bool is_sync = message->has_flag(Message::kFlagIsSync);
if (!controller_->SendMessage(message))
return false;
......
......@@ -15,6 +15,7 @@
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/component_export.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
......@@ -59,6 +60,12 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfacePtrStateBase {
return endpoint_client_ && endpoint_client_->has_pending_responders();
}
#if DCHECK_IS_ON()
void SetNextCallLocation(const base::Location& location) {
endpoint_client_->SetNextCallLocation(location);
}
#endif
protected:
InterfaceEndpointClient* endpoint_client() const {
return endpoint_client_.get();
......@@ -116,6 +123,13 @@ class InterfacePtrState : public InterfacePtrStateBase {
return proxy_.get();
}
void SetNextCallLocation(const base::Location& location) {
#if DCHECK_IS_ON()
ConfigureProxyIfNecessary();
InterfacePtrStateBase::SetNextCallLocation(location);
#endif
}
void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
ConfigureProxyIfNecessary();
InterfacePtrStateBase::QueryVersion(callback);
......
......@@ -16,6 +16,8 @@
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_context.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/message_pipe.h"
......@@ -114,6 +116,26 @@ struct Serializer<InterfacePtrDataView<Base>, InterfacePtrInfo<T>> {
}
};
template <typename Base, typename T>
struct Serializer<InterfacePtrDataView<Base>, PendingRemote<T>> {
static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
static void Serialize(PendingRemote<T>& input,
Interface_Data* output,
SerializationContext* context) {
context->AddInterfaceInfo(input.TakePipe(), input.version(), output);
}
static bool Deserialize(Interface_Data* input,
PendingRemote<T>* output,
SerializationContext* context) {
*output = PendingRemote<T>(
context->TakeHandleAs<mojo::MessagePipeHandle>(input->handle),
input->version);
return true;
}
};
template <typename Base, typename T>
struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> {
static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
......@@ -133,6 +155,25 @@ struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> {
}
};
template <typename Base, typename T>
struct Serializer<InterfaceRequestDataView<Base>, PendingReceiver<T>> {
static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
static void Serialize(PendingReceiver<T>& input,
Handle_Data* output,
SerializationContext* context) {
context->AddHandle(ScopedHandle::From(input.TakePipe()), output);
}
static bool Deserialize(Handle_Data* input,
PendingReceiver<T>* output,
SerializationContext* context) {
*output =
PendingReceiver<T>(context->TakeHandleAs<MessagePipeHandle>(*input));
return true;
}
};
} // namespace internal
} // namespace mojo
......
// 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_PENDING_RECEIVER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_PENDING_RECEIVER_H_
#include <utility>
#include "base/macros.h"
#include "mojo/public/cpp/system/message_pipe.h"
namespace mojo {
// A PendingReceiver receives and accumulates a queue of incoming Interface
// method calls made by a single corresponding Remote. PendingReceiver instances
// may be freely moved to another thread/sequence, or even transferred to
// another process via a Mojo interface call (see pending_receiver<T> syntax in
// mojom IDL).
//
// This object should eventually be consumed to bind a Receiver, which will then
// begin dispatching any queued and future incoming method calls to a local
// implementation of Interface. See Receiver documentation for more details.
//
// NOTE: This object is essentially semantic sugar wrapping a message pipe
// handle that is expected to receive Interface messages from a Remote. As such,
// consumers who know what they're doing (i.e. who are confident about what lies
// on the other end of a pipe) may freely convert between a PendingReceiver and
// a raw message pipe handle.
template <typename Interface>
class PendingReceiver {
public:
// Constructs an invalid PendingReceiver. This object is not entangled with
// any Remote and cannot be used to bind a Receiver.
//
// A valid PendingReceiver is commonly obtained by calling
// |Remote::BindNewReceiver()| on an existing unbound Remote instance or less
// commonly by calling calling |PendingRemote::MakeReceiver()| on an existing
// but invalid PendingRemote instance.
PendingReceiver() = default;
PendingReceiver(PendingReceiver&&) noexcept = default;
// Constructs a valid PendingReceiver from a valid raw message pipe handle.
explicit PendingReceiver(ScopedMessagePipeHandle pipe)
: pipe_(std::move(pipe)) {
DCHECK(pipe_.is_valid());
}
~PendingReceiver() = default;
PendingReceiver& operator=(PendingReceiver&&) noexcept = default;
// 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(); }
// 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(); }
// Takes ownership of this PendingReceiver's message pipe handle. After this
// call, the PendingReceiver is no longer in a valid state and can no longer
// be used to bind a Receiver.
ScopedMessagePipeHandle TakePipe() WARN_UNUSED_RESULT {
return std::move(pipe_);
}
private:
ScopedMessagePipeHandle pipe_;
DISALLOW_COPY_AND_ASSIGN(PendingReceiver);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_PENDING_RECEIVER_H_
// 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_PENDING_REMOTE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_PENDING_REMOTE_H_
#include <cstdint>
#include <utility>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
namespace mojo {
// A valid PendingRemote is entangled with exactly one Receiver or
// PendingReceiver, and can be consumed to bind a Remote in order to begin
// issuing method calls to that receiver. See Remote documentation for more
// details.
//
// PendingRemote instances may be freely moved to another thread/sequence, or
// even transferred to another process via a Mojo interface call (see
// pending_remote<T> syntax in mojom IDL).
//
// NOTE: This object is essentially semantic sugar wrapping a raw message pipe
// handle that is expected to send Interface messages of a specified version
// (typically 0) to a Receiver. As such, consumers who know what they're doing
// (i.e. who are confident about what lies on the other side of a pipe) may
// freely convert between a PendingRemote and a 2-tuple of
// [raw message pipe handle, expected interface version number].
template <typename Interface>
class PendingRemote {
public:
// Constructs an invalid PendingRemote. This object is not entangled with any
// receiver and cannot be used to bind a Remote.
//
// A valid PendingRemote is typically obtained by calling
// |Receiver::BindNewRemote()| on an existing unbound Receiver instance.
//
// To simultaneously create a valid PendingRemote and an entangled
// PendingReceiver for rarer cases where both objects need to be passed
// elsewhere, use the |MakeReceiver()| method defined below.
PendingRemote() = default;
PendingRemote(PendingRemote&&) noexcept = default;
// 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) {
DCHECK(pipe_.is_valid());
}
~PendingRemote() = default;
PendingRemote& operator=(PendingRemote&&) noexcept = default;
// 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(); }
// 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;
}
// 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 TakePipe() WARN_UNUSED_RESULT {
version_ = 0;
return std::move(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_; }
// Creates a new PendingReceiver and entangles this PendingRemote with it.
// May only be called on an invalid PendingRemote.
PendingReceiver<Interface> MakeReceiver() WARN_UNUSED_RESULT {
DCHECK(!is_valid()) << "PendingRemote already has a receiver";
MessagePipe pipe;
pipe_ = std::move(pipe.handle0);
return PendingReceiver<Interface>(std::move(pipe.handle1));
}
private:
ScopedMessagePipeHandle pipe_;
uint32_t version_ = 0;
DISALLOW_COPY_AND_ASSIGN(PendingRemote);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_PENDING_REMOTE_H_
// 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_RECEIVER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_RECEIVER_H_
#include <utility>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.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"
#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
namespace mojo {
// A Receiver is used to receive and dispatch Interface method calls to a local
// implementation of Interface. Every Receiver object is permanently linked to
// an implementation of Interface at construction time. The Receiver begins
// receiving and scheduling method calls to the implementation once it becomes
// bound by consuming a PendingReceiver, either at construction time or by
// calling |Bind()|.
//
// Receiver is NOT thread- or sequence- safe and must be used from a single
// (but otherwise arbitrary) sequence. All bound Receiver objects are associated
// with a base::SequencedTaskRunner which the Receiver uses exclusively to
// schedule incoming method calls and disconnection notifications.
//
// IMPORTANT: In the name of memory safety, Interface method calls and
// disconnection notifications scheduled by a Receiver object will NEVER run
// beyond the lifetime of the Receiver object.
template <typename Interface,
typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
class Receiver {
public:
// Typically (and by default) a Receiver uses a raw pointer to reference its
// linked Interface implementation object, because typically that
// implementation object owns the Receiver. An alternative |ImplRefTraits| may
// be provided as a second Receiver template argument in order to use a
// different reference type.
using ImplPointerType = typename ImplRefTraits::PointerType;
// Constructs an unbound Receiver linked to |impl| for the duration of the
// Receive's lifetime. The Receiver can be bound later by calling |Bind()| or
// |BindNewRemote()|. An unbound Receiver does not schedule any asynchronous
// tasks.
explicit Receiver(ImplPointerType impl) : internal_state_(std::move(impl)) {}
// Constructs a bound Receiver by consuming |pending_receiver|. The Receiver
// is permanently linked to |impl| and will schedule incoming |impl| method
// and disconnection notifications on the default SequencedTaskRunner (i.e.
// base::SequencedTaskRunnerHandle::Get() at construction time).
Receiver(ImplPointerType impl, PendingReceiver<Interface> pending_receiver)
: Receiver(std::move(impl), std::move(pending_receiver), nullptr) {}
// Similar to above but the constructed Receiver schedules all tasks via
// |task_runner| instead of the default SequencedTaskRunner. |task_runner|
// must run tasks on the same sequence that owns this Receiver.
Receiver(ImplPointerType impl,
PendingReceiver<Interface> pending_receiver,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: internal_state_(std::move(impl)) {
Bind(std::move(pending_receiver), std::move(task_runner));
}
~Receiver() = default;
// Indicates whether this Receiver is bound, meaning it may continue to
// receive Interface method calls from a remote caller.
//
// NOTE: A Receiver is NEVER passively unbound. The only way for it to become
// unbound is to explicitly call |reset()| or |Unbind()|.
bool is_bound() const { return internal_state_.is_bound(); }
// Sets a OnceClosure to be invoked if this Receiver is cut off from its
// Remote (or PendingRemote). This can happen if the corresponding Remote (or
// unconsumed PendingRemote) has been destroyed, or if the Remote sends a
// malformed message. Must only be called on a bound Receiver object, and only
// remains set as long as the Receiver is both bound and connected.
//
// If ever invoked, |handler| will be scheduled asynchronously on the
// Receiver's bound SequencedTaskRunner.
void set_disconnect_handler(base::OnceClosure handler) {
internal_state_.set_connection_error_handler(std::move(handler));
}
// Resets this Receiver to an unbound state. An unbound Receiver will NEVER
// schedule method calls or disconnection notifications, and any pending tasks
// which were scheduled prior to unbinding are effectively cancelled.
void reset() { internal_state_.Close(); }
// Binds this Receiver, connecting it to a new PendingRemote which is
// returned for transmission elsewhere (typically to a Remote who will consume
// it to start making calls).
//
// The Receiver will schedule incoming |impl| method calls and disconnection
// notifications on the default SequencedTaskRunner (i.e.
// base::SequencedTaskRunnerHandle::Get() at the time of this call). Must only
// be called on an unbound Receiver.
PendingRemote<Interface> BindNewRemote() WARN_UNUSED_RESULT {
return BindNewRemote(nullptr);
}
// Like above, but the Receiver will schedule incoming |impl| method calls and
// disconnection notifications on |task_runner| rather than on the default
// SequencedTaskRunner. Must only be called on an unbound Receiver.
// |task_runner| must run tasks on the same sequence that owns this Receiver.
PendingRemote<Interface> BindNewRemote(
scoped_refptr<base::SequencedTaskRunner> task_runner) WARN_UNUSED_RESULT {
DCHECK(!is_bound()) << "Receiver is already bound";
PendingRemote<Interface> remote;
Bind(remote.MakeReceiver(), std::move(task_runner));
return remote;
}
// Binds this Receiver by consuming |pending_receiver|, which must be valid.
// Must only be called on an unbound Receiver.
//
// The newly bound Receiver will schedule incoming |impl| method calls and
// disconnection notifications on the default SequencedTaskRunner (i.e.
// base::SequencedTaskRunnerHandle::Get() at the time of this call).
void Bind(PendingReceiver<Interface> pending_receiver) {
DCHECK(pending_receiver.is_valid());
Bind(std::move(pending_receiver), nullptr);
}
// Like above, but the newly bound Receiver will schedule incoming |impl|
// method calls and disconnection notifications on |task_runner| instead of
// the default SequencedTaskRunner. Must only be called on an unbound
// Receiver. |task_runner| must run tasks on the same sequence that owns this
// Receiver.
void Bind(PendingReceiver<Interface> pending_receiver,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
internal_state_.Bind(pending_receiver.TakePipe(), std::move(task_runner));
}
// Unbinds this Receiver, preventing any further |impl| method calls or
// disconnection notifications from being scheduled by it. Any such tasks that
// were scheduled prior to unbinding are effectively cancelled.
//
// Returns a PendingReceiver which remains connected to this receiver's
// Remote and which may be transferred elsewhere and consumed by another
// Receiver. Any messages received but not actually dispatched by this
// Receiver remain intact within the returned PendingReceiver and can be
// dispatched by whomever binds with it later.
//
// Note that a Receiver should not be unbound while there are still living
// response callbacks that haven't been invoked, as once the Receiver is
// unbound those response callbacks are no longer valid and the Remote will
// never be able to receive its expected responses.
PendingReceiver<Interface> Unbind() {
CHECK(!internal_state_.HasAssociatedInterfaces());
return PendingReceiver<Interface>(
internal_state_.Unbind().PassMessagePipe());
}
private:
internal::BindingState<Interface, ImplRefTraits> internal_state_;
DISALLOW_COPY_AND_ASSIGN(Receiver);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_RECEIVER_H_
This diff is collapsed.
......@@ -30,6 +30,7 @@ source_set("tests") {
"message_queue.h",
"multiplex_router_unittest.cc",
"native_struct_unittest.cc",
"new_endpoint_types_unittest.cc",
"report_bad_message_unittest.cc",
"request_response_unittest.cc",
"router_test_util.cc",
......@@ -54,6 +55,7 @@ source_set("tests") {
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
"//mojo/public/cpp/test_support:test_utils",
"//mojo/public/interfaces/bindings/tests:other_test_interfaces",
"//mojo/public/interfaces/bindings/tests:test_associated_interfaces",
"//mojo/public/interfaces/bindings/tests:test_export_component",
"//mojo/public/interfaces/bindings/tests:test_export_component2",
......
// 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 <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/interfaces/bindings/tests/new_endpoint_types.test-mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace new_endpoint_types {
class FactoryImpl;
class WidgetImpl : public mojom::Widget {
public:
WidgetImpl(FactoryImpl* factory,
mojo::PendingReceiver<mojom::Widget> receiver,
mojo::PendingRemote<mojom::WidgetClient> client)
: factory_(factory),
receiver_(this, std::move(receiver)),
client_(std::move(client)) {
client_.rpc(FROM_HERE)->OnInitialized();
receiver_.set_disconnect_handler(
base::BindOnce(&WidgetImpl::OnDisconnect, base::Unretained(this)));
}
~WidgetImpl() override = default;
// mojom::Widget:
void Click() override {
for (auto& observer : observers_)
observer.rpc(FROM_HERE)->OnClick();
}
void AddObserver(
mojo::PendingRemote<mojom::WidgetObserver> observer) override {
observers_.emplace_back(std::move(observer));
}
private:
void OnDisconnect();
FactoryImpl* const factory_;
mojo::Receiver<mojom::Widget> receiver_;
mojo::Remote<mojom::WidgetClient> client_;
std::vector<mojo::Remote<mojom::WidgetObserver>> observers_;
DISALLOW_COPY_AND_ASSIGN(WidgetImpl);
};
class FactoryImpl : public mojom::WidgetFactory {
public:
explicit FactoryImpl(mojo::PendingReceiver<mojom::WidgetFactory> receiver)
: receiver_(this, std::move(receiver)) {}
~FactoryImpl() override = default;
// mojom::WidgetFactory:
void CreateWidget(mojo::PendingReceiver<mojom::Widget> receiver,
mojo::PendingRemote<mojom::WidgetClient> client) override {
widgets_.push_back(std::make_unique<WidgetImpl>(this, std::move(receiver),
std::move(client)));
}
void DestroyWidget(WidgetImpl* widget) {
for (auto it = widgets_.begin(); it != widgets_.end(); ++it) {
if (it->get() == widget) {
widgets_.erase(it);
return;
}
}
}
private:
mojo::Receiver<mojom::WidgetFactory> receiver_;
std::vector<std::unique_ptr<WidgetImpl>> widgets_;
DISALLOW_COPY_AND_ASSIGN(FactoryImpl);
};
void WidgetImpl::OnDisconnect() {
// Deletes |this|.
factory_->DestroyWidget(this);
}
class ClientImpl : public mojom::WidgetClient {
public:
ClientImpl() = default;
~ClientImpl() override = default;
mojo::PendingRemote<mojom::WidgetClient> BindNewRemote() {
return receiver_.BindNewRemote();
}
void WaitForInitialize() { wait_loop_.Run(); }
// mojom::WidgetClient:
void OnInitialized() override { wait_loop_.Quit(); }
private:
mojo::Receiver<mojom::WidgetClient> receiver_{this};
base::RunLoop wait_loop_;
DISALLOW_COPY_AND_ASSIGN(ClientImpl);
};
class ObserverImpl : public mojom::WidgetObserver {
public:
ObserverImpl() = default;
~ObserverImpl() override = default;
mojo::PendingRemote<mojom::WidgetObserver> BindNewRemote() {
auto remote = receiver_.BindNewRemote();
receiver_.set_disconnect_handler(
base::BindOnce(&ObserverImpl::OnDisconnect, base::Unretained(this)));
return remote;
}
void WaitForClick() { click_loop_.Run(); }
void WaitForDisconnect() { disconnect_loop_.Run(); }
// mojom::WidgetObserver:
void OnClick() override { click_loop_.Quit(); }
private:
void OnDisconnect() { disconnect_loop_.Quit(); }
mojo::Receiver<mojom::WidgetObserver> receiver_{this};
base::RunLoop click_loop_;
base::RunLoop disconnect_loop_;
DISALLOW_COPY_AND_ASSIGN(ObserverImpl);
};
TEST(NewEndpointTypesTest, BasicUsage) {
// A simple smoke/compile test for new bindings endpoint types. Used to
// demonstrate look & feel as well as to ensure basic completeness and
// correctness.
base::test::ScopedTaskEnvironment task_environment;
// A Remote<T> exposes a callable T interface which sends messages to a remote
// implementation of T. Here we create a new unbound Remote which will control
// a remote implementation of |mojom::WidgetFactory|.
mojo::Remote<mojom::WidgetFactory> factory;
EXPECT_FALSE(factory.is_bound());
// |factory_impl| is a concrete implementation of |mojom::WidgetFactory|. With
// Mojo interfaces, the implementation can live in the same process as the
// Remote<T> calling it, or it can live in another process. For simplicity in
// this test we have the implementation living in the test process.
//
// |BindNewReceiver()| creates a new message pipe to carry
// |mojom:WidgetFactory| interface messages. It binds one end to the
// |factory| Remote above, and the other end is passed to |factory_impl| so
// it can receive messages.
FactoryImpl factory_impl(factory.BindNewReceiver());
EXPECT_TRUE(factory.is_bound());
// Similar to above, we create another Remote. this time to control a
// |mojom::Widget| implementation somewhere.
mojo::Remote<mojom::Widget> widget;
// |client| is an implementation of |mojom::WidgetClient|. This is a common
// pattern for Mojo interfaces -- to have a Remote for some Foo interface
// living alongside a corresponding implementation of a FooClient interface.
// The pattern allows for two-way communication using separate but
// closely-related types of endpoints.
ClientImpl client;
// Here we send two message pipes to the remote factory. This |CreateWidget|
// call will be dispatched asynchronously to |factory_impl| via Mojo. Notice
// that, inline, we create a new |mojom::Widget| pipe as well as a new
// |mojom::WidgetClient| pipe. The Widget's Receiver endpoint is passed to
// the factory implementation, as is the WidgetClient's Remote endpoint.
// This allows the factory to bind and begin receiving Widget messages on
// one pipe, and to bind and begin sending WidgetClient messages on the other.
factory.rpc(FROM_HERE)->CreateWidget(widget.BindNewReceiver(),
client.BindNewRemote());
// Similar to |client| above, we create some implementations of
// |mojom::WidgetObserver| here to receive messages from Remote
// WidgetObserver caller on the factory implementation's side of the world.
ObserverImpl observer1, observer2;
// Similar to the |CreateWidget| call above, here we create new WidgetObserver
// pipes (one for each impl object) and pass their Remote ends to the remote
// Widget implementation to bind and use. This allows the remote Widget
// implementation to send messages to both |observer1| and |observer2|.
widget.rpc(FROM_HERE)->AddObserver(observer1.BindNewRemote());
widget.rpc(FROM_HERE)->AddObserver(observer2.BindNewRemote());
// When the FactoryImpl asynchronously receives our |CreateWidget| call, it
// will send back a |mojom::WidgetClient::Initialize()| message to our
// |client| object using the Remote passed to |CreateWidget|. This waits for
// that message.
client.WaitForInitialize();
// Send another message, this time to the remote Widget implementation.
widget.rpc(FROM_HERE)->Click();
// When the remote Widget implementation receives a |Click()| message, it
// broadcasts a |mojom::WidgetObserver::OnClick()| event to all registered
// WidgetObservers on the Widget. We wait for each of our observers to
// receive that message here.
observer1.WaitForClick();
observer2.WaitForClick();
// Remotes (and Receivers, for that matter) remain bound until explicitly
// unbound by their owner.
widget.reset();
EXPECT_FALSE(widget.is_bound());
// Resetting the Remote<Widget> above eventually triggers the remote Widget
// implementation's disconnection handler. That handler in turn tears down
// the Widget implementation, including the Remote<WidgetObserver> endpoints
// it owns. This in turn will eventually trigger our local WidgetObserver
// instances' disconnection handlers. We wait for that to happen here.
observer1.WaitForDisconnect();
observer2.WaitForDisconnect();
}
} // namespace new_endpoint_types
} // namespace test
} // namespace mojo
......@@ -484,3 +484,12 @@ mojom("echo") {
"echo_import/echo_import.mojom",
]
}
# These could probably be merged with "test_interfaces" at some point.
mojom("other_test_interfaces") {
testonly = true
cpp_only = true
sources = [
"new_endpoint_types.test-mojom",
]
}
// 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.test.new_endpoint_types.mojom;
interface WidgetObserver {
OnClick();
};
interface Widget {
Click();
AddObserver(pending_remote<WidgetObserver> observer);
};
interface WidgetClient {
OnInitialized();
};
interface WidgetFactory {
CreateWidget(pending_receiver<Widget> receiver,
pending_remote<WidgetClient> client);
};
......@@ -2,6 +2,8 @@
class {{export_attribute}} {{interface.name}}Proxy
: public {{interface.name}} {
public:
using InterfaceType = {{interface.name}};
explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
{%- for method in interface.methods %}
......
......@@ -42,6 +42,8 @@ namespace {{variant}} {
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
{%- if for_blink %}
......
......@@ -224,7 +224,9 @@ class Generator(generator.Generator):
mojom.IsAnyHandleKind(kind) or
mojom.IsInterfaceKind(kind) or
mojom.IsInterfaceRequestKind(kind) or
mojom.IsAssociatedKind(kind)):
mojom.IsAssociatedKind(kind) or
mojom.IsPendingRemoteKind(kind) or
mojom.IsPendingReceiverKind(kind)):
pass
elif mojom.IsArrayKind(kind):
AddKind(kind.kind)
......@@ -585,6 +587,12 @@ class Generator(generator.Generator):
if mojom.IsInterfaceRequestKind(kind):
return "%sRequest" % self._GetNameForKind(
kind.kind, add_same_module_namespaces=add_same_module_namespaces)
if mojom.IsPendingRemoteKind(kind):
return "mojo::PendingRemote<%s>" % self._GetNameForKind(
kind.kind, add_same_module_namespaces=add_same_module_namespaces)
if mojom.IsPendingReceiverKind(kind):
return "mojo::PendingReceiver<%s>" % self._GetNameForKind(
kind.kind, add_same_module_namespaces=add_same_module_namespaces)
if mojom.IsAssociatedInterfaceKind(kind):
return "%sAssociatedPtrInfo" % self._GetNameForKind(
kind.kind, add_same_module_namespaces=add_same_module_namespaces)
......@@ -671,9 +679,9 @@ class Generator(generator.Generator):
return ("mojo::internal::Pointer<mojo::internal::Map_Data<%s, %s>>" %
(self._GetCppFieldType(kind.key_kind),
self._GetCppFieldType(kind.value_kind)))
if mojom.IsInterfaceKind(kind):
if mojom.IsInterfaceKind(kind) or mojom.IsPendingRemoteKind(kind):
return "mojo::internal::Interface_Data"
if mojom.IsInterfaceRequestKind(kind):
if mojom.IsInterfaceRequestKind(kind) or mojom.IsPendingReceiverKind(kind):
return "mojo::internal::Handle_Data"
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::internal::AssociatedInterface_Data"
......@@ -889,6 +897,12 @@ class Generator(generator.Generator):
return "%sPtrDataView" % _GetName(kind)
if mojom.IsInterfaceRequestKind(kind):
return "%sRequestDataView" % _GetName(kind.kind)
if mojom.IsPendingRemoteKind(kind):
return ("mojo::InterfacePtrDataView<%sInterfaceBase>" %
_GetName(kind.kind))
if mojom.IsPendingReceiverKind(kind):
return ("mojo::InterfaceRequestDataView<%sInterfaceBase>" %
_GetName(kind.kind))
if mojom.IsAssociatedInterfaceKind(kind):
return "%sAssociatedPtrInfoDataView" % _GetName(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
......
......@@ -208,6 +208,17 @@ if (enable_mojom_typemapping) {
# cpp_only (optional)
# If set to true, only the C++ bindings targets will be generated.
#
# NOTE: If the global |enable_ipc_fuzzer| build arg is true, JS bindings
# will still be generated even when |cpp_only| is set to |true|, unless
# you also set |enable_fuzzing| to |false| in your mojom target.
#
# enable_fuzzing (optional)
# Enables generation of fuzzing sources for the target if the global build
# arg |enable_ipc_fuzzer| is also set to |true|. Defaults to |true|. If
# fuzzing generation is enabled for a target, the target will always
# generate JS bindings even if |cpp_only| is set to |true|. See note
# above.
#
# support_lazy_serialization (optional)
# If set to |true|, generated C++ bindings will effectively prefer to
# transmit messages in an unserialized form when going between endpoints
......@@ -408,6 +419,10 @@ template("mojom") {
write_file("$target_gen_dir/$target_name.deps_sources_list", deps_sources)
generate_fuzzing = enable_ipc_fuzzer && (!defined(invoker.enable_fuzzing) ||
invoker.enable_fuzzing) &&
(!defined(invoker.testonly) || !invoker.testonly)
if (defined(invoker.sources)) {
parser_target_name = "${target_name}__parser"
enabled_features = []
......@@ -1092,7 +1107,7 @@ template("mojom") {
public_deps += [ "//mojo/public/cpp/bindings:wtf_support" ]
}
if (enable_ipc_fuzzer) {
if (generate_fuzzing) {
# Generate JS bindings by default if IPC fuzzer is enabled.
public_deps += [ ":$js_data_deps_target_name" ]
}
......@@ -1191,7 +1206,7 @@ template("mojom") {
}
}
if (enable_ipc_fuzzer || !defined(invoker.cpp_only) || !invoker.cpp_only) {
if (generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) {
if (defined(invoker.sources)) {
generator_js_target_name = "${target_name}_js__generator"
generator_js_lite_outputs =
......@@ -1237,7 +1252,7 @@ template("mojom") {
args += message_scrambling_args
}
if (enable_ipc_fuzzer) {
if (generate_fuzzing) {
args += [ "--generate_fuzzing" ]
}
}
......
......@@ -490,6 +490,30 @@ class Map(ReferenceKind):
return GenericRepr(self, {'key_kind': True, 'value_kind': True})
class PendingRemote(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
def __init__(self, kind=None):
if not isinstance(kind, Interface):
raise Exception(
'pending_remote<T> requires T to be an interface type. Got %r' %
kind.spec)
ReferenceKind.__init__(self, 'rmt:' + kind.spec)
self.kind = kind
class PendingReceiver(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
def __init__(self, kind=None):
if not isinstance(kind, Interface):
raise Exception(
'pending_receiver<T> requires T to be an interface type. Got %r' %
kind.spec)
ReferenceKind.__init__(self, 'rcv:' + kind.spec)
self.kind = kind
class InterfaceRequest(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
......@@ -838,6 +862,13 @@ def IsInterfaceRequestKind(kind):
def IsAssociatedInterfaceRequestKind(kind):
return isinstance(kind, AssociatedInterfaceRequest)
def IsPendingRemoteKind(kind):
return isinstance(kind, PendingRemote)
def IsPendingReceiverKind(kind):
return isinstance(kind, PendingReceiver)
def IsEnumKind(kind):
return isinstance(kind, Enum)
......@@ -875,7 +906,8 @@ def IsAnyHandleKind(kind):
def IsAnyInterfaceKind(kind):
return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind) or
IsAssociatedKind(kind))
IsAssociatedKind(kind) or IsPendingRemoteKind(kind) or
IsPendingReceiverKind(kind))
def IsAnyHandleOrInterfaceKind(kind):
......
......@@ -44,11 +44,12 @@ class PackedField(object):
@classmethod
def GetSizeForKind(cls, kind):
if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct,
mojom.Interface, mojom.AssociatedInterface)):
mojom.Interface, mojom.AssociatedInterface,
mojom.PendingRemote)):
return 8
if isinstance(kind, mojom.Union):
return 16
if isinstance(kind, mojom.InterfaceRequest):
if isinstance(kind, (mojom.InterfaceRequest, mojom.PendingReceiver)):
kind = mojom.MSGPIPE
if isinstance(kind, mojom.AssociatedInterfaceRequest):
return 4
......
......@@ -69,7 +69,7 @@ def _MapKind(kind):
base_kind = _MapKind(kind[0:-1])
# NOTE: This doesn't rule out enum types. Those will be detected later, when
# cross-reference is established.
reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso', 'rmt', 'rcv')
if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
raise Exception(
'A type (spec "%s") cannot be made nullable' % base_kind)
......@@ -87,6 +87,12 @@ def _MapKind(kind):
if kind.startswith('asso<'):
assert kind.endswith('>')
return 'asso:' + _MapKind(kind[5:-1])
if kind.startswith('rmt<'):
assert kind.endswith('>')
return 'rmt:' + _MapKind(kind[4:-1])
if kind.startswith('rcv<'):
assert kind.endswith('>')
return 'rcv:' + _MapKind(kind[4:-1])
if kind in map_to_kind:
return map_to_kind[kind]
return 'x:' + kind
......@@ -202,6 +208,10 @@ def _Kind(kinds, spec, scope):
kind = mojom.Array(_Kind(kinds, spec[colon+1:], scope), length)
elif spec.startswith('r:'):
kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope))
elif spec.startswith('rmt:'):
kind = mojom.PendingRemote(_Kind(kinds, spec[4:], scope))
elif spec.startswith('rcv:'):
kind = mojom.PendingReceiver(_Kind(kinds, spec[4:], scope))
elif spec.startswith('m['):
# Isolate the two types from their brackets.
......
......@@ -65,7 +65,9 @@ class Lexer(object):
'DEFAULT',
'ARRAY',
'MAP',
'ASSOCIATED'
'ASSOCIATED',
'PENDING_REMOTE',
'PENDING_RECEIVER'
)
keyword_map = {}
......
......@@ -271,7 +271,9 @@ class Parser(object):
p[0] = p[1]
def p_basictypename(self, p):
"""basictypename : identifier
"""basictypename : remotetype
| receivertype
| identifier
| ASSOCIATED identifier
| handletype"""
if len(p) == 2:
......@@ -279,6 +281,14 @@ class Parser(object):
else:
p[0] = "asso<" + p[2] + ">"
def p_remotetype(self, p):
"""remotetype : PENDING_REMOTE LANGLE identifier RANGLE"""
p[0] = "rmt<%s>" % p[3]
def p_receivertype(self, p):
"""receivertype : PENDING_RECEIVER LANGLE identifier RANGLE"""
p[0] = "rcv<%s>" % p[3]
def p_handletype(self, p):
"""handletype : HANDLE
| HANDLE LANGLE NAME RANGLE"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment