Skip to content

Commit

Permalink
Fixed|libdeng2|Widget: Crash when notifying a tree
Browse files Browse the repository at this point in the history
If a widget tree was modified while it was being notified, a crash
could occur because the widget list used for iteration had become
invalid.
  • Loading branch information
skyjake committed Nov 20, 2013
1 parent fb6d16d commit 6ebf284
Showing 1 changed file with 30 additions and 9 deletions.
39 changes: 30 additions & 9 deletions doomsday/libdeng2/src/widgets/widget.cpp
Expand Up @@ -277,9 +277,13 @@ Widget &Widget::insertBefore(Widget *child, Widget const &otherChild)
Widget *Widget::remove(Widget &child)
{
DENG2_ASSERT(child.d->parent == this);
child.d->parent = 0;
DENG2_ASSERT(d->children.contains(&child));

child.d->parent = 0;
d->children.removeOne(&child);

DENG2_ASSERT(!d->children.contains(&child));

if(!child.name().isEmpty())
{
d->index.remove(child.name());
Expand Down Expand Up @@ -365,18 +369,19 @@ String Widget::uniqueName(String const &name) const
Widget::NotifyArgs::Result Widget::notifyTree(NotifyArgs const &args)
{
NotifyArgs::Result result = NotifyArgs::Continue;

bool preNotified = false;

DENG2_FOR_EACH_CONST(Instance::Children, i, d->children)
for(int idx = 0; idx < d->children.size(); ++idx)
{
if(*i == args.until)
Widget *i = d->children.at(idx);

if(i == args.until)
{
result = NotifyArgs::Abort;
break;
}

if(args.conditionFunc && !((*i)->*args.conditionFunc)())
if(args.conditionFunc && !(i->*args.conditionFunc)())
continue; // Skip this one.

if(args.preNotifyFunc && !preNotified)
Expand All @@ -385,12 +390,28 @@ Widget::NotifyArgs::Result Widget::notifyTree(NotifyArgs const &args)
(this->*args.preNotifyFunc)();
}

((*i)->*args.notifyFunc)();
(i->*args.notifyFunc)();

if((*i)->notifyTree(args) == NotifyArgs::Abort)
if(i != d->children.at(idx))
{
result = NotifyArgs::Abort;
break;
// The list of children was modified; let's update the current
// index accordingly.
idx = d->children.indexOf(i);

// The current widget cannot be removed.
DENG2_ASSERT(idx >= 0);

i = d->children.at(idx);
}

// Continue down the tree by notifying any children of this widget.
if(i->childCount())
{
if(i->notifyTree(args) == NotifyArgs::Abort)
{
result = NotifyArgs::Abort;
break;
}
}
}

Expand Down

0 comments on commit 6ebf284

Please sign in to comment.