summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/scheduler
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/scheduler
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/scheduler')
-rw-r--r--testing/web-platform/tests/scheduler/META.yml4
-rw-r--r--testing/web-platform/tests/scheduler/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/scheduler/post-task-abort-reason.any.js37
-rw-r--r--testing/web-platform/tests/scheduler/post-task-delay.any.js11
-rw-r--r--testing/web-platform/tests/scheduler/post-task-result-success.any.js8
-rw-r--r--testing/web-platform/tests/scheduler/post-task-result-throws.any.js9
-rw-r--r--testing/web-platform/tests/scheduler/post-task-run-order.any.js21
-rw-r--r--testing/web-platform/tests/scheduler/post-task-then-detach.html28
-rw-r--r--testing/web-platform/tests/scheduler/post-task-with-abort-signal-in-handler.any.js20
-rw-r--r--testing/web-platform/tests/scheduler/post-task-with-abort-signal.any.js11
-rw-r--r--testing/web-platform/tests/scheduler/post-task-with-aborted-signal.any.js10
-rw-r--r--testing/web-platform/tests/scheduler/post-task-with-signal-and-priority.any.js14
-rw-r--r--testing/web-platform/tests/scheduler/post-task-with-signal-from-detached-iframe.html32
-rw-r--r--testing/web-platform/tests/scheduler/post-task-without-signals.any.js10
-rw-r--r--testing/web-platform/tests/scheduler/scheduler-replaceable.any.js12
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-abort-completed-tasks.any.js19
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-abort-signal-and-priority.any.js16
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-abort1.any.js16
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-abort2.any.js23
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-setPriority-delayed-task.any.js31
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-setPriority-recursive.any.js12
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-setPriority-repeated.any.js60
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-setPriority1.any.js24
-rw-r--r--testing/web-platform/tests/scheduler/task-controller-setPriority2.any.js22
-rw-r--r--testing/web-platform/tests/scheduler/task-signal-any-abort.tentative.any.js6
-rw-r--r--testing/web-platform/tests/scheduler/task-signal-any-post-task-run-order.tentative.any.js73
-rw-r--r--testing/web-platform/tests/scheduler/task-signal-any-priority.tentative.any.js213
-rw-r--r--testing/web-platform/tests/scheduler/task-signal-onprioritychange.any.js14
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js85
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-inherit-across-promises.any.js65
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-priority-idle-callbacks.html60
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-priority-posttask.any.js193
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-priority-timers.any.js94
-rw-r--r--testing/web-platform/tests/scheduler/tentative/yield/yield-then-detach.html26
34 files changed, 1282 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scheduler/META.yml b/testing/web-platform/tests/scheduler/META.yml
new file mode 100644
index 0000000000..ef9511daa4
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/META.yml
@@ -0,0 +1,4 @@
+spec: https://wicg.github.io/scheduling-apis/
+suggested_reviewers:
+ - shaseley
+ - natechapin
diff --git a/testing/web-platform/tests/scheduler/WEB_FEATURES.yml b/testing/web-platform/tests/scheduler/WEB_FEATURES.yml
new file mode 100644
index 0000000000..4b5218b8b6
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: scheduler
+ files: "**"
diff --git a/testing/web-platform/tests/scheduler/post-task-abort-reason.any.js b/testing/web-platform/tests/scheduler/post-task-abort-reason.any.js
new file mode 100644
index 0000000000..27eff9f080
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-abort-reason.any.js
@@ -0,0 +1,37 @@
+// META: title=Scheduler: postTask uses abort reason
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ const reason = new Error("Custom Abort Error");
+ controller.abort(reason);
+ return promise_rejects_exactly(t, reason, scheduler.postTask(() => {}, {signal}));
+}, 'Calling postTask with an aborted TaskSignal rejects the promise with the abort reason');
+
+promise_test(t => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+ const reason = new Error("Custom Abort Error");
+ controller.abort(reason);
+ return promise_rejects_exactly(t, reason, scheduler.postTask(() => {}, {signal}));
+}, 'Calling postTask with an aborted AbortSignal rejects the promise with the abort reason');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ const reason = new Error("Custom Abort Error");
+ const result = scheduler.postTask(() => {}, {signal});
+ controller.abort(reason);
+ return promise_rejects_exactly(t, reason, result);
+}, 'Aborting a TaskSignal rejects the promise of a scheduled task with the abort reason');
+
+promise_test(t => {
+ const reason = new Error("Custom Abort Error");
+ const controller = new AbortController();
+ const signal = controller.signal;
+ const result = scheduler.postTask(() => {}, {signal});
+ controller.abort(reason);
+ return promise_rejects_exactly(t, reason, result);
+}, 'Aborting an AbortSignal rejects the promise of a scheduled task with the abort reason');
diff --git a/testing/web-platform/tests/scheduler/post-task-delay.any.js b/testing/web-platform/tests/scheduler/post-task-delay.any.js
new file mode 100644
index 0000000000..cf96f6703b
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-delay.any.js
@@ -0,0 +1,11 @@
+// META: title=Scheduler: postTask Delayed Tasks
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const start = performance.now();
+ return scheduler.postTask(() => {
+ const elapsed = performance.now() - start;
+ assert_greater_than_equal(elapsed, 10);
+ }, {priority: 'user-blocking', delay: 10});
+}, 'Tests basic scheduler.postTask with a delay');
diff --git a/testing/web-platform/tests/scheduler/post-task-result-success.any.js b/testing/web-platform/tests/scheduler/post-task-result-success.any.js
new file mode 100644
index 0000000000..dd73c148e9
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-result-success.any.js
@@ -0,0 +1,8 @@
+// META: title=Scheduler: postTask Promise Value
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const result = await scheduler.postTask(() => 1234);
+ assert_equals(result, 1234);
+}, 'Test the task promise is resolved with the callback return value');
diff --git a/testing/web-platform/tests/scheduler/post-task-result-throws.any.js b/testing/web-platform/tests/scheduler/post-task-result-throws.any.js
new file mode 100644
index 0000000000..7155c94eac
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-result-throws.any.js
@@ -0,0 +1,9 @@
+// META: title=Scheduler: postTask Error Propagation
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ const testError = new Error('Failed');
+ const task = scheduler.postTask(() => { throw testError; });
+ return promise_rejects_exactly(t, testError, task, 'postTask should propagate the error');
+}, 'Test postTask rejects the associated promise with the callback error');
diff --git a/testing/web-platform/tests/scheduler/post-task-run-order.any.js b/testing/web-platform/tests/scheduler/post-task-run-order.any.js
new file mode 100644
index 0000000000..acbe86744c
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-run-order.any.js
@@ -0,0 +1,21 @@
+// META: title=Scheduler: Tasks Run in Priority Order
+// META: global=window,worker
+
+promise_test(async t => {
+ const runOrder = [];
+ const schedule = (id, priority) => scheduler.postTask(() => { runOrder.push(id); }, {priority});
+
+ // Post tasks in reverse priority order and expect they are run from highest
+ // to lowest priority.
+ const tasks = [];
+ tasks.push(schedule('B1', 'background'));
+ tasks.push(schedule('B2', 'background'));
+ tasks.push(schedule('UV1', 'user-visible'));
+ tasks.push(schedule('UV2', 'user-visible'));
+ tasks.push(schedule('UB1', 'user-blocking'));
+ tasks.push(schedule('UB2', 'user-blocking'));
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(),'UB1,UB2,UV1,UV2,B1,B2');
+}, 'Test scheduler.postTask task run in priority order');
diff --git a/testing/web-platform/tests/scheduler/post-task-then-detach.html b/testing/web-platform/tests/scheduler/post-task-then-detach.html
new file mode 100644
index 0000000000..402f34dc0c
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-then-detach.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Scheduler: postTask in Detached Scheduler</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<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';
+
+async_test(t => {
+ onload = () => {
+ let didRun = false;
+ const frame = document.body.appendChild(document.createElement('iframe'));
+ frame.contentWindow.scheduler.postTask(() => { didRun = true; });
+ document.body.removeChild(frame);
+
+ // We cannot assume anything about ordering between postTask tasks and
+ // non-postTask or postTask tasks from different schedulers, so the best we
+ // can do is give the task time to run.
+ t.step_timeout(() => {
+ assert_false(didRun, 'The task should not have run.');
+ t.done();
+ }, 10);
+ }
+}, 'Test scheduler.postTask() from an iframe that is removed before the task runs');
+
+</script>
diff --git a/testing/web-platform/tests/scheduler/post-task-with-abort-signal-in-handler.any.js b/testing/web-platform/tests/scheduler/post-task-with-abort-signal-in-handler.any.js
new file mode 100644
index 0000000000..1fd416c775
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-with-abort-signal-in-handler.any.js
@@ -0,0 +1,20 @@
+// META: title=Scheduler: postTask with a signal and abort the signal when running the callback
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return promise_rejects_dom(t, 'AbortError', scheduler.postTask(() => {
+ controller.abort();
+ }, { signal }));
+}, 'Posting a task with a signal and abort the signal when running the sync callback');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ controller.abort();
+ }, { signal });
+}, 'Posting a task with a signal and abort the signal when running the async callback');
diff --git a/testing/web-platform/tests/scheduler/post-task-with-abort-signal.any.js b/testing/web-platform/tests/scheduler/post-task-with-abort-signal.any.js
new file mode 100644
index 0000000000..41cbafba90
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-with-abort-signal.any.js
@@ -0,0 +1,11 @@
+// META: title=Scheduler: postTask and AbortSignal
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+ const taskResult = scheduler.postTask(() => {}, {signal});
+ controller.abort();
+ return promise_rejects_dom(t, 'AbortError', taskResult);
+}, 'Test that scheduler.postTask() accepts an AbortSignal that is not also a TaskSignal');
diff --git a/testing/web-platform/tests/scheduler/post-task-with-aborted-signal.any.js b/testing/web-platform/tests/scheduler/post-task-with-aborted-signal.any.js
new file mode 100644
index 0000000000..4e5d42c048
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-with-aborted-signal.any.js
@@ -0,0 +1,10 @@
+// META: title=Scheduler: postTask with an Aborted Signal
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ controller.abort();
+ return promise_rejects_dom(t, 'AbortError', scheduler.postTask(() => {}, {signal}));
+}, 'Posting a task with an aborted signal rejects with an AbortError');
diff --git a/testing/web-platform/tests/scheduler/post-task-with-signal-and-priority.any.js b/testing/web-platform/tests/scheduler/post-task-with-signal-and-priority.any.js
new file mode 100644
index 0000000000..ba40d7cf53
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-with-signal-and-priority.any.js
@@ -0,0 +1,14 @@
+// META: title=Scheduler: Signal and Priority Combination
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const task1Result = scheduler.postTask(() => 'task1', {priority: 'user-visible'});
+
+ const controller = new TaskController({priority: 'background'});
+ const signal = controller.signal
+ const task2Result = scheduler.postTask(() => 'task2', {priority: 'user-blocking', signal});
+
+ const result = await Promise.race([task1Result, task2Result]);
+ assert_equals('task2', result);
+}, 'Test when scheduler.postTask() is passed both a signal and a priority');
diff --git a/testing/web-platform/tests/scheduler/post-task-with-signal-from-detached-iframe.html b/testing/web-platform/tests/scheduler/post-task-with-signal-from-detached-iframe.html
new file mode 100644
index 0000000000..acd974cbc7
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-with-signal-from-detached-iframe.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Scheduler: postTask with Detached Frame's Signal</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<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.addEventListener('load', resolve);
+ });
+
+ const frame = document.createElement('iframe');
+ frame.srcdoc = `
+ <script>
+ const controller = new TaskController();
+ window.childFrameSignal = controller.signal;
+ <\/script>`
+ await new Promise((resolve) => {
+ frame.addEventListener('load', resolve)
+ document.body.appendChild(frame);
+ });
+
+ const signal = frame.contentWindow.childFrameSignal;
+ document.body.removeChild(frame);
+ return scheduler.postTask(() => {}, {signal});
+}, 'Test scheduler.postTask() with a signal from a detached iframe');
+
+</script>
diff --git a/testing/web-platform/tests/scheduler/post-task-without-signals.any.js b/testing/web-platform/tests/scheduler/post-task-without-signals.any.js
new file mode 100644
index 0000000000..f5fe3e11a6
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/post-task-without-signals.any.js
@@ -0,0 +1,10 @@
+// META: title=Scheduler: Basic Functionality without Signals
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ for (const priority of ['user-blocking', 'user-visible', 'background']) {
+ const result = await scheduler.postTask(() => priority, {priority});
+ assert_equals(result, priority);
+ }
+}, 'Basic functionality for scheduler.postTask() without using TaskSignals');
diff --git a/testing/web-platform/tests/scheduler/scheduler-replaceable.any.js b/testing/web-platform/tests/scheduler/scheduler-replaceable.any.js
new file mode 100644
index 0000000000..12bf1116dd
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/scheduler-replaceable.any.js
@@ -0,0 +1,12 @@
+// META: title=Scheduler: scheduler should be replaceable
+// META: global=window,worker
+'use strict';
+
+test(() => {
+ class Scheduler {
+ constructor() {
+ scheduler = this;
+ }
+ }
+ new Scheduler();
+}, 'Tests replacing window.scheduler with a different object');
diff --git a/testing/web-platform/tests/scheduler/task-controller-abort-completed-tasks.any.js b/testing/web-platform/tests/scheduler/task-controller-abort-completed-tasks.any.js
new file mode 100644
index 0000000000..fc96038e64
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-abort-completed-tasks.any.js
@@ -0,0 +1,19 @@
+// META: title=Scheduler: Aborting Completed Tasks is a No-op
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const controller1 = new TaskController();
+ const controller2 = new TaskController();
+
+ await scheduler.postTask(() => {}, {signal: controller1.signal});
+
+ const task = scheduler.postTask(() => {}, {signal: controller2.signal});
+ controller2.abort();
+ await promise_rejects_dom(t, 'AbortError', task);
+
+ // The tasks associated with these controllers have completed, so this should
+ // not lead to any unhandled rejections.
+ controller1.abort();
+ controller2.abort();
+}, 'Aborting completed tasks should be a no-op.');
diff --git a/testing/web-platform/tests/scheduler/task-controller-abort-signal-and-priority.any.js b/testing/web-platform/tests/scheduler/task-controller-abort-signal-and-priority.any.js
new file mode 100644
index 0000000000..168fe92f54
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-abort-signal-and-priority.any.js
@@ -0,0 +1,16 @@
+// META: title=Scheduler: TaskController.abort() with Signal and Priority
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ const task1 = scheduler.postTask(() => {}, {signal});
+ const task2 = scheduler.postTask(() => {}, {priority: 'background', signal});
+
+ controller.abort();
+
+ await promise_rejects_dom(t, 'AbortError', task1);
+ return promise_rejects_dom(t, 'AbortError', task2);
+}, 'Test that when scheduler.postTask() is given both a signal and priority, the signal abort is honored');
diff --git a/testing/web-platform/tests/scheduler/task-controller-abort1.any.js b/testing/web-platform/tests/scheduler/task-controller-abort1.any.js
new file mode 100644
index 0000000000..fc7e02ce37
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-abort1.any.js
@@ -0,0 +1,16 @@
+// META: title=Scheduler: TaskController.abort() Basic Functionality
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ let didRun = false;
+ const taskResult = scheduler.postTask(() => { didRun = true; }, {signal});
+
+ controller.abort();
+
+ await promise_rejects_dom(t, 'AbortError', taskResult);
+ assert_false(didRun);
+}, 'Test that TaskController.abort() prevents a task from running and rejects the promise');
diff --git a/testing/web-platform/tests/scheduler/task-controller-abort2.any.js b/testing/web-platform/tests/scheduler/task-controller-abort2.any.js
new file mode 100644
index 0000000000..075715b565
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-abort2.any.js
@@ -0,0 +1,23 @@
+// META: title=Scheduler: TaskController.abort() Aborts Correct Task
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const taskControllers = [];
+ const taskResults = [];
+
+ for (let i = 0; i < 5; i++) {
+ const controller = new TaskController();
+ taskControllers.push(controller);
+
+ const signal = controller.signal;
+ taskResults.push(scheduler.postTask(() => i, {signal}));
+ }
+
+ const abortedTask = taskResults.splice(2, 1)[0];
+ taskControllers[2].abort();
+ await promise_rejects_dom(t, 'AbortError', abortedTask);
+
+ const result = await Promise.all(taskResults);
+ assert_equals(result.toString(), '0,1,3,4');
+}, 'Test aborting a task aborts the appropriate task');
diff --git a/testing/web-platform/tests/scheduler/task-controller-setPriority-delayed-task.any.js b/testing/web-platform/tests/scheduler/task-controller-setPriority-delayed-task.any.js
new file mode 100644
index 0000000000..43d24c8f92
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-setPriority-delayed-task.any.js
@@ -0,0 +1,31 @@
+// META: title=Scheduler: Change Delayed Task Priority
+// META: global=window,worker
+'use strict';
+
+promise_test(t => {
+ let taskCount = 0;
+ const start = performance.now();
+ const controller = new TaskController({priority: 'background'});
+
+ const task1 = scheduler.postTask(() => {
+ assert_equals(++taskCount, 1);
+ controller.setPriority('user-blocking');
+ }, {priority: 'user-blocking', delay: 10});
+
+ const task2 = scheduler.postTask(() => {
+ assert_equals(++taskCount, 2);
+
+ const elapsed = performance.now() - start;
+
+ if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1){
+ // Firefox returns the timings with different precision,
+ // so we put 19 here.
+ assert_greater_than_equal(elapsed, 19);
+ } else {
+ assert_greater_than_equal(elapsed, 20);
+ }
+ }, {signal: controller.signal, delay: 20});
+
+ return Promise.all([task1, task2]);
+
+}, "Tests delay when changing a delayed task's priority");
diff --git a/testing/web-platform/tests/scheduler/task-controller-setPriority-recursive.any.js b/testing/web-platform/tests/scheduler/task-controller-setPriority-recursive.any.js
new file mode 100644
index 0000000000..ebc4ccd950
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-setPriority-recursive.any.js
@@ -0,0 +1,12 @@
+// META: title=Scheduler: Recursive TaskController.setPriority()
+// META: global=window,worker
+'use strict';
+
+async_test(t => {
+ const controller = new TaskController();
+ controller.signal.onprioritychange = t.step_func_done(() => {
+ assert_equals(controller.signal.priority, 'background');
+ assert_throws_dom('NotAllowedError', () => { controller.setPriority('user-blocking'); });
+ });
+ controller.setPriority('background');
+}, 'Test that TaskController.setPriority() throws an error if called recursively');
diff --git a/testing/web-platform/tests/scheduler/task-controller-setPriority-repeated.any.js b/testing/web-platform/tests/scheduler/task-controller-setPriority-repeated.any.js
new file mode 100644
index 0000000000..fae3ec6c48
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-setPriority-repeated.any.js
@@ -0,0 +1,60 @@
+// META: title=Scheduler: TaskController.setPriority() repeated calls
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ const tasks = [];
+ const runOrder = [];
+ const callback = id => { runOrder.push(id); };
+
+ tasks.push(scheduler.postTask(() => callback(0), {signal}));
+ tasks.push(scheduler.postTask(() => callback(1), {priority: 'user-blocking'}));
+ tasks.push(scheduler.postTask(() => callback(2), {priority: 'user-visible' }));
+
+ controller.setPriority('background');
+ assert_equals(signal.priority, 'background');
+
+ await Promise.all(tasks);
+ assert_equals(runOrder.toString(), '1,2,0');
+
+ while (tasks.length) { tasks.pop(); }
+ while (runOrder.length) { runOrder.pop(); }
+
+ tasks.push(scheduler.postTask(() => callback(3), {signal}));
+ tasks.push(scheduler.postTask(() => callback(4), {priority: 'user-blocking'}));
+ tasks.push(scheduler.postTask(() => callback(5), {priority: 'user-visible' }));
+
+ controller.setPriority('user-blocking');
+ assert_equals(signal.priority, 'user-blocking');
+
+ await Promise.all(tasks);
+ assert_equals(runOrder.toString(), '3,4,5');
+}, 'TaskController.setPriority() changes the priority of all associated tasks when called repeatedly');
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ const tasks = [];
+ const runOrder = [];
+ const callback = id => { runOrder.push(id); };
+
+ tasks.push(scheduler.postTask(() => callback(0), {signal}));
+ tasks.push(scheduler.postTask(() => callback(1), {priority: 'user-blocking'}));
+ tasks.push(scheduler.postTask(() => callback(2), {priority: 'user-visible' }));
+
+ controller.setPriority('background');
+ assert_equals(signal.priority, 'background');
+
+ controller.setPriority('user-visible');
+ assert_equals(signal.priority, 'user-visible');
+
+ controller.setPriority('user-blocking');
+ assert_equals(signal.priority, 'user-blocking');
+
+ await Promise.all(tasks);
+ assert_equals(runOrder.toString(), '0,1,2');
+}, 'TaskController.setPriority() changes the priority of all associated tasks when called repeatedly before tasks run');
diff --git a/testing/web-platform/tests/scheduler/task-controller-setPriority1.any.js b/testing/web-platform/tests/scheduler/task-controller-setPriority1.any.js
new file mode 100644
index 0000000000..a59c20cacc
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-setPriority1.any.js
@@ -0,0 +1,24 @@
+// META: title=Scheduler: TaskController.setPriority()
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ const tasks = [];
+ const runOrder = [];
+ const callback = id => { runOrder.push(id); };
+
+ for (let i = 0; i < 5; i++)
+ tasks.push(scheduler.postTask(() => callback(i), {signal}));
+ tasks.push(scheduler.postTask(() => callback(5), {priority: 'user-blocking'}));
+ tasks.push(scheduler.postTask(() => callback(6), {priority: 'user-visible' }));
+
+ controller.setPriority('background');
+ assert_equals(signal.priority, 'background');
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(), '5,6,0,1,2,3,4');
+}, 'Test that TaskController.setPriority() changes the priority of all associated tasks');
diff --git a/testing/web-platform/tests/scheduler/task-controller-setPriority2.any.js b/testing/web-platform/tests/scheduler/task-controller-setPriority2.any.js
new file mode 100644
index 0000000000..716b3af33e
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-controller-setPriority2.any.js
@@ -0,0 +1,22 @@
+// META: title=Scheduler: TaskController.setPriority and Task Order
+// META: global=window,worker
+'use strict';
+
+promise_test(async t => {
+ const tasks = [];
+ const runOrder = [];
+ const taskControllers = [];
+
+ for (let i = 0; i < 5; i++) {
+ taskControllers.push(new TaskController({priority: 'background'}));
+ const signal = taskControllers[i].signal;
+ tasks.push(scheduler.postTask(() => { runOrder.push(i); }, {signal}));
+ }
+
+ taskControllers[2].setPriority('user-blocking');
+ assert_equals(taskControllers[2].signal.priority, 'user-blocking');
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(), '2,0,1,3,4');
+}, 'Test TaskController.setPriority() affects task order.');
diff --git a/testing/web-platform/tests/scheduler/task-signal-any-abort.tentative.any.js b/testing/web-platform/tests/scheduler/task-signal-any-abort.tentative.any.js
new file mode 100644
index 0000000000..4afcde901d
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-signal-any-abort.tentative.any.js
@@ -0,0 +1,6 @@
+// META: global=window,worker
+// META: script=../dom/abort/resources/abort-signal-any-tests.js
+
+abortSignalAnySignalOnlyTests(TaskSignal);
+abortSignalAnyTests(TaskSignal, AbortController);
+abortSignalAnyTests(TaskSignal, TaskController);
diff --git a/testing/web-platform/tests/scheduler/task-signal-any-post-task-run-order.tentative.any.js b/testing/web-platform/tests/scheduler/task-signal-any-post-task-run-order.tentative.any.js
new file mode 100644
index 0000000000..889217b081
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-signal-any-post-task-run-order.tentative.any.js
@@ -0,0 +1,73 @@
+// META: title=Scheduler: Tasks Run in Priority Order
+// META: global=window,worker
+
+promise_test(async t => {
+ const runOrder = [];
+ const schedule = (id, signal) => scheduler.postTask(() => { runOrder.push(id); }, {signal});
+
+ const tasks = [];
+ tasks.push(schedule('B1', TaskSignal.any([], {priority: 'background'})));
+ tasks.push(schedule('B2', TaskSignal.any([], {priority: 'background'})));
+ tasks.push(schedule('UV1', TaskSignal.any([], {priority: 'user-visible'})));
+ tasks.push(schedule('UV2', TaskSignal.any([], {priority: 'user-visible'})));
+ tasks.push(schedule('UB1', TaskSignal.any([], {priority: 'user-blocking'})));
+ tasks.push(schedule('UB2', TaskSignal.any([], {priority: 'user-blocking'})));
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(),'UB1,UB2,UV1,UV2,B1,B2');
+}, 'scheduler.postTask() tasks run in priority order with a fixed priority composite signal');
+
+promise_test(async t => {
+ const runOrder = [];
+ const schedule = (id, priorityOrSignal) => {
+ if (priorityOrSignal instanceof TaskSignal) {
+ return scheduler.postTask(() => { runOrder.push(id); }, {signal: priorityOrSignal});
+ } else {
+ return scheduler.postTask(() => { runOrder.push(id); }, {priority: priorityOrSignal});
+ }
+ };
+
+ const controller = new TaskController({priority: 'user-blocking'});
+ const signal = TaskSignal.any([], {priority: controller.signal});
+
+ const tasks = [];
+ tasks.push(schedule('B1', signal));
+ tasks.push(schedule('B2', signal));
+ tasks.push(schedule('UV1', 'user-visible'));
+ tasks.push(schedule('UV2', 'user-visible'));
+ tasks.push(schedule('UB1', 'user-blocking'));
+ tasks.push(schedule('UB2', 'user-blocking'));
+
+ controller.setPriority('background');
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(),'UB1,UB2,UV1,UV2,B1,B2');
+}, 'scheduler.postTask() tasks run in priority order with a dynamic priority composite signal');
+
+promise_test(async t => {
+ const runOrder = [];
+ const schedule = (id, priorityOrSignal) => {
+ if (priorityOrSignal instanceof TaskSignal) {
+ return scheduler.postTask(() => { runOrder.push(id); }, {signal: priorityOrSignal});
+ } else {
+ return scheduler.postTask(() => { runOrder.push(id); }, {priority: priorityOrSignal});
+ }
+ };
+
+ const parentSignal = TaskSignal.any([], {priority: 'background'});
+ const signal = TaskSignal.any([], {priority: parentSignal});
+
+ const tasks = [];
+ tasks.push(schedule('B1', signal));
+ tasks.push(schedule('B2', signal));
+ tasks.push(schedule('UV1', 'user-visible'));
+ tasks.push(schedule('UV2', 'user-visible'));
+ tasks.push(schedule('UB1', 'user-blocking'));
+ tasks.push(schedule('UB2', 'user-blocking'));
+
+ await Promise.all(tasks);
+
+ assert_equals(runOrder.toString(),'UB1,UB2,UV1,UV2,B1,B2');
+}, 'scheduler.postTask() tasks run in priority order with a composite signal whose source has fixed priority');
diff --git a/testing/web-platform/tests/scheduler/task-signal-any-priority.tentative.any.js b/testing/web-platform/tests/scheduler/task-signal-any-priority.tentative.any.js
new file mode 100644
index 0000000000..46af80b8b1
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-signal-any-priority.tentative.any.js
@@ -0,0 +1,213 @@
+// META: global=window,worker
+
+test((t) => {
+ const signal = TaskSignal.any([]);
+ assert_true(signal instanceof TaskSignal);
+ assert_equals(signal.priority, 'user-visible');
+}, "TaskSignal.any() returns a user-visible TaskSignal when no priority is specified");
+
+test((t) => {
+ let signal = TaskSignal.any([], {priority: 'user-blocking'});
+ assert_equals(signal.priority, 'user-blocking');
+
+ signal = TaskSignal.any([], {priority: 'user-visible'});
+ assert_equals(signal.priority, 'user-visible');
+
+ signal = TaskSignal.any([], {priority: 'background'});
+ assert_equals(signal.priority, 'background');
+}, "TaskSignal.any() returns a signal with the correct priority when intialized with a string");
+
+test((t) => {
+ let controller = new TaskController({priority: 'user-blocking'});
+ let signal = TaskSignal.any([], {priority: controller.signal});
+ assert_equals(signal.priority, 'user-blocking');
+
+ controller = new TaskController({priority: 'user-visible'});
+ signal = TaskSignal.any([], {priority: controller.signal});
+ assert_equals(signal.priority, 'user-visible');
+
+ controller = new TaskController({priority: 'background'});
+ signal = TaskSignal.any([], {priority: controller.signal});
+ assert_equals(signal.priority, 'background');
+}, "TaskSignal.any() returns a signal with the correct priority when intialized with a TaskSignal");
+
+test((t) => {
+ let controller = new TaskController({priority: 'user-blocking'});
+ let signal = TaskSignal.any([], {priority: controller.signal});
+ assert_equals(signal.priority, 'user-blocking');
+
+ controller.setPriority('user-visible');
+ assert_equals(signal.priority, 'user-visible');
+
+ controller.setPriority('background');
+ assert_equals(signal.priority, 'background');
+
+ controller.setPriority('user-blocking');
+ assert_equals(signal.priority, 'user-blocking');
+}, "TaskSignal.any() returns a signal with dynamic priority");
+
+test((t) => {
+ const controller = new TaskController();
+ const signal = TaskSignal.any([], {priority: controller.signal});
+
+ let eventFiredCount = 0;
+ signal.onprioritychange = t.step_func((e) => {
+ assert_equals(e.target, signal,
+ `The event target is the signal returned by TaskSignal.any()`);
+ ++eventFiredCount;
+ });
+
+ controller.setPriority('background');
+ assert_equals(eventFiredCount, 1);
+
+ controller.setPriority('user-visible');
+ assert_equals(eventFiredCount, 2);
+
+ controller.setPriority('user-blocking');
+ assert_equals(eventFiredCount, 3);
+}, "Priority change events fire for composite signals");
+
+
+test((t) => {
+ const controller = new TaskController();
+ let signal = TaskSignal.any([], {priority: controller.signal});
+ signal = TaskSignal.any([], {priority: signal});
+ signal = TaskSignal.any([], {priority: signal});
+ signal = TaskSignal.any([], {priority: signal});
+ signal = TaskSignal.any([], {priority: signal});
+
+ assert_equals(signal.priority, 'user-visible');
+
+ let eventFiredCount = 0;
+ signal.onprioritychange = t.step_func((e) => {
+ assert_equals(e.target, signal,
+ "The event target is the signal returned by TaskSignal.any()");
+ ++eventFiredCount;
+ });
+
+ controller.setPriority('background');
+ assert_equals(eventFiredCount, 1);
+ assert_equals(signal.priority, 'background');
+
+ controller.setPriority('user-visible');
+ assert_equals(eventFiredCount, 2);
+ assert_equals(signal.priority, 'user-visible');
+
+ controller.setPriority('user-blocking');
+ assert_equals(eventFiredCount, 3);
+ assert_equals(signal.priority, 'user-blocking');
+}, "Priority change events fire for composite signals with intermediate sources");
+
+test((t) => {
+ const controller = new TaskController();
+ const signals = [];
+ const results = [];
+
+ let id = 0;
+ for (let i = 0; i < 3; i++) {
+ const signal = TaskSignal.any([], {priority: controller.signal});
+ const eventId = id++;
+ signal.addEventListener('prioritychange', () => {
+ results.push(eventId);
+ });
+ signals.push(signal);
+ }
+ for (let i = 0; i < 3; i++) {
+ const signal = TaskSignal.any([], {priority: signals[i]});
+ const eventId = id++;
+ signal.addEventListener('prioritychange', () => {
+ results.push(eventId);
+ });
+ }
+
+ controller.setPriority('background');
+ assert_equals(results.toString(), '0,1,2,3,4,5')
+
+ controller.setPriority('user-blocking');
+ assert_equals(results.toString(), '0,1,2,3,4,5,0,1,2,3,4,5')
+}, "Priority change propagates to multiple dependent signals in the right order");
+
+test((t) => {
+ const controller = new TaskController();
+ const signal = TaskSignal.any([], {priority: controller.signal});
+
+ let fired = false;
+ signal.onabort = t.step_func(() => {
+ assert_unreached("The signal should not abort");
+ fired = true;
+ });
+
+ controller.abort();
+ assert_false(fired);
+}, "TaskSignal.any() does not propagate abort when not given dependent abort signals");
+
+test((t) => {
+ const taskController = new TaskController();
+ const abortController = new AbortController();
+ const signal = TaskSignal.any([abortController.signal], {priority: taskController.signal});
+
+ let priorityFireCount = 0;
+ signal.onprioritychange = t.step_func(() => {
+ ++priorityFireCount;
+ });
+
+ let abortFired = false;
+ signal.onabort = t.step_func(() => {
+ abortFired = true;
+ });
+
+ taskController.setPriority('background');
+ assert_equals(signal.priority, 'background');
+ assert_equals(priorityFireCount, 1);
+
+ taskController.abort();
+ assert_false(abortFired, "The signal should use abortController for abort");
+
+ abortController.abort();
+ assert_true(abortFired);
+
+ taskController.setPriority('user-visible');
+ assert_equals(signal.priority, 'user-visible');
+ assert_equals(priorityFireCount, 2);
+}, "TaskSignal.any() propagates abort and priority");
+
+
+test((t) => {
+ const controller = new TaskController();
+ const signal = TaskSignal.any([AbortSignal.abort()], {priority: controller.signal});
+
+ let fired = false;
+ signal.onprioritychange = t.step_func(() => {
+ fired = true;
+ });
+
+ controller.setPriority('background');
+ assert_true(fired);
+}, "TaskSignal.any() propagates priority after returning an aborted signal");
+
+test((t) => {
+ // Add a dependent in the initial event dispatch stage.
+ let controller = new TaskController();
+ let fired = false;
+ controller.signal.onprioritychange = t.step_func(() => {
+ fired = true;
+ const newSignal = TaskSignal.any([], {priority: controller.signal});
+ assert_equals(newSignal.priority, 'background');
+ newSignal.onprioritychange = t.unreached_func('onprioritychange called');
+ });
+ controller.setPriority('background');
+ assert_true(fired);
+
+ // Add a dependent while signaling prioritychange on dependents.
+ fired = false;
+ controller = new TaskController();
+ const signal = TaskSignal.any([], {priority: controller.signal});
+ signal.onprioritychange = t.step_func(() => {
+ fired = true;
+ const newSignal = TaskSignal.any([], {priority: signal});
+ assert_equals(newSignal.priority, 'background');
+ newSignal.onprioritychange = t.unreached_func('onprioritychange called');
+ });
+ controller.setPriority('background');
+ assert_true(fired);
+}, "TaskSignal.any() does not fire prioritychange for dependents added during prioritychange");
diff --git a/testing/web-platform/tests/scheduler/task-signal-onprioritychange.any.js b/testing/web-platform/tests/scheduler/task-signal-onprioritychange.any.js
new file mode 100644
index 0000000000..7f59e1f1f8
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/task-signal-onprioritychange.any.js
@@ -0,0 +1,14 @@
+// META: title=Scheduler: TaskSignal onprioritychange
+// META: global=window,worker
+'use strict';
+
+async_test(t => {
+ const controller = new TaskController({priority: 'user-visible'});
+ controller.signal.onprioritychange = t.step_func_done((event) => {
+ assert_equals(controller.signal.priority, 'background');
+ assert_equals(event.type, 'prioritychange');
+ assert_equals(event.target.priority, 'background');
+ assert_equals(event.previousPriority, 'user-visible');
+ });
+ controller.setPriority('background');
+}, 'Test that TaskSignal.onprioritychange listens for prioritychange events');
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js b/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js
new file mode 100644
index 0000000000..e96694f5ab
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js
@@ -0,0 +1,85 @@
+'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;
+ const task = scheduler.postTask(async () => {
+ controller.abort();
+ const p = scheduler.yield({signal: 'inherit'});
+ await promise_rejects_dom(t, 'AbortError', p);
+ }, {signal});
+ return promise_rejects_dom(t, 'AbortError', task);
+}, 'yield() with an aborted signal (inherit signal)');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ const task = scheduler.postTask(async () => {
+ controller.abort();
+ const p = scheduler.yield({signal: 'inherit', priority: 'background'});
+ await promise_rejects_dom(t, 'AbortError', p);
+ }, {signal});
+ return promise_rejects_dom(t, 'AbortError', task);
+}, 'yield() with an aborted signal (inherit signal priority override)');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ const task = scheduler.postTask(async () => {
+ controller.abort();
+ await scheduler.yield({priority: 'inherit'});
+ }, {signal});
+ return promise_rejects_dom(t, 'AbortError', task);
+}, 'yield() with an aborted signal (inherit priority only)');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'});
+ t.step(() => assert_false(signal.aborted));
+ const p = scheduler.yield({signal});
+ await promise_rejects_dom(t, 'AbortError', p);
+ });
+}, 'yield() aborted in a separate task');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'});
+ t.step(() => assert_false(signal.aborted));
+ const p = scheduler.yield({signal: 'inherit'});
+ await promise_rejects_dom(t, 'AbortError', p);
+ }, {signal});
+}, 'yield() aborted in a separate task (inherit)');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'});
+ t.step(() => assert_false(signal.aborted));
+ const p = scheduler.yield({signal: 'inherit', priority: 'background'});
+ await promise_rejects_dom(t, 'AbortError', p);
+ }, {signal});
+}, 'yield() aborted in a separate task (inherit signal priority override)');
+
+promise_test(t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'});
+ t.step(() => assert_false(signal.aborted));
+ await scheduler.yield({priority: 'inherit'});
+ t.step(() => assert_true(signal.aborted));
+ }, {signal});
+}, 'yield() aborted in a separate task (inherit priority only)');
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-inherit-across-promises.any.js b/testing/web-platform/tests/scheduler/tentative/yield/yield-inherit-across-promises.any.js
new file mode 100644
index 0000000000..eaa0125a78
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-inherit-across-promises.any.js
@@ -0,0 +1,65 @@
+'use strict';
+
+function postInheritPriorityTestTask(config) {
+ const ids = [];
+ const task = scheduler.postTask(async () => {
+ await new Promise(resolve => setTimeout(resolve));
+ await fetch('/common/blank.html');
+ await new Promise(resolve => setTimeout(resolve));
+ const subtask = scheduler.postTask(() => { ids.push('subtask'); }, {priority: config.subTaskPriority});
+ await scheduler.yield(config.yieldOptions);
+ ids.push('yield');
+ await subtask;
+ }, config.taskOptions);
+ return {task, ids}
+}
+
+for (let priority of ['user-blocking', 'background']) {
+ const expected = priority == 'user-blocking' ? 'yield,subtask' : 'subtask,yield';
+ promise_test(async t => {
+ const config = {
+ taskOptions: {priority},
+ subTaskPriority: 'user-blocking',
+ yieldOptions: {priority: 'inherit'},
+ };
+ const {task, ids} = postInheritPriorityTestTask(config);
+ await task;
+ assert_equals(ids.join(), expected);
+ }, `yield() inherits priority (string) across promises (${priority})`);
+
+ promise_test(async t => {
+ const signal = (new TaskController({priority})).signal;
+ const config = {
+ taskOptions: {signal},
+ subTaskPriority: 'user-blocking',
+ yieldOptions: {signal: 'inherit'},
+ };
+ const {task, ids} = postInheritPriorityTestTask(config);
+ await task;
+ assert_equals(ids.join(), expected);
+ }, `yield() inherits priority (signal) across promises (${priority})`);
+
+ promise_test(async t => {
+ const config = {
+ taskOptions: {priority},
+ subTaskPriority: 'user-blocking',
+ yieldOptions: {signal: 'inherit'},
+ };
+ const {task, ids} = postInheritPriorityTestTask(config);
+ await task;
+ assert_equals(ids.join(), expected);
+ }, `yield() inherits priority (priority string with signal inherit) across promises (${priority})`);
+}
+
+promise_test(async t => {
+ const controller = new TaskController();
+ const signal = controller.signal;
+ return scheduler.postTask(async () => {
+ await new Promise(resolve => setTimeout(resolve));
+ await fetch('/common/blank.html');
+ await new Promise(resolve => setTimeout(resolve));
+ controller.abort();
+ const p = scheduler.yield({signal: 'inherit'});
+ await promise_rejects_dom(t, 'AbortError', p);
+ }, {signal});
+}, `yield() inherits abort across promises`);
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-idle-callbacks.html b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-idle-callbacks.html
new file mode 100644
index 0000000000..d47e8c5eba
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-idle-callbacks.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<title>Scheduler: yield inheritance in requestIdleCallback</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';
+
+// Queues a requestIdleCallback that schedules 2 user-visible tasks, 2
+// background tasks, another requestIdleCallback, and then yields 3 times using
+// `yieldParams`.
+//
+// Returns {tasks, ids} where `tasks` is an array of promises associated with
+// the tasks and `ids` is an array of task ids appended to by the scheduled
+// tasks.
+function postTestTasks(yieldParams) {
+ const ids = [];
+ const task = new Promise(resolve => {
+ requestIdleCallback(async () => {
+ ids.push('i1');
+
+ const subtasks = [];
+ subtasks.push(scheduler.postTask(() => { ids.push('uv1'); }));
+ subtasks.push(scheduler.postTask(() => { ids.push('uv2'); }));
+ subtasks.push(scheduler.postTask(() => { ids.push('bg1'); }, {priority: 'background'}));
+ subtasks.push(scheduler.postTask(() => { ids.push('bg2'); }, {priority: 'background'}));
+ subtasks.push(new Promise(resolve => {
+ requestIdleCallback(() => {
+ ids.push('i2');
+ resolve();
+ });
+ }));
+
+ for (let i = 1; i <= 3; i++) {
+ await scheduler.yield(yieldParams);
+ ids.push('y' + i);
+ }
+ await Promise.all(subtasks);
+ resolve();
+ });
+ });
+ return {task, ids};
+}
+
+const expected_inherited_task_order = 'i1,uv1,uv2,y1,y2,y3,bg1,bg2,i2';
+
+promise_test(async t => {
+ const {task, ids} = postTestTasks({priority: "inherit"});
+ await task;
+ assert_equals(ids.join(), expected_inherited_task_order);
+}, 'requestIdleCallback() yields at background priority when inheriting priority');
+
+promise_test(async t => {
+ const {task, ids} = postTestTasks({signal: "inherit"});
+ await task;
+ assert_equals(ids.join(), expected_inherited_task_order);
+}, 'requestIdleCallback() yields at background priority when inheriting signal');
+
+</script>
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-posttask.any.js b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-posttask.any.js
new file mode 100644
index 0000000000..0700094dcf
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-posttask.any.js
@@ -0,0 +1,193 @@
+'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 => {
+ for (const config of priorityConfigs) {
+ const {tasks, ids} =
+ postTestTasks(config.options, {priority: 'inherit'});
+ await Promise.all(tasks);
+ assert_equals(ids.join(), config.expected);
+ }
+}, 'yield() with postTask tasks (inherit priority)');
+
+promise_test(async t => {
+ for (const config of signalConfigs) {
+ const {tasks, ids} =
+ postTestTasks(config.options, {signal: 'inherit'});
+ await Promise.all(tasks);
+ assert_equals(ids.join(), config.expected);
+ }
+}, 'yield() with postTask tasks (inherit signal)');
+
+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');
+
+promise_test(async t => {
+ const ids = [];
+
+ const controller = new TaskController();
+ const signal = controller.signal;
+
+ await scheduler.postTask(async () => {
+ ids.push('y0');
+
+ const subtasks = [];
+ subtasks.push(scheduler.postTask(() => { ids.push('uv1'); }));
+ subtasks.push(scheduler.postTask(() => { ids.push('uv2'); }));
+
+ // 'user-visible' continuations.
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y1');
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y2');
+
+ controller.setPriority('background');
+
+ // 'background' continuations.
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y3');
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y4');
+
+ await Promise.all(subtasks);
+ }, {signal});
+
+ assert_equals(ids.join(), 'y0,y1,y2,uv1,uv2,y3,y4');
+}, 'yield() with TaskSignal has dynamic priority')
+
+promise_test(async t => {
+ const ids = [];
+
+ await scheduler.postTask(async () => {
+ ids.push('y0');
+
+ const subtasks = [];
+ subtasks.push(scheduler.postTask(() => { ids.push('ub1'); }, {priority: 'user-blocking'}));
+ subtasks.push(scheduler.postTask(() => { ids.push('uv1'); }));
+
+ // Ignore inherited signal (user-visible continuations).
+ await scheduler.yield();
+ ids.push('y1');
+ await scheduler.yield();
+ ids.push('y2');
+
+ subtasks.push(scheduler.postTask(() => { ids.push('ub2'); }, {priority: 'user-blocking'}));
+ subtasks.push(scheduler.postTask(() => { ids.push('uv2'); }));
+
+ // Now use inherited signal (user-blocking continuations).
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y3');
+ await scheduler.yield({signal: 'inherit'});
+ ids.push('y4');
+
+ await Promise.all(subtasks);
+ }, {priority: 'user-blocking'});
+
+ assert_equals(ids.join(), 'y0,ub1,y1,y2,y3,y4,ub2,uv1,uv2');
+}, 'yield() mixed inheritance and default')
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-timers.any.js b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-timers.any.js
new file mode 100644
index 0000000000..ff5a3d4b33
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-priority-timers.any.js
@@ -0,0 +1,94 @@
+'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)');
+
+promise_test(async t => {
+ const {tasks, ids} = postTestTasks({priority: 'inherit'});
+ await Promise.all(tasks);
+ assert_equals(ids.join(), taskOrders['user-visible']);
+}, 'yield() with timer tasks (inherit priority)');
+
+promise_test(async t => {
+ const {tasks, ids} = postTestTasks({signal: 'inherit'});
+ await Promise.all(tasks);
+ assert_equals(ids.join(), taskOrders['user-visible']);
+}, 'yield() with timer tasks (inherit signal)');
diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-then-detach.html b/testing/web-platform/tests/scheduler/tentative/yield/yield-then-detach.html
new file mode 100644
index 0000000000..835f9e7a62
--- /dev/null
+++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-then-detach.html
@@ -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>