diff --git a/functional-samples/cookbook.push/README.md b/functional-samples/cookbook.push/README.md new file mode 100644 index 0000000000..7158121957 --- /dev/null +++ b/functional-samples/cookbook.push/README.md @@ -0,0 +1,20 @@ +# Service worker with push notification + +This sample demonstrates using the [Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) with `self.registration.pushManager.subscribe()` and specifically how to use `userVisibleOnly` to silence required notifications when receiving a push message in a service worker based extension. + +## Overview + +By calling a method in the sample and using an external push server website we can simulate an extension receiving a push message where it is required to emit a notification and where it can bypass that requirement (`userVisibleOnly = false`). + +## Running this extension + +1. Clone this repository. +1. Ensure your operating system allows your browser to show desktop notification. For [MacOS](https://support.apple.com/guide/mac-help/change-notifications-settings-mh40583/mac) this, for Google Chrome, requires "Google Chrome" and "Google Chrome Helper (Alerts)" to be allowed. +1. Go to the [web push test server](https://web-push-codelab.glitch.me/) and copy the “Public Key” to the `APPLICATION_SERVER_PUBLIC_KEY` variable in background.js. +1. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked). +1. Click “service worker (Inactive)” on the extension to load DevTools for background.js +1. In DevTools call: `await SubscribeUserVisibleOnlyFalse();` +1. Copy the output after “Subscription data to be sent to the push notification server:” and paste it into the [web push test server](https://web-push-codelab.glitch.me/) inside “Subscription to Send To” text box +1. Enter some text into “Text to Send” +1. Click “SEND PUSH MESSAGE” +1. Observe that there is no notification with the text you sent in the browser and no generic notification notification being shown by the browser (that is usually enforced). You’ll see the text you sent in the DevTools log proving the extension service worker received the push data. diff --git a/functional-samples/cookbook.push/background.js b/functional-samples/cookbook.push/background.js new file mode 100644 index 0000000000..3617882a93 --- /dev/null +++ b/functional-samples/cookbook.push/background.js @@ -0,0 +1,77 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "subscribeUserVisibleOnlyFalse" }]*/ + +const APPLICATION_SERVER_PUBLIC_KEY = ''; + +async function subscribeUserVisibleOnlyFalse() { + const applicationServerKey = urlB64ToUint8Array( + APPLICATION_SERVER_PUBLIC_KEY + ); + try { + let subscriptionData = await self.registration.pushManager.subscribe({ + // With our new change[1], this can be set to false. Before it must + // always be set to true otherwise an error will be thrown about + // permissions denied. + userVisibleOnly: false, + applicationServerKey: applicationServerKey + }); + console.log('[Service Worker] Extension is subscribed to push server.'); + logSubscriptionDataToConsole(subscriptionData); + } catch (error) { + console.error('[Service Worker] Failed to subscribe, error: ', error); + } +} + +function logSubscriptionDataToConsole(subscription) { + // The `subscription` data would normally only be know by the push server, + // but for this sample we'll print it out to the console so it can be pasted + // into a testing push notification server (at + // https://web-push-codelab.glitch.me/) to send push messages to this + // endpoint (extension). + console.log( + '[Service Worker] Subscription data to be pasted in the test push' + + 'notification server: ' + ); + console.log(JSON.stringify(subscription)); +} + +// Push message event listener. +self.addEventListener('push', function (event) { + console.log('[Service Worker] Push Received.'); + console.log( + `[Service Worker] Push had this data/text: "${event.data.text()}"` + ); + + // Before `userVisibleOnly` could be passed as false we would have to show a + // notification at this point, but now we no longer have to. +}); + +// Helper method for converting the server key to an array that is passed +// when subscribing to a push server. +function urlB64ToUint8Array(base64String) { + const padding = '='.repeat((4 - (base64String.length % 4)) % 4); + const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); + + const rawData = atob(base64); + const outputArray = new Uint8Array(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +} + +// [1]: https://chromiumdash.appspot.com/commit/f6a8800208dc4bc20a0250a7964983ce5aa746f0 diff --git a/functional-samples/cookbook.push/manifest.json b/functional-samples/cookbook.push/manifest.json new file mode 100644 index 0000000000..6b19fceae5 --- /dev/null +++ b/functional-samples/cookbook.push/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Service worker with push notification", + "version": "1.0", + "manifest_version": 3, + "background": { + "service_worker": "background.js" + }, + "permissions": ["notifications"] +}