[Notifications Applet] Don't crash when containment() disappears

Authored by broulik on Apr 6 2017, 4:02 PM.



When deleting the panel the notifications applet is in, the containment is removed but the corona is still there and rightfully emits availableScreenRectChanged.
This will cause us to crash when we try to access the no-longer existing containment.

BUG: 378508
FIXED-IN: 5.8.7

Test Plan

Even though we no longer emit the signal when tearing down, we still do (correctly) when manually removing the panel. The containment goes away nonetheless.

No longer crashes when I remove the panel. Resizing the panel still repositions notification popups.

Diff Detail

R120 Plasma Workspace
Automatic diff as part of commit; lint not applicable.
Automatic diff as part of commit; unit tests not applicable.
broulik created this revision.Apr 6 2017, 4:02 PM
Restricted Application added a project: Plasma. · View Herald TranscriptApr 6 2017, 4:02 PM
Restricted Application added a subscriber: plasma-devel. · View Herald Transcript

Your reasoning behind the bug is missing some steps.

QObject destruction deletes children *then* emits QObject::destroyed
Applets are children of the containment. (see Containment::addApplet())

So if the notification applet is in the panel, it's fine (and won't crash)

What you actually have is: panel containment -> system tray -> system tray containment -> notification applet

System tray must be not setting applets as children of the system tray containment? Or not deleting it's containment when it deletes the applet?

i added the applet to a panel directly and it would crash without this patch, with is fine. Frankly i didn't test the in tray case.

davidedmundson accepted this revision.Apr 10 2017, 10:04 PM

Heh, finally found the bug.

I was a bit wrong.
QObject::destroyed() is emitted before we delete the children.

But that still doesn't explain everything, the containment won't get deleted till after the child applet is deleted; so why would containment() be null?
We know it's not deleted yet, so it's not really "gone" like your comment says,

The issue is that containment() is a qobject_cast recursive search of parents. (for some reason)

qobject_cast calls metaobject() which is virtual. Calling a virtual method during an objects destructor can result in unexpected behaviour.
Our parent is still valid, but we've called Containment::~Containment, Applet::~Applet and we're currently in QObject::~QObject, so when we call a method on a base class it doesn't get dynamically dispatched as it should.

So when we call parent()->metaObject() we now get the metaobject for QObject not the metaobject of Containment.

This means the qobject_cast fails, and that's why Applet::containment() is null even though the containment is still there.

This revision is now accepted and ready to land.Apr 10 2017, 10:04 PM
anthonyfieroni added inline comments.

I think, corona (witch is parent of containment) is still alive and that's why slot is invoked.

broulik added inline comments.Apr 11 2017, 1:26 PM

Obviously, the corona manages all the containments, we only delete a single containment, though.

RE: your message on IRC this morning.

Yes, you should submit this.

I explained in my comment why it's needed. It's not quite for the reason you initially said, but the solution is still valid.
Ideally fix your comment about the containment being gone.

This revision was automatically updated to reflect the committed changes.