зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1820485 [wpt PR 38826] - LoAF: Initial support for long scripts, a=testonly
Automatic update from web-platform-tests LoAF: Initial support for long scripts AnimationFrameMonitor signs into the probe system to track scripts. Scripts that are 5ms or longer that are part of a LoAF (long amimation frame) are reported as one of the LoAF entry. Since some of the probes can be recursive, and also layouts/styles can be recursive, AnimationFrameTimingMonitor maintains a little state machine so that only the top-level scripts are captured: - CompileAndRunScript is called for a <script> tag. - ExecuteScript is called for a script tag (after compilation) and also when an imported module is executed. - UserCallback is called specifically for callbacks who implemented it. - CallFunction is called *a lot* and is there to collect source location for the top level script. - WillHandlePromise is added. Note that task that begin with a promise resolution end only when the next microtask queue is emptied. Note the following: - Only script *entry points* are reported, as in, there are no recursions or time-overlaps between scripts. - To reduce overhead, bookekeping is done once it's clear that the script is longer than 5ms. If this still creates too much overhead, we can increase the number and measure even longer scripts only. - The state machine is somewhat similar to the one in PerformanceMonitor. However, the two classes have a different lifecycle and PerformanceMonitor has a lot of legacy, so copying some of the logic seemed less costly than trying to unify. Missing pieces: - Not all the user callbacks are measured/probed, e.g. PerformanceObserver callbacks. This would require a lot of detail and fine-tuning. - At first, PromiseResolver-based entry points don't have a lot of details, e.g. source location and name. This will be done in a later phase. - Queue time and presentation time are still missing. Explainer: https://github.com/w3c/longtasks/blob/loaf-explainer/loaf-explainer.md Design doc: https://docs.google.com/document/d/1SeMd4KbXWZf0ZnRSMvYhjSBpXPBln5xrRyTu2Gr68BY/edit# Change-Id: I57b62ab51b3f1ab28bbfbcc2d992df4cc10d38ec Bug: 1392685 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4268371 Reviewed-by: Jeremy Roman <jbroman@chromium.org> Reviewed-by: Yoav Weiss <yoavweiss@chromium.org> Commit-Queue: Noam Rosenthal <nrosenthal@chromium.org> Cr-Commit-Position: refs/heads/main@{#1114430} -- wpt-commits: b8e9469a49469720bb72a078c4ecde798daa70f5 wpt-pr: 38826
This commit is contained in:
Родитель
b95d3f786b
Коммит
3a9295d799
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: basic</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Long Animation Frame: event handlers</h1>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
test_self_event_listener(t => {
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.src = "/images/green.png";
|
||||||
|
img.addEventListener("load", () => {
|
||||||
|
busy_wait();
|
||||||
|
});
|
||||||
|
document.body.appendChild(img);
|
||||||
|
t.add_cleanup(() => img.remove());
|
||||||
|
}, "IMG.onload");
|
||||||
|
|
||||||
|
test_self_event_listener(t => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/common/dummy.xml");
|
||||||
|
xhr.addEventListener("load", () => {
|
||||||
|
busy_wait();
|
||||||
|
});
|
||||||
|
xhr.send();
|
||||||
|
}, "XMLHttpRequest.onload");
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: requestIdleCallback</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Long Animation Frame: requestIdleCallback</h1>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
setup(() =>
|
||||||
|
assert_implements(window.requestIdleCallback,
|
||||||
|
'requestIdleCallback is not supported.'));
|
||||||
|
|
||||||
|
/*
|
||||||
|
promise_test(async t => {
|
||||||
|
await expect_no_long_frame(() => requestIdleCallback(busy_wait), t);
|
||||||
|
}, 'A long busy wait in an idle callback is not a long animation frame');
|
||||||
|
*/
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const segment_duration = very_long_frame_duration / 2;
|
||||||
|
requestIdleCallback(() => {
|
||||||
|
busy_wait(segment_duration);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
busy_wait(segment_duration);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await expect_long_frame(() => {}, t);
|
||||||
|
}, 'A long busy wait split between an idle callback and a ' +
|
||||||
|
'requestAnimationFrame is a long animation frame');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -15,7 +15,7 @@ const host_info = get_host_info();
|
||||||
const {ORIGIN, REMOTE_ORIGIN, HTTP_NOTSAMESITE_ORIGIN} = host_info;
|
const {ORIGIN, REMOTE_ORIGIN, HTTP_NOTSAMESITE_ORIGIN} = host_info;
|
||||||
|
|
||||||
promise_test(async t => {
|
promise_test(async t => {
|
||||||
const executor = await prepare_exec_iframe(t, ORIGIN);
|
const [executor] = await prepare_exec_iframe(t, ORIGIN);
|
||||||
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
||||||
const deadline = performance.now() + duration;
|
const deadline = performance.now() + duration;
|
||||||
while (performance.now() < deadline) {}
|
while (performance.now() < deadline) {}
|
||||||
|
@ -23,7 +23,7 @@ promise_test(async t => {
|
||||||
}, 'A long busy wait without render in a same-origin iframe is not a long animation frame');
|
}, 'A long busy wait without render in a same-origin iframe is not a long animation frame');
|
||||||
|
|
||||||
promise_test(async t => {
|
promise_test(async t => {
|
||||||
const executor = await prepare_exec_iframe(t, HTTP_NOTSAMESITE_ORIGIN);
|
const [executor] = await prepare_exec_iframe(t, HTTP_NOTSAMESITE_ORIGIN);
|
||||||
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
||||||
const deadline = performance.now() + duration;
|
const deadline = performance.now() + duration;
|
||||||
while (performance.now() < deadline) {}
|
while (performance.now() < deadline) {}
|
||||||
|
@ -31,7 +31,7 @@ promise_test(async t => {
|
||||||
}, 'A long busy wait in a cross-origin iframe is not a long animation frame');
|
}, 'A long busy wait in a cross-origin iframe is not a long animation frame');
|
||||||
|
|
||||||
promise_test(async t => {
|
promise_test(async t => {
|
||||||
const executor = await prepare_exec_iframe(t, ORIGIN);
|
const [executor] = await prepare_exec_iframe(t, ORIGIN);
|
||||||
await expect_long_frame(() => executor.execute_script(async (duration) => {
|
await expect_long_frame(() => executor.execute_script(async (duration) => {
|
||||||
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||||
const deadline = performance.now() + duration;
|
const deadline = performance.now() + duration;
|
||||||
|
@ -40,7 +40,7 @@ promise_test(async t => {
|
||||||
}, 'A long busy wait in a same-origin requestAnimationFrame is a long animation frame');
|
}, 'A long busy wait in a same-origin requestAnimationFrame is a long animation frame');
|
||||||
|
|
||||||
promise_test(async t => {
|
promise_test(async t => {
|
||||||
const executor = await prepare_exec_popup(t, ORIGIN);
|
const [executor] = await prepare_exec_popup(t, ORIGIN);
|
||||||
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
await expect_no_long_frame(() => executor.execute_script((duration) => {
|
||||||
const deadline = performance.now() + duration;
|
const deadline = performance.now() + duration;
|
||||||
while (performance.now() < deadline) {}
|
while (performance.now() < deadline) {}
|
||||||
|
@ -49,7 +49,7 @@ promise_test(async t => {
|
||||||
|
|
||||||
for (const origin of ["ORIGIN", "REMOTE_ORIGIN", "HTTP_NOTSAMESITE_ORIGIN"]) {
|
for (const origin of ["ORIGIN", "REMOTE_ORIGIN", "HTTP_NOTSAMESITE_ORIGIN"]) {
|
||||||
promise_test(async t => {
|
promise_test(async t => {
|
||||||
const executor = await prepare_exec_iframe(t, host_info[origin]);
|
const [executor] = await prepare_exec_iframe(t, host_info[origin]);
|
||||||
const entry = await executor.execute_script(async (duration) => {
|
const entry = await executor.execute_script(async (duration) => {
|
||||||
const entryPromise = new Promise(resolve => new PerformanceObserver(list => {
|
const entryPromise = new Promise(resolve => new PerformanceObserver(list => {
|
||||||
resolve(list.getEntries(0));
|
resolve(list.getEntries(0));
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: basic</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/common/get-host-info.sub.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Long Animation Frame: promise resolvers</h1>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script type="module">
|
||||||
|
|
||||||
|
const {REMOTE_ORIGIN} = get_host_info();
|
||||||
|
|
||||||
|
test_promise_script(async t => {
|
||||||
|
await fetch("/common/dummy.xml");
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
}, "resolve", "Promise.resolve", "fetch");
|
||||||
|
|
||||||
|
test_promise_script(async t => {
|
||||||
|
const response = await fetch("/common/dummy.xml");
|
||||||
|
await response.text();
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
}, "resolve", "Promise.resolve", "Response.text");
|
||||||
|
|
||||||
|
test_promise_script(async t => {
|
||||||
|
const response = await import("/loading/resources/dummy.js");
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
}, "resolve", "Promise.resolve", "import");
|
||||||
|
|
||||||
|
test_promise_script(async t => {
|
||||||
|
fetch(new URL("/common/dummy.xml", REMOTE_ORIGIN).href, {mode: "cors"})
|
||||||
|
.catch(() => {
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
})
|
||||||
|
}, "reject", "Promise.reject" );
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: basic</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Long Animation Frame: script blocks</h1>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
test_self_script_block(t => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.innerHTML = `(${busy_wait.toString()})()`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, location.href, "classic-script");
|
||||||
|
|
||||||
|
test_self_script_block(t => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.type = "module";
|
||||||
|
script.innerHTML = `(${busy_wait.toString()})()`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, location.href, "module-script");
|
||||||
|
|
||||||
|
test_self_script_block(t => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = "resources/busy.js";
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, new URL("resources/busy.js", location.href).href, "classic-script");
|
||||||
|
|
||||||
|
test_self_script_block(t => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = "resources/busy.js";
|
||||||
|
script.type = "module";
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, new URL("resources/busy.js", location.href).href, "module-script");
|
||||||
|
|
||||||
|
test_self_script_block(t => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.type = "module";
|
||||||
|
script.innerHTML = `import("./resources/busy.js?import");`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, new URL("resources/busy.js?import", location.href).href, "execute-script");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: window attribution</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<body>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/common/get-host-info.sub.js"></script>
|
||||||
|
<script src="/common/utils.js"></script>
|
||||||
|
<script src="/common/dispatcher/dispatcher.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const host_info = get_host_info();
|
||||||
|
const {ORIGIN, REMOTE_ORIGIN, HTTP_NOTSAMESITE_ORIGIN} = host_info;
|
||||||
|
|
||||||
|
promise_test (async t => {
|
||||||
|
const [entry, script] = await expect_long_frame_with_script(() => {
|
||||||
|
requestAnimationFrame(() => busy_wait());
|
||||||
|
}, () => true, t);
|
||||||
|
assert_equals(script.windowAttribution, "self");
|
||||||
|
assert_equals(script.window, window);
|
||||||
|
}, 'Scripts in this window should be self-attributed');
|
||||||
|
|
||||||
|
promise_test (async t => {
|
||||||
|
const [executor, iframe] = await prepare_exec_iframe(t, ORIGIN);
|
||||||
|
const [entry, script] = await expect_long_frame_with_script(() =>
|
||||||
|
executor.execute_script(async (duration) => {
|
||||||
|
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||||
|
const deadline = performance.now() + duration;
|
||||||
|
while (performance.now() < deadline) {}
|
||||||
|
}, [very_long_frame_duration]), () => true, t);
|
||||||
|
assert_equals(script.windowAttribution, "descendant");
|
||||||
|
assert_equals(script.window, iframe.contentWindow);
|
||||||
|
}, 'Scripts in subframes should be descendant-attributed');
|
||||||
|
|
||||||
|
promise_test (async t => {
|
||||||
|
const [executor1, iframe1] = await prepare_exec_iframe(t, ORIGIN);
|
||||||
|
const [executor2, iframe2] = await prepare_exec_iframe(t, ORIGIN);
|
||||||
|
const [entry, script] = await expect_long_frame_with_script(() =>
|
||||||
|
executor1.execute_script(async (duration) => {
|
||||||
|
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||||
|
const deadline = performance.now() + duration;
|
||||||
|
while (performance.now() < deadline) {}
|
||||||
|
}, [very_long_frame_duration]), () => true, t);
|
||||||
|
const find_entry = win =>
|
||||||
|
win.performance.getEntriesByType("long-animation-frame").find(
|
||||||
|
e => e.duration >= very_long_frame_duration &&
|
||||||
|
e.scripts.length).scripts[0];
|
||||||
|
|
||||||
|
const iframe1_entry = find_entry(iframe1.contentWindow);
|
||||||
|
const iframe2_entry = find_entry(iframe2.contentWindow);
|
||||||
|
assert_equals(iframe1_entry.windowAttribution, "self");
|
||||||
|
assert_equals(iframe2_entry.windowAttribution, "same-page");
|
||||||
|
assert_equals(iframe1_entry.window, iframe1.contentWindow);
|
||||||
|
assert_equals(iframe2_entry.window, iframe1.contentWindow);
|
||||||
|
}, 'Scripts in subframes should be same-page-attributed to other subframes');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Long Animation Frame Timing: basic</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="resources/utils.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Long Animation Frame: user callbacks</h1>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
test_self_user_callback(t =>
|
||||||
|
t.step_timeout(() => busy_wait()), "Window.setTimeout");
|
||||||
|
|
||||||
|
test_self_user_callback(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
busy_wait();
|
||||||
|
clearInterval(interval);
|
||||||
|
}, 10);
|
||||||
|
}, "Window.setInterval");
|
||||||
|
test_self_user_callback(() =>
|
||||||
|
requestAnimationFrame(() => busy_wait()), "Window.requestAnimationFrame");
|
||||||
|
|
||||||
|
test_self_user_callback(t => {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
document.body.appendChild(element);
|
||||||
|
t.add_cleanup(() => element.remove());
|
||||||
|
new ResizeObserver((entries, observer) => {
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
observer.disconnect();
|
||||||
|
}).observe(element);
|
||||||
|
}, "ResizeObserver.callback");
|
||||||
|
|
||||||
|
test_self_user_callback(t => {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
element.innerText = "123";
|
||||||
|
t.add_cleanup(() => element.remove());
|
||||||
|
new IntersectionObserver((entries, observer) => {
|
||||||
|
busy_wait(very_long_frame_duration);
|
||||||
|
observer.disconnect();
|
||||||
|
}).observe(element);
|
||||||
|
document.body.appendChild(element);
|
||||||
|
}, "IntersectionObserver.callback");
|
||||||
|
|
||||||
|
test_self_user_callback(t =>
|
||||||
|
scheduler.postTask(() => busy_wait()), "Scheduler.postTask");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,4 @@
|
||||||
|
(() => {
|
||||||
|
const deadline = performance.now() + 360;
|
||||||
|
while (performance.now() < deadline) {}
|
||||||
|
})();
|
|
@ -5,14 +5,18 @@ setup(() =>
|
||||||
|
|
||||||
const very_long_frame_duration = 360;
|
const very_long_frame_duration = 360;
|
||||||
|
|
||||||
function loaf_promise() {
|
function loaf_promise(t) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const observer = new PerformanceObserver(entries => {
|
const observer = new PerformanceObserver(entries => {
|
||||||
const entry = entries.getEntries()[0];
|
const entry = entries.getEntries()[0];
|
||||||
if (entry.duration >= very_long_frame_duration)
|
if (entry.duration >= very_long_frame_duration) {
|
||||||
|
observer.disconnect();
|
||||||
resolve(entry);
|
resolve(entry);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.add_cleanup(() => observer.disconnect());
|
||||||
|
|
||||||
observer.observe({entryTypes: ['long-animation-frame']});
|
observer.observe({entryTypes: ['long-animation-frame']});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -28,9 +32,9 @@ async function expect_long_frame(cb, t) {
|
||||||
await windowLoaded;
|
await windowLoaded;
|
||||||
await new Promise(resolve => t.step_timeout(resolve, 0));
|
await new Promise(resolve => t.step_timeout(resolve, 0));
|
||||||
const timeout = new Promise((resolve, reject) =>
|
const timeout = new Promise((resolve, reject) =>
|
||||||
t.step_timeout(() => reject("timeout"), no_long_frame_timeout));
|
t.step_timeout(() => resolve("timeout"), no_long_frame_timeout));
|
||||||
const receivedLongFrame = loaf_promise();
|
const receivedLongFrame = loaf_promise(t);
|
||||||
await cb();
|
await cb(t);
|
||||||
const entry = await Promise.race([
|
const entry = await Promise.race([
|
||||||
receivedLongFrame,
|
receivedLongFrame,
|
||||||
timeout
|
timeout
|
||||||
|
@ -38,10 +42,24 @@ async function expect_long_frame(cb, t) {
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function expect_long_frame_with_script(cb, predicate, t) {
|
||||||
|
for (let i = 0; i < 10; ++i) {
|
||||||
|
const entry = await expect_long_frame(cb, t);
|
||||||
|
if (!entry.scripts.length)
|
||||||
|
continue;
|
||||||
|
for (const script of entry.scripts) {
|
||||||
|
if (predicate(script))
|
||||||
|
return [entry, script];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
async function expect_no_long_frame(cb, t) {
|
async function expect_no_long_frame(cb, t) {
|
||||||
await windowLoaded;
|
await windowLoaded;
|
||||||
for (let i = 0; i < 5; ++i) {
|
for (let i = 0; i < 5; ++i) {
|
||||||
const receivedLongFrame = loaf_promise();
|
const receivedLongFrame = loaf_promise(t);
|
||||||
await cb();
|
await cb();
|
||||||
const result = await Promise.race([receivedLongFrame,
|
const result = await Promise.race([receivedLongFrame,
|
||||||
new Promise(resolve => t.step_timeout(() => resolve("timeout"),
|
new Promise(resolve => t.step_timeout(() => resolve("timeout"),
|
||||||
|
@ -62,7 +80,7 @@ async function prepare_exec_iframe(t, origin) {
|
||||||
iframe.src = url.href;
|
iframe.src = url.href;
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
await new Promise(resolve => iframe.addEventListener("load", resolve));
|
await new Promise(resolve => iframe.addEventListener("load", resolve));
|
||||||
return new RemoteContext(uuid);
|
return [new RemoteContext(uuid), iframe];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,5 +90,38 @@ async function prepare_exec_popup(t, origin) {
|
||||||
url.searchParams.set("uuid", uuid);
|
url.searchParams.set("uuid", uuid);
|
||||||
const popup = window.open(url);
|
const popup = window.open(url);
|
||||||
t.add_cleanup(() => popup.close());
|
t.add_cleanup(() => popup.close());
|
||||||
return new RemoteContext(uuid);
|
return [new RemoteContext(uuid), popup];
|
||||||
|
}
|
||||||
|
function test_loaf_script(cb, name, type, label) {
|
||||||
|
promise_test(async t => {
|
||||||
|
const [entry, script] = await expect_long_frame_with_script(cb,
|
||||||
|
script => (script.type === type && script.duration >= very_long_frame_duration), t);
|
||||||
|
|
||||||
|
assert_true(!!entry, "Entry detected");
|
||||||
|
assert_equals(script.name, name);
|
||||||
|
assert_greater_than_equal(script.duration, very_long_frame_duration);
|
||||||
|
assert_greater_than_equal(entry.duration, script.duration);
|
||||||
|
assert_greater_than_equal(script.executionStart, script.startTime);
|
||||||
|
assert_greater_than_equal(script.startTime, entry.startTime)
|
||||||
|
assert_equals(script.window, window);
|
||||||
|
assert_equals(script.forcedStyleAndLayoutDuration, 0);
|
||||||
|
assert_equals(script.windowAttribution, "self");
|
||||||
|
}, `LoAF script: ${name} ${type},${label ? ` ${label}` : ''}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_self_user_callback(cb, name) {
|
||||||
|
test_loaf_script(cb, name, "user-callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_self_event_listener(cb, name) {
|
||||||
|
test_loaf_script(cb, name, "event-listener");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_promise_script(cb, resolve_or_reject, name, label) {
|
||||||
|
test_loaf_script(cb, name, `${resolve_or_reject}-promise`, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_self_script_block(cb, name, type) {
|
||||||
|
test_loaf_script(cb, name, type);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче