diff options
Diffstat (limited to '')
-rw-r--r-- | dom/events/test/event_leak_utils.js | 84 |
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}` + ); +} |