Bug 1822437 [wpt PR 39001] - Scheduling APIs: Implement scheduler.yield() prototype (part 1), a=testonly

Automatic update from web-platform-tests
Scheduling APIs: Implement scheduler.yield() prototype (part 1)

This adds the full API surface of scheduler.yield() to the IDL and
implements all the options except inheritance (split off to keep the
size reasonable). Details:

 - The scheduler's fixed and dynamic priority collections are
   mirrored for continuations, and algorithms have been updated to
   select the collection based on WebSchedulingQueueType.
 - When yield is called, the options are used to get a TaskSignal
   for the continuation, using the same algorithm as postTask().
   This in turn is used to select the continuation queue which is
   used to schedule a DOMTaskContinuation (wrapper around resolving
   the yield() promise).
 - Passing "inherit" for the signal or priority option will CHECK.
   This will be implemented in a follow-up CL.
 - Tentative WPT test coverage is added for the implemented portion
   of the API.

Bug: 979020
Change-Id: I601cd0ce801f02bdc236995124a36951431da5b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4324266
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1118256}

--

wpt-commits: 8ab8d9d6ae09d7cf62657115a1fd4366b7de8b11
wpt-pr: 39001
This commit is contained in:
Scott Haseley 2023-03-20 20:14:57 +00:00 коммит произвёл moz-wptsync-bot
Родитель f013d1378d
Коммит 5ded787e91
5 изменённых файлов: 240 добавлений и 0 удалений

Просмотреть файл

@ -215,6 +215,7 @@ SET TIMEOUT: resize-observer/resources/iframe.html
SET TIMEOUT: resource-timing/resources/nested-contexts.js
SET TIMEOUT: reporting/resources/first-csp-report.https.sub.html
SET TIMEOUT: reporting/resources/second-csp-report.https.sub.html
SET TIMEOUT: scheduler/tentative/yield/yield-priority-timers.any.js
SET TIMEOUT: secure-contexts/basic-popup-and-iframe-tests.https.js
SET TIMEOUT: service-workers/cache-storage/cache-abort.https.any.js
SET TIMEOUT: service-workers/service-worker/activation.https.html

Просмотреть файл

@ -0,0 +1,20 @@
'use strict';
promise_test(t => {
const signal = AbortSignal.abort();
return scheduler.postTask(async () => {
const p = scheduler.yield({signal});
await promise_rejects_dom(t, 'AbortError', p);
});
}, 'yield() with an aborted signal');
promise_test(t => {
const controller = new TaskController();
const signal = controller.signal;
return scheduler.postTask(async () => {
scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'});
assert_false(signal.aborted);
const p = scheduler.yield({signal});
await promise_rejects_dom(t, 'AbortError', p);
});
}, 'yield() aborted in a separate task');

Просмотреть файл

@ -0,0 +1,111 @@
'use strict';
// Posts a postTask task with `yieldyTaskParams` that yields 3 times using
// `yieldParams`, then posts 2 tasks of each priority, in descending order.
//
// Returns {tasks, ids} where `tasks` is an array of promises returned by
// postTask and `ids` is an array of task ids appended to by the scheduled
// tasks.
function postTestTasks(yieldyTaskParams, yieldParams) {
const tasks = [];
const ids = [];
tasks.push(scheduler.postTask(async () => {
ids.push('y0');
for (let i = 1; i < 4; i++) {
await scheduler.yield(yieldParams);
ids.push('y' + i);
}
}, yieldyTaskParams));
tasks.push(
scheduler.postTask(() => {ids.push('ub1')}, {priority: 'user-blocking'}));
tasks.push(
scheduler.postTask(() => {ids.push('ub2')}, {priority: 'user-blocking'}));
tasks.push(
scheduler.postTask(() => {ids.push('uv1')}, {priority: 'user-visible'}));
tasks.push(
scheduler.postTask(() => {ids.push('uv2')}, {priority: 'user-visible'}));
tasks.push(
scheduler.postTask(() => {ids.push('bg1')}, {priority: 'background'}));
tasks.push(
scheduler.postTask(() => {ids.push('bg2')}, {priority: 'background'}));
return {tasks, ids};
}
// Expected task orders for `postTestTasks` tasks.
const taskOrders = {
'user-blocking': 'y0,y1,y2,y3,ub1,ub2,uv1,uv2,bg1,bg2',
'user-visible': 'ub1,ub2,y0,y1,y2,y3,uv1,uv2,bg1,bg2',
'background': 'ub1,ub2,uv1,uv2,y0,y1,y2,y3,bg1,bg2',
};
const priorityConfigs = [
{options: {}, expected: taskOrders['user-visible']},
{options: {priority: 'user-visible'}, expected: taskOrders['user-visible']},
{options: {priority: 'user-blocking'}, expected: taskOrders['user-blocking']},
{options: {priority: 'background'}, expected: taskOrders['background']},
];
const fixedPrioritySignals = {
'user-blocking': (new TaskController({priority: 'user-blocking'})).signal,
'user-visible': (new TaskController({priority: 'user-visible'})).signal,
'background': (new TaskController({priority: 'background'})).signal,
};
const signalConfigs = [
{
options: {signal: fixedPrioritySignals['user-visible']},
expected: taskOrders['user-visible']
},
{
options: {signal: fixedPrioritySignals['user-blocking']},
expected: taskOrders['user-blocking']
},
{
options: {signal: fixedPrioritySignals['background']},
expected: taskOrders['background']
},
];
promise_test(async t => {
for (const config of priorityConfigs) {
const {tasks, ids} = postTestTasks(config.options, config.options);
await Promise.all(tasks);
assert_equals(ids.join(), config.expected);
}
}, 'yield() with postTask tasks (priority option)');
promise_test(async t => {
for (const config of signalConfigs) {
const {tasks, ids} = postTestTasks(config.options, config.options);
await Promise.all(tasks);
assert_equals(ids.join(), config.expected);
}
}, 'yield() with postTask tasks (signal option)');
promise_test(async t => {
const expected = 'y0,ub1,ub2,uv1,uv2,y1,y2,y3,bg1,bg2';
const {tasks, ids} = postTestTasks(
{priority: 'user-blocking'}, {priority: 'background'});
await Promise.all(tasks);
assert_equals(ids.join(), expected);
}, 'yield() with different priority from task (priority)');
promise_test(async t => {
const expected = 'y0,ub1,ub2,uv1,uv2,y1,y2,y3,bg1,bg2';
const bgSignal = (new TaskController({priority: 'background'})).signal;
const {tasks, ids} =
postTestTasks({priority: 'user-blocking'}, {signal: bgSignal});
await Promise.all(tasks);
assert_equals(ids.join(), expected);
}, 'yield() with different priority from task (signal)');
promise_test(async t => {
const bgSignal = (new TaskController({priority: 'background'})).signal;
const {tasks, ids} = postTestTasks(
{priority: 'user-blocking'},
{signal: bgSignal, priority: 'user-blocking'});
await Promise.all(tasks);
assert_equals(ids.join(), taskOrders['user-blocking']);
}, 'yield() priority overrides signal');

