Title: [261391] trunk/LayoutTests
Revision
261391
Author
[email protected]
Date
2020-05-08 10:14:15 -0700 (Fri, 08 May 2020)

Log Message

Code pattern in GC tests in LayoutTests is broken
https://bugs.webkit.org/show_bug.cgi?id=211595

Reviewed by Saam Barati.

LayoutTests have several tests which attempt to ensure that document is not leaked. But they are broken since these tests are not correctly
handling conservative GC of _javascript_Core. These tests are taking a following approach.

    1. Allocate *one* iframe doing something inside it.
    2. Remove iframe from the parent document to make it released.
    3. Repeatedly invoke GC and test whether (1)'s document gets collected.

This is not the right approach. Since _javascript_Core has conservative GC, it is easily possible that (1)'s pointer value remains in some of
conservative roots: CPU registers, stack etc. And _javascript_Core conservative GC scans it and keeps it alive. The above approach makes the
test super flaky and any unrelated changes in _javascript_Core and C compiler can make this test failed.

This is not a problem in practice. Web pages are executing various kind of code and they clobber conservative roots. However, tests in
LayoutTests are too simple to keep it alive accidentally.

The right approach for conservative GC is the following.

    1. Allocate *many* iframes doing something inside them with loop. By creating iframes with loop, for every iteration, it is likely that
       the same CPU registers and stack locations are overwritten by the last created iframe reference. This dramatically reduces the possibility
       for GC to find these addresses in conservative roots.
    2. Remove iframes from the parent document to make them released.
    3. Repeatedly invoke GC and test whether *one of (1) iframes* gets collected at least. Theoretically this is still possible that completely
       unrelated integer value in conservative roots can point to the reference of (1). By allocating many iframes in (1) and testing one of them,
       we can reduce the possibility of this conflict.

This patch adds testDocumentIsNotLeaked helper function to enforce this pattern. And rewrite broken tests with this helper to make it less-flaky.

* highlight/highlight-world-leak.html:
* http/tests/IndexedDB/collect-IDB-objects.https.html:
* http/tests/media/clearkey/collect-webkit-media-session.html:
* http/tests/media/media-stream/collect-media-devices.https.html:
* http/tests/resources/gc.js: Added.
(window.gc.gcRec):
(window.gc.window.gc):
(nukeArray):
(async testDocumentIsNotLeaked):
* intersection-observer/no-document-leak.html:
* performance-api/performance-observer-no-document-leak.html:
* resources/gc.js:
(nukeArray):
(async testDocumentIsNotLeaked):
* webanimations/leak-document-with-web-animation.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261390 => 261391)


--- trunk/LayoutTests/ChangeLog	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/ChangeLog	2020-05-08 17:14:15 UTC (rev 261391)
@@ -1,3 +1,52 @@
+2020-05-07  Yusuke Suzuki  <[email protected]>
+
+        Code pattern in GC tests in LayoutTests is broken
+        https://bugs.webkit.org/show_bug.cgi?id=211595
+
+        Reviewed by Saam Barati.
+
+        LayoutTests have several tests which attempt to ensure that document is not leaked. But they are broken since these tests are not correctly
+        handling conservative GC of _javascript_Core. These tests are taking a following approach.
+
+            1. Allocate *one* iframe doing something inside it.
+            2. Remove iframe from the parent document to make it released.
+            3. Repeatedly invoke GC and test whether (1)'s document gets collected.
+
+        This is not the right approach. Since _javascript_Core has conservative GC, it is easily possible that (1)'s pointer value remains in some of
+        conservative roots: CPU registers, stack etc. And _javascript_Core conservative GC scans it and keeps it alive. The above approach makes the
+        test super flaky and any unrelated changes in _javascript_Core and C compiler can make this test failed.
+
+        This is not a problem in practice. Web pages are executing various kind of code and they clobber conservative roots. However, tests in
+        LayoutTests are too simple to keep it alive accidentally.
+
+        The right approach for conservative GC is the following.
+
+            1. Allocate *many* iframes doing something inside them with loop. By creating iframes with loop, for every iteration, it is likely that
+               the same CPU registers and stack locations are overwritten by the last created iframe reference. This dramatically reduces the possibility
+               for GC to find these addresses in conservative roots.
+            2. Remove iframes from the parent document to make them released.
+            3. Repeatedly invoke GC and test whether *one of (1) iframes* gets collected at least. Theoretically this is still possible that completely
+               unrelated integer value in conservative roots can point to the reference of (1). By allocating many iframes in (1) and testing one of them,
+               we can reduce the possibility of this conflict.
+
+        This patch adds testDocumentIsNotLeaked helper function to enforce this pattern. And rewrite broken tests with this helper to make it less-flaky.
+
+        * highlight/highlight-world-leak.html:
+        * http/tests/IndexedDB/collect-IDB-objects.https.html:
+        * http/tests/media/clearkey/collect-webkit-media-session.html:
+        * http/tests/media/media-stream/collect-media-devices.https.html:
+        * http/tests/resources/gc.js: Added.
+        (window.gc.gcRec):
+        (window.gc.window.gc):
+        (nukeArray):
+        (async testDocumentIsNotLeaked):
+        * intersection-observer/no-document-leak.html:
+        * performance-api/performance-observer-no-document-leak.html:
+        * resources/gc.js:
+        (nukeArray):
+        (async testDocumentIsNotLeaked):
+        * webanimations/leak-document-with-web-animation.html:
+
 2020-05-08  Lauro Moura  <[email protected]>
 
         [GTK][WPE] Create common glib expectation file

