Blob Blame History Raw
From a6239811610a036af57a3f8f41e13658d0a5be8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
Date: Mon, 12 Aug 2019 16:25:48 +0200
Subject: [PATCH 1/6] workspacesDisplay: Disconnect MetaLater and parent
 signals on destroy

When the WorkspacesDisplay actor is destroyed we should remove the
ongoing later and parent widget connections to avoid accessing an
invalid object on callback.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/700
---
 js/ui/workspacesView.js | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index dd7bb0e2a..5dea68f5d 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -470,60 +470,76 @@ var WorkspacesDisplay = new Lang.Class({
         panAction.connect('gesture-cancel', () => {
             clickAction.release();
             for (let i = 0; i < this._workspacesViews.length; i++)
                 this._workspacesViews[i].endSwipeScroll();
         });
         panAction.connect('gesture-end', () => {
             clickAction.release();
             for (let i = 0; i < this._workspacesViews.length; i++)
                 this._workspacesViews[i].endSwipeScroll();
         });
         Main.overview.addAction(panAction);
         this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
 
         this._primaryIndex = Main.layoutManager.primaryIndex;
 
         this._workspacesViews = [];
         this._primaryScrollAdjustment = null;
 
         this._settings = new Gio.Settings({ schema_id: OVERRIDE_SCHEMA });
         this._settings.connect('changed::workspaces-only-on-primary',
                                this._workspacesOnlyOnPrimaryChanged.bind(this));
         this._workspacesOnlyOnPrimaryChanged();
 
         this._switchWorkspaceNotifyId = 0;
 
         this._notifyOpacityId = 0;
         this._scrollEventId = 0;
         this._keyPressEventId = 0;
 
         this._fullGeometry = null;
+
+        this.actor.connect('destroy', this._onDestroy.bind(this));
+    },
+
+    _onDestroy() {
+        if (this._notifyOpacityId) {
+            let parent = this.actor.get_parent();
+            if (parent)
+                parent.disconnect(this._notifyOpacityId);
+            this._notifyOpacityId = 0;
+        }
+
+        if (this._parentSetLater) {
+            Meta.later_remove(this._parentSetLater);
+            this._parentSetLater = 0;
+        }
     },
 
     _onPan(action) {
         let [dist, dx, dy] = action.get_motion_delta(0);
         let adjustment = this._scrollAdjustment;
         if (global.screen.layout_rows == -1)
             adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
         else if (this.actor.text_direction == Clutter.TextDirection.RTL)
             adjustment.value += (dx / this.actor.width) * adjustment.page_size;
         else
             adjustment.value -= (dx / this.actor.width) * adjustment.page_size;
         return false;
     },
 
     navigateFocus(from, direction) {
         return this._getPrimaryView().actor.navigate_focus(from, direction, false);
     },
 
     show(fadeOnPrimary) {
         this._updateWorkspacesViews();
         for (let i = 0; i < this._workspacesViews.length; i++) {
             let animationType;
             if (fadeOnPrimary && i == this._primaryIndex)
                 animationType = AnimationType.FADE;
             else
                 animationType = AnimationType.ZOOM;
             this._workspacesViews[i].animateToOverview(animationType);
         }
 
         this._restackedNotifyId =
@@ -612,61 +628,65 @@ var WorkspacesDisplay = new Lang.Class({
             if (!adjustment)
                 continue;
 
             // the adjustments work in terms of workspaces, so the
             // values map directly
             adjustment.value = this._scrollAdjustment.value;
         }
     },
 
     _getMonitorIndexForEvent(event) {
         let [x, y] = event.get_coords();
         let rect = new Meta.Rectangle({ x: x, y: y, width: 1, height: 1 });
         return global.screen.get_monitor_index_for_rect(rect);
     },
 
     _getPrimaryView() {
         if (!this._workspacesViews.length)
             return null;
         return this._workspacesViews[this._primaryIndex];
     },
 
     activeWorkspaceHasMaximizedWindows() {
         return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
     },
 
     _parentSet(actor, oldParent) {
         if (oldParent && this._notifyOpacityId)
             oldParent.disconnect(this._notifyOpacityId);
         this._notifyOpacityId = 0;
 
-        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+        if (this._parentSetLater)
+            return;
+
+        this._parentSetLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+            this._parentSetLater = 0;
             let newParent = this.actor.get_parent();
             if (!newParent)
                 return;
 
             // This is kinda hackish - we want the primary view to
             // appear as parent of this.actor, though in reality it
             // is added directly to Main.layoutManager.overviewGroup
             this._notifyOpacityId = newParent.connect('notify::opacity', () => {
                 let opacity = this.actor.get_parent().opacity;
                 let primaryView = this._getPrimaryView();
                 if (!primaryView)
                     return;
                 primaryView.actor.opacity = opacity;
                 primaryView.actor.visible = opacity != 0;
             });
         });
     },
 
     // This geometry should always be the fullest geometry
     // the workspaces switcher can ever be allocated, as if
     // the sliding controls were never slid in at all.
     setWorkspacesFullGeometry(geom) {
         this._fullGeometry = geom;
         this._updateWorkspacesFullGeometry();
     },
 
     _updateWorkspacesFullGeometry() {
         if (!this._workspacesViews.length)
             return;
 
-- 
2.26.2