Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<html>
<head>
<title>WebMIDI Listener Test</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="MIDITestUtils.js"></script>
</head>
<body onload="runTests()">
<iframe id="subdomain"></iframe>
<iframe id="localhost"></iframe>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
const filePath = "/tests/dom/midi/tests/file_midi_permission_gated.html";
// Generally this runs on example.com but with --enable-xorigin-tests it runs
// on example.org.
let subdomainURL = "https://test1." + location.host + filePath;
$("subdomain").src = subdomainURL;
// For some reason the mochitest server returns "Bad request" with localhost,
// but permits the loopback address. That's good enough for testing purposes.
$("localhost").src = "http://127.0.0.1:8888" + filePath;
function waitForMessage() {
return new Promise((resolve) => {
window.addEventListener("message", (e) => resolve(e.data), {once: true});
});
}
async function runTests() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.webmidi.enabled", true],
["midi.testing", true],
],
});
ok(
await SpecialPowers.testPermission(
"midi-sysex",
SpecialPowers.Services.perms.UNKNOWN_ACTION,
document
),
"midi-sysex value should have UNKNOWN permission"
);
ok(
await SpecialPowers.testPermission(
"midi-sysex",
SpecialPowers.Services.perms.UNKNOWN_ACTION,
subdomainURL
),
"permission should also not be set for subdomain"
);
let onChangeCalled = 0;
let onChangeCalledWithSysex = 0;
// We expect the same states with and without sysex support.
const expectedChangedStates = ["denied", "granted", "prompt"];
const results = [];
for (let sysex of [false, true]) {
let result = await navigator.permissions.query({ name: "midi", sysex });
is(result?.state, "prompt", "expected 'prompt' permission status");
// Register two unique listeners that should be invoked every time we
// change permissions in the rest of this test case: one with sysex
// support, and the other one without.
if (sysex) {
result.onchange = () => {
is(
result.state,
expectedChangedStates[onChangeCalledWithSysex++],
"expected change event with sysex support"
);
};
results.push(result);
} else {
result.onchange = () => {
is(
result.state,
expectedChangedStates[onChangeCalled++],
"expected change event"
);
};
results.push(result);
}
}
// Explicitly set the permission as blocked, and expect the
// `requestMIDIAccess` call to be automatically rejected (not having any
// permission set would trigger the synthetic addon install provided by
// AddonManager and SitePermsAddonProvider).
await SpecialPowers.addPermission(
"midi-sysex",
SpecialPowers.Services.perms.DENY_ACTION,
document
);
await SpecialPowers.addPermission(
"midi",
SpecialPowers.Services.perms.DENY_ACTION,
document
);
for (let sysex of [false, true]) {
try {
await navigator.requestMIDIAccess({ sysex });
ok(false, "MIDI Access Request gate allowed but expected to be denied");
} catch (ex) {
ok(true, "MIDI Access Request denied by default");
}
let result = await navigator.permissions.query({ name: "midi", sysex });
// We expect "denied" because that's what has been set above (with
// `SpecialPowers.addPermission()`). In practice, this state should
// never be returned since explicit rejection is handled at the add-on
// installation level.
is(result?.state, "denied", "expected 'denied' permission status");
}
// Gated permission should prompt for localhost.
//
// Note: We don't appear to have good test machinery anymore for
// navigating prompts from a plain mochitest. If you uncomment the lines
// below and run the test interactively, it should pass. Given that this
// is a niche feature that's unlikely to break, it doesn't seem worth
// investing in complicated test infrastructure to check it in automation.
// for (let sysex of [false, true]) {
// $("localhost").contentWindow.postMessage(sysex, "*");
// let response = await waitForMessage();
// is(response, "succeeded", "MIDI Access Request allowed for localhost");
// }
// When an addon is installed, the permission is inserted. Test
// that the request succeeds after we insert the permission.
await SpecialPowers.addPermission(
"midi-sysex",
SpecialPowers.Services.perms.ALLOW_ACTION,
document
);
await SpecialPowers.addPermission(
"midi",
SpecialPowers.Services.perms.ALLOW_ACTION,
document
);
// Gated permission should allow access after addon inserted permission.
for (let sysex of [false, true]) {
try {
await navigator.requestMIDIAccess({ sysex });
ok(true, "MIDI Access Request allowed");
} catch (ex) {
ok(false, "MIDI Access Request failed");
}
let result = await navigator.permissions.query({ name: "midi", sysex });
is(result?.state, "granted", "expected 'granted' permission status");
}
// Gated permission should also apply to subdomains.
for (let sysex of [false, true]) {
$("subdomain").contentWindow.postMessage(sysex, "*");
let response = await waitForMessage();
is(response, "succeeded", "MIDI Access Request allowed for subdomain");
}
is(
onChangeCalled,
expectedChangedStates.length - 1,
`expected onchange listener to have been called ${expectedChangedStates.length - 1} times`
);
is(
onChangeCalledWithSysex,
expectedChangedStates.length - 1,
`expected onchange listener to have been called ${expectedChangedStates.length - 1} times (sysex)`
);
// Remove the permission.
await SpecialPowers.removePermission("midi-sysex", document);
await SpecialPowers.removePermission("midi", document);
results.forEach(result => result.onchange = null);
SimpleTest.finish();
}
</script>
</body>
</html>