Bug 1450358 P0 Factor the event listener runtime leak checking test into a reusable test framework. r=baku

This commit is contained in:
Ben Kelly 2018-04-04 11:25:42 -07:00
parent 6cee50b1b0
commit 5e3b64d3ec
4 changed files with 85 additions and 55 deletions

View File

@ -0,0 +1,81 @@
// 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 two GC'd to avoid intermittent delayed collection.
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}`);
}

View File

@ -15,6 +15,7 @@ support-files =
bug418986-3.js
error_event_worker.js
empty.js
event_leak_utils.js
window_bug493251.html
window_bug659071.html
window_wheel_default_action.html

View File

@ -233,6 +233,7 @@ support-files =
sw_storage_not_allow.js
update_worker.sjs
self_update_worker.sjs
!/dom/events/test/event_leak_utils.js
[test_bug1151916.html]
[test_bug1240436.html]

View File

@ -8,26 +8,16 @@
<title>Bug 1447871 - Test some service worker leak conditions</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="/tests/dom/events/test/event_leak_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
const scope = new URL("empty.html?leak_tests", location).href;
const script = new URL("empty.js", location).href;
// 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;
}
// Manipulate service worker DOM objects in the frame's context.
// Its important here that we create a listener callback from
// the DOM objects back to the frame's global in order to
@ -45,31 +35,6 @@ async function useServiceWorker(contentWindow) {
};
}
// This function defines the basic form of the test cases. We create an
// iframe, manipulate some service worker DOM objects, 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 leakTest(name, callback) {
let frame = await withFrame(document, "empty.html");
await useServiceWorker(frame.contentWindow);
let weakRef = SpecialPowers.Cu.getWeakReference(frame.contentWindow);
ok(weakRef.get(), `should be able to create a weak reference - ${name}`);
if (callback) {
await callback(frame);
}
frame.remove();
frame = null;
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}`);
}
async function runTest() {
await SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
@ -81,25 +46,7 @@ async function runTest() {
await waitForState(reg.installing, "activated");
try {
// Test if we leak in the case where we do nothing special to
// the frame before removing it from the DOM.
await leakTest("default");
// Test the case where we navigate the frame before removing it
// from the DOM so that the service worker using window ends up
// in bfcache.
await leakTest("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 service worker using window ends
// up getting replaced.
await leakTest("document.open()", frame => {
frame.contentDocument.open();
frame.contentDocument.close();
});
await checkForEventListenerLeaks("ServiceWorker", useServiceWorker);
} catch (e) {
ok(false, e);
} finally {