Skip to content

Commit

Permalink
Add leak tests for NodeIterator and TreeWalker
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=119718

Reviewed by Darin Adler, Geoffrey Garen and Chris Dumez.

Add the leak tests based on https://chromium.googlesource.com/chromium/blink/+/65db79784ecd4a7e744d10a26befe5159638a56c
These tests have been modified to account for the fact WebKit / JSC uses a conservative GC as opposed to V8's precise GC.

* LayoutTests/fast/dom/NodeIterator/NodeIterator-dont-overcollect-expected.txt: Added.
* LayoutTests/fast/dom/NodeIterator/NodeIterator-dont-overcollect.html: Added.
* LayoutTests/fast/dom/NodeIterator/NodeIterator-leak-document-expected.txt: Added.
* LayoutTests/fast/dom/NodeIterator/NodeIterator-leak-document.html: Added.
* LayoutTests/fast/dom/TreeWalker/TreeWalker-leak-document-expected.txt: Added.
* LayoutTests/fast/dom/TreeWalker/TreeWalker-leak-document.html: Added.
* LayoutTests/fast/dom/resources/leak-check.js: Added.
(getCounterValues):
(loadSourceIntoIframe):
(async doLeakTest):
(htmlToUrl):
(grabScriptText):

Canonical link: https://commits.webkit.org/254711@main
  • Loading branch information
rniwa committed Sep 21, 2022
1 parent 543702e commit b17ccd4
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 0 deletions.
@@ -0,0 +1,10 @@
This test asserts that NodeIterator's callback is kept alive while NodeIterator is alive even if all JS references go away.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS callbackWasTriggered is true
PASS successfullyParsed is true

TEST COMPLETE

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<body>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../../resources/gc.js"></script>
<script>
description("This test asserts that NodeIterator's callback is kept alive while NodeIterator is alive even if all JS references go away.")
var callbackWasTriggered = false;
var callback = function(node) {
callbackWasTriggered = true;
return NodeFilter.FILTER_ACCEPT;
};
var nodeIterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, callback, false);
gc();
callback = null;
gc();
nodeIterator.nextNode();
shouldBeTrue('callbackWasTriggered');
nodeIterator = null;
gc();
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>
@@ -0,0 +1,10 @@
This test asserts that document doesn't leak when a NodeFilter callback referencing the document is created.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS endingDocumentCount - startingDocumentCount < testingCount * 0.8 is true
PASS successfullyParsed is true

TEST COMPLETE

16 changes: 16 additions & 0 deletions LayoutTests/fast/dom/NodeIterator/NodeIterator-leak-document.html
@@ -0,0 +1,16 @@
<html>
<body>
<script id='targetJS' type='text/html'>
keepNodeIterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, function(node) { return NodeFilter.SHOW_ELEMENT; }, false);
</script>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../../resources/gc.js"></script>
<script src="../resources/leak-check.js"></script>
<script>
description("This test asserts that document doesn't leak when a NodeFilter callback referencing the document is created.");
var target = '<script>'+grabScriptText('targetJS')+'<'+'/script>';
doLeakTest(htmlToUrl(target));
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>
@@ -0,0 +1,10 @@
This test asserts that document doesn't leak when a NodeFilter callback referencing the document is created.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS endingDocumentCount - startingDocumentCount < testingCount * 0.8 is true
PASS successfullyParsed is true

TEST COMPLETE

16 changes: 16 additions & 0 deletions LayoutTests/fast/dom/TreeWalker/TreeWalker-leak-document.html
@@ -0,0 +1,16 @@
<html>
<body>
<script id='targetJS' type='text/html'>
keepTreeWalker = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT, function(node) { return NodeFilter.SHOW_ELEMENT; }, false);
</script>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../../resources/gc.js"></script>
<script src="../resources/leak-check.js"></script>
<script>
description("This test asserts that document doesn't leak when a NodeFilter callback referencing the document is created.");
var target = '<script>'+grabScriptText('targetJS')+'<'+'/script>';
doLeakTest(htmlToUrl(target));
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>
43 changes: 43 additions & 0 deletions LayoutTests/fast/dom/resources/leak-check.js
@@ -0,0 +1,43 @@
// include fast/js/resources/js-test-pre.js before this file.
jsTestIsAsync = true;
let startingDocumentCount;
let endingDocumentCount;
const testingCount = 20;

async function doLeakTest(src, tolerance) {
const frame = document.createElement('frame');
document.body.appendChild(frame);

function loadSourceIntoIframe(src) {
return new Promise((resolve) => {
var originalSrc = frame.src;
frame.onload = resolve;
frame.src = src;
})
}

if (!window.internals) {
debug("This test only runs on DumpRenderTree, as it requires existence of window.internals and cross-domain resource access check disabled.");
finishJSTest();
}

await loadSourceIntoIframe('about:blank');
startingDocumentCount = internals.numberOfLiveDocuments();
for (let i = 0; i < testingCount; ++i) {
await loadSourceIntoIframe(src);
await loadSourceIntoIframe('about:blank');
gc();
}
endingDocumentCount = internals.numberOfLiveDocuments();
shouldBeTrue('endingDocumentCount - startingDocumentCount < testingCount * 0.8');
finishJSTest();
}

function htmlToUrl(html) {
return 'data:text/html;charset=utf-8,' + html;
}

function grabScriptText(id) {
return document.getElementById(id).innerText;
}
// include fast/js/resources/js-test-post.js after this file.

0 comments on commit b17ccd4

Please sign in to comment.