summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/test_enumerateDevices.html
blob: 48bec0006a488b56e2ca784ff7e1ca5305e3ca7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<!DOCTYPE HTML>
<html>
<head>
  <script src="mediaStreamPlayback.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({ title: "Run enumerateDevices code", bug: "1046245" });
/**
  Tests covering enumerateDevices API and deviceId constraint. Exercise code.
*/

async function mustSucceedWithStream(msg, f) {
  try {
    const stream = await f();
    for (const track of stream.getTracks()) {
      track.stop();
    }
    ok(true, msg + " must succeed");
  } catch (e) {
    is(e.name, null, msg + " must succeed: " + e.message);
  }
}

async function mustFailWith(msg, reason, constraint, f) {
  try {
    await f();
    ok(false, msg + " must fail");
  } catch(e) {
    is(e.name, reason, msg + " must fail: " + e.message);
    if (constraint) {
      is(e.constraint, constraint, msg + " must fail w/correct constraint.");
    }
  }
}

const gUM = c => navigator.mediaDevices.getUserMedia(c);

const kinds = ["videoinput", "audioinput", "audiooutput"];

function validateDevice({kind, label, deviceId, groupId}) {
  ok(kinds.includes(kind), "Known device kind");
  is(deviceId.length, 44, "deviceId length id as expected for Firefox");
  ok(label.length !== undefined, "Device label: " + label);
  isnot(groupId, "", "groupId must be present.");
}

runTest(async () => {
  await pushPrefs(["media.navigator.streams.fake", true]);

  // Validate enumerated devices after gUM.
  for (const track of (await gUM({video: true, audio: true})).getTracks()) {
    track.stop();
  }

  let devices = await navigator.mediaDevices.enumerateDevices();
  ok(devices.length, "At least one device found");
  const jsoned = JSON.parse(JSON.stringify(devices));
  is(jsoned[0].kind, devices[0].kind, "kind survived serializer");
  is(jsoned[0].deviceId, devices[0].deviceId, "deviceId survived serializer");
  for (const device of devices) {
    validateDevice(device);
    if (device.kind == "audiooutput") continue;
    // Test deviceId constraint
    let deviceId = device.deviceId;
    let constraints = (device.kind == "videoinput") ? { video: { deviceId } }
                                                    : { audio: { deviceId } };
    for (const track of (await gUM(constraints)).getTracks()) {
      is(typeof(track.label), "string", "Track label is a string");
      is(track.label, device.label, "Track label is the device label");
      track.stop();
    }
  }

  const unknownId = "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=";

  // Check deviceId failure paths for video.

  await mustSucceedWithStream("unknown plain deviceId on video",
                              () => gUM({ video: { deviceId: unknownId } }));
  await mustSucceedWithStream("unknown plain deviceId on audio",
                              () => gUM({ audio: { deviceId: unknownId } }));
  await mustFailWith("unknown exact deviceId on video",
                     "OverconstrainedError", "deviceId",
                     () => gUM({ video: { deviceId: { exact: unknownId } } }));
  await mustFailWith("unknown exact deviceId on audio",
                     "OverconstrainedError", "deviceId",
                     () => gUM({ audio: { deviceId: { exact: unknownId } } }));

  // Check that deviceIds are stable for same origin and differ across origins.

  const path = "/tests/dom/media/webrtc/tests/mochitests/test_enumerateDevices_iframe.html";
  const origins = ["https://example.com", "https://test1.example.com"];
  info(window.location);

  const haveDevicesMap = new Promise(resolve => {
    const map = new Map();
    window.addEventListener("message", ({origin, data}) => {
      ok(origins.includes(origin), "Got message from expected origin");
      map.set(origin, JSON.parse(data));
      if (map.size < origins.length) return;
      resolve(map);
    });
  });

  await Promise.all(origins.map(origin => {
    const iframe = document.createElement("iframe");
    iframe.src = origin + path;
    iframe.allow = "camera;microphone;speaker-selection";
    info(iframe.src);
    document.documentElement.appendChild(iframe);
    return new Promise(resolve => iframe.onload = resolve);
  }));
  let devicesMap = await haveDevicesMap;
  let [sameOriginDevices, differentOriginDevices] = origins.map(o => devicesMap.get(o));

  is(sameOriginDevices.length, devices.length, "same origin same devices");
  is(differentOriginDevices.length, devices.length, "cross origin same devices");
  [...sameOriginDevices, ...differentOriginDevices].forEach(d => validateDevice(d));

  for (const device of sameOriginDevices) {
    ok(devices.find(d => d.deviceId == device.deviceId),
       "Same origin deviceId for " + device.label + " must match");
  }
  for (const device of differentOriginDevices) {
    ok(!devices.find(d => d.deviceId == device.deviceId),
         "Different origin deviceId for " + device.label + " must be different");
  }

  // Check the special case of no devices found.
  await pushPrefs(["media.navigator.streams.fake", false],
                  ["media.audio_loopback_dev", "none"],
                  ["media.video_loopback_dev", "none"]);
  devices = await navigator.mediaDevices.enumerateDevices();
  is(devices.length, 0, "No devices");
});
</script>
</pre>
</body>
</html>