summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/leak
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/test/leak')
-rw-r--r--addon-sdk/source/test/leak/jetpack-package.ini7
-rw-r--r--addon-sdk/source/test/leak/leak-utils.js80
-rw-r--r--addon-sdk/source/test/leak/test-leak-event-dom-closed-window.js29
-rw-r--r--addon-sdk/source/test/leak/test-leak-tab-events.js46
-rw-r--r--addon-sdk/source/test/leak/test-leak-window-events.js65
5 files changed, 227 insertions, 0 deletions
diff --git a/addon-sdk/source/test/leak/jetpack-package.ini b/addon-sdk/source/test/leak/jetpack-package.ini
new file mode 100644
index 000000000..0632bdc87
--- /dev/null
+++ b/addon-sdk/source/test/leak/jetpack-package.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ leak-utils.js
+
+[test-leak-window-events.js]
+[test-leak-event-dom-closed-window.js]
+[test-leak-tab-events.js]
diff --git a/addon-sdk/source/test/leak/leak-utils.js b/addon-sdk/source/test/leak/leak-utils.js
new file mode 100644
index 000000000..e01255ec8
--- /dev/null
+++ b/addon-sdk/source/test/leak/leak-utils.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Cu, Ci } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const { SelfSupportBackend } = Cu.import("resource:///modules/SelfSupportBackend.jsm", {});
+const Startup = Cu.import("resource://gre/modules/sdk/system/Startup.js", {}).exports;
+
+// Adapted from the SpecialPowers.exactGC() code. We don't have a
+// window to operate on so we cannot use the exact same logic. We
+// use 6 GC iterations here as that is what is needed to clean up
+// the windows we have tested with.
+function gc() {
+ return new Promise(resolve => {
+ Cu.forceGC();
+ Cu.forceCC();
+ let count = 0;
+ function genGCCallback() {
+ Cu.forceCC();
+ return function() {
+ if (++count < 5) {
+ Cu.schedulePreciseGC(genGCCallback());
+ } else {
+ resolve();
+ }
+ }
+ }
+
+ Cu.schedulePreciseGC(genGCCallback());
+ });
+}
+
+// Execute the given test function and verify that we did not leak windows
+// in the process. The test function must return a promise or be a generator.
+// If the promise is resolved, or generator completes, with an sdk loader
+// object then it will be unloaded after the memory measurements.
+exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) {
+
+ // SelfSupportBackend periodically tries to open windows. This can
+ // mess up our window leak detection below, so turn it off.
+ SelfSupportBackend.uninit();
+
+ // Wait for the browser to finish loading.
+ yield Startup.onceInitialized;
+
+ // Track windows that are opened in an array of weak references.
+ let weakWindows = [];
+ function windowObserver(subject, topic) {
+ let supportsWeak = subject.QueryInterface(Ci.nsISupportsWeakReference);
+ if (supportsWeak) {
+ weakWindows.push(Cu.getWeakReference(supportsWeak));
+ }
+ }
+ Services.obs.addObserver(windowObserver, "domwindowopened", false);
+
+ // Execute the body of the test.
+ let testLoader = yield asyncTestFunc(assert);
+
+ // Stop tracking new windows and attempt to GC any resources allocated
+ // by the test body.
+ Services.obs.removeObserver(windowObserver, "domwindowopened", false);
+ yield gc();
+
+ // Check to see if any of the windows we saw survived the GC. We consider
+ // these leaks.
+ assert.ok(weakWindows.length > 0, "should see at least one new window");
+ for (let i = 0; i < weakWindows.length; ++i) {
+ assert.equal(weakWindows[i].get(), null, "window " + i + " should be GC'd");
+ }
+
+ // Finally, unload the test body's loader if it provided one. We do this
+ // after our leak detection to avoid free'ing things on unload. Users
+ // don't tend to unload their addons very often, so we want to find leaks
+ // that happen while addons are in use.
+ if (testLoader) {
+ testLoader.unload();
+ }
+}
diff --git a/addon-sdk/source/test/leak/test-leak-event-dom-closed-window.js b/addon-sdk/source/test/leak/test-leak-event-dom-closed-window.js
new file mode 100644
index 000000000..c398462ab
--- /dev/null
+++ b/addon-sdk/source/test/leak/test-leak-event-dom-closed-window.js
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { asyncWindowLeakTest } = require("./leak-utils");
+const { Loader } = require('sdk/test/loader');
+const openWindow = require("sdk/window/utils").open;
+
+exports["test sdk/event/dom does not leak when attached to closed window"] = function*(assert) {
+ yield asyncWindowLeakTest(assert, _ => {
+ return new Promise(resolve => {
+ let loader = Loader(module);
+ let { open } = loader.require('sdk/event/dom');
+ let w = openWindow();
+ w.addEventListener("DOMWindowClose", function windowClosed(evt) {
+ w.removeEventListener("DOMWindowClose", windowClosed);
+ // The sdk/event/dom module tries to clean itself up when DOMWindowClose
+ // is fired. Verify that it doesn't leak if its attached to an
+ // already closed window either. (See bug 1268898.)
+ open(w.document, "TestEvent1");
+ resolve(loader);
+ });
+ w.close();
+ });
+ });
+}
+
+require("sdk/test").run(exports);
diff --git a/addon-sdk/source/test/leak/test-leak-tab-events.js b/addon-sdk/source/test/leak/test-leak-tab-events.js
new file mode 100644
index 000000000..4266c04fc
--- /dev/null
+++ b/addon-sdk/source/test/leak/test-leak-tab-events.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { asyncWindowLeakTest } = require("./leak-utils");
+const { Loader } = require('sdk/test/loader');
+const openWindow = require("sdk/window/utils").open;
+
+exports["test sdk/tab/events does not leak new window"] = function*(assert) {
+ yield asyncWindowLeakTest(assert, _ => {
+ return new Promise(resolve => {
+ let loader = Loader(module);
+ let { events } = loader.require('sdk/tab/events');
+ let w = openWindow();
+ w.addEventListener("load", function windowLoaded(evt) {
+ w.removeEventListener("load", windowLoaded);
+ w.addEventListener("DOMWindowClose", function windowClosed(evt) {
+ w.removeEventListener("DOMWindowClose", windowClosed);
+ resolve(loader);
+ });
+ w.close();
+ });
+ });
+ });
+}
+
+exports["test sdk/tab/events does not leak when attached to existing window"] = function*(assert) {
+ yield asyncWindowLeakTest(assert, _ => {
+ return new Promise(resolve => {
+ let loader = Loader(module);
+ let w = openWindow();
+ w.addEventListener("load", function windowLoaded(evt) {
+ w.removeEventListener("load", windowLoaded);
+ let { events } = loader.require('sdk/tab/events');
+ w.addEventListener("DOMWindowClose", function windowClosed(evt) {
+ w.removeEventListener("DOMWindowClose", windowClosed);
+ resolve(loader);
+ });
+ w.close();
+ });
+ });
+ });
+}
+
+require("sdk/test").run(exports);
diff --git a/addon-sdk/source/test/leak/test-leak-window-events.js b/addon-sdk/source/test/leak/test-leak-window-events.js
new file mode 100644
index 000000000..ceb20f475
--- /dev/null
+++ b/addon-sdk/source/test/leak/test-leak-window-events.js
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// Opening new windows in Fennec causes issues
+module.metadata = {
+ engines: {
+ 'Firefox': '*'
+ }
+};
+
+const { asyncWindowLeakTest } = require("./leak-utils.js");
+const { Loader } = require("sdk/test/loader");
+const { open } = require("sdk/window/utils");
+
+exports["test window/events for leaks"] = function*(assert) {
+ yield asyncWindowLeakTest(assert, _ => {
+ return new Promise((resolve, reject) => {
+ let loader = Loader(module);
+ let { events } = loader.require("sdk/window/events");
+ let { on, off } = loader.require("sdk/event/core");
+
+ on(events, "data", function handler(e) {
+ try {
+ if (e.type === "load") {
+ e.target.close();
+ }
+ else if (e.type === "close") {
+ off(events, "data", handler);
+
+ // Let asyncWindowLeakTest call loader.unload() after the
+ // leak check.
+ resolve(loader);
+ }
+ } catch (e) {
+ reject(e);
+ }
+ });
+
+ // Open a window. This will trigger our data events.
+ open();
+ });
+ });
+};
+
+exports["test window/events for leaks with existing window"] = function*(assert) {
+ yield asyncWindowLeakTest(assert, _ => {
+ return new Promise((resolve, reject) => {
+ let loader = Loader(module);
+ let w = open();
+ w.addEventListener("load", function windowLoaded(evt) {
+ w.removeEventListener("load", windowLoaded);
+ let { events } = loader.require("sdk/window/events");
+ w.addEventListener("DOMWindowClose", function windowClosed(evt) {
+ w.removeEventListener("DOMWindowClose", windowClosed);
+ resolve(loader);
+ });
+ w.close();
+ });
+ });
+ });
+};
+
+require("sdk/test").run(exports);