// META: global=window-module // META: script=/resources/testdriver.js // META: script=/resources/testdriver-vendor.js // META: script=/notifications/resources/helpers.js // META: variant=?includeAppServerKey=true // META: variant=?includeAppServerKey=false import { encrypt } from "./resources/helpers.js" import { createVapid } from "./resources/vapid.js"; const includeAppServerKey = new URL(location.href).searchParams.get("includeAppServerKey") === "true"; let registration; async function subscribe(t) { if (includeAppServerKey) { const vapid = await createVapid(); const subscription = await registration.pushManager.subscribe({ applicationServerKey: vapid.publicKey }); t.add_cleanup(() => subscription.unsubscribe()); return { vapid, subscription }; } // without key try { const subscription = await registration.pushManager.subscribe(); t.add_cleanup(() => subscription.unsubscribe()); return { subscription }; } catch (err) { if (err.name === "NotSupportedError") { // happens if and only if applicationServerKey omission is disallowed, // which is permitted per the spec. Throwing OptionalFeatureUnsupportedError marks the // result as PRECONDITION_FAILED. // // https://w3c.github.io/push-api/#subscribe-method // If the options argument does not include a non-null value for the applicationServerKey // member, and the push service requires one to be given, queue a global task on the // networking task source using global to reject promise with a "NotSupportedError" // DOMException. throw new OptionalFeatureUnsupportedError(description); } else { throw err; } } } async function pushMessage(subscription, { vapid, message }) { const result = !message ? { headers: { TTL: 15 } } : await encrypt( message, subscription.getKey("p256dh"), subscription.getKey("auth") ); if (includeAppServerKey) { result.headers.Authorization = await vapid.generateAuthHeader( new URL(subscription.endpoint).origin ); } const promise = new Promise(r => { navigator.serviceWorker.addEventListener("message", r, { once: true }) }); await fetch(subscription.endpoint, { method: "post", ...result }); return (await promise).data; } promise_setup(async () => { await trySettingPermission("granted"); registration = await prepareActiveServiceWorker("push-sw.js"); }); promise_test(async (t) => { const { vapid, subscription } = await subscribe(t); const event = await pushMessage(subscription, { vapid }); assert_equals(event.constructor, "PushEvent"); assert_equals(event.data, null); }, "Posting to the push endpoint should fire push event on the service worker"); const entries = [ { isJSON: false, message: new TextEncoder().encode("Hello") }, { isJSON: false, message: new Uint8Array([226, 130, 40, 240, 40, 140, 188]) }, { isJSON: true, message: new TextEncoder().encode(JSON.stringify({ hello: "world" })) }, { isJSON: false, message: new Uint8Array() }, { isJSON: false, message: new Uint8Array([0x48, 0x69, 0x21, 0x20, 0xf0, 0x9f, 0x91, 0x80]) }, ]; for (const { isJSON, message } of entries) { promise_test(async (t) => { const { vapid, subscription } = await subscribe(t); const event = await pushMessage(subscription, { vapid, message }); assert_equals(event.constructor, "PushEvent"); assert_array_equals(new Uint8Array(event.data.arrayBuffer), message); assert_array_equals(new Uint8Array(await event.data.blob.arrayBuffer()), message); assert_equals(event.data.text, new TextDecoder().decode(message)); assert_equals(event.data.json.ok, isJSON); if (isJSON) { assert_array_equals( new TextEncoder().encode(JSON.stringify(event.data.json.value)), message ); } }, `Posting to the push endpoint with encrypted data ${message} should fire push event on the service worker`); }