react-native-tvos 0.77.0-0rc5 → 0.77.2-0

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 (80) hide show
  1. package/Libraries/Components/TV/TVFocusGuideView.js +0 -1
  2. package/Libraries/Components/View/ViewPropTypes.d.ts +4 -2
  3. package/Libraries/Core/ReactNativeVersion.js +2 -2
  4. package/Libraries/Core/setUpDeveloperTools.js +2 -3
  5. package/Libraries/Image/Image.android.js +2 -0
  6. package/Libraries/Image/ImageViewNativeComponent.js +3 -4
  7. package/Libraries/Image/RCTImageLoader.mm +9 -1
  8. package/Libraries/Pressability/Pressability.js +2 -2
  9. package/Libraries/Text/TextInput/RCTBaseTextInputView.mm +1 -1
  10. package/Libraries/Utilities/HMRClient.js +0 -28
  11. package/Libraries/Utilities/HMRClientProdShim.js +0 -1
  12. package/React/Base/RCTConvert.mm +3 -1
  13. package/React/Base/RCTVersion.m +2 -2
  14. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +85 -31
  15. package/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +1 -7
  16. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +5 -2
  17. package/React/Views/RCTTVView.m +5 -2
  18. package/React/Views/ScrollView/RCTScrollView.m +63 -26
  19. package/ReactAndroid/api/ReactAndroid.api +3 -0
  20. package/ReactAndroid/cmake-utils/ReactNative-application.cmake +13 -3
  21. package/ReactAndroid/gradle.properties +3 -3
  22. package/ReactAndroid/src/main/AndroidManifest.xml +1 -1
  23. package/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +8 -0
  24. package/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +12 -1
  25. package/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java +37 -0
  26. package/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java +6 -1
  27. package/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java +1 -1
  28. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/DebugOverlayController.java +7 -2
  29. package/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +8 -2
  30. package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +15 -8
  31. package/ReactAndroid/src/main/java/com/facebook/react/modules/i18nmanager/I18nManagerModule.kt +6 -1
  32. package/ReactAndroid/src/main/java/com/facebook/react/modules/permissions/PermissionsModule.kt +15 -4
  33. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
  34. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt +21 -41
  35. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/BackgroundDrawable.kt +0 -1
  36. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/BorderDrawable.kt +0 -1
  37. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java +0 -1
  38. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt +141 -158
  39. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/Drawable.kt +17 -0
  40. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/LayerDrawable.kt +19 -0
  41. package/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutlineDrawable.kt +0 -1
  42. package/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt +1 -1
  43. package/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +22 -2
  44. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java +10 -2
  45. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +36 -27
  46. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +3 -3
  47. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +16 -2
  48. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +38 -31
  49. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +4 -2
  50. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputLocalData.java +13 -2
  51. package/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java +12 -2
  52. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java +2 -1
  53. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +1 -0
  54. package/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt +15 -0
  55. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  56. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +22 -4
  57. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h +5 -0
  58. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +51 -22
  59. package/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp +6 -0
  60. package/ReactCommon/react/renderer/attributedstring/TextAttributes.h +2 -0
  61. package/ReactCommon/react/renderer/attributedstring/conversions.h +5 -0
  62. package/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +12 -0
  63. package/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +2 -3
  64. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h +24 -3
  65. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +6 -46
  66. package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +4 -5
  67. package/gradle/libs.versions.toml +1 -1
  68. package/package.json +15 -12
  69. package/react-native.config.js +11 -21
  70. package/scripts/codegen/generate-artifacts-executor.js +8 -4
  71. package/scripts/generate-codegen-artifacts.js +6 -1
  72. package/sdks/hermes-engine/hermes-utils.rb +2 -2
  73. package/sdks/hermesc/linux64-bin/hermesc +0 -0
  74. package/sdks/hermesc/osx-bin/hermes +0 -0
  75. package/sdks/hermesc/osx-bin/hermesc +0 -0
  76. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  77. package/sdks/hermesc/win64-bin/msvcp140.dll +0 -0
  78. package/sdks/hermesc/win64-bin/vcruntime140.dll +0 -0
  79. package/sdks/hermesc/win64-bin/vcruntime140_1.dll +0 -0
  80. package/src/private/featureflags/ReactNativeFeatureFlags.js +2 -2
