Blame SOURCES/0001-dnd-Nullify-_dragActor-after-we-ve-destroyed-it-and-.patch

bf7e93
From e0414e7cc7cc9e997659f297637f35d9202b6dab Mon Sep 17 00:00:00 2001
bf7e93
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
bf7e93
Date: Tue, 5 Dec 2017 22:41:17 +0100
bf7e93
Subject: [PATCH] dnd: Nullify _dragActor after we've destroyed it, and avoid
bf7e93
 invalid access
bf7e93
bf7e93
We need to avoid that we use the _dragActor instance after that it has
bf7e93
been destroyed or we'll get errors. We now set it to null when this
bf7e93
happens, protecting any access to that.
bf7e93
bf7e93
Add a DragState enum-like object to keep track of the state
bf7e93
instead of using booleans.
bf7e93
bf7e93
Remove duplicated handler on 'destroy' and just use a generic one.
bf7e93
bf7e93
https://bugzilla.gnome.org/show_bug.cgi?id=791233
bf7e93
---
bf7e93
 js/ui/dnd.js | 65 +++++++++++++++++++++++++++++++---------------------
bf7e93
 1 file changed, 39 insertions(+), 26 deletions(-)
bf7e93
bf7e93
diff --git a/js/ui/dnd.js b/js/ui/dnd.js
bf7e93
index a38607c24..431c60d6c 100644
bf7e93
--- a/js/ui/dnd.js
bf7e93
+++ b/js/ui/dnd.js
bf7e93
@@ -27,6 +27,12 @@ var DragMotionResult = {
bf7e93
     CONTINUE:  3
bf7e93
 };
bf7e93
 
