react-native 0.84.0-nightly-20251205-95cc1e767 → 0.84.0-nightly-20251207-e4a5a56df

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 (43) hide show
  1. package/Libraries/Animated/nodes/AnimatedProps.js +29 -3
  2. package/Libraries/Core/ReactNativeVersion.js +1 -1
  3. package/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +7 -0
  4. package/Libraries/NativeAnimation/React-RCTAnimation.podspec +1 -0
  5. package/React/Base/RCTVersion.m +1 -1
  6. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +7 -0
  7. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +2 -0
  8. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +10 -0
  9. package/ReactAndroid/gradle.properties +1 -1
  10. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +9 -5
  11. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  12. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +1 -10
  13. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt +1 -0
  14. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +42 -28
  15. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  16. package/ReactCommon/jsi/jsi/jsi.cpp +2 -1
  17. package/ReactCommon/jsinspector-modern/HostAgent.cpp +2 -2
  18. package/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +10 -5
  19. package/ReactCommon/jsinspector-modern/InspectorInterfaces.h +2 -2
  20. package/ReactCommon/jsinspector-modern/TracingAgent.cpp +1 -1
  21. package/ReactCommon/react/renderer/animated/AnimatedModule.cpp +19 -1
  22. package/ReactCommon/react/renderer/animated/AnimatedModule.h +8 -0
  23. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +83 -9
  24. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +6 -0
  25. package/ReactCommon/react/renderer/animated/drivers/FrameAnimationDriver.cpp +1 -0
  26. package/ReactCommon/react/renderer/animated/nodes/ColorAnimatedNode.cpp +0 -1
  27. package/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h +1 -0
  28. package/ReactCommon/react/renderer/animated/nodes/RoundAnimatedNode.cpp +1 -1
  29. package/ReactCommon/react/renderer/animated/nodes/ValueAnimatedNode.cpp +1 -1
  30. package/ReactCommon/react/renderer/animated/tests/AnimatedNodeTests.cpp +67 -0
  31. package/ReactCommon/react/renderer/animated/tests/AnimationDriverTests.cpp +59 -0
  32. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +81 -0
  33. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +83 -0
  34. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +26 -15
  35. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +7 -4
  36. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +69 -0
  37. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h +32 -0
  38. package/ReactCommon/react/renderer/uimanager/UIManager.cpp +6 -0
  39. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  40. package/package.json +9 -9
  41. package/sdks/hermes-engine/version.properties +1 -1
  42. package/src/private/animated/NativeAnimatedHelper.js +29 -1
  43. package/src/private/specs_DEPRECATED/modules/NativeAnimatedModule.js +4 -0
@@ -14,7 +14,9 @@ import type {AnimatedNodeConfig} from './AnimatedNode';
14
14
  import type {AnimatedStyleAllowlist} from './AnimatedStyle';
15
15
 
16
16
  import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
17
+ import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
17
18
  import {findNodeHandle} from '../../ReactNative/RendererProxy';
19
+ import {getNodeFromPublicInstance} from '../../ReactPrivate/ReactNativePrivateInterface';
18
20
  import flattenStyle from '../../StyleSheet/flattenStyle';
19
21
  import {AnimatedEvent} from '../AnimatedEvent';
20
22
  import AnimatedNode from './AnimatedNode';
@@ -251,7 +253,9 @@ export default class AnimatedProps extends AnimatedNode {
251
253
  super.__setPlatformConfig(platformConfig);
252
254
 
253
255
  if (this._target != null) {
254
- this.#connectAnimatedView(this._target);
256
+ const target = this._target;
257
+ this.#connectAnimatedView(target);
258
+ this.#connectShadowNode(target);
255
259
  }
256
260
  }
257
261
  }
@@ -260,9 +264,10 @@ export default class AnimatedProps extends AnimatedNode {
260
264
  if (this._target?.instance === instance) {
261
265
  return;
262
266
  }
263
- this._target = {instance, connectedViewTag: null};
267
+ const target = (this._target = {instance, connectedViewTag: null});
264
268
  if (this.__isNative) {
265
- this.#connectAnimatedView(this._target);
269
+ this.#connectAnimatedView(target);
270
+ this.#connectShadowNode(target);
266
271
  }
267
272
  }
