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