tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

getdisplaymedia.https.html (12352B)


      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");
     15 
     16 const stopTracks = stream => stream.getTracks().forEach(track => track.stop());
     17 const j = obj => JSON.stringify(obj);
     18 
     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);
     24 }
     25 
     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`);
     37 
     38 [
     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`));
     52 
     53 [
     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 ].forEach(constraints => promise_test(async t => {
     63  await test_driver.bless('getDisplayMedia()');
     64  const p = navigator.mediaDevices.getDisplayMedia(constraints);
     65  t.add_cleanup(async () => {
     66    try { stopTracks(await p) } catch {}
     67  });
     68  await promise_rejects_js(
     69    t, TypeError, Promise.race([p, Promise.resolve()]),
     70    'getDisplayMedia should have returned an already-rejected promise.');
     71 }, `getDisplayMedia(${j(constraints)}) must fail with TypeError`));
     72 
     73 [
     74 {video: true, audio: true},
     75 {audio: true},
     76 ].forEach(constraints => promise_test(async t => {
     77  const stream = await getDisplayMedia(constraints);
     78  t.add_cleanup(() => stopTracks(stream));
     79  assert_greater_than_equal(stream.getTracks().length, 1);
     80  assert_less_than_equal(stream.getTracks().length, 2);
     81  assert_equals(stream.getVideoTracks().length, 1);
     82  assert_less_than_equal(stream.getAudioTracks().length, 1);
     83 }, `getDisplayMedia(${j(constraints)}) must succeed with video maybe audio`));
     84 
     85 [
     86 {width: {max: 360}},
     87 {height: {max: 240}},
     88 {width: {max: 360}, height: {max: 240}},
     89 {frameRate: {max: 4}},
     90 {frameRate: {max: 4}, width: {max: 360}},
     91 {frameRate: {max: 4}, height: {max: 240}},
     92 {frameRate: {max: 4}, width: {max: 360}, height: {max: 240}},
     93 ].forEach(constraints => promise_test(async t => {
     94  const stream = await getDisplayMedia({video: constraints});
     95  t.add_cleanup(() => stopTracks(stream));
     96  const {width, height, frameRate} = stream.getTracks()[0].getSettings();
     97  assert_greater_than_equal(width, 1);
     98  assert_greater_than_equal(height, 1);
     99  assert_greater_than_equal(frameRate, 1);
    100  if (constraints.width) {
    101    assert_less_than_equal(width, constraints.width.max);
    102  }
    103  if (constraints.height) {
    104    assert_less_than_equal(height, constraints.height.max);
    105  }
    106  if (constraints.frameRate) {
    107    assert_less_than_equal(frameRate, constraints.frameRate.max);
    108  }
    109 }, `getDisplayMedia({video: ${j(constraints)}}) must be constrained`));
    110 
    111 const someSizes = [
    112 {width: 160},
    113 {height: 120},
    114 {width: 80},
    115 {height: 60},
    116 {width: 158},
    117 {height: 118},
    118 ];
    119 
    120 someSizes.forEach(constraints => promise_test(async t => {
    121  const stream = await getDisplayMedia({video: constraints});
    122  t.add_cleanup(() => stopTracks(stream));
    123  const {width, height, frameRate} = stream.getTracks()[0].getSettings();
    124  if (constraints.width) {
    125    assert_equals(width, constraints.width);
    126  } else {
    127    assert_equals(height, constraints.height);
    128  }
    129  assert_greater_than_equal(frameRate, 1);
    130 }, `getDisplayMedia({video: ${j(constraints)}}) must be downscaled precisely`));
    131 
    132 promise_test(async t => {
    133  const video = {height: 240};
    134  const stream = await getDisplayMedia({video});
    135  t.add_cleanup(() => stopTracks(stream));
    136  const [track] = stream.getVideoTracks();
    137  const {height} = track.getSettings();
    138  assert_equals(height, video.height);
    139  for (const constraints of someSizes) {
    140    await track.applyConstraints(constraints);
    141    const {width, height} = track.getSettings();
    142    if (constraints.width) {
    143      assert_equals(width, constraints.width);
    144    } else {
    145      assert_equals(height, constraints.height);
    146    }
    147  }
    148 }, `applyConstraints(width or height) must downscale precisely`);
    149 
    150 [
    151 {video: {width: {max: 0}}},
    152 {video: {height: {max: 0}}},
    153 {video: {frameRate: {max: 0}}},
    154 {video: {width: {max: -1}}},
    155 {video: {height: {max: -1}}},
    156 {video: {frameRate: {max: -1}}},
    157 ].forEach(constraints => promise_test(async t => {
    158  try {
    159    stopTracks(await getDisplayMedia(constraints));
    160  } catch (err) {
    161    assert_equals(err.name, 'OverconstrainedError', err.message);
    162    return;
    163  }
    164  assert_unreached('getDisplayMedia should have failed');
    165 }, `getDisplayMedia(${j(constraints)}) must fail with OverconstrainedError`));
    166 
    167 [
    168 {width: {max: 0}},
    169 {height: {max: 0}},
    170 {frameRate: {max: 0}},
    171 {width: {max: -1}},
    172 {height: {max: -1}},
    173 {frameRate: {max: -1}},
    174 {width: {min: 100, max: 10}},
    175 {height: {min: 100, max: 10}},
    176 {frameRate: {min: 100, max: 10}},
    177 ].forEach(constraints => promise_test(async t => {
    178  const stream = await getDisplayMedia();
    179  t.add_cleanup(() => stopTracks(stream));
    180  const [track] = stream.getTracks();
    181  try {
    182    await track.applyConstraints(constraints);
    183  } catch (err) {
    184    assert_equals(err.name, 'OverconstrainedError', err.message);
    185    assert_equals(err.constraint, Object.keys(constraints)[0], "Constraint is set");
    186    return;
    187  }
    188  assert_unreached('applyConstraints hould have failed');
    189 }, `applyConstraints(${j(constraints)}) for display media must fail with OverconstrainedError`));
    190 
    191 // Content shell picks a fake desktop device by default.
    192 promise_test(async t => {
    193  const stream = await getDisplayMedia({video: true});
    194  t.add_cleanup(() => stopTracks(stream));
    195  assert_equals(stream.getVideoTracks().length, 1);
    196  const track = stream.getVideoTracks()[0];
    197  assert_equals(track.kind, "video");
    198  assert_equals(track.enabled, true);
    199  assert_equals(track.readyState, "live");
    200  track.stop();
    201  assert_equals(track.readyState, "ended");
    202 }, 'getDisplayMedia() resolves with stream with video track');
    203 
    204 {
    205  const displaySurfaces = ['monitor', 'window', 'browser'];
    206  displaySurfaces.forEach((displaySurface) => {
    207    promise_test(async t => {
    208      const stream = await getDisplayMedia({video: {displaySurface}});
    209      t.add_cleanup(() => stopTracks(stream));
    210      const settings = stream.getVideoTracks()[0].getSettings();
    211      assert_equals(settings.displaySurface, displaySurface);
    212      assert_any(assert_equals, settings.logicalSurface, [true, false]);
    213      assert_any(assert_equals, settings.cursor, ['never', 'always', 'motion']);
    214      assert_false("suppressLocalAudioPlayback" in settings);
    215    }, `getDisplayMedia({"video":{"displaySurface":"${displaySurface}"}}) with getSettings`);
    216  })
    217 }
    218 
    219 {
    220  const properties = ["displaySurface"];
    221  properties.forEach((property) => {
    222    test(() => {
    223      const supportedConstraints =
    224        navigator.mediaDevices.getSupportedConstraints();
    225      assert_true(supportedConstraints[property]);
    226    }, property + " is supported");
    227  });
    228 }
    229 
    230 [
    231 {video: {displaySurface: "monitor"}},
    232 {video: {displaySurface: "window"}},
    233 {video: {displaySurface: "browser"}},
    234 {selfBrowserSurface: "include"},
    235 {selfBrowserSurface: "exclude"},
    236 {surfaceSwitching: "include"},
    237 {surfaceSwitching: "exclude"},
    238 {systemAudio: "include"},
    239 {systemAudio: "exclude"},
    240 {windowAudio: "exclude"},
    241 {windowAudio: "window"},
    242 {windowAudio: "system"},
    243 ].forEach(constraints => promise_test(async t => {
    244  const stream = await getDisplayMedia(constraints);
    245  t.add_cleanup(() => stopTracks(stream));
    246 }, `getDisplayMedia(${j(constraints)}) must succeed`));
    247 
    248 [
    249 {selfBrowserSurface: "invalid"},
    250 {surfaceSwitching: "invalid"},
    251 {systemAudio: "invalid"},
    252 {windowAudio: "invalid"},
    253 {monitorTypeSurfaces: "invalid"},
    254 ].forEach(constraints => promise_test(async t => {
    255  await test_driver.bless('getDisplayMedia()');
    256  const p = navigator.mediaDevices.getDisplayMedia(constraints);
    257  t.add_cleanup(async () => {
    258    try { stopTracks(await p) } catch {}
    259  });
    260  await promise_rejects_js(
    261    t, TypeError, Promise.race([p, Promise.resolve()]),
    262    'getDisplayMedia should have returned an already-rejected promise.');
    263 }, `getDisplayMedia(${j(constraints)}) must fail with TypeError`));
    264 
    265 test(() => {
    266  const supportedConstraints =
    267    navigator.mediaDevices.getSupportedConstraints();
    268  assert_true(supportedConstraints.suppressLocalAudioPlayback);
    269 }, "suppressLocalAudioPlayback is supported");
    270 
    271 {
    272  const suppressLocalAudioPlaybacks = [true, false];
    273  suppressLocalAudioPlaybacks.forEach((suppressLocalAudioPlayback) => {
    274    promise_test(async (t) => {
    275      const stream = await getDisplayMedia({
    276        audio: { suppressLocalAudioPlayback },
    277      });
    278      t.add_cleanup(() => stopTracks(stream));
    279      const [videoTrack] = stream.getVideoTracks();
    280      assert_false("suppressLocalAudioPlayback" in videoTrack.getSettings());
    281      const [audioTrack] = stream.getAudioTracks();
    282      const audioTrackSettings = audioTrack.getSettings();
    283      assert_true("suppressLocalAudioPlayback" in audioTrackSettings);
    284      assert_equals(
    285        audioTrackSettings.suppressLocalAudioPlayback,
    286        suppressLocalAudioPlayback
    287      );
    288      await audioTrack.applyConstraints();
    289      assert_true("suppressLocalAudioPlayback" in audioTrackSettings);
    290      assert_equals(
    291        audioTrackSettings.suppressLocalAudioPlayback,
    292        suppressLocalAudioPlayback
    293      );
    294    }, `getDisplayMedia({"audio":{"suppressLocalAudioPlayback":${suppressLocalAudioPlayback}}}) with getSettings`);
    295  });
    296 }
    297 
    298 promise_test(async t => {
    299  const stream = await getDisplayMedia({video: true});
    300  t.add_cleanup(() => stopTracks(stream));
    301  const capabilities = stream.getVideoTracks()[0].getCapabilities();
    302  assert_any(
    303      assert_equals, capabilities.displaySurface,
    304      ['monitor', 'window', 'browser']);
    305 }, 'getDisplayMedia() with getCapabilities');
    306 
    307 promise_test(async (t) => {
    308  const constraints = {
    309    video: { displaySurface: "monitor" },
    310    monitorTypeSurfaces: "exclude",
    311  };
    312  await test_driver.bless('getDisplayMedia()');
    313  const p = navigator.mediaDevices.getDisplayMedia(constraints);
    314  t.add_cleanup(async () => {
    315    try { stopTracks(await p) } catch {}
    316  });
    317  await promise_rejects_js(
    318    t, TypeError, Promise.race([p, Promise.resolve()]),
    319    'getDisplayMedia should have returned an already-rejected promise.');
    320  }, `getDisplayMedia({"video":{"displaySurface":"monitor"},"monitorTypeSurfaces":"exclude"}) rejects with TypeError`);
    321 
    322 promise_test(async (t) => {
    323  const stream = await getDisplayMedia({
    324    video: { displaySurface: "monitor" },
    325    monitorTypeSurfaces: "include",
    326  });
    327  t.add_cleanup(() => stopTracks(stream));
    328  const { displaySurface } = stream.getTracks()[0].getSettings();
    329  assert_equals(displaySurface, "monitor");
    330 }, `getDisplayMedia({"video":{"displaySurface":"monitor"},"monitorTypeSurfaces":"include"}) resolves with a monitor track`);
    331 
    332 promise_test(async (t) => {
    333  const stream = await getDisplayMedia({
    334    monitorTypeSurfaces: "exclude",
    335  });
    336  t.add_cleanup(() => stopTracks(stream));
    337  const { displaySurface } = stream.getTracks()[0].getSettings();
    338  assert_any(assert_equals, displaySurface, ["window", "browser"]);
    339 }, `getDisplayMedia({"monitorTypeSurfaces":"exclude"}) resolves with a non monitor track`);
    340 
    341 </script>