2 Any copyright is dedicated to the Public Domain.
3 http://creativecommons.org/publicdomain/zero/1.0/
10 <title>Test for Permissions API
</title>
11 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
12 <link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css">
17 <script type=
"application/javascript">
18 /*globals SpecialPowers, SimpleTest, is, ok, */
26 } = SpecialPowers.Ci.nsIPermissionManager;
28 SimpleTest.waitForExplicitFinish();
30 const OTHER_PERMISSIONS = [{
34 name: 'notifications',
35 type: 'desktop-notification'
38 type: 'desktop-notification'
40 name: 'persistent-storage',
41 type: 'persistent-storage'
47 const MEDIA_PERMISSIONS = [{
55 const PERMISSIONS = [...OTHER_PERMISSIONS, ...MEDIA_PERMISSIONS];
57 const UNSUPPORTED_PERMISSIONS = [
58 { name: 'foobarbaz' }, // Not in spec, for testing only.
61 // Create a closure, so that tests are run on the correct window object.
62 function createPermissionTester(iframe) {
63 const iframeWindow = iframe.contentWindow;
65 async setPermissions(allow, context = iframeWindow.document) {
66 const permissions = PERMISSIONS.map(({ type }) =
> {
73 await SpecialPowers.popPermissions();
74 return SpecialPowers.pushPermissions(permissions);
76 checkPermissions(permissions, expectedState, mediaExpectedState = expectedState) {
77 const promisesToQuery = permissions.map(({ name: expectedName }) =
> {
78 return iframeWindow.navigator.permissions
79 .query({ name: expectedName })
81 ({ state, name }) =
> {
82 is(name, expectedName, `correct name for '${expectedName}'`);
83 if (['camera', 'microphone'].includes(expectedName)) {
84 is(state, mediaExpectedState, `correct state for '${expectedName}'`);
86 is(state, expectedState, `correct state for '${expectedName}'`);
89 () =
> ok(false, `query should not have rejected for '${name}'`)
92 return Promise.all(promisesToQuery);
94 checkUnsupportedPermissions(permissions) {
95 const promisesToQuery = permissions.map(({ name }) =
> {
96 return iframeWindow.navigator.permissions
99 () =
> ok(false, `query should not have resolved for '${name}'`),
101 is(error.name, 'TypeError',
102 `query should have thrown TypeError for '${name}'`);
106 return Promise.all(promisesToQuery);
108 promiseStateChanged(name, state) {
109 return iframeWindow.navigator.permissions
112 return new Promise( resolve =
> {
113 status.onchange = () =
> {
114 status.onchange = null;
115 is(status.state, state, `state changed for '${name}'`);
120 () =
> ok(false, `query should not have rejected for '${name}'`));
122 testStatusOnChange() {
123 return new Promise((resolve) =
> {
124 SpecialPowers.popPermissions(() =
> {
125 const permission = 'geolocation';
126 const promiseGranted = this.promiseStateChanged(permission, 'granted');
127 this.setPermissions(ALLOW_ACTION);
128 promiseGranted.then(async () =
> {
129 const promisePrompt = this.promiseStateChanged(permission, 'prompt');
130 await SpecialPowers.popPermissions();
131 return promisePrompt;
137 return iframeWindow.navigator.permissions
138 .query({ name: 'invalid' })
140 () =
> ok(false, 'invalid query should not have resolved'),
141 () =
> ok(true, 'invalid query should have rejected')
144 async testNotFullyActiveDoc() {
145 const iframe1 = await createIframe();
146 const expectedErrorClass = iframe1.contentWindow.DOMException;
147 const permAPI = iframe1.contentWindow.navigator.permissions;
148 // Document no longer fully active
150 await new Promise((res) =
> {
151 permAPI.query({ name:
"geolocation" }).catch((error) =
> {
153 error instanceof expectedErrorClass,
154 "DOMException from other realm"
159 "Must reject with a InvalidStateError"
166 async testNotFullyActiveChange() {
167 await SpecialPowers.popPermissions();
168 const iframe2 = await createIframe();
169 const initialStatus = await iframe2.contentWindow.navigator.permissions.query(
170 { name:
"geolocation" }
172 await SpecialPowers.pushPermissions([
175 allow: PROMPT_ACTION,
176 context: iframe2.contentWindow.document,
182 "Initially the iframe's permission is prompt"
185 // Document no longer fully active
186 const stolenDoc = iframe2.contentWindow.document;
188 initialStatus.onchange = () =
> {
189 ok(false,
"onchange must not fire when document is not fully active.");
191 // We set it to grant for this origin, but the PermissionStatus doesn't change.
192 await SpecialPowers.pushPermissions([
202 "Inactive document's permission must not change"
205 // Re-attach the iframe
206 document.body.appendChild(iframe2);
207 await new Promise((res) =
> (iframe2.onload = res));
208 // Fully active again
209 const newStatus = await iframe2.contentWindow.navigator.permissions.query({
212 is(newStatus.state,
"granted",
"Reflect that we are granted");
214 const newEventPromise = new Promise((res) =
> (newStatus.onchange = res));
215 await SpecialPowers.pushPermissions([
219 context: iframe2.contentWindow.document,
223 await newEventPromise;
224 is(initialStatus.state,
"prompt",
"Remains prompt, as it's actually dead.");
225 is(newStatus.state,
"denied",
"New status must be 'denied'.");
231 function createIframe() {
232 return new Promise((resolve) =
> {
233 const iframe = document.createElement('iframe');
234 iframe.src = 'file_empty.html';
235 iframe.onload = () =
> resolve(iframe);
236 document.body.appendChild(iframe);
240 window.onload = async () =
> {
242 const tester = createPermissionTester(await createIframe());
243 await tester.checkUnsupportedPermissions(UNSUPPORTED_PERMISSIONS);
244 await tester.setPermissions(UNKNOWN_ACTION);
245 await tester.checkPermissions(PERMISSIONS, 'prompt');
246 await tester.setPermissions(PROMPT_ACTION);
247 await tester.checkPermissions(PERMISSIONS, 'prompt', 'granted');
248 await tester.setPermissions(ALLOW_ACTION);
249 await tester.checkPermissions(PERMISSIONS, 'granted');
250 await tester.setPermissions(DENY_ACTION);
251 await tester.checkPermissions(PERMISSIONS, 'denied');
252 await tester.testStatusOnChange();
253 await tester.testInvalidQuery();
254 await tester.testNotFullyActiveDoc();
255 await tester.testNotFullyActiveChange();
257 await SpecialPowers.pushPrefEnv({
259 [
"privacy.resistFingerprinting", true]
262 await tester.setPermissions(PROMPT_ACTION);
263 await tester.checkPermissions(PERMISSIONS, 'prompt', 'prompt');
264 await SpecialPowers.popPrefEnv();
266 await SpecialPowers.pushPrefEnv({
268 [
"permissions.media.query.enabled", false]
271 await tester.setPermissions(UNKNOWN_ACTION);
272 await tester.checkUnsupportedPermissions(MEDIA_PERMISSIONS);
273 await tester.checkPermissions(OTHER_PERMISSIONS, 'prompt');