Make FlBasicMessageChannelResponseHandle a GObject to detect unresponded and double responded messages. (#18514)

diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc
index 5559c4a..086c8bf 100644
--- a/shell/platform/linux/fl_basic_message_channel.cc
+++ b/shell/platform/linux/fl_basic_message_channel.cc
@@ -23,33 +23,51 @@
   gpointer message_handler_data;
 };
 
-// Wrap the binary messenger handle for type safety and to make the API
-// consistent
 struct _FlBasicMessageChannelResponseHandle {
+  GObject parent_instance;
+
   FlBinaryMessengerResponseHandle* response_handle;
 };
 
-static FlBasicMessageChannelResponseHandle* response_handle_new(
-    FlBinaryMessengerResponseHandle* response_handle) {
-  FlBasicMessageChannelResponseHandle* handle =
-      static_cast<FlBasicMessageChannelResponseHandle*>(
-          g_malloc0(sizeof(FlBasicMessageChannelResponseHandle)));
-  handle->response_handle = response_handle;
-
-  return handle;
-}
-
-static void response_handle_free(FlBasicMessageChannelResponseHandle* handle) {
-  g_free(handle);
-}
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBasicMessageChannelResponseHandle,
-                              response_handle_free);
-
 // Added here to stop the compiler from optimising this function away
 G_MODULE_EXPORT GType fl_basic_message_channel_get_type();
 
 G_DEFINE_TYPE(FlBasicMessageChannel, fl_basic_message_channel, G_TYPE_OBJECT)
+G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle,
+              fl_basic_message_channel_response_handle,
+              G_TYPE_OBJECT)
+
+static void fl_basic_message_channel_response_handle_dispose(GObject* object) {
+  FlBasicMessageChannelResponseHandle* self =
+      FL_BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE(object);
+
+  g_clear_object(&self->response_handle);
+
+  G_OBJECT_CLASS(fl_basic_message_channel_response_handle_parent_class)
+      ->dispose(object);
+}
+
+static void fl_basic_message_channel_response_handle_class_init(
+    FlBasicMessageChannelResponseHandleClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose =
+      fl_basic_message_channel_response_handle_dispose;
+}
+
+static void fl_basic_message_channel_response_handle_init(
+    FlBasicMessageChannelResponseHandle* self) {}
+
+static FlBasicMessageChannelResponseHandle*
+fl_basic_message_channel_response_handle_new(
+    FlBinaryMessengerResponseHandle* response_handle) {
+  FlBasicMessageChannelResponseHandle* self =
+      FL_BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE(g_object_new(
+          fl_basic_message_channel_response_handle_get_type(), nullptr));
+
+  self->response_handle =
+      FL_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_ref(response_handle));
+
+  return self;
+}
 
 // Called when a binary message is received on this channel
 static void message_cb(FlBinaryMessenger* messenger,
@@ -74,8 +92,9 @@
                                       nullptr);
   }
 
-  self->message_handler(self, message_value,
-                        response_handle_new(response_handle),
+  g_autoptr(FlBasicMessageChannelResponseHandle) handle =
+      fl_basic_message_channel_response_handle_new(response_handle);
+  self->message_handler(self, message_value, handle,
                         self->message_handler_data);
 }
 
@@ -146,18 +165,18 @@
     GError** error) {
   g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), FALSE);
   g_return_val_if_fail(response_handle != nullptr, FALSE);
-
-  // Take reference to ensure it is freed
-  g_autoptr(FlBasicMessageChannelResponseHandle) owned_response_handle =
-      response_handle;
+  g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
 
   g_autoptr(GBytes) data =
       fl_message_codec_encode_message(self->codec, message, error);
   if (data == nullptr)
     return FALSE;
 
