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>
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
);
23 return navigator
.mediaDevices
.getDisplayMedia(constraints
);
27 const p
= navigator
.mediaDevices
.getDisplayMedia({video
: true});
28 t
.add_cleanup(async () => {
29 try { stopTracks(await p
) } catch {}
31 // Race a settled promise to check that the returned promise is already
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`);
40 {video
: true, audio
: false},
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`));
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 {}
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},
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`));
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`));
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
);
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
);
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
=> {
160 stopTracks(await
getDisplayMedia(constraints
));
162 assert_equals(err
.name
, 'OverconstrainedError', err
.message
);
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");
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
) => {
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`));
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
);
257 audioTrackSettings
.suppressLocalAudioPlayback
,
258 suppressLocalAudioPlayback
260 await audioTrack
.applyConstraints();
261 assert_true("suppressLocalAudioPlayback" in audioTrackSettings
);
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();
275 assert_equals
, capabilities
.displaySurface
,
276 ['monitor', 'window', 'browser']);
277 }, 'getDisplayMedia() with getCapabilities');