Skip to content

Commit

Permalink
Add WPT cross origin nested iframe test for notification
Browse files Browse the repository at this point in the history
Differential Revision: https://phabricator.services.mozilla.com/D219680

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1914008
gecko-commit: c6aed7144a64580f6ca77373ad3d274fd6a8f97d
gecko-reviewers: asuth
  • Loading branch information
saschanaz authored and moz-wptsync-bot committed Sep 14, 2024
1 parent be5d397 commit 79bc1b3
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 0 deletions.
77 changes: 77 additions & 0 deletions notifications/cross-origin-nested.tentative.https.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Notifications in cross origin iframes</title>
<link rel="help" href="https://github.com/whatwg/notifications/issues/177">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/helpers.js"></script>
<script>
// The syntax below will give us a third party URL.
// See: https://web-platform-tests.org/writing-tests/server-features.html
const thirdPartyIframe =
'https://{{hosts[alt][]}}:{{ports[https][0]}}/notifications/resources/cross-origin-nested-parent.sub.html';
const promises = new Map();

// Firefox and Chrome deny notification permission in a third party partitioned
// iframe even if the permission is granted for origin of the iframe.

// Set up the listeners and then create a third party iframe.
// The iframe will again create a first party iframe.
promise_setup(async () => {
await trySettingPermission("granted");

// parent: the third party iframe
// child: the first party iframe in the third party one (ABA)
for (const iframe of ["parent", "child"]) {
// from the iframe window, or the worker opened from there
for (const worker of ["", "Worker"]) {
// permission attribute (.permission), or
// the permission request (.requestPermission())
for (const type of ["", "Request"]) {
const sender = iframe + worker + type;
promises.set(sender, new Promise(r => window.addEventListener("message", ev => {
if (ev.data.sender === sender) {
r(ev.data);
}
})));
}
}
}

const iframe = document.createElement("iframe");
iframe.src = thirdPartyIframe;
document.body.append(iframe);
})

promise_test(async t => {
const result = await promises.get("parent");
assert_equals(result.permission, "denied", `should deny the permission`);
assert_false(result.shown, `notification should not be shown`);

const parentRequestResult = await promises.get("parentRequest");
assert_equals(parentRequestResult.permission, "denied", "should deny the permission request");
}, "third party iframe");

promise_test(async t => {
const result = await promises.get("child");
assert_equals(result.permission, "granted", `should grant the permission`);
assert_true(result.shown, `notification should be shown`);

const childRequestResult = await promises.get("childRequest");
assert_equals(childRequestResult.permission, "granted", "should accept the permission request");
}, "nested first party iframe");

promise_test(async t => {
const result = await promises.get("parentWorker");
assert_equals(result.permission, "denied", `should deny the permission`);
assert_false(result.shown, `notification should not be shown`);
}, "third party worker");

promise_test(async t => {
const result = await promises.get("childWorker");
assert_equals(result.permission, "granted", `should grant the permission`);
assert_true(result.shown, `notification should be shown`);
}, "nested first party worker");
</script>
36 changes: 36 additions & 0 deletions notifications/cross-origin-serviceworker.tentative.https.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Notifications in cross origin iframes</title>
<link rel="help" href="https://github.com/whatwg/notifications/issues/177">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/helpers.js"></script>
<script>
// The syntax below will give us a third party URL.
// See: https://web-platform-tests.org/writing-tests/server-features.html
const thirdPartyOrigin = 'https://{{hosts[alt][]}}:{{ports[https][0]}}';
const thirdPartyIframe =
`${thirdPartyOrigin}/notifications/resources/cross-origin-serviceworker-iframe.sub.html`;
let promise;

promise_setup(async () => {
await trySettingPermission("granted");

promise = new Promise(r => window.addEventListener("message", ev => {
if ("shown" in ev.data) {
r(ev.data)
}
}));

const iframe = document.createElement("iframe");
iframe.src = thirdPartyIframe;
document.body.append(iframe);
})