Modified: trunk/LayoutTests/highlight/highlight-world-leak.html (261390 => 261391)


--- trunk/LayoutTests/highlight/highlight-world-leak.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/highlight/highlight-world-leak.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -1,36 +1,48 @@
 <!DOCTYPE html>
 <html>
 <body>
-<iframe id="testFrame" src=""
 <script src=""
+<script src=""
 <script>
 description("Tests that using Highlight does not cause the document to get leaked.");
 window.jsTestIsAsync = true;
 
-function documentShouldDie(documentIdentifier)
-{
-    return new Promise(function(resolve, reject) {
-        handle = setInterval(function() {
-            gc();
-            if (internals && !internals.isDocumentAlive(documentIdentifier)) {
-                clearInterval(handle);
-                resolve();
+window._onload_ = function () {
+    testDocumentIsNotLeaked(
+        async function initAndRemove(frameCount)
+        {
+            let frames = await new Promise((resolve, reject) => {
+                let frames = [];
+                let counter = 0;
+                function onLoad() {
+                    counter++;
+                    if (counter == frameCount)
+                        resolve(frames);
+                }
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = document.createElement('iframe');
+                    frame.src = ""
+                    frame._onload_ = onLoad;
+                    document.body.appendChild(frame);
+                    frames.push(frame);
+                }
+            });
+            let frameIdentifiers = [];
+            for (let i = 0; i < frameCount; ++i) {
+                let frame = frames[i];
+                frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                frame.remove();
             }
-        }, 10);
-    });
-}
+            nukeArray(frames);
+            frames = null;
+            return frameIdentifiers;
+        }
+    ).then(
+        () => testPassed("Document did not leak"),
+        (error) => testFailed(error.message)
+    ).finally(finishJSTest);
+};
 
-var testFrame = document.getElementById("testFrame");
-_onload_ = function() {
-    let frameDocumentIdentifier = internals.documentIdentifier(testFrame.contentDocument);
-    testFrame.remove();
-    setTimeout(function() {
-        documentShouldDie(frameDocumentIdentifier).then(function() {
-            testPassed("Document did not leak");
-            finishJSTest();
-        });
-    });
-};
 </script>
 <script src=""
 </body>

Modified: trunk/LayoutTests/http/tests/IndexedDB/collect-IDB-objects.https.html (261390 => 261391)


--- trunk/LayoutTests/http/tests/IndexedDB/collect-IDB-objects.https.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/http/tests/IndexedDB/collect-IDB-objects.https.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,47 +2,51 @@
 <meta charset="utf-8">
 <script src=""
 <script src=""
+<script src=""
 <script>
-function waitFor(duration)
-{
-    return new Promise((resolve) => setTimeout(resolve, duration));
-}
 
