Add GDestroyNotify callbacks on handlers (#18546)
diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc
index 0c27513..be5ad98 100644
--- a/shell/platform/linux/fl_basic_message_channel.cc
+++ b/shell/platform/linux/fl_basic_message_channel.cc
@@ -12,6 +12,9 @@
// Messenger to communicate on
FlBinaryMessenger* messenger;
+ // TRUE if the channel has been closed
+ gboolean channel_closed;
+
// Channel name
gchar* name;
@@ -21,6 +24,7 @@
// Function called when a message is received
FlBasicMessageChannelMessageHandler message_handler;
gpointer message_handler_data;
+ GDestroyNotify message_handler_destroy_notify;
};
struct _FlBasicMessageChannelResponseHandle {
@@ -106,17 +110,37 @@
g_task_return_pointer(task, result, g_object_unref);
}
+// Called when the channel handler is closed
+static void channel_closed_cb(gpointer user_data) {
+ g_autoptr(FlBasicMessageChannel) self = FL_BASIC_MESSAGE_CHANNEL(user_data);
+
+ self->channel_closed = TRUE;
+
+ // Disconnect handler
+ if (self->message_handler_destroy_notify != nullptr)
+ self->message_handler_destroy_notify(self->message_handler_data);
+ self->message_handler = nullptr;
+ self->message_handler_data = nullptr;
+ self->message_handler_destroy_notify = nullptr;
+}
+
static void fl_basic_message_channel_dispose(GObject* object) {
FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL(object);
if (self->messenger != nullptr)
fl_binary_messenger_set_message_handler_on_channel(
- self->messenger, self->name, nullptr, nullptr);
+ self->messenger, self->name, nullptr, nullptr, nullptr);
g_clear_object(&self->messenger);
g_clear_pointer(&self->name, g_free);
g_clear_object(&self->codec);
+ if (self->message_handler_destroy_notify != nullptr)
+ self->message_handler_destroy_notify(self->message_handler_data);
+ self->message_handler = nullptr;
+ self->message_handler_data = nullptr;
+ self->message_handler_destroy_notify = nullptr;
+
G_OBJECT_CLASS(fl_basic_message_channel_parent_class)->dispose(object);
}
@@ -143,7 +167,8 @@
self->codec = FL_MESSAGE_CODEC(g_object_ref(codec));
fl_binary_messenger_set_message_handler_on_channel(
- self->messenger, self->name, message_cb, self);
+ self->messenger, self->name, message_cb, g_object_ref(self),
+ channel_closed_cb);
return self;
}
@@ -151,11 +176,25 @@
G_MODULE_EXPORT void fl_basic_message_channel_set_message_handler(
FlBasicMessageChannel* self,
FlBasicMessageChannelMessageHandler handler,
- gpointer user_data) {
+ gpointer user_data,
+ GDestroyNotify destroy_notify) {
g_return_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self));
+ // Don't set handler if channel closed
+ if (self->channel_closed) {
+ g_warning(
+ "Attempted to set message handler on closed FlBasicMessageChannel");
+ if (destroy_notify != nullptr)
+ destroy_notify(user_data);
+ return;
+ }
+
+ if (self->message_handler_destroy_notify != nullptr)
+ self->message_handler_destroy_notify(self->message_handler_data);
+
self->message_handler = handler;
self->message_handler_data = user_data;
+ self->message_handler_destroy_notify = destroy_notify;
}
G_MODULE_EXPORT gboolean fl_basic_message_channel_respond(
diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc
index 319c38b..50a8c21 100644
--- a/shell/platform/linux/fl_binary_messenger.cc
+++ b/shell/platform/linux/fl_binary_messenger.cc
@@ -76,26 +76,34 @@
typedef struct {
FlBinaryMessengerMessageHandler message_handler;
gpointer message_handler_data;
+ GDestroyNotify message_handler_destroy_notify;
} PlatformMessageHandler;
static PlatformMessageHandler* platform_message_handler_new(
FlBinaryMessengerMessageHandler handler,
- gpointer user_data) {
+ gpointer user_data,
+ GDestroyNotify destroy_notify) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(
g_malloc0(sizeof(PlatformMessageHandler)));
self->message_handler = handler;
self->message_handler_data = user_data;
+ self->message_handler_destroy_notify = destroy_notify;
return self;
}
static void platform_message_handler_free(gpointer data) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(data);
+ if (self->message_handler_destroy_notify)
+ self->message_handler_destroy_notify(self->message_handler_data);
g_free(self);
}
static void engine_weak_notify_cb(gpointer user_data, GObject* object) {
FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data);
self->engine = nullptr;
+
+ // Disconnect any handlers
+ g_hash_table_remove_all(self->platform_message_handlers);
}
static gboolean fl_binary_messenger_platform_message_cb(
@@ -151,7 +159,7 @@
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
fl_engine_set_platform_message_handler(
- engine, fl_binary_messenger_platform_message_cb, self);
+ engine, fl_binary_messenger_platform_message_cb, self, NULL);
return self;
}
@@ -160,13 +168,25 @@
FlBinaryMessenger* self,
const gchar* channel,
FlBinaryMessengerMessageHandler handler,
- gpointer user_data) {
+ gpointer user_data,
+ GDestroyNotify destroy_notify) {
g_return_if_fail(FL_IS_BINARY_MESSENGER(self));
g_return_if_fail(channel != nullptr);
+ // Don't set handlers if engine already gone
+ if (self->engine == nullptr) {
+ g_warning(
+ "Attempted to set message handler on closed FlBinaryMessenger without "
+ "engine");
+ if (destroy_notify != nullptr)
+ destroy_notify(user_data);
+ return;
+ }
+
if (handler != nullptr)
- g_hash_table_replace(self->platform_message_handlers, g_strdup(channel),
- platform_message_handler_new(handler, user_data));
+ g_hash_table_replace(
+ self->platform_message_handlers, g_strdup(channel),
+ platform_message_handler_new(handler, user_data, destroy_notify));
else
g_hash_table_remove(self->platform_message_handlers, channel);
}
diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc
index d40b14d..df27c56 100644
--- a/shell/platform/linux/fl_engine.cc
+++ b/shell/platform/linux/fl_engine.cc
@@ -29,6 +29,7 @@
// Function to call when a platform message is received
FlEnginePlatformMessageHandler platform_message_handler;
gpointer platform_message_handler_data;
+ GDestroyNotify platform_message_handler_destroy_notify;
};
G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error)
@@ -161,6 +162,12 @@
g_clear_object(&self->renderer);
g_clear_object(&self->binary_messenger);
+ if (self->platform_message_handler_destroy_notify)
+ self->platform_message_handler_destroy_notify(
+ self->platform_message_handler_data);
+ self->platform_message_handler_data = nullptr;
+ self->platform_message_handler_destroy_notify = nullptr;
+
G_OBJECT_CLASS(fl_engine_parent_class)->dispose(object);
}
@@ -240,12 +247,18 @@
void fl_engine_set_platform_message_handler(
FlEngine* self,
FlEnginePlatformMessageHandler handler,
- gpointer user_data) {
+ gpointer user_data,
+ GDestroyNotify destroy_notify) {
g_return_if_fail(FL_IS_ENGINE(self));
g_return_if_fail(handler != nullptr);
+ if (self->platform_message_handler_destroy_notify)
+ self->platform_message_handler_destroy_notify(
+ self->platform_message_handler_data);
+
self->platform_message_handler = handler;
self->platform_message_handler_data = user_data;
+ self->platform_message_handler_destroy_notify = destroy_notify;
}
gboolean fl_engine_send_platform_message_response(
diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h
index 1007cfe..0d5216f 100644
--- a/shell/platform/linux/fl_engine_private.h
+++ b/shell/platform/linux/fl_engine_private.h
@@ -60,6 +60,8 @@
* @engine: an #FlEngine.
* @handler: function to call when a platform message is received.
* @user_data: (closure): user data to pass to @handler.
+ * @destroy_notify: (allow-none): a function which gets called to free
+ * @user_data, or %NULL.
*
* Registers the function called when a platform message is reveived. Call
* fl_engine_send_platform_message_response() with the response to this message.
@@ -70,7 +72,8 @@
void fl_engine_set_platform_message_handler(
FlEngine* engine,
FlEnginePlatformMessageHandler handler,
- gpointer user_data);
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
/**
* fl_engine_start:
diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc
index c8d61fa..b2ea8dc 100644
--- a/shell/platform/linux/fl_method_channel.cc
+++ b/shell/platform/linux/fl_method_channel.cc
@@ -16,6 +16,9 @@
// Messenger to communicate on
FlBinaryMessenger* messenger;
+ // TRUE if the channel has been closed
+ gboolean channel_closed;
+
// Channel name
gchar* name;
@@ -25,6 +28,7 @@
// Function called when a method call is received
FlMethodChannelMethodCallHandler method_call_handler;
gpointer method_call_handler_data;
+ GDestroyNotify method_call_handler_destroy_notify;
};
// Added here to stop the compiler from optimising this function away
@@ -65,17 +69,37 @@
g_task_return_pointer(task, result, g_object_unref);
}
+// Called when the channel handler is closed
+static void channel_closed_cb(gpointer user_data) {
+ g_autoptr(FlMethodChannel) self = FL_METHOD_CHANNEL(user_data);
+
+ self->channel_closed = TRUE;
+
+ // Disconnect handler
+ if (self->method_call_handler_destroy_notify != nullptr)
+ self->method_call_handler_destroy_notify(self->method_call_handler_data);
+ self->method_call_handler = nullptr;
+ self->method_call_handler_data = nullptr;
+ self->method_call_handler_destroy_notify = nullptr;
+}
+
static void fl_method_channel_dispose(GObject* object) {
FlMethodChannel* self = FL_METHOD_CHANNEL(object);
if (self->messenger != nullptr)
fl_binary_messenger_set_message_handler_on_channel(
- self->messenger, self->name, nullptr, nullptr);
+ self->messenger, self->name, nullptr, nullptr, nullptr);
g_clear_object(&self->messenger);
g_clear_pointer(&self->name, g_free);
g_clear_object(&self->codec);
+ if (self->method_call_handler_destroy_notify != nullptr)
+ self->method_call_handler_destroy_notify(self->method_call_handler_data);
+ self->method_call_handler = nullptr;
+ self->method_call_handler_data = nullptr;
+ self->method_call_handler_destroy_notify = nullptr;
+
G_OBJECT_CLASS(fl_method_channel_parent_class)->dispose(object);
}
@@ -101,7 +125,8 @@
self->codec = FL_METHOD_CODEC(g_object_ref(codec));
fl_binary_messenger_set_message_handler_on_channel(
- self->messenger, self->name, message_cb, self);
+ self->messenger, self->name, message_cb, g_object_ref(self),
+ channel_closed_cb);
return self;
}
@@ -109,11 +134,24 @@
G_MODULE_EXPORT void fl_method_channel_set_method_call_handler(
FlMethodChannel* self,
FlMethodChannelMethodCallHandler handler,
- gpointer user_data) {
+ gpointer user_data,
+ GDestroyNotify destroy_notify) {
g_return_if_fail(FL_IS_METHOD_CHANNEL(self));
+ // Don't set handler if channel closed
+ if (self->channel_closed) {
+ g_warning("Attempted to set method call handler on closed FlMethodChannel");
+ if (destroy_notify != nullptr)
+ destroy_notify(user_data);
+ return;
+ }
+
+ if (self->method_call_handler_destroy_notify != nullptr)
+ self->method_call_handler_destroy_notify(self->method_call_handler_data);
+
self->method_call_handler = handler;
self->method_call_handler_data = user_data;
+ self->method_call_handler_destroy_notify = destroy_notify;
}
G_MODULE_EXPORT void fl_method_channel_invoke_method(
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 870b09c..b9fec01 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
@@ -70,7 +70,8 @@
* g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new ();
* channel = fl_basic_message_channel_new (messenger, "flutter/foo",
* FL_MESSAGE_CODEC (codec));
- * fl_basic_message_channel_set_message_handler (channel, message_cb, NULL);
+ * fl_basic_message_channel_set_message_handler (channel, message_cb, NULL,
+ * NULL);
*
* g_autoptr(FlValue) message = fl_value_new_string ("Hello World");
* fl_basic_message_channel_send (channel, message, NULL,
@@ -131,13 +132,21 @@
* @handler: (allow-none): function to call when a message is received on this
* channel or %NULL to disable the handler.
* @user_data: (closure): user data to pass to @handler.
+ * @destroy_notify: (allow-none): a function which gets called to free
+ * @user_data, or %NULL.
*
- * Sets the function called when a message is received.
+ * Sets the function called when a message is received from the Dart side of the
+ * channel. See #FlBasicMessageChannelMessageHandler for details on how to
+ * respond to messages.
+ *
+ * The handler is removed if the channel is closed or is replaced by another
+ * handler, set @destroy_notify if you want to detect this.
*/
void fl_basic_message_channel_set_message_handler(
FlBasicMessageChannel* channel,
FlBasicMessageChannelMessageHandler handler,
- gpointer user_data);
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
/**
* fl_basic_message_channel_respond:
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 6792fcd..4f73986 100644
--- a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
+++ b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
@@ -83,17 +83,22 @@
* @handler: (allow-none): function to call when a message is received on this
* channel or %NULL to disable a handler
* @user_data: (closure): user data to pass to @handler.
+ * @destroy_notify: (allow-none): a function which gets called to free
+ * @user_data, or %NULL.
*
* Sets the function called when a platform message is received on the given
- * channel. Call fl_binary_messenger_send_response() when the message is
- * handled. Ownership of #FlBinaryMessengerResponseHandle is transferred to the
- * caller, and the call must be responded to to avoid memory leaks.
+ * channel. See #FlBinaryMessengerMessageHandler for details on how to respond
+ * to messages.
+ *
+ * The handler is removed if the channel is closed or is replaced by another
+ * handler, set @destroy_notify if you want to detect this.
*/
void fl_binary_messenger_set_message_handler_on_channel(
FlBinaryMessenger* messenger,
const gchar* channel,
FlBinaryMessengerMessageHandler handler,
- gpointer user_data);
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
/**
* fl_binary_messenger_send_response:
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_channel.h b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
index 0ef04ff..f4102ed 100644
--- a/shell/platform/linux/public/flutter_linux/fl_method_channel.h
+++ b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
@@ -84,7 +84,8 @@
* g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new ();
* channel =
* fl_method_channel_new(messenger, "flutter/foo", FL_METHOD_CODEC (codec));
- * fl_method_channel_set_method_call_handler (channel, method_call_cb, NULL);
+ * fl_method_channel_set_method_call_handler (channel, method_call_cb, NULL,
+ * NULL);
*
* g_autoptr(FlValue) args = fl_value_new_string ("Hello World");
* fl_method_channel_invoke_method (channel, "Foo.foo", args,
@@ -105,7 +106,7 @@
* Function called when a method call is received. Respond to the method call
* with fl_method_call_respond(). If the response is not occurring in this
* callback take a reference to @method_call and release that once it has been
- * responded to.Failing to respond before the last reference to @method_call is
+ * responded to. Failing to respond before the last reference to @method_call is
* dropped is a programming error.
*/
typedef void (*FlMethodChannelMethodCallHandler)(FlMethodChannel* channel,
@@ -132,14 +133,21 @@
* @channel: an #FlMethodChannel.
* @handler: function to call when a method call is received on this channel.
* @user_data: (closure): user data to pass to @handler.
+ * @destroy_notify: (allow-none): a function which gets called to free
+ * @user_data, or %NULL.
*
- * Sets the function called when a method is called. Call
- * fl_method_call_respond() when the method completes.
+ * Sets the function called when a method call is received from the Dart side of
+ * the channel. See #FlMethodChannelMethodCallHandler for details on how to
+ * respond to method calls.
+ *
+ * The handler is removed if the channel is closed or is replaced by another
+ * handler, set @destroy_notify if you want to detect this.
*/
void fl_method_channel_set_method_call_handler(
FlMethodChannel* channel,
FlMethodChannelMethodCallHandler handler,
- gpointer user_data);
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
/**
* fl_method_channel_invoke_method: