Retire failed extension notification records (#35134)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-28 10:19:22 -07:00
committed by GitHub
co-authored by OrcaWin
parent 247035b026
commit e7e8bdd2f9
2 changed files with 49 additions and 2 deletions
@@ -82,6 +82,46 @@ function invoke(caller: BrowserExtensionCaller, method: string, args: unknown[]
const options = { type: 'basic', title: 'Notification', message: 'Retire with its extension' }
describe('notification extension ownership', () => {
it.each(['create', 'update'])(
'forgets a failed native %s without reporting a dismissal',
(method) => {
const { caller } = notificationCaller(`failed-${method}`)
const sibling = { ...caller, extension: { ...caller.extension, id: 'b'.repeat(32) } }
invoke(caller, 'create', ['failed', options])
if (method === 'update') {
invoke(caller, 'update', ['failed', { message: 'updated' }])
}
const failed = mocks.notifications.at(-1)
if (!failed) {
throw new Error('Missing native notification')
}
invoke(sibling, 'create', ['live', options])
vi.clearAllMocks()
failed.emit('failed', {}, 'Native notification delivery failed')
expect(invoke(caller, 'getAll')).toEqual({})
expect(invoke(caller, 'update', ['failed', { message: 'retry' }])).toBe(false)
expect(invoke(caller, 'clear', ['failed'])).toBe(false)
expect(invoke(sibling, 'getAll')).toEqual({ live: true })
expect(failed.eventNames()).toEqual([])
expect(failed.close).not.toHaveBeenCalled()
expect(emitBrowserExtensionEvent).not.toHaveBeenCalled()
invoke(caller, 'create', ['failed', options])
failed.emit('failed', {}, 'Late old failure')
failed.emit('close', { reason: 'timedOut' })
expect(invoke(caller, 'getAll')).toEqual({ failed: true })
expect(invoke(caller, 'clear', ['failed'])).toBe(true)
expect(emitBrowserExtensionEvent).toHaveBeenLastCalledWith(
{ session: caller.session, extensionId: caller.extension.id },
'notifications',
'onClosed',
['failed', false]
)
}
)
it('closes and forgets unloaded notifications while preserving sibling extensions and profiles', () => {
const { caller, extensions } = notificationCaller('owner')
const sibling = { ...caller, extension: { ...caller.extension, id: 'b'.repeat(32) } }
@@ -93,12 +93,14 @@ function emit(caller: BrowserExtensionCaller, event: string, args: unknown[]): v
)
}
function detach(entry: Shown): void {
function detach(entry: Shown, closeNative = true): void {
const { notification } = entry
entry.notification = null
if (notification) {
notification.removeAllListeners()
notification.close()
if (closeNative) {
notification.close()
}
}
}
@@ -138,6 +140,11 @@ function show(caller: BrowserExtensionCaller, id: string, entry: Shown): void {
shownFor(caller).delete(id)
emit(caller, 'onClosed', [id, details.reason !== 'timedOut'])
})
notification.once('failed', () => {
shownFor(caller).delete(id)
// Electron destroys the native notification after dispatching this failure.
detach(entry, false)
})
entry.notification = notification
notification.show()
}