Просмотреть файл

@ -0,0 +1,82 @@
'use strict';
// Queues a zero ms timer that yields 3 times using `yieldParams`, then posts 2
// more 0 ms timers.
//
// Returns {tasks, ids} where `tasks` is an array of promises associated with
// the timers and `ids` is an array of task ids appended to by the scheduled
// tasks.
function postTestTasks(yieldParams) {
const tasks = [];
const ids = [];
tasks.push(new Promise(resolve => {
setTimeout(async () => {
ids.push('t1');
for (let i = 1; i < 4; i++) {
await scheduler.yield(yieldParams);
ids.push('y' + i);
}
resolve();
});
}));
tasks.push(new Promise(resolve => {
setTimeout(() => { ids.push('t2'); resolve(); });
}));
tasks.push(new Promise(resolve => {
setTimeout(() => { ids.push('t3'); resolve(); });
}));
return {tasks, ids};
}
// Expected task orders for `postTestTasks` tasks.
const taskOrders = {
'user-blocking': 't1,y1,y2,y3,t2,t3',
'user-visible': 't1,y1,y2,y3,t2,t3',
'background': 't1,t2,t3,y1,y2,y3',
};
const priorityConfigs = [
{options: {}, expected: taskOrders['user-visible']},
{options: {priority: 'user-visible'}, expected: taskOrders['user-visible']},
{options: {priority: 'user-blocking'}, expected: taskOrders['user-blocking']},
{options: {priority: 'background'}, expected: taskOrders['background']},
];
const fixedPrioritySignals = {
'user-blocking': (new TaskController({priority: 'user-blocking'})).signal,
'user-visible': (new TaskController({priority: 'user-visible'})).signal,
'background': (new TaskController({priority: 'background'})).signal,
};
const signalConfigs = [
{
options: {signal: fixedPrioritySignals['user-visible']},
expected: taskOrders['user-visible']
},
{
options: {signal: fixedPrioritySignals['user-blocking']},
expected: taskOrders['user-blocking']
},
{
options: {signal: fixedPrioritySignals['background']},
expected: taskOrders['background']
},
];
promise_test(async t => {
for (const config of priorityConfigs) {
const {tasks, ids} = postTestTasks(config.options);
await Promise.all(tasks);
assert_equals(ids.join(), config.expected);
}
}, 'yield() with timer tasks (priority option)');
promise_test(async t => {
for (const config of signalConfigs) {
const {tasks, ids} = postTestTasks(config.options);
await Promise.all(tasks);
assert_equals(ids.join(), config.expected);
}
}, 'yield() with timer tasks (signal option)');

Просмотреть файл

@ -0,0 +1,26 @@
<!doctype html>
<title>Scheduler: yield in Detached Scheduler</title>
<link rel="help" href="https://github.com/WICG/scheduling-apis">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
promise_test(async t => {
await new Promise(resolve => window.onload = resolve);
const frame = document.body.appendChild(document.createElement('iframe'));
const iframeDOMException = frame.contentWindow.DOMException;
const iframeScheduler = frame.contentWindow.scheduler;
let didRun = false;
iframeScheduler.yield().then(() => { didRun = true; });
document.body.removeChild(frame);
await promise_rejects_dom(t, 'NotSupportedError', iframeDOMException, iframeScheduler.yield());
await new Promise(resolve => t.step_timeout(resolve, 10));
assert_false(didRun, 'The continuation should not have run.');
}, 'Test scheduler.yield() from an iframe that is removed');
</script>