fune/dom/media/test/cloneElementVisually_helpers.js
Narcis Beleuzu 6a96b612b6 Backed out 5 changesets (bug 1556854) for mda failures on /test_media_selection.html CLOSED TREE
Backed out changeset 61a4022333fa (bug 1556854)
Backed out changeset cfd809c63877 (bug 1556854)
Backed out changeset 057cc9878594 (bug 1556854)
Backed out changeset 0f02ff8a5c67 (bug 1556854)
Backed out changeset 8719996c65b3 (bug 1556854)
2019-10-24 13:47:37 +03:00

224 lines
6.6 KiB
JavaScript

const TEST_VIDEO_1 = "http://mochi.test:8888/tests/dom/media/test/bipbop_225w_175kbps.mp4";
const TEST_VIDEO_2 = "http://mochi.test:8888/tests/dom/media/test/pixel_aspect_ratio.mp4";
const LONG_VIDEO = "http://mochi.test:8888/tests/dom/media/test/gizmo.mp4";
/**
* Ensure that the original <video> is prepped and ready to play
* before starting any other tests.
*/
async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
[ "media.test.video-suspend", true ],
[ "media.suspend-bkgnd-video.enabled", true ],
[ "media.suspend-bkgnd-video.delay-ms", 500 ],
[ "media.dormant-on-pause-timeout-ms", 0 ],
[ "media.cloneElementVisually.testing", true ],
],
});
let originalVideo = document.getElementById("original");
await setVideoSrc(originalVideo, TEST_VIDEO_1);
}
/**
* Given a canvas, as well as a width and height of something to be
* blitted onto that canvas, makes any necessary adjustments to the
* canvas to work with the current display resolution.
*
* @param canvas (<canvas> element)
* The canvas to be adjusted.
* @param width (int)
* The width of the image to be blitted.
* @param height (int)
* The height of the image to be blitted.
* @return CanvasRenderingContext2D (SpecialPowers wrapper)
*/
function getWrappedScaledCanvasContext(canvas, width, height) {
let devRatio = window.devicePixelRatio || 1;
let ctx = SpecialPowers.wrap(canvas.getContext("2d"));
let backingRatio = ctx.webkitBackingStorePixelRatio || 1;
let ratio = 1;
ratio = devRatio / backingRatio;
canvas.width = ratio * width;
canvas.height = ratio * height;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
ctx.scale(ratio, ratio);
return ctx;
}
/**
* Given a <video> element in the DOM, figures out its location, and captures
* a snapshot of what it's currently presenting.
*
* @param video (<video> element)
* @return ImageData (SpecialPowers wrapper)
*/
function captureFrameImageData(video) {
// Flush layout first, just in case the decoder has recently
// updated the dimensions of the video.
let rect = video.getBoundingClientRect();
let width = video.videoWidth;
let height = video.videoHeight;
let canvas = document.createElement("canvas");
let ctx = getWrappedScaledCanvasContext(canvas, width, height);
ctx.drawWindow(window, rect.left, rect.top, width, height, "rgb(0,0,0)");
return ctx.getImageData(0, 0, width, height);
}
/**
* Given two <video> elements, captures snapshots of what they're currently
* displaying, and asserts that they're identical.
*
* @param video1 (<video> element)
* A video element to compare against.
* @param video2 (<video> element)
* A video to compare with video1.
* @return Promise
* @resolves
* Resolves as true if the two videos match.
*/
async function assertVideosMatch(video1, video2) {
let video1Frame = captureFrameImageData(video1);
let video2Frame = captureFrameImageData(video2);
let left = document.getElementById("left");
let leftCtx = getWrappedScaledCanvasContext(left, video1Frame.width, video1Frame.height);
leftCtx.putImageData(video1Frame, 0, 0);
let right = document.getElementById("right");
let rightCtx = getWrappedScaledCanvasContext(right, video2Frame.width, video2Frame.height);
rightCtx.putImageData(video2Frame, 0, 0);
if (video1Frame.data.length != video2Frame.data.length) {
return false;
}
let leftDataURL = left.toDataURL();
let rightDataURL = right.toDataURL();
if (leftDataURL != rightDataURL) {
dump("Left frame: " + leftDataURL + "\n\n");
dump("Right frame: " + rightDataURL + "\n\n");
return false;
}
return true;
}
/**
* Testing helper function that constructs a node clone of a video,
* injects it into the DOM, and then runs an async testing function.
* This also does the work of removing the clone before resolving.
*
* @param video (<video> element)
* The video to clone the node from.
* @param asyncFn (async function)
* A test function that will be passed the new clone as its
* only argument.
* @return Promise
* @resolves
* When the asyncFn resolves and the clone has been removed
* from the DOM.
*/
async function withNewClone(video, asyncFn) {
let clone = video.cloneNode();
clone.id = "clone";
clone.src = "";
let content = document.getElementById("content");
content.appendChild(clone);
try {
await asyncFn(clone);
} finally {
clone.remove();
}
}
/**
* Sets the src on a video and waits until its ready.
*
* @param video (<video> element)
* The video to set the src on.
* @param src (string)
* The URL to set as the source on a video.
* @return Promise
* @resolves
* When the video fires the "canplay" event.
*/
async function setVideoSrc(video, src) {
let promiseReady = waitForEventOnce(video, "canplay");
video.src = src;
await promiseReady;
}
/**
* Returns a Promise once target emits a particular event
* once.
*
* @param target (DOM node)
* The target to monitor for the event.
* @param event (string)
* The name of the event to wait for.
* @return Promise
* @resolves
* When the event fires, and resolves to the event.
*/
function waitForEventOnce(target, event) {
return new Promise(resolve => {
target.addEventListener(event, resolve, { once: true });
});
}
/**
* Polls the video debug data as a hacky way of knowing when
* when the decoders have shut down.
*
* @param video (<video> element)
* @return Promise
* @resolves
* When the decoder has shut down.
*/
async function waitForShutdownDecoder(video) {
await SimpleTest.promiseWaitForCondition(async () => {
let readerData = SpecialPowers.wrap(video).mozDebugReaderData;
return readerData.includes(": shutdown");
}, "Video decoder should eventually shut down.");
}
/**
* Ensures that both hiding and pausing the video causes the
* video to suspend and make dormant its decoders, respectively.
*
* @param video (<video element)
*/
async function ensureVideoSuspendable(video) {
video = SpecialPowers.wrap(video);
ok(!video.hasSuspendTaint(), "Should be suspendable");
// First, we'll simulate putting the video in the background by
// making it invisible.
let suspendPromise = waitForEventOnce(video, "mozentervideosuspend");
video.setVisible(false);
await suspendPromise;
ok(true, "Suspended after the video was made invisible.");
video.setVisible(true)
ok(!video.hasSuspendTaint(), "Should still be suspendable.");
// Next, we'll pause the video.
await video.pause();
await waitForShutdownDecoder(video);
ok(true, "Shutdown decoder after the video was paused.");
await video.play();
}