268
273
 
@@ -283,6 +288,27 @@ export default class AnimatedProps extends AnimatedNode {
283
288
  target.connectedViewTag = viewTag;
284
289
  }
285
290
 
291
+ #connectShadowNode(target: TargetView): void {
292
+ if (
293
+ !ReactNativeFeatureFlags.cxxNativeAnimatedEnabled() ||
294
+ //eslint-disable-next-line
295
+ !ReactNativeFeatureFlags.useSharedAnimatedBackend()
296
+ ) {
297
+ return;
298
+ }
299
+
300
+ invariant(this.__isNative, 'Expected node to be marked as "native"');
301
+ // $FlowExpectedError[incompatible-type] - target.instance may be an HTMLElement but we need ReactNativeElement for Fabric
302
+ const shadowNode = getNodeFromPublicInstance(target.instance);
303
+ if (shadowNode == null) {
304
+ return;
305
+ }
306
+ NativeAnimatedHelper.API.connectAnimatedNodeToShadowNodeFamily(
307
+ this.__getNativeTag(),
308
+ shadowNode,
309
+ );
310
+ }
311
+
286
312
  #disconnectAnimatedView(target: TargetView): void {
287
313
  invariant(this.__isNative, 'Expected node to be marked as "native"');
288
314
  const viewTag = target.connectedViewTag;
@@ -29,7 +29,7 @@ export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 84;
31
31
  static patch: number = 0;
32
- static prerelease: string | null = 'nightly-20251205-95cc1e767';
32
+ static prerelease: string | null = 'nightly-20251207-e4a5a56df';
33
33
 
34
34
  static getVersionString(): string {
35
35
  return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
@@ -10,6 +10,7 @@
10
10
  #import <React/RCTInitializing.h>
11
11
  #import <React/RCTNativeAnimatedNodesManager.h>
12
12
  #import <React/RCTNativeAnimatedTurboModule.h>
13
+ #import <react/debug/react_native_assert.h>
13
14
  #import <react/featureflags/ReactNativeFeatureFlags.h>
14
15
 
15
16
  #import "RCTAnimationPlugins.h"
@@ -165,6 +166,12 @@ RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)
165
166
  }];
166
167
  }
167
168
 
169
+ RCT_EXPORT_METHOD(connectAnimatedNodeToShadowNodeFamily : (double)nodeTag shadowNode : (NSDictionary *)shadowNode)
170
+ {
171
+ // This method should only be called when using CxxNativeAnimated
172
+ react_native_assert(false);
173
+ }
174
+
168
175
  RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag)
169
176
  {
170
177
  [self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
@@ -48,6 +48,7 @@ Pod::Spec.new do |s|
48
48
  add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"])
49
49
  add_dependency(s, "React-NativeModulesApple")
50
50
  add_dependency(s, "React-featureflags")
51
+ add_dependency(s, "React-debug")
51
52
 
52
53
  add_rn_third_party_dependencies(s)
53
54
  add_rncore_dependency(s)
@@ -24,7 +24,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(84),
26
26
  RCTVersionPatch: @(0),
27
- RCTVersionPrerelease: @"nightly-20251205-95cc1e767",
27
+ RCTVersionPrerelease: @"nightly-20251207-e4a5a56df",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -365,6 +365,10 @@ namespace facebook::react {
365
365
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToView", @selector(connectAnimatedNodeToView:viewTag:), args, count);
366
366
  }
367
367
 
368
+ static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToShadowNodeFamily(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
369
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "connectAnimatedNodeToShadowNodeFamily", @selector(connectAnimatedNodeToShadowNodeFamily:shadowNode:), args, count);
370
+ }
371
+
368
372
  static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
369
373
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "disconnectAnimatedNodeFromView", @selector(disconnectAnimatedNodeFromView:viewTag:), args, count);
370
374
  }
