-
Notifications
You must be signed in to change notification settings - Fork 31
Add Web Notifications support #2067
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
e5edc06
Add webNotifications feature for macOS
diegoreymendez 9f55e0a
Fix: Coerce notification options to strings for Swift decoding
diegoreymendez 5aab99c
Move webNotifications to webCompat setting
diegoreymendez 6b6a332
Add nativeEnabled setting and isSecureContext gate to webNotifications
diegoreymendez a84135f
Merge branch 'main' into diego/support-website-notifications
diegoreymendez f091fc8
Remove debug logging from webNotificationsFix
diegoreymendez 3a8dc54
Address PR feedback for webNotificationsFix
diegoreymendez cac0a60
Revert shimInterface change due to TypeScript constraints
diegoreymendez ac47e29
Use wrapFunction for Notification constructor, fix close() onclose ha…
diegoreymendez 1d73fba
Merge branch 'main' into diego/support-website-notifications
diegoreymendez 631608c
Update Notification.permission after requestPermission resolves
diegoreymendez a5993e5
Make close() idempotent to prevent multiple onclose events
diegoreymendez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| import ContentFeature from '../content-feature.js'; | ||
| import { wrapToString } from '../wrapper-utils.js'; | ||
|
|
||
| /** | ||
| * Web Notifications feature - provides a polyfill for the Web Notifications API | ||
| * that communicates with native code for permission management and notification display. | ||
| */ | ||
| export default class WebNotifications extends ContentFeature { | ||
| /** @type {Map<string, object>} */ | ||
| #notifications = new Map(); | ||
|
|
||
| init() { | ||
| console.log('[WebNotifications] init() called'); | ||
diegoreymendez marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.#initNotificationPolyfill(); | ||
| console.log('[WebNotifications] Notification polyfill installed'); | ||
| } | ||
|
|
||
| #initNotificationPolyfill() { | ||
|
Check failure on line 18 in injected/src/features/web-notifications.js
|
||
| const feature = this; | ||
|
Check failure on line 19 in injected/src/features/web-notifications.js
|
||
|
|
||
| /** | ||
| * NotificationPolyfill - replaces the native Notification API | ||
| */ | ||
| class NotificationPolyfill { | ||
| /** @type {string} */ | ||
| #id; | ||
| /** @type {string} */ | ||
| title; | ||
| /** @type {string} */ | ||
| body; | ||
| /** @type {string} */ | ||
| icon; | ||
| /** @type {string} */ | ||
| tag; | ||
| /** @type {any} */ | ||
| data; | ||
|
|
||
| // Event handlers | ||
| /** @type {((this: Notification, ev: Event) => any) | null} */ | ||
| onclick = null; | ||
| /** @type {((this: Notification, ev: Event) => any) | null} */ | ||
| onclose = null; | ||
| /** @type {((this: Notification, ev: Event) => any) | null} */ | ||
| onerror = null; | ||
| /** @type {((this: Notification, ev: Event) => any) | null} */ | ||
| onshow = null; | ||
|
|
||
| /** | ||
| * @returns {'default' | 'denied' | 'granted'} | ||
| */ | ||
| static get permission() { | ||
| // For now, always return 'granted' - Project 5 will query native | ||
| return 'granted'; | ||
| } | ||
|
|
||
| /** | ||
| * @param {NotificationPermissionCallback} [deprecatedCallback] | ||
| * @returns {Promise<NotificationPermission>} | ||
| */ | ||
| static async requestPermission(deprecatedCallback) { | ||
| try { | ||
diegoreymendez marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const result = await feature.request('requestPermission', {}); | ||
| const permission = result?.permission || 'granted'; | ||
| if (deprecatedCallback) { | ||
| deprecatedCallback(permission); | ||
| } | ||
| return permission; | ||
| } catch (e) { | ||
| feature.log.error('requestPermission failed:', e); | ||
| const fallback = 'granted'; | ||
jonathanKingston marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (deprecatedCallback) { | ||
| deprecatedCallback(fallback); | ||
| } | ||
| return fallback; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @returns {number} | ||
| */ | ||
| static get maxActions() { | ||
| return 2; | ||
| } | ||
|
|
||
| /** | ||
| * @param {string} title | ||
| * @param {NotificationOptions} [options] | ||
| */ | ||
| constructor(title, options = {}) { | ||
| this.#id = crypto.randomUUID(); | ||
| this.title = String(title); | ||
| this.body = options.body ? String(options.body) : ''; | ||
| this.icon = options.icon ? String(options.icon) : ''; | ||
| this.tag = options.tag ? String(options.tag) : ''; | ||
| this.data = options.data; | ||
|
|
||
| feature.#notifications.set(this.#id, this); | ||
|
|
||
| feature.notify('showNotification', { | ||
| id: this.#id, | ||
| title: this.title, | ||
| body: this.body, | ||
| icon: this.icon, | ||
| tag: this.tag, | ||
| }); | ||
| } | ||
|
|
||
| close() { | ||
| feature.notify('closeNotification', { id: this.#id }); | ||
| feature.#notifications.delete(this.#id); | ||
| } | ||
| } | ||
|
|
||
| // Wrap the constructor to make toString() look native | ||
| const wrappedNotification = wrapToString( | ||
| NotificationPolyfill, | ||
| NotificationPolyfill, | ||
| 'function Notification() { [native code] }', | ||
| ); | ||
|
|
||
| // Wrap static methods | ||
| const wrappedRequestPermission = wrapToString( | ||
| NotificationPolyfill.requestPermission.bind(NotificationPolyfill), | ||
| NotificationPolyfill.requestPermission, | ||
| 'function requestPermission() { [native code] }', | ||
| ); | ||
|
|
||
| // Subscribe to notification events from native | ||
| this.subscribe('notificationEvent', (data) => { | ||
| const notification = this.#notifications.get(data.id); | ||
| if (!notification) return; | ||
|
|
||
| const eventName = `on${data.event}`; | ||
| if (typeof notification[eventName] === 'function') { | ||
| try { | ||
| notification[eventName].call(notification, new Event(data.event)); | ||
|
Check failure on line 136 in injected/src/features/web-notifications.js
|
||
| } catch (e) { | ||
| feature.log.error(`Error in ${eventName} handler:`, e); | ||
| } | ||
| } | ||
|
|
||
| // Clean up on close event | ||
| if (data.event === 'close') { | ||
| this.#notifications.delete(data.id); | ||
| } | ||
| }); | ||
|
|
||
| // Define the Notification property on globalThis | ||
| this.defineProperty(globalThis, 'Notification', { | ||
| value: wrappedNotification, | ||
| writable: true, | ||
| configurable: true, | ||
| enumerable: false, | ||
| }); | ||
|
|
||
| // Define permission getter (return value directly to avoid recursion) | ||
| this.defineProperty(globalThis.Notification, 'permission', { | ||
| get: () => 'granted', // For now, always return 'granted' - Project 5 will query native | ||
| configurable: true, | ||
| enumerable: true, | ||
| }); | ||
|
|
||
| // Define maxActions getter (return value directly to avoid recursion) | ||
| this.defineProperty(globalThis.Notification, 'maxActions', { | ||
| get: () => 2, | ||
| configurable: true, | ||
| enumerable: true, | ||
| }); | ||
|
|
||
| // Define requestPermission | ||
| this.defineProperty(globalThis.Notification, 'requestPermission', { | ||
| value: wrappedRequestPermission, | ||
| writable: true, | ||
| configurable: true, | ||
| enumerable: true, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
15 changes: 15 additions & 0 deletions
15
injected/src/messages/web-notifications/closeNotification.notify.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "type": "object", | ||
| "title": "CloseNotificationParams", | ||
| "description": "Parameters for closing a web notification", | ||
| "additionalProperties": false, | ||
| "required": ["id"], | ||
| "properties": { | ||
| "id": { | ||
| "description": "Unique identifier of the notification to close", | ||
| "type": "string" | ||
| } | ||
| } | ||
| } | ||
|
|
20 changes: 20 additions & 0 deletions
20
injected/src/messages/web-notifications/notificationEvent.subscribe.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "type": "object", | ||
| "title": "NotificationEventParams", | ||
| "description": "Subscription for notification lifecycle events from native", | ||
| "additionalProperties": false, | ||
| "required": ["id", "event"], | ||
| "properties": { | ||
| "id": { | ||
| "description": "Unique identifier of the notification", | ||
| "type": "string" | ||
| }, | ||
| "event": { | ||
| "description": "The event type that occurred", | ||
| "type": "string", | ||
| "enum": ["show", "close", "click", "error"] | ||
| } | ||
| } | ||
| } | ||
|
|
9 changes: 9 additions & 0 deletions
9
injected/src/messages/web-notifications/requestPermission.request.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "type": "object", | ||
| "title": "RequestPermissionParams", | ||
| "description": "Parameters for requesting notification permission", | ||
| "additionalProperties": false, | ||
| "properties": {} | ||
| } | ||
|
|
16 changes: 16 additions & 0 deletions
16
injected/src/messages/web-notifications/requestPermission.response.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "type": "object", | ||
| "title": "RequestPermissionResponse", | ||
| "description": "Response from notification permission request", | ||
| "additionalProperties": false, | ||
| "required": ["permission"], | ||
| "properties": { | ||
| "permission": { | ||
| "description": "The permission state", | ||
| "type": "string", | ||
| "enum": ["default", "denied", "granted"] | ||
| } | ||
| } | ||
| } | ||
|
|
31 changes: 31 additions & 0 deletions
31
injected/src/messages/web-notifications/showNotification.notify.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "type": "object", | ||
| "title": "ShowNotificationParams", | ||
| "description": "Parameters for showing a web notification", | ||
| "additionalProperties": false, | ||
| "required": ["id", "title"], | ||
| "properties": { | ||
| "id": { | ||
| "description": "Unique identifier for the notification instance", | ||
| "type": "string" | ||
| }, | ||
| "title": { | ||
| "description": "The notification title", | ||
| "type": "string" | ||
| }, | ||
| "body": { | ||
| "description": "The notification body text", | ||
| "type": "string" | ||
| }, | ||
| "icon": { | ||
| "description": "URL of the notification icon", | ||
| "type": "string" | ||
| }, | ||
| "tag": { | ||
| "description": "Tag for grouping notifications", | ||
| "type": "string" | ||
| } | ||
| } | ||
| } | ||
|
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.