From 4259676f6e09de275d29dcae5b00026f26faa9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 3 Jan 2019 11:53:13 +0100 Subject: [PATCH] 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 --- js/ui/dnd.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/js/ui/dnd.js b/js/ui/dnd.js index 8483e89fc..665881363 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -411,10 +411,15 @@ var _Draggable = new Lang.Class({ return true; }, + _pickTargetActor() { + return this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, + this._dragX, this._dragY); + }, + _updateDragHover() { this._updateHoverId = 0; - let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, - this._dragX, this._dragY); + let target = this._pickTargetActor(); + let dragEvent = { x: this._dragX, y: this._dragY, @@ -422,6 +427,18 @@ var _Draggable = new Lang.Class({ source: this.actor._delegate, 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++) { let motionFunc = dragMonitors[i].dragMotion; if (motionFunc) { @@ -432,6 +449,7 @@ var _Draggable = new Lang.Class({ } } } + dragEvent.targetActor.disconnect(targetActorDestroyHandlerId); while (target) { if (target._delegate && target._delegate.handleDragOver) {