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,