react-native-windows 0.83.0-preview.3 → 0.84.0-preview.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/.flowconfig +2 -2
  2. package/Folly/Folly.vcxproj +2 -0
  3. package/Libraries/Animated/AnimatedEvent.js +2 -2
  4. package/Libraries/Animated/animations/Animation.js +1 -3
  5. package/Libraries/Animated/createAnimatedComponent.js +8 -5
  6. package/Libraries/Animated/nodes/AnimatedColor.js +1 -1
  7. package/Libraries/Animated/nodes/AnimatedInterpolation.js +40 -4
  8. package/Libraries/Animated/nodes/AnimatedNode.js +3 -3
  9. package/Libraries/Animated/nodes/AnimatedObject.js +16 -11
  10. package/Libraries/Animated/nodes/AnimatedProps.js +43 -12
  11. package/Libraries/Animated/nodes/AnimatedStyle.js +12 -12
  12. package/Libraries/Animated/nodes/AnimatedValue.js +6 -3
  13. package/Libraries/Animated/nodes/AnimatedValueXY.js +1 -1
  14. package/Libraries/BatchedBridge/MessageQueue.js +24 -22
  15. package/Libraries/Blob/URL.js +34 -3
  16. package/Libraries/Blob/URLSearchParams.js +1 -0
  17. package/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts +9 -1
  18. package/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +6 -1
  19. package/Libraries/Components/AccessibilityInfo/AccessibilityInfo.windows.js +6 -1
  20. package/Libraries/Components/Button.js +2 -5
  21. package/Libraries/Components/Button.windows.js +2 -5
  22. package/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js +1 -1
  23. package/Libraries/Components/DrawerAndroid/DrawerLayoutAndroidTypes.js +4 -4
  24. package/Libraries/Components/Keyboard/Keyboard.js +2 -2
  25. package/Libraries/Components/Pressable/Pressable.js +8 -8
  26. package/Libraries/Components/Pressable/Pressable.windows.js +14 -13
  27. package/Libraries/Components/TextInput/TextInput.flow.js +13 -13
  28. package/Libraries/Components/TextInput/TextInput.flow.windows.js +19 -18
  29. package/Libraries/Components/TextInput/TextInput.js +33 -34
  30. package/Libraries/Components/TextInput/TextInput.windows.js +40 -39
  31. package/Libraries/Components/Touchable/PooledClass.js +2 -2
  32. package/Libraries/Components/Touchable/TouchableBounce.js +3 -3
  33. package/Libraries/Components/Touchable/TouchableBounce.windows.js +3 -3
  34. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +1 -1
  35. package/Libraries/Components/Touchable/TouchableNativeFeedback.windows.js +1 -1
  36. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +9 -9
  37. package/Libraries/Components/Touchable/TouchableWithoutFeedback.windows.js +9 -9
  38. package/Libraries/Components/View/ViewPropTypes.js +16 -6
  39. package/Libraries/Components/View/ViewPropTypes.windows.js +21 -11
  40. package/Libraries/Core/Devtools/loadBundleFromServer.js +6 -3
  41. package/Libraries/Core/Devtools/loadBundleFromServer.windows.js +6 -3
  42. package/Libraries/Core/Devtools/symbolicateStackTrace.js +1 -1
  43. package/Libraries/Core/ExceptionsManager.js +3 -3
  44. package/Libraries/Core/RawEventEmitter.js +1 -1
  45. package/Libraries/Core/ReactFiberErrorDialog.js +1 -1
  46. package/Libraries/Core/ReactNativeVersion.js +2 -2
  47. package/Libraries/Core/setUpDeveloperTools.js +1 -1
  48. package/Libraries/Core/setUpErrorHandling.js +1 -1
  49. package/Libraries/Core/setUpReactDevTools.js +6 -23
  50. package/Libraries/EventEmitter/NativeEventEmitter.js +2 -2
  51. package/Libraries/Image/Image.android.js +2 -2
  52. package/Libraries/Image/Image.ios.js +2 -2
  53. package/Libraries/Image/Image.windows.js +2 -2
  54. package/Libraries/Image/ImageProps.js +1 -1
  55. package/Libraries/Image/ImageTypes.flow.js +2 -2
  56. package/Libraries/Interaction/InteractionManager.js +3 -3
  57. package/Libraries/Interaction/PanResponder.js +1 -1
  58. package/Libraries/LayoutAnimation/LayoutAnimation.js +1 -1
  59. package/Libraries/Linking/Linking.js +1 -1
  60. package/Libraries/Lists/FlatList.js +2 -2
  61. package/Libraries/LogBox/Data/LogBoxData.js +31 -4
  62. package/Libraries/LogBox/Data/LogBoxLog.js +2 -2
  63. package/Libraries/LogBox/Data/LogBoxSymbolication.js +1 -1
  64. package/Libraries/LogBox/Data/parseLogBoxLog.js +5 -5
  65. package/Libraries/LogBox/LogBox.js +7 -7
  66. package/Libraries/Modal/Modal.windows.js +1 -7
  67. package/Libraries/NativeComponent/BaseViewConfig.android.js +12 -0
  68. package/Libraries/NativeComponent/StaticViewConfigValidator.js +4 -4
  69. package/Libraries/NativeComponent/ViewConfigIgnore.js +1 -1
  70. package/Libraries/NativeComponent/ViewConfigIgnore.windows.js +1 -1
  71. package/Libraries/Network/RCTNetworking.android.js +3 -3
  72. package/Libraries/Network/RCTNetworking.ios.js +2 -2
  73. package/Libraries/Network/RCTNetworking.js.flow +2 -2
  74. package/Libraries/Network/RCTNetworking.windows.js +2 -2
  75. package/Libraries/Pressability/Pressability.js +9 -9
  76. package/Libraries/Pressability/Pressability.windows.js +17 -16
  77. package/Libraries/ReactNative/AppRegistry.flow.js +1 -1
  78. package/Libraries/ReactNative/BridgelessUIManager.js +1 -1
  79. package/Libraries/ReactNative/FabricUIManager.js +1 -1
  80. package/Libraries/ReactNative/PaperUIManager.js +1 -1
  81. package/Libraries/ReactNative/PaperUIManager.windows.js +1 -1
  82. package/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js +1 -1
  83. package/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstanceUtils.js +2 -2
  84. package/Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload.js +1 -1
  85. package/Libraries/ReactNative/RendererImplementation.js +1 -1
  86. package/Libraries/ReactNative/getNativeComponentAttributes.js +1 -1
  87. package/Libraries/ReactPrivate/ReactNativePrivateInterface.js +1 -2
  88. package/Libraries/Renderer/implementations/ReactFabric-dev.js +3 -3
  89. package/Libraries/Renderer/implementations/ReactFabric-prod.js +3 -3
  90. package/Libraries/Renderer/implementations/ReactFabric-profiling.js +3 -3
  91. package/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +11 -4
  92. package/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +11 -4
  93. package/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +11 -4
  94. package/Libraries/Renderer/shims/ReactFabric.js +3 -1
  95. package/Libraries/Renderer/shims/ReactFeatureFlags.js +3 -1
  96. package/Libraries/Renderer/shims/ReactNative.js +2 -2
  97. package/Libraries/Renderer/shims/ReactNativeTypes.js +3 -1
  98. package/Libraries/Renderer/shims/ReactNativeTypes.windows.js +3 -1
  99. package/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js +3 -1
  100. package/Libraries/Renderer/shims/createReactNativeComponentClass.js +3 -1
  101. package/Libraries/Settings/Settings.ios.js +1 -1
  102. package/Libraries/StyleSheet/PlatformColorValueTypes.android.js +2 -2
  103. package/Libraries/StyleSheet/PlatformColorValueTypes.ios.js +1 -1
  104. package/Libraries/StyleSheet/PlatformColorValueTypes.js.flow +4 -2
  105. package/Libraries/StyleSheet/StyleSheetExports.js +1 -1
  106. package/Libraries/StyleSheet/StyleSheetTypes.d.ts +99 -4
  107. package/Libraries/StyleSheet/StyleSheetTypes.js +6 -8
  108. package/Libraries/StyleSheet/processFilter.js +1 -1
  109. package/Libraries/Text/Text.js +46 -6
  110. package/Libraries/Text/Text.windows.js +46 -6
  111. package/Libraries/Text/TextNativeComponent.js +1 -1
  112. package/Libraries/Text/TextProps.js +7 -7
  113. package/Libraries/Text/TextProps.windows.js +7 -7
  114. package/Libraries/Types/CodegenTypes.js +1 -1
  115. package/Libraries/Types/CoreEventTypes.js +31 -0
  116. package/Libraries/Types/CoreEventTypes.windows.js +31 -13
  117. package/Libraries/Types/ReactDevToolsTypes.js +2 -2
  118. package/Libraries/Utilities/Appearance.js +2 -2
  119. package/Libraries/Utilities/DevLoadingView.js +8 -1
  120. package/Libraries/Utilities/DevSettings.js +3 -3
  121. package/Libraries/Utilities/HMRClient.js +4 -3
  122. package/Libraries/Utilities/RCTLog.js +3 -3
  123. package/Libraries/Utilities/ReactNativeTestTools.js +1 -1
  124. package/Libraries/Utilities/SceneTracker.js +1 -1
  125. package/Libraries/Utilities/codegenNativeCommands.js +1 -1
  126. package/Libraries/Utilities/deepFreezeAndThrowOnMutationInDev.js +3 -3
  127. package/Libraries/Utilities/logError.js +1 -1
  128. package/Libraries/Utilities/stringifySafe.js +7 -7
  129. package/Libraries/Utilities/useRefEffect.js +1 -1
  130. package/Libraries/WebSocket/WebSocketInterceptor.js +3 -3
  131. package/Libraries/promiseRejectionTrackingOptions.js +8 -8
  132. package/Libraries/vendor/core/ErrorUtils.js +10 -10
  133. package/Libraries/vendor/emitter/EventEmitter.js +6 -6
  134. package/Microsoft.ReactNative/Fabric/AbiViewProps.cpp +5 -0
  135. package/Microsoft.ReactNative/Fabric/AbiViewProps.h +1 -0
  136. package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +58 -20
  137. package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +5 -0
  138. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +302 -67
  139. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +19 -4
  140. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +23 -12
  141. package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +4 -0
  142. package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h +1 -0
  143. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +0 -2
  144. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +98 -44
  145. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +4 -2
  146. package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +2 -3
  147. package/Microsoft.ReactNative/Fabric/ReactTaggedView.h +2 -2
  148. package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp +3 -3
  149. package/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.h +3 -1
  150. package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +0 -1
  151. package/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl +2 -0
  152. package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.cpp +3 -3
  153. package/Microsoft.ReactNative/Modules/Animated/AnimatedNode.h +3 -2
  154. package/Microsoft.ReactNative/Modules/Timing.h +2 -1
  155. package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +6 -0
  156. package/Microsoft.ReactNative/ViewProps.idl +9 -0
  157. package/Microsoft.ReactNative.Cxx/ReactCommon/TurboModule.h +12 -2
  158. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  159. package/PropertySheets/JSEngine.props +1 -1
  160. package/ReactCommon/ReactCommon.vcxproj +7 -1
  161. package/ReactCommon/ReactCommon.vcxproj.filters +0 -3
  162. package/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/JSIndexedRAMBundle.cpp +5 -5
  163. package/ReactCommon/TEMP_UntilReactCommonUpdate/cxxreact/ReactMarker.cpp +0 -1
  164. package/ReactCommon/TEMP_UntilReactCommonUpdate/jsiexecutor/jsireact/JSIExecutor.cpp +2 -43
  165. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/nativemodule/core/ReactCommon/TurboModule.h +12 -2
  166. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/attributedstring/TextAttributes.cpp +291 -0
  167. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/EventDispatcher.cpp +0 -1
  168. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/EventQueueProcessor.cpp +1 -3
  169. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/propsConversions.h +199 -0
  170. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/uimanager/UIManager.cpp +6 -0
  171. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/runtime/ReactInstance.cpp +17 -10
  172. package/Scripts/creaternwapp.cmd +8 -1
  173. package/Scripts/creaternwlib.cmd +21 -2
  174. package/Shared/DevSettings.h +0 -3
  175. package/Shared/Modules/WebSocketModule.cpp +0 -1
  176. package/Shared/Modules/WebSocketModule.h +62 -3
  177. package/Shared/OInstance.cpp +5 -0
  178. package/Shared/Shared.vcxitems +4 -6
  179. package/Shared/Shared.vcxitems.filters +218 -709
  180. package/codegen/NativeAccessibilityManagerSpec.g.h +2 -0
  181. package/codegen/NativeAnimatedModuleSpec.g.h +21 -15
  182. package/codegen/NativeDevLoadingViewSpec.g.h +3 -3
  183. package/codegen/NativeIntentAndroidSpec.g.h +16 -3
  184. package/codegen/NativeReactNativeFeatureFlagsSpec.g.h +209 -191
  185. package/codegen/react/components/rnwcore/EventEmitters.h +1 -0
  186. package/codegen/react/components/rnwcore/Props.h +14 -0
  187. package/codegen/rnwcoreJSI.h +246 -211
  188. package/index.js +3 -0
  189. package/index.windows.js +3 -0
  190. package/jest/mock.js +2 -2
  191. package/jest/mockComponent.js +1 -1
  192. package/jest/setup.js +1 -1
  193. package/package.json +22 -21
  194. package/src/private/animated/NativeAnimatedHelper.js +130 -125
  195. package/src/private/animated/NativeAnimatedValidation.js +7 -4
  196. package/src/private/animated/createAnimatedPropsHook.js +13 -5
  197. package/src/private/animated/createAnimatedPropsMemoHook.js +10 -10
  198. package/src/private/components/virtualview/VirtualView.js +16 -9
  199. package/src/private/components/virtualview/logger/VirtualViewLogger.js +21 -0
  200. package/src/private/components/virtualview/logger/VirtualViewLoggerTypes.js +24 -0
  201. package/src/private/devsupport/devmenu/elementinspector/ElementProperties.js +1 -1
  202. package/src/private/devsupport/devmenu/elementinspector/Inspector.js +6 -25
  203. package/src/private/devsupport/devmenu/elementinspector/InspectorPanel.js +1 -27
  204. package/src/private/devsupport/devmenu/elementinspector/XHRInterceptor.js +3 -2
  205. package/src/private/featureflags/ReactNativeFeatureFlags.js +63 -42
  206. package/src/private/featureflags/ReactNativeFeatureFlagsBase.js +3 -3
  207. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +12 -9
  208. package/src/private/renderer/errorhandling/ErrorHandlers.js +7 -4
  209. package/src/private/specs_DEPRECATED/modules/NativeAccessibilityManager.js +1 -1
  210. package/src/private/specs_DEPRECATED/modules/NativeAnimatedModule.js +4 -0
  211. package/src/private/specs_DEPRECATED/modules/NativeDevLoadingView.js +1 -0
  212. package/src/private/webapis/dom/events/CustomEvent.js +3 -3
  213. package/src/private/webapis/dom/nodes/internals/ReactNativeDocumentElementInstanceHandle.js +1 -1
  214. package/src/private/webapis/dom/nodes/internals/ReactNativeDocumentInstanceHandle.js +1 -1
  215. package/src/private/webapis/dom/nodes/specs/NativeDOM.js +29 -29
  216. package/src/private/webapis/dom/oldstylecollections/HTMLCollection.js +1 -1
  217. package/src/private/webapis/dom/oldstylecollections/NodeList.js +2 -2
  218. package/src/private/webapis/geometry/DOMRectReadOnly.js +1 -1
  219. package/src/private/webapis/html/events/MessageEvent.js +3 -3
  220. package/src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks.js +3 -3
  221. package/src/private/webapis/intersectionobserver/IntersectionObserver.js +53 -5
  222. package/src/private/webapis/intersectionobserver/internals/IntersectionObserverManager.js +2 -2
  223. package/src/private/webapis/intersectionobserver/specs/NativeIntersectionObserver.js +4 -4
  224. package/src/private/webapis/microtasks/specs/NativeMicrotasks.js +1 -1
  225. package/src/private/webapis/mutationobserver/MutationObserver.js +1 -1
  226. package/src/private/webapis/mutationobserver/MutationRecord.js +1 -1
  227. package/src/private/webapis/mutationobserver/specs/NativeMutationObserver.js +4 -4
  228. package/src/private/webapis/performance/Performance.js +1 -1
  229. package/src/private/webapis/performance/UserTiming.js +1 -1
  230. package/src/private/webapis/performance/specs/NativePerformance.js +3 -3
  231. package/src/private/webapis/structuredClone/structuredClone.js +3 -3
  232. package/src/types/globals.d.ts +30 -1
  233. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/image/conversions.h +0 -178
  234. package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/imagemanager/primitives.h +0 -183
  235. package/Shared/Modules/WebSocketTurboModule.h +0 -71
  236. package/src/private/devsupport/devmenu/elementinspector/NetworkOverlay.js +0 -628
  237. package/src/private/devsupport/devmenu/perfmonitor/PerformanceOverlay.js +0 -66
