diff --git a/shell/browser/ui/tray_icon_cocoa.mm b/shell/browser/ui/tray_icon_cocoa.mm index 88bffe8538b37..0e1fcd41d70d7 100644 --- a/shell/browser/ui/tray_icon_cocoa.mm +++ b/shell/browser/ui/tray_icon_cocoa.mm @@ -173,8 +173,16 @@ - (void)popUpContextMenu:(electron::AtomMenuModel*)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..beee2014af9ba 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()', () => {