@@ -410,23 +410,26 @@ public class TextLayoutManager {
410
410
  if (widthYogaMeasureMode == YogaMeasureMode.EXACTLY) {
411
411
  desiredWidth = width;
412
412
  }
413
-
414
413
  int hintWidth = (int) Math.ceil(desiredWidth);
415
- StaticLayout.Builder builder =
414
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
415
+ StaticLayout.Builder builder =
416
416
  StaticLayout.Builder.obtain(text, 0, spanLength, paint, hintWidth)
417
- .setAlignment(alignment)
418
- .setLineSpacing(0.f, 1.f)
419
- .setIncludePad(includeFontPadding)
420
- .setBreakStrategy(textBreakStrategy)
421
- .setHyphenationFrequency(hyphenationFrequency)
422
- .setTextDirection(
423
- isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
424
-
425
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
426
- builder.setUseLineSpacingFromFallbacks(true);
427
- }
417
+ .setAlignment(alignment)
418
+ .setLineSpacing(0.f, 1.f)
419
+ .setIncludePad(includeFontPadding)
420
+ .setBreakStrategy(textBreakStrategy)
421
+ .setHyphenationFrequency(hyphenationFrequency)
422
+ .setTextDirection(
423
+ isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
424
+
425
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
426
+ builder.setUseLineSpacingFromFallbacks(true);
427
+ }
428
428
 
429
- layout = builder.build();
429
+ layout = builder.build();
430
+ } else {
431
+ layout = new StaticLayout(text, paint, hintWidth, alignment, 1.0f, 0.0f, includeFontPadding);
432
+ }
430
433
 
431
434
  } else if (boring != null && (unconstrainedWidth || boring.width <= width)) {
432
435
  int boringLayoutWidth = boring.width;
@@ -445,25 +448,29 @@ public class TextLayoutManager {
445
448
  text, paint, boringLayoutWidth, alignment, 1.f, 0.f, boring, includeFontPadding);
446
449
  } else {
447
450
  // Is used for multiline, boring text and the width is known.
448
- StaticLayout.Builder builder =
449
- StaticLayout.Builder.obtain(text, 0, spanLength, paint, (int) Math.ceil(width))
450
- .setAlignment(alignment)
451
- .setLineSpacing(0.f, 1.f)
452
- .setIncludePad(includeFontPadding)
453
- .setBreakStrategy(textBreakStrategy)
454
- .setHyphenationFrequency(hyphenationFrequency)
455
- .setTextDirection(
456
- isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
457
-
458
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
459
- builder.setJustificationMode(justificationMode);
460
- }
451
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
452
+ StaticLayout.Builder builder =
453
+ StaticLayout.Builder.obtain(text, 0, spanLength, paint, (int) Math.ceil(width))
454
+ .setAlignment(alignment)
455
+ .setLineSpacing(0.f, 1.f)
456
+ .setIncludePad(includeFontPadding)
457
+ .setBreakStrategy(textBreakStrategy)
458
+ .setHyphenationFrequency(hyphenationFrequency)
459
+ .setTextDirection(
460
+ isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
461
+
462
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
463
+ builder.setJustificationMode(justificationMode);
464
+ }
461
465
 
462
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
463
- builder.setUseLineSpacingFromFallbacks(true);
464
- }
466
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
467
+ builder.setUseLineSpacingFromFallbacks(true);
468
+ }
465
469
 
466
- layout = builder.build();
470
+ layout = builder.build();
471
+ } else {
472
+ layout = new StaticLayout(text, paint, (int) Math.ceil(width), alignment, 1.0f, 0.0f, includeFontPadding);
473
+ }
467
474
  }
468
475
  return layout;
469
476
  }
@@ -232,7 +232,9 @@ public class ReactEditText extends AppCompatEditText {
232
232
  public void onDestroyActionMode(ActionMode mode) {}
233
233
  };
234
234
  setCustomSelectionActionModeCallback(customActionModeCallback);
235
- setCustomInsertionActionModeCallback(customActionModeCallback);
235
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
236
+ setCustomInsertionActionModeCallback(customActionModeCallback);
237
+ }
236
238
  }
237
239
 
238
240
  @Override
@@ -753,7 +755,7 @@ public class ReactEditText extends AppCompatEditText {
753
755
  }
754
756
  mDisableTextDiffing = false;