@@ -250,7 +250,10 @@ void CompositionEventHandler::Initialize() noexcept {
250
250
  if (strongThis->SurfaceId() == -1)
251
251
  return;
252
252
 
253
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
253
+ auto *rootView = strongThis->RootComponentView();
254
+ if (!rootView)
255
+ return;
256
+ auto focusedComponent = rootView->GetFocusedComponent();
254
257
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
255
258
  auto keyArgs =
256
259
  winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
@@ -276,7 +279,10 @@ void CompositionEventHandler::Initialize() noexcept {
276
279
  if (strongThis->SurfaceId() == -1)
277
280
  return;
278
281
 
279
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
282
+ auto *rootView = strongThis->RootComponentView();
283
+ if (!rootView)
284
+ return;
285
+ auto focusedComponent = rootView->GetFocusedComponent();
280
286
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
281
287
  auto keyArgs =
282
288
  winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
@@ -303,7 +309,10 @@ void CompositionEventHandler::Initialize() noexcept {
303
309
  if (strongThis->SurfaceId() == -1)
304
310
  return;
305
311
 
306
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
312
+ auto *rootView = strongThis->RootComponentView();
313
+ if (!rootView)
314
+ return;
315
+ auto focusedComponent = rootView->GetFocusedComponent();
307
316
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
308
317
  auto charArgs = winrt::make<
309
318
  winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
@@ -330,7 +339,10 @@ void CompositionEventHandler::Initialize() noexcept {
330
339
  if (strongThis->SurfaceId() == -1)
331
340
  return;
332
341
 
333
- auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
342
+ auto *rootView = strongThis->RootComponentView();
343
+ if (!rootView)
344
+ return;
345
+ auto focusedComponent = rootView->GetFocusedComponent();
334
346
  if (focusedComponent) {
335
347
  auto tag =
336
348
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
@@ -358,6 +370,7 @@ CompositionEventHandler::~CompositionEventHandler() {
358
370
  pointerSource.PointerMoved(m_pointerMovedToken);
359
371
  pointerSource.PointerCaptureLost(m_pointerCaptureLostToken);
360
372
  pointerSource.PointerWheelChanged(m_pointerWheelChangedToken);
373
+ pointerSource.PointerExited(m_pointerExitedToken);
361
374
  auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island);
362
375
  keyboardSource.KeyDown(m_keyDownToken);
363
376
  keyboardSource.KeyUp(m_keyUpToken);
@@ -380,10 +393,15 @@ facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept {
380
393
  return -1;
381
394
  }
382
395
 
383
- winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView &
396
+ winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *
384
397
  CompositionEventHandler::RootComponentView() const noexcept {
385
398
  auto island = m_wkRootView.get();
386
- return *winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)->GetComponentView();
399
+ if (!island) {
400
+ return nullptr;
401
+ }
402
+ return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>(island)
403
+ ->GetComponentView()
404
+ .get();
387
405
  }
388
406
 
389
407
  void CompositionEventHandler::onPointerWheelChanged(
@@ -398,8 +416,11 @@ void CompositionEventHandler::onPointerWheelChanged(
398
416
 
399
417
  // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we
400
418
  // need to apply the current origin
401
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
402
- auto tag = RootComponentView().hitTest(ptScaled, ptLocal);
419
+ auto *rootView = RootComponentView();
420
+ if (!rootView)
421
+ return;
422
+ ptScaled += rootView->layoutMetrics().frame.origin;
423
+ auto tag = rootView->hitTest(ptScaled, ptLocal);
403
424
 
404
425
  if (tag == -1)
405
426
  return;
@@ -553,7 +574,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
553
574
  case WM_CHAR:
554
575
  case WM_SYSCHAR: {
555
576
  if (auto strongRootView = m_wkRootView.get()) {
556
- auto focusedComponent = RootComponentView().GetFocusedComponent();
577
+ auto *rootView = RootComponentView();
578
+ if (!rootView)
579
+ break;
580
+ auto focusedComponent = rootView->GetFocusedComponent();
557
581
  auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
558
582
  auto args = winrt::make<
559
583
  winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>(
@@ -576,7 +600,10 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
576
600
  case WM_SYSKEYDOWN:
577
601
  case WM_SYSKEYUP: {
578
602
  if (auto strongRootView = m_wkRootView.get()) {
579
- auto focusedComponent = RootComponentView().GetFocusedComponent();
603
+ auto *rootView = RootComponentView();
604
+ if (!rootView)
605
+ break;
606
+ auto focusedComponent = rootView->GetFocusedComponent();
580
607
  auto keyboardSource = winrt::make<CompositionKeyboardSource>(this);
581
608
  auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::KeyRoutedEventArgs>(
582
609
  focusedComponent
@@ -608,9 +635,12 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
608
635
 
609
636
  void CompositionEventHandler::onKeyDown(
610
637
  const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
611
- RootComponentView().UseKeyboardForProgrammaticFocus(true);
638
+ auto *rootView = RootComponentView();
639
+ if (!rootView)
640
+ return;
641
+ rootView->UseKeyboardForProgrammaticFocus(true);
612
642
 
613
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
643
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
614
644
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyDown(args);
615
645
 
616
646
  if (args.Handled())
@@ -633,7 +663,7 @@ void CompositionEventHandler::onKeyDown(
633
663
  }
634
664
 
635
665
  if (!fCtrl && args.Key() == winrt::Windows::System::VirtualKey::Tab) {
636
- if (RootComponentView().TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
666
+ if (rootView->TryMoveFocus(!fShift, winrt::Microsoft::ReactNative::FocusState::Keyboard)) {
637
667
  args.Handled(true);
638
668
  }
639
669
 
@@ -643,9 +673,12 @@ void CompositionEventHandler::onKeyDown(
643
673
 
644
674
  void CompositionEventHandler::onKeyUp(
645
675
  const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
646
- RootComponentView().UseKeyboardForProgrammaticFocus(true);
676
+ auto *rootView = RootComponentView();
677
+ if (!rootView)
678
+ return;
679
+ rootView->UseKeyboardForProgrammaticFocus(true);
647
680
 
648
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
681
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
649
682
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)->OnKeyUp(args);
650
683
 
651
684
  if (args.Handled())
@@ -655,7 +688,10 @@ void CompositionEventHandler::onKeyUp(
655
688
 
656
689
  void CompositionEventHandler::onCharacterReceived(
657
690
  const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
658
- if (auto focusedComponent = RootComponentView().GetFocusedComponent()) {
691
+ auto *rootView = RootComponentView();
692
+ if (!rootView)
693
+ return;
694
+ if (auto focusedComponent = rootView->GetFocusedComponent()) {
659
695
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
660
696
  ->OnCharacterReceived(args);
661
697
 
@@ -664,7 +700,7 @@ void CompositionEventHandler::onCharacterReceived(
664
700
  }
665
701
  }
666
702
 
667
- std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot(
703
+ std::vector<winrt::Microsoft::ReactNative::ComponentView> CompositionEventHandler::GetTouchableViewsInPathToRoot(
668
704
  const winrt::Microsoft::ReactNative::ComponentView &componentView) {
669
705
  std::vector<winrt::Microsoft::ReactNative::ComponentView> results;
670
706
  auto view = componentView;
@@ -674,6 +710,7 @@ std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPat
674
710
  }
675
711
  view = view.Parent();
676
712
  }
713
+
677
714
  return results;
678
715
  }
679
716
 
@@ -974,8 +1011,8 @@ void CompositionEventHandler::UpdateActiveTouch(
974
1011
  // activeTouch.touch.isEraser = false;
975
1012
  activeTouch.touch.pagePoint.x = ptScaled.x;
976
1013
  activeTouch.touch.pagePoint.y = ptScaled.y;
977
- activeTouch.touch.screenPoint.x = ptLocal.x;
978
- activeTouch.touch.screenPoint.y = ptLocal.y;
1014
+ activeTouch.touch.screenPoint.x = ptScaled.x;
1015
+ activeTouch.touch.screenPoint.y = ptScaled.y;
979
1016
  activeTouch.touch.offsetPoint.x = ptLocal.x;
980
1017
  activeTouch.touch.offsetPoint.y = ptLocal.y;
981
1018
  activeTouch.touch.timestamp = static_cast<facebook::react::Float>(
@@ -986,8 +1023,6 @@ void CompositionEventHandler::UpdateActiveTouch(
986
1023
  // activeTouch.touch.shiftKey = false;
987
1024
  // activeTouch.touch.ctrlKey = false;
988
1025
  // activeTouch.touch.altKey = false;
989
-
990
- // activeTouch.touch.isPrimary = true;
991
1026
  }
992
1027
 
993
1028
  facebook::react::PointerEvent CreatePointerEventFromIncompleteHoverData(
@@ -1030,9 +1065,12 @@ void CompositionEventHandler::getTargetPointerArgs(
1030
1065
 
1031
1066
  // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we need
1032
1067
  // to apply the current origin
1033
- ptScaled += RootComponentView().layoutMetrics().frame.origin;
1068
+ auto *rootView = RootComponentView();
1069
+ if (!rootView)
1070
+ return;
1071
+ ptScaled += rootView->layoutMetrics().frame.origin;
1034
1072
 
1035
- if (std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId) != m_capturedPointers.end()) {
1073
+ if (m_capturedPointers.count(pointerId)) {
1036
1074
  assert(m_pointerCapturingComponentTag != -1);
1037
1075
  tag = m_pointerCapturingComponentTag;
1038
1076
 
@@ -1044,7 +1082,7 @@ void CompositionEventHandler::getTargetPointerArgs(
1044
1082
  ptLocal.y = ptScaled.y - (clientRect.top / strongRootView.ScaleFactor());
1045
1083
  }
1046
1084
  } else {
1047
- tag = RootComponentView().hitTest(ptScaled, ptLocal);
1085
+ tag = rootView->hitTest(ptScaled, ptLocal);
1048
1086
  }
1049
1087
  }
1050
1088
 
@@ -1054,16 +1092,49 @@ void CompositionEventHandler::onPointerCaptureLost(
1054
1092
  if (SurfaceId() == -1)
1055
1093
  return;
1056
1094
 
1057
- if (m_pointerCapturingComponentTag) {
1095
+ if (m_pointerCapturingComponentTag != -1) {
1058
1096
  // copy array to avoid iterator being invalidated during deletion
1059
- std::vector<PointerId> capturedPointers = m_capturedPointers;
1097
+ std::unordered_set<PointerId> capturedPointers = m_capturedPointers;
1060
1098
 
1061
1099
  for (auto pointerId : capturedPointers) {
1062
1100
  releasePointerCapture(pointerId, m_pointerCapturingComponentTag);
1101
+
1102
+ // Cancel any active touch for this pointer so React Native is notified that
1103
+ // the touch ended. Without this, m_activeTouches retains a zombie entry and
1104
+ // RN JS is never told the touch is gone — leaving Pressables stuck in a
1105
+ // pressed state after a system-interrupted gesture (e.g. system back swipe,
1106
+ // Alt+Tab, another window coming foreground).
1107
+ auto activeTouch = m_activeTouches.find(pointerId);
1108
+ if (activeTouch != m_activeTouches.end()) {
1109
+ ActiveTouch cancelledTouchCopy = std::move(activeTouch->second);
1110
+ m_activeTouches.erase(activeTouch);
1111
+ if (cancelledTouchCopy.eventEmitter) {
1112
+ DispatchSynthesizedTouchCancelForActiveTouch(cancelledTouchCopy, pointerPoint, keyModifiers);
1113
+ }
1114
+ }
1063
1115
  }
1064
1116
 
1065
1117
  m_pointerCapturingComponentTag = -1;
1066
1118
  }
1119
+
1120
+ // Also cancel any active touch for the specific pointer that lost capture, even
1121
+ // when no JS-level CapturePointer was ever issued. This handles ScrollView (and
1122
+ // any other VisualInteractionSource) calling TryRedirectForManipulation: the OS
1123
+ // reassigns the pointer to the InteractionTracker, fires PointerCaptureLost, and
1124
+ // then stops delivering PointerMoved/PointerReleased to us. Without this cleanup
1125
+ // m_activeTouches keeps a zombie entry whose target is the originally-pressed
1126
+ // Pressable, leaving it visually pressed and causing later taps to be attributed
1127
+ // to that original target. If the entry was already cleared above (for a JS-level
1128
+ // capture) or by onPointerReleased running first, the find() is a no-op.
1129
+ PointerId pointerId = pointerPoint.PointerId();
1130
+ auto activeTouch = m_activeTouches.find(pointerId);
1131
+ if (activeTouch != m_activeTouches.end()) {
1132
+ ActiveTouch cancelledTouchCopy = std::move(activeTouch->second);
1133
+ m_activeTouches.erase(activeTouch);
1134
+ if (cancelledTouchCopy.eventEmitter) {
1135
+ DispatchSynthesizedTouchCancelForActiveTouch(cancelledTouchCopy, pointerPoint, keyModifiers);
1136
+ }
1137
+ }
1067
1138
  }
1068
1139
 
1069
1140
  void CompositionEventHandler::onPointerMoved(
@@ -1103,10 +1174,11 @@ void CompositionEventHandler::onPointerMoved(
1103
1174
 
1104
1175
  auto handler = [&, targetView, pointerEvent, isActiveTouch](
1105
1176
  std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1177
+ auto *rootViewForEmitter = RootComponentView();
1106
1178
  const auto eventEmitter = targetView
1107
1179
  ? winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(targetView)
1108
1180
  ->eventEmitterAtPoint(pointerEvent.offsetPoint)
1109
- : RootComponentView().eventEmitterAtPoint(pointerEvent.offsetPoint);
1181
+ : (rootViewForEmitter ? rootViewForEmitter->eventEmitterAtPoint(pointerEvent.offsetPoint) : nullptr);
1110
1182
 
1111
1183
  if (eventEmitter != nullptr) {
1112
1184
  eventEmitter->onPointerMove(pointerEvent);
@@ -1132,7 +1204,10 @@ void CompositionEventHandler::ClearAllHoveredForPointer(const facebook::react::P
1132
1204
  // events. If we get null for the targetView, that means that the mouse is no over any components, so we have no
1133
1205
  // element to send the move event to. However we need to send something so that any previously hovered elements
1134
1206
  // are no longer hovered.
1135
- auto children = RootComponentView().Children();
1207
+ auto *rootView = RootComponentView();
1208
+ if (!rootView)
1209
+ return;
1210
+ auto children = rootView->Children();
1136
1211
  if (auto size = children.Size()) {
1137
1212
  auto firstChild = children.GetAt(0);
1138
1213
  if (auto childEventEmitter =
@@ -1172,28 +1247,67 @@ void CompositionEventHandler::onPointerExited(
1172
1247
  }
1173
1248
  }
1174
1249
 
1250
+ // Windows touch pointer IDs can be arbitrarily large (e.g. 2233). React Native's JS
1251
+ // touch handler uses identifiers as array indices and warns/misbehaves for values > 20.
1252
+ // This function maps each live Windows pointer to a small identifier in [0, 19] by
1253
+ // scanning m_activeTouches for in-use slots and cycling from the last assigned index.
1254
+ // Identifier MOUSE_POINTER_ID (1) is permanently reserved for mouse and never returned here.
1255
+ int CompositionEventHandler::AllocateTouchIdentifier() noexcept {
1256
+ constexpr int kMaxTouchIdentifier = 20;
1257
+ for (int i = 0; i < kMaxTouchIdentifier; i++) {
1258
+ int candidate = (m_touchId + i) % kMaxTouchIdentifier;
1259
+ if (candidate == static_cast<int>(MOUSE_POINTER_ID)) {
1260
+ continue; // reserved for mouse
1261
+ }
1262
+ bool inUse = std::any_of(m_activeTouches.begin(), m_activeTouches.end(), [candidate](const auto &pair) {
1263
+ return pair.second.touch.identifier == candidate;
1264
+ });
1265
+ if (!inUse) {
1266
+ m_touchId = (candidate + 1) % kMaxTouchIdentifier;
1267
+ return candidate;
1268
+ }
1269
+ }
1270
+ // All non-mouse slots occupied (> 19 simultaneous touch/pen points) — wrap anyway,
1271
+ // skipping the mouse-reserved slot.
1272
+ int fallback = m_touchId;
1273
+ m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
1274
+ if (fallback == static_cast<int>(MOUSE_POINTER_ID)) {
1275
+ fallback = m_touchId;
1276
+ m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
1277
+ }
1278
+ return fallback;
1279
+ }
1280
+
1175
1281
  void CompositionEventHandler::onPointerPressed(
1176
1282
  const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
1177
1283
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1178
1284
  namespace Composition = winrt::Microsoft::ReactNative::Composition;
1179
1285
 
1180
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1286
+ auto *rootView = RootComponentView();
1287
+ if (!rootView)
1288
+ return;
1289
+ rootView->UseKeyboardForProgrammaticFocus(false);
1181
1290
 
1182
1291
  // Clears any active text selection when left pointer is pressed
1183
1292
  if (pointerPoint.Properties().PointerUpdateKind() != Composition::Input::PointerUpdateKind::RightButtonPressed) {
1184
- RootComponentView().ClearCurrentTextSelection();
1293
+ rootView->ClearCurrentTextSelection();
1185
1294
  }
1186
1295
 
1187
1296
  PointerId pointerId = pointerPoint.PointerId();
1188
1297
 
1189
- auto staleTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1190
- return pair.second.touch.identifier == pointerId;
1191
- });
1298
+ auto staleTouch = m_activeTouches.find(pointerId);
1192
1299
 
1193
1300
  if (staleTouch != m_activeTouches.end()) {
1194
- // A pointer with this ID already exists - Should we fire a button cancel or something?
1195
- // assert(false);
1196
- return;
1301
+ // A previous pointer with this ID was never properly released (e.g., app lost focus,
1302
+ // pointer left window). Cancel the stale touch and clean it up so the new press can proceed.
1303
+ // Copy and erase before dispatching to avoid holding a reference into m_activeTouches
1304
+ // across DispatchSynthesizedTouchCancelForActiveTouch, which calls HandleIncomingPointerEvent
1305
+ // and iterates m_activeTouches internally.
1306
+ ActiveTouch staleTouchCopy = std::move(staleTouch->second);
1307
+ m_activeTouches.erase(staleTouch);
1308
+ if (staleTouchCopy.eventEmitter) {
1309
+ DispatchSynthesizedTouchCancelForActiveTouch(staleTouchCopy, pointerPoint, keyModifiers);
1310
+ }
1197
1311
  }
1198
1312
 
1199
1313
  const auto eventType = TouchEventType::Start;
@@ -1214,7 +1328,18 @@ void CompositionEventHandler::onPointerPressed(
1214
1328
  ->OnPointerPressed(args);
1215
1329
 
1216
1330
  ActiveTouch activeTouch{0};
1217
- activeTouch.touchType = UITouchType::Mouse;
1331
+ switch (pointerPoint.PointerDeviceType()) {
1332
+ case Composition::Input::PointerDeviceType::Touch:
1333
+ activeTouch.touchType = UITouchType::Touch;
1334
+ break;
1335
+ case Composition::Input::PointerDeviceType::Pen:
1336
+ activeTouch.touchType = UITouchType::Pen;
1337
+ break;
1338
+ case Composition::Input::PointerDeviceType::Mouse:
1339
+ default:
1340
+ activeTouch.touchType = UITouchType::Mouse;
1341
+ break;
1342
+ }
1218
1343
 
1219
1344
  // Map PointerUpdateKind to W3C button value
1220
1345
  // https://developer.mozilla.org/docs/Web/API/MouseEvent/button
@@ -1246,20 +1371,33 @@ void CompositionEventHandler::onPointerPressed(
1246
1371
  ->eventEmitterAtPoint(ptLocal)) {
1247
1372
  activeTouch.eventEmitter = eventEmitter;
1248
1373
  activeTouch.touch.target = targetComponentView.Tag();
1249
- // activeTouch.componentView = componentView;
1374
+ activeTouch.initialComponentView = targetComponentView;
1250
1375
  break;
1251
1376
  }
1252
1377
  targetComponentView = targetComponentView.Parent();
1253
1378
  }
1254
1379
 
1380
+ // Don't register the touch if no eventEmitter was found — inserting a null-emitter entry
1381
+ // into m_activeTouches would block future presses with the same pointer ID.
1382
+ if (!activeTouch.eventEmitter) {
1383
+ return;
1384
+ }
1385
+
1255
1386
  UpdateActiveTouch(activeTouch, ptScaled, ptLocal);
1256
1387
 
1257
- // activeTouch.touch.isPrimary = true;
1258
- activeTouch.touch.identifier = pointerId;
1388
+ activeTouch.isPrimary = pointerId == 1;
1389
+ // Map the Windows pointer ID to a small identifier (0–19) safe for use as a JS array index.
1390
+ // Windows touch IDs can be arbitrarily large (e.g. 2233), which causes React Native to warn
1391
+ // and corrupts touch state, leaving Pressables stuck after a scroll.
1392
+ // Mouse pointer ID is always 1 (MOUSE_POINTER_ID), which is already within the safe range —
1393
+ // use it directly to preserve stable, predictable identifier assignment for mouse input.
1394
+ activeTouch.touch.identifier = (pointerPoint.PointerDeviceType() == Composition::Input::PointerDeviceType::Mouse)
1395
+ ? static_cast<int>(MOUSE_POINTER_ID)
1396
+ : AllocateTouchIdentifier();
1259
1397
 
1260
1398
  // If the pointer has not been marked as hovering over views before the touch started, we register
1261
1399
  // that the activeTouch should not maintain its hovered state once the pointer has been lifted.
1262
- auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(activeTouch.touch.identifier);
1400
+ auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(pointerId);
1263
1401
  if (currentlyHoveredTags == m_currentlyHoveredViewsPerPointer.end() || currentlyHoveredTags->second.empty()) {
1264
1402
  activeTouch.shouldLeaveWhenReleased = true;
1265
1403
  }
@@ -1275,11 +1413,12 @@ void CompositionEventHandler::onPointerReleased(
1275
1413
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
1276
1414
  int pointerId = pointerPoint.PointerId();
1277
1415
 
1278
- RootComponentView().UseKeyboardForProgrammaticFocus(false);
1416
+ auto *rootView = RootComponentView();
1417
+ if (!rootView)
1418
+ return;
1419
+ rootView->UseKeyboardForProgrammaticFocus(false);
1279
1420
 
1280
- auto activeTouch = std::find_if(m_activeTouches.begin(), m_activeTouches.end(), [pointerId](const auto &pair) {
1281
- return pair.second.touch.identifier == pointerId;
1282
- });
1421
+ auto activeTouch = m_activeTouches.find(pointerId);
1283
1422
 
1284
1423
  if (activeTouch == m_activeTouches.end()) {
1285
1424
  return;
@@ -1291,8 +1430,13 @@ void CompositionEventHandler::onPointerReleased(
1291
1430
  facebook::react::Point ptLocal, ptScaled;
1292
1431
  getTargetPointerArgs(fabricuiManager, pointerPoint, tag, ptScaled, ptLocal);
1293
1432
 
1294
- if (tag == -1)
1433
+ if (tag == -1) {
1434
+ if (activeTouch->second.eventEmitter) {
1435
+ DispatchSynthesizedTouchCancelForActiveTouch(activeTouch->second, pointerPoint, keyModifiers);
1436
+ }
1437
+ m_activeTouches.erase(pointerId);
1295
1438
  return;
1439
+ }
1296
1440
 
1297
1441
  auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view;
1298
1442
  auto args = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerRoutedEventArgs>(
@@ -1324,7 +1468,7 @@ bool CompositionEventHandler::CapturePointer(
1324
1468
  }
1325
1469
 
1326
1470
  m_pointerCapturingComponentTag = tag;
1327
- m_capturedPointers.push_back(pointer.PointerId());
1471
+ m_capturedPointers.insert(pointer.PointerId());
1328
1472
  return true;
1329
1473
  }
1330
1474
 
@@ -1339,11 +1483,9 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1339
1483
  bool result = false;
1340
1484
 
1341
1485
  if (m_pointerCapturingComponentTag == tag) {
1342
- auto it = std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId);
1343
- if (it == m_capturedPointers.end()) {
1486
+ if (m_capturedPointers.erase(pointerId) == 0) {
1344
1487
  return false;
1345
1488
  }
1346
- m_capturedPointers.erase(it);
1347
1489
 
1348
1490
  if (std::shared_ptr<FabricUIManager> fabricuiManager =
1349
1491
  ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) {
@@ -1354,7 +1496,7 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo
1354
1496
  ->OnPointerCaptureLost();
1355
1497
  }
1356
1498
 
1357
- if (m_capturedPointers.size() == 0) {
1499
+ if (m_capturedPointers.empty()) {
1358
1500
  m_pointerCapturingComponentTag = -1;
1359
1501
  return true;
1360
1502
  }
@@ -1459,11 +1601,83 @@ facebook::react::PointerEvent CompositionEventHandler::CreatePointerEventFromAct
1459
1601
 
1460
1602
  // event.tangentialPressure = 0.0;
1461
1603
  // event.twist = 0;
1462
- // event.isPrimary = activeTouch.isPrimary;
1604
+ event.isPrimary = activeTouch.isPrimary;
1463
1605
 
1464
1606
  return event;
1465
1607
  }
1466
1608
 
1609
+ bool CompositionEventHandler::IsPointerWithinInitialTree(const ActiveTouch &activeTouch) noexcept {
1610
+ auto initialComponentView = activeTouch.initialComponentView.view();
1611
+ if (!initialComponentView)
1612
+ return false;
1613
+
1614
+ auto *rootView = RootComponentView();
1615
+ if (!rootView)
1616
+ return false;
1617
+
1618
+ facebook::react::Point ptLocal;
1619
+ auto currentTag = rootView->hitTest(activeTouch.touch.pagePoint, ptLocal);
1620
+ if (currentTag == -1)
1621
+ return false;
1622
+
1623
+ auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties());
1624
+ if (!fabricuiManager)
1625
+ return false;
1626
+
1627
+ auto initialTag = initialComponentView.Tag();
1628
+ auto &viewRegistry = fabricuiManager->GetViewRegistry();
1629
+ auto currentView = viewRegistry.componentViewDescriptorWithTag(currentTag).view;
1630
+ while (currentView) {
1631
+ if (currentView.Tag() == initialTag)
1632
+ return true;
1633
+ currentView = currentView.Parent();
1634
+ }
1635
+
1636
+ return false;
1637
+ }
1638
+
1639
+ void CompositionEventHandler::DispatchSynthesizedTouchCancelForActiveTouch(
1640
+ const ActiveTouch &cancelledTouch,
1641
+ const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
1642
+ winrt::Windows::System::VirtualKeyModifiers keyModifiers) {
1643
+ if (!cancelledTouch.eventEmitter) {
1644
+ return;
1645
+ }
1646
+
1647
+ facebook::react::PointerEvent pointerEvent =
1648
+ CreatePointerEventFromActiveTouch(cancelledTouch, TouchEventType::Cancel);
1649
+ winrt::Microsoft::ReactNative::ComponentView targetView{nullptr};
1650
+ facebook::react::SharedTouchEventEmitter emitter = cancelledTouch.eventEmitter;
1651
+ auto pointerHandler = [emitter, pointerEvent](std::vector<winrt::Microsoft::ReactNative::ComponentView> &) {
1652
+ emitter->onPointerCancel(pointerEvent);
1653
+ };
1654
+ HandleIncomingPointerEvent(pointerEvent, targetView, pointerPoint, keyModifiers, pointerHandler);
1655
+
1656
+ facebook::react::TouchEvent touchEvent;
1657
+ touchEvent.changedTouches.insert(cancelledTouch.touch);
1658
+
1659
+ for (const auto &pair : m_activeTouches) {
1660
+ if (!pair.second.eventEmitter) {
1661
+ continue;
1662
+ }
1663
+
1664
+ if (touchEvent.changedTouches.find(pair.second.touch) != touchEvent.changedTouches.end()) {
1665
+ continue;
1666
+ }
1667
+
1668
+ touchEvent.touches.insert(pair.second.touch);
1669
+ }
1670
+
1671
+ for (const auto &pair : m_activeTouches) {
1672
+ if (pair.second.eventEmitter == cancelledTouch.eventEmitter &&
1673
+ touchEvent.changedTouches.find(pair.second.touch) == touchEvent.changedTouches.end()) {
1674
+ touchEvent.targetTouches.insert(pair.second.touch);
1675
+ }
1676
+ }
1677
+
1678
+ cancelledTouch.eventEmitter->onTouchCancel(touchEvent);
1679
+ }
1680
+
1467
1681
  // If we have events that include multiple pointer updates, we should change arg from pointerId to vector<pointerId>
1468
1682
  void CompositionEventHandler::DispatchTouchEvent(
1469
1683
  TouchEventType eventType,
@@ -1480,7 +1694,15 @@ void CompositionEventHandler::DispatchTouchEvent(
1480
1694
 
1481
1695
  facebook::react::TouchEvent event;
1482
1696
 
1483
- size_t index = 0;
1697
+ // First pass: build changedTouches and the set of unique emitters from every active
1698
+ // touch. The per-pointer PointerEvent dispatch (onPointerDown/Move/Up/Cancel/Click) is
1699
+ // fired only for the touch whose state actually changed — non-changed touches contribute
1700
+ // to the W3C TouchEvent's touches/targetTouches sets in the loops below but must not
1701
+ // re-fire pointer events of their own. Previously we dispatched the per-pointer event
1702
+ // for every entry in m_activeTouches, which produced duplicated onPointerMove on
1703
+ // non-moving fingers and replayed onPointerUp/onClick on stale targets after the OS
1704
+ // reclaimed a pointer (e.g. ScrollView manipulation redirect leaving a zombie touch).
1705
+ const ActiveTouch *changedTouch = nullptr;
1484
1706
  for (const auto &pair : m_activeTouches) {
1485
1707
  const auto &activeTouch = pair.second;
1486
1708
 
@@ -1488,42 +1710,55 @@ void CompositionEventHandler::DispatchTouchEvent(
1488
1710
  continue;
1489
1711
  }
1490
1712
 
1491
- if (activeTouch.touch.identifier == pointerId) {
1713
+ if (pair.first == pointerId) {
1714
+ changedTouch = &activeTouch;
1492
1715
  event.changedTouches.insert(activeTouch.touch);
1493
1716
  }
1494
1717
  uniqueEventEmitters.insert(activeTouch.eventEmitter);
1718
+ }
1495
1719
 
1496
- facebook::react::PointerEvent pointerEvent = CreatePointerEventFromActiveTouch(activeTouch, eventType);
1720
+ if (changedTouch) {
1721
+ facebook::react::PointerEvent pointerEvent = CreatePointerEventFromActiveTouch(*changedTouch, eventType);
1497
1722
 
1498
1723
  winrt::Microsoft::ReactNative::ComponentView targetView{nullptr};
1499
- bool shouldLeave = (eventType == TouchEventType::End && activeTouch.shouldLeaveWhenReleased) ||
1724
+ bool shouldLeave = (eventType == TouchEventType::End && changedTouch->shouldLeaveWhenReleased) ||
1500
1725
  eventType == TouchEventType::Cancel;
1501
1726
  if (!shouldLeave) {
1502
- const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1503
- facebook::react::Point ptLocal;
1504
- auto targetTag = RootComponentView().hitTest(pointerEvent.clientPoint, ptLocal);
1505
- if (targetTag != -1) {
1506
- auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1507
- targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1727
+ auto *rootViewForHit = RootComponentView();
1728
+ if (rootViewForHit) {
1729
+ const auto &viewRegistry = fabricuiManager->GetViewRegistry();
1730
+ facebook::react::Point ptLocal;
1731
+ auto targetTag = rootViewForHit->hitTest(pointerEvent.clientPoint, ptLocal);
1732
+ if (targetTag != -1) {
1733
+ auto targetComponentViewDescriptor = viewRegistry.componentViewDescriptorWithTag(targetTag);
1734
+ targetView = FindClosestFabricManagedTouchableView(targetComponentViewDescriptor.view);
1735
+ }
1508
1736
  }
1509
1737
  }
1510
1738
 
1511
- auto handler = [&activeTouch, eventType, &pointerEvent](
1739
+ auto handler = [this, changedTouch, eventType, &pointerEvent](
1512
1740
  std::vector<winrt::Microsoft::ReactNative::ComponentView> &eventPathViews) {
1513
1741
  switch (eventType) {
1514
1742
  case TouchEventType::Start:
1515
- activeTouch.eventEmitter->onPointerDown(pointerEvent);
1743
+ changedTouch->eventEmitter->onPointerDown(pointerEvent);
1516
1744
  break;
1517
1745
  case TouchEventType::Move: {
1518
- activeTouch.eventEmitter->onPointerMove(pointerEvent);
1746
+ changedTouch->eventEmitter->onPointerMove(pointerEvent);
1519
1747
  break;
1520
1748
  }
1521
1749
  case TouchEventType::End:
1522
- activeTouch.eventEmitter->onPointerUp(pointerEvent);
1750
+ changedTouch->eventEmitter->onPointerUp(pointerEvent);
1751
+ if (pointerEvent.isPrimary && pointerEvent.button == 0) {
1752
+ if (IsPointerWithinInitialTree(*changedTouch)) {
1753
+ changedTouch->eventEmitter->onClick(pointerEvent);
1754
+ }
1755
+ } /* else if (IsPointerWithinInitialTree(*changedTouch)) {
1756
+ changedTouch->eventEmitter->onAuxClick(pointerEvent);
1757
+ } */
1523
1758
  break;
1524
1759
  case TouchEventType::Cancel:
1525
1760
  case TouchEventType::CaptureLost:
1526
- activeTouch.eventEmitter->onPointerCancel(pointerEvent);
1761
+ changedTouch->eventEmitter->onPointerCancel(pointerEvent);
1527
1762
  break;
1528
1763
  }
1529
1764
  };