Bug 1789795 [wpt PR 35828] - Add displaySurface to MediaTrackCapabilities, a=testonly
[gecko.git] / testing / web-platform / tests / screen-capture / getdisplaymedia.https.html
blob095b98dea4c5e5156731e97bcd9b1aba075e0ae7
1 <!doctype html>
2 <meta charset=utf-8>
3 <title>getDisplayMedia</title>
4 <meta name="timeout" content="long">
5 <button id="button">User gesture</button>
6 <script src="/resources/testharness.js"></script>
7 <script src="/resources/testharnessreport.js"></script>
8 <script src="/resources/testdriver.js"></script>
9 <script src="/resources/testdriver-vendor.js"></script>
10 <script>
11 'use strict';
12 test(() => {
13 assert_idl_attribute(navigator.mediaDevices, 'getDisplayMedia');
14 }, "getDisplayMedia in navigator.mediaDevices");
16 const stopTracks = stream => stream.getTracks().forEach(track => track.stop());
17 const j = obj => JSON.stringify(obj);
19 async function getDisplayMedia(constraints) {
20 const p = new Promise(r => button.onclick = r);
21 await test_driver.click(button);
22 await p;
23 return navigator.mediaDevices.getDisplayMedia(constraints);
26 promise_test(t => {
27 const p = navigator.mediaDevices.getDisplayMedia({video: true});
28 t.add_cleanup(async () => {
29 try { stopTracks(await p) } catch {}
30 });
31 // Race a settled promise to check that the returned promise is already
32 // rejected.
33 return promise_rejects_dom(
34 t, 'InvalidStateError', Promise.race([p, Promise.resolve()]),
35 'getDisplayMedia should have returned an already-rejected promise.');
36 }, `getDisplayMedia() must require user activation`);
39 {video: true},
40 {video: true, audio: false},
41 {video: {}},
42 {audio: false},
43 {},
44 undefined
45 ].forEach(constraints => promise_test(async t => {
46 const stream = await getDisplayMedia(constraints);
47 t.add_cleanup(() => stopTracks(stream));
48 assert_equals(stream.getTracks().length, 1);
49 assert_equals(stream.getVideoTracks().length, 1);
50 assert_equals(stream.getAudioTracks().length, 0);
51 }, `getDisplayMedia(${j(constraints)}) must succeed with video`));
54 {video: false},
55 {video: {advanced: [{width: 320}]}},
56 {video: {width: {min: 320}}},
57 {video: {width: {exact: 320}}},
58 {video: {height: {min: 240}}},
59 {video: {height: {exact: 240}}},
60 {video: {frameRate: {min: 4}}},
61 {video: {frameRate: {exact: 4}}},
62 {video: false, audio: true},
63 ].forEach(constraints => promise_test(async t => {
64 await test_driver.bless('getDisplayMedia()');
65 const p = navigator.mediaDevices.getDisplayMedia(constraints);
66 t.add_cleanup(async () => {
67 try { stopTracks(await p) } catch {}
68 });
69 await promise_rejects_js(
70 t, TypeError, Promise.race([p, Promise.resolve()]),
71 'getDisplayMedia should have returned an already-rejected promise.');
72 }, `getDisplayMedia(${j(constraints)}) must fail with TypeError`));
75 {video: true, audio: true},
76 {audio: true},
77 ].forEach(constraints => promise_test(async t => {
78 const stream = await getDisplayMedia(constraints);
79 t.add_cleanup(() => stopTracks(stream));
80 assert_greater_than_equal(stream.getTracks().length, 1);
81 assert_less_than_equal(stream.getTracks().length, 2);
82 assert_equals(stream.getVideoTracks().length, 1);
83 assert_less_than_equal(stream.getAudioTracks().length, 1);
84 }, `getDisplayMedia(${j(constraints)}) must succeed with video maybe audio`));
87 {width: {max: 360}},
88 {height: {max: 240}},
89 {width: {max: 360}, height: {max: 240}},
90 {frameRate: {max: 4}},
91 {frameRate: {max: 4}, width: {max: 360}},
92 {frameRate: {max: 4}, height: {max: 240}},
93 {frameRate: {max: 4}, width: {max: 360}, height: {max: 240}},
94 ].forEach(constraints => promise_test(async t => {
95 const stream = await getDisplayMedia({video: constraints});
96 t.add_cleanup(() => stopTracks(stream));
97 const {width, height, frameRate} = stream.getTracks()[0].getSettings();
98 assert_greater_than_equal(width, 1);
99 assert_greater_than_equal(height, 1);
100 assert_greater_than_equal(frameRate, 1);
101 if (constraints.width) {
102 assert_less_than_equal(width, constraints.width.max);
104 if (constraints.height) {
105 assert_less_than_equal(height, constraints.height.max);
107 if (constraints.frameRate) {
108 assert_less_than_equal(frameRate, constraints.frameRate.max);
110 }, `getDisplayMedia({video: ${j(constraints)}}) must be constrained`));
112 const someSizes = [
113 {width: 160},
114 {height: 120},
115 {width: 80},
116 {height: 60},
117 {width: 158},
118 {height: 118},
121 someSizes.forEach(constraints => promise_test(async t => {
122 const stream = await getDisplayMedia({video: constraints});
123 t.add_cleanup(() => stopTracks(stream));
124 const {width, height, frameRate} = stream.getTracks()[0].getSettings();
125 if (constraints.width) {
126 assert_equals(width, constraints.width);
127 } else {
128 assert_equals(height, constraints.height);
130 assert_greater_than_equal(frameRate, 1);
131 }, `getDisplayMedia({video: ${j(constraints)}}) must be downscaled precisely`));
133 promise_test(async t => {
134 const video = {height: 240};
135 const stream = await getDisplayMedia({video});
136 t.add_cleanup(() => stopTracks(stream));
137 const [track] = stream.getVideoTracks();
138 const {height} = track.getSettings();
139 assert_equals(height, video.height);
140 for (const constraints of someSizes) {
141 await track.applyConstraints(constraints);
142 const {width, height} = track.getSettings();
143 if (constraints.width) {
144 assert_equals(width, constraints.width);
145 } else {
146 assert_equals(height, constraints.height);
149 }, `applyConstraints(width or height) must downscale precisely`);
152 {video: {width: {max: 0}}},
153 {video: {height: {max: 0}}},
154 {video: {frameRate: {max: 0}}},
155 {video: {width: {max: -1}}},
156 {video: {height: {max: -1}}},
157 {video: {frameRate: {max: -1}}},
158 ].forEach(constraints => promise_test(async t => {
159 try {
160 stopTracks(await getDisplayMedia(constraints));
161 } catch (err) {
162 assert_equals(err.name, 'OverconstrainedError', err.message);
163 return;
165 assert_unreached('getDisplayMedia should have failed');
166 }, `getDisplayMedia(${j(constraints)}) must fail with OverconstrainedError`));
168 // Content shell picks a fake desktop device by default.
169 promise_test(async t => {
170 const stream = await getDisplayMedia({video: true});
171 t.add_cleanup(() => stopTracks(stream));
172 assert_equals(stream.getVideoTracks().length, 1);
173 const track = stream.getVideoTracks()[0];
174 assert_equals(track.kind, "video");
175 assert_equals(track.enabled, true);
176 assert_equals(track.readyState, "live");
177 track.stop();
178 assert_equals(track.readyState, "ended");
179 }, 'getDisplayMedia() resolves with stream with video track');
182 const displaySurfaces = ['monitor', 'window', 'browser'];
183 displaySurfaces.forEach((displaySurface) => {
184 promise_test(async t => {
185 const stream = await getDisplayMedia({video: {displaySurface}});
186 t.add_cleanup(() => stopTracks(stream));
187 const settings = stream.getVideoTracks()[0].getSettings();
188 assert_equals(settings.displaySurface, displaySurface);
189 assert_any(assert_equals, settings.logicalSurface, [true, false]);
190 assert_any(assert_equals, settings.cursor, ['never', 'always', 'motion']);
191 assert_false("suppressLocalAudioPlayback" in settings);
192 }, `getDisplayMedia({"video":{"displaySurface":"${displaySurface}"}}) with getSettings`);
197 const properties = ["displaySurface"];
198 properties.forEach((property) => {
199 test(() => {
200 const supportedConstraints =
201 navigator.mediaDevices.getSupportedConstraints();
202 assert_true(supportedConstraints[property]);
203 }, property + " is supported");
208 {video: {displaySurface: "monitor"}},
209 {video: {displaySurface: "window"}},
210 {video: {displaySurface: "browser"}},
211 {selfBrowserSurface: "include"},
212 {selfBrowserSurface: "exclude"},
213 {surfaceSwitching: "include"},
214 {surfaceSwitching: "exclude"},
215 {systemAudio: "include"},
216 {systemAudio: "exclude"},
217 ].forEach(constraints => promise_test(async t => {
218 const stream = await getDisplayMedia(constraints);
219 t.add_cleanup(() => stopTracks(stream));
220 }, `getDisplayMedia(${j(constraints)}) must succeed`));
223 {selfBrowserSurface: "invalid"},
224 {surfaceSwitching: "invalid"},
225 {systemAudio: "invalid"},
226 ].forEach(constraints => promise_test(async t => {
227 await test_driver.bless('getDisplayMedia()');
228 const p = navigator.mediaDevices.getDisplayMedia(constraints);
229 t.add_cleanup(async () => {
230 try { stopTracks(await p) } catch {}
232 await promise_rejects_js(
233 t, TypeError, Promise.race([p, Promise.resolve()]),
234 'getDisplayMedia should have returned an already-rejected promise.');
235 }, `getDisplayMedia(${j(constraints)}) must fail with TypeError`));
237 test(() => {
238 const supportedConstraints =
239 navigator.mediaDevices.getSupportedConstraints();
240 assert_true(supportedConstraints.suppressLocalAudioPlayback);
241 }, "suppressLocalAudioPlayback is supported");
244 const suppressLocalAudioPlaybacks = [true, false];
245 suppressLocalAudioPlaybacks.forEach((suppressLocalAudioPlayback) => {
246 promise_test(async (t) => {
247 const stream = await getDisplayMedia({
248 audio: { suppressLocalAudioPlayback },
250 t.add_cleanup(() => stopTracks(stream));
251 const [videoTrack] = stream.getVideoTracks();
252 assert_false("suppressLocalAudioPlayback" in videoTrack.getSettings());
253 const [audioTrack] = stream.getAudioTracks();
254 const audioTrackSettings = audioTrack.getSettings();
255 assert_true("suppressLocalAudioPlayback" in audioTrackSettings);
256 assert_equals(
257 audioTrackSettings.suppressLocalAudioPlayback,
258 suppressLocalAudioPlayback
260 await audioTrack.applyConstraints();
261 assert_true("suppressLocalAudioPlayback" in audioTrackSettings);
262 assert_equals(
263 audioTrackSettings.suppressLocalAudioPlayback,
264 suppressLocalAudioPlayback
266 }, `getDisplayMedia({"audio":{"suppressLocalAudioPlayback":${suppressLocalAudioPlayback}}}) with getSettings`);
270 promise_test(async t => {
271 const stream = await getDisplayMedia({video: true});
272 t.add_cleanup(() => stopTracks(stream));
273 const capabilities = stream.getVideoTracks()[0].getCapabilities();
274 assert_any(
275 assert_equals, capabilities.displaySurface,
276 ['monitor', 'window', 'browser']);
277 }, 'getDisplayMedia() with getCapabilities');
279 </script>