755
757
 
756
- if (getBreakStrategy() != reactTextUpdate.getTextBreakStrategy()) {
758
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getBreakStrategy() != reactTextUpdate.getTextBreakStrategy()) {
757
759
  setBreakStrategy(reactTextUpdate.getTextBreakStrategy());
758
760
  }
759
761
 
@@ -7,6 +7,10 @@
7
7
 
8
8
  package com.facebook.react.views.textinput;
9
9
 
10
+ import android.annotation.TargetApi;
11
+ import android.graphics.text.LineBreaker;
12
+ import android.os.Build;
13
+ import android.text.Layout;
10
14
  import android.text.SpannableStringBuilder;
11
15
  import android.util.TypedValue;
12
16
  import android.widget.EditText;
@@ -22,6 +26,7 @@ public final class ReactTextInputLocalData {
22
26
  private final int mBreakStrategy;
23
27
  private final CharSequence mPlaceholder;
24
28
 
29
+ @TargetApi(Build.VERSION_CODES.M)
25
30
  public ReactTextInputLocalData(EditText editText) {
26
31
  mText = new SpannableStringBuilder(editText.getText());
27
32
  mTextSize = editText.getTextSize();
@@ -29,7 +34,11 @@ public final class ReactTextInputLocalData {
29
34
  mPlaceholder = editText.getHint();
30
35
  mMinLines = editText.getMinLines();
31
36
  mMaxLines = editText.getMaxLines();
32
- mBreakStrategy = editText.getBreakStrategy();
37
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
38
+ mBreakStrategy = editText.getBreakStrategy();
39
+ } else {
40
+ mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
41
+ }
33
42
  }
34
43
 
35
44
  public void apply(EditText editText) {
@@ -39,6 +48,8 @@ public final class ReactTextInputLocalData {
39
48
  editText.setMaxLines(mMaxLines);
40
49
  editText.setInputType(mInputType);
41
50
  editText.setHint(mPlaceholder);
42
- editText.setBreakStrategy(mBreakStrategy);
51
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
52
+ editText.setBreakStrategy(mBreakStrategy);
53
+ }
43
54
  }
44
55
  }
@@ -7,6 +7,8 @@
7
7
 
8
8
  package com.facebook.react.views.textinput;
9
9
 
10
+ import android.annotation.TargetApi;
11
+ import android.os.Build;
10
12
  import android.text.Layout;
11
13
  import android.util.TypedValue;
12
14
  import android.view.ViewGroup;
@@ -32,6 +34,7 @@ import com.facebook.yoga.YogaMeasureMode;
32
34
  import com.facebook.yoga.YogaMeasureOutput;
33
35
  import com.facebook.yoga.YogaNode;
34
36
 
37
+ @TargetApi(Build.VERSION_CODES.M)
35
38
  @VisibleForTesting
36
39
  public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