-var resolveCallback, rejectCallback;
 var promise = new Promise((resolve, reject) => {
-    resolveCallback = resolve;
-    rejectCallback = reject;
+    if (!window.internals) {
+        reject("Test require internals API");
+        return;
+    }
+    window._onload_ = function () {
+        let promise = testDocumentIsNotLeaked(
+            async function initAndRemove(frameCount)
+            {
+                let frames = await new Promise((resolve, reject) => {
+                    let frames = [];
+                    let counter = 0;
+                    function onMessage() {
+                        counter++;
+                        if (counter == frameCount)
+                            resolve(frames);
+                    }
+                    window.addEventListener("message", onMessage);
+                    for (let i = 0; i < frameCount; ++i) {
+                        let frame = document.createElement('iframe');
+                        frame.src = ""
+                        document.body.appendChild(frame);
+                        frames.push(frame);
+                    }
+                });
+                let frameIdentifiers = [];
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = frames[i];
+                    frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                    frame.src = ""
+                }
+                nukeArray(frames);
+                frames = null;
+                return frameIdentifiers;
+            }
+        );
+        resolve(promise);
+    };
 });
 
-async function done()
-{
-    try {
-        const frameIdentifier = internals.documentIdentifier(iframe.contentDocument);
-        iframe.src = ""
-        let counter = 0;
-        while (++counter < 50) {
-            if (!internals.isDocumentAlive(frameIdentifier)) {
-                resolveCallback();
-                return;
-            }
-            if (window.GCController)
-                GCController.collect();
-
-            await waitFor(50);
-        }
-        rejectCallback("Test failed");
-    } catch (e) {
-        rejectCallback("Test failed: exception " + e);
-    }
-}
-
-window.addEventListener("message", done);
-
 promise_test((test) => {
-    if (!window.internals)
-        rejectCallback("Test require internals API");
     return promise;
 }, "Ensuring frame document gets collected after being stopped in the middle of IDB operations");
 
 </script>
-<iframe src="" id="iframe"></iframe>

Modified: trunk/LayoutTests/http/tests/media/clearkey/collect-webkit-media-session.html (261390 => 261391)


--- trunk/LayoutTests/http/tests/media/clearkey/collect-webkit-media-session.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/http/tests/media/clearkey/collect-webkit-media-session.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,47 +2,51 @@
 <meta charset="utf-8">
 <script src=""
 <script src=""
+<script src=""
 <script>
-function waitFor(duration)
-{
-    return new Promise((resolve) => setTimeout(resolve, duration));
-}
 
-var resolveCallback, rejectCallback;
 var promise = new Promise((resolve, reject) => {
-    resolveCallback = resolve;
-    rejectCallback = reject;
+    if (!window.internals) {
+        reject("Test require internals API");
+        return;
+    }
+    window._onload_ = function () {
+        let promise = testDocumentIsNotLeaked(
+            async function initAndRemove(frameCount)
+            {
+                let frames = await new Promise((resolve, reject) => {
+                    let frames = [];
+                    let counter = 0;
+                    function onMessage() {
+                        counter++;
+                        if (counter == frameCount)
+                            resolve(frames);
+                    }
+                    window.addEventListener("message", onMessage);
+                    for (let i = 0; i < frameCount; ++i) {
+                        let frame = document.createElement('iframe');
+                        frame.src = ""
+                        document.body.appendChild(frame);
+                        frames.push(frame);
+                    }
+                });
+                let frameIdentifiers = [];
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = frames[i];
+                    frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                    frame.src = ""
+                }
+                nukeArray(frames);
+                frames = null;
+                return frameIdentifiers;
+            }
+        );
+        resolve(promise);
+    };
 });
 
-async function done()
-{
-    try {
-        const frameIdentifier = internals.documentIdentifier(iframe.contentDocument);
-        iframe.src = ""
-        let counter = 0;
-        while (++counter < 50) {
-            if (!internals.isDocumentAlive(frameIdentifier)) {
-                resolveCallback();
-                return;
-            }
-            if (window.GCController)
-                GCController.collect();
-
-            await waitFor(50);
-        }
-        rejectCallback("Test failed");
-    } catch (e) {
-        rejectCallback("Test failed: exception " + e);
-    }
-}
-
-window.addEventListener("message", done);
-
 promise_test((test) => {
-    if (!window.internals)
-        rejectCallback("Test require internals API");
     return promise;
 }, "Ensure that the frame's document get collected after being stopped while doing some webkit media session calls");
 
 </script>
-<iframe src="" id="iframe"></iframe>

Modified: trunk/LayoutTests/http/tests/media/media-stream/collect-media-devices.https.html (261390 => 261391)


--- trunk/LayoutTests/http/tests/media/media-stream/collect-media-devices.https.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/http/tests/media/media-stream/collect-media-devices.https.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,47 +2,51 @@
 <meta charset="utf-8">
 <script src=""
 <script src=""
+<script src=""
 <script>
