react-native-windows 0.81.15 → 0.81.18

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 (29) hide show
  1. package/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp +20 -31
  2. package/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.h +4 -1
  3. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +56 -4
  4. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +6 -1
  5. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +135 -18
  6. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +10 -0
  7. package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +5 -0
  8. package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h +2 -0
  9. package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +3 -1
  10. package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/HostPlatformColor.h +3 -0
  11. package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +2 -7
  12. package/Microsoft.ReactNative/IReactPackageBuilder.idl +5 -0
  13. package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +1 -1
  14. package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +2 -0
  15. package/Microsoft.ReactNative/ReactPackageBuilder.cpp +7 -0
  16. package/Microsoft.ReactNative/ReactPackageBuilder.h +1 -0
  17. package/Microsoft.ReactNative/TurboModulesProvider.cpp +5 -10
  18. package/Microsoft.ReactNative/TurboModulesProvider.h +2 -0
  19. package/Microsoft.ReactNative.Cxx/ModuleRegistration.cpp +8 -2
  20. package/Microsoft.ReactNative.Cxx/ModuleRegistration.h +17 -4
  21. package/Microsoft.ReactNative.Cxx/NativeModules.h +5 -0
  22. package/PropertySheets/External/Microsoft.ReactNative.Uwp.CSharpApp.targets +1 -1
  23. package/PropertySheets/External/Microsoft.ReactNative.Uwp.CppApp.targets +1 -1
  24. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  25. package/PropertySheets/JSEngine.props +2 -1
  26. package/PropertySheets/WinUI.props +3 -3
  27. package/Scripts/Tfs/Invoke-WebRequestWithRetry.ps1 +40 -0
  28. package/Shared/Shared.vcxitems.filters +2 -0
  29. package/package.json +1 -1
@@ -9,18 +9,6 @@
9
9
 
