245 lines
7.8 KiB
HTML
245 lines
7.8 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<title>BrowserCaptureMediaStreamTrack restrictTo()</title>
|
|
<link rel="help" href="https://screen-share.github.io/element-capture/">
|
|
</head>
|
|
|
|
<body>
|
|
<p class="instructions">
|
|
When prompted, accept to give permission to use your audio, video devices.
|
|
</p>
|
|
<h1 class="instructions">Description</h1>
|
|
<p class="instructions">
|
|
This test checks that restricting BrowserCaptureMediaStreamTrack works as
|
|
expected.
|
|
</p>
|
|
|
|
<style>
|
|
div {
|
|
height: 100px;
|
|
}
|
|
.stacking {
|
|
opacity: 0.9;
|
|
}
|
|
#container {
|
|
columns:4;
|
|
column-fill:auto;
|
|
}
|
|
.fragmentize {
|
|
height: 50px;
|
|
}
|
|
#target {
|
|
background: linear-gradient(red, blue);
|
|
}
|
|
</style>
|
|
|
|
|
|
<div id='container'>
|
|
<div id='target'></div>
|
|
</div>
|
|
<video id="video"
|
|
style="border: 2px blue dotted; width: 250px; height: 250px;"
|
|
autoplay playsinline muted></video>
|
|
|
|
<script src=/resources/testharness.js></script>
|
|
<script src=/resources/testharnessreport.js></script>
|
|
<script src=/resources/testdriver.js></script>
|
|
<script src=/resources/testdriver-vendor.js></script>
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
// For more information, see:
|
|
// https://screen-share.github.io/element-capture/#elements-eligible-for-restriction
|
|
const EligibilityRequirement = {
|
|
StackingContext: "StackingContext",
|
|
OnlyOneBoxFragment: "OnlyOneBoxFragment",
|
|
FlattenedIn3D: "FlattenedIn3D",
|
|
};
|
|
|
|
// The target div.
|
|
const div = document.getElementById('target');
|
|
|
|
// Returns a promise that, if successful, will resolve to a media stream.
|
|
async function getDisplayMedia() {
|
|
return test_driver.bless('getDisplayMedia', () =>
|
|
navigator.mediaDevices.getDisplayMedia({
|
|
video: { displaySurface: "browser" },
|
|
selfBrowserSurface: "include",
|
|
}));
|
|
}
|
|
|
|
// Returns a promise that will resolve successfully if at least one frame is
|
|
// read before the timeout.
|
|
function assertFrameRead(t, state, message) {
|
|
const last_frame_count = state.frame_count;
|
|
return t.step_wait(() => state.frame_count > last_frame_count,
|
|
message, 5000, 10);
|
|
}
|
|
|
|
// Returns a promise that will resolve successfully if there are no frames
|
|
// produced for an entire second after being called.
|
|
function assertStopsProducingFrames(t, state, message) {
|
|
let last_frame_count = state.frame_count;
|
|
|
|
return t.step_timeout(() => {
|
|
assert_equals(state.frame_count, last_frame_count);
|
|
}, 1000);
|
|
}
|
|
|
|
function makeDivEligible() {
|
|
// Must always have a stacking context to be eligible.
|
|
div.classList.add("stacking");
|
|
div.parentElement.classList.remove("fragmented");
|
|
div.style.transform = "";
|
|
}
|
|
|
|
function makeDivIneligible(state) {
|
|
switch(state.eligibilityParam) {
|
|
case EligibilityRequirement.StackingContext:
|
|
div.classList.remove("stacking");
|
|
break;
|
|
|
|
case EligibilityRequirement.OnlyOneBoxFragment:
|
|
div.parentElement.classList.add("fragmented");
|
|
break;
|
|
|
|
case EligibilityRequirement.FlattenedIn3D:
|
|
div.style.transform = "rotateY(90deg)";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Restore element state after each test.
|
|
function cleanupDiv() {
|
|
div.classList.remove("stacking");
|
|
div.parentElement.classList.remove("fragmented");
|
|
div.style.transform = "";
|
|
}
|
|
|
|
function startAnimation(t, state) {
|
|
let count = 0;
|
|
function animate() {
|
|
if (!state.running) {
|
|
return;
|
|
}
|
|
count += 1;
|
|
div.innerText = count;
|
|
window.requestAnimationFrame(animate);
|
|
}
|
|
window.requestAnimationFrame(animate);
|
|
|
|
// Stop animation as part of cleanup.
|
|
t.add_cleanup(() => { state.running = false; });
|
|
}
|
|
|
|
// Updates the state.frame_count value whenever a new frame is received on
|
|
// the passed in media stream track.
|
|
async function readFromTrack(state, track) {
|
|
while (state.running) {
|
|
const reader = new MediaStreamTrackProcessor(track).readable.getReader();
|
|
while (true) {
|
|
const frameOrDone = await reader.read();
|
|
if (frameOrDone.done) {
|
|
break;
|
|
}
|
|
frameOrDone.value.close();
|
|
state.frame_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parameterized test method. Note that this returns a Promise that will be
|
|
// resolved to represent success of the entire promise test.
|
|
async function runTest(t, eligibilityParam) {
|
|
let state = {
|
|
eligibilityParam: eligibilityParam,
|
|
frame_count: 0,
|
|
running: true,
|
|
reading: false,
|
|
};
|
|
startAnimation(t, state);
|
|
|
|
let videoTrack = undefined;
|
|
return getDisplayMedia().then(stream => {
|
|
t.add_cleanup(() => {
|
|
stream.getTracks().forEach(track => track.stop());
|
|
});
|
|
assert_true(!!stream, "should have resolved to a stream.");
|
|
assert_true(stream.active, "stream should be active.");
|
|
assert_equals(stream.getVideoTracks().length, 1);
|
|
|
|
[videoTrack] = stream.getVideoTracks();
|
|
assert_true(videoTrack instanceof MediaStreamTrack,
|
|
"track should be either MediaStreamTrack or a subclass thereof.");
|
|
assert_equals(videoTrack.readyState, "live", "track should be live.");
|
|
|
|
// Consume the stream in a video element.
|
|
const video = document.querySelector('video');
|
|
video.srcObject = stream;
|
|
|
|
// Remove the video source, so that the stream object can be released.
|
|
t.add_cleanup(() => {video.srcObject = null});
|
|
|
|
// Keep track of the number of frames used.
|
|
const readPromise = readFromTrack(state, videoTrack);
|
|
t.add_cleanup(() => readPromise);
|
|
|
|
return assertFrameRead(t, state, "Track should produce frames.");
|
|
}).then(() => {
|
|
assert_true(!!RestrictionTarget, "RestrictionTarget exposed.");
|
|
assert_true(!!RestrictionTarget.fromElement,
|
|
"RestrictionTarget.fromElement exposed.");
|
|
|
|
return RestrictionTarget.fromElement(div);
|
|
}).then(restrictionTarget => {
|
|
assert_true(!!videoTrack.restrictTo, "restrictTo exposed.");
|
|
assert_true(typeof videoTrack.restrictTo === 'function',
|
|
"restrictTo is a function.");
|
|
|
|
return videoTrack.restrictTo(restrictionTarget);
|
|
}).then(() => {
|
|
// By default, elements are not eligible for restriction due to not being
|
|
// placed in their own stacking context.
|
|
return assertStopsProducingFrames(t, state,
|
|
"No new frames after restriction.");
|
|
});
|
|
|
|
// TODO(crbug.com/333770107): once the issue with the
|
|
// --disable-threaded-compositing flag is resolved on Chrome's check in bots
|
|
// the rest of this test should be enabled.
|
|
// ).then(() => {
|
|
// // Should be unpaused now that the element is eligible.
|
|
// makeDivEligible();
|
|
|
|
// // Make sure the element state is restored to default between tests.
|
|
// t.add_cleanup(() => { cleanupDiv(); });
|
|
|
|
// // Restart the reader now that the stream is producing frames again.
|
|
// return assertFrameRead(t, state,
|
|
// "Received at least one frame after becoming eligible.");
|
|
// }).then(() => {
|
|
|
|
// // Should pause if it becomes ineligible again.
|
|
// makeDivIneligible(state);
|
|
// return assertStopsProducingFrames(t, state,
|
|
// "No new frames after becoming ineligible again.");
|
|
// });
|
|
}
|
|
|
|
// Test parameterizations.
|
|
[
|
|
EligibilityRequirement.StackingContext,
|
|
EligibilityRequirement.OnlyOneBoxFragment,
|
|
EligibilityRequirement.FlattenedIn3D,
|
|
].forEach(param =>
|
|
promise_test(t => runTest(t, param),
|
|
`Tests that restricting MediaStreamTrack objects works as expected (${param}).`
|
|
));
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|