summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/mediacapture-streams
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/mediacapture-streams')
-rw-r--r--testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-restrictTo.https.html245
1 files changed, 245 insertions, 0 deletions
diff --git a/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-restrictTo.https.html b/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-restrictTo.https.html
new file mode 100644
index 0000000000..4b0da740bd
--- /dev/null
+++ b/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-restrictTo.https.html
@@ -0,0 +1,245 @@
+<!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>