push-event.https.any.js (3988B)
1 // META: global=window-module 2 // META: script=/resources/testdriver.js 3 // META: script=/resources/testdriver-vendor.js 4 // META: script=/notifications/resources/helpers.js 5 // META: variant=?includeAppServerKey=true 6 // META: variant=?includeAppServerKey=false 7 8 import { encrypt } from "./resources/helpers.js" 9 import { createVapid } from "./resources/vapid.js"; 10 11 const includeAppServerKey = new URL(location.href).searchParams.get("includeAppServerKey") === "true"; 12 let registration; 13 14 async function subscribe(t) { 15 if (includeAppServerKey) { 16 const vapid = await createVapid(); 17 const subscription = await registration.pushManager.subscribe({ 18 applicationServerKey: vapid.publicKey 19 }); 20 t.add_cleanup(() => subscription.unsubscribe()); 21 return { vapid, subscription }; 22 } 23 24 // without key 25 try { 26 const subscription = await registration.pushManager.subscribe(); 27 t.add_cleanup(() => subscription.unsubscribe()); 28 return { subscription }; 29 } catch (err) { 30 if (err.name === "NotSupportedError") { 31 // happens if and only if applicationServerKey omission is disallowed, 32 // which is permitted per the spec. Throwing OptionalFeatureUnsupportedError marks the 33 // result as PRECONDITION_FAILED. 34 // 35 // https://w3c.github.io/push-api/#subscribe-method 36 // If the options argument does not include a non-null value for the applicationServerKey 37 // member, and the push service requires one to be given, queue a global task on the 38 // networking task source using global to reject promise with a "NotSupportedError" 39 // DOMException. 40 throw new OptionalFeatureUnsupportedError(description); 41 } else { 42 throw err; 43 } 44 } 45 } 46 47 async function pushMessage(subscription, { vapid, message }) { 48 const result = !message 49 ? { headers: { TTL: 15 } } 50 : await encrypt( 51 message, 52 subscription.getKey("p256dh"), 53 subscription.getKey("auth") 54 ); 55 56 if (includeAppServerKey) { 57 result.headers.Authorization = await vapid.generateAuthHeader( 58 new URL(subscription.endpoint).origin 59 ); 60 } 61 62 const promise = new Promise(r => { 63 navigator.serviceWorker.addEventListener("message", r, { once: true }) 64 }); 65 66 await fetch(subscription.endpoint, { 67 method: "post", 68 ...result 69 }); 70 71 return (await promise).data; 72 } 73 74 promise_setup(async () => { 75 await trySettingPermission("granted"); 76 registration = await prepareActiveServiceWorker("push-sw.js"); 77 }); 78 79 promise_test(async (t) => { 80 const { vapid, subscription } = await subscribe(t); 81 82 const event = await pushMessage(subscription, { vapid }); 83 84 assert_equals(event.constructor, "PushEvent"); 85 assert_equals(event.data, null); 86 }, "Posting to the push endpoint should fire push event on the service worker"); 87 88 const entries = [ 89 { isJSON: false, message: new TextEncoder().encode("Hello") }, 90 { isJSON: false, message: new Uint8Array([226, 130, 40, 240, 40, 140, 188]) }, 91 { isJSON: true, message: new TextEncoder().encode(JSON.stringify({ hello: "world" })) }, 92 { isJSON: false, message: new Uint8Array() }, 93 { isJSON: false, message: new Uint8Array([0x48, 0x69, 0x21, 0x20, 0xf0, 0x9f, 0x91, 0x80]) }, 94 ]; 95 96 for (const { isJSON, message } of entries) { 97 promise_test(async (t) => { 98 const { vapid, subscription } = await subscribe(t); 99 100 const event = await pushMessage(subscription, { vapid, message }); 101 102 assert_equals(event.constructor, "PushEvent"); 103 assert_array_equals(new Uint8Array(event.data.arrayBuffer), message); 104 assert_array_equals(new Uint8Array(await event.data.blob.arrayBuffer()), message); 105 assert_equals(event.data.text, new TextDecoder().decode(message)); 106 107 assert_equals(event.data.json.ok, isJSON); 108 if (isJSON) { 109 assert_array_equals( 110 new TextEncoder().encode(JSON.stringify(event.data.json.value)), 111 message 112 ); 113 } 114 115 }, `Posting to the push endpoint with encrypted data ${message} should fire push event on the service worker`); 116 }