37
40
  implements YogaMeasureFunction {
@@ -50,7 +53,9 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
50
53
  public ReactTextInputShadowNode(
51
54
  @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {
52
55
  super(reactTextViewManagerCallback);
53
- mTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
56
+ mTextBreakStrategy = (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
57
+ ? Layout.BREAK_STRATEGY_SIMPLE
58
+ : Layout.BREAK_STRATEGY_HIGH_QUALITY;
54
59
 
55
60
  initMeasureFunction();
56
61
  }
@@ -112,7 +117,8 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
112
117
  editText.setLines(mNumberOfLines);
113
118
  }
114
119
 
115
- if (editText.getBreakStrategy() != mTextBreakStrategy) {
120
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
121
+ && editText.getBreakStrategy() != mTextBreakStrategy) {
116
122
  editText.setBreakStrategy(mTextBreakStrategy);
117
123
  }
118
124
  }
@@ -175,6 +181,10 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
175
181
 
176
182
  @Override
177
183
  public void setTextBreakStrategy(@Nullable String textBreakStrategy) {
184
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
185
+ return;
186
+ }
187
+
178
188
  if (textBreakStrategy == null || "simple".equals(textBreakStrategy)) {
179
189
  mTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
180
190
  } else if ("highQuality".equals(textBreakStrategy)) {
@@ -13,6 +13,7 @@ import android.graphics.Color;
13
13
  import android.graphics.drawable.ColorDrawable;
14
14
  import android.graphics.drawable.Drawable;
15
15
  import android.graphics.drawable.RippleDrawable;
16
+ import android.os.Build;
16
17
  import android.util.TypedValue;
17
18
  import androidx.annotation.Nullable;
18
19
  import com.facebook.infer.annotation.Nullsafe;
@@ -79,7 +80,7 @@ public class ReactDrawableHelper {
79
80
 
80
81
  private static @Nullable Drawable setRadius(
81
82
  ReadableMap drawableDescriptionDict, @Nullable Drawable drawable) {
82
- if (drawableDescriptionDict.hasKey("rippleRadius") && drawable instanceof RippleDrawable) {
83
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && drawableDescriptionDict.hasKey("rippleRadius") && drawable instanceof RippleDrawable) {
83
84
  RippleDrawable rippleDrawable = (RippleDrawable) drawable;
84
85
  double rippleRadius = drawableDescriptionDict.getDouble("rippleRadius");
85
86
  rippleDrawable.setRadius((int) PixelUtil.toPixelFromDIP(rippleRadius));
@@ -110,6 +110,7 @@ public class ReactViewGroup extends ViewGroup
110
110
  private boolean trapFocusDown = false;
111
111
  private boolean trapFocusLeft = false;
112
112
  private boolean trapFocusRight = false;
113
+ public boolean hasTVPreferredFocus = false;
113
114
 
114
115
 
115
116
  /**
@@ -8,9 +8,11 @@
8
8
  package com.facebook.react.views.view
9
9
 
10
10
  import android.accessibilityservice.AccessibilityServiceInfo
11
+ import android.annotation.TargetApi
11
12
  import android.content.Context
12
13
  import android.content.pm.PackageManager
13
14
  import android.graphics.Rect
15
+ import android.os.Build
14
16
  import android.view.View
15
17
  import android.view.ViewGroup
16
18
  import android.view.accessibility.AccessibilityManager
@@ -82,6 +84,7 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
82
84
  return view
83
85
  }
84
86
 
87
+
85
88
  @ReactProp(name = "accessible")
86
89
  public open fun setAccessible(view: ReactViewGroup, accessible: Boolean) {
87
90
  view.isFocusable = accessible
@@ -109,6 +112,17 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
109
112
 
110
113
  @ReactProp(name = "hasTVPreferredFocus")
111
114
  public open fun setTVPreferredFocus(view: ReactViewGroup, hasTVPreferredFocus: Boolean) {
115
+ /*
116
+ * React prop functions like this one gets called repeatedly on the New Architecture
117
+ * no matter the prop has changed or not. Contrary to others, `hasTVPreferredFocus` has
118
+ * a side effect, calling `requestFocus` function on the view which disrupts the user flow
119
+ * and should only called once when the property changes to `true.
120
+ * We keep a special state in the View class and run a comparison here to mitigate
121
+ * that problem.
122
+ */
123
+ if (view.hasTVPreferredFocus == hasTVPreferredFocus) return;
124
+ view.hasTVPreferredFocus = hasTVPreferredFocus;
125
+
112
126
  if (hasTVPreferredFocus) {
113
127
  view.isFocusable = true
114
128
  view.isFocusableInTouchMode = true
@@ -269,6 +283,7 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
269
283
  BackgroundStyleApplicator.setFeedbackUnderlay(view, bg)
270
284
  }
271
285
 
286
+ @TargetApi(Build.VERSION_CODES.M)
272
287
  @ReactProp(name = "nativeForegroundAndroid")
273
288
  public open fun setNativeForeground(view: ReactViewGroup, foreground: ReadableMap?) {
274
289
  view.foreground =
@@ -17,8 +17,8 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 77;
20
- int32_t Patch = 0;
21
- std::string_view Prerelease = "0rc5";
20
+ int32_t Patch = 2;
21
+ std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
24
24
  } // namespace facebook::react
@@ -123,6 +123,15 @@ std::vector<ExportedMethod> parseExportedMethods(std::string moduleName, Class m
123
123
  NSArray<RCTMethodArgument *> *arguments;
124
124
  SEL objCMethodSelector = NSSelectorFromString(RCTParseMethodSignature(methodInfo->objcName, &arguments));
125
125
  NSMethodSignature *objCMethodSignature = [moduleClass instanceMethodSignatureForSelector:objCMethodSelector];
126
+ if (objCMethodSignature == nullptr) {
127
+ RCTLogWarn(
128
+ @"The objective-c `%s` method signature for the JS method `%@` can not be found in the ObjecitveC definition of the %s module.\nThe `%@` JS method will not be available.",
129
+ methodInfo->objcName,
130
+ jsMethodName,
131
+ moduleName.c_str(),
132
+ jsMethodName);
133
+ continue;
134
+ }
126
135
  std::string objCMethodReturnType = [objCMethodSignature methodReturnType];
127
136
 
128
137
  if (objCMethodSignature.numberOfArguments - 2 != [arguments count]) {
@@ -337,7 +346,7 @@ void ObjCInteropTurboModule::setInvocationArg(
337
346
  SEL selector = selectorForType(argumentType);
338
347
 
339
348
  if ([RCTConvert respondsToSelector:selector]) {
340
- id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
349
+ id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
341
350
 
342
351
  if (objCArgType == @encode(char)) {
343
352
  char arg = RCTConvertTo<char>(selector, objCArg);
@@ -437,6 +446,15 @@ void ObjCInteropTurboModule::setInvocationArg(
437
446
 
438
447
  if (objCArgType == @encode(id)) {
439
448
  id arg = RCTConvertTo<id>(selector, objCArg);
449
+
450
+ // Handle the special case where there is an argument and it must be nil
451
+ // Without this check, the JS side will receive an object.
452
+ // See: discussion at
453
+ // https://github.com/facebook/react-native/pull/49250#issuecomment-2668465893
454
+ if (arg == [NSNull null]) {
455
+ return;
456
+ }
457
+
440
458
  if (arg) {
441
459
  [retainedObjectsForInvocation addObject:arg];
442
460
  }
@@ -491,7 +509,7 @@ void ObjCInteropTurboModule::setInvocationArg(
491
509
  }
492
510
 
493
511
  RCTResponseSenderBlock arg =
494
- (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
512
+ (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
495
513
  if (arg) {
496
514
  [retainedObjectsForInvocation addObject:arg];
497
515
  }
@@ -506,7 +524,7 @@ void ObjCInteropTurboModule::setInvocationArg(
506
524
  }
507
525
 
508
526
  RCTResponseSenderBlock senderBlock =
509
- (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
527
+ (RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
510
528
  RCTResponseErrorBlock arg = ^(NSError *error) {
511
529
  senderBlock(@[ RCTJSErrorFromNSError(error) ]);
512
530
  };
@@ -536,7 +554,7 @@ void ObjCInteropTurboModule::setInvocationArg(
536
554
  runtime, errorPrefix + "JavaScript argument must be a plain object. Got " + getType(runtime, jsiArg));
537
555
  }
538
556
 
539
- id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
557
+ id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
540
558
 
541
559
  RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
542
560
  RCTManagedPointer *box = convert([RCTCxxConvert class], selector, arg);
@@ -32,6 +32,11 @@ using EventEmitterCallback = std::function<void(const std::string &, id)>;
32
32
  namespace TurboModuleConvertUtils {
33
33
  jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
34
34
  id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
35
+ id convertJSIValueToObjCObject(
36
+ jsi::Runtime &runtime,
37
+ const jsi::Value &value,
38
+ std::shared_ptr<CallInvoker> jsInvoker,
39
+ BOOL useNSNull);
35
40
  } // namespace TurboModuleConvertUtils
36
41
 
37
42
  template <>
@@ -57,7 +57,7 @@ static jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *va
57
57
 
58
58
  static jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value)
59
59
  {
60
- return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: "");
60
+ return jsi::String::createFromUtf8(runtime, [value UTF8String] ? [value UTF8String] : "");
61
61
  }
62
62
 
63
63
  static jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value)
@@ -111,21 +111,27 @@ static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::St
111
111
  return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
112
112
  }
113
113
 
114
- static NSArray *
115
- convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker)
114
+ static NSArray *convertJSIArrayToNSArray(
115
+ jsi::Runtime &runtime,
116
+ const jsi::Array &value,
117
+ std::shared_ptr<CallInvoker> jsInvoker,
118
+ BOOL useNSNull)
116
119
  {
117
120
  size_t size = value.size(runtime);
118
121
  NSMutableArray *result = [NSMutableArray new];
119
122
  for (size_t i = 0; i < size; i++) {
120
123
  // Insert kCFNull when it's `undefined` value to preserve the indices.
121
- id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker);
124
+ id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull);
122
125
  [result addObject:convertedObject ? convertedObject : (id)kCFNull];
123
126
  }
124
127
  return [result copy];
125
128
  }
126
129
 
127
- static NSDictionary *
128
- convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker)
130
+ static NSDictionary *convertJSIObjectToNSDictionary(
131
+ jsi::Runtime &runtime,
132
+ const jsi::Object &value,
133
+ std::shared_ptr<CallInvoker> jsInvoker,
134
+ BOOL useNSNull)
129
135
  {
130
136
  jsi::Array propertyNames = value.getPropertyNames(runtime);
131
137
  size_t size = propertyNames.size(runtime);
@@ -133,7 +139,7 @@ convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value,
133
139
  for (size_t i = 0; i < size; i++) {
134
140
  jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
135
141
  NSString *k = convertJSIStringToNSString(runtime, name);
136
- id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker);
142
+ id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, useNSNull);
137
143
  if (v) {
138
144
  result[k] = v;
139
145
  }
@@ -161,9 +167,21 @@ convertJSIFunctionToCallback(jsi::Runtime &rt, jsi::Function &&function, std::sh
161
167
 
162
168
  id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
163
169
  {
164
- if (value.isUndefined() || value.isNull()) {
170
+ return convertJSIValueToObjCObject(runtime, value, jsInvoker, NO);
171
+ }
172
+
173
+ id convertJSIValueToObjCObject(
174
+ jsi::Runtime &runtime,
175
+ const jsi::Value &value,
176
+ std::shared_ptr<CallInvoker> jsInvoker,
177
+ BOOL useNSNull)
178
+ {
179
+ if (value.isUndefined() || (value.isNull() && !useNSNull)) {
165
180
  return nil;
166
181
  }
182
+ if (value.isNull() && useNSNull) {
183
+ return [NSNull null];
184
+ }
167
185
  if (value.isBool()) {
168
186
  return @(value.getBool());
169
187
  }
@@ -176,12 +194,12 @@ id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, s
176
194
  if (value.isObject()) {
177
195
  jsi::Object o = value.getObject(runtime);
178
196
  if (o.isArray(runtime)) {
179
- return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker);
197
+ return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, useNSNull);
180
198
  }
181
199
  if (o.isFunction(runtime)) {
182
200
  return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker);
183
201
  }
184
- return convertJSIObjectToNSDictionary(runtime, o, jsInvoker);
202
+ return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull);
185
203
  }
186
204
 
187
205
  throw std::runtime_error("Unsupported jsi::Value kind");
@@ -195,7 +213,11 @@ static jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string
195
213
  /**
196
214
  * Creates JSError with current JS runtime and NSException stack trace.
197
215
  */
198
- static jsi::JSError convertNSExceptionToJSError(jsi::Runtime &runtime, NSException *exception)
216
+ static jsi::JSError convertNSExceptionToJSError(
217
+ jsi::Runtime &runtime,
218
+ NSException *exception,
219
+ const std::string &moduleName,
220
+ const std::string &methodName)
199
221
  {
200
222
  std::string reason = [exception.reason UTF8String];
201
223
 
@@ -206,7 +228,8 @@ static jsi::JSError convertNSExceptionToJSError(jsi::Runtime &runtime, NSExcepti
206
228
  cause.setProperty(
207
229
  runtime, "stackReturnAddresses", convertNSArrayToJSIArray(runtime, exception.callStackReturnAddresses));
208
230
 
209
- jsi::Value error = createJSRuntimeError(runtime, "Exception in HostFunction: " + reason);
231
+ std::string message = moduleName + "." + methodName + " raised an exception: " + reason;
232
+ jsi::Value error = createJSRuntimeError(runtime, message);
210
233
  error.asObject(runtime).setProperty(runtime, "cause", std::move(cause));
211
234
  return {runtime, std::move(error)};
212
235
  }
@@ -338,28 +361,34 @@ id ObjCTurboModule::performMethodInvocation(
338
361
  }
339
362
 
340
363
  if (isSync) {
341
- TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str());
364
+ TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName);
342
365
  } else {
343
- TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter);
366
+ TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, asyncCallCounter);
344
367
  }
345
368
 
346
369
  @try {
347
370
  [inv invokeWithTarget:strongModule];
348
371
  } @catch (NSException *exception) {
349
- throw convertNSExceptionToJSError(runtime, exception);
372
+ if (isSync) {
373
+ // We can only convert NSException to JSError in sync method calls.
374
+ // See https://github.com/reactwg/react-native-new-architecture/discussions/276#discussioncomment-12567155
375
+ throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
376
+ } else {
377
+ @throw exception;
378
+ }
350
379
  } @finally {
351
380
  [retainedObjectsForInvocation removeAllObjects];
352
381
  }
353
382
 
354
383
  if (!isSync) {
355
- TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter);
384
+ TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, asyncCallCounter);
356
385
  return;
357
386
  }
358
387
 
359
388
  void *rawResult;
360
389
  [inv getReturnValue:&rawResult];
361
390
  result = (__bridge id)rawResult;
362
- TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodNameStr.c_str());
391
+ TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName);
363
392
  };