@@ -448,6 +452,9 @@ namespace facebook::react {
448
452
  methodMap_["connectAnimatedNodeToView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToView};
449
453
 
450
454
 
455
+ methodMap_["connectAnimatedNodeToShadowNodeFamily"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_connectAnimatedNodeToShadowNodeFamily};
456
+
457
+
451
458
  methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_disconnectAnimatedNodeFromView};
452
459
 
453
460
 
@@ -323,6 +323,8 @@ saveValueCallback:(RCTResponseSenderBlock)saveValueCallback;
323
323
  - (void)extractAnimatedNodeOffset:(double)nodeTag;
324
324
  - (void)connectAnimatedNodeToView:(double)nodeTag
325
325
  viewTag:(double)viewTag;
326
+ - (void)connectAnimatedNodeToShadowNodeFamily:(double)nodeTag
327
+ shadowNode:(NSDictionary *)shadowNode;
326
328
  - (void)disconnectAnimatedNodeFromView:(double)nodeTag
327
329
  viewTag:(double)viewTag;
328
330
  - (void)restoreDefaultValues:(double)nodeTag;
@@ -1490,6 +1490,7 @@ protected:
1490
1490
  methodMap_["flattenAnimatedNodeOffset"] = MethodMetadata {.argCount = 1, .invoker = __flattenAnimatedNodeOffset};
1491
1491
  methodMap_["extractAnimatedNodeOffset"] = MethodMetadata {.argCount = 1, .invoker = __extractAnimatedNodeOffset};
1492
1492
  methodMap_["connectAnimatedNodeToView"] = MethodMetadata {.argCount = 2, .invoker = __connectAnimatedNodeToView};
1493
+ methodMap_["connectAnimatedNodeToShadowNodeFamily"] = MethodMetadata {.argCount = 2, .invoker = __connectAnimatedNodeToShadowNodeFamily};
1493
1494
  methodMap_["disconnectAnimatedNodeFromView"] = MethodMetadata {.argCount = 2, .invoker = __disconnectAnimatedNodeFromView};
1494
1495
  methodMap_["restoreDefaultValues"] = MethodMetadata {.argCount = 1, .invoker = __restoreDefaultValues};
1495
1496
  methodMap_["dropAnimatedNode"] = MethodMetadata {.argCount = 1, .invoker = __dropAnimatedNode};
@@ -1638,6 +1639,15 @@ private:
1638
1639
  count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asNumber());return jsi::Value::undefined();
1639
1640
  }
1640
1641
 
