test_midi_permission_gated.html (6765B)
1 <html> 2 <head> 3 <title>WebMIDI Listener Test</title> 4 <script src="/tests/SimpleTest/SimpleTest.js"></script> 5 <script type="application/javascript" src="MIDITestUtils.js"></script> 6 </head> 7 8 <body onload="runTests()"> 9 <iframe id="subdomain"></iframe> 10 <iframe id="localhost"></iframe> 11 <script class="testbody" type="application/javascript"> 12 SimpleTest.waitForExplicitFinish(); 13 const filePath = "/tests/dom/midi/tests/file_midi_permission_gated.html"; 14 // Generally this runs on example.com but with --enable-xorigin-tests it runs 15 // on example.org. 16 let subdomainURL = "https://test1." + location.host + filePath; 17 $("subdomain").src = subdomainURL; 18 // For some reason the mochitest server returns "Bad request" with localhost, 19 // but permits the loopback address. That's good enough for testing purposes. 20 $("localhost").src = "http://127.0.0.1:8888" + filePath; 21 22 function waitForMessage() { 23 return new Promise((resolve) => { 24 window.addEventListener("message", (e) => resolve(e.data), {once: true}); 25 }); 26 } 27 28 async function runTests() { 29 await SpecialPowers.pushPrefEnv({ 30 set: [ 31 ["dom.webmidi.enabled", true], 32 ["midi.testing", true], 33 ], 34 }); 35 ok( 36 await SpecialPowers.testPermission( 37 "midi-sysex", 38 SpecialPowers.Services.perms.UNKNOWN_ACTION, 39 document 40 ), 41 "midi-sysex value should have UNKNOWN permission" 42 ); 43 ok( 44 await SpecialPowers.testPermission( 45 "midi-sysex", 46 SpecialPowers.Services.perms.UNKNOWN_ACTION, 47 subdomainURL 48 ), 49 "permission should also not be set for subdomain" 50 ); 51 52 let onChangeCalled = 0; 53 let onChangeCalledWithSysex = 0; 54 // We expect the same states with and without sysex support. 55 const expectedChangedStates = ["denied", "granted", "prompt"]; 56 57 const results = []; 58 for (let sysex of [false, true]) { 59 let result = await navigator.permissions.query({ name: "midi", sysex }); 60 is(result?.state, "prompt", "expected 'prompt' permission status"); 61 // Register two unique listeners that should be invoked every time we 62 // change permissions in the rest of this test case: one with sysex 63 // support, and the other one without. 64 if (sysex) { 65 result.onchange = () => { 66 is( 67 result.state, 68 expectedChangedStates[onChangeCalledWithSysex++], 69 "expected change event with sysex support" 70 ); 71 }; 72 results.push(result); 73 } else { 74 result.onchange = () => { 75 is( 76 result.state, 77 expectedChangedStates[onChangeCalled++], 78 "expected change event" 79 ); 80 }; 81 results.push(result); 82 } 83 } 84 85 // Explicitly set the permission as blocked, and expect the 86 // `requestMIDIAccess` call to be automatically rejected (not having any 87 // permission set would trigger the synthetic addon install provided by 88 // AddonManager and SitePermsAddonProvider). 89 await SpecialPowers.addPermission( 90 "midi-sysex", 91 SpecialPowers.Services.perms.DENY_ACTION, 92 document 93 ); 94 await SpecialPowers.addPermission( 95 "midi", 96 SpecialPowers.Services.perms.DENY_ACTION, 97 document 98 ); 99 for (let sysex of [false, true]) { 100 try { 101 await navigator.requestMIDIAccess({ sysex }); 102 ok(false, "MIDI Access Request gate allowed but expected to be denied"); 103 } catch (ex) { 104 ok(true, "MIDI Access Request denied by default"); 105 } 106 107 let result = await navigator.permissions.query({ name: "midi", sysex }); 108 // We expect "denied" because that's what has been set above (with 109 // `SpecialPowers.addPermission()`). In practice, this state should 110 // never be returned since explicit rejection is handled at the add-on 111 // installation level. 112 is(result?.state, "denied", "expected 'denied' permission status"); 113 } 114 115 // Gated permission should prompt for localhost. 116 // 117 // Note: We don't appear to have good test machinery anymore for 118 // navigating prompts from a plain mochitest. If you uncomment the lines 119 // below and run the test interactively, it should pass. Given that this 120 // is a niche feature that's unlikely to break, it doesn't seem worth 121 // investing in complicated test infrastructure to check it in automation. 122 // for (let sysex of [false, true]) { 123 // $("localhost").contentWindow.postMessage(sysex, "*"); 124 // let response = await waitForMessage(); 125 // is(response, "succeeded", "MIDI Access Request allowed for localhost"); 126 // } 127 128 // When an addon is installed, the permission is inserted. Test 129 // that the request succeeds after we insert the permission. 130 await SpecialPowers.addPermission( 131 "midi-sysex", 132 SpecialPowers.Services.perms.ALLOW_ACTION, 133 document 134 ); 135 await SpecialPowers.addPermission( 136 "midi", 137 SpecialPowers.Services.perms.ALLOW_ACTION, 138 document 139 ); 140 // Gated permission should allow access after addon inserted permission. 141 for (let sysex of [false, true]) { 142 try { 143 await navigator.requestMIDIAccess({ sysex }); 144 ok(true, "MIDI Access Request allowed"); 145 } catch (ex) { 146 ok(false, "MIDI Access Request failed"); 147 } 148 149 let result = await navigator.permissions.query({ name: "midi", sysex }); 150 is(result?.state, "granted", "expected 'granted' permission status"); 151 } 152 153 // Gated permission should also apply to subdomains. 154 for (let sysex of [false, true]) { 155 $("subdomain").contentWindow.postMessage(sysex, "*"); 156 let response = await waitForMessage(); 157 is(response, "succeeded", "MIDI Access Request allowed for subdomain"); 158 } 159 160 is( 161 onChangeCalled, 162 expectedChangedStates.length - 1, 163 `expected onchange listener to have been called ${expectedChangedStates.length - 1} times` 164 ); 165 is( 166 onChangeCalledWithSysex, 167 expectedChangedStates.length - 1, 168 `expected onchange listener to have been called ${expectedChangedStates.length - 1} times (sysex)` 169 ); 170 171 // Remove the permission. 172 await SpecialPowers.removePermission("midi-sysex", document); 173 await SpecialPowers.removePermission("midi", document); 174 175 results.forEach(result => result.onchange = null); 176 177 SimpleTest.finish(); 178 } 179 </script> 180 </body> 181 </html>