dnd: Repick target actor if destroyed mid iteration

The picked target actor may be destroyed (e.g. hover style change
resulting in the ClutterTexture to be destroyed). If we don't handle
this, GJS will abort when it sees the exception caused by Javascript
code trying to access the destroyed target actor.

To handle it, listen on the 'destroy' signal on the target actor, and
repick, so a valid actor is passed to the next motion callback.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/632

(cherry picked from commit 4259676f6e)
This commit is contained in:
Jonas Ådahl 2019-01-03 11:53:13 +01:00 committed by Marco Trevisan (Treviño)
parent fbf194f6a1
commit fa2ddcc52f

View File

@ -396,10 +396,15 @@ var _Draggable = new Lang.Class({
return true; return true;
}, },
_pickTargetActor() {
return this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
},
_updateDragHover() { _updateDragHover() {
this._updateHoverId = 0; this._updateHoverId = 0;
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, let target = this._pickTargetActor();
this._dragX, this._dragY);
let dragEvent = { let dragEvent = {
x: this._dragX, x: this._dragX,
y: this._dragY, y: this._dragY,
@ -407,6 +412,18 @@ var _Draggable = new Lang.Class({
source: this.actor._delegate, source: this.actor._delegate,
targetActor: target targetActor: target
}; };
let targetActorDestroyHandlerId;
let handleTargetActorDestroyClosure;
handleTargetActorDestroyClosure = () => {
target = this._pickTargetActor();
dragEvent.targetActor = target;
targetActorDestroyHandlerId =
target.connect('destroy', handleTargetActorDestroyClosure);
};
targetActorDestroyHandlerId =
target.connect('destroy', handleTargetActorDestroyClosure);
for (let i = 0; i < dragMonitors.length; i++) { for (let i = 0; i < dragMonitors.length; i++) {
let motionFunc = dragMonitors[i].dragMotion; let motionFunc = dragMonitors[i].dragMotion;
if (motionFunc) { if (motionFunc) {
@ -417,6 +434,7 @@ var _Draggable = new Lang.Class({
} }
} }
} }
dragEvent.targetActor.disconnect(targetActorDestroyHandlerId);
while (target) { while (target) {
if (target._delegate && target._delegate.handleDragOver) { if (target._delegate && target._delegate.handleDragOver) {