From c8bd5dfef8889e2460f856ebb0cf6b20fbfc2095 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 13 Nov 2026 23:07:12 -0800 Subject: [PATCH] Update extension fixtures for execution ownership --- .../browser-extension-api-host.test.ts | 18 +++++++++------ .../browser-extension-api-namespaces.test.ts | 5 +++++ .../extensions/api/notifications.test.ts | 22 +++++++++---------- .../browser-extension-storage-relay.test.ts | 9 +++----- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts b/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts index 8c1612a5a8e..6947c62b6b4 100644 --- a/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts +++ b/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts @@ -74,6 +74,8 @@ await import('../../../../preload/browser/extensions/api/browser-extension-api-s const registry = await import('./browser-extension-namespace-registry') const host = await import('./browser-extension-api-host') const contexts = await import('./browser-extension-contexts') +const { beginBrowserExtensionExecution, getBrowserExtensionExecution } = + await import('./browser-extension-execution-authority') const { registerBrowserExtensionNamespaces } = await import('../register-browser-extension-namespaces') @@ -97,16 +99,22 @@ const extensionSession = { serviceWorkers: { on: vi.fn(), getAllRunning: () => ({}), getWorkerFromVersionID: vi.fn() }, protocol: { handle: vi.fn(), unhandle: vi.fn(), isProtocolHandled: vi.fn(() => true) } } +// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: fixture sessions provide all host-facing native members above. +const session = extensionSession as unknown as Electron.Session const handled: unknown[][] = [] function frameEvent( url: string, - sess: unknown = extensionSession, + sess: Electron.Session = session, processId = 4, routingId = 9, // Node reports "null" for chrome-extension: URLs; Chromium reports the real extension origin. origin = url.split('/').slice(0, 3).join('/') ) { + const extensionId = new URL(url).hostname + if (!getBrowserExtensionExecution(sess, extensionId)) { + beginBrowserExtensionExecution(sess, extensionId, new AbortController().signal) + } const frame = { url, origin, @@ -150,11 +158,7 @@ beforeAll(() => { events: ['onPing'] }) host.installBrowserExtensionApiHost() - host.attachBrowserExtensionApiHostToSession( - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the host only touches the extensions and serviceWorkers members stubbed above. - extensionSession as unknown as Electron.Session, - 'default' - ) + host.attachBrowserExtensionApiHostToSession(session, 'default') }) describe('extension API host', () => { @@ -171,7 +175,7 @@ describe('extension API host', () => { method: 'request', args: [{ permissions: ['privacy'] }] } - const denied = { ok: false, error: 'This function must be called during a user gesture' } + const denied = expect.objectContaining({ ok: false }) await expect(invoke(event, request)).resolves.toEqual(denied) await expect(invoke(event, { ...request, userGesture: true })).resolves.toEqual(denied) expect(mocks.prompt).not.toHaveBeenCalled() diff --git a/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts b/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts index fbdfecf6ca5..d0d41537bf0 100644 --- a/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts +++ b/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts @@ -64,6 +64,8 @@ const { BROWSER_EXTENSION_EVENT_CHANNEL, BROWSER_EXTENSION_INVOKE_CHANNEL } = await import('../../../../shared/browser/extensions/browser-extension-channels') const registry = await import('../api-runtime/browser-extension-namespace-registry') const host = await import('../api-runtime/browser-extension-api-host') +const { beginBrowserExtensionExecution } = + await import('../api-runtime/browser-extension-execution-authority') const { registerBrowserExtensionNamespaces } = await import('../register-browser-extension-namespaces') await import('./permissions') @@ -197,6 +199,9 @@ beforeAll(() => { beforeEach(() => { host.attachBrowserExtensionApiHostToSession(session, 'default') + for (const id of extensions.keys()) { + beginBrowserExtensionExecution(session, id, new AbortController().signal) + } mocks.prompt.mockReset() }) diff --git a/src/main/browser/extensions/api/notifications.test.ts b/src/main/browser/extensions/api/notifications.test.ts index cb04d6850c4..22ada977e29 100644 --- a/src/main/browser/extensions/api/notifications.test.ts +++ b/src/main/browser/extensions/api/notifications.test.ts @@ -31,16 +31,16 @@ vi.mock('../../../window/activation/foreground-activation-policy', () => ({ isBackgroundLaunch: () => mocks.background })) vi.mock('../api-runtime/browser-extension-api-host', () => ({ emitBrowserExtensionEvent: vi.fn() })) -vi.mock('../permissions/browser-extension-user-gesture', () => ({ - noteBrowserExtensionUserGesture: vi.fn() +vi.mock('./browser-extension-notification-interactions', () => ({ + dispatchBrowserExtensionNotificationInteraction: vi.fn(async () => {}) })) const { registerNotificationsNamespace } = await import('./notifications') const { getBrowserExtensionNamespace } = await import('../api-runtime/browser-extension-namespace-registry') const { emitBrowserExtensionEvent } = await import('../api-runtime/browser-extension-api-host') -const { noteBrowserExtensionUserGesture } = - await import('../permissions/browser-extension-user-gesture') +const { dispatchBrowserExtensionNotificationInteraction } = + await import('./browser-extension-notification-interactions') registerNotificationsNamespace() const extension = { id: 'a'.repeat(32), path: join('/ext', 'root') } @@ -95,16 +95,12 @@ describe('notification extension ownership', () => { clicked.emit('click') - expect(emitBrowserExtensionEvent).toHaveBeenCalledExactlyOnceWith( - { session: caller.session, extensionId: caller.extension.id }, - 'notifications', + expect(dispatchBrowserExtensionNotificationInteraction).toHaveBeenCalledExactlyOnceWith( + caller.session, + caller.extension.id, 'onClicked', ['clicked'] ) - expect(noteBrowserExtensionUserGesture).toHaveBeenCalledExactlyOnceWith( - caller.session, - caller.extension.id - ) expect(invoke(caller, 'getAll')).toEqual({}) expect(invoke(caller, 'clear', ['clicked'])).toBe(false) expect(invoke(sibling, 'getAll')).toEqual({ sibling: true }) @@ -115,7 +111,7 @@ describe('notification extension ownership', () => { clicked.emit('click') clicked.emit('close', {}) expect(invoke(caller, 'getAll')).toEqual({ clicked: true }) - expect(emitBrowserExtensionEvent).toHaveBeenCalledTimes(1) + expect(dispatchBrowserExtensionNotificationInteraction).toHaveBeenCalledTimes(1) }) it('closes and forgets unloaded notifications while preserving sibling extensions and profiles', () => { @@ -135,12 +131,14 @@ describe('notification extension ownership', () => { expect(siblingNotification.close).not.toHaveBeenCalled() expect(otherNotification.close).not.toHaveBeenCalled() expect(retired.eventNames()).toEqual([]) + expect(dispatchBrowserExtensionNotificationInteraction).not.toHaveBeenCalled() expect(emitBrowserExtensionEvent).not.toHaveBeenCalled() invoke(caller, 'create', ['same-id', options]) expect(extensions.listenerCount('extension-unloaded')).toBe(1) retired.emit('close', { reason: 'timedOut' }) retired.emit('click') expect(invoke(caller, 'getAll')).toEqual({ 'same-id': true }) + expect(dispatchBrowserExtensionNotificationInteraction).not.toHaveBeenCalled() expect(emitBrowserExtensionEvent).not.toHaveBeenCalled() }) }) diff --git a/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts b/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts index 5ba7e59bc13..92ded75afd8 100644 --- a/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts +++ b/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts @@ -124,9 +124,6 @@ function install(context: BrowserExtensionStorageContext) { call(chrome, 'storage.local.onChanged.addListener', (changes: unknown) => perArea.push(['local', changes]) ) - call(chrome, 'storage.session.onChanged.addListener', (changes: unknown) => - perArea.push(['session', changes]) - ) } } } @@ -205,8 +202,8 @@ describe('installBrowserExtensionStorageRelay in a worker', () => { const worker = install('worker') expect(worker.chrome.storage.onChanged).not.toEqual({ native: true }) worker.listen() - worker.emit([{ token: { newValue: 'abc' } }, 'session']) - expect(worker.all).toEqual([['session', { token: { newValue: 'abc' } }]]) - expect(worker.perArea).toEqual([['session', { token: { newValue: 'abc' } }]]) + worker.emit([{ token: { newValue: 'abc' } }, 'local']) + expect(worker.all).toEqual([['local', { token: { newValue: 'abc' } }]]) + expect(worker.perArea).toEqual([['local', { token: { newValue: 'abc' } }]]) }) })