-  return fl_binary_messenger_send_response(
-      self->messenger, owned_response_handle->response_handle, data, error);
+  gboolean result = fl_binary_messenger_send_response(
+      self->messenger, response_handle->response_handle, data, error);
+  g_clear_object(&response_handle->response_handle);
+
+  return result;
 }
 
 G_MODULE_EXPORT void fl_basic_message_channel_send(FlBasicMessageChannel* self,
diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc
index 58a2e3e..319c38b 100644
--- a/shell/platform/linux/fl_binary_messenger.cc
+++ b/shell/platform/linux/fl_binary_messenger.cc
@@ -10,6 +10,9 @@
 
 #include <gmodule.h>
 
+G_DEFINE_QUARK(fl_binary_messenger_codec_error_quark,
+               fl_binary_messenger_codec_error)
+
 struct _FlBinaryMessenger {
   GObject parent_instance;
 
@@ -21,12 +24,61 @@
 
 G_DEFINE_TYPE(FlBinaryMessenger, fl_binary_messenger, G_TYPE_OBJECT)
 
+struct _FlBinaryMessengerResponseHandle {
+  GObject parent_instance;
+
+  // Messenger sending response on
+  FlBinaryMessenger* messenger;
+
+  // Handle to send the response with. This is cleared to nullptr when it is
+  // used.
+  const FlutterPlatformMessageResponseHandle* response_handle;
+};
+
+G_DEFINE_TYPE(FlBinaryMessengerResponseHandle,
+              fl_binary_messenger_response_handle,
+              G_TYPE_OBJECT)
+
+static void fl_binary_messenger_response_handle_dispose(GObject* object) {
+  FlBinaryMessengerResponseHandle* self =
+      FL_BINARY_MESSENGER_RESPONSE_HANDLE(object);
+
+  if (self->response_handle != nullptr && self->messenger->engine != nullptr)
+    g_critical("FlBinaryMessengerResponseHandle was not responded to");
+
+  g_clear_object(&self->messenger);
+  self->response_handle = nullptr;
+
+  G_OBJECT_CLASS(fl_binary_messenger_response_handle_parent_class)
+      ->dispose(object);
+}
+
+static void fl_binary_messenger_response_handle_class_init(
+    FlBinaryMessengerResponseHandleClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_response_handle_dispose;
+}
+
+static void fl_binary_messenger_response_handle_init(
+    FlBinaryMessengerResponseHandle* self) {}
+
+static FlBinaryMessengerResponseHandle* fl_binary_messenger_response_handle_new(
+    FlBinaryMessenger* messenger,
+    const FlutterPlatformMessageResponseHandle* response_handle) {
+  FlBinaryMessengerResponseHandle* self = FL_BINARY_MESSENGER_RESPONSE_HANDLE(
+      g_object_new(fl_binary_messenger_response_handle_get_type(), nullptr));
+
+  self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
+  self->response_handle = response_handle;
+
+  return self;
+}
+
 typedef struct {
   FlBinaryMessengerMessageHandler message_handler;
   gpointer message_handler_data;
 } PlatformMessageHandler;
 
-PlatformMessageHandler* platform_message_handler_new(
+static PlatformMessageHandler* platform_message_handler_new(
     FlBinaryMessengerMessageHandler handler,
     gpointer user_data) {
   PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(
@@ -36,32 +88,11 @@
   return self;
 }
 
-void platform_message_handler_free(gpointer data) {
+static void platform_message_handler_free(gpointer data) {
   PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(data);
   g_free(self);
 }
 
-struct _FlBinaryMessengerResponseHandle {
-  const FlutterPlatformMessageResponseHandle* response_handle;
-};
-
-static FlBinaryMessengerResponseHandle* response_handle_new(
-    const FlutterPlatformMessageResponseHandle* response_handle) {
-  FlBinaryMessengerResponseHandle* self =
-      static_cast<FlBinaryMessengerResponseHandle*>(
-          g_malloc0(sizeof(FlBinaryMessengerResponseHandle)));
-  self->response_handle = response_handle;
-
-  return self;
-}
-
-static void response_handle_free(FlBinaryMessengerResponseHandle* self) {
-  g_free(self);
-}
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBinaryMessengerResponseHandle,
-                              response_handle_free);
-
 static void engine_weak_notify_cb(gpointer user_data, GObject* object) {
   FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data);
   self->engine = nullptr;
@@ -75,14 +106,13 @@
     void* user_data) {
   FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data);
 
-  FlBinaryMessengerResponseHandle* handle =
-      response_handle_new(response_handle);
-
   PlatformMessageHandler* handler = static_cast<PlatformMessageHandler*>(
       g_hash_table_lookup(self->platform_message_handlers, channel));
   if (handler == nullptr)
     return FALSE;
 
+  g_autoptr(FlBinaryMessengerResponseHandle) handle =
+      fl_binary_messenger_response_handle_new(self, response_handle);
   handler->message_handler(self, channel, message, handle,
                            handler->message_handler_data);
 
@@ -148,16 +178,23 @@
     GError** error) {
   g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE);
   g_return_val_if_fail(response_handle != nullptr, FALSE);
-
-  // Take reference to ensure it is freed
-  g_autoptr(FlBinaryMessengerResponseHandle) owned_response_handle =
-      response_handle;
+  g_return_val_if_fail(response_handle->messenger == self, FALSE);
+  g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
 
   if (self->engine == nullptr)
     return TRUE;
 
+  if (response_handle->response_handle == nullptr) {
+    g_set_error(
+        error, FL_BINARY_MESSENGER_ERROR,
+        FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
+        "Attempted to respond to a message that is already responded to");
+    return FALSE;
+  }
+
   gboolean result = fl_engine_send_platform_message_response(
-      self->engine, owned_response_handle->response_handle, response, error);
+      self->engine, response_handle->response_handle, response, error);
+  response_handle->response_handle = nullptr;
 
   return result;
 }
