diff --git a/shell/browser/ui/tray_icon_cocoa.mm b/shell/browser/ui/tray_icon_cocoa.mm index eee3ad7c62725..ba1751066039b 100644 --- a/shell/browser/ui/tray_icon_cocoa.mm +++ b/shell/browser/ui/tray_icon_cocoa.mm @@ -173,8 +173,16 @@ - (void)popUpContextMenu:(electron::ElectronMenuModel*)menu_model { useDefaultAccelerator:NO]); // Hacky way to mimic design of ordinary tray menu. [statusItem_ setMenu:[menuController menu]]; + // -performClick: is a blocking call, which will run the task loop inside + // itself. This can potentially include running JS, which can result in + // this object being released. We take a temporary reference here to make + // sure we stay alive long enough to successfully return from this + // function. + // TODO(nornagon/codebytere): Avoid nesting task loops here. + [self retain]; [[statusItem_ button] performClick:self]; [statusItem_ setMenu:[menuController_ menu]]; + [self release]; return; } diff --git a/spec-main/api-tray-spec.ts b/spec-main/api-tray-spec.ts index ebf1a76cc3bef..c1e755b222ac8 100644 --- a/spec-main/api-tray-spec.ts +++ b/spec-main/api-tray-spec.ts @@ -51,6 +51,13 @@ describe('tray module', () => { }) tray.popUpContextMenu() }) + + it('can be called with a menu', () => { + const menu = Menu.buildFromTemplate([{ label: 'Test' }]); + expect(() => { + tray.popUpContextMenu(menu); + }).to.not.throw(); + }); }) describe('tray.getBounds()', () => {