-function waitFor(duration)
-{
-    return new Promise((resolve) => setTimeout(resolve, duration));
-}
 
-var resolveCallback, rejectCallback;
 var promise = new Promise((resolve, reject) => {
-    resolveCallback = resolve;
-    rejectCallback = reject;
+    if (!window.internals) {
+        reject("Test require internals API");
+        return;
+    }
+    window._onload_ = function () {
+        let promise = testDocumentIsNotLeaked(
+            async function initAndRemove(frameCount)
+            {
+                let frames = await new Promise((resolve, reject) => {
+                    let frames = [];
+                    let counter = 0;
+                    function onMessage() {
+                        counter++;
+                        if (counter == frameCount)
+                            resolve(frames);
+                    }
+                    window.addEventListener("message", onMessage);
+                    for (let i = 0; i < frameCount; ++i) {
+                        let frame = document.createElement('iframe');
+                        frame.src = ""
+                        document.body.appendChild(frame);
+                        frames.push(frame);
+                    }
+                });
+                let frameIdentifiers = [];
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = frames[i];
+                    frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                    frame.src = ""
+                }
+                nukeArray(frames);
+                frames = null;
+                return frameIdentifiers;
+            }
+        );
+        resolve(promise);
+    };
 });
 
-async function done()
-{
-    try {
-        const frameIdentifier = internals.documentIdentifier(iframe.contentDocument);
-        iframe.src = ""
-        let counter = 0;
-        while (++counter < 50) {
-            if (!internals.isDocumentAlive(frameIdentifier)) {
-                resolveCallback();
-                return;
-            }
-            if (window.GCController)
-                GCController.collect();
-
-            await waitFor(50);
-        }
-        rejectCallback("Test failed");
-    } catch (e) {
-        rejectCallback("Test failed: exception " + e);
-    }
-}
-
-window.addEventListener("message", done);
-
 promise_test((test) => {
-    if (!window.internals)
-        rejectCallback("Test require internals API");
     return promise;
 }, "Ensure that the frame's document get collected after being stopped while using MediaDevices");
 
 </script>
-<iframe src="" id="iframe"></iframe>

Added: trunk/LayoutTests/http/tests/resources/gc.js (0 => 261391)


--- trunk/LayoutTests/http/tests/resources/gc.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resources/gc.js	2020-05-08 17:14:15 UTC (rev 261391)
@@ -0,0 +1,57 @@
+// If there is no window.gc() already defined, define one using the best
+// method we can find.
+// The slow fallback should not hit in the actual test environment.
+if (!window.gc)
+{
+    window.gc = function()
+    {
+        if (window.GCController)
+            return GCController.collect();
+
+        console.warn('Tests are running without the ability to do manual garbage collection. They will still work, but coverage will be suboptimal.');
+        function gcRec(n) {
+            if (n < 1)
+                return {};
+            var temp = {i: "ab" + i + (i / 100000)};
+            temp += "foo";
+            gcRec(n-1);
+        }
+        for (var i = 0; i < 10000; i++)
+            gcRec(10);
+    }
+}
+
+// Fill array with null to avoid potential GC reachability. frames = null can be enough in 99.9% cases,
+// but we are extra careful to make tests non-flaky: considering about the case that array is captured
+// somewhere (like DFG constants).
+function nukeArray(array)
+{
+    for (let index = 0; index < array.length; ++index)
+        array[index] = null;
+    array.length = 0;
+}
+
+async function testDocumentIsNotLeaked(init, options = {})
+{
+    let gcCount = options.gcCount ?? 50;
+    let documentCount = options.documentCount ?? 20;
+    let additionalCheck = options.additionalCheck ?? function () { return true; };
+
+    function waitFor(duration)
+    {
+        return new Promise((resolve) => setTimeout(resolve, duration));
+    }
+
+    let frameIdentifiers = await init(documentCount);
+    for (let counter = 0; counter < gcCount; ++counter) {
+        for (let i = 0; i < documentCount; ++i) {
+            let frameIdentifier = frameIdentifiers[i];
+            if (!internals.isDocumentAlive(frameIdentifier) && additionalCheck())
+                return "Document did not leak";
+        }
+        gc();
+        await waitFor(50);
+    }
+
+    throw new Error("Document is leaked");
+}

Modified: trunk/LayoutTests/intersection-observer/no-document-leak.html (261390 => 261391)


--- trunk/LayoutTests/intersection-observer/no-document-leak.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/intersection-observer/no-document-leak.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,37 +2,60 @@
 <html>
 <head>
 <script src=""
