Repository: electron/electron
File: shell/browser/ui/cocoa/electron_ns_window.mm (Objective-C++)

Code before:

namespace electron {

int ScopedDisableResize::disable_resize_ = 0;

}  // namespace electron

@interface NSWindow (PrivateAPI)
- (int64_t)_resizeDirectionForMouseLocation:(CGPoint)location;
@end

// See components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@interface NSView (CRFrameViewAdditions)
- (void)cr_mouseDownOnFrameView:(NSEvent*)event;
@end

typedef void (*MouseDownImpl)(id, SEL, NSEvent*);

namespace {
MouseDownImpl g_nsthemeframe_mousedown;
MouseDownImpl g_nsnextstepframe_mousedown;
}  // namespace

// This class is never instantiated, it's just a container for our swizzled
// mouseDown method.
@interface SwizzledMethodsClass : NSView
@end

@implementation SwizzledMethodsClass
- (void)swiz_nsthemeframe_mouseDown:(NSEvent*)event {
  if ([self.window respondsToSelector:@selector(shell)]) {
    electron::NativeWindowMac* shell =
        (electron::NativeWindowMac*)[(id)self.window shell];
    if (shell && !shell->has_frame())
      [self cr_mouseDownOnFrameView:event];
    g_nsthemeframe_mousedown(self, @selector(mouseDown:), event);
  }
}

- (void)swiz_nsnextstepframe_mouseDown:(NSEvent*)event {
  if ([self.window respondsToSelector:@selector(shell)]) {
    electron::NativeWindowMac* shell =
        (electron::NativeWindowMac*)[(id)self.window shell];
    if (shell && !shell->has_frame()) {
      [self cr_mouseDownOnFrameView:event];
    }
    g_nsnextstepframe_mousedown(self, @selector(mouseDown:), event);
  }
}

- (void)swiz_nsview_swipeWithEvent:(NSEvent*)event {

Review comment (bug):
Some additional context from my own research for those curious:

Deep within AppKit there is a bug that throws an exception. I've been able to replicate the exception being thrown in native macOS apps like Terminal (that is, I attach LLDB, set a breakpoint on `objc_exception_throw`, open the tab overview, and click the + tile), so I don't think this is merely a third-party issue. Terminal, however, is somehow able to recover gracefully from this exception while we are not. As this exception is thrown deep within AppKit, it's not particularly easy to targetedly handle it on its own. It's unclear to me how Terminal does it, and if we can even replicate what it does.

Additionally, tabbing is weird in general on macOS. The "tabs" are really just windows that macOS internals display as tabs. Additionally, when playing around in Xcode, I found its really easy to create a tab handling setup that crashes in this same way (the + tile crashing the app), or has other bugs. Perhaps more engineering effort can be done to find a more "proper" setup that doesn't trigger this bug. However, to repeat, it's unclear if that is even possible.

While I'm not a fan of swizzling, it might be the most appropriate "easy" solution at this time. I still have yet to test this on my machine (still building), but I have trust that this does at least patch over the issue.

Code after:

namespace electron {

int ScopedDisableResize::disable_resize_ = 0;

}  // namespace electron

@interface NSWindow (PrivateAPI)
- (int64_t)_resizeDirectionForMouseLocation:(CGPoint)location;
@end

// See components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@interface NSView (CRFrameViewAdditions)
- (void)cr_mouseDownOnFrameView:(NSEvent*)event;
@end

typedef void (*MouseDownImpl)(id, SEL, NSEvent*);

// Work around an Apple bug where the visual tab picker's
// grid animation creates NSLayoutConstraints against nil layout anchors,
// crashing in NSVisualTabPickerShadowTileView. This happens when a new tabbed
// window is created while the tab picker is open — the "+" tile (and possibly
// others) have broken internal state. Rather than patching individual tile
// animation methods, short-circuit the entire grid animation by swizzling
// NSVisualTabPickerGridView's -startGridAnimation:completionHandler: to
// immediately invoke the completion handler without running the animation.
typedef void (*StartGridAnimationIMP)(id, SEL, id, id);
static StartGridAnimationIMP g_orig_startGridAnimation = nullptr;

static void Patched_startGridAnimation(id self,
                                       SEL _cmd,
                                       id animation,
                                       void (^completionHandler)(void)) {
  if (completionHandler)
    completionHandler();
}

static void SwizzleTabPickerGridAnimation() {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    Class cls = NSClassFromString(@"NSVisualTabPickerGridView");
    if (!cls)
      return;
    SEL sel = @selector(startGridAnimation:completionHandler:);
    Method method = class_getInstanceMethod(cls, sel);
    if (!method)
      return;
    g_orig_startGridAnimation =
        (StartGridAnimationIMP)method_getImplementation(method);
    method_setImplementation(method, (IMP)Patched_startGridAnimation);
  });