bf7e93
+var DragState = {
bf7e93
+    INIT:      0,
bf7e93
+    DRAGGING:  1,
bf7e93
+    CANCELLED: 2,
bf7e93
+};
bf7e93
+
bf7e93
 var DRAG_CURSOR_MAP = {
bf7e93
     0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
bf7e93
     1: Meta.Cursor.DND_COPY,
bf7e93
@@ -78,6 +84,8 @@ var _Draggable = new Lang.Class({
bf7e93
                                         dragActorOpacity: undefined });
bf7e93
 
bf7e93
         this.actor = actor;
bf7e93
+        this._dragState = DragState.INIT;
bf7e93
+
bf7e93
         if (!params.manualMode) {
bf7e93
             this.actor.connect('button-press-event',
bf7e93
                                this._onButtonPress.bind(this));
bf7e93
@@ -88,7 +96,7 @@ var _Draggable = new Lang.Class({
bf7e93
         this.actor.connect('destroy', () => {
bf7e93
             this._actorDestroyed = true;
bf7e93
 
bf7e93
-            if (this._dragInProgress && this._dragCancellable)
bf7e93
+            if (this._dragState == DragState.DRAGGING && this._dragCancellable)
bf7e93
                 this._cancelDrag(global.get_current_time());
bf7e93
             this.disconnectAll();
bf7e93
         });
bf7e93
@@ -100,7 +108,6 @@ var _Draggable = new Lang.Class({
bf7e93
         this._dragActorOpacity = params.dragActorOpacity;
bf7e93
 
bf7e93
         this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
bf7e93
-        this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
bf7e93
         this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
bf7e93
         this._dragCancellable = true;
bf7e93
 
bf7e93
@@ -206,9 +213,10 @@ var _Draggable = new Lang.Class({
bf7e93
             (event.type() == Clutter.EventType.TOUCH_END &&
bf7e93
              global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
bf7e93
             this._buttonDown = false;
bf7e93
-            if (this._dragInProgress) {
bf7e93
+            if (this._dragState == DragState.DRAGGING) {
bf7e93
                 return this._dragActorDropped(event);
bf7e93
-            } else if (this._dragActor != null && !this._animationInProgress) {
bf7e93
+            } else if ((this._dragActor != null || this._dragState == DragState.CANCELLED) &&
bf7e93
+                       !this._animationInProgress) {
bf7e93
                 // Drag must have been cancelled with Esc.
bf7e93
                 this._dragComplete();
bf7e93
                 return Clutter.EVENT_STOP;
bf7e93
@@ -222,14 +230,14 @@ var _Draggable = new Lang.Class({
bf7e93
         } else if (event.type() == Clutter.EventType.MOTION ||
bf7e93
                    (event.type() == Clutter.EventType.TOUCH_UPDATE &&
bf7e93
                     global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
bf7e93
-            if (this._dragInProgress) {
bf7e93
+            if (this._dragActor && this._dragState == DragState.DRAGGING) {
bf7e93
                 return this._updateDragPosition(event);
bf7e93
-            } else if (this._dragActor == null) {
bf7e93
+            } else if (this._dragActor == null && this._dragState != DragState.CANCELLED) {
bf7e93
                 return this._maybeStartDrag(event);
bf7e93
             }
bf7e93
         // We intercept KEY_PRESS event so that we can process Esc key press to cancel
bf7e93
         // dragging and ignore all other key presses.
bf7e93
-        } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragInProgress) {
bf7e93
+        } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragState == DragState.DRAGGING) {
bf7e93
             let symbol = event.get_key_symbol();
bf7e93
             if (symbol == Clutter.Escape) {
bf7e93
                 this._cancelDrag(event.get_time());
bf7e93
@@ -265,7 +273,7 @@ var _Draggable = new Lang.Class({
bf7e93
      */
bf7e93
     startDrag(stageX, stageY, time, sequence) {
bf7e93
         currentDraggable = this;
bf7e93
-        this._dragInProgress = true;
bf7e93
+        this._dragState = DragState.DRAGGING;
bf7e93
 
bf7e93
         // Special-case St.Button: the pointer grab messes with the internal
bf7e93
         // state, so force a reset to a reasonable state here
bf7e93
@@ -342,6 +350,13 @@ var _Draggable = new Lang.Class({
bf7e93
             Shell.util_set_hidden_from_pick(this._dragActor, true);
bf7e93
         }
bf7e93
 
bf7e93
+        this._dragActorDestroyId = this._dragActor.connect('destroy', () => {
bf7e93
+            // Cancel ongoing animation (if any)
bf7e93
+            this._finishAnimation();
bf7e93
+
bf7e93
+            this._dragActor = null;
bf7e93
+            this._dragState = DragState.CANCELLED;
bf7e93
+        });
bf7e93
         this._dragOrigOpacity = this._dragActor.opacity;
bf7e93
         if (this._dragActorOpacity != undefined)
bf7e93
             this._dragActor.opacity = this._dragActorOpacity;
bf7e93
@@ -500,7 +515,7 @@ var _Draggable = new Lang.Class({
bf7e93
                                                 event.get_time())) {
bf7e93
                     // If it accepted the drop without taking the actor,
bf7e93
                     // handle it ourselves.
bf7e93
-                    if (this._dragActor.get_parent() == Main.uiGroup) {
bf7e93
+                    if (this._dragActor && this._dragActor.get_parent() == Main.uiGroup) {
bf7e93
                         if (this._restoreOnSuccess) {
bf7e93
                             this._restoreDragActor(event.get_time());
bf7e93
                             return true;
bf7e93
@@ -508,7 +523,7 @@ var _Draggable = new Lang.Class({
bf7e93
                             this._dragActor.destroy();
bf7e93
                     }
bf7e93
 
bf7e93
-                    this._dragInProgress = false;
bf7e93
+                    this._dragState = DragState.INIT;
bf7e93
                     global.screen.set_cursor(Meta.Cursor.DEFAULT);
bf7e93
                     this.emit('drag-end', event.get_time(), true);
bf7e93
                     this._dragComplete();
bf7e93
@@ -557,20 +572,22 @@ var _Draggable = new Lang.Class({
bf7e93
 
bf7e93
     _cancelDrag(eventTime) {
bf7e93
         this.emit('drag-cancelled', eventTime);
bf7e93
-        this._dragInProgress = false;
bf7e93
-        let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
bf7e93
+        let wasCancelled = (this._dragState == DragState.CANCELLED);
bf7e93
+        this._dragState = DragState.CANCELLED;
bf7e93
 
bf7e93
-        if (this._actorDestroyed) {
bf7e93
+        if (this._actorDestroyed || wasCancelled) {
bf7e93
             global.screen.set_cursor(Meta.Cursor.DEFAULT);
bf7e93
             if (!this._buttonDown)
bf7e93
                 this._dragComplete();
bf7e93
             this.emit('drag-end', eventTime, false);
bf7e93
-            if (!this._dragOrigParent)
bf7e93
+            if (!this._dragOrigParent && this._dragActor)
bf7e93
                 this._dragActor.destroy();
bf7e93
 
bf7e93
             return;
bf7e93
         }
bf7e93
 
bf7e93
+        let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
bf7e93
+
bf7e93
         this._animateDragEnd(eventTime,
bf7e93
                              { x: snapBackX,
bf7e93
                                y: snapBackY,
bf7e93
@@ -581,7 +598,7 @@ var _Draggable = new Lang.Class({
bf7e93
     },
bf7e93
 
bf7e93
     _restoreDragActor(eventTime) {
bf7e93
-        this._dragInProgress = false;
bf7e93
+        this._dragState = DragState.INIT;
bf7e93
         let [restoreX, restoreY, restoreScale] = this._getRestoreLocation();
bf7e93
 
bf7e93
         // fade the actor back in at its original location
bf7e93
@@ -596,12 +613,6 @@ var _Draggable = new Lang.Class({
bf7e93
     _animateDragEnd(eventTime, params) {
bf7e93
         this._animationInProgress = true;
bf7e93
 
bf7e93
-        // finish animation if the actor gets destroyed
bf7e93
-        // during it
bf7e93
-        this._dragActorDestroyId =
bf7e93
-            this._dragActor.connect('destroy',
bf7e93
-                                    this._finishAnimation.bind(this));
bf7e93
-
bf7e93
         params['opacity']          = this._dragOrigOpacity;
bf7e93
         params['transition']       = 'easeOutQuad';
bf7e93
         params['onComplete']       = this._onAnimationComplete;
bf7e93
@@ -624,9 +635,6 @@ var _Draggable = new Lang.Class({
bf7e93
     },
bf7e93
 
bf7e93
     _onAnimationComplete(dragActor, eventTime) {
bf7e93
-        dragActor.disconnect(this._dragActorDestroyId);
bf7e93
-        this._dragActorDestroyId = 0;
bf7e93
-
bf7e93
         if (this._dragOrigParent) {
bf7e93
             Main.uiGroup.remove_child(this._dragActor);
bf7e93
             this._dragOrigParent.add_actor(this._dragActor);
bf7e93
@@ -641,7 +649,7 @@ var _Draggable = new Lang.Class({
bf7e93
     },
bf7e93
 
bf7e93
     _dragComplete() {
bf7e93
-        if (!this._actorDestroyed)
bf7e93
+        if (!this._actorDestroyed && this._dragActor)
bf7e93
             Shell.util_set_hidden_from_pick(this._dragActor, false);
bf7e93
 
bf7e93
         this._ungrabEvents();
bf7e93
@@ -652,7 +660,12 @@ var _Draggable = new Lang.Class({
bf7e93
             this._updateHoverId = 0;
bf7e93
         }
bf7e93
 
bf7e93
-        this._dragActor = undefined;
bf7e93
+        if (this._dragActor) {
bf7e93
+            this._dragActor.disconnect(this._dragActorDestroyId);
bf7e93
+            this._dragActor = null;
bf7e93
+        }
bf7e93
+
bf7e93
+        this._dragState = DragState.INIT;
bf7e93
         currentDraggable = null;
bf7e93
     }
bf7e93
 });
bf7e93
-- 
bf7e93
2.19.1
bf7e93