1642
+ static jsi::Value __connectAnimatedNodeToShadowNodeFamily(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
1643
+ static_assert(
1644
+ bridging::getParameterCount(&T::connectAnimatedNodeToShadowNodeFamily) == 3,
1645
+ "Expected connectAnimatedNodeToShadowNodeFamily(...) to have 3 parameters");
1646
+ bridging::callFromJs<void>(rt, &T::connectAnimatedNodeToShadowNodeFamily, static_cast<NativeAnimatedModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
1647
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber(),
1648
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asObject(rt));return jsi::Value::undefined();
1649
+ }
1650
+
1641
1651
  static jsi::Value __disconnectAnimatedNodeFromView(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
1642
1652
  static_assert(
1643
1653
  bridging::getParameterCount(&T::disconnectAnimatedNodeFromView) == 3,
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.84.0-nightly-20251205-95cc1e767
1
+ VERSION_NAME=0.84.0-nightly-20251207-e4a5a56df
2
2
  react.internal.publishingGroup=com.facebook.react
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -92,17 +92,21 @@ internal class FrameTimingsObserver(
92
92
  if (copyResult == PixelCopy.SUCCESS) {
93
93
  CoroutineScope(Dispatchers.Default).launch {
94
94
  try {
95
- val scaleFactor = 0.5f
95
+ val scaleFactor = 0.25f
96
96
  val scaledWidth = (width * scaleFactor).toInt()
97
97
  val scaledHeight = (height * scaleFactor).toInt()
98
98
  val scaledBitmap =
99
99
  Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true)
100
100
 
101
101
  val outputStream = ByteArrayOutputStream()
102
- scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
103
- val jpegBytes = outputStream.toByteArray()
104
- val jpegBase64 = Base64.encodeToString(jpegBytes, Base64.NO_WRAP)
105
- continuation.resume(jpegBase64)
102
+ val compressFormat =
103
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
104
+ Bitmap.CompressFormat.WEBP_LOSSY
105
+ else Bitmap.CompressFormat.WEBP
106
+ scaledBitmap.compress(compressFormat, 0, outputStream)
107
+ val bytes = outputStream.toByteArray()
108
+ val base64 = Base64.encodeToString(bytes, Base64.NO_WRAP)
109
+ continuation.resume(base64)
106
110
 
107
111
  scaledBitmap.recycle()
108
112
  } catch (e: Exception) {
@@ -15,6 +15,6 @@ public object ReactNativeVersion {
15
15
  "major" to 0,
16
16
  "minor" to 84,
17
17
  "patch" to 0,
18
- "prerelease" to "nightly-20251205-95cc1e767"
18
+ "prerelease" to "nightly-20251207-e4a5a56df"
19
19
  )
20
20
  }
@@ -780,8 +780,6 @@ public class ReactScrollView extends ScrollView
780
780
  if (mPagingEnabled) {
781
781
  flingAndSnap(correctedVelocityY);
782
782
  } else if (mScroller != null) {
783
- // FB SCROLLVIEW CHANGE
784
-
785
783
  // We provide our own version of fling that uses a different call to the standard OverScroller
786
784
  // which takes into account the possibility of adding new content while the ScrollView is
787
785
  // animating. Because we give essentially no max Y for the fling, the fling will continue as
@@ -806,8 +804,6 @@ public class ReactScrollView extends ScrollView
806
804
  );
807
805
 
808
806
  ViewCompat.postInvalidateOnAnimation(this);
809
-
810
- // END FB SCROLLVIEW CHANGE
811
807
  } else {
812
808
  super.fling(correctedVelocityY);
813
809
  }
@@ -1266,11 +1262,8 @@ public class ReactScrollView extends ScrollView
1266
1262
  @Override
1267
1263
  protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
1268
1264
  if (mScroller != null && mContentView != null) {
1269
- // FB SCROLLVIEW CHANGE
1270
-
1271
1265
  // This is part two of the reimplementation of fling to fix the bounce-back bug. See #fling()
1272
- // for
1273
- // more information.
1266
+ // for more information.
1274
1267
 
1275
1268
  if (!mScroller.isFinished() && mScroller.getCurrY() != mScroller.getFinalY()) {
1276
1269
  int scrollRange = getMaxScrollY();
@@ -1279,8 +1272,6 @@ public class ReactScrollView extends ScrollView
1279
1272
  scrollY = scrollRange;
1280
1273
  }
1281
1274
  }
1282
-
1283
- // END FB SCROLLVIEW CHANGE
1284
1275
  }
1285
1276
 
1286
1277
  if (ReactNativeFeatureFlags.shouldTriggerResponderTransferOnScrollAndroid()
@@ -219,6 +219,7 @@ internal class ReactTextViewAccessibilityDelegate(
219
219
  node.contentDescription = accessibleTextSpan.description
220
220
  node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK)
221
221
  node.setBoundsInParent(bounds)
222
+ node.setClickable(true)
222
223
  node.roleDescription = hostView.resources.getString(R.string.link_description)
223
224
  node.className = AccessibilityRole.getValue(AccessibilityRole.BUTTON)
224
225
  }
@@ -902,21 +902,7 @@ internal object TextLayoutManager {
902
902
  paint: TextPaint,
903
903
  ): Unit {
904
904
  var boring = isBoring(text, paint)
905
- var layout =
906
- createLayout(
907
- text,
908
- boring,
909
- width,
910
- widthYogaMeasureMode,
911
- includeFontPadding,
912
- textBreakStrategy,
913
- hyphenationFrequency,
914
- alignment,
915
- justificationMode,
916
- null,
917
- ReactConstants.UNSET,
918
- paint,
919
- )
905
+ var layout: Layout
920
906
 
921
907
  // Minimum font size is 4pts to match the iOS implementation.
922
908
  val minimumFontSize =
@@ -929,20 +915,20 @@ internal object TextLayoutManager {
929
915
  currentFontSize = max(currentFontSize, span.size).toInt()
930
916
  }
931
917
 
932
- val initialFontSize = currentFontSize
933
- while (
934
- currentFontSize > minimumFontSize &&
935
- ((maximumNumberOfLines != ReactConstants.UNSET &&
936
- maximumNumberOfLines != 0 &&
937
- layout.lineCount > maximumNumberOfLines) ||
938
- (heightYogaMeasureMode != YogaMeasureMode.UNDEFINED && layout.height > height) ||
939
- (text.length == 1 && layout.getLineWidth(0) > width))
940
- ) {
941
- // TODO: We could probably use a smarter algorithm here. This will require 0(n)
942
- // measurements based on the number of points the font size needs to be reduced by.
943
- currentFontSize -= max(1, 1.dpToPx().toInt())
918
+ var intervalStart = minimumFontSize
919
+ var intervalEnd = currentFontSize
920
+ var previousFontSize = currentFontSize
944
921
 
945
- val ratio = currentFontSize.toFloat() / initialFontSize.toFloat()
922
+ // `true` instead of `intervalStart != intervalEnd` so that the last iteration where both are at
923
+ // the same size goes through and updates all relevant objects with the final font size
924
+ while (true) {
925
+ // Always use the point closer to the end of the interval, this way at the end when
926
+ // end - start == 1, we land at current = end instead of current = start. In the first case
927
+ // one measurement may be enough if intervalEnd is small enough to fit. In the second case
928
+ // we always end up doing two measurements to check whether intervalEnd would fit.
929
+ val currentFontSize = (intervalStart + intervalEnd + 1) / 2
930
+
931
+ val ratio = currentFontSize.toFloat() / previousFontSize.toFloat()
946
932
  paint.textSize = max((paint.textSize * ratio).toInt(), minimumFontSize).toFloat()
947
933
 
948
934
  val sizeSpans = text.getSpans(0, text.length, ReactAbsoluteSizeSpan::class.java)
@@ -973,6 +959,34 @@ internal object TextLayoutManager {
973
959
  ReactConstants.UNSET,
974
960
  paint,
975
961
  )
962
+
963
+ if (intervalStart == intervalEnd) {
964
+ // everything is updated at this point
965
+ break
966
+ }
967
+
968
+ val singleLineTextExceedsWidth = text.length == 1 && layout.getLineWidth(0) > width
969
+ val exceedsHeight =
970
+ heightYogaMeasureMode != YogaMeasureMode.UNDEFINED && layout.height > height
971
+ val exceedsMaximumNumberOfLines =
972
+ maximumNumberOfLines != ReactConstants.UNSET &&
973
+ maximumNumberOfLines != 0 &&
974
+ layout.lineCount > maximumNumberOfLines
975
+
976
+ if (
977
+ currentFontSize > minimumFontSize &&
978
+ (exceedsMaximumNumberOfLines || exceedsHeight || singleLineTextExceedsWidth)
979
+ ) {
980
+ // Text doesn't fit the constraints. If intervalEnd - intervalStart == 1, it's known that
981
+ // the correct font size is intervalStart. Set intervalEnd to match intervalStart and do one
982
+ // more iteration to update layout correctly.
983
+ intervalEnd = if (intervalEnd - intervalStart == 1) intervalStart else currentFontSize
984
+ } else {
985
+ // Text fits the constraints
986
+ intervalStart = currentFontSize
987
+ }
988
+
989
+ previousFontSize = currentFontSize
976
990
  }
977
991
  }
978
992
 
@@ -22,7 +22,7 @@ constexpr struct {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 84;
24
24
  int32_t Patch = 0;
25
- std::string_view Prerelease = "nightly-20251205-95cc1e767";
25
+ std::string_view Prerelease = "nightly-20251207-e4a5a56df";
26
26
  } ReactNativeVersion;
27
27
 
28
28
  } // namespace facebook::react
@@ -231,8 +231,9 @@ inline char hexDigit(unsigned x) {
231
231
  // ASCII characters
232
232
  bool isAllASCII(const char16_t* utf16, size_t length) {
233
233
  for (const char16_t* e = utf16 + length; utf16 != e; ++utf16) {
234
- if (*utf16 > 0x7F)
234
+ if (*utf16 > 0x7F) {
235
235
  return false;
236
+ }
236
237
  }
237
238
  return true;
238
239
  }
@@ -147,7 +147,7 @@ class HostAgent::Impl final {
147
147
  if (InspectorFlags::getInstance().getNetworkInspectionEnabled()) {
148
148
  if (req.method == "Network.enable") {
149
149
  auto& inspector = getInspectorInstance();
150
- if (inspector.getSystemState().registeredPagesCount > 1) {
150
+ if (inspector.getSystemState().registeredHostsCount > 1) {
151
151
  frontendChannel_(
152
152
  cdp::jsonError(
153
153
  req.id,
@@ -231,7 +231,7 @@ class HostAgent::Impl final {
231
231
  "ReactNativeApplication.metadataUpdated",
232
232
  createHostMetadataPayload(hostMetadata_)));
233
233
  auto& inspector = getInspectorInstance();
234
- bool isSingleHost = inspector.getSystemState().registeredPagesCount <= 1;
234
+ bool isSingleHost = inspector.getSystemState().registeredHostsCount <= 1;
235
235
  if (!isSingleHost) {
236
236
  emitSystemStateChanged(isSingleHost);
237
237
  }
@@ -67,8 +67,8 @@ class InspectorImpl : public IInspector {
67
67
  public:
68
68
  explicit SystemStateListener(InspectorSystemState& state) : state_(state) {}
69
69
 
70
- void onPageAdded(int /*pageId*/) override {
71
- state_.registeredPagesCount++;
70
+ void unstable_onHostTargetAdded() override {
71
+ state_.registeredHostsCount++;
72
72
  }
73
73
 
74
74
  private:
@@ -94,6 +94,7 @@ class InspectorImpl : public IInspector {
94
94
  ConnectFunc connectFunc_;
95
95
  InspectorTargetCapabilities capabilities_;
96
96
  };
97
+
97
98
  mutable std::mutex mutex_;
98
99
  int nextPageId_{1};
99
100
  std::map<int, Page> pages_;
@@ -142,9 +143,13 @@ int InspectorImpl::addPage(
142
143
  pageId,
143
144
  Page{pageId, description, vm, std::move(connectFunc), capabilities});
144
145
 
145
- for (const auto& listenerWeak : listeners_) {
146
- if (auto listener = listenerWeak.lock()) {
147
- listener->onPageAdded(pageId);
146
+ // Strong assumption: If prefersFuseboxFrontend is set, the page added is a
147
+ // HostTarget and not a legacy Hermes runtime target.
148
+ if (capabilities.prefersFuseboxFrontend) {
149
+ for (const auto& listenerWeak : listeners_) {
150
+ if (auto listener = listenerWeak.lock()) {
151
+ listener->unstable_onHostTargetAdded();
152
+ }
148
153
  }
149
154
  }
150
155
 
@@ -53,7 +53,7 @@ using InspectorPage = InspectorPageDescription;
53
53
 
54
54
  struct InspectorSystemState {
55
55
  /** The total count of pages registered during the app lifetime. */
56
- int registeredPagesCount;
56
+ int registeredHostsCount;
57
57
  };
58
58
 
59
59
  /// IRemoteConnection allows the VM to send debugger messages to the client.
@@ -83,7 +83,7 @@ class JSINSPECTOR_EXPORT ILocalConnection : public IDestructible {
83
83
  class JSINSPECTOR_EXPORT IPageStatusListener : public IDestructible {
84
84
  public:
85
85
  virtual ~IPageStatusListener() = 0;
86
- virtual void onPageAdded(int /*pageId*/) {}
86
+ virtual void unstable_onHostTargetAdded() {}
87
87
  virtual void onPageRemoved(int /*pageId*/) {}
88
88
  };
89
89
 
@@ -50,7 +50,7 @@ TracingAgent::~TracingAgent() {
50
50
  bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
51
51
  if (req.method == "Tracing.start") {
52
52
  auto& inspector = getInspectorInstance();
53
- if (inspector.getSystemState().registeredPagesCount > 1) {
53
+ if (inspector.getSystemState().registeredHostsCount > 1) {
54
54
  frontendChannel_(
55
55
  cdp::jsonError(
56
56
  req.id,
@@ -9,9 +9,9 @@
9
9
 
10
10
  #include <glog/logging.h>
11
11
  #include <jsi/JSIDynamic.h>
12
+ #include <react/renderer/bridging/bridging.h>
12
13
 
13
14
  namespace facebook::react {
14
-
15
15
  AnimatedModule::AnimatedModule(
16
16
  std::shared_ptr<CallInvoker> jsInvoker,
17
17
  std::shared_ptr<NativeAnimatedNodesManagerProvider> nodesManagerProvider)
@@ -160,6 +160,19 @@ void AnimatedModule::connectAnimatedNodeToView(
160
160
  ConnectAnimatedNodeToViewOp{.nodeTag = nodeTag, .viewTag = viewTag});
161
161
  }
162
162
 
163
+ void AnimatedModule::connectAnimatedNodeToShadowNodeFamily(
164
+ jsi::Runtime& rt,
165
+ Tag nodeTag,
166
+ jsi::Object shadowNodeObj) {
167
+ const auto& shadowNode = Bridging<std::shared_ptr<const ShadowNode>>::fromJs(
168
+ rt, jsi::Value(rt, shadowNodeObj));
169
+
170
+ operations_.emplace_back(
171
+ ConnectAnimatedNodeToShadowNodeFamilyOp{
172
+ .nodeTag = nodeTag,
173
+ .shadowNodeFamily = shadowNode->getFamilyShared()});
174
+ }
175
+
163
176
  void AnimatedModule::disconnectAnimatedNodeFromView(
164
177
  jsi::Runtime& /*rt*/,
165
178
  Tag nodeTag,
@@ -282,6 +295,11 @@ void AnimatedModule::executeOperation(
282
295
  DisconnectAnimatedNodeFromViewOp>) {
283
296
  nodesManager->disconnectAnimatedNodeFromView(
284
297
  op.nodeTag, op.viewTag);
298
+ } else if constexpr (std::is_same_v<
299
+ T,
300
+ ConnectAnimatedNodeToShadowNodeFamilyOp>) {
301
+ nodesManager->connectAnimatedNodeToShadowNodeFamily(
302
+ op.nodeTag, op.shadowNodeFamily);
285
303
  } else if constexpr (std::is_same_v<T, RestoreDefaultValuesOp>) {
286
304
  nodesManager->restoreDefaultValues(op.nodeTag);
287
305
  } else if constexpr (std::is_same_v<T, DropAnimatedNodeOp>) {
@@ -87,6 +87,11 @@ class AnimatedModule : public NativeAnimatedModuleCxxSpec<AnimatedModule>, publi
87
87
  Tag viewTag{};
88
88
  };
89
89
 
90
+ struct ConnectAnimatedNodeToShadowNodeFamilyOp {
91
+ Tag nodeTag{};
92
+ std::shared_ptr<const ShadowNodeFamily> shadowNodeFamily{};
93
+ };
94
+
90
95
  struct DisconnectAnimatedNodeFromViewOp {
91
96
  Tag nodeTag{};
92
97
  Tag viewTag{};
@@ -124,6 +129,7 @@ class AnimatedModule : public NativeAnimatedModuleCxxSpec<AnimatedModule>, publi
124
129
  SetAnimatedNodeOffsetOp,
125
130
  SetAnimatedNodeValueOp,
126
131
  ConnectAnimatedNodeToViewOp,
132
+ ConnectAnimatedNodeToShadowNodeFamilyOp,
127
133
  DisconnectAnimatedNodeFromViewOp,
128
134
  RestoreDefaultValuesOp,
129
135
  FlattenAnimatedNodeOffsetOp,
@@ -176,6 +182,8 @@ class AnimatedModule : public NativeAnimatedModuleCxxSpec<AnimatedModule>, publi
176
182
 
177
183
  void connectAnimatedNodeToView(jsi::Runtime &rt, Tag nodeTag, Tag viewTag);
178
184
 
185
+ void connectAnimatedNodeToShadowNodeFamily(jsi::Runtime &rt, Tag nodeTag, jsi::Object shadowNode);
186
+
179
187
  void disconnectAnimatedNodeFromView(jsi::Runtime &rt, Tag nodeTag, Tag viewTag);
180
188
 
181
189
  void restoreDefaultValues(jsi::Runtime &rt, Tag nodeTag);