summaryrefslogtreecommitdiffstats
path: root/dom/midi/tests/test_midi_permission_gated.html
blob: 0e85e99e9cea8ee7c73e52dad28a5c3668727da6 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<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>