364
393
 
365
394
  if (isSync) {
@@ -401,23 +430,23 @@ void ObjCTurboModule::performVoidMethodInvocation(
401
430
  }
402
431
 
403
432
  if (shouldVoidMethodsExecuteSync_) {
404
- TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str());
433
+ TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName);
405
434
  } else {
406
- TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter);
435
+ TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, asyncCallCounter);
407
436
  }
408
437
 
409
438
  @try {
410
439
  [inv invokeWithTarget:strongModule];
411
440
  } @catch (NSException *exception) {
412
- throw convertNSExceptionToJSError(runtime, exception);
441
+ throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
413
442
  } @finally {
414
443
  [retainedObjectsForInvocation removeAllObjects];
415
444
  }
416
445
 
417
446
  if (shouldVoidMethodsExecuteSync_) {
418
- TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodNameStr.c_str());
447
+ TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName);
419
448
  } else {
420
- TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter);
449
+ TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, asyncCallCounter);
421
450
  }
422
451
 
423
452
  return;
@@ -46,6 +46,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
46
46
  allowFontScaling = textAttributes.allowFontScaling.has_value()
47
47
  ? textAttributes.allowFontScaling
48
48
  : allowFontScaling;
49
+ maxFontSizeMultiplier = !std::isnan(textAttributes.maxFontSizeMultiplier)
50
+ ? textAttributes.maxFontSizeMultiplier
51
+ : maxFontSizeMultiplier;
49
52
  dynamicTypeRamp = textAttributes.dynamicTypeRamp.has_value()
