From 6e0f8f1e49e4bca843cd6ed94d4cd5dea1d59932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 10 Nov 2026 03:40:01 -0800 Subject: [PATCH] Remove the native EventTarget event-dispatch flag (#59896) Summary: Remove `enableNativeEventTargetEventDispatching` and retain its enabled paths unconditionally. Preserve the separate `enableImperativeEvents` gate and existing test behavior. Test edits remove retired flag configuration and correct the typing required by the ungated `EventTarget` inheritance; no assertions or helper functions were added. Regenerate the public API snapshot for the permanent `EventTarget` inheritance. Changelog: [Internal] Reviewed By: zeyap Differential Revision: D130671804 --- packages/react-native/ReactNativeApi.d.ts | 332 +++++++++++------- .../ReactNativeFeatureFlags.config.js | 11 - .../featureflags/ReactNativeFeatureFlags.js | 8 +- .../EventDispatching-benchmark-itest.js | 1 - .../__tests__/EventTargetDispatching-itest.js | 128 +++---- .../__tests__/ResponderEventTarget-itest.js | 1 - .../renderer/events/dispatchNativeEvent.js | 3 +- .../private/webapis/dom/events/EventTarget.js | 93 ++--- .../__tests__/EventTarget-benchmark-itest.js | 1 - .../webapis/dom/nodes/ReactNativeElement.js | 34 +- .../private/webapis/dom/nodes/ReadOnlyNode.js | 11 +- .../private/webapis/dom/nodes/ReadOnlyText.js | 34 +- .../__tests__/ReactNativeElement-itest.js | 23 +- ...NativeElement-traversal-benchmark-itest.js | 1 - .../dom/nodes/__tests__/ReadOnlyText-itest.js | 18 +- 15 files changed, 347 insertions(+), 352 deletions(-) diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 11a5302585ba..44cf53314e67 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<59570f49ad456d1306656541407d4293>> + * @generated SignedSource<<6ee0da046817c3326773621f8330ade3>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -343,7 +343,6 @@ declare const RCTNetworking_default: { ): void } declare let ReactFabric_default: ReactFabricType -declare const ReadOnlyNodeBase: typeof Object declare const registerCallableModule: typeof registerCallableModule_default declare const registerCallableModule_default: RegisterCallableModule declare const requireNativeComponent: typeof requireNativeComponent_default @@ -908,6 +907,16 @@ declare type $$index = typeof $$index declare type $$NativeDeviceInfo = typeof $$NativeDeviceInfo declare type $$ReactFabric = typeof $$ReactFabric declare type $$ScrollViewContext = typeof $$ScrollViewContext +declare class AbortSignal extends EventTarget_default { + static abort(reason: unknown): AbortSignal + get aborted(): boolean + static any(signals: AbortSignal[]): AbortSignal + get onabort(): EventCallback | null + set onabort(listener: EventCallback | null | undefined) + get reason(): unknown + throwIfAborted(): void + static timeout(timeInMs: number): AbortSignal +} declare type absoluteFill = typeof absoluteFill declare type AbsoluteFillStyle = { readonly bottom: 0 @@ -1105,6 +1114,13 @@ declare type add = typeof add declare function addChangeListener( listener: (preferences: AppearancePreferences) => void, ): EventSubscription +declare type AddEventListenerOptions = Readonly< + EventListenerOptions & { + once?: boolean + passive?: boolean + signal?: AbortSignal + } +> declare class Alert { static alert( title: null | string | undefined, @@ -2079,12 +2095,41 @@ declare type ErrorUtils = { setGlobalHandler(fun: ErrorHandler): void } declare type event = typeof event +declare class Event_default { + static readonly AT_TARGET: 2 + readonly AT_TARGET: 2 + static readonly BUBBLING_PHASE: 3 + readonly BUBBLING_PHASE: 3 + static readonly CAPTURING_PHASE: 1 + readonly CAPTURING_PHASE: 1 + static readonly NONE: 0 + readonly NONE: 0 + get bubbles(): boolean + get cancelable(): boolean + get cancelBubble(): boolean + set cancelBubble(value: boolean) + get composed(): boolean + composedPath(): ReadonlyArray + constructor(type: string, options?: EventInit | null | undefined) + get currentTarget(): EventTarget_default | null + get defaultPrevented(): boolean + get eventPhase(): EventPhase + get isTrusted(): boolean + preventDefault(): void + get rnIsDirect(): boolean + stopImmediatePropagation(): void + stopPropagation(): void + get target(): EventTarget_default | null + get timeStamp(): number + get type(): string +} declare type EventArgs = | null | undefined | { [key: string]: string } +declare type EventCallback = (event: Event_default) => void declare type EventConfig = { listener?: (v: NativeSyntheticEvent) => unknown platformConfig?: PlatformConfig @@ -2113,10 +2158,41 @@ declare class EventEmitter< declare type EventEmitter_2 = ( handler: (v: T) => Promise | void, ) => EventSubscription +declare type EventHandler = { + handleEvent(event: Event_default): void +} +declare interface EventInit { + readonly bubbles?: boolean + readonly cancelable?: boolean + readonly composed?: boolean + readonly rnIsDirect?: boolean +} +declare type EventListener = EventCallback | EventHandler +declare type EventListenerOptions = { + readonly capture?: boolean +} declare type EventName = (() => string) | string +declare type EventPhase = + | (typeof Event_default)["AT_TARGET"] + | (typeof Event_default)["BUBBLING_PHASE"] + | (typeof Event_default)["CAPTURING_PHASE"] + | (typeof Event_default)["NONE"] declare interface EventSubscription { remove(): void } +declare class EventTarget_default { + addEventListener( + type: string, + callback: EventListener | null, + optionsOrUseCapture?: AddEventListenerOptions | boolean, + ): void + dispatchEvent(event: Event_default): boolean + removeEventListener( + type: string, + callback: EventListener, + optionsOrUseCapture?: boolean | EventListenerOptions, + ): void +} declare type ExceptionData = { componentStack: string | undefined extraData?: Object @@ -3958,7 +4034,7 @@ declare class ReadOnlyElement_default extends ReadOnlyNode_default { get tagName(): string get textContent(): string } -declare class ReadOnlyNode_default extends ReadOnlyNodeBase { +declare class ReadOnlyNode_default extends EventTarget_default { static ATTRIBUTE_NODE: number static CDATA_SECTION_NODE: number static COMMENT_NODE: number @@ -5765,31 +5841,31 @@ declare type WrapperComponentProvider = ( appKey?: string, ) => React.ComponentType export { - AccessibilityActionEvent, // a276ec6e + AccessibilityActionEvent, // 9c114ac5 AccessibilityActionInfo, // c94545a7 - AccessibilityInfo, // c4a3b4ed + AccessibilityInfo, // 5ccdc096 AccessibilityProps, // 95dbb1d8 AccessibilityRole, // f2f2e066 AccessibilityState, // b0c2b3f7 AccessibilityValue, // cf8bcb74 ActionSheetIOS, // b558559e ActionSheetIOSOptions, // 1756eb5a - ActivityIndicator, // d5bcb272 - ActivityIndicatorInstance, // 1b5e0dd5 - ActivityIndicatorProps, // 508a24c6 + ActivityIndicator, // b9f6c361 + ActivityIndicatorInstance, // 203a7308 + ActivityIndicatorProps, // 45a4dd7e Alert, // a398a509 AlertButton, // bf1a3b60 AlertButtonStyle, // ec9fb242 AlertOptions, // 8a116d2a AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // ab3b6335 - AnimatedFlatList, // 8a120aa8 - AnimatedImage, // 91126d6f - AnimatedScrollView, // 5a621f5a - AnimatedSectionList, // eeb7442a - AnimatedText, // c710a0a7 - AnimatedView, // 1ba76ffd + Animated, // f1e490c6 + AnimatedFlatList, // 7dd37b2f + AnimatedImage, // fd235a5f + AnimatedScrollView, // 6e7d4228 + AnimatedSectionList, // ae393fe9 + AnimatedText, // 55bc68a2 + AnimatedView, // ed267c3c AppConfig, // 80afc5fa AppRegistry, // b906537d AppState, // db34d225 @@ -5801,12 +5877,12 @@ export { AutoCapitalize, // c0e857a0 BackHandler, // f139fc69 BackPressEventName, // 4620fb76 - BlurEvent, // d5e68e96 + BlurEvent, // 81e2d52d BoxShadowValue, // b679703f - Button, // 0abcdf75 - ButtonInstance, // 64b68c93 - ButtonProps, // 9886bcc6 - CodegenTypes, // 4aca14ff + Button, // a8154deb + ButtonInstance, // 2171b004 + ButtonProps, // 39ba1271 + CodegenTypes, // 8225fbd1 ColorSchemeName, // 6615edd6 ColorValue, // 98989a8f ComponentProvider, // bf78f91b @@ -5822,10 +5898,10 @@ export { DimensionsPayload, // 653bc26c DisplayMetrics, // 1dc35cef DisplayMetricsAndroid, // 872e62eb - DrawerLayoutAndroid, // beec8621 + DrawerLayoutAndroid, // e17f5c7f DrawerLayoutAndroidInstance, // aa243cbf - DrawerLayoutAndroidProps, // 53289373 - DrawerSlideEvent, // 38213685 + DrawerLayoutAndroidProps, // 888a0cd8 + DrawerSlideEvent, // 4fa8f7ff DropShadowValue, // e9df2606 DynamicColorIOS, // d96c228c DynamicColorIOSTuple, // 023ce58e @@ -5840,31 +5916,31 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // bf3b6bb5 - FlatListInstance, // 715f033d - FlatListProps, // 72d330d2 - FocusEvent, // f2e4363d + FlatList, // 93582d36 + FlatListInstance, // 44658d70 + FlatListProps, // 9bd2f266 + FocusEvent, // eaa2af9d FontVariant, // 7c7558bb - GestureResponderEvent, // 5cd2a663 - GestureResponderHandlers, // c67c25e0 - HostComponent, // 22c9ea3d - HostInstance, // 17bb0a55 + GestureResponderEvent, // 8a8611e2 + GestureResponderHandlers, // 27e135ff + HostComponent, // 4de3b110 + HostInstance, // ba280920 I18nManager, // f9870e00 IEventEmitter, // fbef6131 IOSKeyboardEvent, // e67bfe3a IgnorePattern, // ec6f6ece - Image, // 9aebdb1b - ImageBackground, // d197b60d - ImageBackgroundInstance, // 2e7c380e - ImageBackgroundProps, // 910aaf4a - ImageErrorEvent, // b6227714 - ImageInstance, // df709524 - ImageLoadEvent, // 31709c38 - ImageProgressEventIOS, // 9243d460 - ImageProps, // a5ad5e76 + Image, // 40dabe90 + ImageBackground, // 8cba11e4 + ImageBackgroundInstance, // 059f07eb + ImageBackgroundProps, // 9256cb15 + ImageErrorEvent, // 91b7bd15 + ImageInstance, // f5ca8548 + ImageLoadEvent, // 26c72f36 + ImageProgressEventIOS, // 78694e24 + ImageProps, // d0584850 ImagePropsAndroid, // 283349a4 - ImagePropsBase, // bd51e73b - ImagePropsIOS, // 3baac578 + ImagePropsBase, // 6203d7f7 + ImagePropsIOS, // ebd30376 ImageRequireSource, // 681d683b ImageResizeMode, // d51106e2 ImageResolvedAssetSource, // f3060931 @@ -5877,13 +5953,13 @@ export { InputAccessoryViewProps, // 73685b83 InputModeOptions, // 4e8581b9 Insets, // e7fe432a - KeyDownEvent, // a12d46ea + KeyDownEvent, // cc4773b1 KeyEvent, // 20fa4267 - KeyUpEvent, // 0b5d0144 + KeyUpEvent, // c4e5a70b Keyboard, // a26f7bd4 - KeyboardAvoidingView, // 1fee106e - KeyboardAvoidingViewInstance, // 26062fd4 - KeyboardAvoidingViewProps, // 635ae7fc + KeyboardAvoidingView, // 668b6171 + KeyboardAvoidingViewInstance, // ce03459c + KeyboardAvoidingViewProps, // 53cc84d8 KeyboardEvent, // c3f895d4 KeyboardEventEasing, // af4091c8 KeyboardEventName, // 59299ad6 @@ -5896,7 +5972,7 @@ export { LayoutAnimationProperty, // 52995f01 LayoutAnimationType, // 2da0a29b LayoutAnimationTypes, // 081b3bde - LayoutChangeEvent, // 9e7d74fd + LayoutChangeEvent, // a28c1eb1 LayoutConformanceProps, // 055f03b8 LayoutRectangle, // 6601b294 Linking, // 73c2b170 @@ -5908,33 +5984,33 @@ export { MeasureInWindowOnSuccessCallback, // a285f598 MeasureLayoutOnSuccessCallback, // 3592502a MeasureOnSuccessCallback, // 82824e59 - Modal, // f4b88be5 - ModalBaseProps, // a6e4334f - ModalInstance, // 0f04c1bf - ModalProps, // c07a247a + Modal, // d422cbdb + ModalBaseProps, // fe84ee7f + ModalInstance, // e66062fb + ModalProps, // ee71f2ac ModalPropsAndroid, // 515fb173 - ModalPropsIOS, // a65a8f5c - ModeChangeEvent, // ccf30dc4 - MouseEvent, // ac7f3af7 + ModalPropsIOS, // 2b335b15 + ModeChangeEvent, // 9ccef558 + MouseEvent, // 9384296b NativeAppEventEmitter, // 08d4c47d NativeColorValue, // d2094c29 - NativeComponentRegistry, // 3f499295 + NativeComponentRegistry, // e6c5a348 NativeEventEmitter, // 27f97c1a NativeEventSubscription, // de3942e7 NativeModules, // 4597cd36 - NativeMouseEvent, // 35158274 - NativePointerEvent, // dd1165e1 + NativeMouseEvent, // 7c2bc26f + NativePointerEvent, // 1af89ea3 NativeScrollEvent, // caad7f53 - NativeSyntheticEvent, // e201ef20 + NativeSyntheticEvent, // 0ece11ce NativeTouchEvent, // 59b676df NativeUIEvent, // 44ac26ac Networking, // 65945401 OpaqueColorValue, // 25f3fa5b PackagerAsset, // d1c88cf4 - PanResponder, // bd82bf2d - PanResponderCallbacks, // 8f0a8183 + PanResponder, // d5320cc2 + PanResponderCallbacks, // 6028170f PanResponderGestureState, // 54baf558 - PanResponderInstance, // 7231229d + PanResponderInstance, // 542547cf Permission, // 08f1c82f PermissionStatus, // 4b7de97b PermissionsAndroid, // 8a0bc8d8 @@ -5944,32 +6020,32 @@ export { PlatformOSType, // 0a17561e PlatformSelectSpec, // 09ed7758 PointValue, // 69db075f - PointerEvent, // a506d9d7 - PressabilityConfig, // 0fd655fe - PressabilityEventHandlers, // be359ba3 - Pressable, // 56235a5f + PointerEvent, // 06310bdb + PressabilityConfig, // ed721435 + PressabilityEventHandlers, // 314a0b05 + Pressable, // b430b931 PressableAndroidRippleConfig, // ee32eaca - PressableInstance, // ed26f040 - PressableProps, // 7a18f8cd + PressableInstance, // a13c73cb + PressableProps, // 5f06b483 PressableStateCallbackType, // f83728cd ProcessedColorValue, // 33f74304 - ProgressBarAndroid, // 30b4a1eb - ProgressBarAndroidInstance, // f17242be - ProgressBarAndroidProps, // 98fa6a86 + ProgressBarAndroid, // 7d199258 + ProgressBarAndroidInstance, // e25bcc95 + ProgressBarAndroidProps, // 07593242 PublicRootInstance, // 8040afd7 - PublicTextInstance, // f9adf69a + PublicTextInstance, // 42c897b3 PushNotificationEventName, // 84e7e150 PushNotificationIOS, // b4d1fe78 PushNotificationPermissions, // c2e7ae4f Rationale, // 5df1b1c1 ReactNativeVersion, // abd76827 - RefreshControl, // e74ab0c4 - RefreshControlInstance, // 7e1aa88d - RefreshControlProps, // e728e273 + RefreshControl, // db42b24c + RefreshControlInstance, // fd5c0365 + RefreshControlProps, // e839b56d RefreshControlPropsAndroid, // 8ac931ca RefreshControlPropsIOS, // 72a36381 Registry, // e7beb42e - ResponderSyntheticEvent, // f5da3211 + ResponderSyntheticEvent, // 7bb455c7 ReturnKeyTypeOptions, // afd47ba3 Role, // af7b889d RootTag, // 3cd10504 @@ -5978,25 +6054,25 @@ export { Runnable, // 557d13d9 Runnables, // 75114853 SafeAreaInsets, // 64d7a605 - SafeAreaInsetsChangeEvent, // 4ffd35e5 - SafeAreaView, // dda99293 - SafeAreaViewInstance, // 2b5d4391 + SafeAreaInsetsChangeEvent, // e63da69b + SafeAreaView, // c3188b34 + SafeAreaViewInstance, // 620923d2 ScaledSize, // 07e417c7 - ScrollEvent, // 956fdb27 - ScrollResponderType, // 52c38666 + ScrollEvent, // 5b300f75 + ScrollResponderType, // d454c555 ScrollToLocationParamsType, // d7ecdad1 - ScrollView, // 6d572294 - ScrollViewImperativeMethods, // 682315af - ScrollViewInstance, // bcc836b9 - ScrollViewProps, // 405604c7 + ScrollView, // ded391d4 + ScrollViewImperativeMethods, // bbff2b8c + ScrollViewInstance, // a59c167a + ScrollViewProps, // 0f5f149e ScrollViewPropsAndroid, // 02f3df2e - ScrollViewPropsIOS, // c378379b + ScrollViewPropsIOS, // b2cf82e0 ScrollViewScrollToOptions, // 3313411e SectionBase, // 9f13db00 - SectionList, // c04ef194 + SectionList, // 0c0b716d SectionListData, // 1a4de01a - SectionListInstance, // c18e8111 - SectionListProps, // 6a531c1d + SectionListInstance, // 2ce6ff23 + SectionListProps, // 270e3f29 SectionListRenderItem, // 715b2086 SectionListRenderItemInfo, // 4a48a922 Separators, // 6a45f7e3 @@ -6015,55 +6091,55 @@ export { StyleProp, // fa0e9b4a StyleSheet, // cf156a8b SubmitBehavior, // c4ddf490 - Switch, // 7ea8a527 - SwitchChangeEvent, // 3b472666 - SwitchInstance, // 73dce99f - SwitchProps, // aa00dcf4 + Switch, // b810ece4 + SwitchChangeEvent, // 4d12c11f + SwitchInstance, // 389a240b + SwitchProps, // c86198ca Systrace, // 2a0a0871 TVViewPropsIOS, // 1c612e4e TargetedEvent, // 16e98910 TaskProvider, // 266dedf2 - Text, // 935ec305 + Text, // 841fd00c TextContentType, // 239b3ecc - TextInput, // cf6467dc + TextInput, // f4502971 TextInputAndroidProps, // 9ebbc103 - TextInputBlurEvent, // b4c7a58c - TextInputChangeEvent, // 92363e10 - TextInputContentSizeChangeEvent, // 1fe0e260 - TextInputEndEditingEvent, // 7aa9093a - TextInputFocusEvent, // ef604040 + TextInputBlurEvent, // c04c4bf0 + TextInputChangeEvent, // ed7f8b04 + TextInputContentSizeChangeEvent, // fb5ec129 + TextInputEndEditingEvent, // bfb401fc + TextInputFocusEvent, // eec98df4 TextInputIOSProps, // fb3c9327 - TextInputInstance, // 53fe7b73 - TextInputKeyPressEvent, // cbbc322a - TextInputProps, // bb087c92 - TextInputSelectionChangeEvent, // 4a8f10c2 - TextInputSubmitEditingEvent, // e811fdbd - TextInstance, // 7c7e2069 - TextLayoutEvent, // 1a21e24a - TextProps, // b3707dfc + TextInputInstance, // d442c53f + TextInputKeyPressEvent, // 07548cd0 + TextInputProps, // 0166e0c9 + TextInputSelectionChangeEvent, // 131f3be5 + TextInputSubmitEditingEvent, // f2ad5e3b + TextInstance, // 5d4340ff + TextLayoutEvent, // bcd28f9a + TextProps, // 78881b55 TextStyle, // 898adfd1 ToastAndroid, // 88a8969a - TouchableHighlight, // a015710f - TouchableHighlightInstance, // 811af7ca - TouchableHighlightProps, // faaeb188 - TouchableNativeFeedback, // 744ee0ee - TouchableNativeFeedbackInstance, // b90e3bae - TouchableNativeFeedbackProps, // 39abde16 - TouchableOpacity, // 1125dbb7 - TouchableOpacityInstance, // 0cd8a347 - TouchableOpacityProps, // c126e610 - TouchableWithoutFeedback, // 92cfc35d - TouchableWithoutFeedbackProps, // 7e8a325c + TouchableHighlight, // c355bdc0 + TouchableHighlightInstance, // 86235e9b + TouchableHighlightProps, // 9d69cf73 + TouchableNativeFeedback, // aa19af38 + TouchableNativeFeedbackInstance, // 063cefb1 + TouchableNativeFeedbackProps, // 8c37965a + TouchableOpacity, // f7c31bda + TouchableOpacityInstance, // 174a3ad9 + TouchableOpacityProps, // 839b5f73 + TouchableWithoutFeedback, // 3371c1e0 + TouchableWithoutFeedbackProps, // 86d9d4ef TransformsStyle, // 65e70f18 TurboModule, // dfe29706 TurboModuleRegistry, // 4ace6db2 UIManager, // 6a18fc05 UTFSequence, // ad625158 Vibration, // 31e4bbf8 - View, // 85575778 - ViewInstance, // 0283d2fc - ViewProps, // 43cbabe5 - ViewPropsAndroid, // 66d760c6 + View, // 37dd3f10 + ViewInstance, // da166ce1 + ViewProps, // 0a38b735 + ViewPropsAndroid, // 66397d7f ViewPropsIOS, // 58ee19bf ViewStyle, // 4e2746d6 ViewabilityConfig, // eb3f7ed8 @@ -6071,22 +6147,22 @@ export { VirtualViewMode, // 6be59722 VirtualizedList, // 68c7345e VirtualizedListInstance, // 423ee7c0 - VirtualizedListProps, // 9a26f0e0 + VirtualizedListProps, // 7eb90be3 VirtualizedSectionList, // 9fd9cd61 VirtualizedSectionListInstance, // 12b706d5 - VirtualizedSectionListProps, // c00a3bac + VirtualizedSectionListProps, // 645dfd17 WrapperComponentProvider, // 6ce47ba4 codegenNativeCommands, // 322f3f4e - codegenNativeComponent, // 3da62daa + codegenNativeComponent, // c9cccb03 findNodeHandle, // 93f80214 processColor, // 6e877698 registerCallableModule, // 55bda579 - requireNativeComponent, // da1880ff + requireNativeComponent, // 20fb4b1e useAnimatedColor, // 1faf88ce useAnimatedValue, // dc9d6f5c useAnimatedValueXY, // dc9057ea useAppState, // 9338748a useColorScheme, // d585efdb - usePressability, // af291ad3 + usePressability, // 0b21f83a useWindowDimensions, // bb4b683f } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index e735c509a92d..f0ff30b96a84 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -1160,17 +1160,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - enableNativeEventTargetEventDispatching: { - defaultValue: true, - metadata: { - dateAdded: '2026-04-13', - description: - 'When enabled, the React Native renderer dispatches events through the W3C EventTarget API (addEventListener/dispatchEvent) instead of the legacy plugin-based system.', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, externalElementInspectionEnabled: { defaultValue: true, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 3d0282fc66ac..e3659e8f79ba 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<248a6701e98d6850499d84dd5c2bff5f>> + * @generated SignedSource<<715bdce4ff85be24d125b1c894da41d8>> * @flow strict * @noformat */ @@ -34,7 +34,6 @@ export type ReactNativeFeatureFlagsJsOnly = Readonly<{ animatedKeepListenersOnDetach: Getter, animatedShouldSyncValueBeforeStartCallback: Getter, deferFlatListFocusChangeRenderUpdate: Getter, - enableNativeEventTargetEventDispatching: Getter, externalElementInspectionEnabled: Getter, fixCrossOrientationNestedListViewability: Getter, fixVirtualizeListCollapseWindowSize: Getter, @@ -179,11 +178,6 @@ export const animatedShouldSyncValueBeforeStartCallback: Getter = creat */ export const deferFlatListFocusChangeRenderUpdate: Getter = createJavaScriptFlagGetter('deferFlatListFocusChangeRenderUpdate', false); -/** - * When enabled, the React Native renderer dispatches events through the W3C EventTarget API (addEventListener/dispatchEvent) instead of the legacy plugin-based system. - */ -export const enableNativeEventTargetEventDispatching: Getter = createJavaScriptFlagGetter('enableNativeEventTargetEventDispatching', true); - /** * Enable the external inspection API for DevTools to communicate with the Inspector overlay. */ diff --git a/packages/react-native/src/private/renderer/core/__tests__/EventDispatching-benchmark-itest.js b/packages/react-native/src/private/renderer/core/__tests__/EventDispatching-benchmark-itest.js index c5955a43bd84..6dcb057947f0 100644 --- a/packages/react-native/src/private/renderer/core/__tests__/EventDispatching-benchmark-itest.js +++ b/packages/react-native/src/private/renderer/core/__tests__/EventDispatching-benchmark-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:* * @flow strict-local * @format */ diff --git a/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js b/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js index 4c6841b60e26..b8010d4f15c2 100644 --- a/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js +++ b/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:* * @fantom_flags enableImperativeEvents:* * @flow strict-local * @format @@ -480,13 +479,10 @@ describe('EventTarget-based Event Dispatching', () => { }); // --- addEventListener / removeEventListener on refs --- - // These tests require both `enableNativeEventTargetEventDispatching` and - // `enableImperativeEvents` to be enabled, since the public `addEventListener` - // API on element refs is only available when both flags are on. They are - // skipped for the other flag combinations. + // These tests require `enableImperativeEvents` to be enabled, since the + // public `addEventListener` API on element refs is only available then. - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - ReactNativeFeatureFlags.enableImperativeEvents() + (ReactNativeFeatureFlags.enableImperativeEvents() === true ? describe : describe.skip)('addEventListener / removeEventListener', () => { it('addEventListener on a ref receives dispatched events', () => { @@ -869,74 +865,66 @@ describe('EventTarget-based Event Dispatching', () => { } }); - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() - ? it - : it.skip)( - 'event.target points to the original target and event.currentTarget changes at each step', - () => { - const root = Fantom.createRoot(); - const parentRef = React.createRef>(); - const childRef = React.createRef>(); - const targets: Array<{target: unknown, currentTarget: unknown}> = []; + it('event.target points to the original target and event.currentTarget changes at each step', () => { + const root = Fantom.createRoot(); + const parentRef = React.createRef>(); + const childRef = React.createRef>(); + const targets: Array<{target: unknown, currentTarget: unknown}> = []; - Fantom.runTask(() => { - root.render( + Fantom.runTask(() => { + root.render( + { + targets.push({ + target: e.target, + currentTarget: e.currentTarget, + }); + }} + onPointerUp={(e: $FlowFixMe) => { + targets.push({ + target: e.target, + currentTarget: e.currentTarget, + }); + }}> { - targets.push({ - target: e.target, - currentTarget: e.currentTarget, - }); - }} + ref={childRef} onPointerUp={(e: $FlowFixMe) => { targets.push({ target: e.target, currentTarget: e.currentTarget, }); - }}> - { - targets.push({ - target: e.target, - currentTarget: e.currentTarget, - }); - }} - /> - , - ); - }); - - Fantom.dispatchNativeEvent( - childRef, - 'onPointerUp', - {x: 0, y: 0}, - { - category: Fantom.NativeEventCategory.Discrete, - }, + }} + /> + , ); + }); - expect(targets).toHaveLength(3); + Fantom.dispatchNativeEvent( + childRef, + 'onPointerUp', + {x: 0, y: 0}, + { + category: Fantom.NativeEventCategory.Discrete, + }, + ); - // event.target is always the original target element - expect(targets[0].target).toBe(childRef.current); - expect(targets[1].target).toBe(childRef.current); - expect(targets[2].target).toBe(childRef.current); + expect(targets).toHaveLength(3); - // event.currentTarget changes at each propagation step - // Capture: parent - expect(targets[0].currentTarget).toBe(parentRef.current); - // Bubble: child, then parent - expect(targets[1].currentTarget).toBe(childRef.current); - expect(targets[2].currentTarget).toBe(parentRef.current); - }, - ); + // event.target is always the original target element + expect(targets[0].target).toBe(childRef.current); + expect(targets[1].target).toBe(childRef.current); + expect(targets[2].target).toBe(childRef.current); - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - ReactNativeFeatureFlags.enableImperativeEvents() - ? it - : it.skip)( + // event.currentTarget changes at each propagation step + // Capture: parent + expect(targets[0].currentTarget).toBe(parentRef.current); + // Bubble: child, then parent + expect(targets[1].currentTarget).toBe(childRef.current); + expect(targets[2].currentTarget).toBe(parentRef.current); + }); + + (ReactNativeFeatureFlags.enableImperativeEvents() === true ? it : it.skip)( 'direct (non-bubbling) events do not propagate via addEventListener', () => { const root = Fantom.createRoot(); @@ -983,8 +971,7 @@ describe('EventTarget-based Event Dispatching', () => { }, ); - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - ReactNativeFeatureFlags.enableImperativeEvents() + (ReactNativeFeatureFlags.enableImperativeEvents() === true ? describe : describe.skip)('bubbling to document element and document', () => { it('event bubbles from child up to the document element', () => { @@ -1374,9 +1361,7 @@ describe('EventTarget-based Event Dispatching', () => { }); }); - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() - ? describe - : describe.skip)('event timestamps', () => { + describe('event timestamps', () => { it('event preserves native timestamp from nativeEvent.timeStamp', () => { const root = Fantom.createRoot(); const ref = React.createRef>(); @@ -1661,11 +1646,8 @@ describe('EventTarget-based Event Dispatching', () => { }); // The dispatch-behavior tests use `addEventListener` on refs and the - // document element, which is only available when both - // `enableNativeEventTargetEventDispatching` and `enableImperativeEvents` - // are enabled. - (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - ReactNativeFeatureFlags.enableImperativeEvents() + // document element, which requires `enableImperativeEvents` to be enabled. + (ReactNativeFeatureFlags.enableImperativeEvents() === true ? describe : describe.skip)('direct events (rnIsDirect) — dispatch behavior', () => { it('dispatches onLayout to the target but never to ancestor onLayout props or bubble listeners', () => { diff --git a/packages/react-native/src/private/renderer/core/__tests__/ResponderEventTarget-itest.js b/packages/react-native/src/private/renderer/core/__tests__/ResponderEventTarget-itest.js index 5fe4be506587..f9b3a294da84 100644 --- a/packages/react-native/src/private/renderer/core/__tests__/ResponderEventTarget-itest.js +++ b/packages/react-native/src/private/renderer/core/__tests__/ResponderEventTarget-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:* * @flow strict-local * @format */ diff --git a/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js b/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js index 0deb93576773..77ec770750a0 100644 --- a/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js +++ b/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js @@ -33,8 +33,7 @@ import { * 1. Responder negotiation (touch handling, grant/release lifecycle) * 2. Normal event dispatch via dispatchTrustedEvent (capture/bubble phases) * - * Called from the React renderer's dispatchEvent when - * enableNativeEventTargetEventDispatching is enabled. + * Called from the React renderer's dispatchEvent. */ export default function dispatchNativeEvent( target: ?EventTarget, diff --git a/packages/react-native/src/private/webapis/dom/events/EventTarget.js b/packages/react-native/src/private/webapis/dom/events/EventTarget.js index 936626aac150..2487d1e54e6a 100644 --- a/packages/react-native/src/private/webapis/dom/events/EventTarget.js +++ b/packages/react-native/src/private/webapis/dom/events/EventTarget.js @@ -13,9 +13,9 @@ * See https://dom.spec.whatwg.org/#interface-eventtarget. */ +import type {AbortSignal as InternalAbortSignal} from '../abort-api/AbortSignal'; import type {EventPhase} from './Event'; -import * as ReactNativeFeatureFlags from '../../../featureflags/ReactNativeFeatureFlags'; import {setPlatformObject} from '../../webidl/PlatformObjects'; import Event from './Event'; import { @@ -51,7 +51,7 @@ export type AddEventListenerOptions = Readonly<{ ...EventListenerOptions, passive?: boolean, once?: boolean, - signal?: AbortSignal, + signal?: InternalAbortSignal, }>; type EventListenerRegistration = { @@ -403,66 +403,49 @@ function invoke( setCurrentTarget(event, eventTarget); - if (ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching()) { - // Resolve the prop-based listener. Pass the event so subclasses can read - // its `type` and any pre-resolved internal slots; pass `isCapture` - // separately because it can't be derived from `event.eventPhase` (which - // is `AT_TARGET` during both passes through the target node). - // $FlowExpectedError[prop-missing] - const propListener: EventCallback | null = eventTarget[ - EVENT_TARGET_GET_DECLARATIVE_LISTENER_KEY - ](event, isCapture); - - const listenersByType = getListenersForPhase(eventTarget, isCapture); - const maybeListeners = listenersByType?.get(event.type); - - // Fast path: only a prop listener (no `addEventListener` listeners). - // This is the overwhelmingly common case for React-driven dispatch. - if (maybeListeners == null) { - if (propListener == null) { - return; - } - const currentEvent = global.event; - global.event = event; - try { - propListener.call(eventTarget, event); - } catch (error) { - handleListenerError(error, errorState); - } - global.event = currentEvent; - return; - } + // Resolve the prop-based listener. Pass the event so subclasses can read + // its `type` and any pre-resolved internal slots; pass `isCapture` + // separately because it can't be derived from `event.eventPhase` (which + // is `AT_TARGET` during both passes through the target node). + // $FlowExpectedError[prop-missing] + const propListener: EventCallback | null = eventTarget[ + EVENT_TARGET_GET_DECLARATIVE_LISTENER_KEY + ](event, isCapture); - // Slow path: combine prop listener + addEventListener listeners. - const listeners: Array = []; - if (propListener != null) { - listeners.push({ - callback: propListener, - passive: false, - once: false, - removed: false, - }); + const listenersByType = getListenersForPhase(eventTarget, isCapture); + const maybeListeners = listenersByType?.get(event.type); + + // Fast path: only a prop listener (no `addEventListener` listeners). + // This is the overwhelmingly common case for React-driven dispatch. + if (maybeListeners == null) { + if (propListener == null) { + return; } - for (const registration of maybeListeners.values()) { - listeners.push(registration); + const currentEvent = global.event; + global.event = event; + try { + propListener.call(eventTarget, event); + } catch (error) { + handleListenerError(error, errorState); } - invokeListeners(eventTarget, event, listeners, isCapture, errorState); + global.event = currentEvent; return; } - // Legacy path (flag OFF): only `addEventListener` listeners. - const listenersByType = getListenersForPhase(eventTarget, isCapture); - const maybeListeners = listenersByType?.get(event.type); - if (maybeListeners == null) { - return; + // Slow path: combine prop listener + addEventListener listeners. + const listeners: Array = []; + if (propListener != null) { + listeners.push({ + callback: propListener, + passive: false, + once: false, + removed: false, + }); + } + for (const registration of maybeListeners.values()) { + listeners.push(registration); } - invokeListeners( - eventTarget, - event, - Array.from(maybeListeners.values()), - isCapture, - errorState, - ); + invokeListeners(eventTarget, event, listeners, isCapture, errorState); } function invokeListeners( diff --git a/packages/react-native/src/private/webapis/dom/events/__tests__/EventTarget-benchmark-itest.js b/packages/react-native/src/private/webapis/dom/events/__tests__/EventTarget-benchmark-itest.js index c8769794e63d..a23a755038fa 100644 --- a/packages/react-native/src/private/webapis/dom/events/__tests__/EventTarget-benchmark-itest.js +++ b/packages/react-native/src/private/webapis/dom/events/__tests__/EventTarget-benchmark-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:* * @flow strict-local * @format */ diff --git a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js index 2ba8b0df8b38..dfcfb73eafc4 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js +++ b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeElement.js @@ -357,22 +357,20 @@ export const ReactNativeElement_public: typeof ReactNativeElement = ReactNativeElement_public.prototype = ReactNativeElement.prototype; // The public imperative EventTarget API (`addEventListener`, -// `removeEventListener`, `dispatchEvent`) is only inherited by this final class -// when `enableNativeEventTargetEventDispatching` is enabled (which makes -// `ReadOnlyNode` extend `EventTarget`). Until that public API is finalized, it -// is gated behind `enableImperativeEvents`: when off we remove those methods -// from this final class. Native/internal event dispatch does not rely on these -// public methods, so removing them is safe. -if ( - ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - !ReactNativeFeatureFlags.enableImperativeEvents() -) { - const prototype: interface { - addEventListener?: unknown, - removeEventListener?: unknown, - dispatchEvent?: unknown, - } = ReactNativeElement.prototype; - prototype.addEventListener = undefined; - prototype.removeEventListener = undefined; - prototype.dispatchEvent = undefined; +// `removeEventListener`, `dispatchEvent`) is gated behind +// `enableImperativeEvents` until it is finalized. Native/internal event +// dispatch does not rely on these public methods. +if (!ReactNativeFeatureFlags.enableImperativeEvents()) { + for (const method of [ + 'addEventListener', + 'removeEventListener', + 'dispatchEvent', + ]) { + Object.defineProperty(ReactNativeElement.prototype, method, { + value: undefined, + writable: true, + enumerable: true, + configurable: true, + }); + } } diff --git a/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js b/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js index f1aa1b7ff0fb..c0fc6268eb80 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js +++ b/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyNode.js @@ -15,7 +15,6 @@ import type {InstanceHandle} from './internals/NodeInternals'; import type ReactNativeDocument from './ReactNativeDocument'; import type ReadOnlyElement from './ReadOnlyElement'; -import * as ReactNativeFeatureFlags from '../../../featureflags/ReactNativeFeatureFlags'; import {setPlatformObject} from '../../webidl/PlatformObjects'; import EventTarget from '../events/EventTarget'; import {EVENT_TARGET_GET_THE_PARENT_KEY} from '../events/internals/EventTargetInternals'; @@ -29,14 +28,6 @@ import { } from './internals/NodeInternals'; import NativeDOM from './specs/NativeDOM'; -// $FlowFixMe[unsupported-variance-annotation] -// $FlowFixMe[incompatible-type] -const ReadOnlyNodeBase: typeof Object = - ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() - ? EventTarget - : // $FlowFixMe[incompatible-type] - Object; - // Ideally, this class would be exported as-is, but calling super() in a // subclass is a very slow operation the way that Babel transforms it at // the moment. @@ -51,7 +42,7 @@ const ReadOnlyNodeBase: typeof Object = // hierarchy correctly. /** @build-types protected-constructor */ -class ReadOnlyNode extends ReadOnlyNodeBase { +class ReadOnlyNode extends EventTarget { constructor( instanceHandle: InstanceHandle, // This will be null for the document node itself. diff --git a/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyText.js b/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyText.js index 80626774ae88..53c50d443d8b 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyText.js +++ b/packages/react-native/src/private/webapis/dom/nodes/ReadOnlyText.js @@ -42,22 +42,20 @@ export const ReadOnlyText_public: typeof ReadOnlyText = ReadOnlyText_public.prototype = ReadOnlyText.prototype; // The public imperative EventTarget API (`addEventListener`, -// `removeEventListener`, `dispatchEvent`) is only inherited by this final class -// when `enableNativeEventTargetEventDispatching` is enabled (which makes -// `ReadOnlyNode` extend `EventTarget`). Until that public API is finalized, it -// is gated behind `enableImperativeEvents`: when off we remove those methods -// from this final class. Native/internal event dispatch does not rely on these -// public methods, so removing them is safe. -if ( - ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching() && - !ReactNativeFeatureFlags.enableImperativeEvents() -) { - const prototype: interface { - addEventListener?: unknown, - removeEventListener?: unknown, - dispatchEvent?: unknown, - } = ReadOnlyText.prototype; - prototype.addEventListener = undefined; - prototype.removeEventListener = undefined; - prototype.dispatchEvent = undefined; +// `removeEventListener`, `dispatchEvent`) is gated behind +// `enableImperativeEvents` until it is finalized. Native/internal event +// dispatch does not rely on these public methods. +if (!ReactNativeFeatureFlags.enableImperativeEvents()) { + for (const method of [ + 'addEventListener', + 'removeEventListener', + 'dispatchEvent', + ]) { + Object.defineProperty(ReadOnlyText.prototype, method, { + value: undefined, + writable: true, + enumerable: true, + configurable: true, + }); + } } diff --git a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-itest.js b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-itest.js index b334b3c85662..ca0d7660da02 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-itest.js +++ b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-itest.js @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. * * @fantom_flags enableFabricCommitBranching:* - * @fantom_flags enableNativeEventTargetEventDispatching:true * @fantom_flags enableImperativeEvents:* * @flow strict-local * @format @@ -32,18 +31,12 @@ import { } from 'react-native'; import {ReactNativeFeatureFlags} from 'react-native/react-private-interface'; -// The public imperative EventTarget API is not part of the static type of this -// final class (it is only present at runtime, gated by feature flags), so we -// cast to an interface with optional members to inspect/use it without Flow -// errors. Optional members make this a valid upcast and let us assert both -// presence (`'function'`) and absence (`'undefined'`). +// The public imperative EventTarget API is gated at runtime. Read-only +// optional members let these tests inspect its presence or absence. type MaybeEventTarget = interface { - addEventListener?: (type: string, callback: (event: Event) => void) => void, - removeEventListener?: ( - type: string, - callback: (event: Event) => void, - ) => void, - dispatchEvent?: (event: Event) => boolean, + readonly addEventListener?: unknown, + readonly removeEventListener?: unknown, + readonly dispatchEvent?: unknown, }; /* eslint-disable no-bitwise */ @@ -1664,8 +1657,8 @@ describe('ReactNativeElement', () => { }); describe('imperative EventTarget API', () => { - // These tests run with `enableNativeEventTargetEventDispatching:true` and - // `enableImperativeEvents:*` (see the `@fantom_flags` pragmas). The public + // These tests run with `enableImperativeEvents:*` (see the + // `@fantom_flags` pragma). The public // EventTarget API is gated behind `enableImperativeEvents`: when it is off // the methods are removed from this final class, when it is on they are // available. @@ -1736,7 +1729,7 @@ describe('ReactNativeElement', () => { root.render(); }); - const element = nullthrows(ref.current) as MaybeEventTarget; + const element = ensureInstance(ref.current, EventTarget); const listener = jest.fn(); element.addEventListener?.('custom', listener); diff --git a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-traversal-benchmark-itest.js b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-traversal-benchmark-itest.js index 985a8b0fac18..4f58ef4fa46b 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-traversal-benchmark-itest.js +++ b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeElement-traversal-benchmark-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:* * @flow strict-local * @format */ diff --git a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReadOnlyText-itest.js b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReadOnlyText-itest.js index 1361457c67f1..291a37ace973 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReadOnlyText-itest.js +++ b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReadOnlyText-itest.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @fantom_flags enableNativeEventTargetEventDispatching:true * @fantom_flags enableImperativeEvents:* * @flow strict-local * @format @@ -32,15 +31,12 @@ function ensureReadOnlyNode(value: unknown): Node { return ensureInstance(value, Node); } -// The public imperative EventTarget API is not part of the static type of this -// final class (it is only present at runtime, gated by feature flags), so we -// cast to an interface with optional members to inspect it without Flow errors. -// Optional members make this a valid upcast and let us assert both presence -// (`'function'`) and absence (`'undefined'`). +// The public imperative EventTarget API is gated at runtime. Read-only +// optional members let these tests inspect its presence or absence. type MaybeEventTarget = interface { - addEventListener?: unknown, - removeEventListener?: unknown, - dispatchEvent?: unknown, + readonly addEventListener?: unknown, + readonly removeEventListener?: unknown, + readonly dispatchEvent?: unknown, }; describe('ReadOnlyText', () => { @@ -330,8 +326,8 @@ describe('ReadOnlyText', () => { }); describe('imperative EventTarget API', () => { - // These tests run with `enableNativeEventTargetEventDispatching:true` and - // `enableImperativeEvents:*` (see the `@fantom_flags` pragmas). The public + // These tests run with `enableImperativeEvents:*` (see the + // `@fantom_flags` pragma). The public // EventTarget API is gated behind `enableImperativeEvents`: when it is off // the methods are removed from this final class, when it is on they are // available.