summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/video-rvfc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/video-rvfc
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/video-rvfc')
-rw-r--r--testing/web-platform/tests/video-rvfc/META.yml3
-rw-r--r--testing/web-platform/tests/video-rvfc/README.md7
-rw-r--r--testing/web-platform/tests/video-rvfc/idlharness.window.js17
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-before-xr-session.https.html66
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-dom.html59
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-during-xr-session.https.html60
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-parallel.html65
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-repeating.html89
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback-webrtc.https.html165
-rw-r--r--testing/web-platform/tests/video-rvfc/request-video-frame-callback.html167
10 files changed, 698 insertions, 0 deletions
diff --git a/testing/web-platform/tests/video-rvfc/META.yml b/testing/web-platform/tests/video-rvfc/META.yml
new file mode 100644
index 0000000000..5e581f4234
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/META.yml
@@ -0,0 +1,3 @@
+spec: https://wicg.github.io/video-rvfc/
+suggested_reviewers:
+ - tguilbert
diff --git a/testing/web-platform/tests/video-rvfc/README.md b/testing/web-platform/tests/video-rvfc/README.md
new file mode 100644
index 0000000000..ab7a353051
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/README.md
@@ -0,0 +1,7 @@
+# HTMLVideoElement.requestVideoFrameCallback specification Tests
+
+The HTMLVideoElement.requestVideoFrameCallback specification is available here: https://wicg.github.io/video-rvfc
+
+GitHub repository: https://github.com/WICG/video-rvfc
+
+File an issue: https://github.com/wicg/video-rvfc/issues/new
diff --git a/testing/web-platform/tests/video-rvfc/idlharness.window.js b/testing/web-platform/tests/video-rvfc/idlharness.window.js
new file mode 100644
index 0000000000..fa9e4e0988
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/idlharness.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: timeout=long
+
+'use strict';
+
+idl_test(
+ ['video-rvfc'],
+ ['html', 'dom'],
+ idl_array => {
+ idl_array.add_objects({
+ HTMLVideoElement: ['video'],
+ });
+ self.video = document.createElement('video');
+ }
+);
+
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-before-xr-session.https.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-before-xr-session.https.html
new file mode 100644
index 0000000000..5277fbba92
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-before-xr-session.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<title>Test that video.rVFC callbacks started before an XRSession work.</title>
+<body>
+</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="../webxr/resources/webxr_util.js"></script>
+<script src="../webxr/resources/webxr_test_constants.js"></script>
+<script>
+
+// Start the video.rVFC callbacks before starting the XR Session.
+let video = document.createElement('video');
+video.src = getVideoURI('/media/movie_5');
+
+var numberVFCs = 0;
+let videoCallback = () => {
+ numberVFCs++;
+ video.requestVideoFrameCallback(videoCallback);
+}
+
+video.requestVideoFrameCallback(videoCallback);
+video.play();
+
+let testFunction = async function(session, fakeDeviceController, t) {
+ let watcherDone = new Event("watcherdone");
+ let eventWatcher = new EventWatcher(t, session, ["end", "watcherdone"]);
+ let eventPromise = eventWatcher.wait_for(["end", "watcherdone"]);
+
+ numberVFCs = 0;
+
+ function onXRFrame(time, frame) {
+ if(numberVFCs >= 2) {
+ // Make sure video.rVFCs are still coming through before ending the
+ // session.
+ session.end();
+ }
+
+ session.requestAnimationFrame(onXRFrame);
+ }
+
+ function onSessionEnd(event) {
+ // Make sure we are still getting rVFC callbacks after the session end.
+ numberVFCs = 0;
+ t.step_wait_func(() => numberVFCs >= 2,
+ () => session.dispatchEvent(watcherDone),
+ "Time out waiting for VFC callbacks");
+ }
+
+ session.addEventListener("end", onSessionEnd, false);
+ session.requestAnimationFrame(onXRFrame);
+
+ return eventPromise;
+}
+
+xr_session_promise_test('Make sure video.rVFC works during a non-immersive session',
+ testFunction, TRACKED_IMMERSIVE_DEVICE, 'inline');
+
+video.currentTime = 0;
+
+xr_session_promise_test('Make sure video.rVFC works during an immersive session',
+ testFunction, TRACKED_IMMERSIVE_DEVICE, 'immersive-vr');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-dom.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-dom.html
new file mode 100644
index 0000000000..c1804b4edd
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-dom.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<title>Test the video.requestVideoFrameCallback() API for non visible video elements.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<body>
+</body>
+<script>
+var testVideo = {
+ url: getVideoURI('/media/movie_5'),
+ height: 240,
+ width: 320,
+}
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+
+ video.requestVideoFrameCallback(done);
+ video.src = testVideo.url;
+ await video.play();
+
+ return promise;
+}, 'Test a video outside of the DOM can still use video.rVFC.');
+
+function rvfcStyleTest(applyStyle, description) {
+ promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+ applyStyle(video);
+
+ video.requestVideoFrameCallback(
+ t.step_func( _ => {
+ // Make sure we can receive more than one callback.
+ video.requestVideoFrameCallback(done);
+ })
+ );
+
+ video.src = testVideo.url;
+ await video.play();
+
+ return promise;
+ }, description);
+}
+
+rvfcStyleTest((video) => { video.style.display = "none"},
+ 'Test video.rVFC works with "display:none".');
+
+rvfcStyleTest((video) => { video.style.visibility = "hidden"},
+ 'Test video.rVFC works with "visibility:hidden".');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-during-xr-session.https.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-during-xr-session.https.html
new file mode 100644
index 0000000000..34561ee5fd
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-during-xr-session.https.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<title>Test that video.rVFC callbacks started during an XRSession work.</title>
+<body>
+ <canvas/>
+</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="../webxr/resources/webxr_util.js"></script>
+<script src="../webxr/resources/webxr_test_constants.js"></script>
+<script>
+
+let testFunction = async function(session, fakeDeviceController, t) {
+ let watcherDone = new Event("watcherdone");
+ let eventWatcher = new EventWatcher(t, session, ["end", "watcherdone"]);
+ let eventPromise = eventWatcher.wait_for(["end", "watcherdone"]);
+
+ // Start the video.rVFC callbacks while we are in the the XR Session.
+ let video = document.createElement('video');
+ video.src = getVideoURI('/media/movie_5');
+
+ var numberVFCs = 0;
+ let videoCallback = () => {
+ numberVFCs++;
+ video.requestVideoFrameCallback(videoCallback);
+ }
+
+ video.requestVideoFrameCallback(videoCallback);
+ video.play();
+
+ function onXRFrame(time, frame) {
+ if(numberVFCs >= 2) {
+ // Make sure video.rVFCs are coming through before ending the
+ // session.
+ session.end();
+ }
+
+ session.requestAnimationFrame(onXRFrame);
+ }
+
+ function onSessionEnd(event) {
+ // Make sure we are still getting rVFC callbacks after the session end.
+ numberVFCs = 0;
+ t.step_wait_func(() => numberVFCs >= 2,
+ () => session.dispatchEvent(watcherDone),
+ "Time out waiting for VFC callbacks");
+ }
+
+ session.addEventListener("end", onSessionEnd, false);
+ session.requestAnimationFrame(onXRFrame);
+
+ return eventPromise;
+}
+
+xr_session_promise_test('Make sure video.rVFC callbacks started during an immersive session continue after it ends',
+ testFunction, TRACKED_IMMERSIVE_DEVICE, 'immersive-vr');
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-parallel.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-parallel.html
new file mode 100644
index 0000000000..682fd0ac8f
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-parallel.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<title>Test having multiple video.rVFC callbacks in flight for a single element.</title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script>
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let firstTime;
+ let firstMetadata;
+
+ video.requestVideoFrameCallback(t.step_func((time, metadata) => {
+ firstTime = time;
+ firstMetadata = metadata;
+ }));
+
+ video.requestVideoFrameCallback(t.step_func((time, metadata) => {
+ assert_equals(firstTime, time);
+ assert_object_equals(firstMetadata, metadata);
+ done();
+ }));
+
+ video.src = getVideoURI('/media/movie_5');
+ video.play();
+
+ return promise;
+}, 'Test callbacks get the same information.');
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let secondCallbackId;
+
+ video.requestVideoFrameCallback(
+ t.step_func(_ => { video.cancelVideoFrameCallback(secondCallbackId); })
+ );
+
+ secondCallbackId = video.requestVideoFrameCallback(
+ t.step_func(_ => {
+ assert_unreached("Cancelled callbacks shouldn't be executed")
+ })
+ );
+
+ // NOTE: This callback should be executed last.
+ video.requestVideoFrameCallback(done);
+
+ video.src = getVideoURI('/media/movie_5');
+ video.play();
+
+ return promise;
+}, 'Test we can cancel callbacks from callbacks.');
+</script>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-repeating.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-repeating.html
new file mode 100644
index 0000000000..38e4abafd4
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-repeating.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<title>Test repeatedly chaining video.rVFC() callbacks.</title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script>
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let firstTime;
+ video.requestVideoFrameCallback(t.step_func((time) => {
+ firstTime = time;
+
+ // Queue up a callback and make sure it's not immediately executed.
+ let secondTime;
+ video.requestVideoFrameCallback(t.step_func((time) => {
+ secondTime = time;
+ assert_greater_than(secondTime, firstTime, "Callbacks should be executed on the next frame");
+ }))
+
+ // Queue up a second callback, and make sure it's called at the same time
+ // as the one we just queued up.
+ video.requestVideoFrameCallback(t.step_func((time) => {
+ assert_equals(time, secondTime, "Callbacks queued together should be called at the same time");
+ done();
+ }))
+
+ }));
+
+ video.src = getVideoURI('/media/movie_5');
+ await video.play();
+
+ return promise;
+}, 'Test new callbacks are only called on the next frame.');
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let maxNumberOfCalls = 10;
+ let currentCallNumber = 0;
+ let lastMetadata;
+
+ function verifyMetadata(last, current) {
+ assert_greater_than(current.presentedFrames, last.presentedFrames, "presentedFrames should be monotonically increasing");
+ assert_greater_than(current.presentationTime, last.presentationTime, "presentationTime should be monotonically increasing");
+ assert_greater_than(current.expectedDisplayTime, last.expectedDisplayTime, "expectedDisplayTime should be monotonically increasing");
+
+ // We aren't seeking through the file, so this should be increasing from frame to frame.
+ assert_greater_than(current.mediaTime, last.mediaTime, "mediaTime should be increasing");
+
+ // The test video's size doesn't change.
+ assert_equals(current.width, last.width, "width should remain constant");
+ assert_equals(current.height, last.height, "height should remain constant");
+ }
+
+ function repeatingCallback(time, metadata) {
+ // Skip the first call to verifyMetadata.
+ if (currentCallNumber)
+ verifyMetadata(lastMetadata, metadata)
+
+ lastMetadata = metadata;
+
+ if (++currentCallNumber > maxNumberOfCalls) {
+ done()
+ } else {
+ video.requestVideoFrameCallback(t.step_func(repeatingCallback));
+ }
+ }
+
+ video.requestVideoFrameCallback(t.step_func(repeatingCallback));
+
+ video.src = getVideoURI('/media/movie_5');
+ await video.play();
+
+ return promise;
+}, 'Test chaining calls to video.rVFC, and verify the required parameters.');
+</script>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback-webrtc.https.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-webrtc.https.html
new file mode 100644
index 0000000000..dcf97e4ca9
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback-webrtc.https.html
@@ -0,0 +1,165 @@
+<!doctype html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>WebRTC video.requestVideoFrameCallback() test</title>
+ <script src="/webrtc/RTCPeerConnection-helper.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <div>
+ <video id="local-view" muted autoplay="autoplay"></video>
+ <video id="remote-view" muted autoplay="autoplay"/>
+ </video>
+ </div>
+
+ <!-- These files are in place when executing on W3C. -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript">
+ var test = async_test('Test video.requestVideoFrameCallback() parameters for WebRTC applications.');
+
+ //
+ // This test is based on /webrtc/simplecall.https.html, but it calls to
+ // video.requestVideoFrameCallback() before ending, to verify WebRTC required
+ // and optional parameters.
+ //
+
+ var gFirstConnection = null;
+ var gSecondConnection = null;
+ var gCallbackCounter = 0;
+ var verify_params = (now, metadata) => {
+ gCallbackCounter = gCallbackCounter + 1;
+ assert_greater_than(now, 0);
+
+ // Verify all required fields
+ assert_greater_than(metadata.presentationTime, 0);
+ assert_greater_than(metadata.expectedDisplayTime, 0);
+ assert_greater_than(metadata.presentedFrames, 0);
+ assert_greater_than(metadata.width, 0);
+ assert_greater_than(metadata.height, 0);
+ assert_true("mediaTime" in metadata, "mediaTime should be present");
+
+ // Verify WebRTC only fields.
+ assert_true("rtpTimestamp" in metadata, "rtpTimestamp should be present");
+ assert_true("receiveTime" in metadata, "receiveTime should be present");
+ // captureTime is not available until roundtrip time estimation is done.
+ if (gCallbackCounter > 60 || "captureTime" in metadata) {
+ assert_true("captureTime" in metadata, "captureTime should be present");
+ test.done();
+ }
+ else {
+ // Keep requesting callbacks.
+ document.getElementById('remote-view').requestVideoFrameCallback(test.step_func(verify_params));
+ }
+ }
+
+ var verify_local_metadata = (now, metadata) => {
+ assert_greater_than(metadata.expectedDisplayTime, 0);
+ assert_greater_than(metadata.presentedFrames, 0);
+ assert_greater_than(metadata.width, 0);
+ assert_greater_than(metadata.height, 0);
+ assert_true("captureTime" in metadata, "captureTime should always be present for local sources.");
+ assert_greater_than(metadata.captureTime, 0);
+ }
+
+ // If the remote video gets video data that implies the negotiation
+ // as well as the ICE and DTLS connection are up.
+ document.getElementById('remote-view')
+ .addEventListener('loadedmetadata', function() {
+ document.getElementById('remote-view').requestVideoFrameCallback(test.step_func(verify_params));
+ });
+
+ document.getElementById('local-view')
+ .addEventListener('loadmetadata', function() {
+ document.getElementById('local-view').requestVideoFrameCallback(test.step_func_done(verify_local_metadata));
+ });
+
+
+ function getNoiseStreamOkCallback(localStream) {
+ gFirstConnection = new RTCPeerConnection(null);
+ test.add_cleanup(() => gFirstConnection.close());
+ gFirstConnection.onicecandidate = onIceCandidateToFirst;
+
+ gSecondConnection = new RTCPeerConnection(null);
+ test.add_cleanup(() => gSecondConnection.close());
+ gSecondConnection.onicecandidate = onIceCandidateToSecond;
+ gSecondConnection.ontrack = onRemoteTrack;
+
+ localStream.getTracks().forEach(function(track) {
+ // Bidirectional streams are needed in order for captureTime to be
+ // populated. Use the same source in both directions.
+ gFirstConnection.addTrack(track, localStream);
+ gSecondConnection.addTrack(track, localStream);
+ });
+
+ gFirstConnection.createOffer().then(onOfferCreated, failed('createOffer'));
+
+ var videoTag = document.getElementById('local-view');
+ videoTag.srcObject = localStream;
+ };
+
+ var onOfferCreated = test.step_func(function(offer) {
+ gFirstConnection.setLocalDescription(offer);
+
+ // This would normally go across the application's signaling solution.
+ // In our case, the "signaling" is to call this function.
+ receiveCall(offer.sdp);
+ });
+
+ function receiveCall(offerSdp) {
+ var parsedOffer = new RTCSessionDescription({ type: 'offer',
+ sdp: offerSdp });
+ gSecondConnection.setRemoteDescription(parsedOffer);
+
+ gSecondConnection.createAnswer().then(onAnswerCreated,
+ failed('createAnswer'));
+ };
+
+ var onAnswerCreated = test.step_func(function(answer) {
+ gSecondConnection.setLocalDescription(answer);
+
+ // Similarly, this would go over the application's signaling solution.
+ handleAnswer(answer.sdp);
+ });
+
+ function handleAnswer(answerSdp) {
+ var parsedAnswer = new RTCSessionDescription({ type: 'answer',
+ sdp: answerSdp });
+ gFirstConnection.setRemoteDescription(parsedAnswer);
+ };
+
+ var onIceCandidateToFirst = test.step_func(function(event) {
+ // If event.candidate is null = no more candidates.
+ if (event.candidate) {
+ gSecondConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onIceCandidateToSecond = test.step_func(function(event) {
+ if (event.candidate) {
+ gFirstConnection.addIceCandidate(event.candidate);
+ }
+ });
+
+ var onRemoteTrack = test.step_func(function(event) {
+ var videoTag = document.getElementById('remote-view');
+ if (!videoTag.srcObject) {
+ videoTag.srcObject = event.streams[0];
+ }
+ });
+
+ // Returns a suitable error callback.
+ function failed(function_name) {
+ return test.unreached_func('WebRTC called error callback for ' + function_name);
+ }
+
+ // This function starts the test.
+ test.step(function() {
+ getNoiseStream({ video: true, audio: true })
+ .then(test.step_func(getNoiseStreamOkCallback), failed('getNoiseStream'));
+ });
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/video-rvfc/request-video-frame-callback.html b/testing/web-platform/tests/video-rvfc/request-video-frame-callback.html
new file mode 100644
index 0000000000..256216e8fc
--- /dev/null
+++ b/testing/web-platform/tests/video-rvfc/request-video-frame-callback.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<title>Test the basics of the video.requestVideoFrameCallback() API.</title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script>
+var testVideo = {
+ url: getVideoURI('/media/movie_5'),
+ height: 240,
+ width: 320,
+}
+
+var altTestVideo = {
+ url: getVideoURI('/media/counting'),
+ height: 240,
+ width: 320,
+}
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let id = video.requestVideoFrameCallback(
+ t.step_func((time, metadata) => {
+ assert_true(time > 0);
+ assert_equals(metadata.height, testVideo.height);
+ assert_equals(metadata.width, testVideo.width);
+ done();
+ })
+ );
+
+ assert_true(id > 0);
+
+ video.src = testVideo.url;
+ await video.play();
+
+ return promise;
+}, 'Test we can register a video.rVFC callback.');
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ video.requestVideoFrameCallback(
+ t.step_func(video_now => {
+ // Queue a call to window.rAF, and make sure it is executed within the
+ // same turn of the event loop (with the same 'time' parameter).
+ window.requestAnimationFrame( t.step_func( window_now => {
+ assert_equals(video_now, window_now);
+ done();
+ }));
+ })
+ );
+
+ video.src = testVideo.url;
+ await video.play();
+
+ return promise;
+}, 'Test video.rVFC callbacks run before window.rAF callbacks.');
+
+
+promise_test(async function(t) {
+ let done;
+ const promise = new Promise(resolve => done = resolve);
+
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ let id = video.requestVideoFrameCallback(
+ t.step_func(_ => {
+ assert_unreached("Cancelled callbacks shouldn't be executed")
+ })
+ );
+
+ video.cancelVideoFrameCallback(id);
+
+ video.requestVideoFrameCallback(
+ t.step_func(_ => {
+ // At this point, the other callback shouldn't have fired, but
+ // give it some more time and really make sure it doesn't, by going
+ // throught the event loop once more.
+ t.step_timeout(() => { done(); }, 0);
+ })
+ );
+
+ video.src = testVideo.url;
+ await video.play();
+
+ return promise;
+}, 'Test we can cancel a video.rVFC request.');
+
+test(function(t) {
+ let video = document.createElement('video');
+ document.body.appendChild(video);
+
+ // requestVideoFrameCallback() expects 1 function as a parameter.
+ assert_throws_js(TypeError, _ => { video.requestVideoFrameCallback() } );
+ assert_throws_js(TypeError, _ => { video.requestVideoFrameCallback(0) });
+ assert_throws_js(TypeError, _ => { video.requestVideoFrameCallback("foo") });
+
+ // cancelVideoFrameCallback() expects 1 number as a parameter
+ assert_throws_js(TypeError, _ => { video.cancelVideoFrameCallback() } );
+
+ // Invalid calls are just no-ops
+ video.cancelVideoFrameCallback(_ => {});
+ video.cancelVideoFrameCallback(NaN);
+ video.cancelVideoFrameCallback("foo");
+ video.cancelVideoFrameCallback(12345);
+ video.cancelVideoFrameCallback(-1);
+
+}, 'Test invalid calls to the video.rVFC API.');
+
+promise_test(async function(t) {
+ let video = document.createElement('video');
+ video.autoplay = true;
+ document.body.appendChild(video);
+
+ let first_width = 0;
+ let first_height = 0;
+
+ video.src = testVideo.url;
+
+ await video.play();
+
+ // Switch to and from a second video, and make sure we get rVFC calls at
+ // each step.
+ return new Promise((resolve, reject) => {
+ let onReturnToOriginalVideo = t.step_func((now, metadata) => {
+ assert_equals(first_width, metadata.width);
+ assert_equals(first_height, metadata.height);
+
+ resolve();
+ });
+
+ let onAltVideoFirstFrame = t.step_func((now, metadata) => {
+ // The videos have different sizes, and shouldn't match.
+ assert_not_equals(first_width, metadata.width);
+ assert_not_equals(first_height, metadata.height);
+
+ // Swith back to the original video.
+ video.requestVideoFrameCallback(onReturnToOriginalVideo);
+ video.src = testVideo.url;
+ });
+
+ let onFirstFrame = t.step_func((now, metadata) => {
+ first_width = metadata.width;
+ first_height = metadata.height;
+
+ // Callbacks should persist after changing the source to the alt video.
+ video.requestVideoFrameCallback(onAltVideoFirstFrame);
+ video.src = altTestVideo.url;
+ })
+
+ video.requestVideoFrameCallback(onFirstFrame);
+ });
+}, 'Test video.rVFC does not stop when switching sources.');
+
+</script>
+</html>