<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!
DOCTYPE HTML>
<
html>
<
head>
<
meta charset=
"utf-8">
<
title>Test for Permissions API</
title>
<
script src=
"/tests/SimpleTest/SimpleTest.js"></
script>
<
link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css">
</
head>
<
body>
<
pre id=
"test"></
pre>
<
script type=
"application/javascript">
/*globals SpecialPowers, SimpleTest, is, ok, */
'use strict';
const {
UNKNOWN_ACTION,
PROMPT_ACTION,
ALLOW_ACTION,
DENY_ACTION
} = SpecialPowers.Ci.nsIPermissionManager;
SimpleTest.waitForExplicitFinish();
const OTHER_PERMISSIONS = [{
name:
'geolocation',
type:
'geo'
}, {
name:
'notifications',
type:
'desktop-notification'
}, {
name:
'push',
type:
'desktop-notification'
}, {
name:
'persistent-storage',
type:
'persistent-storage'
}, {
name:
'midi',
type:
'midi'
}, ];
const MEDIA_PERMISSIONS = [{
name:
'camera',
type:
'camera'
}, {
name:
'microphone',
type:
'microphone'
}, ];
const PERMISSIONS = [...OTHER_PERMISSIONS, ...MEDIA_PERMISSIONS];
const UNSUPPORTED_PERMISSIONS = [
{ name:
'foobarbaz' }, // Not in spec, for testing only.
];
// Create a closure, so that tests are run on the correct window
object.
function createPermissionTester(
iframe) {
const iframeWindow =
iframe.contentWindow;
return {
async setPermissions(allow, context = iframeWindow.document) {
const permissions = PERMISSIONS.
map(({ type }) => {
return {
type,
allow,
context,
};
});
await SpecialPowers.popPermissions();
return SpecialPowers.pushPermissions(permissions);
},
checkPermissions(permissions, expectedState, mediaExpectedState = expectedState) {
const promisesToQuery = permissions.
map(({ name: expectedName }) => {
return iframeWindow.navigator.permissions
.query({ name: expectedName })
.then(
({ state, name }) => {
is(name, expectedName, `correct name for
'${expectedName}'`);
if ([
'camera',
'microphone'].includes(expectedName)) {
is(state, mediaExpectedState, `correct state for
'${expectedName}'`);
} else {
is(state, expectedState, `correct state for
'${expectedName}'`);
}
},
() => ok(false, `query should not have rejected for
'${name}'`)
);
});
return Promise.all(promisesToQuery);
},
checkUnsupportedPermissions(permissions) {
const promisesToQuery = permissions.
map(({ name }) => {
return iframeWindow.navigator.permissions
.query({ name })
.then(
() => ok(false, `query should not have resolved for
'${name}'`),
error => {
is(error.name,
'TypeError',
`query should have thrown TypeError for
'${name}'`);
}
);
});
return Promise.all(promisesToQuery);
},
promiseStateChanged(name, state) {
return iframeWindow.navigator.permissions
.query({ name })
.then(status => {
return new Promise( resolve => {
status.onchange = () => {
status.onchange = null;
is(status.state, state, `state changed for
'${name}'`);
resolve();
};
});
},
() => ok(false, `query should not have rejected for
'${name}'`));
},
testStatusOnChange() {
return new Promise((resolve) => {
SpecialPowers.popPermissions(() => {
const permission =
'geolocation';
const promiseGranted = this.promiseStateChanged(permission,
'granted');
this.setPermissions(ALLOW_ACTION);
promiseGranted.then(async () => {
const promisePrompt = this.promiseStateChanged(permission,
'prompt');
await SpecialPowers.popPermissions();
return promisePrompt;
}).then(resolve);
});
});
},
testInvalidQuery() {
return iframeWindow.navigator.permissions
.query({ name:
'invalid' })
.then(
() => ok(false,
'invalid query should not have resolved'),
() => ok(true,
'invalid query should have rejected')
);
},
async testNotFullyActiveDoc() {
const iframe1 = await createIframe();
const expectedErrorClass = iframe1.contentWindow.DOMException;
const permAPI = iframe1.contentWindow.navigator.permissions;
// Document no longer fully active
iframe1.remove();
await new Promise((res) => {
permAPI.query({ name:
"geolocation" }).catch((error) => {
ok(
error instanceof expectedErrorClass,
"DOMException from other realm"
);
is(
error.name,
"InvalidStateError",
"Must reject with a InvalidStateError"
);
iframe1.remove();
res();
});
});
},
async testNotFullyActiveChange() {
await SpecialPowers.popPermissions();
const iframe2 = await createIframe();
const initialStatus = await iframe2.contentWindow.navigator.permissions.query(
{ name:
"geolocation" }
);
await SpecialPowers.pushPermissions([
{
type:
"geo",
allow: PROMPT_ACTION,
context: iframe2.contentWindow.document,
},
]);
is(
initialStatus.state,
"prompt",
"Initially the iframe's permission is prompt"
);
// Document no longer fully active
const stolenDoc = iframe2.contentWindow.document;
iframe2.remove();
initialStatus.onchange = () => {
ok(false,
"onchange must not fire when document is not fully active.");
};
// We set it to grant for this origin, but the PermissionStatus doesn
't change.
await SpecialPowers.pushPermissions([
{
type:
"geo",
allow: ALLOW_ACTION,
context: stolenDoc,
},
]);
is(
initialStatus.state,
"prompt",
"Inactive document's permission must not change"
);
// Re-attach the
iframe
document.
body.appendChild(iframe2);
await new Promise((res) => (iframe2.onload = res));
// Fully active again
const newStatus = await iframe2.contentWindow.navigator.permissions.query({
name:
"geolocation",
});
is(newStatus.state,
"granted",
"Reflect that we are granted");
const newEventPromise = new Promise((res) => (newStatus.onchange = res));
await SpecialPowers.pushPermissions([
{
type:
"geo",
allow: DENY_ACTION,
context: iframe2.contentWindow.document,
},
]);
// Event fires...
await newEventPromise;
is(initialStatus.state,
"prompt",
"Remains prompt, as it's actually dead.");
is(newStatus.state,
"denied",
"New status must be 'denied'.");
iframe2.remove();
},
};
}
function createIframe() {
return new Promise((resolve) => {
const
iframe = document.createElement(
'iframe');
iframe.src =
'file_empty.html';
iframe.onload = () => resolve(
iframe);
document.
body.appendChild(
iframe);
});
}
window.onload = async () => {
try {
const tester = createPermissionTester(await createIframe());
await tester.checkUnsupportedPermissions(UNSUPPORTED_PERMISSIONS);
await tester.setPermissions(UNKNOWN_ACTION);
await tester.checkPermissions(PERMISSIONS,
'prompt');
await tester.setPermissions(PROMPT_ACTION);
await tester.checkPermissions(PERMISSIONS,
'prompt',
'granted');
await tester.setPermissions(ALLOW_ACTION);
await tester.checkPermissions(PERMISSIONS,
'granted');
await tester.setPermissions(DENY_ACTION);
await tester.checkPermissions(PERMISSIONS,
'denied');
await tester.testStatusOnChange();
await tester.testInvalidQuery();
await tester.testNotFullyActiveDoc();
await tester.testNotFullyActiveChange();
await SpecialPowers.pushPrefEnv({
set: [
[
"privacy.resistFingerprinting", true]
]
});
await tester.setPermissions(PROMPT_ACTION);
await tester.checkPermissions(PERMISSIONS,
'prompt',
'prompt');
await SpecialPowers.popPrefEnv();
await SpecialPowers.pushPrefEnv({
set: [
[
"permissions.media.query.enabled", false]
]
});
await tester.setPermissions(UNKNOWN_ACTION);
await tester.checkUnsupportedPermissions(MEDIA_PERMISSIONS);
await tester.checkPermissions(OTHER_PERMISSIONS,
'prompt');
} finally {
SimpleTest.finish();
}
};
</
script>
</
body>
</
html>