+<script src=""
 </head>
 <body>
-<iframe id="testFrame" src=""
+<iframe id="testFrame" src=""
 <script>
 description("Tests that using IntersectionObserver does not cause the document to get leaked.");
 window.jsTestIsAsync = true;
 
-function documentShouldDie(documentIdentifier)
-{
-    return new Promise(function(resolve, reject) {
-        handle = setInterval(function() {
-            gc();
-            if (internals && !internals.isDocumentAlive(documentIdentifier) && internals.numberOfIntersectionObservers(document) == 0) {
-                clearInterval(handle);
-                resolve();
+let totalCount = 0;
+window._onload_ = function () {
+    testDocumentIsNotLeaked(
+        async function initAndRemove(frameCount)
+        {
+            totalCount = frameCount;
+            let frames = await new Promise((resolve, reject) => {
+                let frames = [];
+                let counter = 0;
+                function onLoad() {
+                    counter++;
+                    if (counter == frameCount)
+                        resolve(frames);
+                }
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = document.createElement('iframe');
+                    frame.src = ""
+                    frame._onload_ = onLoad;
+                    document.body.appendChild(frame);
+                    frames.push(frame);
+                }
+            });
+            totalCount = internals.numberOfIntersectionObservers(document);
+            let frameIdentifiers = [];
+            for (let i = 0; i < frameCount; ++i) {
+                let frame = frames[i];
+                frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                frame.remove();
             }
-        }, 10);
-    });
-}
+            nukeArray(frames);
+            frames = null;
+            return frameIdentifiers;
+        },
+        {
+            additionalCheck: function()
+            {
+                let count = internals.numberOfIntersectionObservers(document);
+                return count < totalCount || count === 0;
+            }
+        }
+    ).then(
+        () => testPassed("Document did not leak"),
+        (error) => testFailed(error.message)
+    ).finally(finishJSTest);
+};
 
-var testFrame = document.getElementById("testFrame");
-testFrame._onload_ = function() {
-    let frameDocumentIdentifier = internals.documentIdentifier(testFrame.contentDocument);
-    testFrame.remove();
-    setTimeout(function() {
-        documentShouldDie(frameDocumentIdentifier).then(function() {
-            testPassed("Document did not leak");
-            finishJSTest();
-        });
-    });
-};
 </script>
 <script src=""
 </body>

Modified: trunk/LayoutTests/performance-api/performance-observer-no-document-leak.html (261390 => 261391)


--- trunk/LayoutTests/performance-api/performance-observer-no-document-leak.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/performance-api/performance-observer-no-document-leak.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,36 +2,49 @@
 <html>
 <head>
 <script src=""
+<script src=""
 </head>
 <body>
-<iframe id="testFrame" src=""
 <script>
 description("Tests that using PerformanceObserver does not cause the document to get leaked.");
 window.jsTestIsAsync = true;
 
-function documentShouldDie(documentIdentifier)
-{
-    return new Promise(function(resolve, reject) {
-        handle = setInterval(function() {
-            gc();
-            if (!internals.isDocumentAlive(documentIdentifier)) {
-                clearInterval(handle);
-                resolve();
+window._onload_ = function () {
+    testDocumentIsNotLeaked(
+        async function initAndRemove(frameCount)
+        {
+            let frames = await new Promise((resolve, reject) => {
+                let frames = [];
+                let counter = 0;
+                function onLoad() {
+                    counter++;
+                    if (counter == frameCount)
+                        resolve(frames);
+                }
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = document.createElement('iframe');
+                    frame.src = ""
+                    frame._onload_ = onLoad;
+                    document.body.appendChild(frame);
+                    frames.push(frame);
+                }
+            });
+            let frameIdentifiers = [];
+            for (let i = 0; i < frameCount; ++i) {
+                let frame = frames[i];
+                frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                frame.remove();
             }
-        }, 10);
-    });
-}
+            nukeArray(frames);
+            frames = null;
+            return frameIdentifiers;
+        }
+    ).then(
+        () => testPassed("Document did not leak"),
+        (error) => testFailed(error.message)
+    ).finally(finishJSTest);
+};
 
-_onload_ = function() {
-    setTimeout(function() {
-        let frameDocumentIdentifier = internals.documentIdentifier(testFrame.contentDocument);
-        testFrame.remove();
-        documentShouldDie(frameDocumentIdentifier).then(function() {
-            testPassed("Document did not leak");
-            finishJSTest();
-        });
-    }, 10);
-}
 </script>
 <script src=""
 </body>

