clutter/actor: Remove transitions when removing an effect

If there are any in-progress transitions on any properties of the
effect, these will cause a crash next time they tick and update, as they
will try to access a `@effects.${effect_name}.${property_name}` property
on the `ClutterActor` which no longer resolves to an effect. In some
cases this will be because `priv->effects` itself is now `NULL` on the
`ClutterActor`.

This can be triggered by rapidly toggling screen time limits on and off
in gnome-shell with a low screen time limit which has already been
reached for the day. It will alternately add a desaturation effect and
fade-in transition, then remove the effect, then the transition will
update and crash.

Avoid this by removing relevant transitions when removing an effect.

Do the same for the other two groups of metas: constraints and actions,
as they will be subject to the same bug (under different reproducer
conditions). And the same for when any of these three meta groups are
cleared, as that could also trigger the same bug.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/8168
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4222>
This commit is contained in:
Philip Withnall 2025-01-20 15:18:43 +00:00 committed by Marge Bot
parent b674048184
commit c250f602bd

View File

@ -3251,6 +3251,57 @@ cull_actor (ClutterActor *self,
return TRUE;
}
/* Remove any transitions on properties with @prefix. */
static void
_clutter_actor_remove_transitions_for_prefix (ClutterActor *actor,
const char *prefix)
{
ClutterAnimationInfo *info;
info = _clutter_actor_get_animation_info (actor);
if (info->transitions != NULL)
{
GHashTableIter iter;
gpointer key, value;
g_autoptr (GPtrArray) to_remove = g_ptr_array_new_with_free_func (NULL);
g_hash_table_iter_init (&iter, info->transitions);
while (g_hash_table_iter_next (&iter, &key, &value))
{
if (g_str_has_prefix (key, prefix))
g_ptr_array_add (to_remove, key);
}
for (unsigned int i = 0; i < to_remove->len; i++)
clutter_actor_remove_transition (actor, to_remove->pdata[i]);
}
}
/* Remove any transitions on the properties of @meta.
* @section should be "actions", "constraints" or "effects" */
static void
_clutter_actor_remove_transitions_for_meta_internal (ClutterActor *actor,
const char *section,
ClutterActorMeta *meta)
{
g_autofree char *meta_prefix =
g_strdup_printf ("@%s.%s.", section,
clutter_actor_meta_get_name (meta));
_clutter_actor_remove_transitions_for_prefix (actor, meta_prefix);
}
/* Remove any transitions on the properties of any #ClutterActorMeta in @section.
* @section should be "actions", "constraints" or "effects" */
static void
_clutter_actor_remove_transitions_for_meta_section_internal (ClutterActor *actor,
const char *section)
{
g_autofree char *meta_prefix = g_strdup_printf ("@%s.", section);
_clutter_actor_remove_transitions_for_prefix (actor, meta_prefix);
}
/* This is the same as clutter_actor_add_effect except that it doesn't
queue a redraw and it doesn't notify on the effect property */
static void
@ -3279,6 +3330,10 @@ _clutter_actor_remove_effect_internal (ClutterActor *self,
if (priv->effects == NULL)
return;
/* Remove any transitions on the effects properties. */
_clutter_actor_remove_transitions_for_meta_internal (self, "effects",
CLUTTER_ACTOR_META (effect));
_clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
@ -13728,6 +13783,10 @@ clutter_actor_remove_action (ClutterActor *self,
if (priv->actions == NULL)
return;
/* Remove any transitions on the actionss properties. */
_clutter_actor_remove_transitions_for_meta_internal (self, "actions",
CLUTTER_ACTOR_META (action));
_clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
@ -13763,6 +13822,9 @@ clutter_actor_remove_action_by_name (ClutterActor *self,
if (meta == NULL)
return;
/* Remove any transitions on the actionss properties. */
_clutter_actor_remove_transitions_for_meta_internal (self, "actions", meta);
_clutter_meta_group_remove_meta (priv->actions, meta);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
@ -13829,6 +13891,7 @@ clutter_actor_clear_actions (ClutterActor *self)
if (self->priv->actions == NULL)
return;
_clutter_actor_remove_transitions_for_meta_section_internal (self, "actions");
_clutter_meta_group_clear_metas_no_internal (self->priv->actions);
}
@ -13920,6 +13983,10 @@ clutter_actor_remove_constraint (ClutterActor *self,
if (priv->constraints == NULL)
return;
/* Remove any transitions on the constraints properties. */
_clutter_actor_remove_transitions_for_meta_internal (self, "constraints",
CLUTTER_ACTOR_META (constraint));
_clutter_meta_group_remove_meta (priv->constraints,
CLUTTER_ACTOR_META (constraint));
@ -13958,6 +14025,9 @@ clutter_actor_remove_constraint_by_name (ClutterActor *self,
if (meta == NULL)
return;
/* Remove any transitions on the constraints properties. */
_clutter_actor_remove_transitions_for_meta_internal (self, "constraints", meta);
_clutter_meta_group_remove_meta (priv->constraints, meta);
clutter_actor_queue_relayout (self);
}
@ -14023,6 +14093,7 @@ clutter_actor_clear_constraints (ClutterActor *self)
if (self->priv->constraints == NULL)
return;
_clutter_actor_remove_transitions_for_meta_section_internal (self, "constraints");
_clutter_meta_group_clear_metas_no_internal (self->priv->constraints);
clutter_actor_queue_relayout (self);
@ -14248,6 +14319,7 @@ clutter_actor_clear_effects (ClutterActor *self)
if (self->priv->effects == NULL)
return;
_clutter_actor_remove_transitions_for_meta_section_internal (self, "effects");
_clutter_meta_group_clear_metas_no_internal (self->priv->effects);
clutter_actor_queue_redraw (self);