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
142
143
144
145
146
147
|
<!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" });
/**
This is a modified copy of test_enumerateDevices.html testing the
enumerateDevices() legacy version and deviceId constraint.
*/
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],
["media.devices.enumerate.legacy.enabled", true]);
// Validate enumerated devices before gUM (legacy).
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;
is(device.label, "", "Device label is empty");
// Test deviceId constraint
let deviceId = device.deviceId;
let constraints = (device.kind == "videoinput") ? { video: { deviceId } }
: { audio: { deviceId } };
let namedDevices;
for (const track of (await gUM(constraints)).getTracks()) {
is(typeof(track.label), "string", "Track label is a string");
isnot(track.label.length, 0, "Track label is not empty");
if (!namedDevices) {
namedDevices = await navigator.mediaDevices.enumerateDevices();
}
const namedDevice = namedDevices.find(d => d.deviceId == device.deviceId);
is(track.label, namedDevice.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_pre_gum.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>
|