Modified: trunk/LayoutTests/resources/gc.js (261390 => 261391)


--- trunk/LayoutTests/resources/gc.js	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/resources/gc.js	2020-05-08 17:14:15 UTC (rev 261391)
@@ -20,3 +20,38 @@
             gcRec(10);
     }
 }
+
+// Fill array with null to avoid potential GC reachability. frames = null can be enough in 99.9% cases,
+// but we are extra careful to make tests non-flaky: considering about the case that array is captured
+// somewhere (like DFG constants).
+function nukeArray(array)
+{
+    for (let index = 0; index < array.length; ++index)
+        array[index] = null;
+    array.length = 0;
+}
+
+async function testDocumentIsNotLeaked(init, options = {})
+{
+    let gcCount = options.gcCount ?? 50;
+    let documentCount = options.documentCount ?? 20;
+    let additionalCheck = options.additionalCheck ?? function () { return true; };
+
+    function waitFor(duration)
+    {
+        return new Promise((resolve) => setTimeout(resolve, duration));
+    }
+
+    let frameIdentifiers = await init(documentCount);
+    for (let counter = 0; counter < gcCount; ++counter) {
+        for (let i = 0; i < documentCount; ++i) {
+            let frameIdentifier = frameIdentifiers[i];
+            if (!internals.isDocumentAlive(frameIdentifier) && additionalCheck())
+                return "Document did not leak";
+        }
+        gc();
+        await waitFor(50);
+    }
+
+    throw new Error("Document is leaked");
+}

Modified: trunk/LayoutTests/webanimations/leak-document-with-web-animation.html (261390 => 261391)


--- trunk/LayoutTests/webanimations/leak-document-with-web-animation.html	2020-05-08 17:13:36 UTC (rev 261390)
+++ trunk/LayoutTests/webanimations/leak-document-with-web-animation.html	2020-05-08 17:14:15 UTC (rev 261391)
@@ -2,49 +2,49 @@
 <html>
 <body _onload_="runTest()">
 <script src=""
+<script src=""
 <script>
 description("This test asserts that Document doesn't leak when a Web Animation is created.");
 
-if (window.internals)
+if (window.internals) {
     jsTestIsAsync = true;
 
-function runTest() {
-    if (!window.internals)
-        return;
-
-    var frame = document.body.appendChild(document.createElement("iframe"));
-
-    frame._onload_ = function() {
-        if (frame.src ="" 'about:blank')
-            return true;
-
-        documentIdentifier = internals.documentIdentifier(frame.contentDocument);
-        debug("The iframe has finished loading.");
-
-        frame.remove();
-        frame = null;
-
-        gc();
-        timeout = 0;
-        handle = setInterval(() => {
-            if (!internals.isDocumentAlive(documentIdentifier)) {
-                clearInterval(handle);
-                testPassed("The document was destroyed");
-                finishJSTest();
-                return;
+    window._onload_ = function () {
+        testDocumentIsNotLeaked(
+            async function initAndRemove(frameCount)
+            {
+                let frames = await new Promise((resolve, reject) => {
+                    let frames = [];
+                    let counter = 0;
+                    function onLoad() {
+                        counter++;
+                        if (counter == frameCount)
+                            resolve(frames);
+                    }
+                    for (let i = 0; i < frameCount; ++i) {
+                        let frame = document.createElement('iframe');
+                        frame.src = ""
+                        frame._onload_ = onLoad;
+                        document.body.appendChild(frame);
+                        frames.push(frame);
+                    }
+                });
+                debug("The iframe has finished loading.");
+                let frameIdentifiers = [];
+                for (let i = 0; i < frameCount; ++i) {
+                    let frame = frames[i];
+                    frameIdentifiers.push(internals.documentIdentifier(frame.contentDocument));
+                    frame.remove();
+                }
+                nukeArray(frames);
+                frames = null;
+                return frameIdentifiers;
             }
-            timeout++;
-            if (timeout == 500) {
-                clearInterval(handle);
-                testFailed("The document was leaked");
-                finishJSTest();
-                return;
-            }
-            gc();
-        }, 10);
-    }
-
-    frame.src = '';
+        ).then(
+            () => testPassed("The document was destroyed"),
+            (error) => testFailed(error.message)
+        ).finally(finishJSTest);
+    };
 }
 
 </script>
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to