Blob Blame History Raw
From 477e87ac1a1a99cd4d7a9ee7bb789975e9fc1fbf Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 28 Sep 2020 22:28:08 -0400
Subject: [PATCH 10/13] userWidget: Disconnect signal connections on destroy

At the moment an Avatar (and StIcon) are getting leaked
every time the screen is locked.

This is because the Avatar class sets up a global signal
connection that prevents the object from getting garbage
collected when its actor is destroyed.

This commit adds a destroy handler to disconnect the
signal connection.
---
 js/ui/userWidget.js | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/js/ui/userWidget.js b/js/ui/userWidget.js
index 4e1ce25e9..0a80fa672 100644
--- a/js/ui/userWidget.js
+++ b/js/ui/userWidget.js
@@ -9,63 +9,73 @@ const Gio = imports.gi.Gio;
 const GObject = imports.gi.GObject;
 const Lang = imports.lang;
 const St = imports.gi.St;
 
 const Params = imports.misc.params;
 
 var AVATAR_ICON_SIZE = 64;
 
 // Adapted from gdm/gui/user-switch-applet/applet.c
 //
 // Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
 // Copyright (C) 2008,2009 Red Hat, Inc.
 
 var Avatar = new Lang.Class({
     Name: 'Avatar',
 
     _init(user, params) {
         this._user = user;
         params = Params.parse(params, { reactive: false,
                                         iconSize: AVATAR_ICON_SIZE,
                                         styleClass: 'framed-user-icon' });
         this._iconSize = params.iconSize;
 
         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
         this.actor = new St.Bin({ style_class: params.styleClass,
                                   track_hover: params.reactive,
                                   reactive: params.reactive,
                                   width: this._iconSize * scaleFactor,
                                   height: this._iconSize * scaleFactor });
 
+        this.actor.connect('destroy', this._onDestroy.bind(this));
+
         // Monitor the scaling factor to make sure we recreate the avatar when needed.
         let themeContext = St.ThemeContext.get_for_stage(global.stage);
-        themeContext.connect('notify::scale-factor', this.update.bind(this));
+        this._scaleFactorChangedId = themeContext.connect('notify::scale-factor', this.update.bind(this));
+    },
+
+    _onDestroy() {
+        if (this._scaleFactorChangedId != 0) {
+            let themeContext = St.ThemeContext.get_for_stage(global.stage);
+            themeContext.disconnect(this._scaleFactorChangedId);
+            this._scaleFactorChangedId = 0;
+        }
     },
 
     setSensitive(sensitive) {
         this.actor.can_focus = sensitive;
         this.actor.reactive = sensitive;
     },
 
     update() {
         let iconFile = this._user.get_icon_file();
         if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
             iconFile = null;
 
         if (iconFile) {
             let file = Gio.File.new_for_path(iconFile);
             this.actor.child = null;
             this.actor.style = 'background-image: url("%s");'.format(iconFile);
         } else {
             this.actor.style = null;
             this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
                                              icon_size: this._iconSize });
         }
 
         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
         this.actor.set_size(this._iconSize * scaleFactor, this._iconSize * scaleFactor);
     }
 });
 
 var UserWidgetLabel = new Lang.Class({
     Name: 'UserWidgetLabel',
     Extends: St.Widget,
-- 
2.25.1