Skip to content

Fabric: mounting a child into a View with overflow hidden and a box shadow crashes with -[NSView insertSubview:atIndex:] #3154

Open

Description

@rajzik

Environment

  • react-native-macos 0.81.9, New Architecture (Fabric)
  • macOS 27.0.1, Apple silicon

Description

On macOS, a Fabric View whose style clips its overflow ink (overflow: 'hidden' together with a box shadow or an outline, see styleWouldClipOverflowInk) gets a separate container view from currentContainerView. That container is allocated as RCTPlatformView, which is a plain NSView on macOS:

// React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm
RCTPlatformView *_containerView; // [macOS]
...
_containerView = [[RCTPlatformView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; // [macOS]

mountChildComponentView:index: then sends the UIKit-style insertSubview:atIndex: to self.currentContainerView. Only RCTUIView implements that selector, so the next child mounted into such a view aborts the process:

NSInvalidArgumentException: -[NSView insertSubview:atIndex:]: unrecognized selector sent to instance
-[RCTViewComponentView mountChildComponentView:index:]  (RCTViewComponentView.mm:203)
RCTPerformMountInstructions
-[RCTMountingManager performTransaction:]

main still allocates RCTPlatformView here (packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm).

Steps to reproduce

  1. Render a View with overflow: 'hidden' and a boxShadow (or an outline).
  2. Mount a child into it after the container exists, for example by toggling a child on and off.
  3. The app aborts with the exception above.

We hit it through our app (a mail composer closing or sending) rather than a minimal project, so these steps are derived from the code path.

Suggested fix

Allocate the container as RCTUIView, which is what the currentContainerView getter already declares as its return type:

-  RCTPlatformView *_containerView; // [macOS]
+  RCTUIView *_containerView; // [macOS]
...
-      _containerView = [[RCTPlatformView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; // [macOS]
+      _containerView = [[RCTUIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; // [macOS]
...
       _containerView.clipsToBounds = self.clipsToBounds;
+      _containerView.layer.masksToBounds = self.clipsToBounds;

The explicit layer assignment preserves overflow clipping: RCTUIView synthesizes clipsToBounds without forwarding it to AppKit or updating masksToBounds.

We ship this as a pnpm patch of 0.81.9. With it, an XCUITest journey that opens and closes our composer 21 times in one process keeps the app running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions