summaryrefslogtreecommitdiffstats
path: root/dom/events/test/event_leak_utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/test/event_leak_utils.js')
-rw-r--r--dom/events/test/event_leak_utils.js84
1 files changed, 84 insertions, 0 deletions
diff --git a/dom/events/test/event_leak_utils.js b/dom/events/test/event_leak_utils.js
new file mode 100644
index 0000000000..86b26a4136
--- /dev/null
+++ b/dom/events/test/event_leak_utils.js
@@ -0,0 +1,84 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// This function runs a number of tests where:
+//
+// 1. An iframe is created
+// 2. The target callback is executed with the iframe's contentWindow as
+// an argument.
+// 3. The iframe is destroyed and GC is forced.
+// 4. Verifies that the iframe's contentWindow has been GC'd.
+//
+// Different ways of destroying the iframe are checked. Simple
+// remove(), destruction via bfcache, or replacement by document.open().
+//
+// Please pass a target callback that exercises the API under
+// test using the given window. The callback should try to leave the
+// API active to increase the liklihood of provoking an API. Any activity
+// should be canceled by the destruction of the window.
+async function checkForEventListenerLeaks(name, target) {
+ // Test if we leak in the case where we do nothing special to
+ // the frame before removing it from the DOM.
+ await _eventListenerLeakStep(target, `${name} default`);
+
+ // Test the case where we navigate the frame before removing it
+ // from the DOM so that the window using the target API ends up
+ // in bfcache.
+ await _eventListenerLeakStep(target, `${name} bfcache`, frame => {
+ frame.src = "about:blank";
+ return new Promise(resolve => (frame.onload = resolve));
+ });
+
+ // Test the case where we document.open() the frame before removing
+ // it from the DOM so that the window using the target API ends
+ // up getting replaced.
+ await _eventListenerLeakStep(target, `${name} document.open()`, frame => {
+ frame.contentDocument.open();
+ frame.contentDocument.close();
+ });
+}
+
+// ----------------
+// Internal helpers
+// ----------------
+
+// Utility function to create a loaded iframe.
+async function _withFrame(doc, url) {
+ let frame = doc.createElement("iframe");
+ frame.src = url;
+ doc.body.appendChild(frame);
+ await new Promise(resolve => (frame.onload = resolve));
+ return frame;
+}
+
+// This function defines the basic form of the test cases. We create an
+// iframe, execute the target callback to manipulate the DOM, remove the frame
+// from the DOM, and then check to see if the frame was GC'd. The caller
+// may optionally pass in a callback that will be executed with the
+// frame as an argument before removing it from the DOM.
+async function _eventListenerLeakStep(target, name, extra) {
+ let frame = await _withFrame(document, "empty.html");
+
+ await target(frame.contentWindow);
+
+ let weakRef = SpecialPowers.Cu.getWeakReference(frame.contentWindow);
+ ok(weakRef.get(), `should be able to create a weak reference - ${name}`);
+
+ if (extra) {
+ await extra(frame);
+ }
+
+ frame.remove();
+ frame = null;
+
+ // Perform many GC's to avoid intermittent delayed collection.
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+
+ ok(
+ !weakRef.get(),
+ `iframe content window should be garbage collected - ${name}`
+ );
+}