DCHECK when the Runnable associated with a Callback is destructed without being invoked, unless a connection error occurred or the pipe was manually closed first.
BUG=455298
R=jamesr@chromium.org, sky@chromium.org, jamesr, sky
Review URL: https://codereview.chromium.org/1003773002
diff --git a/mojo/public/cpp/bindings/lib/connector.h b/mojo/public/cpp/bindings/lib/connector.h
index 7c1d445..dfbd514 100644
--- a/mojo/public/cpp/bindings/lib/connector.h
+++ b/mojo/public/cpp/bindings/lib/connector.h
@@ -63,6 +63,9 @@
// a quiescent state.
ScopedMessagePipeHandle PassMessagePipe();
+ // Is the connector bound to a MessagePipe handle?
+ bool is_valid() const { return message_pipe_.is_valid(); }
+
// Waits for the next message on the pipe, blocking until one arrives or an
// error happens. Returns |true| if a message has been delivered, |false|
// otherwise.
diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc
index da1e7e1..e64f1fa 100644
--- a/mojo/public/cpp/bindings/lib/router.cc
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -11,7 +11,7 @@
// ----------------------------------------------------------------------------
-class ResponderThunk : public MessageReceiver {
+class ResponderThunk : public MessageReceiverWithStatus {
public:
explicit ResponderThunk(const SharedData<Router*>& router)
: router_(router), accept_was_invoked_(false) {}
@@ -44,6 +44,12 @@
return result;
}
+ // MessageReceiverWithStatus implementation:
+ bool IsValid() override {
+ Router* router = router_.value();
+ return router && !router->encountered_error() && router->is_valid();
+ }
+
private:
SharedData<Router*> router_;
bool accept_was_invoked_;
@@ -118,7 +124,7 @@
bool Router::HandleIncomingMessage(Message* message) {
if (message->has_flag(kMessageExpectsResponse)) {
if (incoming_receiver_) {
- MessageReceiver* responder = new ResponderThunk(weak_self_);
+ MessageReceiverWithStatus* responder = new ResponderThunk(weak_self_);
bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
if (!ok)
delete responder;
diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h
index 8254bab..86b8897 100644
--- a/mojo/public/cpp/bindings/lib/router.h
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -24,7 +24,7 @@
// Sets the receiver to handle messages read from the message pipe that do
// not have the kMessageIsResponse flag set.
- void set_incoming_receiver(MessageReceiverWithResponder* receiver) {
+ void set_incoming_receiver(MessageReceiverWithResponderStatus* receiver) {
incoming_receiver_ = receiver;
}
@@ -38,6 +38,9 @@
// waiting to read from the pipe.
bool encountered_error() const { return connector_.encountered_error(); }
+ // Is the router bound to a MessagePipe handle?
+ bool is_valid() const { return connector_.is_valid(); }
+
void CloseMessagePipe() { connector_.CloseMessagePipe(); }
ScopedMessagePipeHandle PassMessagePipe() {
@@ -81,7 +84,7 @@
FilterChain filters_;
Connector connector_;
SharedData<Router*> weak_self_;
- MessageReceiverWithResponder* incoming_receiver_;
+ MessageReceiverWithResponderStatus* incoming_receiver_;
ResponderMap responders_;
uint64_t next_request_id_;
bool testing_mode_;
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 80cd6d5..b763d9a 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -106,6 +106,39 @@
MOJO_WARN_UNUSED_RESULT = 0;
};
+// A MessageReceiver that is also able to provide status about the state
+// of the underlying MessagePipe to which it will be forwarding messages
+// received via the |Accept()| call.
+class MessageReceiverWithStatus : public MessageReceiver {
+ public:
+ ~MessageReceiverWithStatus() override {}
+
+ // Returns |true| if this MessageReceiver is currently bound to a MessagePipe,
+ // the pipe has not been closed, and the pipe has not encountered an error.
+ virtual bool IsValid() = 0;
+};
+
+// An alternative to MessageReceiverWithResponder for cases in which it
+// is necessary for the implementor of this interface to know about the status
+// of the MessagePipe which will carry the responses.
+class MessageReceiverWithResponderStatus : public MessageReceiver {
+ public:
+ ~MessageReceiverWithResponderStatus() override {}
+
+ // A variant on Accept that registers a MessageReceiverWithStatus (known as
+ // the responder) to handle the response message generated from the given
+ // message. Any of the responder's methods (Accept or IsValid) may be called
+ // during AcceptWithResponder or some time after its return.
+ //
+ // NOTE: Upon returning true, AcceptWithResponder assumes ownership of
+ // |responder| and will delete it after calling |responder->Accept| or upon
+ // its own destruction.
+ //
+ virtual bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder)
+ MOJO_WARN_UNUSED_RESULT = 0;
+};
+
// Read a single message from the pipe and dispatch to the given receiver. The
// receiver may be null, in which case the message is simply discarded.
// Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 6d09151..6c67751 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -9,6 +9,7 @@
sources = [
"array_unittest.cc",
+ "binding_callback_unittest.cc",
"binding_unittest.cc",
"bounds_checker_unittest.cc",
"buffer_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
new file mode 100644
index 0000000..34b040c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright 2015 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/interface_ptr.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/binding_callback_test_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// The tests in this file are designed to test the interaction between a
+// Callback and its associated Binding. If a Callback is deleted before
+// being used we DCHECK fail--unless the associated Binding has already
+// been closed or deleted. This contract must be explained to the Mojo
+// application developer. For example it is the developer's responsibility to
+// ensure that the Binding is destroyed before an unused Callback is destroyed.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mojo {
+namespace test {
+namespace {
+
+// A Runnable object that saves the last value it sees via the
+// provided int32_t*. Used on the client side.
+class ValueSaver {
+ public:
+ explicit ValueSaver(int32_t* last_value_seen)
+ : last_value_seen_(last_value_seen) {}
+ void Run(int32_t x) const { *last_value_seen_ = x; }
+
+ private:
+ int32_t* const last_value_seen_;
+};
+
+// An implementation of BindingCallbackTestInterface used on the server side.
+// All it does is save the values and Callbacks it sees.
+class BindingCallbackTestInterfaceImpl : public BindingCallbackTestInterface {
+ public:
+ BindingCallbackTestInterfaceImpl()
+ : last_server_value_seen_(0),
+ callback_saved_(new Callback<void(int32_t)>()) {}
+
+ ~BindingCallbackTestInterfaceImpl() override {
+ if (callback_saved_) {
+ delete callback_saved_;
+ }
+ }
+
+ // Run's the callback previously saved from the last invocation
+ // of |EchoInt()|.
+ bool RunCallback() {
+ if (callback_saved_) {
+ callback_saved_->Run(last_server_value_seen_);
+ return true;
+ }
+ return false;
+ }
+
+ // Delete's the previously saved callback.
+ void DeleteCallback() {
+ delete callback_saved_;
+ callback_saved_ = nullptr;
+ }
+
+ // Saves its two input values in member variables and does nothing else.
+ void EchoInt(int32_t x, const Callback<void(int32_t)>& callback) {
+ last_server_value_seen_ = x;
+ *callback_saved_ = callback;
+ }
+
+ void resetLastServerValueSeen() { last_server_value_seen_ = 0; }
+
+ int32_t last_server_value_seen() const { return last_server_value_seen_; }
+
+ private:
+ int32_t last_server_value_seen_;
+ Callback<void(int32_t)>* callback_saved_;
+};
+
+class BindingCallbackTest : public testing::Test {
+ public:
+ ~BindingCallbackTest() override {}
+
+ protected:
+ int32_t last_client_callback_value_seen_;
+ ScopedMessagePipeHandle handle_client_;
+ ScopedMessagePipeHandle handle_server_;
+ BindingCallbackTestInterfacePtr interface_ptr_;
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+ void SetUp() {
+ CreateMessagePipe(nullptr, &handle_client_, &handle_server_);
+ // Create the client InterfacePtr.
+ interface_ptr_ =
+ MakeProxy<BindingCallbackTestInterface>(handle_client_.Pass());
+ }
+
+ private:
+ Environment env_;
+ RunLoop loop_;
+};
+
+// Tests that the InterfacePtr and the Binding can communicate with each
+// other normally.
+TEST_F(BindingCallbackTest, Basic) {
+ // Create the ServerImpl and the Binding.
+ BindingCallbackTestInterfaceImpl server_impl;
+ Binding<BindingCallbackTestInterface> binding(
+ &server_impl,
+ MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass()));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now run the Callback.
+ server_impl.RunCallback();
+ PumpMessages();
+
+ // Check that the client has now seen the correct value.
+ EXPECT_EQ(7, last_client_callback_value_seen_);
+
+ // Initialize the test values again.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method again.
+ interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(13, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now run the Callback again.
+ server_impl.RunCallback();
+ PumpMessages();
+
+ // Check that the client has now seen the correct value again.
+ EXPECT_EQ(13, last_client_callback_value_seen_);
+}
+
+// Tests that running the Callback after the Binding has been deleted
+// results in a clean failure.
+TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) {
+ // Create the ServerImpl.
+ BindingCallbackTestInterfaceImpl server_impl;
+ {
+ // Create the binding in an inner scope so it can be deleted first.
+ Binding<BindingCallbackTestInterface> binding(
+ &server_impl,
+ MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass()));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ }
+ // The binding has now been destroyed and the pipe is closed.
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now try to run the Callback. This should do nothing since the pipe
+ // is closed.
+ EXPECT_TRUE(server_impl.RunCallback());
+ PumpMessages();
+
+ // Check that the client has still not seen the correct value.
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Attempt to invoke the method again and confirm that an error was
+ // encountered.
+ interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ EXPECT_TRUE(interface_ptr_.encountered_error());
+}
+
+// Tests that deleting a Callback without running it after the corresponding
+// binding has already been deleted does not result in a crash.
+TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) {
+ // Create the ServerImpl.
+ BindingCallbackTestInterfaceImpl server_impl;
+ {
+ // Create the binding in an inner scope so it can be deleted first.
+ Binding<BindingCallbackTestInterface> binding(
+ &server_impl,
+ MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass()));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ }
+ // The binding has now been destroyed and the pipe is closed.
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Delete the callback without running it. This should not
+ // cause a problem because the insfrastructure can detect that the
+ // binding has already been destroyed and the pipe is closed.
+ server_impl.DeleteCallback();
+}
+
+// Tests that closing a Binding allows us to delete a callback
+// without running it without encountering a crash.
+TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) {
+ // Create the ServerImpl and the Binding.
+ BindingCallbackTestInterfaceImpl server_impl;
+ Binding<BindingCallbackTestInterface> binding(
+ &server_impl,
+ MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass()));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now close the Binding.
+ binding.Close();
+
+ // Delete the callback without running it. This should not
+ // cause a crash because the insfrastructure can detect that the
+ // binding has already been closed.
+ server_impl.DeleteCallback();
+
+ // Check that the client has still not seen the correct value.
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+}
+
+// Tests that deleting a Callback without using it before the
+// Binding has been destroyed or closed results in a DCHECK.
+TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) {
+ // Create the ServerImpl and the Binding.
+ BindingCallbackTestInterfaceImpl server_impl;
+ Binding<BindingCallbackTestInterface> binding(
+ &server_impl,
+ MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass()));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Delete the callback without running it. This should cause a crash
+ // due to a DCHECK.
+ EXPECT_DEATH(server_impl.DeleteCallback(),
+ "Check failed: !callback_was_dropped.");
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/router_unittest.cc b/mojo/public/cpp/bindings/tests/router_unittest.cc
index ed3792f..d3d8ccb 100644
--- a/mojo/public/cpp/bindings/tests/router_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/router_unittest.cc
@@ -47,19 +47,20 @@
internal::MessageQueue* queue_;
};
-class ResponseGenerator : public MessageReceiverWithResponder {
+class ResponseGenerator : public MessageReceiverWithResponderStatus {
public:
ResponseGenerator() {}
bool Accept(Message* message) override { return false; }
bool AcceptWithResponder(Message* message,
- MessageReceiver* responder) override {
+ MessageReceiverWithStatus* responder) override {
EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse));
bool result = SendResponse(
message->name(), message->request_id(),
reinterpret_cast<const char*>(message->payload()), responder);
+ EXPECT_TRUE(responder->IsValid());
delete responder;
return result;
}
@@ -84,7 +85,7 @@
~LazyResponseGenerator() override { delete responder_; }
bool AcceptWithResponder(Message* message,
- MessageReceiver* responder) override {
+ MessageReceiverWithStatus* responder) override {
name_ = message->name();
request_id_ = message->request_id();
request_string_ =
@@ -95,6 +96,8 @@
bool has_responder() const { return !!responder_; }
+ bool responder_is_valid() const { return responder_->IsValid(); }
+
// Send the response and delete the responder.
void CompleteWithResponse() { Complete(true); }
@@ -112,7 +115,7 @@
responder_ = nullptr;
}
- MessageReceiver* responder_;
+ MessageReceiverWithStatus* responder_;
uint32_t name_;
uint64_t request_id_;
std::string request_string_;
@@ -261,6 +264,7 @@
EXPECT_TRUE(message_queue.IsEmpty());
// Send the response.
+ EXPECT_TRUE(generator.responder_is_valid());
generator.CompleteWithResponse();
PumpMessages();
@@ -283,6 +287,7 @@
EXPECT_TRUE(message_queue.IsEmpty());
// Send the second response.
+ EXPECT_TRUE(generator.responder_is_valid());
generator.CompleteWithResponse();
PumpMessages();
@@ -358,6 +363,7 @@
EXPECT_TRUE(generator.has_responder());
}
+ EXPECT_FALSE(generator.responder_is_valid());
generator.CompleteWithResponse(); // This should end up doing nothing.
}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
index dabc3f6..d159a09 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -7,6 +7,7 @@
mojom("test_interfaces") {
testonly = true
sources = [
+ "binding_callback_test_interfaces.mojom",
"math_calculator.mojom",
"no_module.mojom",
"rect.mojom",
diff --git a/mojo/public/interfaces/bindings/tests/binding_callback_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/binding_callback_test_interfaces.mojom
new file mode 100644
index 0000000..aa3c40d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/binding_callback_test_interfaces.mojom
@@ -0,0 +1,8 @@
+// Copyright 2015 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.
+
+// Interface used by BindingCallbackTest.
+interface BindingCallbackTestInterface {
+ EchoInt(int32 param0) => (int32 param0);
+};
\ No newline at end of file
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index 5831423..fd6b643 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -115,31 +115,33 @@
: public {{class_name}}::{{method.name}}Callback::Runnable {
public:
virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+ // Is the Mojo application destroying the callback without running it
+ // and without first closing the pipe?
+ bool callback_was_dropped = responder_ && responder_->IsValid();
+ // If the Callback was dropped then deleting the responder will close
+ // the pipe so the calling application knows to stop waiting for a reply.
delete responder_;
- // TODO(rudominer) DCHECK if |was_run_| is false and we don't have evidence
- // that we are in a legitamte shutdown case such as the Connector detected
- // an error or Close() was invoked.
+ MOJO_DCHECK(!callback_was_dropped) << "The callback passed to "
+ "{{class_name}}::{{method.name}}({%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback) "
+ "was never run.";
}
{{class_name}}_{{method.name}}_ProxyToResponder(
uint64_t request_id,
- mojo::MessageReceiver* responder)
+ mojo::MessageReceiverWithStatus* responder)
: request_id_(request_id),
- responder_(responder),
- was_run_(false) {
+ responder_(responder) {
}
void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override;
private:
uint64_t request_id_;
- mutable mojo::MessageReceiver* responder_;
- mutable bool was_run_;
+ mutable mojo::MessageReceiverWithStatus* responder_;
MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
};
void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
{{interface_macros.declare_params("in_", method.response_parameters)}}) const {
- was_run_ = true;
{{struct_macros.get_serialized_size(response_params_struct, "in_%s")}}
mojo::internal::ResponseMessageBuilder builder(
{{message_name}}, size, request_id_);
@@ -159,6 +161,8 @@
: sink_(nullptr) {
}
+{{class_name}}Stub::~{{interface.name}}Stub() {}
+
{#--- Stub definition #}
bool {{class_name}}Stub::Accept(mojo::Message* message) {
@@ -188,7 +192,7 @@
}
bool {{class_name}}Stub::AcceptWithResponder(
- mojo::Message* message, mojo::MessageReceiver* responder) {
+ mojo::Message* message, mojo::MessageReceiverWithStatus* responder) {
{%- if interface.methods %}
switch (message->header()->name) {
{%- for method in interface.methods %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
index 1abc3b9..05a2495 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -1,12 +1,13 @@
-class {{interface.name}}Stub : public mojo::MessageReceiverWithResponder {
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus {
public:
{{interface.name}}Stub();
+ ~{{interface.name}}Stub() override;
void set_sink({{interface.name}}* sink) { sink_ = sink; }
{{interface.name}}* sink() { return sink_; }
bool Accept(mojo::Message* message) override;
bool AcceptWithResponder(mojo::Message* message,
- mojo::MessageReceiver* responder) override;
+ mojo::MessageReceiverWithStatus* responder) override;
private:
{{interface.name}}* sink_;
diff --git a/services/view_manager/view_manager_service_apptest.cc b/services/view_manager/view_manager_service_apptest.cc
index 01bdd0f..a3bcc26 100644
--- a/services/view_manager/view_manager_service_apptest.cc
+++ b/services/view_manager/view_manager_service_apptest.cc
@@ -333,6 +333,7 @@
EventPtr event,
const Callback<void()>& callback) override {
tracker()->OnViewInputEvent(view_id, event.Pass());
+ callback.Run();
}
void OnViewSharedPropertyChanged(uint32_t view,
const String& name,