diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 1c25dfe39b35ffb3de55d089163195d0a102d48d..481934f69c9e4420483fb83ae829cecb01dec382 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -188,7 +188,7 @@ var BaseAppView = GObject.registerClass({ // Filter the apps through the user’s parental controls. this._parentalControlsManager = ParentalControlsManager.getDefault(); - this._parentalControlsManager.connect('app-filter-changed', () => { + this._appFilterChangedId = this._parentalControlsManager.connect('app-filter-changed', () => { this._redisplay(); }); @@ -206,6 +206,11 @@ var BaseAppView = GObject.registerClass({ } _onDestroy() { + if (this._appFilterChangedId) { + this._parentalControlsManager.disconnect(this._appFilterChangedId); + this._appFilterChangedId = 0; + } + this._removeDelayedMove(); this._disconnectDnD(); } @@ -1362,7 +1367,8 @@ var AppSearchProvider = class AppSearchProvider { let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { if (this._parentalControlsManager.initialized) { this._parentalControlsManager.disconnect(initializedId); - this.getInitialResultSet(terms, callback, _cancellable); + if (!_cancellable || !_cancellable.is_cancelled()) + this.getInitialResultSet(terms, callback, _cancellable); } }); return; diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js index 13d1d4019dc7ac9c00b19f214aa862083f3382b0..ef69b7be5c694ed4f5a2851f5b7ee5fc0b1beb66 100644 --- a/js/ui/dateMenu.js +++ b/js/ui/dateMenu.js @@ -121,9 +121,30 @@ class EventsSection extends St.Button { this.child.add_child(this._eventsList); this._appSys = Shell.AppSystem.get_default(); - this._appSys.connect('installed-changed', + this._installedChangedId = this._appSys.connect('installed-changed', this._appInstalledChanged.bind(this)); this._appInstalledChanged(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._installedChangedId) { + this._appSys.disconnect(this._installedChangedId); + this._installedChangedId = 0; + } + + if (this._eventSource) { + if (this._eventSourceChangedId) { + this._eventSource.disconnect(this._eventSourceChangedId); + this._eventSourceChangedId = 0; + } + if (this._eventSourceHasCalendarsId) { + this._eventSource.disconnect(this._eventSourceHasCalendarsId); + this._eventSourceHasCalendarsId = 0; + } + this._eventSource = null; + } } setDate(date) { @@ -139,9 +160,21 @@ class EventsSection extends St.Button { if (!(eventSource instanceof Calendar.EventSourceBase)) throw new Error('Event source is not valid type'); + if (this._eventSource) { + if (this._eventSourceChangedId) { + this._eventSource.disconnect(this._eventSourceChangedId); + this._eventSourceChangedId = 0; + } + if (this._eventSourceHasCalendarsId) { + this._eventSource.disconnect(this._eventSourceHasCalendarsId); + this._eventSourceHasCalendarsId = 0; + } + } + this._eventSource = eventSource; - this._eventSource.connect('changed', this._reloadEvents.bind(this)); - this._eventSource.connect('notify::has-calendars', + this._eventSourceChangedId = this._eventSource.connect('changed', + this._reloadEvents.bind(this)); + this._eventSourceHasCalendarsId = this._eventSource.connect('notify::has-calendars', this._sync.bind(this)); this._sync(); } @@ -293,24 +326,59 @@ class WorldClocksSection extends St.Button { this.child = this._grid; this._clocksApp = null; + this._cancellable = new Gio.Cancellable(); this._clocksProxy = new ClocksProxy( Gio.DBus.session, 'org.gnome.clocks', '/org/gnome/clocks', this._onProxyReady.bind(this), - null /* cancellable */, + this._cancellable, Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES); this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.world-clocks', }); - this._settings.connect('changed', this._clocksChanged.bind(this)); + this._clocksChangedId = this._settings.connect('changed', this._clocksChanged.bind(this)); this._clocksChanged(); this._appSystem = Shell.AppSystem.get_default(); - this._appSystem.connect('installed-changed', + this._installedChangedId = this._appSystem.connect('installed-changed', this._sync.bind(this)); this._sync(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._proxyPropertiesChangedId) { + this._clocksProxy.disconnect(this._proxyPropertiesChangedId); + this._proxyPropertiesChangedId = 0; + } + + if (this._installedChangedId) { + this._appSys.disconnect(this._installedChangedId); + this._installedChangedId = 0; + } + + if (this._clocksChangedId) { + this._settings.disconnect(this._clocksChangedId); + this._clocksChangedId = 0; + } + + if (this._clockNotifyId) { + this._clock.disconnect(this._clockNotifyId); + this._clockNotifyId = 0; + } + + if (this._tzNotifyId) { + this._clock.disconnect(this._tzNotifyId); + this._tzNotifyId = 0; + } } vfunc_clicked() { @@ -452,7 +520,7 @@ class WorldClocksSection extends St.Button { return; } - this._clocksProxy.connect('g-properties-changed', + this._proxyPropertiesChangedId = this._clocksProxy.connect('g-properties-changed', this._onClocksPropertiesChanged.bind(this)); this._onClocksPropertiesChanged(); } @@ -510,8 +578,17 @@ class WeatherSection extends St.Button { layout.hookup_style(this._forecastGrid); box.add_child(this._forecastGrid); - this._weatherClient.connect('changed', this._sync.bind(this)); + this._weatherChangedId = this._weatherClient.connect('changed', this._sync.bind(this)); this._sync(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._weatherChangedId) { + this._weatherClient.disconnect(this._weatherChangedId); + this._weatherChangedId = 0; + } } vfunc_map() { @@ -669,43 +746,70 @@ class MessagesIndicator extends St.Icon { y_align: Clutter.ActorAlign.CENTER, }); - this._sources = []; + this._sources = {}; this._count = 0; this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications', }); - this._settings.connect('changed::show-banners', this._sync.bind(this)); + this._showBannersChangedId = this._settings.connect('changed::show-banners', this._sync.bind(this)); - Main.messageTray.connect('source-added', this._onSourceAdded.bind(this)); - Main.messageTray.connect('source-removed', this._onSourceRemoved.bind(this)); - Main.messageTray.connect('queue-changed', this._updateCount.bind(this)); + this._sourceAddedId = Main.messageTray.connect('source-added', this._onSourceAdded.bind(this)); + this._sourceRemovedId = Main.messageTray.connect('source-removed', this._onSourceRemoved.bind(this)); + this._queueChangedId = Main.messageTray.connect('queue-changed', this._updateCount.bind(this)); let sources = Main.messageTray.getSources(); sources.forEach(source => this._onSourceAdded(null, source)); this._sync(); - this.connect('destroy', () => { - this._settings.run_dispose(); - this._settings = null; - }); + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._showBannersChangedId) { + this._settings.disconnect(this._showBannersChangedId); + this._showBannersChangedId = 0; + } + + if (this._sourceAddedId) { + Main.messageTray.disconnect(this._sourceAddedId); + this._sourceAddedId = 0; + } + if (this._sourceRemovedId) { + Main.messageTray.disconnect(this._sourceRemovedId); + this._sourceRemovedId = 0; + } + if (this._queueChangedId) { + Main.messageTray.disconnect(this._queueChangedId); + this._queueChangedId = 0; + } + + for (let source in this._sources) { + let countChangedId = this._sources[source]; + source.disconnect(countChangedId); + } + this._sources = {}; } _onSourceAdded(tray, source) { - source.connect('notify::count', this._updateCount.bind(this)); - this._sources.push(source); + let countChangedId = source.connect('notify::count', this._updateCount.bind(this)); + this._sources[source] = countChangedId; this._updateCount(); } _onSourceRemoved(tray, source) { - this._sources.splice(this._sources.indexOf(source), 1); + let countChangedId = this._sources[source]; + if (countChangedId) + source.disconnect(countChangedId); + delete this._sources[source]; this._updateCount(); } _updateCount() { let count = 0; - this._sources.forEach(source => (count += source.unseenCount)); + for (let source in this._sources) + count += source.unseenCount; this._count = count - Main.messageTray.queueCount; this._sync(); @@ -872,24 +976,39 @@ class DateMenuButton extends PanelMenu.Button { this._clock = new GnomeDesktop.WallClock(); this._clock.bind_property('clock', this._clockDisplay, 'text', GObject.BindingFlags.SYNC_CREATE); - this._clock.connect('notify::timezone', this._updateTimeZone.bind(this)); + this._tzNotifyId = this._clock.connect('notify::timezone', this._updateTimeZone.bind(this)); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); } - _getEventSource() { - return new Calendar.DBusEventSource(); + _onDestroy() { + super._onDestroy(); + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + if (this._tzNotifyId) { + this._clock.disconnect(this._tzNotifyId); + this._tzNotifyId = 0; + } + + if (this._eventSource) { + this._eventSource.destroy(); + this._eventSource = null; + } } _setEventSource(eventSource) { if (this._eventSource) this._eventSource.destroy(); + this._eventSource = eventSource; + this._calendar.setEventSource(eventSource); this._eventsItem.setEventSource(eventSource); - - this._eventSource = eventSource; } _updateTimeZone() { @@ -905,7 +1024,7 @@ class DateMenuButton extends PanelMenu.Button { let eventSource; let showEvents = Main.sessionMode.showCalendarEvents; if (showEvents) - eventSource = this._getEventSource(); + eventSource = new Calendar.DBusEventSource(); else eventSource = new Calendar.EmptyEventSource(); diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js index 04406e2255b1133f76eb7f3e322ffe55e184e805..fc6c215fbf5585eef57de1a6fed3f7ff59ba9975 100644 --- a/js/ui/status/accessibility.js +++ b/js/ui/status/accessibility.js @@ -42,7 +42,10 @@ class ATIndicator extends PanelMenu.Button { this.add_child(this._hbox); this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); - this._a11ySettings.connect('changed::%s'.format(KEY_ALWAYS_SHOW), this._queueSyncMenuVisibility.bind(this)); + this._a11ySettingsChangedId = this._a11ySettings.connect('changed::%s'.format(KEY_ALWAYS_SHOW), + this._queueSyncMenuVisibility.bind(this)); + + this._settingsChangedIds = []; let highContrast = this._buildHCItem(); this.menu.addMenuItem(highContrast); @@ -80,6 +83,27 @@ class ATIndicator extends PanelMenu.Button { this._syncMenuVisibility(); } + _onDestroy() { + super._onDestroy(); + + if (this._a11ySettingsChangedId) { + this._a11ySettings.disconnect(this._a11ySettingsChangedId); + this._a11ySettingsChangedId = null; + } + + this._settingsChangedIds.forEach(pair => { + let settings = pair[0]; + let sourceId = pair[1]; + settings.disconnect(sourceId); + }); + this._settingsChangedIds = []; + + if (this._syncMenuVisibilityIdle) { + GLib.source_remove(this._syncMenuVisibilityIdle); + this._syncMenuVisibilityIdle = 0; + } + } + _syncMenuVisibility() { this._syncMenuVisibilityIdle = 0; @@ -118,11 +142,12 @@ class ATIndicator extends PanelMenu.Button { settings.is_writable(key), enabled => settings.set_boolean(key, enabled)); - settings.connect('changed::%s'.format(key), () => { + let changedId = settings.connect('changed::%s'.format(key), () => { widget.setToggleState(settings.get_boolean(key)); this._queueSyncMenuVisibility(); }); + this._settingsChangedIds.push([settings, changedId]); return widget; } @@ -150,7 +175,7 @@ class ATIndicator extends PanelMenu.Button { } }); - interfaceSettings.connect('changed::%s'.format(KEY_GTK_THEME), () => { + let changedId = interfaceSettings.connect('changed::%s'.format(KEY_GTK_THEME), () => { let value = interfaceSettings.get_string(KEY_GTK_THEME); if (value == HIGH_CONTRAST_THEME) { highContrast.setToggleState(true); @@ -161,12 +186,14 @@ class ATIndicator extends PanelMenu.Button { this._queueSyncMenuVisibility(); }); + this._settingsChangedIds.push([interfaceSettings, changedId]); - interfaceSettings.connect('changed::%s'.format(KEY_ICON_THEME), () => { + changedId = interfaceSettings.connect('changed::%s'.format(KEY_ICON_THEME), () => { let value = interfaceSettings.get_string(KEY_ICON_THEME); if (value != HIGH_CONTRAST_THEME) iconTheme = value; }); + this._settingsChangedIds.push([interfaceSettings, changedId]); return highContrast; } @@ -187,13 +214,14 @@ class ATIndicator extends PanelMenu.Button { } }); - settings.connect('changed::%s'.format(KEY_TEXT_SCALING_FACTOR), () => { + let changedId = settings.connect('changed::%s'.format(KEY_TEXT_SCALING_FACTOR), () => { factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); let active = factor > 1.0; widget.setToggleState(active); this._queueSyncMenuVisibility(); }); + this._settingsChangedIds.push([settings, changedId]); return widget; } diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js index 8510944f5956f579d9ad7dac90f7ae0b486747f3..8edfa6ea66486702e67db710b646c87468546700 100644 --- a/js/ui/status/bluetooth.js +++ b/js/ui/status/bluetooth.js @@ -26,16 +26,20 @@ class Indicator extends PanelMenu.SystemIndicator { this._indicator.icon_name = 'bluetooth-active-symbolic'; this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP); + this._cancellable = new Gio.Cancellable(); this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, (proxy, error) => { if (error) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, + Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } - this._sync(); - }); - this._proxy.connect('g-properties-changed', this._queueSync.bind(this)); + }, + this._cancellable); + this._proxyPropertiesChangedId = this._proxy.connect('g-properties-changed', + this._queueSync.bind(this)); this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); this._item.icon.icon_name = 'bluetooth-active-symbolic'; @@ -54,11 +58,51 @@ class Indicator extends PanelMenu.SystemIndicator { this._client = new GnomeBluetooth.Client(); this._model = this._client.get_model(); - this._model.connect('row-deleted', this._queueSync.bind(this)); - this._model.connect('row-changed', this._queueSync.bind(this)); - this._model.connect('row-inserted', this._sync.bind(this)); - Main.sessionMode.connect('updated', this._sync.bind(this)); + this._rowDeletedId = this._model.connect('row-deleted', this._queueSync.bind(this)); + this._rowChangedId = this._model.connect('row-changed', this._queueSync.bind(this)); + this._rowInsertedId = this._model.connect('row-inserted', this._sync.bind(this)); + + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sync.bind(this)); this._sync(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._proxyPropertiesChangedId) { + this._proxy.disconnect(this._proxyPropertiesChangedId); + this._proxyPropertiesChangedId = null; + } + + if (this._rowDeletedId) { + this._model.disconnect(this._rowDeletedId); + this._rowDeletedId = 0; + } + + if (this._rowChangedId) { + this._model.disconnect(this._rowChangedId); + this._rowChangedId = 0; + } + + if (this._rowInsertedId) { + this._model.disconnect(this._rowInsertedId); + this._rowInsertedId = 0; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + if (this._syncId) { + GLib.source_remove(this._syncId); + this._syncId = 0; + } } _setHadSetupDevices(value) { diff --git a/js/ui/status/brightness.js b/js/ui/status/brightness.js index 17247880f5d4642ab083b989135345db426f53ea..66915c0eaafb6addc3fca387a2cf6d7de5ceb6cb 100644 --- a/js/ui/status/brightness.js +++ b/js/ui/status/brightness.js @@ -19,16 +19,20 @@ var Indicator = GObject.registerClass( class Indicator extends PanelMenu.SystemIndicator { _init() { super._init(); + this._cancellable = new Gio.Cancellable(); this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, (proxy, error) => { if (error) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, + Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } - - this._proxy.connect('g-properties-changed', this._sync.bind(this)); this._sync(); - }); + }, + this._cancellable); + this._proxyPropertiesChangedId = this._proxy.connect('g-properties-changed', + this._sync.bind(this)); this._item = new PopupMenu.PopupBaseMenuItem({ activate: false }); this.menu.addMenuItem(this._item); @@ -51,6 +55,20 @@ class Indicator extends PanelMenu.SystemIndicator { this._item.connect('scroll-event', (actor, event) => { return this._slider.emit('scroll-event', event); }); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._proxyPropertiesChangedId) { + this._proxy.disconnect(this._proxyPropertiesChangedId); + this._proxyPropertiesChangedId = 0; + } } _sliderChanged() { diff --git a/js/ui/status/dwellClick.js b/js/ui/status/dwellClick.js index ce13f73bae758aaf4a7fe6eaed354282767f7db8..a7a32fb5687031a96a8fa2a5c345adcfa839d5cd 100644 --- a/js/ui/status/dwellClick.js +++ b/js/ui/status/dwellClick.js @@ -45,11 +45,13 @@ class DwellClickIndicator extends PanelMenu.Button { this.add_child(this._hbox); this._a11ySettings = new Gio.Settings({ schema_id: MOUSE_A11Y_SCHEMA }); - this._a11ySettings.connect('changed::%s'.format(KEY_DWELL_CLICK_ENABLED), this._syncMenuVisibility.bind(this)); - this._a11ySettings.connect('changed::%s'.format(KEY_DWELL_MODE), this._syncMenuVisibility.bind(this)); + this._clickEnabledChangedId = this._a11ySettings.connect('changed::%s'.format(KEY_DWELL_CLICK_ENABLED), + this._syncMenuVisibility.bind(this)); + this._modeChangedId = this._a11ySettings.connect('changed::%s'.format(KEY_DWELL_MODE), + this._syncMenuVisibility.bind(this)); this._seat = Clutter.get_default_backend().get_default_seat(); - this._seat.connect('ptr-a11y-dwell-click-type-changed', this._updateClickType.bind(this)); + this._seatClickTypeChanged = this._seat.connect('ptr-a11y-dwell-click-type-changed', this._updateClickType.bind(this)); this._addDwellAction(DWELL_CLICK_MODES.primary); this._addDwellAction(DWELL_CLICK_MODES.double); @@ -60,6 +62,23 @@ class DwellClickIndicator extends PanelMenu.Button { this._syncMenuVisibility(); } + _onDestroy() { + super._onDestroy(); + + if (this._clickEnabledChangedId) { + this._a11ySettings.disconnect(this._clickEnabledChangedId); + this._clickEnabledChangedId = 0; + } + if (this._modeChangedId) { + this._a11ySettings.disconnect(this._modeChangedId); + this._modeChangedId = 0; + } + if (this._seatClickTypeChanged) { + this._seat.disconnect(this._seatClickTypeChanged); + this._seatClickTypeChanged = 0; + } + } + _syncMenuVisibility() { this.visible = this._a11ySettings.get_boolean(KEY_DWELL_CLICK_ENABLED) && diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js index 43fd89db2e4f2780dff3106a55a8878e9cb9e79a..a804edb1b90208ca13c9afa5b8d433018aa895c7 100644 --- a/js/ui/status/keyboard.js +++ b/js/ui/status/keyboard.js @@ -825,8 +825,6 @@ class InputSourceIndicator extends PanelMenu.Button { _init() { super._init(0.5, _("Keyboard")); - this.connect('destroy', this._onDestroy.bind(this)); - this._menuItems = {}; this._indicatorLabels = {}; @@ -847,7 +845,7 @@ class InputSourceIndicator extends PanelMenu.Button { this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), this._showLayout.bind(this)); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); this._inputSourceManager = getInputSourceManager(); @@ -855,10 +853,25 @@ class InputSourceIndicator extends PanelMenu.Button { this._inputSourceManager.connect('sources-changed', this._sourcesChanged.bind(this)); this._inputSourceManagerCurrentSourceChangedId = this._inputSourceManager.connect('current-source-changed', this._currentSourceChanged.bind(this)); + this._inputSourceChangedIds = []; this._inputSourceManager.reload(); } _onDestroy() { + super._onDestroy(); + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + this._inputSourceChangedIds.forEach(pair => { + let is = pair[0]; + let sourceId = pair[1]; + is.disconnect(sourceId); + }); + this._inputSourceChangedIds = []; + if (this._inputSourceManager) { this._inputSourceManager.disconnect(this._inputSourceManagerSourcesChangedId); this._inputSourceManager.disconnect(this._inputSourceManagerCurrentSourceChangedId); @@ -895,10 +908,11 @@ class InputSourceIndicator extends PanelMenu.Button { this._menuItems[i] = menuItem; this._indicatorLabels[i] = indicatorLabel; - is.connect('changed', () => { + let changedId = is.connect('changed', () => { menuItem.indicator.set_text(is.shortName); indicatorLabel.set_text(is.shortName); }); + this._inputSourceChangedIds.push([is, changedId]); this.menu.addMenuItem(menuItem, menuIndex++); this._container.add_actor(indicatorLabel); diff --git a/js/ui/status/location.js b/js/ui/status/location.js index 4250ed0fe9dc20b05d1bd906cbd4717b9edeca7a..ba69ded8a6755e7700dd46e97603ed4439a9de0e 100644 --- a/js/ui/status/location.js +++ b/js/ui/status/location.js @@ -47,11 +47,13 @@ class Indicator extends PanelMenu.SystemIndicator { _init() { super._init(); + this._cancellable = new Gio.Cancellable(); + this._settings = new Gio.Settings({ schema_id: LOCATION_SCHEMA }); - this._settings.connect('changed::%s'.format(ENABLED), - this._onMaxAccuracyLevelChanged.bind(this)); - this._settings.connect('changed::%s'.format(MAX_ACCURACY_LEVEL), - this._onMaxAccuracyLevelChanged.bind(this)); + this._enabledChangedId = this._settings.connect('changed::%s'.format(ENABLED), + this._onMaxAccuracyLevelChanged.bind(this)); + this._maxAccuracyLevelChangedId = this._settings.connect('changed::%s'.format(MAX_ACCURACY_LEVEL), + this._onMaxAccuracyLevelChanged.bind(this)); this._indicator = this._addIndicator(); this._indicator.icon_name = 'find-location-symbolic'; @@ -73,11 +75,50 @@ class Indicator extends PanelMenu.SystemIndicator { 0, this._connectToGeoclue.bind(this), this._onGeoclueVanished.bind(this)); - Main.sessionMode.connect('updated', this._onSessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._onSessionUpdated.bind(this)); this._onSessionUpdated(); this._onMaxAccuracyLevelChanged(); this._connectToGeoclue(); this._connectToPermissionStore(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._enabledChangedId) { + this._settings.disconnect(this._enabledChangedId); + this._enabledChangedId = 0; + } + + if (this._maxAccuracyLevelChangedId) { + this._settings.disconnect(this._maxAccuracyLevelChangedId); + this._maxAccuracyLevelChangedId = 0; + } + + if (this._propertiesChangedId) { + this._managerProxy.disconnect(this._propertiesChangedId); + this._propertiesChangedId = 0; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + if (this._watchId) { + Gio.bus_unwatch_name(this._watchId); + this._watchId = 0; + } + + if (this._agent) { + this._agent.unexport(); + this._agent = null; + } } get MaxAccuracyLevel() { @@ -119,13 +160,15 @@ class Indicator extends PanelMenu.SystemIndicator { new GeoclueManager(Gio.DBus.system, 'org.freedesktop.GeoClue2', '/org/freedesktop/GeoClue2/Manager', - this._onManagerProxyReady.bind(this)); + this._onManagerProxyReady.bind(this), + this._cancellable); return true; } _onManagerProxyReady(proxy, error) { if (error != null) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log(error.message); this._connecting = false; return; } @@ -212,12 +255,14 @@ class Indicator extends PanelMenu.SystemIndicator { _connectToPermissionStore() { this._permStoreProxy = null; - new PermissionStore.PermissionStore(this._onPermStoreProxyReady.bind(this)); + new PermissionStore.PermissionStore(this._onPermStoreProxyReady.bind(this), + this._cancellable); } _onPermStoreProxyReady(proxy, error) { if (error != null) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } diff --git a/js/ui/status/network.js b/js/ui/status/network.js index 3d4acd5b08978c465969017794274a27b389fe03..36591ef1c264a93b3c128cf72f89140fcb5d530b 100644 --- a/js/ui/status/network.js +++ b/js/ui/status/network.js @@ -761,11 +761,12 @@ class NMWirelessDialog extends ModalDialog.ModalDialog { GLib.Source.set_name_by_id(this._scanTimeoutId, '[gnome-shell] this._onScanTimeout'); this._onScanTimeout(); - let id = Main.sessionMode.connect('updated', () => { + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', () => { if (Main.sessionMode.allowSettings) return; - Main.sessionMode.disconnect(id); + Main.sessionMode.disconnect(this._sessionModeUpdatedid); + this._sessionModeUpdatedId = 0; this.close(); }); @@ -798,6 +799,10 @@ class NMWirelessDialog extends ModalDialog.ModalDialog { GLib.source_remove(this._scanTimeoutId); this._scanTimeoutId = 0; } + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedid); + this._sessionModeUpdatedId = 0; + } } _onScanTimeout() { @@ -1635,6 +1640,8 @@ class Indicator extends PanelMenu.SystemIndicator { _init() { super._init(); + this._cancellable = new Gio.Cancellable(); + this._primaryIndicator = this._addIndicator(); this._vpnIndicator = this._addIndicator(); @@ -1655,10 +1662,30 @@ class Indicator extends PanelMenu.SystemIndicator { this._ctypes[NM.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN; this._getClient(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } } async _getClient() { - this._client = await NM.Client.new_async(null); + try { + this._client = await NM.Client.new_async(this._cancellable); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + logError(e); + return; + } this._activeConnections = []; this._connections = []; @@ -1704,7 +1731,7 @@ class Indicator extends PanelMenu.SystemIndicator { this._client.connect('connection-added', this._connectionAdded.bind(this)); this._client.connect('connection-removed', this._connectionRemoved.bind(this)); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); } @@ -2020,10 +2047,14 @@ class Indicator extends PanelMenu.SystemIndicator { this._closeConnectivityCheck(path); } else if (result == PortalHelperResult.RECHECK) { try { - const state = await this._client.check_connectivity_async(null); + const state = await this._client.check_connectivity_async( + this._cancellable); if (state >= NM.ConnectivityState.FULL) this._closeConnectivityCheck(path); - } catch (e) { } + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log('Failed to check connectivity status: %s'.format(e.toString())); + } } else { log('Invalid result from portal helper: %s'.format(result)); } @@ -2068,7 +2099,8 @@ class Indicator extends PanelMenu.SystemIndicator { proxy.connectSignal('Done', this._portalHelperDone.bind(this)); proxy.AuthenticateRemote(path, '', timestamp); - }); + }, + this._cancellable); } this._connectivityQueue.push(path); diff --git a/js/ui/status/nightLight.js b/js/ui/status/nightLight.js index c595c3da02ab8de31ff70efee60426b3d77d52d7..d7d1a4d6d20a8cb0a3903ebb9547741ce4872d1a 100644 --- a/js/ui/status/nightLight.js +++ b/js/ui/status/nightLight.js @@ -22,16 +22,19 @@ class Indicator extends PanelMenu.SystemIndicator { this._indicator = this._addIndicator(); this._indicator.icon_name = 'night-light-symbolic'; + this._cancellable = new Gio.Cancellable(); this._proxy = new ColorProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, (proxy, error) => { if (error) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } - this._proxy.connect('g-properties-changed', - this._sync.bind(this)); this._sync(); - }); + }, + this._cancellable); + this._proxyPropertiesChangedId = this._proxy.connect('g-properties-changed', + this._sync.bind(this)); this._item = new PopupMenu.PopupSubMenuMenuItem("", true); this._item.icon.icon_name = 'night-light-symbolic'; @@ -45,9 +48,28 @@ class Indicator extends PanelMenu.SystemIndicator { this._item.menu.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop'); this.menu.addMenuItem(this._item); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); this._sync(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._proxyPropertiesChangedId) { + this._proxy.disconnect(this._proxyPropertiesChangedId); + this._proxyPropertiesChangedId = 0; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } } _sessionUpdated() { diff --git a/js/ui/status/power.js b/js/ui/status/power.js index 0e8471fdfa64bff9f2ca0a8f8ff91b76af50f3a9..e5f581a6887755c53d919bfc40c4160e182348bd 100644 --- a/js/ui/status/power.js +++ b/js/ui/status/power.js @@ -23,8 +23,8 @@ class Indicator extends PanelMenu.SystemIndicator { super._init(); this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); - this._desktopSettings.connect('changed::%s'.format(SHOW_BATTERY_PERCENTAGE), - this._sync.bind(this)); + this._settingsChangedId = this._desktopSettings.connect('changed::%s'.format(SHOW_BATTERY_PERCENTAGE), + this._sync.bind(this)); this._indicator = this._addIndicator(); this._percentageLabel = new St.Label({ y_expand: true, @@ -32,23 +32,51 @@ class Indicator extends PanelMenu.SystemIndicator { this.add_child(this._percentageLabel); this.add_style_class_name('power-status'); + this._cancellable = new Gio.Cancellable(); this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH, (proxy, error) => { if (error) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, + Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } - this._proxy.connect('g-properties-changed', - this._sync.bind(this)); this._sync(); - }); + }, + this._cancellable); + this._proxyPropertiesChangedId = this._proxy.connect('g-properties-changed', + this._sync.bind(this)); this._item = new PopupMenu.PopupSubMenuMenuItem("", true); this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop'); this.menu.addMenuItem(this._item); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._settingsChangedId) { + this._desktopSettings.disconnect(this._settingsChangedId); + this._settingsChangedId = 0; + } + + if (this._proxyPropertiesChangedId) { + this._proxy.disconnect(this._proxyPropertiesChangedId); + this._proxyPropertiesChangedId = 0; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } } _sessionUpdated() { diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js index feafe61550229296d67b387662ccd73f04be35a4..afbd69273f237e31a412cdbc20047ee9ae8c3a91 100644 --- a/js/ui/status/remoteAccess.js +++ b/js/ui/status/remoteAccess.js @@ -11,9 +11,9 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator { _init() { super._init(); - let controller = global.backend.get_remote_access_controller(); + this._controller = global.backend.get_remote_access_controller(); - if (!controller) + if (!this._controller) return; // We can't possibly know about all types of screen sharing on X11, so @@ -28,9 +28,18 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator { this._recordingIndicator = null; this._menuSection = null; - controller.connect('new-handle', (o, handle) => { + this._controllerNewHandleId = this._controller.connect('new-handle', (o, handle) => { this._onNewHandle(handle); }); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._controllerNewHandleId) { + this._controller.disconnect(this._controllerNewHandleId); + this._controllerNewHandleId = 0; + } } _ensureControls() { diff --git a/js/ui/status/rfkill.js b/js/ui/status/rfkill.js index 9f8b09dd2e340070f5f766eca1b5789c3cc6e73e..3732ec1d5c81398f712514c975bcbabb503c0d3e 100644 --- a/js/ui/status/rfkill.js +++ b/js/ui/status/rfkill.js @@ -21,7 +21,9 @@ var RfkillManager = class { this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, (proxy, error) => { if (error) { - log(error.message); + if (!error.matches(Gio.IOErrorEnum, + Gio.IOErrorEnum.CANCELLED)) + log(error.message); return; } this._proxy.connect('g-properties-changed', @@ -67,7 +69,7 @@ class Indicator extends PanelMenu.SystemIndicator { super._init(); this._manager = getRfkillManager(); - this._manager.connect('airplane-mode-changed', this._sync.bind(this)); + this._airplaneModeChangedId = this._manager.connect('airplane-mode-changed', this._sync.bind(this)); this._indicator = this._addIndicator(); this._indicator.icon_name = 'airplane-mode-symbolic'; @@ -84,10 +86,24 @@ class Indicator extends PanelMenu.SystemIndicator { this._item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); this.menu.addMenuItem(this._item); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); this._sync(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._airplaneModeChangedId) { + this._manager.disconnect(this._airplaneModeChangedId); + this._airplaneModeChangedId = 0; + } + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } } _sessionUpdated() { diff --git a/js/ui/status/system.js b/js/ui/status/system.js index e64b62cf4ef27165117a7ff1eabb97e467a36f86..baee673fb30b5bf3d2bdfcbe5d2ad492dd4c838e 100644 --- a/js/ui/status/system.js +++ b/js/ui/status/system.js @@ -39,8 +39,22 @@ class Indicator extends PanelMenu.SystemIndicator { }); this._updateSessionSubMenu(); - Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + if (this._orientationLockIconChangedId) { + this._systemActions.disconnect(this._orientationLockIconChangedId); + this._orientationLockIconChangedId = 0; + } } _sessionUpdated() { @@ -73,7 +87,7 @@ class Indicator extends PanelMenu.SystemIndicator { this._orientationLockItem, 'visible', bindFlags); - this._systemActions.connect('notify::orientation-lock-icon', () => { + this._orientationLockIconChangedId = this._systemActions.connect('notify::orientation-lock-icon', () => { let iconName = this._systemActions.orientation_lock_icon; let labelText = this._systemActions.getName("lock-orientation"); diff --git a/js/ui/status/thunderbolt.js b/js/ui/status/thunderbolt.js index d98355deeba9818d3f0fa6e203106456678e5dbb..d9c46b830c12e701d7b89c4f11df7481e843882e 100644 --- a/js/ui/status/thunderbolt.js +++ b/js/ui/status/thunderbolt.js @@ -51,6 +51,7 @@ const BOLT_DBUS_PATH = '/org/freedesktop/bolt'; var Client = class { constructor() { + this._cancellable = new Gio.Cancellable(); this._proxy = null; this.probing = false; this._getProxy(); @@ -66,9 +67,10 @@ var Client = class { BOLT_DBUS_NAME, BOLT_DBUS_PATH, BOLT_DBUS_CLIENT_IFACE, - null); + this._cancellable); } catch (e) { - log('error creating bolt proxy: %s'.format(e.message)); + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log('error creating bolt proxy: %s'.format(e.message)); return; } this._propsChangedId = this._proxy.connect('g-properties-changed', this._onPropertiesChanged.bind(this)); @@ -99,6 +101,11 @@ var Client = class { /* public methods */ close() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + if (!this._proxy) return; @@ -141,6 +148,11 @@ var AuthRobot = class { } close() { + if (this._enrollDevicesIdleId) { + GLib.source_remove(this._enrollDevicesIdleId); + this._enrollDevicesIdleId = 0; + } + this.disconnectAll(); this._client = null; } @@ -184,11 +196,13 @@ var AuthRobot = class { return; this._enrolling = true; - GLib.idle_add(GLib.PRIORITY_DEFAULT, - this._enrollDevicesIdle.bind(this)); + this._enrollDevicesIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, + this._enrollDevicesIdle.bind(this)); } _onEnrollDone(device, error) { + this._enrollDeviceDoneId = 0; + if (error) this.emit('enroll-failed', device, error); @@ -199,12 +213,14 @@ var AuthRobot = class { this._enrolling = this._devicesToEnroll.length > 0; if (this._enrolling) { - GLib.idle_add(GLib.PRIORITY_DEFAULT, - this._enrollDevicesIdle.bind(this)); + this._enrollDevicesIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, + this._enrollDevicesIdle.bind(this)); } } _enrollDevicesIdle() { + this._enrollDevicesIdleId = 0; + let devices = this._devicesToEnroll; let dev = devices.shift(); @@ -226,6 +242,8 @@ class Indicator extends PanelMenu.SystemIndicator { _init() { super._init(); + this._cancellable = new Gio.Cancellable(); + this._indicator = this._addIndicator(); this._indicator.icon_name = 'thunderbolt-symbolic'; @@ -237,25 +255,40 @@ class Indicator extends PanelMenu.SystemIndicator { this._robot.connect('enroll-device', this._onEnrollDevice.bind(this)); this._robot.connect('enroll-failed', this._onEnrollFailed.bind(this)); - Main.sessionMode.connect('updated', this._sync.bind(this)); + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sync.bind(this)); + this._sync(); this._source = null; this._perm = null; this._createPermission(); + + this.connect('destroy', this._onDestroy.bind(this)); } async _createPermission() { try { - this._perm = await Polkit.Permission.new('org.freedesktop.bolt.enroll', null, null); + this._perm = await Polkit.Permission.new('org.freedesktop.bolt.enroll', null, + this._cancellable); } catch (e) { - log('Failed to get PolKit permission: %s'.format(e.toString())); + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + log('Failed to get PolKit permission: %s'.format(e.toString())); } } _onDestroy() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + this._robot.close(); this._client.close(); + + if (this._sessionModeUpdatedId) { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } } _ensureSource() { diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js index 7b50658715f91a8871155cc27cc70124f50b0f78..2345fdf7fef0a9ade600d7a80c8eac6a8983d8e4 100644 --- a/js/ui/status/volume.js +++ b/js/ui/status/volume.js @@ -36,7 +36,8 @@ var StreamSlider = class { this._slider = new Slider.Slider(0); this._soundSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.sound' }); - this._soundSettings.connect('changed::%s'.format(ALLOW_AMPLIFIED_VOLUME_KEY), this._amplifySettingsChanged.bind(this)); + this._settingsChangedId = this._soundSettings.connect('changed::%s'.format(ALLOW_AMPLIFIED_VOLUME_KEY), + this._amplifySettingsChanged.bind(this)); this._amplifySettingsChanged(); this._sliderChangedId = this._slider.connect('notify::value', @@ -65,6 +66,21 @@ var StreamSlider = class { this._icons = []; } + destroy() { + if (this._settingsChangedId) { + this._soundSettings.disconnect(this._settingsChangedId); + this._settingsChangedId = 0; + } + if (this._mutedChangedId) { + this._stream.disconnect(this._mutedChangedId); + this._mutedChangedId = 0; + } + if (this._volumeChangedId) { + this._stream.disconnect(this._volumeChangedId); + this._volumeChangedId = 0; + } + } + get stream() { return this._stream; } @@ -224,6 +240,15 @@ var OutputStreamSlider = class extends StreamSlider { ]; } + destroy() { + if (this._portChangedId) { + this._stream.disconnect(this._portChangedId); + this._portChangedId = 0; + } + + super.destroy(); + } + _connectStream(stream) { super._connectStream(stream); this._portChangedId = stream.connect('notify::port', this._portChanged.bind(this)); @@ -270,8 +295,8 @@ var InputStreamSlider = class extends StreamSlider { constructor(control) { super(control); this._slider.accessible_name = _("Microphone"); - this._control.connect('stream-added', this._maybeShowInput.bind(this)); - this._control.connect('stream-removed', this._maybeShowInput.bind(this)); + this._streamAddedId = this._control.connect('stream-added', this._maybeShowInput.bind(this)); + this._streamRemovedId = this._control.connect('stream-removed', this._maybeShowInput.bind(this)); this._icon.icon_name = 'audio-input-microphone-symbolic'; this._icons = [ 'microphone-sensitivity-muted-symbolic', @@ -281,6 +306,19 @@ var InputStreamSlider = class extends StreamSlider { ]; } + destroy() { + if (this._streamAddedId) { + this._control.disconnect(this._streamAddedId); + this._streamAddedId = 0; + } + if (this._streamRemovedId) { + this._control.disconnect(this._streamRemovedId); + this._streamRemovedId = 0; + } + + super.destroy(); + } + _connectStream(stream) { super._connectStream(stream); this._maybeShowInput(); @@ -318,9 +356,9 @@ var VolumeMenu = class extends PopupMenu.PopupMenuSection { this.hasHeadphones = false; this._control = control; - this._control.connect('state-changed', this._onControlStateChanged.bind(this)); - this._control.connect('default-sink-changed', this._readOutput.bind(this)); - this._control.connect('default-source-changed', this._readInput.bind(this)); + this._stateChangedId = this._control.connect('state-changed', this._onControlStateChanged.bind(this)); + this._defaultSinkChangedId = this._control.connect('default-sink-changed', this._readOutput.bind(this)); + this._defaultSourceChangedId = this._control.connect('default-source-changed', this._readInput.bind(this)); this._output = new OutputStreamSlider(this._control); this._output.connect('stream-updated', () => { @@ -342,6 +380,35 @@ var VolumeMenu = class extends PopupMenu.PopupMenuSection { this._onControlStateChanged(); } + destroy() { + if (this._stateChangedId) { + this._control.disconnect(this._stateChangedId); + this._stateChangedId = 0; + } + + if (this._defaultSinkChangedId) { + this._control.disconnect(this._defaultSinkChangedId); + this._defaultSinkChangedId = 0; + } + + if (this._defaultSourceChangedId) { + this._control.disconnect(this._defaultSourceChangedId); + this._defaultSourceChangedId = 0; + } + + if (this._input) { + this._input.destroy(); + this._input = null; + } + + if (this._output) { + this._output.destroy(); + this._output = null; + } + + super.destroy(); + } + scroll(event) { return this._output.scroll(event); }