diff --git a/engine/src/flutter/shell/platform/linux/fl_basic_message_channel.cc b/engine/src/flutter/shell/platform/linux/fl_basic_message_channel.cc index 5559c4a9910..086c8bff591 100644 --- a/engine/src/flutter/shell/platform/linux/fl_basic_message_channel.cc +++ b/engine/src/flutter/shell/platform/linux/fl_basic_message_channel.cc @@ -23,33 +23,51 @@ struct _FlBasicMessageChannel { 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( - 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 @@ static void message_cb(FlBinaryMessenger* messenger, 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 @@ G_MODULE_EXPORT gboolean fl_basic_message_channel_respond( 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/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc b/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc index 58a2e3ee57c..319c38b9581 100644 --- a/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc +++ b/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc @@ -10,6 +10,9 @@ #include +G_DEFINE_QUARK(fl_binary_messenger_codec_error_quark, + fl_binary_messenger_codec_error) + struct _FlBinaryMessenger { GObject parent_instance; @@ -21,12 +24,61 @@ struct _FlBinaryMessenger { 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( @@ -36,32 +88,11 @@ PlatformMessageHandler* platform_message_handler_new( return self; } -void platform_message_handler_free(gpointer data) { +static void platform_message_handler_free(gpointer data) { PlatformMessageHandler* self = static_cast(data); g_free(self); } -struct _FlBinaryMessengerResponseHandle { - const FlutterPlatformMessageResponseHandle* response_handle; -}; - -static FlBinaryMessengerResponseHandle* response_handle_new( - const FlutterPlatformMessageResponseHandle* response_handle) { - FlBinaryMessengerResponseHandle* self = - static_cast( - 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 @@ static gboolean fl_binary_messenger_platform_message_cb( void* user_data) { FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data); - FlBinaryMessengerResponseHandle* handle = - response_handle_new(response_handle); - PlatformMessageHandler* handler = static_cast( 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 @@ G_MODULE_EXPORT gboolean fl_binary_messenger_send_response( 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/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h index d5a966937a1..6c24a7de4ed 100644 --- a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h +++ b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h @@ -23,6 +23,12 @@ G_DECLARE_FINAL_TYPE(FlBasicMessageChannel, 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 @@ G_DECLARE_FINAL_TYPE(FlBasicMessageChannel, /** * 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 @@ void fl_basic_message_channel_set_message_handler( /** * 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/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h index c29e9789247..6792fcddc3a 100644 --- a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h +++ b/engine/src/flutter/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 @@ G_DECLARE_FINAL_TYPE(FlBinaryMessenger, /** * 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 @@ void fl_binary_messenger_set_message_handler_on_channel( /** * 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