50
53
  ? textAttributes.dynamicTypeRamp
51
54
  : dynamicTypeRamp;
@@ -168,6 +171,7 @@ bool TextAttributes::operator==(const TextAttributes& rhs) const {
168
171
  rhs.accessibilityRole,
169
172
  rhs.role,
170
173
  rhs.textTransform) &&
174
+ floatEquality(maxFontSizeMultiplier, rhs.maxFontSizeMultiplier) &&
171
175
  floatEquality(opacity, rhs.opacity) &&
172
176
  floatEquality(fontSize, rhs.fontSize) &&
173
177
  floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
@@ -211,6 +215,8 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
211
215
  debugStringConvertibleItem("fontStyle", fontStyle),
212
216
  debugStringConvertibleItem("fontVariant", fontVariant),
213
217
  debugStringConvertibleItem("allowFontScaling", allowFontScaling),
218
+ debugStringConvertibleItem(
219
+ "maxFontSizeMultiplier", maxFontSizeMultiplier),
214
220
  debugStringConvertibleItem("dynamicTypeRamp", dynamicTypeRamp),
215
221
  debugStringConvertibleItem("letterSpacing", letterSpacing),
216
222
 
@@ -51,6 +51,7 @@ class TextAttributes : public DebugStringConvertible {
51
51
  std::optional<FontStyle> fontStyle{};
52
52
  std::optional<FontVariant> fontVariant{};
53
53
  std::optional<bool> allowFontScaling{};
54
+ Float maxFontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
54
55
  std::optional<DynamicTypeRamp> dynamicTypeRamp{};
55
56
  Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
56
57
  std::optional<TextTransform> textTransform{};
@@ -117,6 +118,7 @@ struct hash<facebook::react::TextAttributes> {
117
118
  textAttributes.opacity,
118
119
  textAttributes.fontFamily,
119
120
  textAttributes.fontSize,
121
+ textAttributes.maxFontSizeMultiplier,
120
122
  textAttributes.fontSizeMultiplier,
121
123
  textAttributes.fontWeight,
122
124
  textAttributes.fontStyle,
@@ -910,6 +910,7 @@ constexpr static MapBuffer::Key TA_KEY_LINE_BREAK_STRATEGY = 25;
910
910
  constexpr static MapBuffer::Key TA_KEY_ROLE = 26;
911
911
  constexpr static MapBuffer::Key TA_KEY_TEXT_TRANSFORM = 27;
912
912
  constexpr static MapBuffer::Key TA_KEY_ALIGNMENT_VERTICAL = 28;
913
+ constexpr static MapBuffer::Key TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
913
914
 
914
915
  // constants for ParagraphAttributes serialization
915
916
  constexpr static MapBuffer::Key PA_KEY_MAX_NUMBER_OF_LINES = 0;
@@ -1004,6 +1005,10 @@ inline MapBuffer toMapBuffer(const TextAttributes& textAttributes) {
1004
1005
  builder.putBool(
1005
1006
  TA_KEY_ALLOW_FONT_SCALING, *textAttributes.allowFontScaling);
1006
1007
  }
1008
+ if (!std::isnan(textAttributes.maxFontSizeMultiplier)) {
1009
+ builder.putDouble(
1010
+ TA_KEY_MAX_FONT_SIZE_MULTIPLIER, textAttributes.maxFontSizeMultiplier);
1011
+ }
1007
1012
  if (!std::isnan(textAttributes.letterSpacing)) {
1008
1013
  builder.putDouble(TA_KEY_LETTER_SPACING, textAttributes.letterSpacing);
1009
1014
  }
@@ -73,6 +73,12 @@ static TextAttributes convertRawProp(
73
73
  "allowFontScaling",
74
74
  sourceTextAttributes.allowFontScaling,
75
75
  defaultTextAttributes.allowFontScaling);
76
+ textAttributes.maxFontSizeMultiplier = convertRawProp(
77
+ context,
78
+ rawProps,
79
+ "maxFontSizeMultiplier",
80
+ sourceTextAttributes.maxFontSizeMultiplier,
81
+ defaultTextAttributes.maxFontSizeMultiplier);
76
82
  textAttributes.dynamicTypeRamp = convertRawProp(
77
83
  context,
78
84
  rawProps,
@@ -266,6 +272,12 @@ void BaseTextProps::setProp(
266
272
  defaults, value, textAttributes, fontVariant, "fontVariant");
267
273
  REBUILD_FIELD_SWITCH_CASE(
268
274
  defaults, value, textAttributes, allowFontScaling, "allowFontScaling");
275
+ REBUILD_FIELD_SWITCH_CASE(
276
+ defaults,
277
+ value,
278
+ textAttributes,
279
+ maxFontSizeMultiplier,
280
+ "maxFontSizeMultiplier");
269
281
  REBUILD_FIELD_SWITCH_CASE(
270
282
  defaults, value, textAttributes, letterSpacing, "letterSpacing");
271
283
  REBUILD_FIELD_SWITCH_CASE(
@@ -208,8 +208,7 @@ inline bool operator==(
208
208
  return areAttributedStringsEquivalentLayoutWise(
209
209
  lhs.attributedString, rhs.attributedString) &&
210
210
  lhs.paragraphAttributes == rhs.paragraphAttributes &&
211
- lhs.layoutConstraints.maximumSize.width ==
212
- rhs.layoutConstraints.maximumSize.width;
211
+ lhs.layoutConstraints == rhs.layoutConstraints;
213
212
  }
214
213
 
215
214
  inline bool operator!=(
@@ -243,7 +242,7 @@ struct hash<facebook::react::TextMeasureCacheKey> {
243
242
  return facebook::react::hash_combine(
244
243
  attributedStringHashLayoutWise(key.attributedString),
245
244
  key.paragraphAttributes,
246
- key.layoutConstraints.maximumSize.width);
245
+ key.layoutConstraints);
247
246
  }
248
247
  };
249
248