| const windowLoaded = new Promise(resolve => window.addEventListener('load', resolve)); |
| setup(() => |
| assert_implements(window.PerformanceLongAnimationFrameTiming, |
| 'Long animation frames are not supported.')); |
| |
| const very_long_frame_duration = 360; |
| |
| function loaf_promise(t) { |
| return new Promise(resolve => { |
| const observer = new PerformanceObserver(entries => { |
| const entry = entries.getEntries()[0]; |
| if (entry.duration >= very_long_frame_duration) { |
| observer.disconnect(); |
| resolve(entry); |
| } |
| }); |
| |
| t.add_cleanup(() => observer.disconnect()); |
| |
| observer.observe({entryTypes: ['long-animation-frame']}); |
| }); |
| } |
| |
| const no_long_frame_timeout = very_long_frame_duration * 2; |
| |
| function busy_wait(ms_delay = very_long_frame_duration) { |
| const deadline = performance.now() + ms_delay; |
| while (performance.now() < deadline) {} |
| } |
| |
| async function expect_long_frame(cb, t) { |
| await windowLoaded; |
| await new Promise(resolve => t.step_timeout(resolve, 0)); |
| const timeout = new Promise((resolve, reject) => |
| t.step_timeout(() => resolve("timeout"), no_long_frame_timeout)); |
| const receivedLongFrame = loaf_promise(t); |
| await cb(t); |
| const entry = await Promise.race([ |
| receivedLongFrame, |
| timeout |
| ]); |
| 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) { |
| await windowLoaded; |
| for (let i = 0; i < 5; ++i) { |
| const receivedLongFrame = loaf_promise(t); |
| await cb(); |
| const result = await Promise.race([receivedLongFrame, |
| new Promise(resolve => t.step_timeout(() => resolve("timeout"), |
| no_long_frame_timeout))]); |
| if (result === "timeout") |
| return false; |
| } |
| |
| throw new Error("Consistently creates long frame"); |
| } |
| |
| async function prepare_exec_iframe(t, origin) { |
| const iframe = document.createElement("iframe"); |
| t.add_cleanup(() => iframe.remove()); |
| const url = new URL("/common/dispatcher/remote-executor.html", origin); |
| const uuid = token(); |
| url.searchParams.set("uuid", uuid); |
| iframe.src = url.href; |
| document.body.appendChild(iframe); |
| await new Promise(resolve => iframe.addEventListener("load", resolve)); |
| return [new RemoteContext(uuid), iframe]; |
| } |
| |
| |
| async function prepare_exec_popup(t, origin) { |
| const url = new URL("/common/dispatcher/remote-executor.html", origin); |
| const uuid = token(); |
| url.searchParams.set("uuid", uuid); |
| const popup = window.open(url); |
| t.add_cleanup(() => popup.close()); |
| 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); |
| } |