mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 932880 - Fix window leaks in App Manager. r=past
CLOSED TREE
This commit is contained in:
parent
b53c481fcc
commit
9ad29bd326
@ -19,11 +19,11 @@ module.exports = ConnectionStore = function(connection) {
|
||||
|
||||
ObservableObject.call(this, {status:null,host:null,port:null});
|
||||
|
||||
this._destroy = this._destroy.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this._feedStore = this._feedStore.bind(this);
|
||||
|
||||
this._connection = connection;
|
||||
this._connection.once(Connection.Events.DESTROYED, this._destroy);
|
||||
this._connection.once(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.on(Connection.Events.STATUS_CHANGED, this._feedStore);
|
||||
this._connection.on(Connection.Events.PORT_CHANGED, this._feedStore);
|
||||
this._connection.on(Connection.Events.HOST_CHANGED, this._feedStore);
|
||||
@ -32,12 +32,18 @@ module.exports = ConnectionStore = function(connection) {
|
||||
}
|
||||
|
||||
ConnectionStore.prototype = {
|
||||
_destroy: function() {
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._feedStore);
|
||||
this._connection.off(Connection.Events.PORT_CHANGED, this._feedStore);
|
||||
this._connection.off(Connection.Events.HOST_CHANGED, this._feedStore);
|
||||
_knownConnectionStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
destroy: function() {
|
||||
if (this._connection) {
|
||||
// While this.destroy is bound using .once() above, that event may not
|
||||
// have occurred when the ConnectionStore client calls destroy, so we
|
||||
// manually remove it here.
|
||||
this._connection.off(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._feedStore);
|
||||
this._connection.off(Connection.Events.PORT_CHANGED, this._feedStore);
|
||||
this._connection.off(Connection.Events.HOST_CHANGED, this._feedStore);
|
||||
_knownConnectionStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
}
|
||||
},
|
||||
|
||||
_feedStore: function() {
|
||||
|
@ -18,6 +18,11 @@ const DeviceStore = require("devtools/app-manager/device-store");
|
||||
const simulatorsStore = require("devtools/app-manager/simulators-store");
|
||||
const adbStore = require("devtools/app-manager/builtin-adb-store");
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
UI.destroy();
|
||||
});
|
||||
|
||||
let UI = {
|
||||
init: function() {
|
||||
this.useFloatingScrollbarsIfNeeded();
|
||||
@ -54,10 +59,7 @@ let UI = {
|
||||
let pre = document.querySelector("#logs > pre");
|
||||
pre.textContent = this.connection.logs;
|
||||
pre.scrollTop = pre.scrollTopMax;
|
||||
this.connection.on(Connection.Events.NEW_LOG, (event, str) => {
|
||||
pre.textContent += "\n" + str;
|
||||
pre.scrollTop = pre.scrollTopMax;
|
||||
});
|
||||
this.connection.on(Connection.Events.NEW_LOG, this._onNewLog);
|
||||
|
||||
this.template = new Template(document.body, this.store, Utils.l10n);
|
||||
this.template.start();
|
||||
@ -66,6 +68,18 @@ let UI = {
|
||||
this._onSimulatorDisconnected = this._onSimulatorDisconnected.bind(this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.store.destroy();
|
||||
this.connection.off(Connection.Events.NEW_LOG, this._onNewLog);
|
||||
this.template.destroy();
|
||||
},
|
||||
|
||||
_onNewLog: function(event, str) {
|
||||
let pre = document.querySelector("#logs > pre");
|
||||
pre.textContent += "\n" + str;
|
||||
pre.scrollTop = pre.scrollTopMax;
|
||||
},
|
||||
|
||||
useFloatingScrollbarsIfNeeded: function() {
|
||||
if (Services.appinfo.OS == "Darwin") {
|
||||
return;
|
||||
|
@ -32,12 +32,16 @@ window.addEventListener("message", function(event) {
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
UI.destroy();
|
||||
});
|
||||
|
||||
let UI = {
|
||||
init: function() {
|
||||
this.showFooterIfNeeded();
|
||||
this._onConnectionStatusChange = this._onConnectionStatusChange.bind(this);
|
||||
this.setTab("apps");
|
||||
if (this.connection) {
|
||||
this.onNewConnection();
|
||||
@ -46,6 +50,18 @@ let UI = {
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.connection) {
|
||||
this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
|
||||
}
|
||||
if (this.store) {
|
||||
this.store.destroy();
|
||||
}
|
||||
if (this.template) {
|
||||
this.template.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
showFooterIfNeeded: function() {
|
||||
let footer = document.querySelector("#connection-footer");
|
||||
if (window.parent == window) {
|
||||
@ -73,6 +89,9 @@ let UI = {
|
||||
"apps": new WebappsStore(this.connection),
|
||||
});
|
||||
|
||||
if (this.template) {
|
||||
this.template.destroy();
|
||||
}
|
||||
this.template = new Template(document.body, this.store, Utils.l10n);
|
||||
|
||||
this.template.start();
|
||||
@ -189,3 +208,7 @@ let UI = {
|
||||
return deferred.promise;
|
||||
},
|
||||
}
|
||||
|
||||
// This must be bound immediately, as it might be used via the message listener
|
||||
// before UI.init() has been called.
|
||||
UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI);
|
||||
|
@ -40,13 +40,25 @@ window.addEventListener("message", function(event) {
|
||||
}
|
||||
}, false);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
if (connection) {
|
||||
connection.off(Connection.Status.CONNECTED, onConnected);
|
||||
connection.off(Connection.Status.DISCONNECTED, onDisconnected);
|
||||
}
|
||||
});
|
||||
|
||||
function onNewConnection() {
|
||||
connection.on(Connection.Status.CONNECTED, () => {
|
||||
document.querySelector("#content").classList.add("connected");
|
||||
});
|
||||
connection.on(Connection.Status.DISCONNECTED, () => {
|
||||
document.querySelector("#content").classList.remove("connected");
|
||||
});
|
||||
connection.on(Connection.Status.CONNECTED, onConnected);
|
||||
connection.on(Connection.Status.DISCONNECTED, onDisconnected);
|
||||
}
|
||||
|
||||
function onConnected() {
|
||||
document.querySelector("#content").classList.add("connected");
|
||||
}
|
||||
|
||||
function onDisconnected() {
|
||||
document.querySelector("#content").classList.remove("connected");
|
||||
}
|
||||
|
||||
function selectTab(id) {
|
||||
|
@ -35,7 +35,12 @@ window.addEventListener("message", function(event) {
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}, false);
|
||||
});
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
UI.destroy();
|
||||
});
|
||||
|
||||
let UI = {
|
||||
isReady: false,
|
||||
@ -55,8 +60,15 @@ let UI = {
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.connection) {
|
||||
this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
|
||||
}
|
||||
this.template.destroy();
|
||||
},
|
||||
|
||||
onNewConnection: function() {
|
||||
this.connection.on(Connection.Events.STATUS_CHANGED, () => this._onConnectionStatusChange());
|
||||
this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
|
||||
this._onConnectionStatusChange();
|
||||
},
|
||||
|
||||
@ -427,4 +439,8 @@ let UI = {
|
||||
}
|
||||
};
|
||||
|
||||
// This must be bound immediately, as it might be used via the message listener
|
||||
// before UI.onload() has been called.
|
||||
UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI);
|
||||
|
||||
EventEmitter.decorate(UI);
|
||||
|
@ -69,7 +69,8 @@ function Template(root, store, l10nResolver) {
|
||||
this._root = root;
|
||||
this._doc = this._root.ownerDocument;
|
||||
|
||||
this._store.on("set", (event,path,value) => this._storeChanged(path,value));
|
||||
this._storeChanged = this._storeChanged.bind(this);
|
||||
this._store.on("set", this._storeChanged);
|
||||
}
|
||||
|
||||
Template.prototype = {
|
||||
@ -77,6 +78,12 @@ Template.prototype = {
|
||||
this._processTree(this._root);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._store.off("set", this._storeChanged);
|
||||
this._root = null;
|
||||
this._doc = null;
|
||||
},
|
||||
|
||||
_resolvePath: function(path, defaultValue=null) {
|
||||
|
||||
// From the store, get the value of an object located
|
||||
@ -110,7 +117,7 @@ Template.prototype = {
|
||||
return obj;
|
||||
},
|
||||
|
||||
_storeChanged: function(path, value) {
|
||||
_storeChanged: function(event, path, value) {
|
||||
|
||||
// The store has changed (a "set" event has been emitted).
|
||||
// We need to invalidate and rebuild the affected elements.
|
||||
|
@ -19,19 +19,36 @@ let Utils = (function() {
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
|
||||
function _forwardSetEvent(key, store, finalStore) {
|
||||
store.on("set", function(event, path, value) {
|
||||
function _createSetEventForwarder(key, finalStore) {
|
||||
return function(event, path, value) {
|
||||
finalStore.emit("set", [key].concat(path), value);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function mergeStores(stores) {
|
||||
let finalStore = {object:{}};
|
||||
|
||||
EventEmitter.decorate(finalStore);
|
||||
|
||||
let setEventForwarders = {};
|
||||
|
||||
for (let key in stores) {
|
||||
finalStore.object[key] = stores[key].object,
|
||||
_forwardSetEvent(key, stores[key], finalStore);
|
||||
let store = stores[key];
|
||||
finalStore.object[key] = store.object;
|
||||
setEventForwarders[key] = _createSetEventForwarder(key, finalStore);
|
||||
store.on("set", setEventForwarders[key]);
|
||||
}
|
||||
|
||||
finalStore.destroy = () => {
|
||||
for (let key in stores) {
|
||||
let store = stores[key];
|
||||
store.off("set", setEventForwarders[key]);
|
||||
if (store.destroy) {
|
||||
store.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return finalStore;
|
||||
}
|
||||
|
||||
|
@ -25,21 +25,27 @@ module.exports = DeviceStore = function(connection) {
|
||||
|
||||
this._resetStore();
|
||||
|
||||
this._destroy = this._destroy.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this._onStatusChanged = this._onStatusChanged.bind(this);
|
||||
|
||||
this._connection = connection;
|
||||
this._connection.once(Connection.Events.DESTROYED, this._destroy);
|
||||
this._connection.once(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
this._onStatusChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
DeviceStore.prototype = {
|
||||
_destroy: function() {
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
_knownDeviceStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
destroy: function() {
|
||||
if (this._connection) {
|
||||
// While this.destroy is bound using .once() above, that event may not
|
||||
// have occurred when the DeviceStore client calls destroy, so we
|
||||
// manually remove it here.
|
||||
this._connection.off(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
_knownDeviceStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
}
|
||||
},
|
||||
|
||||
_resetStore: function() {
|
||||
|
@ -101,7 +101,9 @@ function waitForProjectsPanel(deferred = promise.defer()) {
|
||||
let projectsWindow = getProjectsWindow();
|
||||
let projectsUI = projectsWindow.UI;
|
||||
if (!projectsUI) {
|
||||
info("projectsUI false");
|
||||
projectsWindow.addEventListener("load", function onLoad() {
|
||||
info("got load event");
|
||||
projectsWindow.removeEventListener("load", onLoad);
|
||||
waitForProjectsPanel(deferred);
|
||||
});
|
||||
@ -109,10 +111,12 @@ function waitForProjectsPanel(deferred = promise.defer()) {
|
||||
}
|
||||
|
||||
if (projectsUI.isReady) {
|
||||
info("projectsUI ready");
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
info("projectsUI not ready");
|
||||
projectsUI.once("ready", deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
@ -25,21 +25,27 @@ module.exports = WebappsStore = function(connection) {
|
||||
|
||||
this._resetStore();
|
||||
|
||||
this._destroy = this._destroy.bind(this);
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this._onStatusChanged = this._onStatusChanged.bind(this);
|
||||
|
||||
this._connection = connection;
|
||||
this._connection.once(Connection.Events.DESTROYED, this._destroy);
|
||||
this._connection.once(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
this._onStatusChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
WebappsStore.prototype = {
|
||||
_destroy: function() {
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
_knownWebappsStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
destroy: function() {
|
||||
if (this._connection) {
|
||||
// While this.destroy is bound using .once() above, that event may not
|
||||
// have occurred when the WebappsStore client calls destroy, so we
|
||||
// manually remove it here.
|
||||
this._connection.off(Connection.Events.DESTROYED, this.destroy);
|
||||
this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
|
||||
_knownWebappsStores.delete(this._connection);
|
||||
this._connection = null;
|
||||
}
|
||||
},
|
||||
|
||||
_resetStore: function() {
|
||||
|
Loading…
Reference in New Issue
Block a user