10
10
  namespace winrt::Microsoft::ReactNative::Composition::implementation {
11
11
 
12
- // Ideally isColorMeaningful would be sufficient here. But it appears to detect platformColors as not meaningful
13
- // https://github.com/microsoft/react-native-windows/issues/14006
14
- bool isColorMeaningful(
15
- const facebook::react::SharedColor &color,
16
- winrt::Microsoft::ReactNative::Composition::implementation::Theme *theme) noexcept {
17
- if (!color) {
18
- return false;
19
- }
20
-
21
- return theme->Color(*color).A > 0;
22
- }
23
-
24
12
  // We don't want half pixel borders, or border radii - they lead to blurry borders
25
13
  // Also apply scale factor to the radii at this point
26
14
  void pixelRoundBorderRadii(facebook::react::BorderRadii &borderRadii, float scaleFactor) noexcept {
@@ -40,22 +28,20 @@ void pixelRoundBorderRadii(facebook::react::BorderRadii &borderRadii, float scal
40
28
  };
41
29
  }
42
30
 
31
+ float pixelRoundAndScaleBorderWidth(float width, float scaleFactor) noexcept {
32
+ if (width == 0)
33
+ return width = 0;
34
+ return std::max(1.f, std::round(width * scaleFactor));
35
+ }
36
+
43
37
  void scaleAndPixelRoundBorderWidths(
44
38
  facebook::react::LayoutMetrics const &layoutMetrics,
45
39
  facebook::react::BorderMetrics &borderMetrics,
46
40
  float scaleFactor) noexcept {
47
- borderMetrics.borderWidths.left = (borderMetrics.borderWidths.left == 0)
48
- ? 0.f
49
- : std::max(1.f, std::round(borderMetrics.borderWidths.left * scaleFactor));
50
- borderMetrics.borderWidths.top = (borderMetrics.borderWidths.top == 0)
51
- ? 0.f
52
- : std::max(1.f, std::round(borderMetrics.borderWidths.top * scaleFactor));
53
- borderMetrics.borderWidths.right = (borderMetrics.borderWidths.right == 0)
54
- ? 0.f
55
- : std::max(1.f, std::round(borderMetrics.borderWidths.right * scaleFactor));
56
- borderMetrics.borderWidths.bottom = (borderMetrics.borderWidths.bottom == 0)
57
- ? 0.f
58
- : std::max(1.f, std::round(borderMetrics.borderWidths.bottom * scaleFactor));
41
+ borderMetrics.borderWidths.left = pixelRoundAndScaleBorderWidth(borderMetrics.borderWidths.left, scaleFactor);
42
+ borderMetrics.borderWidths.top = pixelRoundAndScaleBorderWidth(borderMetrics.borderWidths.top, scaleFactor);
43
+ borderMetrics.borderWidths.right = pixelRoundAndScaleBorderWidth(borderMetrics.borderWidths.right, scaleFactor);
44
+ borderMetrics.borderWidths.bottom = pixelRoundAndScaleBorderWidth(borderMetrics.borderWidths.bottom, scaleFactor);
59
45
 
60
46
  // If we rounded both sides of the borderWidths up, we may have made the borderWidths larger than the total
61
47
  if (layoutMetrics.frame.size.width * scaleFactor <
@@ -356,7 +342,7 @@ void SetBorderLayerPropertiesCommon(
356
342
  // Clear with transparency
357
343
  pRT->Clear();
358
344
 
359
- if (!isColorMeaningful(borderColor, theme)) {
345
+ if (!facebook::react::isColorMeaningful(borderColor)) {
360
346
  return;
361
347
  }
362
348
 
@@ -724,7 +710,7 @@ winrt::com_ptr<ID2D1GeometryGroup> GetGeometryForRoundedBorder(
724
710
  BorderPrimitive::BorderPrimitive(
725
711
  winrt::Microsoft::ReactNative::Composition::implementation::ComponentView &outer,
726
712
  const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &rootVisual)
727
- : m_outer(&outer), m_rootVisual(rootVisual) {}
713
+ : m_outer(&outer), m_rootVisual(rootVisual), m_ownsRootVisual(false) {}
728
714
 
729
715
  BorderPrimitive::BorderPrimitive(winrt::Microsoft::ReactNative::Composition::implementation::ComponentView &outer)
730
716
  : m_outer(&outer), m_rootVisual(outer.CompositionContext().CreateSpriteVisual()) {}
@@ -740,9 +726,9 @@ bool BorderPrimitive::requiresBorder(
740
726
  auto borderStyle = borderMetrics.borderStyles.left;
741
727
 
742
728
  bool hasMeaningfulColor =
743
- !borderMetrics.borderColors.isUniform() || !isColorMeaningful(borderMetrics.borderColors.left, theme);
729
+ !borderMetrics.borderColors.isUniform() || facebook::react::isColorMeaningful(borderMetrics.borderColors.left);
744
730
  bool hasMeaningfulWidth = !borderMetrics.borderWidths.isUniform() || (borderMetrics.borderWidths.left != 0);
745
- if (!hasMeaningfulColor && !hasMeaningfulWidth) {
731
+ if (!hasMeaningfulColor || !hasMeaningfulWidth) {
746
732
  return false;
747
733
  }
748
734
  return true;
@@ -802,8 +788,10 @@ BorderPrimitive::FindSpecialBorderLayers() const noexcept {
802
788
  nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
803
789
 
804
790
  if (m_numBorderVisuals) {
791
+ auto borderInsertAtIndex = m_ownsRootVisual ? 0 : m_outer->borderInsertAtIndex();
792
+
805
793
  for (uint8_t i = 0; i < m_numBorderVisuals; i++) {
806
- auto visual = m_rootVisual.GetAt(i);
794
+ auto visual = m_rootVisual.GetAt(i + borderInsertAtIndex);
807
795
  layers[i] = visual.as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>();
808
796
  }
809
797
  }
@@ -830,7 +818,7 @@ bool BorderPrimitive::TryUpdateSpecialBorderLayers(
830
818
  auto borderStyle = borderMetrics.borderStyles.left;
831
819
 
832
820
  bool hasMeaningfulColor =
833
- !borderMetrics.borderColors.isUniform() || !isColorMeaningful(borderMetrics.borderColors.left, theme);
821
+ !borderMetrics.borderColors.isUniform() || !facebook::react::isColorMeaningful(borderMetrics.borderColors.left);
834
822
  bool hasMeaningfulWidth = !borderMetrics.borderWidths.isUniform() || (borderMetrics.borderWidths.left != 0);
835
823
  if (!hasMeaningfulColor && !hasMeaningfulWidth) {
836
824
  return false;
@@ -838,9 +826,10 @@ bool BorderPrimitive::TryUpdateSpecialBorderLayers(
838
826
 
839
827
  // Create the special border layers if they don't exist yet
840
828
  if (!spBorderVisuals[0]) {
829
+ auto borderInsertAtIndex = m_ownsRootVisual ? 0 : m_outer->borderInsertAtIndex();
841
830
  for (uint8_t i = 0; i < SpecialBorderLayerCount; i++) {
842
831
  auto visual = m_outer->CompositionContext().CreateSpriteVisual();
843
- m_rootVisual.InsertAt(visual, i);
832
+ m_rootVisual.InsertAt(visual, i + borderInsertAtIndex);
844
833
  spBorderVisuals[i] = std::move(visual);
845
834
  m_numBorderVisuals++;
846
835
  }
@@ -14,6 +14,8 @@ namespace winrt::Microsoft::ReactNative::Composition::implementation {
14
14
 
15
15
  struct ComponentView;
16
16
 
17
+ float pixelRoundAndScaleBorderWidth(float width, float scaleFactor) noexcept;
18
+
17
19
  // Controls adding/removing appropriate visuals to a parent to render a specific border without requiring
18
20
  struct BorderPrimitive {
19
21
  static constexpr size_t SpecialBorderLayerCount = 8;
@@ -74,7 +76,8 @@ struct BorderPrimitive {
74
76
  uint8_t m_numBorderVisuals{0};
75
77
  winrt::Microsoft::ReactNative::Composition::implementation::ComponentView *m_outer;
76
78
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_rootVisual{nullptr};
77
- bool m_needsUpdate{true};
79
+ bool m_needsUpdate : 1 {true};
80
+ bool m_ownsRootVisual : 1 {false};
78
81
  };
79
82
 
80
83
  } // namespace winrt::Microsoft::ReactNative::Composition::implementation
@@ -1098,12 +1098,26 @@ void CompositionEventHandler::onPointerCaptureLost(
1098
1098
  if (SurfaceId() == -1)
1099
1099
  return;
1100
1100
 
1101
- if (m_pointerCapturingComponentTag) {
1101
+ if (m_pointerCapturingComponentTag != -1) {
1102
1102
  // copy array to avoid iterator being invalidated during deletion
1103
1103
  std::unordered_set<PointerId> capturedPointers = m_capturedPointers;
1104
1104
 
1105
1105
  for (auto pointerId : capturedPointers) {
1106
1106
  releasePointerCapture(pointerId, m_pointerCapturingComponentTag);
1107
+
1108
+ // Cancel any active touch for this pointer so React Native is notified that
1109
+ // the touch ended. Without this, m_activeTouches retains a zombie entry and
1110
+ // RN JS is never told the touch is gone — leaving Pressables stuck in a
1111
+ // pressed state after a system-interrupted gesture (e.g. system back swipe,
1112
+ // Alt+Tab, another window coming foreground).
1113
+ auto activeTouch = m_activeTouches.find(pointerId);
1114
+ if (activeTouch != m_activeTouches.end()) {
1115
+ ActiveTouch cancelledTouchCopy = std::move(activeTouch->second);
1116
+ m_activeTouches.erase(activeTouch);
1117
+ if (cancelledTouchCopy.eventEmitter) {
1118
+ DispatchSynthesizedTouchCancelForActiveTouch(cancelledTouchCopy, pointerPoint, keyModifiers);
1119
+ }
1120
+ }
1107
1121
  }
1108
1122
 
1109
1123
  m_pointerCapturingComponentTag = -1;
@@ -1220,6 +1234,37 @@ void CompositionEventHandler::onPointerExited(
1220
1234
  }
1221
1235
  }
1222
1236
 
1237
+ // Windows touch pointer IDs can be arbitrarily large (e.g. 2233). React Native's JS
1238
+ // touch handler uses identifiers as array indices and warns/misbehaves for values > 20.
1239
+ // This function maps each live Windows pointer to a small identifier in [0, 19] by
1240
+ // scanning m_activeTouches for in-use slots and cycling from the last assigned index.
1241
+ // Identifier MOUSE_POINTER_ID (1) is permanently reserved for mouse and never returned here.
1242
+ int CompositionEventHandler::AllocateTouchIdentifier() noexcept {
1243
+ constexpr int kMaxTouchIdentifier = 20;
1244
+ for (int i = 0; i < kMaxTouchIdentifier; i++) {
1245
+ int candidate = (m_touchId + i) % kMaxTouchIdentifier;
1246
+ if (candidate == static_cast<int>(MOUSE_POINTER_ID)) {
1247
+ continue; // reserved for mouse
1248
+ }
1249
+ bool inUse = std::any_of(m_activeTouches.begin(), m_activeTouches.end(), [candidate](const auto &pair) {
1250
+ return pair.second.touch.identifier == candidate;
1251
+ });
1252
+ if (!inUse) {
1253
+ m_touchId = (candidate + 1) % kMaxTouchIdentifier;
1254
+ return candidate;
1255
+ }
1256
+ }
1257
+ // All non-mouse slots occupied (> 19 simultaneous touch/pen points) — wrap anyway,
1258
+ // skipping the mouse-reserved slot.
1259
+ int fallback = m_touchId;
1260
+ m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
1261
+ if (fallback == static_cast<int>(MOUSE_POINTER_ID)) {
1262
+ fallback = m_touchId;
1263
+ m_touchId = (m_touchId + 1) % kMaxTouchIdentifier;
1264
+ }
1265
+ return fallback;
1266
+ }
1267
+
1223
1268
  void CompositionEventHandler::onPointerPressed(
1224
1269
  const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint,
1225
1270
  winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept {
@@ -1328,11 +1373,18 @@ void CompositionEventHandler::onPointerPressed(
1328
1373
  UpdateActiveTouch(activeTouch, ptScaled, ptLocal);
1329
1374
 
1330
1375
  activeTouch.isPrimary = pointerId == 1;
1331
- activeTouch.touch.identifier = pointerId;
1376
+ // Map the Windows pointer ID to a small identifier (0–19) safe for use as a JS array index.
1377
+ // Windows touch IDs can be arbitrarily large (e.g. 2233), which causes React Native to warn
1378
+ // and corrupts touch state, leaving Pressables stuck after a scroll.
1379
+ // Mouse pointer ID is always 1 (MOUSE_POINTER_ID), which is already within the safe range —
1380
+ // use it directly to preserve stable, predictable identifier assignment for mouse input.
1381
+ activeTouch.touch.identifier = (pointerPoint.PointerDeviceType() == Composition::Input::PointerDeviceType::Mouse)
1382
+ ? static_cast<int>(MOUSE_POINTER_ID)
1383
+ : AllocateTouchIdentifier();
1332
1384
 
1333
1385
  // If the pointer has not been marked as hovering over views before the touch started, we register
1334
1386
  // that the activeTouch should not maintain its hovered state once the pointer has been lifted.
1335
- auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(activeTouch.touch.identifier);
1387
+ auto currentlyHoveredTags = m_currentlyHoveredViewsPerPointer.find(pointerId);
1336
1388
  if (currentlyHoveredTags == m_currentlyHoveredViewsPerPointer.end() || currentlyHoveredTags->second.empty()) {
1337
1389
  activeTouch.shouldLeaveWhenReleased = true;
1338
1390
  }
@@ -1647,7 +1699,7 @@ void CompositionEventHandler::DispatchTouchEvent(
1647
1699
  continue;
1648
1700
  }
1649
1701
 
1650
- if (activeTouch.touch.identifier == pointerId) {
1702
+ if (pair.first == pointerId) {
1651
1703
  event.changedTouches.insert(activeTouch.touch);
1652
1704
  }
1653
1705
  uniqueEventEmitters.insert(activeTouch.eventEmitter);
@@ -162,8 +162,13 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
162
162
  void UpdateCursor() noexcept;
163
163
  void SetCursor(facebook::react::Cursor cursor, HCURSOR hcur) noexcept;
164
164
 
165
+ // Allocates a small touch identifier (0–19) that is safe to use as a JS array index.
166
+ // Windows pointer IDs can be arbitrarily large, which causes React Native to warn and
167
+ // back-fill huge arrays, corrupting touch state after scrolling.
168
+ int AllocateTouchIdentifier() noexcept;
169
+
165
170
  std::map<PointerId, ActiveTouch> m_activeTouches; // iOS is map of touch event args to ActiveTouch..?
166
- PointerId m_touchId = 0;
171
+ int m_touchId = 0; // cycling base used by AllocateTouchIdentifier
167
172
 
168
173
  std::map<PointerId, std::vector<ReactTaggedView>> m_currentlyHoveredViewsPerPointer;
169
174
  winrt::weak_ref<winrt::Microsoft::ReactNative::ReactNativeIsland> m_wkRootView;
@@ -40,9 +40,11 @@ constexpr float FOCUS_VISUAL_RADIUS = 3.0f;
40
40
 
41
41
  // m_outerVisual
42
42
  // |
43
- // ----- m_visual <-- Background / clip - Can be a custom visual depending on Component type
43
+ // ----- m_visual - Can be a custom visual depending on Component type
44
44
  // |
45
- // ----- Border Visuals x N (BorderPrimitive attached to m_visual)
45
+ // ----- m_backgroundVisual <-- Background / clip (ComponentViewFeatures::Background)
46
+ // ----- Border Visuals x N (BorderPrimitive attached to m_visual) (ComponentViewFeatures::NativeBorder)
47
+ // ----- Outline Visuals x N(BorderPrimitive) (ComponentViewFeatures::NativeBorder)
46
48
  // ----- <children> (default: directly in m_visual after border visuals)
47
49
  // ----- m_childrenContainer (created on demand when overflow:hidden, children moved here)
48
50
  // ------Focus Visual Container (created when hosting focus visuals)
@@ -81,18 +83,16 @@ facebook::react::Props::Shared ComponentView::props() noexcept {
81
83
  }
82
84
 
83
85
  void ComponentView::onThemeChanged() noexcept {
84
- if ((m_flags & ComponentViewFeatures::Background) == ComponentViewFeatures::Background) {
85
- if (viewProps()->backgroundColor) {
86
- Visual().as<Experimental::ISpriteVisual>().Brush(theme()->Brush(*viewProps()->backgroundColor));
87
- } else {
88
- Visual().as<Experimental::ISpriteVisual>().Brush(nullptr);
89
- }
90
- }
86
+ if (m_backgroundVisual)
87
+ m_backgroundVisual.Brush(theme()->Brush(*viewProps()->backgroundColor));
91
88
 
92
89
  if (m_borderPrimitive) {
93
90
  m_borderPrimitive->onThemeChanged(
94
91
  m_layoutMetrics, BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, *viewProps()));
95
92
  }
93
+ if (m_outlinePrimitive) {
94
+ m_outlinePrimitive->onThemeChanged(outlineLayoutMetrics(), outlineBorderMetrics());
95
+ }
96
96
  if (m_componentHostingFocusVisual) {
97
97
  if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) {
98
98
  auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
@@ -156,10 +156,18 @@ void ComponentView::updateProps(
156
156
 
157
157
  if ((m_flags & ComponentViewFeatures::Background) == ComponentViewFeatures::Background) {
158
158
  if (oldViewProps.backgroundColor != newViewProps.backgroundColor) {
159
- if (newViewProps.backgroundColor) {
160
- Visual().as<Experimental::ISpriteVisual>().Brush(theme()->Brush(*newViewProps.backgroundColor));
159
+ if (facebook::react::isColorMeaningful(newViewProps.backgroundColor)) {
160
+ if (!m_backgroundVisual) {
161
+ m_backgroundVisual = m_compContext.CreateSpriteVisual();
162
+ m_backgroundVisual.RelativeSizeWithOffset({0, 0}, {1.0f, 1.0f});
163
+ Visual().InsertAt(m_backgroundVisual, 0);
164
+ }
165
+ m_backgroundVisual.Brush(theme()->Brush(*newViewProps.backgroundColor));
166
+ updateClippingPath(m_layoutMetrics, *viewProps());
161
167
  } else {
162
- Visual().as<Experimental::ISpriteVisual>().Brush(nullptr);
168
+ if (m_backgroundVisual) {
169
+ m_backgroundVisual.Brush(nullptr);
170
+ }
163
171
  }
164
172
  }
165
173
  }
@@ -168,6 +176,16 @@ void ComponentView::updateProps(
168
176
  m_borderPrimitive->updateProps(oldViewProps, newViewProps);
169
177
  }
170
178
 
179
+ if (m_outlinePrimitive) {
180
+ if (oldViewProps.outlineOffset != newViewProps.outlineOffset ||
181
+ oldViewProps.outlineWidth != newViewProps.outlineWidth ||
182
+ oldViewProps.borderRadii != newViewProps.borderRadii ||
183
+ oldViewProps.outlineColor != newViewProps.outlineColor ||
184
+ oldViewProps.outlineStyle != newViewProps.outlineStyle) {
185
+ m_outlinePrimitive->markNeedsUpdate();
186
+ }
187
+ }
188
+
171
189
  if (m_componentHostingFocusVisual) {
172
190
  if (!newViewProps.enableFocusRing) {
173
191
  m_componentHostingFocusVisual->hostFocusVisual(false, get_strong());
@@ -202,8 +220,9 @@ void ComponentView::updateProps(
202
220
  void ComponentView::updateLayoutMetrics(
203
221
  facebook::react::LayoutMetrics const &layoutMetrics,
204
222
  facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
223
+ updateClippingPath(layoutMetrics, *viewProps());
224
+
205
225
  if ((m_flags & ComponentViewFeatures::NativeBorder) == ComponentViewFeatures::NativeBorder) {
206
- updateClippingPath(layoutMetrics, *viewProps());
207
226
  OuterVisual().Size(
208
227
  {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
209
228
  layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
@@ -220,6 +239,9 @@ void ComponentView::updateLayoutMetrics(
220
239
  if (m_borderPrimitive) {
221
240
  m_borderPrimitive->markNeedsUpdate();
222
241
  }
242
+ if (m_outlinePrimitive) {
243
+ m_outlinePrimitive->markNeedsUpdate();
244
+ }
223
245
 
224
246
  if (m_componentHostingFocusVisual) {
225
247
  m_componentHostingFocusVisual->updateFocusLayoutMetrics();
@@ -303,6 +325,23 @@ void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentView
303
325
  if (m_borderPrimitive) {
304
326
  m_borderPrimitive->finalize(m_layoutMetrics, borderMetrics);
305
327
  }
328
+
329
+ auto outlineMetrics = outlineBorderMetrics();
330
+ if (!m_outlinePrimitive && BorderPrimitive::requiresBorder(outlineMetrics, theme())) {
331
+ m_outlinePrimitive = std::make_shared<BorderPrimitive>(*this);
332
+ Visual().InsertAt(
333
+ m_outlinePrimitive->RootVisual(),
334
+ (m_backgroundVisual ? 1 : 0) + (m_borderPrimitive ? m_borderPrimitive->numberOfVisuals() : 0));
335
+ }
336
+
337
+ if (m_outlinePrimitive) {
338
+ auto offset = pixelRoundAndScaleBorderWidth(viewProps()->outlineWidth, m_layoutMetrics.pointScaleFactor) +
339
+ std::round(viewProps()->outlineOffset * m_layoutMetrics.pointScaleFactor);
340
+ m_outlinePrimitive->RootVisual().Offset({-offset, -offset, 0.0f});
341
+ m_outlinePrimitive->RootVisual().RelativeSizeWithOffset({offset * 2, offset * 2}, {1.0f, 1.0f});
342
+
343
+ m_outlinePrimitive->finalize(outlineLayoutMetrics(), outlineMetrics);
344
+ }
306
345
  }
307
346
 
308
347
  if (m_componentHostingFocusVisual) {
@@ -583,6 +622,67 @@ facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) con
583
622
  return layoutMetrics;
584
623
  }
585
624
 
625
+ facebook::react::LayoutMetrics ComponentView::outlineLayoutMetrics() const noexcept {
626
+ auto &props = *viewProps();
627
+ auto offset = (pixelRoundAndScaleBorderWidth(viewProps()->outlineWidth, m_layoutMetrics.pointScaleFactor) +
628
+ std::round(viewProps()->outlineOffset * m_layoutMetrics.pointScaleFactor)) /
629
+ m_layoutMetrics.pointScaleFactor;
630
+ facebook::react::LayoutMetrics layoutMetrics = m_layoutMetrics;
631
+ layoutMetrics.frame.origin.x -= offset;
632
+ layoutMetrics.frame.origin.y -= offset;
633
+ layoutMetrics.frame.size.height += offset * 2;
634
+ layoutMetrics.frame.size.width += offset * 2;
635
+ return layoutMetrics;
636
+ }
637
+
638
+ facebook::react::BorderMetrics ComponentView::outlineBorderMetrics() const noexcept {
639
+ auto &props = *viewProps();
640
+
641
+ facebook::react::BorderMetrics metrics = BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, props);
642
+ metrics.borderColors.bottom = metrics.borderColors.left = metrics.borderColors.right = metrics.borderColors.top =
643
+ props.outlineColor;
644
+
645
+ auto offset = pixelRoundAndScaleBorderWidth(viewProps()->outlineWidth, m_layoutMetrics.pointScaleFactor) +
646
+ std::round(viewProps()->outlineOffset * m_layoutMetrics.pointScaleFactor);
647
+
648
+ if (metrics.borderRadii.bottomLeft.horizontal)
649
+ metrics.borderRadii.bottomLeft.horizontal = std::max(0.0f, metrics.borderRadii.bottomLeft.horizontal + offset);
650
+ if (metrics.borderRadii.bottomLeft.vertical)
651
+ metrics.borderRadii.bottomLeft.vertical = std::max(0.0f, metrics.borderRadii.bottomLeft.vertical + offset);
652
+ if (metrics.borderRadii.bottomRight.horizontal)
653
+ metrics.borderRadii.bottomRight.horizontal = std::max(0.0f, metrics.borderRadii.bottomRight.horizontal + offset);
654
+ if (metrics.borderRadii.bottomRight.vertical)
655
+ metrics.borderRadii.bottomRight.vertical = std::max(0.0f, metrics.borderRadii.bottomRight.vertical + offset);
656
+ if (metrics.borderRadii.topLeft.horizontal)
657
+ metrics.borderRadii.topLeft.horizontal = std::max(0.0f, metrics.borderRadii.topLeft.horizontal + offset);
658
+ if (metrics.borderRadii.topLeft.vertical)
659
+ metrics.borderRadii.topLeft.vertical = std::max(0.0f, metrics.borderRadii.topLeft.vertical + offset);
660
+ if (metrics.borderRadii.topRight.horizontal)
661
+ metrics.borderRadii.topRight.horizontal = std::max(0.0f, metrics.borderRadii.topRight.horizontal + offset);
662
+ if (metrics.borderRadii.topRight.vertical)
663
+ metrics.borderRadii.topRight.vertical = std::max(0.0f, metrics.borderRadii.topRight.vertical + offset);
664
+
665
+ static_assert(
666
+ facebook::react::BorderStyle::Solid ==
667
+ static_cast<facebook::react::BorderStyle>(facebook::react::OutlineStyle::Solid));
668
+ static_assert(
669
+ facebook::react::BorderStyle::Dotted ==
670
+ static_cast<facebook::react::BorderStyle>(facebook::react::OutlineStyle::Dotted));
671
+ static_assert(
672
+ facebook::react::BorderStyle::Dashed ==
673
+ static_cast<facebook::react::BorderStyle>(facebook::react::OutlineStyle::Dashed));
674
+ assert(
675
+ props.outlineStyle == facebook::react::OutlineStyle::Solid ||
676
+ props.outlineStyle == facebook::react::OutlineStyle::Dotted ||
677
+ props.outlineStyle == facebook::react::OutlineStyle::Dashed);
678
+ metrics.borderStyles.bottom = metrics.borderStyles.left = metrics.borderStyles.right = metrics.borderStyles.top =
679
+ static_cast<facebook::react::BorderStyle>(props.outlineStyle);
680
+
681
+ metrics.borderWidths.bottom = metrics.borderWidths.left = metrics.borderWidths.right = metrics.borderWidths.top =
682
+ pixelRoundAndScaleBorderWidth(viewProps()->outlineWidth, m_layoutMetrics.pointScaleFactor);
683
+ return metrics;
684
+ }
685
+
586
686
  facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
587
687
  bool inner,
588
688
  const facebook::react::LayoutMetrics &layoutMetrics) const noexcept {
@@ -656,7 +756,7 @@ void ComponentView::hostFocusVisual(bool show, winrt::com_ptr<ComponentView> vie
656
756
  assert(
657
757
  view.get() ==
658
758
  this); // When not using lifted comp, focus visuals should always host within their own component
659
- OuterVisual().InsertAt(m_focusPrimitive->m_focusVisual, 1);
759
+ OuterVisual().InsertAt(m_focusPrimitive->m_focusVisual, (m_backgroundVisual ? 2 : 1));
660
760
  }
661
761
  }
662
762
 
@@ -900,9 +1000,23 @@ void ComponentView::Toggle() noexcept {
900
1000
  // no-op
901
1001
  }
902
1002
 
1003
+ // This offset ensures that the m_borderPrimitive inserts its layers above the background
1004
+ int32_t ComponentView::borderInsertAtIndex() const noexcept {
1005
+ return m_backgroundVisual ? 1 : 0;
1006
+ }
1007
+
1008
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView::VisualToApplyBackgroundClipTo()
1009
+ const noexcept {
1010
+ return m_backgroundVisual;
1011
+ }
1012
+
903
1013
  void ComponentView::updateClippingPath(
904
1014
  facebook::react::LayoutMetrics const &layoutMetrics,
905
1015
  const facebook::react::ViewProps &viewProps) noexcept {
1016
+ auto clipTarget = VisualToApplyBackgroundClipTo();
1017
+ if (!clipTarget)
1018
+ return;
1019
+
906
1020
  auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
907
1021
 
908
1022
  bool hasRoundedCorners = borderMetrics.borderRadii.topLeft.horizontal != 0 ||
@@ -921,10 +1035,10 @@ void ComponentView::updateClippingPath(
921
1035
  winrt::com_ptr<ID2D1PathGeometry> pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry(
922
1036
  m_compContext, borderMetrics.borderRadii, {0, 0, 0, 0}, {0, 0, viewWidth, viewHeight});
923
1037
 
924
- Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
1038
+ clipTarget.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
925
1039
  pathGeometry.get());
926
1040
  } else {
927
- Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
1041
+ clipTarget.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
928
1042
  }
929
1043
  }
930
1044
 
@@ -936,6 +1050,9 @@ void ComponentView::indexOffsetForBorder(uint32_t &index) const noexcept {
936
1050
  if (m_borderPrimitive) {
937
1051
  index += m_borderPrimitive->numberOfVisuals();
938
1052
  }
1053
+ if (m_outlinePrimitive) {
1054
+ index += 1;
1055
+ }
939
1056
  }
940
1057
 
941
1058
  void ComponentView::OnRenderingDeviceLost() noexcept {}
@@ -1086,7 +1203,7 @@ void ViewComponentView::ensureVisual() noexcept {
1086
1203
  } else {
1087
1204
  m_visual = createVisual();
1088
1205
  }
1089
- OuterVisual().InsertAt(m_visual, 0);
1206
+ OuterVisual().InsertAt(m_visual, m_backgroundVisual ? 1 : 0);
1090
1207
  }
1091
1208
  }
1092
1209
 
@@ -1384,7 +1501,7 @@ void ViewComponentView::updateChildrenClippingPath(
1384
1501
  }
1385
1502
 
1386
1503
  // Insert m_childrenContainer after border visuals in m_visual
1387
- Visual().InsertAt(m_childrenContainer, borderCount);
1504
+ Visual().InsertAt(m_childrenContainer, (m_backgroundVisual ? 1 : 0) + borderCount);
1388
1505
 
1389
1506
  // Use relative sizing so container automatically tracks parent's size
1390
1507
  m_childrenContainer.RelativeSizeWithOffset({0, 0}, {1, 1});
@@ -111,6 +111,9 @@ struct ComponentView : public ComponentViewT<
111
111
  bool getAcccessiblityIsReadOnly() noexcept override;
112
112
  ToggleState getToggleState() noexcept override;
113
113
  void Toggle() noexcept override;
114
+
115
+ int32_t borderInsertAtIndex() const noexcept;
116
+
114
117
  virtual winrt::Microsoft::ReactNative::implementation::ClipState getClipState() noexcept;
115
118
 
116
119
  virtual std::pair<facebook::react::Cursor, HCURSOR> cursor() const noexcept;
@@ -130,6 +133,8 @@ struct ComponentView : public ComponentViewT<
130
133
  void ThemeChanged(winrt::event_token const &token) noexcept;
131
134
 
132
135
  protected:
136
+ virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual VisualToApplyBackgroundClipTo()
137
+ const noexcept;
133
138
  bool anyHitTestHelper(
134
139
  facebook::react::Tag &targetTag,
135
140
  facebook::react::Point &ptContent,
@@ -141,6 +146,7 @@ struct ComponentView : public ComponentViewT<
141
146
  winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext;
142
147
  comp::CompositionPropertySet m_centerPropSet{nullptr};
143
148
  facebook::react::SharedViewEventEmitter m_eventEmitter;
149
+ winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual m_backgroundVisual{nullptr};
144
150
 
145
151
  private:
146
152
  void updateFocusLayoutMetrics() noexcept;
@@ -157,6 +163,9 @@ struct ComponentView : public ComponentViewT<
157
163
  facebook::react::BorderMetrics focusBorderMetrics(bool inner, const facebook::react::LayoutMetrics &layoutMetrics)
158
164
  const noexcept;
159
165
 
166
+ facebook::react::LayoutMetrics outlineLayoutMetrics() const noexcept;
167
+ facebook::react::BorderMetrics outlineBorderMetrics() const noexcept;
168
+
160
169
  virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual visualToHostFocus() noexcept;
161
170
  virtual winrt::com_ptr<ComponentView> focusVisualRoot(const facebook::react::Rect &focusRect) noexcept;
162
171
 
@@ -168,6 +177,7 @@ struct ComponentView : public ComponentViewT<
168
177
  winrt::com_ptr<ComponentView>
169
178
  m_componentHostingFocusVisual; // The component that we are showing our focus visuals within
170
179
  std::shared_ptr<BorderPrimitive> m_borderPrimitive;
180
+ std::shared_ptr<BorderPrimitive> m_outlinePrimitive;
171
181
  std::unique_ptr<FocusPrimitive> m_focusPrimitive{nullptr};
172
182
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_outerVisual{nullptr};
173
183
  winrt::event<winrt::Windows::Foundation::EventHandler<winrt::IInspectable>> m_themeChangedEvent;
@@ -237,6 +237,11 @@ void ImageComponentView::onThemeChanged() noexcept {
237
237
  Super::onThemeChanged();
238
238
  }
239
239
 
240
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ImageComponentView::VisualToApplyBackgroundClipTo()
241
+ const noexcept {
242
+ return Visual();
243
+ }
244
+
240
245
  void ImageComponentView::ensureDrawingSurface() noexcept {
241
246
  assert(m_reactContext.UIDispatcher().HasThreadAccess());
242
247
 
@@ -53,6 +53,8 @@ struct ImageComponentView : ImageComponentViewT<ImageComponentView, ViewComponen
53
53
 
54
54
  virtual std::string DefaultControlType() const noexcept;
55
55
  static facebook::react::SharedViewProps defaultProps() noexcept;
56
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual VisualToApplyBackgroundClipTo()
57
+ const noexcept override;
56
58
 
57
59
  ImageComponentView(
58
60
  const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
@@ -334,7 +334,9 @@ void ParagraphComponentView::updateVisualBrush() noexcept {
334
334
  break;
335
335
  }
336
336
  }
337
- winrt::check_hresult(m_textLayout->SetTextAlignment(alignment));
337
+
338
+ if (alignment != m_textLayout->GetTextAlignment())
339
+ winrt::check_hresult(m_textLayout->SetTextAlignment(alignment));
338
340
  }
339
341
 
340
342
  requireNewBrush = true;
@@ -99,6 +99,9 @@ inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
99
99
 
100
100
  // windows
101
101
  inline bool hostPlatformColorIsColorMeaningful(Color color) noexcept {
102
+ if (color.m_platformColor.size())
103
+ return true;
104
+
102
105
  auto windowsColor = color.AsWindowsColor();
103
106
  return windowsColor.A > 0;
104
107
  }
@@ -196,12 +196,7 @@ void WindowsTextLayoutManager::GetTextLayout(
196
196
  ));
197
197
 
198
198
  // Apply max width constraint and ellipsis trimming to ensure consistency with rendering
199
- DWRITE_TEXT_METRICS metrics;
200
- winrt::check_hresult(spTextLayout->GetMetrics(&metrics));
201
-
202
- if (metrics.width > size.width) {
203
- spTextLayout->SetMaxWidth(size.width);
204
- }
199
+ spTextLayout->SetMaxWidth(size.width);
205
200
 
206
201
  // Apply DWRITE_TRIMMING for ellipsizeMode
207
202
  DWRITE_TRIMMING trimming = {};
@@ -396,7 +391,7 @@ void WindowsTextLayoutManager::GetTextLayoutByAdjustingFontSizeToFit(
396
391
  }
397
392
  }
398
393
 
399
- // measure entire text (inluding attachments)
394
+ // measure entire text (including attachments)
400
395
  TextMeasurement TextLayoutManager::measure(
401
396
  const AttributedStringBox &attributedStringBox,
402
397
  const ParagraphAttributes &paragraphAttributes,
@@ -32,6 +32,11 @@ namespace Microsoft.ReactNative
32
32
  "NOTE: TurboModules using JSI directly will not run correctly while using @ReactInstanceSettings.UseWebDebugger")
33
33
  void AddTurboModule(String moduleName, ReactModuleProvider moduleProvider);
34
34
 
35
+ DOC_STRING("Adds a custom native module. See @ReactModuleProvider. This will register the"
36
+ "module as a TurboModule. This module will be created and have init called as soon as the"
37
+ "instance is created, event before the module is accessed from JavaScript.")
38
+ void AddEagerInitTurboModule(String moduleName, ReactModuleProvider moduleProvider);
39
+
35
40
  #if !defined(CORE_ABI) && !defined(USE_FABRIC)
36
41
  DOC_STRING("Adds a custom view manager. See @ReactViewManagerProvider.")
37
42
  void AddViewManager(String viewManagerName, ReactViewManagerProvider viewManagerProvider);
@@ -637,7 +637,7 @@
637
637
  <ItemGroup>
638
638
  <PackageReference Include="boost" Version="1.84.0.0" />
639
639
  <PackageReference Include="Microsoft.Windows.CppWinRT" Version="$(CppWinRTVersion)" PrivateAssets="all" />
640
- <PackageReference Include="Microsoft.JavaScript.Hermes" Version="$(HermesVersion)" />
640
+ <PackageReference Include="$(HermesPackageName)" Version="$(HermesVersion)" />
641
641
  <PackageReference Include="$(WinUIPackageName)" Version="$(WinUIPackageVersion)" Condition="'$(OverrideWinUIPackage)'!='true'" />
642
642
  <PackageReference Include="$(V8PackageName)" Version="$(V8Version)" Condition="'$(UseV8)' == 'true'" />
643
643
  </ItemGroup>
@@ -349,6 +349,7 @@ void ReactInstanceWin::LoadModules(
349
349
  registerTurboModule(
350
350
  L"FabricUIManagerBinding",
351
351
  winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::FabricUIManager>());
352
+ turboModulesProvider->AddEagerInit("FabricUIManagerBinding");
352
353
  }
353
354
  #endif
354
355
 
@@ -357,6 +358,7 @@ void ReactInstanceWin::LoadModules(
357
358
  L"UIManager",
358
359
  // TODO: Use MakeTurboModuleProvider after it satisfies ReactNativeSpecs::UIManagerSpec
359
360
  winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::UIManager>());
361
+ turboModulesProvider->AddEagerInit("UIManager");
360
362
  #endif
361
363
 
362
364
  #ifndef CORE_ABI
@@ -59,6 +59,13 @@ void ReactPackageBuilder::AddTurboModule(
59
59
  m_turboModulesProvider->AddModuleProvider(moduleName, moduleProvider, true);
60
60
  }
61
61
 
62
+ void ReactPackageBuilder::AddEagerInitTurboModule(
63
+ hstring const &moduleName,
64
+ ReactModuleProvider const &moduleProvider) noexcept {
65
+ m_turboModulesProvider->AddModuleProvider(moduleName, moduleProvider, true);
66
+ m_turboModulesProvider->AddEagerInit(winrt::to_string(moduleName));
67
+ }
68
+
62
69
  #ifdef USE_FABRIC
63
70
  void ReactPackageBuilder::AddViewComponent(
64
71
  winrt::hstring componentName,
@@ -43,6 +43,7 @@ struct ReactPackageBuilder : winrt::implements<
43
43
  void AddViewManager(hstring const &viewManagerName, ReactViewManagerProvider const &viewManagerProvider) noexcept;
44
44
  #endif
45
45
  void AddTurboModule(hstring const &moduleName, ReactModuleProvider const &moduleProvider) noexcept;
46
+ void AddEagerInitTurboModule(hstring const &moduleName, ReactModuleProvider const &moduleProvider) noexcept;
46
47
 
47
48
  #ifdef USE_FABRIC
48
49
  // IReactPackageBuilderFabric
@@ -494,16 +494,11 @@ std::shared_ptr<facebook::react::TurboModule> TurboModulesProvider::getModule(
494
494
  }
495
495
 
496
496
  std::vector<std::string> TurboModulesProvider::getEagerInitModuleNames() noexcept {
497
- std::vector<std::string> eagerModules;
498
- auto it = m_moduleProviders.find("UIManager");
499
- if (it != m_moduleProviders.end()) {
500
- eagerModules.push_back("UIManager");
501
- }
502
- it = m_moduleProviders.find("FabricUIManagerBinding");
503
- if (it != m_moduleProviders.end()) {
504
- eagerModules.push_back("FabricUIManagerBinding");
505
- }
506
- return eagerModules;
497
+ return m_eagerInitModuleNames;
498
+ }
499
+
500
+ void TurboModulesProvider::AddEagerInit(std::string moduleName) noexcept {
501
+ m_eagerInitModuleNames.push_back(moduleName);
507
502
  }
508
503
 
509
504
  void TurboModulesProvider::SetReactContext(const IReactContext &reactContext) noexcept {
@@ -26,6 +26,7 @@ class TurboModulesProvider final : public facebook::react::TurboModuleRegistry {
26
26
  winrt::hstring const &moduleName,
27
27
  ReactModuleProvider const &moduleProvider,
28
28
  bool overwriteExisting) noexcept;
29
+ void AddEagerInit(std::string moduleName) noexcept;
29
30
  std::shared_ptr<facebook::react::LongLivedObjectCollection> const &LongLivedObjectCollection() noexcept;
30
31
 
31
32
  private:
@@ -33,6 +34,7 @@ class TurboModulesProvider final : public facebook::react::TurboModuleRegistry {
33
34
  std::shared_ptr<facebook::react::LongLivedObjectCollection> m_longLivedObjectCollection{
34
35
  std::make_shared<facebook::react::LongLivedObjectCollection>()};
35
36
  std::unordered_map<std::string, ReactModuleProvider> m_moduleProviders;
37
+ std::vector<std::string> m_eagerInitModuleNames;
36
38
  IReactContext m_reactContext;
37
39
  };
38
40
 
@@ -18,7 +18,10 @@ ModuleRegistration::ModuleRegistration(wchar_t const *moduleName) noexcept : m_m
18
18
  void AddAttributedModules(IReactPackageBuilder const &packageBuilder, bool useTurboModules) noexcept {
19
19
  for (auto const *reg = ModuleRegistration::Head(); reg != nullptr; reg = reg->Next()) {
20
20
  if (useTurboModules || reg->ShouldRegisterAsTurboModule())
21
- packageBuilder.AddTurboModule(reg->ModuleName(), reg->MakeModuleProvider());
21
+ if (reg->IsEagerInit())
22
+ packageBuilder.AddEagerInitTurboModule(reg->ModuleName(), reg->MakeModuleProvider());
23
+ else
24
+ packageBuilder.AddTurboModule(reg->ModuleName(), reg->MakeModuleProvider());
22
25
  else
23
26
  packageBuilder.AddModule(reg->ModuleName(), reg->MakeModuleProvider());
24
27
  }
@@ -31,7 +34,10 @@ bool TryAddAttributedModule(
31
34
  for (auto const *reg = ModuleRegistration::Head(); reg != nullptr; reg = reg->Next()) {
32
35
  if (moduleName == reg->ModuleName()) {
33
36
  if (useTurboModule || reg->ShouldRegisterAsTurboModule()) {
34
- packageBuilder.AddTurboModule(moduleName, reg->MakeModuleProvider());
37
+ if (reg->IsEagerInit())
38
+ packageBuilder.AddEagerInitTurboModule(moduleName, reg->MakeModuleProvider());
39
+ else
40
+ packageBuilder.AddTurboModule(moduleName, reg->MakeModuleProvider());
35
41
  } else {
36
42
  packageBuilder.AddModule(moduleName, reg->MakeModuleProvider());
37
43
  }
@@ -30,10 +30,11 @@ template <typename T>
30
30
  struct IsReactTurboModule : std::bool_constant<ReactIsReactTurboModuleImpl(static_cast<T *>(nullptr))> {};
31
31
 
32
32
  #define INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER( \
33
- moduleStruct, moduleName, eventEmitterName, isReactTurboModule) \
33
+ moduleStruct, moduleName, eventEmitterName, isReactTurboModule, isEagerInit) \
34
34
  struct moduleStruct; \
35
35
  \
36
36
  constexpr bool ReactIsReactTurboModuleImpl(moduleStruct *) noexcept { return isReactTurboModule; } \
37
+ constexpr bool ReactIsEagerInitTurboModuleImpl(moduleStruct *) noexcept { return isEagerInit; } \
37
38
  \
38
39
  template <class TDummy> \
39
40
  struct moduleStruct##_ModuleRegistration final : winrt::Microsoft::ReactNative::ModuleRegistration { \
@@ -44,6 +45,7 @@ struct IsReactTurboModule : std::bool_constant<ReactIsReactTurboModuleImpl(stati
44
45
  } \
45
46
  \
46
47
  bool ShouldRegisterAsTurboModule() const noexcept override { return isReactTurboModule; } \
48
+ bool IsEagerInit() const noexcept override { return isEagerInit; } \
47
49
  \
48
50
  static const moduleStruct##_ModuleRegistration Registration; \
49
51
  }; \
@@ -59,7 +61,7 @@ struct IsReactTurboModule : std::bool_constant<ReactIsReactTurboModuleImpl(stati
59
61
  }
60
62
 
61
63
  #define INTERNAL_REACT_MODULE_3_ARGS(moduleStruct, moduleName, eventEmitterName) \
62
- INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, eventEmitterName, false)
64
+ INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, eventEmitterName, false, false)
63
65
 
64
66
  #define INTERNAL_REACT_MODULE_2_ARGS(moduleStruct, moduleName) \
65
67
  INTERNAL_REACT_MODULE_3_ARGS(moduleStruct, moduleName, L"")
@@ -104,17 +106,27 @@ struct IsReactTurboModule : std::bool_constant<ReactIsReactTurboModuleImpl(stati
104
106
 
105
107
  // Register struct as a ReactNative module.
106
108
  #define INTERNAL_REACT_TURBO_MODULE_2_ARGS(moduleStruct, moduleName) \
107
- INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, L"", true)
109
+ INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, L"", true, false)
110
+
111
+ #define INTERNAL_REACT_EAGER_TURBO_MODULE_2_ARGS(moduleStruct, moduleName) \
112
+ INTERNAL_REACT_MODULE_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, L"", true, true)
108
113
 
109
114
  #define INTERNAL_REACT_TURBO_MODULE_1_ARG(moduleStruct) \
110
115
  INTERNAL_REACT_TURBO_MODULE_2_ARGS(moduleStruct, L## #moduleStruct)
111
116
 
117
+ #define INTERNAL_REACT_EAGER_TURBO_MODULE_1_ARG(moduleStruct) \
118
+ INTERNAL_REACT_EAGER_TURBO_MODULE_2_ARGS(moduleStruct, L## #moduleStruct)
119
+
112
120
  #define INTERNAL_REACT_TURBO_MODULE(...) \
113
121
  INTERNAL_REACT_RECOMPOSER_3((__VA_ARGS__, INTERNAL_REACT_TURBO_MODULE_2_ARGS, INTERNAL_REACT_TURBO_MODULE_1_ARG, ))
114
122
 
123
+ #define INTERNAL_REACT_EAGER_TURBO_MODULE(...) \
124
+ INTERNAL_REACT_RECOMPOSER_3( \
125
+ (__VA_ARGS__, INTERNAL_REACT_EAGER_TURBO_MODULE_2_ARGS, INTERNAL_REACT_EAGER_TURBO_MODULE_1_ARG, ))
126
+
115
127
  // Another version of REACT_MODULE but does not do auto registration
116
128
  #define INTERNAL_REACT_TURBO_MODULE_NOREG_2_ARGS(moduleStruct, moduleName) \
117
- INTERNAL_REACT_MODULE_NO_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, L"", true)
129
+ INTERNAL_REACT_MODULE_NO_REGISTRATION_AND_PROVIDER(moduleStruct, moduleName, L"", true, false)
118
130
 
119
131
  #define INTERNAL_REACT_TURBO_MODULE_NOREG_1_ARG(moduleStruct) \
120
132
  INTERNAL_REACT_TURBO_MODULE_NOREG_2_ARGS(moduleStruct, L## #moduleStruct)
@@ -153,6 +165,7 @@ struct ModuleRegistration {
153
165
 
154
166
  virtual ReactModuleProvider MakeModuleProvider() const noexcept = 0;
155
167
  virtual bool ShouldRegisterAsTurboModule() const noexcept = 0;
168
+ virtual bool IsEagerInit() const noexcept = 0;
156
169
 
157
170
  static ModuleRegistration const *Head() noexcept {
158
171
  return s_head;
@@ -47,6 +47,11 @@
47
47
  #define REACT_TURBO_MODULE(/* moduleStruct, [opt] moduleName */...) \
48
48
  INTERNAL_REACT_TURBO_MODULE(__VA_ARGS__)(__VA_ARGS__)
49
49
 
50
+ // Same as REACT_TURBO_MODULE, but will register using AddEagerInitTurboModule, and this module
51
+ // will be created and init'd on instance creation.
52
+ #define REACT_EAGER_TURBO_MODULE(/* moduleStruct, [opt] moduleName */...) \
53
+ INTERNAL_REACT_EAGER_TURBO_MODULE(__VA_ARGS__)(__VA_ARGS__)
54
+
50
55
  // REACT_MODULE_NOREG is REACT_MODULE without auto registration
51
56
  // they have the same arguments
52
57
  #define REACT_MODULE_NOREG(/* moduleStruct, [opt] moduleName, [opt] eventEmitterName */...) \
@@ -25,7 +25,7 @@
25
25
  <!-- WinUI package name and version are set by WinUI.props -->
26
26
  <PackageReference Include="$(WinUIPackageName)" Version="$(WinUIPackageVersion)" Condition="'$(OverrideWinUIPackage)'!='true'" />
27
27
  <!-- Hermes version is set by JSEngine.props -->
28
- <PackageReference Include="Microsoft.JavaScript.Hermes" Version="$(HermesVersion)" Condition="$(UseHermes)" />
28
+ <PackageReference Include="$(HermesPackageName)" Version="$(HermesVersion)" Condition="$(UseHermes)" />
29
29
  </ItemGroup>
30
30
 
31
31
  <Import Project="$(ReactNativeWindowsDir)PropertySheets\ManagedCodeGen\Microsoft.ReactNative.Managed.CodeGen.targets"
@@ -25,7 +25,7 @@
25
25
  <!-- WinUI package name and version are set by WinUI.props -->
26
26
  <PackageReference Include="$(WinUIPackageName)" Version="$(WinUIPackageVersion)" Condition="'$(OverrideWinUIPackage)'!='true'" />
27
27
  <!-- Hermes version is set by JSEngine.props -->
28
- <PackageReference Include="Microsoft.JavaScript.Hermes" Version="$(HermesVersion)" Condition="$(UseHermes)" />
28
+ <PackageReference Include="$(HermesPackageName)" Version="$(HermesVersion)" Condition="$(UseHermes)" />
29
29
  </ItemGroup>
30
30
 
31
31
  <!-- The props file for bundling is not set up to be just defaults, it assumes to be run at the end of the project. -->
@@ -10,11 +10,11 @@
10
10
  -->
11
11
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
12
12
  <PropertyGroup>
13
- <ReactNativeWindowsVersion>0.81.15</ReactNativeWindowsVersion>
13
+ <ReactNativeWindowsVersion>0.81.18</ReactNativeWindowsVersion>
14
14
  <ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
15
15
  <ReactNativeWindowsMinor>81</ReactNativeWindowsMinor>
16
- <ReactNativeWindowsPatch>15</ReactNativeWindowsPatch>
16
+ <ReactNativeWindowsPatch>18</ReactNativeWindowsPatch>
17
17
  <ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
18
- <ReactNativeWindowsCommitId>38021b85ed4f13c35a4c9aa7cb2fe2e873aa0117</ReactNativeWindowsCommitId>
18
+ <ReactNativeWindowsCommitId>ffbb7afa1dbc3a60b57a6bca6025d3c0537b569e</ReactNativeWindowsCommitId>
19
19
  </PropertyGroup>
20
20
  </Project>
@@ -7,8 +7,9 @@
7
7
  <UseHermes Condition="'$(UseHermes)' == ''">true</UseHermes>
8
8
  <!-- This will be true if (1) the client want to use hermes by setting UseHermes to true OR (2) We are building for UWP where dynamic switching is enabled -->
9
9
  <HermesVersion Condition="'$(HermesVersion)' == ''">0.0.0-2511.7001-d7ca19b3</HermesVersion>
10
+ <HermesPackageName Condition="'$(HermesPackageName)' == ''">Microsoft.JavaScript.Hermes</HermesPackageName>
10
11
  <HermesPackage Condition="'$(HermesPackage)' == '' And Exists('$(PkgMicrosoft_JavaScript_Hermes)')">$(PkgMicrosoft_JavaScript_Hermes)</HermesPackage>
11
- <HermesPackage Condition="'$(HermesPackage)' == ''">$(NuGetPackageRoot)\Microsoft.JavaScript.Hermes\$(HermesVersion)</HermesPackage>
12
+ <HermesPackage Condition="'$(HermesPackage)' == ''">$(NuGetPackageRoot)\$(HermesPackageName)\$(HermesVersion)</HermesPackage>
12
13
  <EnableHermesInspectorInReleaseFlavor Condition="'$(EnableHermesInspectorInReleaseFlavor)' == ''">false</EnableHermesInspectorInReleaseFlavor>
13
14
  <!-- Disable linking Hermes into the output in cases where we need to fully rely on HermesShim -->
14
15
  <HermesNoLink Condition="'$(HermesNoLink)' == '' and '$(Configuration)' == 'Release' and '$(EnableHermesInspectorInReleaseFlavor)' != 'true'">true</HermesNoLink>
@@ -5,12 +5,12 @@
5
5
  <!--
6
6
  Internal versions are typically only located at: https://microsoft.visualstudio.com/DefaultCollection/ProjectReunion/_artifacts/feed/Project.Reunion.nuget.internal/NuGet/Microsoft.WindowsAppSDK/versions
7
7
  For local testing of internal versions, modify WinUI3ExperimentalVersion, and comment out the additional nuget source in NuGet.Config
8
- When this version is updated, be sure to update the default for the enableInternalFeed parameter of /.ado/templates/enable-experimental-winui3.yml and the minimum version of WebView in WebView2.props
8
+ When this version is updated, be sure to update the default for the enableInternalFeed parameter of /.ado/templates/enable-experimental-winui3.yml
9
9
  -->
10
- <WinUI3ExperimentalVersion Condition="'$(WinUI3ExperimentalVersion)'==''">1.7.250127003-experimental3</WinUI3ExperimentalVersion>
10
+ <WinUI3ExperimentalVersion Condition="'$(WinUI3ExperimentalVersion)'==''">1.8.251106002</WinUI3ExperimentalVersion>
11
11
  <!-- This value is also used by the CLI, see /packages/@react-native-windows/cli/.../autolinkWindows.ts -->
12
12
  <WinUI3Version Condition="'$(WinUI3Version)'=='' AND '$(UseExperimentalWinUI3)'=='true'">$(WinUI3ExperimentalVersion)</WinUI3Version>
13
- <WinUI3Version Condition="'$(WinUI3Version)'==''">1.7.250401001</WinUI3Version>
13
+ <WinUI3Version Condition="'$(WinUI3Version)'==''">1.8.251106002</WinUI3Version>
14
14
  <!-- This is needed to prevent build errors with WinAppSDK >= 1.7 trying to double build WindowsAppRuntimeAutoInitializer.cpp -->
15
15
  <WindowsAppSdkAutoInitialize Condition="'$(WindowsAppSdkAutoInitialize)'=='' And $([MSBuild]::VersionGreaterThan('$(WinUI3Version)', '1.7.0'))">false</WindowsAppSdkAutoInitialize>
16
16
  </PropertyGroup>
@@ -0,0 +1,40 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ <#
5
+ .SYNOPSIS
6
+ Downloads a file from a URI with retry logic.
7
+
8
+ .PARAMETER Uri
9
+ The URI to download from.
10
+
11
+ .PARAMETER OutFile
12
+ The output file path.
13
+
14
+ .PARAMETER MaxRetries
15
+ Maximum number of download attempts. Default is 3.
16
+ #>
17
+ param(
18
+ [Parameter(Mandatory)]
19
+ [string]$Uri,
20
+
21
+ [Parameter(Mandatory)]
22
+ [string]$OutFile,
23
+
24
+ [int]$MaxRetries = 3
25
+ )
26
+
27
+ Set-StrictMode -Version Latest
28
+ $ErrorActionPreference = 'Stop'
29
+
30
+ for ($i = 1; $i -le $MaxRetries; $i++) {
31
+ try {
32
+ Write-Host "Downloading $OutFile (attempt $i of $MaxRetries)"
33
+ Invoke-WebRequest -Uri $Uri -OutFile $OutFile
34
+ return
35
+ } catch {
36
+ Write-Host "Attempt $i failed: $_"
37
+ if ($i -eq $MaxRetries) { throw }
38
+ Start-Sleep -Seconds (5 * $i)
39
+ }
40
+ }
@@ -336,6 +336,8 @@
336
336
  <ClCompile Include="$(MSBuildThisFileDirectory)Inspector\ReactInspectorPackagerConnectionDelegate.cpp">
337
337
  <Filter>Inspector</Filter>
338
338
  </ClCompile>
339
+ <ClCompile Include="$(ReactNativeDir)\ReactCommon\cxxreact\Instance.cpp" />
340
+ <ClCompile Include="$(ReactNativeDir)\ReactCommon\cxxreact\JSExecutor.cpp" />
339
341
  </ItemGroup>
340
342
  <ItemGroup>
341
343
  <Filter Include="Source Files">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-windows",
3
- "version": "0.81.15",
3
+ "version": "0.81.18",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",