Be careful about the list of event controllers

We can end up with _gtk_widget_remove_controller getting called
while we are iterating over the list in _gtk_widget_run_controllers.
To avoid trouble, only mark the event controller as dead by
setting data->controller to NULL, and defer the actual freeing
and list manipulation to the loop in _gtk_widget_run_controllers.
Update other places that operate on controllers to handle
data->controller being NULL.
This commit is contained in:
Matthias Clasen 2014-05-09 15:53:27 -04:00 committed by Carlos Garnacho
parent d97e4bf6b7
commit bad1dd4ac7

View File

@ -7256,7 +7256,8 @@ _gtk_widget_get_controllers_evmask (GtkWidget *widget)
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
evmask |= gtk_event_controller_get_event_mask (GTK_EVENT_CONTROLLER (data->controller));
if (data->controller)
evmask |= gtk_event_controller_get_event_mask (GTK_EVENT_CONTROLLER (data->controller));
}
return evmask;
@ -7274,12 +7275,21 @@ _gtk_widget_run_controllers (GtkWidget *widget,
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
l = priv->event_controllers;
while (l != NULL)
{
GList *next = l->next;
data = l->data;
if (phase == data->phase)
if (data->controller == NULL)
{
priv->event_controllers = g_list_delete_link (priv->event_controllers, l);
g_free (data);
}
else if (data->phase == phase)
handled |= gtk_event_controller_handle_event (data->controller, event);
l = next;
}
g_object_unref (widget);
@ -11748,6 +11758,7 @@ gtk_widget_dispose (GObject *object)
{
GtkWidget *widget = GTK_WIDGET (object);
GtkWidgetPrivate *priv = widget->priv;
GList *l;
if (priv->parent)
gtk_container_remove (GTK_CONTAINER (priv->parent), widget);
@ -11770,11 +11781,14 @@ gtk_widget_dispose (GObject *object)
while (priv->attached_windows)
gtk_window_set_attached_to (priv->attached_windows->data, NULL);
while (priv->event_controllers)
for (l = priv->event_controllers; l; l = l->next)
{
EventControllerData *data = priv->event_controllers->data;
_gtk_widget_remove_controller (widget, data->controller);
EventControllerData *data = l->data;
if (data->controller)
_gtk_widget_remove_controller (widget, data->controller);
}
g_list_free_full (priv->event_controllers, g_free);
priv->event_controllers = NULL;
G_OBJECT_CLASS (gtk_widget_parent_class)->dispose (object);
}
@ -16826,19 +16840,15 @@ _gtk_widget_remove_controller (GtkWidget *widget,
GtkEventController *controller)
{
EventControllerData *data;
GtkWidgetPrivate *priv;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
priv = widget->priv;
data = _gtk_widget_has_controller (widget, controller);
if (!data)
return;
priv->event_controllers = g_list_remove (priv->event_controllers, data);
if (g_signal_handler_is_connected (widget, data->grab_notify_id))
g_signal_handler_disconnect (widget, data->grab_notify_id);
@ -16846,5 +16856,5 @@ _gtk_widget_remove_controller (GtkWidget *widget,
g_signal_handler_disconnect (data->controller, data->sequence_state_changed_id);
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (data->controller));
g_object_unref (data->controller);
g_free (data);
data->controller = NULL;
}