From ab2bbc0f795d8659931f86a16acb1f96e30d38b6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 23 Sep 2026 19:31:41 +1200 Subject: [PATCH] mac: subclass NSApplication for CEF and fix close handling Add NebulaApplication (NSApplication) to implement isHandlingSendEvent/setHandlingSendEvent and override sendEvent to use CefScopedSendingEvent, preventing "unrecognized selector" crashes during CEF event/window teardown. Instantiate NebulaApplication in PrepareApp. Also relax the Windows-only guard in NebulaController::OnWindowCloseRequested and update the comment so per-tab closes on macOS don't trigger full app shutdown. Files changed: src/platform/mac/startup_mac.mm, src/app/nebula_controller.cpp. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/app/nebula_controller.cpp | 5 ++--- src/platform/mac/startup_mac.mm | 36 ++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/app/nebula_controller.cpp b/src/app/nebula_controller.cpp index 4c0f649..dc04581 100644 --- a/src/app/nebula_controller.cpp +++ b/src/app/nebula_controller.cpp @@ -241,13 +241,12 @@ void NebulaController::OnWindowResized(const nebula::window::BrowserLayout& layo } void NebulaController::OnWindowCloseRequested() { -#if defined(_WIN32) if (!closing_ && !closing_tab_browsers_.empty()) { // CEF Alloy can bubble a child browser close as WM_CLOSE on the host - // window. Per-tab closes should not turn into full app shutdown. + // window (Windows) or performClose: on the host window (OS X). Per-tab + // closes should not turn into full app shutdown. return; } -#endif if (launch_options_.mode == AppMode::BigPicture && !closing_) { if (!SwitchToSiblingMode(AppMode::Desktop)) { diff --git a/src/platform/mac/startup_mac.mm b/src/platform/mac/startup_mac.mm index 9f2d5e9..fa177eb 100644 --- a/src/platform/mac/startup_mac.mm +++ b/src/platform/mac/startup_mac.mm @@ -8,9 +8,40 @@ #include #include +#include "include/cef_application_mac.h" #include "include/cef_command_line.h" #include "ui/paths.h" +// All CEF client applications must subclass NSApplication and implement the +// CefAppProtocol. CEF's message pump and window teardown code calls +// -[NSApplication isHandlingSendEvent:]/setHandlingSendEvent: (through +// CefScopedSendingEvent) while dispatching events. Against the plain +// NSApplication those selectors do not exist, so any code path that reaches +// them (for example closing a tab while a browser view is being torn down) +// raises "unrecognized selector sent to NSApplication" and crashes. +@interface NebulaApplication : NSApplication { + @private + BOOL handling_send_event_; +} +@end + +@implementation NebulaApplication + +- (BOOL)isHandlingSendEvent { + return handling_send_event_; +} + +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent { + handling_send_event_ = handlingSendEvent; +} + +- (void)sendEvent:(NSEvent*)event { + CefScopedSendingEvent sending_event_scoper; + [super sendEvent:event]; +} + +@end + namespace nebula::platform { namespace { @@ -20,7 +51,10 @@ int g_single_instance_lock = -1; void PrepareApp() { @autoreleasepool { - [NSApplication sharedApplication]; + // Create the shared application as NebulaApplication (CefAppProtocol) + // before anything else touches NSApp so the CEF pipeline always sees + // the protocol methods it requires. + [NebulaApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp finishLaunching]; }