Evas clip: Fix rare crash in _render_pre_clipper_change

Use delete callback instead of direct call to clip_unset,
which lets us know that clip_unset() is called during the
clipper's deletion, as opposed to a simple call.

We can then make sure that the previous object state does
not point to invalid data anymore.

Here is a scenario that could have crashed:
- load and show an edje object, hide it
- change its theme or style
- show it again

@fix
This commit is contained in:
Jean-Philippe Andre 2016-01-28 12:18:34 +09:00
parent cbf2aa2bf9
commit 100a7006b8
3 changed files with 71 additions and 18 deletions

View File

@ -213,6 +213,8 @@ _evas_object_clip_mask_unset(Evas_Object_Protected_Data *obj)
extern const char *o_rect_type;
extern const char *o_image_type;
static Eina_Bool _clipper_del_cb(void *data, Eo *eo_clip, const Eo_Event_Description *desc EINA_UNUSED, void *info EINA_UNUSED);
EOLIAN void
_evas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object *eo_clip)
{
@ -281,6 +283,8 @@ _evas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object *
}
if (obj->cur->clipper)
{
Evas_Object_Protected_Data *old_clip = obj->cur->clipper;
/* unclip */
obj->cur->clipper->clip.cache_clipees_answer = eina_list_free(obj->cur->clipper->clip.cache_clipees_answer);
obj->cur->clipper->clip.clipees = eina_list_remove(obj->cur->clipper->clip.clipees, obj);
@ -320,10 +324,10 @@ _evas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object *
evas_object_change(eo_obj, obj);
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
{
state_write->clipper = NULL;
}
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
if (obj->prev->clipper != old_clip)
eo_do(old_clip->object, eo_event_callback_del(EO_BASE_EVENT_DEL, _clipper_del_cb, eo_obj));
}
/* image object clipper */
@ -350,11 +354,12 @@ _evas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object *
clip->cur->geometry.w, clip->cur->geometry.h);
*/
}
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
{
state_write->clipper = clip;
}
state_write->clipper = clip;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
if (obj->prev->clipper != clip)
eo_do(clip->object, eo_event_callback_add(EO_BASE_EVENT_DEL, _clipper_del_cb, eo_obj));
clip->clip.cache_clipees_answer = eina_list_free(clip->clip.cache_clipees_answer);
clip->clip.clipees = eina_list_append(clip->clip.clipees, obj);
@ -401,7 +406,6 @@ EOLIAN void
_evas_object_clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
{
if (!obj->cur->clipper) return;
evas_object_async_block(obj);
obj->clip.cache_clipees_answer = eina_list_free(obj->clip.cache_clipees_answer);
@ -413,6 +417,8 @@ _evas_object_clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
}
if (obj->cur->clipper)
{
Evas_Object_Protected_Data *old_clip = obj->cur->clipper;
obj->cur->clipper->clip.clipees = eina_list_remove(obj->cur->clipper->clip.clipees, obj);
if (!obj->cur->clipper->clip.clipees)
{
@ -445,12 +451,13 @@ _evas_object_clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
_evas_object_clip_mask_unset(obj->cur->clipper);
}
evas_object_change(obj->cur->clipper->object, obj->cur->clipper);
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
if (obj->prev->clipper != old_clip)
eo_do(old_clip->object, eo_event_callback_del(EO_BASE_EVENT_DEL, _clipper_del_cb, eo_obj));
}
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
{
state_write->clipper = NULL;
}
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
@ -470,6 +477,43 @@ _evas_object_clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
evas_object_clip_across_check(eo_obj, obj);
}
static Eina_Bool
_clipper_del_cb(void *data, Eo *eo_clip, const Eo_Event_Description *desc EINA_UNUSED, void *info EINA_UNUSED)
{
Evas_Object *eo_obj = data;
Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS);
if (!obj) return EO_CALLBACK_CONTINUE;
_evas_object_clip_unset(eo_obj, obj);
if (obj->prev->clipper && (obj->prev->clipper->object == eo_clip))
{
// not removing cb since it's the del cb... it can't be called again!
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, prev)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, prev);
}
return EO_CALLBACK_CONTINUE;
}
void
_evas_object_clip_prev_reset(Evas_Object_Protected_Data *obj, Eina_Bool cur_prev)
{
if (obj->prev->clipper)
{
Evas_Object_Protected_Data *clip = obj->prev->clipper;
if (!cur_prev)
{
EINA_COW_STATE_WRITE_BEGIN(obj->prev->clipper, state_write, prev)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj->prev->clipper, state_write, prev);
}
if (clip != obj->cur->clipper)
eo_do(clip->object, eo_event_callback_del(EO_BASE_EVENT_DEL, _clipper_del_cb, obj->object));
}
}
EOLIAN Eina_List *
_evas_object_clipees_get(Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj)
{

View File

@ -159,6 +159,7 @@ evas_object_cur_prev(Evas_Object *eo_obj)
map_write->prev = map_write->cur;
EINA_COW_WRITE_END(evas_object_map_cow, obj->map, map_write);
}
_evas_object_clip_prev_reset(obj, EINA_TRUE);
eina_cow_memcpy(evas_object_state_cow, (const Eina_Cow_Data **) &obj->prev, obj->cur);
}
@ -704,13 +705,18 @@ _evas_object_eo_base_destructor(Eo *eo_obj, Evas_Object_Protected_Data *obj)
goto end;
}
evas_object_grabs_cleanup(eo_obj, obj);
/* "while" should be used for null check of obj->clip.clipees,
because evas_objct_clip_unset can set null to obj->clip.clipees */
while (obj->clip.clipees)
if (obj->clip.clipees)
{
Evas_Object_Protected_Data *tmp;
tmp = eina_list_data_get(obj->clip.clipees);
evas_object_clip_unset(tmp->object);
ERR("object %p still has %d clippees after del callback",
eo_obj, eina_list_count(obj->clip.clipees));
/* "while" should be used for null check of obj->clip.clipees,
because evas_objct_clip_unset can set null to obj->clip.clipees */
while (obj->clip.clipees)
{
Evas_Object_Protected_Data *tmp;
tmp = eina_list_data_get(obj->clip.clipees);
evas_object_clip_unset(tmp->object);
}
}
EINA_LIST_FOREACH_SAFE(obj->proxy->proxies, l, l2, proxy)
{
@ -734,6 +740,8 @@ _evas_object_eo_base_destructor(Eo *eo_obj, Evas_Object_Protected_Data *obj)
}
if (obj->cur->clipper) evas_object_clip_unset(eo_obj);
_evas_object_clip_prev_reset(obj, EINA_FALSE);
evas_object_map_set(eo_obj, NULL);
if (obj->is_smart) evas_object_smart_del(eo_obj);
_evas_object_event_new();

View File

@ -1650,6 +1650,7 @@ void _above_get(Eo *obj, void *_pd, va_list *list);
void _below_get(Eo *obj, void *_pd, va_list *list);
void _smart_move_children_relative(Eo *obj, void *_pd, va_list *list);
void _smart_clipped_clipper_get(Eo *obj, void *_pd, va_list *list);
void _evas_object_clip_prev_reset(Evas_Object_Protected_Data *obj, Eina_Bool cur_prev);
void _canvas_event_default_flags_set(Eo *e, void *_pd, va_list *list);
void _canvas_event_default_flags_get(Eo *e, void *_pd, va_list *list);