[gnome-shell/gnome-3-28] dnd: Repick target actor if destroyed mid iteration



commit fa2ddcc52f5dc5bda1336abcbaed1bf30234a465
Author: Jonas Ådahl <jadahl gmail com>
Date:   Thu Jan 3 11:53:13 2019 +0100

    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 4259676f6e09de275d29dcae5b00026f26faa9ab)

 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 a38607c24..ec1ba1d41 100644
--- a/js/ui/dnd.js
+++ b/js/ui/dnd.js
@@ -396,10 +396,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,
@@ -407,6 +412,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) {
@@ -417,6 +434,7 @@ var _Draggable = new Lang.Class({
                 }
             }
         }
+        dragEvent.targetActor.disconnect(targetActorDestroyHandlerId);
 
         while (target) {
             if (target._delegate && target._delegate.handleDragOver) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]