diff --git a/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
index d5a9669..6c24a7d 100644
--- a/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
+++ b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
@@ -23,6 +23,12 @@
                      BASIC_MESSAGE_CHANNEL,
                      GObject)
 
+G_DECLARE_FINAL_TYPE(FlBasicMessageChannelResponseHandle,
+                     fl_basic_message_channel_response_handle,
+                     FL,
+                     BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE,
+                     GObject)
+
 /**
  * FlBasicMessageChannel:
  *
@@ -79,19 +85,23 @@
 /**
  * FlBasicMessageChannelResponseHandle:
  *
- * A handle used to respond to messages.
+ * #FlBasicMessageChannelResponseHandle is an object used to send responses
+ * with.
  */
-typedef struct _FlBasicMessageChannelResponseHandle
-    FlBasicMessageChannelResponseHandle;
 
 /**
  * FlBasicMessageChannelMessageHandler:
  * @channel: an #FlBasicMessageChannel.
  * @message: message received.
- * @response_handle: (transfer full): a handle to respond to the message with.
+ * @response_handle: a handle to respond to the message with.
  * @user_data: (closure): data provided when registering this handler.
  *
- * Function called when a message is received.
+ * Function called when a message is received. Call
+ * fl_basic_message_channel_respond() to respond to this message. If the
+ * response is not occurring in this callback take a reference to
+ * @response_handle and release that once it has been responded to. Failing to
+ * respond before the last reference to @response_handle is dropped is a
+ * programming error.
  */
 typedef void (*FlBasicMessageChannelMessageHandler)(
     FlBasicMessageChannel* channel,
@@ -132,7 +142,7 @@
 /**
  * fl_basic_message_channel_respond:
  * @channel: an #FlBasicMessageChannel.
- * @response_handle: (transfer full): handle that was provided in a
+ * @response_handle: handle that was provided in a
  * #FlBasicMessageChannelMessageHandler.
  * @message: (allow-none): message response to send or %NULL for an empty
  * response.
diff --git a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
index c29e978..6792fcd 100644
--- a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
+++ b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
@@ -14,12 +14,33 @@
 
 G_BEGIN_DECLS
 
+/**
+ * FlBinaryMessengerError:
+ * @FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED: unable to send response, this
+ * message has already been responded to.
+ *
+ * Errors for #FlBinaryMessenger objects to set on failures.
+ */
+#define FL_BINARY_MESSENGER_ERROR fl_binary_messenger_codec_error_quark()
+
+typedef enum {
+  FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
+} FlBinaryMessengerError;
+
+GQuark fl_binary_messenger_codec_error_quark(void) G_GNUC_CONST;
+
 G_DECLARE_FINAL_TYPE(FlBinaryMessenger,
                      fl_binary_messenger,
                      FL,
                      BINARY_MESSENGER,
                      GObject)
 
+G_DECLARE_FINAL_TYPE(FlBinaryMessengerResponseHandle,
+                     fl_binary_messenger_response_handle,
+                     FL,
+                     BINARY_MESSENGER_RESPONSE_HANDLE,
+                     GObject)
+
 /**
  * FlBinaryMessenger:
  *
@@ -30,20 +51,23 @@
 /**
  * FlBinaryMessengerResponseHandle:
  *
- * A handle used to respond to platform messages.
+ * #FlBinaryMessengerResponseHandle is an object used to send responses with.
  */
-typedef struct _FlBinaryMessengerResponseHandle FlBinaryMessengerResponseHandle;
 
 /**
  * FlBinaryMessengerMessageHandler:
  * @messenger: an #FlBinaryMessenger.
  * @channel: channel message received on.
  * @message: message content received from Dart.
- * @response_handle: (transfer full): a handle to respond to the message with.
+ * @response_handle: a handle to respond to the message with.
  * @user_data: (closure): data provided when registering this handler.
  *
- * Function called when platform messages are received. The receiver must
- * call fl_binary_messenger_send_response() to avoid leaking the handle.
+ * Function called when platform messages are received. Call
+ * fl_binary_messenger_send_response() to respond to this message. If the
+ * response is not occurring in this callback take a reference to
+ * @response_handle and release that once it has been responded to. Failing to
+ * respond before the last reference to @response_handle is dropped is a
+ * programming error.
  */
 typedef void (*FlBinaryMessengerMessageHandler)(
     FlBinaryMessenger* messenger,
@@ -74,7 +98,7 @@
 /**
  * fl_binary_messenger_send_response:
  * @binary_messenger: an #FlBinaryMessenger.
- * @response_handle: (transfer full): handle that was provided in a
+ * @response_handle: handle that was provided in a
  * #FlBinaryMessengerMessageHandler.
  * @response: (allow-none): response to send or %NULL for an empty response.
  * @error: (allow-none): #GError location to store the error occurring, or %NULL