promise_test(async t => {
const result = await promise;
assert_false(result.shown, `notification should not be shown`);
}, "third party serviceworker");
</script>
6 changes: 6 additions & 0 deletions notifications/permissions-non-secure.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@
test(t => {
assert_equals(Notification.permission, "denied");
}, "Notification.permission must be called from a secure context");

promise_test(async t => {
const worker = new Worker("resources/permission-worker.js", { type: "module" });
const data = await new Promise(r => worker.onmessage = ev => r(ev.data));
assert_equals(data.permission, "denied");
}, "Notification.permission must be called from a secure worker");
</script>
9 changes: 9 additions & 0 deletions notifications/resources/cross-origin-nested-child.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Notification nested iframe first party child</title>
<body>
<script type="module">
import { permissionTest } from "./permission-helper.js"

permissionTest('https://{{hosts[][]}}:{{ports[https][0]}}', "child");
</script>
24 changes: 24 additions & 0 deletions notifications/resources/cross-origin-nested-parent.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Notification nested iframe third party parent</title>
<body>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script type="module">
import { permissionTest } from "./permission-helper.js"

// Grant the permission so that we can confirm the permission being denied
// regardless of the actual permission status.
test_driver.set_test_context(window.top);
await test_driver.set_permission({ name: "notifications" }, "granted");

const firstPartyOrigin = 'https://{{hosts[][]}}:{{ports[https][0]}}';

permissionTest(firstPartyOrigin, "parent");

const nestedChild =
`${firstPartyOrigin}/notifications/resources/cross-origin-nested-child.sub.html`;
const iframe = document.createElement("iframe");
iframe.src = nestedChild;
document.body.append(iframe);
</script>
24 changes: 24 additions & 0 deletions notifications/resources/cross-origin-serviceworker-iframe.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Notification cross origin iframe with service worker</title>
<body>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="helpers.js"></script>
<script type="module">
const firstPartyOrigin = 'https://{{hosts[][]}}:{{ports[https][0]}}';

// As our service worker cannot directly ping the first party window,
// let's be a proxy here.
navigator.serviceWorker.addEventListener("message", ev => {
window.top.postMessage(ev.data, firstPartyOrigin);
});

// Grant the permission so that we can confirm the permission being denied
// regardless of the actual permission status.
test_driver.set_test_context(window.top);
await test_driver.set_permission({ name: "notifications" }, "granted");

await unregisterAllServiceWorker();
const registration = await navigator.serviceWorker.register("permission-sw.js");
</script>
24 changes: 24 additions & 0 deletions notifications/resources/permission-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function permissionTest(origin, sender) {
function ping(message) {
if (!globalThis.WorkerGlobalScope) {
window.top.postMessage(message, origin);
} else {
globalThis.postMessage(message);
}
n.close();
}

const n = new Notification(sender);
const permission = Notification.permission;
n.onshow = () => ping({ sender, permission, shown: true });
n.onerror = () => ping({ sender, permission, shown: false });

if (!globalThis.WorkerGlobalScope) {
Notification.requestPermission().then(permission => ping({ sender: `${sender}Request`, permission }));

const workerUrl = new URL(`./permission-worker.js`, import.meta.url);
workerUrl.searchParams.set("sender", `${sender}Worker`);
const worker = new Worker(workerUrl, { type: "module" });
worker.onmessage = ev => ping(ev.data, origin);
}
}
19 changes: 19 additions & 0 deletions notifications/resources/permission-sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
importScripts("helpers.js");

// Send the result to the clients of this service worker.
async function ping(message) {
const clients = await self.clients.matchAll({ includeUncontrolled: true });
for (const client of clients) {
client.postMessage(message);
}
for (const n of await registration.getNotifications()) {
n.close();
}
}

(async () => {
await untilActivate();

const shown = await registration.showNotification("serviceworker").then(() => true, err => false);
ping({ shown });
})();
4 changes: 4 additions & 0 deletions notifications/resources/permission-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { permissionTest } from "./permission-helper.js"

const params = new URL(import.meta.url).searchParams;
permissionTest(null, params.get("sender"));

0 comments on commit 79bc1b3

Please sign in to comment.