Compare commits

...
Author SHA1 Message Date
Nicola Corti 1887586ee2 Temporarily disable testing on Node 20 2025-07-21 15:58:31 +01:00
Nicola Corti a098b90aed Specify Node version for test_js 2025-07-21 15:53:37 +01:00
Nicola Corti 5f98d404b4 Do not setup-node twice 2025-07-21 15:43:23 +01:00
Riccardo CipolleschiandFacebook GitHub Bot aa27cdba9b Revert "Fix Dimensions window values on Android < 15 (#52481)" (#52732)
Summary:
Commit 86994a6e22 breaks Android for API level 24. Since it has landed last week, we had CI red. reverting this change while we found a valid fix forward.

## Changelog:
[Android][Changed] - Reverted fix `Dimensions` `window` values on Android < 15 when edge-to-edge is enabled

Pull Request resolved: https://github.com/facebook/react-native/pull/52732

Test Plan:
GHA is green.:
https://github.com/facebook/react-native/actions/runs/16414048087/job/46377878366?pr=52732
https://github.com/facebook/react-native/actions/runs/16414048087/job/46377878383?pr=52732

Reviewed By: cortinico, rshest

Differential Revision: D78657920

Pulled By: cipolleschi

fbshipit-source-id: 396a48c9aa7bde3109e25200fe2decc9977efda4
2025-07-21 05:31:43 -07:00
Mateo GuzmánandFacebook GitHub Bot 57b5d7bf6f Make UIManagerModuleListener internal (#52727)
Summary:
This class can be internalized. I've checked there are [no relevant OSS usages](https://github.com/search?type=code&q=NOT+is%3Afork+NOT+org%3Afacebook+NOT+repo%3Areact-native-tvos%2Freact-native-tvos+NOT+repo%3Anuagoz%2Freact-native+NOT+repo%3A2lambda123%2Freact-native+NOT+repo%3Abeanchips%2Ffacebookreactnative+NOT+repo%3AfabOnReact%2Freact-native-notes+NOT+user%3Ahuntie+NOT+user%3Acortinico+NOT+repo%3AMaxdev18%2Fpowersync_app+NOT+repo%3Acarter-0%2Finstagram-decompiled+NOT+repo%3Am0mosenpai%2Finstadamn+NOT+repo%3AA-Star100%2FA-Star100-AUG2-2024+NOT+repo%3Alclnrd%2Fdetox-scrollview-reproductible+NOT+repo%3ADionisisChytiris%2FWorldWiseTrivia_Main+NOT+repo%3Apast3l%2Fhi2+NOT+repo%3AoneDotpy%2FCaribouQuest+NOT+repo%3Abejayoharen%2Fdailytodo+NOT+repo%3Amolangning%2Freversing-discord+NOT+repo%3AScottPrzy%2Freact-native+NOT+repo%3Agabrieldonadel%2Freact-native-visionos+NOT+repo%3AGabriel2308%2FTestes-Soft+NOT+repo%3Adawnzs03%2FflakyBuild+NOT+repo%3Acga2351%2Fcode+NOT+repo%3Astreeg%2Ftcc+NOT+repo%3Asoftware-mansion-labs%2Freact-native-swiftui+NOT+repo%3Apkcsecurity%2Fdecompiled-lightbulb+com.facebook.react.uimanager.UIManagerModuleListener).

## Changelog:

[INTERNAL] - Make com.facebook.react.uimanager.UIManagerModuleListener internal

Pull Request resolved: https://github.com/facebook/react-native/pull/52727

Test Plan:
```bash
yarn test-android
yarn android
```

Reviewed By: cortinico

Differential Revision: D78656919

Pulled By: rshest

fbshipit-source-id: cc9756bdd1c9e702f26cd87493c6f291792b7526
2025-07-21 05:12:22 -07:00
Nick LefeverandFacebook GitHub Bot 1f6eb884bf Fix modal crash on create with initial props (#52729)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52729

The Modal view creation contains initial properties when using Props 2.0. This diff adds support for Modal view creations having initial properties by allowing the `updateProperties` fast path only if the dialog is already initialized.

Without the change, the fast path gets called before the dialog could be initialized which leads to throwing an exception when the dialog is being checked to see if it is initialized.

Changelog: [Internal]

Reviewed By: sammy-SC

Differential Revision: D78638902

fbshipit-source-id: 61ad007b82867fa8b35648e3d8c930ee0e86c80d
2025-07-21 05:02:18 -07:00
Christoph PurrerandFacebook GitHub Bot a0a77f7476 Make it explicit to specify HttpClientFactoryKey and WebSocketClientFactoryKey values (#52715)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52715

Changelog: [Internal]

Right now we rely on compile / link time magic to detect the implementation of
```
WebSocketClientFactory getWebSocketClientFactory();
HttpClientFactory getHttpClientFactory();
```
this actually works until it does not work anymore, see .e.g.:
```
ld.lld: error: undefined symbol: facebook::react::getHttpClientFactory()
>>> referenced by ReactHost.cpp:111 (xplat/js/react-native-github/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp:111)
>>>               xplat/js/react-native-github/packages/react-native/ReactCxxPlatform/react/runtime/__runtimeAndroid__/__objects__/ReactHost.cpp.pic.o:(facebook::react::ReactHost::ReactHost(facebook::react::ReactInstanceConfig, std::__ndk1::shared_ptr<facebook::react::IMountingManager>, std::__ndk1::shared_ptr<facebook::react::RunLoopObserverManager>, std::__ndk1::shared_ptr<facebook::react::ContextContainer const>, std::__ndk1::function<void (facebook::jsi::Runtime&, facebook::react::JsErrorHandler::ProcessedError const&)>, std::__ndk1::function<void (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, unsigned int)>, std::__ndk1::shared_ptr<facebook::react::IDevUIDelegate>, std::__ndk1::vector<std::__ndk1::function<std::__ndk1::shared_ptr<facebook::react::TurboModule> (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, std::__ndk1::shared_ptr<facebook::react::CallInvoker> const&)>, std::__ndk1::allocator<std::__ndk1::function<std::__ndk1::shared_ptr<facebook::react::TurboModule> (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, std::__ndk1::shared_ptr<facebook::react::CallInvoker> const&)>>>, std::__ndk1::shared_ptr<facebook::react::SurfaceDelegate>, std::__ndk1::shared_ptr<facebook::react::NativeAnimatedNodesManagerProvider>, std::__ndk1::function<void (facebook::jsi::Runtime&)>))

ld.lld: error: undefined symbol: facebook::react::getWebSocketClientFactory()
>>> referenced by ReactHost.cpp:117 (xplat/js/react-native-github/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp:117)
>>>               xplat/js/react-native-github/packages/react-native/ReactCxxPlatform/react/runtime/__runtimeAndroid__/__objects__/ReactHost.cpp.pic.o:(facebook::react::ReactHost::ReactHost(facebook::react::ReactInstanceConfig, std::__ndk1::shared_ptr<facebook::react::IMountingManager>, std::__ndk1::shared_ptr<facebook::react::RunLoopObserverManager>, std::__ndk1::shared_ptr<facebook::react::ContextContainer const>, std::__ndk1::function<void (facebook::jsi::Runtime&, facebook::react::JsErrorHandler::ProcessedError const&)>, std::__ndk1::function<void (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, unsigned int)>, std::__ndk1::shared_ptr<facebook::react::IDevUIDelegate>, std::__ndk1::vector<std::__ndk1::function<std::__ndk1::shared_ptr<facebook::react::TurboModule> (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, std::__ndk1::shared_ptr<facebook::react::CallInvoker> const&)>, std::__ndk1::allocator<std::__ndk1::function<std::__ndk1::shared_ptr<facebook::react::TurboModule> (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>> const&, std::__ndk1::shared_ptr<facebook::react::CallInvoker> const&)>>>, std::__ndk1::shared_ptr<facebook::react::SurfaceDelegate>, std::__ndk1::shared_ptr<facebook::react::NativeAnimatedNodesManagerProvider>, std::__ndk1::function<void (facebook::jsi::Runtime&)>))
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
The change here makes it explicit and mandatory to set the specific implementations of these interfaces

Reviewed By: lenaic

Differential Revision: D78529932

fbshipit-source-id: f26876683433a58e078d6720f169702c563ed92b
2025-07-20 23:08:03 -07:00
Vitali ZaidmanandFacebook GitHub Bot 794df48ad6 re-write console stack trace frame urls to be relative to debugger (#52704)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52704

Stack traces for console calls are passed to the debugger when they are relative to device. (e.g. 10.0.2.2 for Android emulator)

Changelog: [android][fixed] fix stack trace linkifying failing when using Android emulator and other situations where the device and debugger have different bundle urls

Reviewed By: motiz88

Differential Revision: D78553183

fbshipit-source-id: 91d7e7ccc99d12ec7d06f4201237ecf557a46c4f
2025-07-20 22:39:52 -07:00
Alex HuntandFacebook GitHub Bot 6d4ea946f8 Address lint warnings (#52702)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52702

Quick pass over some of the main files in `jsinspector-modern` now that C++ lint warnings have become more prevalent / auto-fixable.

Changelog: [Internal]

Reviewed By: motiz88

Differential Revision: D78490415

fbshipit-source-id: 32debcf5f217e847d326498709d50f695902bb5c
2025-07-20 06:12:31 -07:00
generatedunixname89002005287564andFacebook GitHub Bot 8488eaecea Fix CQS signal modernize-use-using in xplat/js/react-native-github/packages [A] [B] [A] (#52718)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52718

Reviewed By: dtolnay

Differential Revision: D78562730

fbshipit-source-id: 24576557eb7a6ba8e655a92f1e7cfe027f99dac0
2025-07-19 12:46:58 -07:00
Marco WangandFacebook GitHub Bot 690cb00353 Deploy 0.276.0 to xplat (#52720)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52720

Changelog: [Internal]

Reviewed By: SamChou19815

Differential Revision: D78605229

fbshipit-source-id: 59ae4a2f316cee8415f6ef2984ca74f906ae0377
2025-07-19 11:08:41 -07:00
Mateo GuzmánandFacebook GitHub Bot 2534aeaddb Migrate Arguments to Kotlin (#52457)
Summary:
Migrate com.facebook.react.bridge.Arguments to Kotlin.

## Changelog:

[Android][Changed] - Migrated com.facebook.react.bridge.Arguments to Kotlin.

Pull Request resolved: https://github.com/facebook/react-native/pull/52457

Test Plan:
```bash
yarn test-android
yarn android
```

Reviewed By: javache

Differential Revision: D78353290

Pulled By: cortinico

fbshipit-source-id: 3d42b44c00a60d34264cb1093991315f5e3c444e
2025-07-19 06:22:25 -07:00
Christoph PurrerandFacebook GitHub Bot ff85e2f6dd Add NativeCxxModuleExampleTests (#52653)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52653

Changelog: [Internal]

This adds an example of Unit Testing a C++ Turbo Module with Google GTest and also adds necessary and useful utility classes for testings

This is GTEST / C++ version of the same tests added in  https://github.com/facebook/react-native/pull/52477

Reviewed By: alanleedev

Differential Revision: D78250302

fbshipit-source-id: 278655779dd17550be9c579d84cc1f7b6e45230d
2025-07-18 22:46:21 -07:00
Christoph PurrerandFacebook GitHub Bot cb94e71845 Delete old location of CallbackWrapper / LongLivedObject (#52649)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52649

Changelog: [Internal]

RN-Windows has been updated > https://github.com/microsoft/react-native-windows/pull/14839

Reviewed By: rshest

Differential Revision: D78312252

fbshipit-source-id: 8e5bd561c4624064cef72395c1179cbe28c247f6
2025-07-18 22:01:44 -07:00
Christoph PurrerandFacebook GitHub Bot 087da29604 Add a TestCallInvoker (#52652)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52652

Changelog: [Internal]

This adds a TestCallInvoker, suitable for unit testing

Reviewed By: alanleedev

Differential Revision: D78453676

fbshipit-source-id: d35e604fedc8656a0430956a811b4a7a07c8ee16
2025-07-18 22:01:36 -07:00
Nicola CortiandFacebook GitHub Bot 34fb932f97 Use allVariants() when publishing ReactAndroid (#52719)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52719

This is really a nit. We don't need to list all the variants here,
we can instead use `allVariants()` which we also use for hermes-engine,
to publish every buildVariant from React Android.

Changelog:
[Internal] [Changed] -

Reviewed By: alanleedev

Differential Revision: D78561729

fbshipit-source-id: 35989051ce966ea07caf26a218eb43c1a2bcac2d
2025-07-18 20:09:48 -07:00
Sam ZhouandFacebook GitHub Bot 1ceba3b470 Prepare react-native for prettier v3 (#52717)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52717

Prettier v3 no longer loads plugin implicitly. This diff first configures the hermes-parser plugin explicitly to prepare for v3 rollout.

Changelog: [Internal]

Reviewed By: pieterv

Differential Revision: D78590158

fbshipit-source-id: dba06f5a823488b72a30ae5b58e37e172f4e736f
2025-07-18 17:51:14 -07:00
Nick GerlemanandFacebook GitHub Bot b3d1d2a0a5 Disable Measure Cache When Also Using Prepared Text Layout (#52714)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52714

If we also have prepared text layout enabled, this cache will fill up with the last 1000 TextInput AttributedString, which isn't very useful. Let's assume we want to get rid of this cache, when we have the prepared layout cache as well.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D78534088

fbshipit-source-id: e9020ebfb7e1210f19e0b31f7423e7a9a90a89d1
2025-07-18 15:19:58 -07:00
Nick GerlemanandFacebook GitHub Bot ea394f6229 Fix incorrect hit testing on text when layout reused (#52692)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52692

D77341994 added a global cache for Facsimile layouts, where we will reuse an existing `android.text.Layout`, and `Spannable`, if one already existed for a same AttributedString, under same layout constraints. An error here, is that we consider a layout reusable, even when shadow views are different, which means there may be different react tags in the underlying AttributedString.

This leaks into the layout itself, and means that if a layout is reused, we can hit test against a stale/incorrect react tag.

The solution to allow reuse here, is to avoid embedding react tag directly into the `android.text.Layout` structure. Instead, we replace references to react tags, with a fragment index, and embed the list of fragment indices in each PreparedLayout. We can hide this "cleverness" within the boundary of `TextLayoutManager`, such that an invalid `PreparedLayout` is never allowed to escape.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D78516079

fbshipit-source-id: 2d9fe9d80f60e6d7e7e40080a0817a08b51c3153
2025-07-18 15:19:58 -07:00
Luna WeiandFacebook GitHub Bot 7ea0ef7a90 Implement windowFocus on ReactVirtualViewExperiment (#52690)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52690

Changelog: [Internal] - Add the window focus experiment to ReactVirtualViewExperimental

Reviewed By: yungsters

Differential Revision: D78502991

fbshipit-source-id: 3e72561835925040e5b240e71734a088908957ed
2025-07-18 14:16:22 -07:00
Luna WeiandFacebook GitHub Bot 36fed563c4 Use experimental VirtualView in FlatListVirtualColumn (#52689)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52689

Changelog: [Internal] - Add support for experimental VirtualView in polyfill

Reviewed By: mdvacca

Differential Revision: D78450011

fbshipit-source-id: cc30394eb1a008cc8c023db2aaf0a6a6ffcd16df
2025-07-18 14:16:22 -07:00
Nicola CortiandFacebook GitHub Bot 391a6b87a0 Rollout preventShadowTreeCommitExhaustionWithLocking in experimental (#52709)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52709

We want to make user for folks in OSS to try
`preventShadowTreeCommitExhaustionWithLocking`. Therefore I'm updating the OSS
release channel for this flag to experimental.

Changelog:
[Internal] [Changed] - Rollout `preventShadowTreeCommitExhaustionWithLocking` in experimental

Reviewed By: rubennorte

Differential Revision: D78558655

fbshipit-source-id: 02a9d216c7b2f8f7bdc1340213f82b70c5692dc7
2025-07-18 10:20:19 -07:00
generatedunixname89002005287564andFacebook GitHub Bot 81c2e798d7 Fix CQS signal readability-braces-around-statements in xplat/js/react-native-github/packages (#52700)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52700

Pull Request resolved: https://github.com/facebook/react-native/pull/52699

Reviewed By: cipolleschi

Differential Revision: D78549834

fbshipit-source-id: a15a9faed579a1e650eabb6e028a48f9307ad22b
2025-07-18 09:30:15 -07:00
Nicola CortiandFacebook GitHub Bot 9f0903780b Bump monorepo packages to 0.82.0-main (#52706)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52706

This just prepares the repo for the next branch cut.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi

Differential Revision: D78558445

fbshipit-source-id: 2132d560dad447b3685874438387a519587f8554
2025-07-18 09:23:10 -07:00
generatedunixname89002005287564andFacebook GitHub Bot ae3b793de6 Fix CQS signal modernize-use-using in xplat/js/react-native-github/packages [A] [A] (#52708)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52708

Reviewed By: rshest

Differential Revision: D78538428

fbshipit-source-id: 6195ead0fa36e5fc4632e9a57362508f55969380
2025-07-18 09:01:22 -07:00
generatedunixname1395667395051502andFacebook GitHub Bot 37a0517b3f Update React Native DevTools binaries
Summary:
Automated update of React Native DevTools binaries
bypass-github-export-checks
Changelog: [Internal]

Reviewed By: robhogan

Differential Revision: D78413554

fbshipit-source-id: 5b0d64f886db4cf121f4a6761e6a3e7fe1ccceec
2025-07-18 08:54:36 -07:00
generatedunixname89002005287564andFacebook GitHub Bot e186f1bf17 Fix CQS signal modernize-use-using in xplat/js/react-native-github/packages [B] (#52693)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52693

Reviewed By: dtolnay

Differential Revision: D78494711

fbshipit-source-id: 870e300b29e41aec488926cdf416f246de96514d
2025-07-18 08:28:34 -07:00
Nicola CortiandFacebook GitHub Bot eb2461c7c9 Create a debugOptimized buildType for Android (#52648)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52648

This creates a `debugOptimized` build type for React Native Android, meaning that we can run C++ optimization on the debug build, while still having the debugger enabled. This is aimed at improving the developer experience for folks developing on low-end devices or emulators.

Users that intend to debug can still use the `debug` variant where the full debug symbols are shipped.

Changelog:

[ANDROID] [ADDED] - Create a debugOptimized buildType for Android

Reviewed By: cipolleschi

Differential Revision: D78425138

fbshipit-source-id: c1e9ea3608e7df10fb871a5584352f0747cf560b
2025-07-18 08:07:54 -07:00
Nicola CortiandFacebook GitHub Bot d89acc1596 Migrate helloworld to use {usesCleartextTraffic} manifest placeholder (#52647)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52647

This removes the need to specify 2 Manifests for apps and we can just use the `main` manifes to toggle if `usesCleartextTraffic` should be enabled or not.

This will have to be replicated in the template repository.

Changelog:
[Android] [Added] - Add support to specify a single Manifest rather than 2 (main/debug) by using the `usesCleartextTraffic` manifest placeholder which is autoconfigured by RNGP.

Reviewed By: cipolleschi

Differential Revision: D78425139

fbshipit-source-id: 9173a014b387d5aed5f7087fa69b7bd49c220f2c
2025-07-18 08:07:54 -07:00
Nicola CortiandFacebook GitHub Bot 5e3edafec6 Migrate RNTester to use {usesCleartextTraffic} Manifest Placeholder (#52620)
Summary:
This creates a `debugOptimized` build type for React Native Android, meaning that we can run C++ optimization on the debug build, while still having the debugger enabled. This is aimed at improving the developer experience for folks developing on low-end devices or emulators.

Users that intend to debug can still use the `debug` variant where the full debug symbols are shipped.

## Changelog:

[ANDROID] [ADDED] - Create a debugOptimized buildType for Android

Pull Request resolved: https://github.com/facebook/react-native/pull/52620

Test Plan:
Tested locally with RNTester by doing:

```
./gradlew installDebugOptimized
```

This is the output of the 3 generated .aar. The size difference is a proof that we're correctly stripping out the C++ debug symbols:

<img width="193" height="54" alt="Screenshot 2025-07-15 at 17 49 50" src="https://github.com/user-attachments/assets/584a0e8d-2d17-40d4-ac29-da09049d6554" />
<img width="235" height="51" alt="Screenshot 2025-07-15 at 17 49 39" src="https://github.com/user-attachments/assets/eda8f9e7-3509-4334-8c16-990e55caa04d" />
<img width="184" height="52" alt="Screenshot 2025-07-15 at 17 49 32" src="https://github.com/user-attachments/assets/a5c94385-bc00-4484-b43e-088ee039827f" />

Rollback Plan:

Reviewed By: cipolleschi

Differential Revision: D78351347

Pulled By: cortinico

fbshipit-source-id: 568a484ba8d2ee6e089cabc95451938e853fbc54
2025-07-18 08:07:54 -07:00
122 changed files with 1320 additions and 804 deletions
+1 -1
View File
@@ -104,4 +104,4 @@ untyped-import
untyped-type-import
[version]
^0.275.0
^0.276.0
@@ -15,8 +15,6 @@ runs:
steps:
- name: Setup xcode
uses: ./.github/actions/setup-xcode
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Restore Hermes workspace
uses: ./.github/actions/restore-hermes-workspace
- name: Restore Cached Artifacts
@@ -45,6 +43,8 @@ runs:
echo "ARTIFACTS_EXIST=true" >> $GITHUB_ENV
echo "ARTIFACTS_EXIST=true" >> $GITHUB_OUTPUT
fi
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Yarn- Install Dependencies
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
uses: ./.github/actions/yarn-install
+2 -2
View File
@@ -116,12 +116,12 @@ runs:
- name: Print Artifacts Directory
shell: bash
run: ls -lR ./packages/react-native/ReactAndroid/external-artifacts/artifacts/
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Setup gradle
uses: ./.github/actions/setup-gradle
with:
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build packages
@@ -14,6 +14,8 @@ inputs:
runs:
using: composite
steps:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Yarn install
uses: ./.github/actions/yarn-install
- name: Configure Git
+2
View File
@@ -35,6 +35,8 @@ runs:
with:
java-version: '17'
distribution: 'zulu'
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Start Metro in Debug
@@ -17,9 +17,6 @@ outputs:
runs:
using: composite
steps:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Setup hermes version
shell: bash
id: hermes-version
@@ -67,6 +64,8 @@ runs:
echo "HERMES_CACHED=true" >> "$GITHUB_OUTPUT"
fi
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Yarn- Install Dependencies
if: ${{ steps.meaningful-cache.outputs.HERMES_CACHED != 'true' }}
uses: ./.github/actions/yarn-install
@@ -23,6 +23,8 @@ runs:
uses: ./.github/actions/setup-xcode
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Create Hermes folder
shell: bash
run: mkdir -p "$HERMES_WS_DIR"
@@ -34,8 +36,6 @@ runs:
- name: Print Downloaded hermes
shell: bash
run: ls -lR "$HERMES_WS_DIR"
- name: Run yarn
uses: ./.github/actions/yarn-install
- name: Setup ruby
uses: ruby/setup-ruby@v1
with:
-2
View File
@@ -2,8 +2,6 @@ name: yarn-install
runs:
using: composite
steps:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Install dependencies
shell: bash
run: |
+2 -1
View File
@@ -583,7 +583,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ["24", "22", "20"]
node-version: ["24", "22"]
# node-version: ["24", "22", "20.19.4"]
steps:
- name: Checkout
uses: actions/checkout@v4
-36
View File
@@ -1,36 +0,0 @@
{
"arrowParens": "avoid",
"bracketSameLine": true,
"bracketSpacing": false,
"requirePragma": true,
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "lf",
"overrides": [
{
"files": ["*.code-workspace"],
"options": {
"parser": "json"
}
},
{
"files": [
"*.js",
"*.js.flow"
],
"options": {
"parser": "hermes"
}
},
{
"files": [
"**/__docs__/*.md"
],
"options": {
"parser": "markdown",
"proseWrap": "always",
"requirePragma": false
}
}
]
}
+48
View File
@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
module.exports = {
arrowParens: 'avoid',
bracketSameLine: true,
bracketSpacing: false,
requirePragma: true,
singleQuote: true,
trailingComma: 'all',
endOfLine: 'lf',
plugins: [
// Using module.parent and createRequire hack to simulate prettier v2 plugin resolution behavior.
// The hack allows us to resolve the plugin from the install location of prettier.
(module.parent
? require('module').createRequire(module.parent.id)
: require
).resolve('prettier-plugin-hermes-parser'),
],
overrides: [
{
files: ['*.code-workspace'],
options: {
parser: 'json',
},
},
{
files: ['*.js', '*.js.flow'],
options: {
parser: 'hermes',
},
},
{
files: ['**/__docs__/*.md'],
options: {
parser: 'markdown',
proseWrap: 'always',
requirePragma: false,
},
},
],
};
+3 -3
View File
@@ -56,8 +56,8 @@
"@electron/packager": "^18.3.6",
"@jest/create-cache-key-function": "^29.7.0",
"@microsoft/api-extractor": "^7.52.2",
"@react-native/metro-babel-transformer": "0.81.0-main",
"@react-native/metro-config": "0.81.0-main",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/metro-config": "0.82.0-main",
"@tsconfig/node22": "22.0.2",
"@types/react": "^19.1.0",
"@typescript-eslint/parser": "^8.36.0",
@@ -82,7 +82,7 @@
"eslint-plugin-redundant-undefined": "^0.4.0",
"eslint-plugin-relay": "^1.8.3",
"flow-api-translator": "0.29.1",
"flow-bin": "^0.275.0",
"flow-bin": "^0.276.0",
"glob": "^7.1.1",
"hermes-eslint": "0.29.1",
"hermes-transform": "0.29.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.81.0-main"
"@react-native/codegen": "0.82.0-main"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,7 +22,7 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.81.0-main",
"@react-native/dev-middleware": "0.82.0-main",
"debug": "^4.4.0",
"invariant": "^2.2.4",
"metro": "^0.83.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env dotslash
// @generated SignedSource<<686df5695b32a90cd465412d979a1a3f>>
{
"name": "React Native DevTools",
"platforms": {
"linux-aarch64": {
"size": 113511487,
"hash": "sha256",
"digest": "c22f7d5e029357f05055ce7c56cc284a0d441d558e103a7b7ebae118c67d0b95",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPRilWDJV8xyKEv9lqYn7Hf2kyZg6nqW8KctGZCXsU95lS_MTFyAmEULrB3J6hGCjqBY2Zl-uq_FjERAIgzbZFWX2eceacTbutBSOKEj6QTZzkVN_L1zPh2lGh24x29MHpkwUzw4E-kAXV43f-11QxqD7orI1QicyOnsmUqeRNKE_QjM9rNRsw6iE8"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-arm64/React Native DevTools"
},
"linux-x86_64": {
"size": 113244728,
"hash": "sha256",
"digest": "d749aee18d6c969f033511ed631b439c578068cc20786974bc0ee707c6c2f177",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQP8NAAXjHJ-9NJThip1nnmVim4yiS1tdYAXmWl2Remiluq5f13nXA8YEadsmGRLWxXz2WJouHXSK47ea4a30fxewyKeTt0niFn3T-lr_91m3Ve5ZS-FOZ_9CRVCkv5zC-Z1FlXJGOnphfWetIU10Pw9tho3IzjdSNlXle0XTrkJ2AyKSl4cKPaX"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-x64/React Native DevTools"
},
"macos-aarch64": {
"size": 108805847,
"hash": "sha256",
"digest": "dc4a6cfa3d2d8646db8793ef497e43ad72be54bb14ed96345d059880ff72ce1e",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPuZx9G4h30mUDM0hZ8y74LbzAi7-S6jnOqq8uyfDdIptLwIgpb8CKB8F2tduvaBFMGd7obwXd2NVy01r6XKVdgAK5-QTp8PYUPUE7VSCi6QAqobXcQ2uq_lY-jgyj8XkV4Ua4gA6KR609Bmh-beSTOrSqMymqVodaGqoGjB_9jVwfD99_PfFlex-Ta"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"macos-x86_64": {
"size": 113766610,
"hash": "sha256",
"digest": "74178859ce6a1c26c32055e192b1b123822c76c827ea86a6dfdb5463b89b1ace",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN6DjGhr8_L7N_cwrh8pi3cfNcbn9dot_QQpgwe3Vv-780FDgi6JUeMvbo_wbFoT2rogxfHZ0-NbXbKdWYwhGUoGeQuikT57QnVMOYuPUTjlQUgYy0Ng9XPhq3iSEYwlMV1XsUJuPFRUg-hIHcgaaA55JoTLXZ1fLYydhHnmgRxGHc4pxaHvSpTtQ"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-shell",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
"keywords": [
"react-native",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,7 +23,7 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.81.0-main",
"@react-native/debugger-frontend": "0.82.0-main",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -184,7 +184,7 @@ describe.each(['HTTP', 'HTTPS'])(
describe.each(['10.0.2.2:8080', '[::1]', 'example.com:2000'])(
'%s aliasing to and from localhost',
sourceHost => {
test('in source map fetching during Debugger.scriptParsed', async () => {
test('Debugger.scriptParsed - in source map fetching', async () => {
serverRef.app.use('/source-map', serveStaticJson({version: 3}));
const {device, debugger_} = await createAndConnectTarget(
serverRef,
@@ -220,6 +220,53 @@ describe.each(['HTTP', 'HTTPS'])(
}
});
test('Runtime.consoleAPICalled - in location resolutions', async () => {
const {device, debugger_} = await createAndConnectTarget(
serverRef,
autoCleanup.signal,
{
app: 'bar-app',
id: 'page1',
title: 'bar-title',
vm: 'bar-vm',
},
{
deviceHostHeader: sourceHost,
},
);
try {
const consoleMessage = await sendFromTargetToDebugger(
device,
debugger_,
'page1',
{
method: 'Runtime.consoleAPICalled',
params: {
stackTrace: {
callFrames: [
{
url: `${protocol.toLowerCase()}://${sourceHost}/bundleFile:1:2`,
field2: 'aaa',
},
{
url: `${protocol.toLowerCase()}://${sourceHost}/bundleFile:5:4`,
field3: 'bbb',
},
],
},
},
},
);
expect(consoleMessage.params.stackTrace.callFrames).toEqual([
{url: `${serverRef.serverBaseUrl}/bundleFile:1:2`, field2: 'aaa'},
{url: `${serverRef.serverBaseUrl}/bundleFile:5:4`, field3: 'bbb'},
]);
} finally {
device.close();
debugger_.close();
}
});
test('in Debugger.setBreakpointByUrl', async () => {
const {device, debugger_} = await createAndConnectTarget(
serverRef,
@@ -844,6 +844,23 @@ export default class Device {
this.#isLegacyPageReloading = false;
}
if (payload.method === 'Runtime.consoleAPICalled') {
const callFrames = payload.params?.stackTrace?.callFrames ?? [];
for (const callFrame of callFrames) {
if (callFrame.url) {
const parsedUrl = this.#tryParseHTTPURL(callFrame.url);
if (parsedUrl) {
// Rewrite device-relative URLs pointing to the server so that they're
// reachable from the frontend.
callFrame.url = this.#deviceRelativeUrlToDebuggerRelativeUrl(
parsedUrl,
debuggerInfo,
).href;
}
}
}
}
}
/**
@@ -48,6 +48,7 @@ export type CDPClientMessage =
export type CDPServerMessage =
| CDPEvent<'Debugger.scriptParsed'>
| CDPEvent<'Runtime.consoleAPICalled'>
| CDPEvent<>
| CDPResponse<'Debugger.getScriptSource'>
| CDPResponse<>;
@@ -84,10 +84,27 @@ export interface Debugger {
*/
sourceMapURL: string,
};
ConsoleAPICalled: {
args: Array<{type: string, value: string}>,
executionContextId: number,
stackTrace: {
timestamp: number,
type: string,
callFrames: Array<{
columnNumber: number,
lineNumber: number,
functionName: string,
scriptId: string,
url: string,
}>,
},
};
}
export type Events = {
'Debugger.scriptParsed': Debugger['ScriptParsedEvent'],
'Runtime.consoleAPICalled': Debugger['ConsoleAPICalled'],
[method: string]: JSONSerializable,
};
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.81.0-main",
"@react-native/eslint-plugin": "0.82.0-main",
"@typescript-eslint/eslint-plugin": "^8.36.0",
"@typescript-eslint/parser": "^8.36.0",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.81.0-main",
"@react-native/codegen": "0.82.0-main",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
@@ -100,10 +100,10 @@ abstract class ReactExtension @Inject constructor(val project: Project) {
* Allows to specify the debuggable variants (by default just 'debug'). Variants in this list will
* not be bundled (the bundle file will not be created and won't be copied over).
*
* Default: ['debug']
* Default: ['debug', 'debugOptimized']
*/
val debuggableVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("debug"))
objects.listProperty(String::class.java).convention(listOf("debug", "debugOptimized"))
/** Hermes Config */
@@ -18,6 +18,7 @@ import com.facebook.react.tasks.GenerateEntryPointTask
import com.facebook.react.tasks.GeneratePackageListTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildTypesForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevServerLocation
import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
@@ -84,6 +85,7 @@ class ReactPlugin : Plugin<Project> {
configureAutolinking(project, extension)
configureCodegen(project, extension, rootExtension, isLibrary = false)
configureResources(project, extension)
configureBuildTypesForApp(project)
}
// Library Only Configuration
@@ -19,6 +19,7 @@ import java.net.Inet4Address
import java.net.NetworkInterface
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import kotlin.plus
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.plugins.AppliedPlugin
@@ -27,6 +28,36 @@ import org.w3c.dom.Element
@Suppress("UnstableApiUsage")
internal object AgpConfiguratorUtils {
fun configureBuildTypesForApp(project: Project) {
val action =
Action<AppliedPlugin> {
project.extensions
.getByType(ApplicationAndroidComponentsExtension::class.java)
.finalizeDsl { ext ->
ext.buildTypes {
val debug =
getByName("debug").apply {
manifestPlaceholders["usesCleartextTraffic"] = "true"
}
getByName("release").apply {
manifestPlaceholders["usesCleartextTraffic"] = "false"
}
maybeCreate("debugOptimized").apply {
manifestPlaceholders["usesCleartextTraffic"] = "true"
initWith(debug)
externalNativeBuild {
cmake {
arguments("-DCMAKE_BUILD_TYPE=Release")
matchingFallbacks += listOf("release")
}
}
}
}
}
}
project.pluginManager.withPlugin("com.android.application", action)
}
fun configureBuildConfigFieldsForApp(project: Project, extension: ReactExtension) {
val action =
Action<AppliedPlugin> {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.81.0-main",
"@react-native/metro-babel-transformer": "0.81.0-main",
"@react-native/js-polyfills": "0.82.0-main",
"@react-native/metro-babel-transformer": "0.82.0-main",
"metro-config": "^0.83.0",
"metro-runtime": "^0.83.0"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/new-app-screen",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "NewAppScreen component for React Native",
"keywords": [
"react-native"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Babel preset for React Native applications",
"repository": {
"type": "git",
@@ -66,7 +66,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.81.0-main",
"@react-native/babel-plugin-codegen": "0.82.0-main",
"babel-plugin-syntax-hermes-parser": "0.29.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Babel transformer for React Native applications.",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
],
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.81.0-main",
"@react-native/babel-preset": "0.82.0-main",
"hermes-parser": "0.29.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/compatibility-check",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "Check a React Native app's boundary between JS and Native for incompatibilities",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"dist"
],
"dependencies": {
"@react-native/codegen": "0.81.0-main"
"@react-native/codegen": "0.82.0-main"
},
"devDependencies": {
"flow-remove-types": "^2.237.2",
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.81.0-main",
"version": "0.82.0-main",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -21,7 +21,7 @@
},
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.81.0-main"
"@react-native/codegen": "0.82.0-main"
},
"peerDependencies": {
"@types/react": "^19.1.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.81.0-main",
"version": "0.82.0-main",
"private": true,
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
"license": "MIT",
@@ -26,7 +26,7 @@
],
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.81.0-main",
"@react-native/babel-preset": "0.82.0-main",
"react-native": "1000.0.0"
},
"peerDependencies": {
@@ -30,6 +30,7 @@ export default function queueMicrotask(callback: Function) {
}
// Try to reuse a lazily allocated resolved promise from closure.
// $FlowFixMe[constant-condition]
(resolvedPromise || (resolvedPromise = Promise.resolve()))
.then(callback)
.catch(error =>
@@ -50,8 +50,9 @@ class LazyVector {
const_reference at(size_type pos) const {
#ifndef _LIBCPP_NO_EXCEPTIONS
if (!(pos < _size))
if (!(pos < _size)) {
throw std::out_of_range("out of range");
}
#else
assert(pos < _size || !"out of range");
#endif
@@ -25,11 +25,13 @@ static NSUInteger RCTDeviceFreeMemory(void)
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS)
if (kern != KERN_SUCCESS) {
return 0;
}
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS)
if (kern != KERN_SUCCESS) {
return 0;
}
return (vm_stat.free_count - vm_stat.speculative_count) * page_size;
}
@@ -278,8 +280,9 @@ static NSUInteger RCTDeviceFreeMemory(void)
- (void)calculateMaxBufferCount
{
NSUInteger bytes = CGImageGetBytesPerRow(self.currentFrame.CGImage) * CGImageGetHeight(self.currentFrame.CGImage);
if (bytes == 0)
if (bytes == 0) {
bytes = 1024;
}
NSUInteger max = 0;
if (self.maxBufferSize > 0) {
@@ -66,7 +66,7 @@
static NSString *const RCTJSThreadName = @"com.facebook.react.JavaScript";
typedef void (^RCTPendingCall)();
using RCTPendingCall = void (^)();
using namespace facebook::jsi;
using namespace facebook::react;
@@ -11,11 +11,11 @@
#import <jsinspector-modern/ReactCdp.h>
typedef facebook::react::jsinspector_modern::NetworkRequestListener RCTInspectorNetworkListener;
using RCTInspectorNetworkListener = facebook::react::jsinspector_modern::NetworkRequestListener;
typedef facebook::react::jsinspector_modern::ScopedExecutor<RCTInspectorNetworkListener> RCTInspectorNetworkExecutor;
using RCTInspectorNetworkExecutor = facebook::react::jsinspector_modern::ScopedExecutor<RCTInspectorNetworkListener>;
typedef facebook::react::jsinspector_modern::LoadNetworkResourceRequest RCTInspectorLoadNetworkResourceRequest;
using RCTInspectorLoadNetworkResourceRequest = facebook::react::jsinspector_modern::LoadNetworkResourceRequest;
/**
* A helper class that wraps around NSURLSession to make network requests.
@@ -8,7 +8,7 @@
#import "RCTInspectorNetworkHelper.h"
#import <React/RCTLog.h>
typedef void (^ListenerBlock)(RCTInspectorNetworkListener *);
using ListenerBlock = void (^)(RCTInspectorNetworkListener *);
@interface RCTInspectorNetworkHelper () <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSURLSession *session;
+2 -2
View File
@@ -205,7 +205,7 @@ RCT_ENUM_CONVERTER(
UIFontWeightRegular,
doubleValue)
typedef BOOL RCTFontStyle;
using RCTFontStyle = BOOL;
RCT_ENUM_CONVERTER(
RCTFontStyle,
(@{
@@ -216,7 +216,7 @@ RCT_ENUM_CONVERTER(
NO,
boolValue)
typedef NSDictionary RCTFontVariantDescriptor;
using RCTFontVariantDescriptor = NSDictionary;
+ (RCTFontVariantDescriptor *)RCTFontVariantDescriptor:(id)json
{
static NSDictionary *mapping;
@@ -71,8 +71,9 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)beginRefreshingProgrammatically
{
if (!_hasMovedToWindow)
if (!_hasMovedToWindow) {
return;
}
UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp;
_refreshingProgrammatically = YES;
@@ -108,8 +109,9 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)endRefreshingProgrammatically
{
if (!_hasMovedToWindow)
if (!_hasMovedToWindow) {
return;
}
// The contentOffset of the scrollview MUST be greater than the contentInset before calling
// endRefreshing otherwise the next pull to refresh will not work properly.
UIScrollView *scrollView = self.scrollView;
@@ -531,20 +531,21 @@ public abstract interface class com/facebook/react/bridge/ActivityEventListener
public fun onUserLeaveHint (Landroid/app/Activity;)V
}
public class com/facebook/react/bridge/Arguments {
public fun <init> ()V
public static fun createArray ()Lcom/facebook/react/bridge/WritableArray;
public static fun createMap ()Lcom/facebook/react/bridge/WritableMap;
public static fun fromArray (Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableArray;
public static fun fromBundle (Landroid/os/Bundle;)Lcom/facebook/react/bridge/WritableMap;
public static fun fromJavaArgs ([Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableNativeArray;
public static fun fromList (Ljava/util/List;)Lcom/facebook/react/bridge/WritableArray;
public static fun makeNativeArray (Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableNativeArray;
public static fun makeNativeArray (Ljava/util/List;)Lcom/facebook/react/bridge/WritableNativeArray;
public static fun makeNativeMap (Landroid/os/Bundle;)Lcom/facebook/react/bridge/WritableNativeMap;
public static fun makeNativeMap (Ljava/util/Map;)Lcom/facebook/react/bridge/WritableNativeMap;
public static fun toBundle (Lcom/facebook/react/bridge/ReadableMap;)Landroid/os/Bundle;
public static fun toList (Lcom/facebook/react/bridge/ReadableArray;)Ljava/util/ArrayList;
public final class com/facebook/react/bridge/Arguments {
public static final field INSTANCE Lcom/facebook/react/bridge/Arguments;
public static final fun createArray ()Lcom/facebook/react/bridge/WritableArray;
public static final fun createMap ()Lcom/facebook/react/bridge/WritableMap;
public static final fun fromArray (Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableArray;
public static final fun fromBundle (Landroid/os/Bundle;)Lcom/facebook/react/bridge/WritableMap;
public static final fun fromJavaArgs (Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableNativeArray;
public static final fun fromJavaArgs ([Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableNativeArray;
public static final fun fromList (Ljava/util/List;)Lcom/facebook/react/bridge/WritableArray;
public static final fun makeNativeArray (Ljava/lang/Object;)Lcom/facebook/react/bridge/WritableNativeArray;
public static final fun makeNativeArray (Ljava/util/List;)Lcom/facebook/react/bridge/WritableNativeArray;
public static final fun makeNativeMap (Landroid/os/Bundle;)Lcom/facebook/react/bridge/WritableNativeMap;
public static final fun makeNativeMap (Ljava/util/Map;)Lcom/facebook/react/bridge/WritableNativeMap;
public static final fun toBundle (Lcom/facebook/react/bridge/ReadableMap;)Landroid/os/Bundle;
public static final fun toList (Lcom/facebook/react/bridge/ReadableArray;)Ljava/util/ArrayList;
}
public final class com/facebook/react/bridge/AssertionException : java/lang/RuntimeException {
@@ -4403,10 +4404,6 @@ public abstract interface class com/facebook/react/uimanager/UIManagerModule$Cus
public abstract fun resolveCustomEventName (Ljava/lang/String;)Ljava/lang/String;
}
public abstract interface class com/facebook/react/uimanager/UIManagerModuleListener {
public abstract fun willDispatchViewUpdates (Lcom/facebook/react/uimanager/UIManagerModule;)V
}
public class com/facebook/react/uimanager/UIViewOperationQueue {
public static final field DEFAULT_MIN_TIME_LEFT_IN_FRAME_FOR_NONBATCHED_OPERATION_MS I
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/uimanager/NativeViewHierarchyManager;I)V
@@ -599,7 +599,7 @@ android {
publishing {
multipleVariants {
withSourcesJar()
includeBuildTypeValues("debug", "release")
allVariants()
}
}
@@ -607,6 +607,15 @@ android {
unitTests { isIncludeAndroidResources = true }
targetSdk = libs.versions.targetSdk.get().toInt()
}
buildTypes {
create("debugOptimized") {
initWith(getByName("debug"))
externalNativeBuild {
cmake { arguments("-DCMAKE_BUILD_TYPE=Release", "-DREACT_NATIVE_DEBUG_OPTIMIZED=True") }
}
}
}
}
tasks.withType<KotlinCompile>().configureEach {
@@ -621,7 +630,6 @@ dependencies {
api(libs.androidx.autofill)
api(libs.androidx.swiperefreshlayout)
api(libs.androidx.tracing)
api(libs.androidx.window)
api(libs.fbjni)
api(libs.fresco)
@@ -306,6 +306,12 @@ android {
}
}
}
buildTypes {
create("debugOptimized") {
initWith(getByName("debug"))
externalNativeBuild { cmake { arguments("-DCMAKE_BUILD_TYPE=Release") } }
}
}
}
sourceSets.getByName("main") {
@@ -869,7 +869,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
private int mDeviceRotation = 0;
/* package */ CustomGlobalLayoutListener() {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext());
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
mVisibleViewArea = new Rect();
mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
}
@@ -992,7 +992,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
return;
}
mDeviceRotation = rotation;
DisplayMetricsHolder.initDisplayMetrics(getContext());
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
emitOrientationChanged(rotation);
}
@@ -1,423 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.bridge;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.proguard.annotations.DoNotStrip;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Nullsafe(Nullsafe.Mode.LOCAL)
@DoNotStrip
public class Arguments {
private static @Nullable Object makeNativeObject(@Nullable Object object) {
if (object == null) {
return null;
} else if (object instanceof Float
|| object instanceof Long
|| object instanceof Byte
|| object instanceof Short) {
return ((Number) object).doubleValue();
} else if (object.getClass().isArray()) {
return makeNativeArray(object);
} else if (object instanceof List) {
return makeNativeArray((List) object);
} else if (object instanceof Map) {
return makeNativeMap((Map<String, Object>) object);
} else if (object instanceof Bundle) {
return makeNativeMap((Bundle) object);
} else if (object instanceof JavaOnlyMap) {
return makeNativeMap(((JavaOnlyMap) object).toHashMap());
} else if (object instanceof JavaOnlyArray) {
return makeNativeArray(((JavaOnlyArray) object).toArrayList());
} else {
// Boolean, Integer, Double, String, WritableNativeArray, WritableNativeMap
return object;
}
}
/**
* This method converts a List into a NativeArray. The data types supported are boolean, int,
* float, double, and String. List, Map, and Bundle objects, as well as arrays, containing values
* of the above types and/or null, or any recursive arrangement of these, are also supported. The
* best way to think of this is a way to generate a Java representation of a json list, from Java
* types which have a natural representation in json.
*/
public static WritableNativeArray makeNativeArray(@Nullable List objects) {
WritableNativeArray nativeArray = new WritableNativeArray();
if (objects == null) {
return nativeArray;
}
for (Object elem : objects) {
elem = makeNativeObject(elem);
if (elem == null) {
nativeArray.pushNull();
} else if (elem instanceof Boolean) {
nativeArray.pushBoolean((Boolean) elem);
} else if (elem instanceof Integer) {
nativeArray.pushInt((Integer) elem);
} else if (elem instanceof Double) {
nativeArray.pushDouble((Double) elem);
} else if (elem instanceof String) {
nativeArray.pushString((String) elem);
} else if (elem instanceof WritableNativeArray) {
nativeArray.pushArray((WritableNativeArray) elem);
} else if (elem instanceof WritableNativeMap) {
nativeArray.pushMap((WritableNativeMap) elem);
} else {
throw new IllegalArgumentException("Could not convert " + elem.getClass());
}
}
return nativeArray;
}
/**
* This overload is like the above, but uses reflection to operate on any primitive or object
* type.
*/
public static <T> WritableNativeArray makeNativeArray(final @Nullable Object objects) {
if (objects == null) {
return new WritableNativeArray();
}
// No explicit check for objects's type here. If it's not an array, the
// Array methods will throw IllegalArgumentException.
return makeNativeArray(
new AbstractList() {
public int size() {
return Array.getLength(objects);
}
public @Nullable Object get(int index) {
return Array.get(objects, index);
}
});
}
private static void addEntry(WritableNativeMap nativeMap, String key, @Nullable Object value) {
value = makeNativeObject(value);
if (value == null) {
nativeMap.putNull(key);
} else if (value instanceof Boolean) {
nativeMap.putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
nativeMap.putInt(key, (Integer) value);
} else if (value instanceof Number) {
nativeMap.putDouble(key, ((Number) value).doubleValue());
} else if (value instanceof String) {
nativeMap.putString(key, (String) value);
} else if (value instanceof WritableNativeArray) {
nativeMap.putArray(key, (WritableNativeArray) value);
} else if (value instanceof WritableNativeMap) {
nativeMap.putMap(key, (WritableNativeMap) value);
} else {
throw new IllegalArgumentException("Could not convert " + value.getClass());
}
}
/**
* This method converts a Map into a NativeMap. Value types are supported as with makeNativeArray.
* The best way to think of this is a way to generate a Java representation of a json object, from
* Java types which have a natural representation in json.
*/
@DoNotStrip
public static WritableNativeMap makeNativeMap(@Nullable Map<String, Object> objects) {
WritableNativeMap nativeMap = new WritableNativeMap();
if (objects == null) {
return nativeMap;
}
for (Map.Entry<String, Object> entry : objects.entrySet()) {
addEntry(nativeMap, entry.getKey(), entry.getValue());
}
return nativeMap;
}
/** Like the above, but takes a Bundle instead of a Map. */
@DoNotStrip
public static WritableNativeMap makeNativeMap(@Nullable Bundle bundle) {
WritableNativeMap nativeMap = new WritableNativeMap();
if (bundle == null) {
return nativeMap;
}
for (String key : bundle.keySet()) {
addEntry(nativeMap, key, bundle.get(key));
}
return nativeMap;
}
/** This method should be used when you need to stub out creating NativeArrays in unit tests. */
public static WritableArray createArray() {
return new WritableNativeArray();
}
/** This method should be used when you need to stub out creating NativeMaps in unit tests. */
public static WritableMap createMap() {
return new WritableNativeMap();
}
public static WritableNativeArray fromJavaArgs(Object[] args) {
WritableNativeArray arguments = new WritableNativeArray();
for (int i = 0; i < args.length; i++) {
Object argument = args[i];
if (argument == null) {
arguments.pushNull();
continue;
}
Class argumentClass = argument.getClass();
if (argumentClass == Boolean.class) {
arguments.pushBoolean(((Boolean) argument).booleanValue());
} else if (argumentClass == Integer.class) {
arguments.pushDouble(((Integer) argument).doubleValue());
} else if (argumentClass == Double.class) {
arguments.pushDouble(((Double) argument).doubleValue());
} else if (argumentClass == Float.class) {
arguments.pushDouble(((Float) argument).doubleValue());
} else if (argumentClass == String.class) {
arguments.pushString(argument.toString());
} else if (argumentClass == WritableNativeMap.class) {
arguments.pushMap((WritableNativeMap) argument);
} else if (argumentClass == WritableNativeArray.class) {
arguments.pushArray((WritableNativeArray) argument);
} else {
throw new RuntimeException("Cannot convert argument of type " + argumentClass);
}
}
return arguments;
}
/**
* Convert an array to a {@link WritableArray}.
*
* @param array the array to convert. Supported types are: {@code String[]}, {@code Bundle[]},
* {@code int[]}, {@code float[]}, {@code double[]}, {@code boolean[]}.
* @return the converted {@link WritableArray}
* @throws IllegalArgumentException if the passed object is none of the above types
*/
public static WritableArray fromArray(Object array) {
WritableArray catalystArray = createArray();
if (array instanceof String[]) {
for (String v : (String[]) array) {
catalystArray.pushString(v);
}
} else if (array instanceof Bundle[]) {
for (Bundle v : (Bundle[]) array) {
catalystArray.pushMap(fromBundle(v));
}
} else if (array instanceof int[]) {
for (int v : (int[]) array) {
catalystArray.pushInt(v);
}
} else if (array instanceof float[]) {
for (float v : (float[]) array) {
catalystArray.pushDouble(v);
}
} else if (array instanceof double[]) {
for (double v : (double[]) array) {
catalystArray.pushDouble(v);
}
} else if (array instanceof boolean[]) {
for (boolean v : (boolean[]) array) {
catalystArray.pushBoolean(v);
}
} else if (array instanceof Parcelable[]) {
for (Parcelable v : (Parcelable[]) array) {
if (v instanceof Bundle) {
catalystArray.pushMap(fromBundle((Bundle) v));
} else {
throw new IllegalArgumentException("Unexpected array member type " + v.getClass());
}
}
} else {
throw new IllegalArgumentException("Unknown array type " + array.getClass());
}
return catalystArray;
}
/**
* Convert a {@link List} to a {@link WritableArray}.
*
* @param list the list to convert. Supported value types are: {@code null}, {@code String},
* {@code Bundle}, {@code List}, {@code Number}, {@code Boolean}, and all array types
* supported in {@link #fromArray(Object)}.
* @return the converted {@link WritableArray}
* @throws IllegalArgumentException if one of the values from the passed list is none of the above
* types
*/
public static WritableArray fromList(List list) {
WritableArray catalystArray = createArray();
for (Object obj : list) {
if (obj == null) {
catalystArray.pushNull();
} else if (obj.getClass().isArray()) {
catalystArray.pushArray(fromArray(obj));
} else if (obj instanceof Bundle) {
catalystArray.pushMap(fromBundle((Bundle) obj));
} else if (obj instanceof List) {
catalystArray.pushArray(fromList((List) obj));
} else if (obj instanceof String) {
catalystArray.pushString((String) obj);
} else if (obj instanceof Integer) {
catalystArray.pushInt((Integer) obj);
} else if (obj instanceof Number) {
catalystArray.pushDouble(((Number) obj).doubleValue());
} else if (obj instanceof Boolean) {
catalystArray.pushBoolean((Boolean) obj);
} else {
throw new IllegalArgumentException("Unknown value type " + obj.getClass());
}
}
return catalystArray;
}
/**
* Convert a {@link Bundle} to a {@link WritableMap}. Supported key types in the bundle are:
*
* <p>
*
* <ul>
* <li>primitive types: int, float, double, boolean
* <li>arrays supported by {@link #fromArray(Object)}
* <li>lists supported by {@link #fromList(List)}
* <li>{@link Bundle} objects that are recursively converted to maps
* </ul>
*
* @param bundle the {@link Bundle} to convert
* @return the converted {@link WritableMap}
* @throws IllegalArgumentException if there are keys of unsupported types
*/
public static WritableMap fromBundle(Bundle bundle) {
WritableMap map = createMap();
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
if (value == null) {
map.putNull(key);
} else if (value.getClass().isArray()) {
map.putArray(key, fromArray(value));
} else if (value instanceof String) {
map.putString(key, (String) value);
} else if (value instanceof Number) {
if (value instanceof Integer) {
map.putInt(key, (Integer) value);
} else {
map.putDouble(key, ((Number) value).doubleValue());
}
} else if (value instanceof Boolean) {
map.putBoolean(key, (Boolean) value);
} else if (value instanceof Bundle) {
map.putMap(key, fromBundle((Bundle) value));
} else if (value instanceof List) {
map.putArray(key, fromList((List) value));
} else {
throw new IllegalArgumentException("Could not convert " + value.getClass());
}
}
return map;
}
/**
* Convert a {@link WritableArray} to a {@link ArrayList}.
*
* @param readableArray the {@link WritableArray} to convert.
* @return the converted {@link ArrayList}.
*/
@Nullable
public static ArrayList toList(@Nullable ReadableArray readableArray) {
if (readableArray == null) {
return null;
}
ArrayList list = new ArrayList();
for (int i = 0; i < readableArray.size(); i++) {
switch (readableArray.getType(i)) {
case Null:
list.add(null);
break;
case Boolean:
list.add(readableArray.getBoolean(i));
break;
case Number:
double number = readableArray.getDouble(i);
if (number == Math.rint(number)) {
// Add as an integer
list.add((int) number);
} else {
// Add as a double
list.add(number);
}
break;
case String:
list.add(readableArray.getString(i));
break;
case Map:
list.add(toBundle(readableArray.getMap(i)));
break;
case Array:
list.add(toList(readableArray.getArray(i)));
break;
default:
throw new IllegalArgumentException("Could not convert object in array.");
}
}
return list;
}
/**
* Convert a {@link WritableMap} to a {@link Bundle}. Note: Each array is converted to an {@link
* ArrayList}.
*
* @param readableMap the {@link WritableMap} to convert.
* @return the converted {@link Bundle}.
*/
@Nullable
public static Bundle toBundle(@Nullable ReadableMap readableMap) {
if (readableMap == null) {
return null;
}
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
Bundle bundle = new Bundle();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ReadableType readableType = readableMap.getType(key);
switch (readableType) {
case Null:
bundle.putString(key, null);
break;
case Boolean:
bundle.putBoolean(key, readableMap.getBoolean(key));
break;
case Number:
// Can be int or double.
bundle.putDouble(key, readableMap.getDouble(key));
break;
case String:
bundle.putString(key, readableMap.getString(key));
break;
case Map:
bundle.putBundle(key, toBundle(readableMap.getMap(key)));
break;
case Array:
bundle.putSerializable(key, toList(readableMap.getArray(key)));
break;
default:
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
}
}
return bundle;
}
}
@@ -0,0 +1,356 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.bridge
import android.os.Bundle
import android.os.Parcelable
import com.facebook.proguard.annotations.DoNotStrip
import java.util.AbstractList
import kotlin.math.round
@DoNotStrip
public object Arguments {
@Suppress("UNCHECKED_CAST")
private fun makeNativeObject(value: Any?): Any? =
when {
value == null -> null
value is Float || value is Long || value is Byte || value is Short ->
(value as Number).toDouble()
value.javaClass.isArray -> makeNativeArray<Any>(value)
value is List<*> -> makeNativeArray(value)
value is Map<*, *> -> makeNativeMap(value as Map<String, Any?>)
value is Bundle -> makeNativeMap(value)
value is JavaOnlyMap -> makeNativeMap(value.toHashMap())
value is JavaOnlyArray -> makeNativeArray(value.toArrayList())
else -> value // Boolean, Integer, Double, String, WritableNativeArray, WritableNativeMap
}
/**
* This method converts a List into a NativeArray. The data types supported are boolean, int,
* float, double, and String. List, Map, and Bundle objects, as well as arrays, containing values
* of the above types and/or null, or any recursive arrangement of these, are also supported. The
* best way to think of this is a way to generate a Java representation of a json list, from Java
* types which have a natural representation in json.
*/
@JvmStatic
public fun makeNativeArray(objects: List<*>?): WritableNativeArray {
val nativeArray = WritableNativeArray()
if (objects == null) {
return nativeArray
}
for (elem in objects) {
when (val value = makeNativeObject(elem)) {
null -> nativeArray.pushNull()
is Boolean -> nativeArray.pushBoolean(value)
is Int -> nativeArray.pushInt(value)
is Double -> nativeArray.pushDouble(value)
is String -> nativeArray.pushString(value)
is WritableNativeArray -> nativeArray.pushArray(value)
is WritableNativeMap -> nativeArray.pushMap(value)
else -> throw IllegalArgumentException("Could not convert ${value.javaClass}")
}
}
return nativeArray
}
/**
* This overload is like the above, but uses reflection to operate on any primitive or object
* type.
*/
@JvmStatic
public fun <T> makeNativeArray(objects: Any?): WritableNativeArray {
if (objects == null) {
return WritableNativeArray()
}
// No explicit check for objects's type here. If it's not an array, the
// Array methods will throw IllegalArgumentException.
return makeNativeArray(
object : AbstractList<Any?>() {
override val size: Int
get() = java.lang.reflect.Array.getLength(objects)
override fun get(index: Int): Any? = java.lang.reflect.Array.get(objects, index)
})
}
private fun addEntry(nativeMap: WritableNativeMap, key: String, value: Any?) {
when (val nativeObjectValue = makeNativeObject(value)) {
null -> nativeMap.putNull(key)
is Boolean -> nativeMap.putBoolean(key, nativeObjectValue)
is Int -> nativeMap.putInt(key, nativeObjectValue)
is Number -> nativeMap.putDouble(key, nativeObjectValue.toDouble())
is String -> nativeMap.putString(key, nativeObjectValue)
is WritableNativeArray -> nativeMap.putArray(key, nativeObjectValue)
is WritableNativeMap -> nativeMap.putMap(key, nativeObjectValue)
else -> throw IllegalArgumentException("Could not convert ${nativeObjectValue.javaClass}")
}
}
/**
* This method converts a Map into a NativeMap. Value types are supported as with makeNativeArray.
* The best way to think of this is a way to generate a Java representation of a json object, from
* Java types which have a natural representation in json.
*/
@DoNotStrip
@JvmStatic
public fun makeNativeMap(objects: Map<String, Any?>?): WritableNativeMap {
val nativeMap = WritableNativeMap()
if (objects == null) {
return nativeMap
}
for ((key, value) in objects) {
addEntry(nativeMap, key, value)
}
return nativeMap
}
/** Like the above, but takes a Bundle instead of a Map. */
@DoNotStrip
@JvmStatic
@Suppress("DEPRECATION")
public fun makeNativeMap(bundle: Bundle?): WritableNativeMap {
val nativeMap = WritableNativeMap()
if (bundle == null) {
return nativeMap
}
for (key in bundle.keySet()) {
addEntry(nativeMap, key, bundle[key])
}
return nativeMap
}
/** This method should be used when you need to stub out creating NativeArrays in unit tests. */
@JvmStatic public fun createArray(): WritableArray = WritableNativeArray()
/** This method should be used when you need to stub out creating NativeMaps in unit tests. */
@JvmStatic public fun createMap(): WritableMap = WritableNativeMap()
@Suppress("UNCHECKED_CAST")
@JvmStatic
@Deprecated(
"Use fromJavaArgs(Array<Any?>) instead. This method is added only to retain compatibility with Java consumers.")
public fun fromJavaArgs(args: Any?): WritableNativeArray = fromJavaArgs(args as Array<Any?>)
@JvmStatic
public fun fromJavaArgs(args: Array<Any?>): WritableNativeArray {
val arguments = WritableNativeArray()
for (i in args.indices) {
val argument = args[i]
when (val argumentClass = argument?.javaClass) {
null -> arguments.pushNull()
Boolean::class.java,
java.lang.Boolean::class.java -> arguments.pushBoolean(argument as Boolean)
Int::class.java,
java.lang.Integer::class.java -> arguments.pushDouble((argument as Number).toDouble())
Double::class.java,
java.lang.Double::class.java -> arguments.pushDouble((argument as Double))
Float::class.java -> arguments.pushDouble((argument as Float).toDouble())
java.lang.Float::class.java -> arguments.pushDouble((argument as Float).toDouble())
String::class.java -> arguments.pushString(argument.toString())
WritableNativeMap::class.java -> arguments.pushMap(argument as WritableNativeMap)
WritableNativeArray::class.java -> arguments.pushArray(argument as WritableNativeArray)
else -> throw RuntimeException("Cannot convert argument of type $argumentClass")
}
}
return arguments
}
/**
* Convert an array to a [WritableArray].
*
* @param array the array to convert. Supported types are: `String[]`, `Bundle[]`, `int[]`,
* `float[]`, `double[]`, `boolean[]`.
* @return the converted [WritableArray]
* @throws IllegalArgumentException if the passed object is none of the above types
*/
@JvmStatic
@Suppress("UNCHECKED_CAST")
public fun fromArray(array: Any): WritableArray {
val catalystArray = createArray()
when {
array is Array<*> && array.isArrayOf<String>() -> {
for (v in array as Array<String?>) {
catalystArray.pushString(v)
}
}
array is Array<*> && array.isArrayOf<Bundle>() -> {
for (v in array as Array<Bundle>) {
catalystArray.pushMap(fromBundle(v))
}
}
array is IntArray -> {
for (v in array) {
catalystArray.pushInt(v)
}
}
array is FloatArray -> {
for (v in array) {
catalystArray.pushDouble(v.toDouble())
}
}
array is DoubleArray -> {
for (v in array) {
catalystArray.pushDouble(v)
}
}
array is BooleanArray -> {
for (v in array) {
catalystArray.pushBoolean(v)
}
}
array is Array<*> && array.isArrayOf<Parcelable>() -> {
for (v in array as Array<Parcelable>) {
if (v is Bundle) {
catalystArray.pushMap(fromBundle(v))
} else {
throw IllegalArgumentException("Unexpected array member type ${v.javaClass}")
}
}
}
else -> throw IllegalArgumentException("Unknown array type ${array.javaClass}")
}
return catalystArray
}
/**
* Convert a [List] to a [WritableArray].
*
* @param list the list to convert. Supported value types are: `null`, `String`, `Bundle`, `List`,
* `Number`, `Boolean`, and all array types supported in [.fromArray].
* @return the converted [WritableArray]
* @throws IllegalArgumentException if one of the values from the passed list is none of the above
* types
*/
@JvmStatic
public fun fromList(list: List<*>): WritableArray {
val catalystArray = createArray()
for (obj in list) {
when {
obj == null -> catalystArray.pushNull()
obj.javaClass.isArray -> catalystArray.pushArray(fromArray(obj))
obj is Bundle -> catalystArray.pushMap(fromBundle(obj))
obj is List<*> -> catalystArray.pushArray(fromList(obj))
obj is String -> catalystArray.pushString(obj)
obj is Int -> catalystArray.pushInt(obj)
obj is Number -> catalystArray.pushDouble(obj.toDouble())
obj is Boolean -> catalystArray.pushBoolean(obj)
else -> throw IllegalArgumentException("Unknown value type ${obj.javaClass}")
}
}
return catalystArray
}
/**
* Convert a [Bundle] to a [WritableMap]. Supported key types in the bundle are:
* * primitive types: int, float, double, boolean
* * arrays supported by [.fromArray]
* * lists supported by [.fromList]
* * [Bundle] objects that are recursively converted to maps
*
* @param bundle the [Bundle] to convert
* @return the converted [WritableMap]
* @throws IllegalArgumentException if there are keys of unsupported types
*/
@JvmStatic
@Suppress("DEPRECATION")
public fun fromBundle(bundle: Bundle): WritableMap {
val map = createMap()
for (key in bundle.keySet()) {
val value = bundle[key]
when {
value == null -> map.putNull(key)
value.javaClass.isArray -> map.putArray(key, fromArray(value))
value is String -> map.putString(key, value)
value is Number -> {
if (value is Int) {
map.putInt(key, value)
} else {
map.putDouble(key, value.toDouble())
}
}
value is Boolean -> map.putBoolean(key, value)
value is Bundle -> map.putMap(key, fromBundle(value))
value is List<*> -> map.putArray(key, fromList(value))
else -> throw IllegalArgumentException("Could not convert ${value.javaClass}")
}
}
return map
}
/**
* Convert a [WritableArray] to a [ArrayList].
*
* @param readableArray the [WritableArray] to convert.
* @return the converted [ArrayList].
*/
@JvmStatic
@Suppress("REDUNDANT_ELSE_IN_WHEN")
public fun toList(readableArray: ReadableArray?): ArrayList<Any?>? {
if (readableArray == null) {
return null
}
val list: ArrayList<Any?> = ArrayList()
for (i in 0..<readableArray.size()) {
when (readableArray.getType(i)) {
ReadableType.Null -> list.add(null)
ReadableType.Boolean -> list.add(readableArray.getBoolean(i))
ReadableType.Number -> {
val number = readableArray.getDouble(i)
if (number == round(number)) {
// Add as an integer
list.add(number.toInt())
} else {
// Add as a double
list.add(number)
}
}
ReadableType.String -> list.add(readableArray.getString(i))
ReadableType.Map -> list.add(toBundle(readableArray.getMap(i)))
ReadableType.Array -> list.add(toList(readableArray.getArray(i)))
else -> throw IllegalArgumentException("Could not convert object in array.")
}
}
return list
}
/**
* Convert a [WritableMap] to a [Bundle]. Note: Each array is converted to an [ArrayList].
*
* @param readableMap the [WritableMap] to convert.
* @return the converted [Bundle].
*/
@JvmStatic
@Suppress("REDUNDANT_ELSE_IN_WHEN")
public fun toBundle(readableMap: ReadableMap?): Bundle? {
if (readableMap == null) {
return null
}
val iterator = readableMap.keySetIterator()
val bundle = Bundle()
while (iterator.hasNextKey()) {
val key = iterator.nextKey()
when (readableMap.getType(key)) {
ReadableType.Null -> bundle.putString(key, null)
ReadableType.Boolean -> bundle.putBoolean(key, readableMap.getBoolean(key))
ReadableType.Number ->
bundle.putDouble(key, readableMap.getDouble(key)) // Can be int or double.
ReadableType.String -> bundle.putString(key, readableMap.getString(key))
ReadableType.Map -> bundle.putBundle(key, toBundle(readableMap.getMap(key)))
ReadableType.Array -> bundle.putSerializable(key, toList(readableMap.getArray(key)))
else -> throw IllegalArgumentException("Could not convert object with key: $key.")
}
}
return bundle
}
}
@@ -22,7 +22,8 @@ internal class CallbackImpl(private val jsInstance: JSInstance, private val call
throw RuntimeException(
"Illegal callback invocation from native module. This callback type only permits a single invocation from native code.")
}
jsInstance.invokeCallback(callbackId, Arguments.fromJavaArgs(args))
@Suppress("UNCHECKED_CAST")
jsInstance.invokeCallback(callbackId, Arguments.fromJavaArgs(args as Array<Any?>))
invoked = true
}
@@ -15,7 +15,7 @@ import com.facebook.proguard.annotations.DoNotStrip
public class CxxCallbackImpl @DoNotStrip private constructor() : HybridClassBase(), Callback {
override fun invoke(vararg args: Any?) {
nativeInvoke(Arguments.fromJavaArgs(args))
@Suppress("UNCHECKED_CAST") nativeInvoke(Arguments.fromJavaArgs(args as Array<Any?>))
}
private external fun nativeInvoke(arguments: NativeArray)
@@ -675,6 +675,18 @@ public class FabricUIManager
: null);
}
@AnyThread
@ThreadConfined(ANY)
@UnstableReactNativeAPI
public PreparedLayout reusePreparedLayoutWithNewReactTags(
PreparedLayout preparedLayout, int[] reactTags) {
return new PreparedLayout(
preparedLayout.getLayout(),
preparedLayout.getMaximumNumberOfLines(),
preparedLayout.getVerticalOffset(),
reactTags);
}
@AnyThread
@ThreadConfined(ANY)
@UnstableReactNativeAPI
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c8b19934d19d6b4514a0395edecb1330>>
* @generated SignedSource<<93aab733661b558c1701b728c18b3d00>>
*/
/**
@@ -23,5 +23,5 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
// We could use JNI to get the defaults from C++,
// but that is more expensive than just duplicating the defaults here.
override fun preventShadowTreeCommitExhaustionWithLocking(): Boolean = true
}
@@ -21,7 +21,6 @@ import com.facebook.react.bridge.NativeArray
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.UIManager
import com.facebook.react.bridge.WritableNativeArray
import com.facebook.react.common.annotations.FrameworkAPI
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.common.build.ReactBuildConfig
@@ -105,9 +104,8 @@ internal class BridgelessReactContext(context: Context, private val reactHost: R
private val reactHost: ReactHostImpl,
private val jsModuleInterface: Class<out JavaScriptModule>
) : InvocationHandler {
override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
val jsArgs: NativeArray =
if (args != null) Arguments.fromJavaArgs(args) else WritableNativeArray()
override fun invoke(proxy: Any, method: Method, args: Array<Any?>): Any? {
val jsArgs: NativeArray = Arguments.fromJavaArgs(args)
reactHost.callFunctionOnModule(
JavaScriptModuleRegistry.getJSModuleName(jsModuleInterface), method.name, jsArgs)
return null
@@ -13,10 +13,8 @@ import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.window.layout.WindowMetricsCalculator
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
/**
* Holds an instance of the current DisplayMetrics so we don't have to thread it through all the
@@ -64,19 +62,9 @@ public object DisplayMetricsHolder {
@JvmStatic
public fun initDisplayMetrics(context: Context) {
val displayMetrics = context.resources.displayMetrics
val windowDisplayMetrics = DisplayMetrics()
windowDisplayMetrics = displayMetrics
val screenDisplayMetrics = DisplayMetrics()
windowDisplayMetrics.setTo(displayMetrics)
screenDisplayMetrics.setTo(displayMetrics)
if (isEdgeToEdgeFeatureFlagOn) {
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context).let {
windowDisplayMetrics.widthPixels = it.bounds.width()
windowDisplayMetrics.heightPixels = it.bounds.height()
}
}
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
// Get the real display metrics if we are using API level 17 or higher.
// The real metrics include system decor elements (e.g. soft menu bar).
@@ -84,8 +72,6 @@ public object DisplayMetricsHolder {
// See:
// http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(screenDisplayMetrics)
DisplayMetricsHolder.windowDisplayMetrics = windowDisplayMetrics
DisplayMetricsHolder.screenDisplayMetrics = screenDisplayMetrics
}
@@ -13,10 +13,10 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
/** Listener used to hook into the UIManager update process. */
@Deprecated("Use UIManagerListener instead. This will be deleted in some future release.")
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
public interface UIManagerModuleListener {
internal interface UIManagerModuleListener {
/**
* Called right before view updates are dispatched at the end of a batch. This is useful if a
* module needs to add UIBlocks to the queue before it is flushed.
*/
public fun willDispatchViewUpdates(uiManager: UIManagerModule)
fun willDispatchViewUpdates(uiManager: UIManagerModule)
}
@@ -256,8 +256,13 @@ public class ReactModalHostView(context: ThemedReactContext) :
if (createNewDialog) {
dismiss()
} else {
updateProperties()
return
// With Props 2.0 the view creation could include initial props. This means the dialog might
// still have to be created before the properties can be set. We only update properties if the
// dialog was already initialized.
dialog?.let {
updateProperties()
return
}
}
// Reset the flag since we are going to create a new dialog
@@ -407,6 +407,9 @@ public class ReactScrollView extends ScrollView
if (mMaintainVisibleContentPositionHelper != null) {
mMaintainVisibleContentPositionHelper.stop();
}
if (mVirtualViewContainerState != null) {
mVirtualViewContainerState.cleanup();
}
}
@Override
@@ -9,6 +9,7 @@ package com.facebook.react.views.scroll
import android.graphics.Rect
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.facebook.common.logging.FLog
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
@@ -46,13 +47,34 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean {
return true
}
internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
internal class VirtualViewContainerState {
private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
private val detectWindowFocus = ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()
private val virtualViews: MutableSet<VirtualView> = mutableSetOf()
private val emptyRect: Rect = Rect()
private val visibleRect: Rect = Rect()
private val prerenderRect: Rect = Rect()
private val onWindowFocusChangeListener =
ViewTreeObserver.OnWindowFocusChangeListener {
debugLog("onWindowFocusChanged")
updateModes()
}
private val scrollView: ViewGroup
constructor(scrollView: ViewGroup) {
this.scrollView = scrollView
if (detectWindowFocus) {
scrollView.viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
public fun cleanup() {
if (detectWindowFocus) {
scrollView.viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
public fun onChange(virtualView: VirtualView) {
if (virtualViews.add(virtualView)) {
@@ -72,7 +94,7 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
// Called on ScrollView onLayout or onScroll
public fun updateState() {
debugLog("VirtualViewContainer.updateState")
debugLog("updateState")
updateModes()
}
@@ -92,8 +114,16 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
when {
rect.isEmpty -> {}
rectsOverlap(rect, visibleRect) -> {
mode = VirtualViewMode.Visible
thresholdRect = visibleRect
if (detectWindowFocus) {
if (scrollView.hasWindowFocus()) {
mode = VirtualViewMode.Visible
} else {
mode = VirtualViewMode.Prerender
}
} else {
mode = VirtualViewMode.Visible
}
}
rectsOverlap(rect, prerenderRect) -> {
mode = VirtualViewMode.Prerender
@@ -115,7 +145,7 @@ private val IS_DEBUG_BUILD =
ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD || ReactBuildConfig.ENABLE_PERFETTO
internal inline fun debugLog(subtag: String, block: () -> String = { "" }) {
if (IS_DEBUG_BUILD) {
if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) {
FLog.d("$DEBUG_TAG:$subtag", block())
}
}
@@ -18,5 +18,6 @@ import com.facebook.proguard.annotations.DoNotStrip
internal class PreparedLayout(
val layout: Layout,
val maximumNumberOfLines: Int,
val verticalOffset: Float
val verticalOffset: Float,
val reactTags: IntArray,
)
@@ -27,7 +27,7 @@ import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.uimanager.BackgroundStyleApplicator
import com.facebook.react.uimanager.ReactCompoundView
import com.facebook.react.uimanager.style.Overflow
import com.facebook.react.views.text.internal.span.ReactTagSpan
import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan
import kotlin.collections.ArrayList
import kotlin.math.roundToInt
@@ -310,8 +310,9 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
override fun hasOverlappingRendering(): Boolean = false
override fun reactTagForTouch(touchX: Float, touchY: Float): Int =
getSpanInCoords(touchX.roundToInt(), touchY.roundToInt(), ReactTagSpan::class.java)?.reactTag
?: id
getSpanInCoords(touchX.roundToInt(), touchY.roundToInt(), ReactFragmentIndexSpan::class.java)
?.fragmentIndex
?.let { preparedLayout?.reactTags[it] } ?: id
@RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private object Api34Utils {
@@ -42,6 +42,8 @@ import com.facebook.react.views.text.internal.span.ReactAbsoluteSizeSpan
import com.facebook.react.views.text.internal.span.ReactBackgroundColorSpan
import com.facebook.react.views.text.internal.span.ReactClickableSpan
import com.facebook.react.views.text.internal.span.ReactForegroundColorSpan
import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan
import com.facebook.react.views.text.internal.span.ReactLinkSpan
import com.facebook.react.views.text.internal.span.ReactOpacitySpan
import com.facebook.react.views.text.internal.span.ReactStrikethroughSpan
import com.facebook.react.views.text.internal.span.ReactTagSpan
@@ -218,7 +220,8 @@ internal object TextLayoutManager {
context: Context,
fragments: MapBuffer,
sb: SpannableStringBuilder,
ops: MutableList<SetSpanOperation>
ops: MutableList<SetSpanOperation>,
outputReactTags: IntArray?
) {
for (i in 0 until fragments.count) {
val fragment = fragments.getMapBuffer(i)
@@ -249,7 +252,11 @@ internal object TextLayoutManager {
(textAttributes.mAccessibilityRole ==
ReactAccessibilityDelegate.AccessibilityRole.LINK)
if (roleIsLink) {
ops.add(SetSpanOperation(start, end, ReactClickableSpan(reactTag)))
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
ops.add(SetSpanOperation(start, end, ReactLinkSpan(i)))
} else {
ops.add(SetSpanOperation(start, end, ReactClickableSpan(reactTag)))
}
}
if (textAttributes.mIsColorSet) {
ops.add(SetSpanOperation(start, end, ReactForegroundColorSpan(textAttributes.mColor)))
@@ -307,7 +314,14 @@ internal object TextLayoutManager {
start, end, CustomLineHeightSpan(textAttributes.effectiveLineHeight)))
}
ops.add(SetSpanOperation(start, end, ReactTagSpan(reactTag)))
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
ops.add(SetSpanOperation(start, end, ReactFragmentIndexSpan(i)))
if (outputReactTags != null) {
outputReactTags[i] = reactTag
}
} else {
ops.add(SetSpanOperation(start, end, ReactTagSpan(reactTag)))
}
}
}
}
@@ -323,7 +337,8 @@ internal object TextLayoutManager {
private fun buildSpannableFromFragmentsOptimized(
context: Context,
fragments: MapBuffer
fragments: MapBuffer,
outputReactTags: IntArray?
): Spannable {
val text = StringBuilder()
val parsedFragments = ArrayList<FragmentAttributes>(fragments.count)
@@ -363,7 +378,7 @@ internal object TextLayoutManager {
val spannable = SpannableString(text)
var start = 0
for (fragment in parsedFragments) {
for ((i, fragment) in parsedFragments.withIndex()) {
val end = start + fragment.length
val spanFlags =
if (start == 0) Spannable.SPAN_INCLUSIVE_INCLUSIVE else Spannable.SPAN_EXCLUSIVE_INCLUSIVE
@@ -386,7 +401,11 @@ internal object TextLayoutManager {
ReactAccessibilityDelegate.AccessibilityRole.LINK)
if (roleIsLink) {
spannable.setSpan(ReactClickableSpan(fragment.reactTag), start, end, spanFlags)
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
spannable.setSpan(ReactLinkSpan(i), start, end, spanFlags)
} else {
spannable.setSpan(ReactClickableSpan(fragment.reactTag), start, end, spanFlags)
}
}
if (fragment.props.isColorSet) {
@@ -453,7 +472,14 @@ internal object TextLayoutManager {
CustomLineHeightSpan(fragment.props.effectiveLineHeight), start, end, spanFlags)
}
spannable.setSpan(ReactTagSpan(fragment.reactTag), start, end, spanFlags)
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
spannable.setSpan(ReactFragmentIndexSpan(i), start, end, spanFlags)
if (outputReactTags != null) {
outputReactTags[i] = fragment.reactTag
}
} else {
spannable.setSpan(ReactTagSpan(fragment.reactTag), start, end, spanFlags)
}
}
start = end
@@ -474,7 +500,10 @@ internal object TextLayoutManager {
} else {
text =
createSpannableFromAttributedString(
context, attributedString, reactTextViewManagerCallback)
context,
attributedString.getMapBuffer(AS_KEY_FRAGMENTS),
reactTextViewManagerCallback,
null)
}
return text
@@ -482,13 +511,12 @@ internal object TextLayoutManager {
private fun createSpannableFromAttributedString(
context: Context,
attributedString: MapBuffer,
reactTextViewManagerCallback: ReactTextViewManagerCallback?
fragments: MapBuffer,
reactTextViewManagerCallback: ReactTextViewManagerCallback?,
outputReactTags: IntArray?
): Spannable {
if (ReactNativeFeatureFlags.enableAndroidTextMeasurementOptimizations()) {
val spannable =
buildSpannableFromFragmentsOptimized(
context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS))
val spannable = buildSpannableFromFragmentsOptimized(context, fragments, outputReactTags)
reactTextViewManagerCallback?.onPostProcessSpannable(spannable)
return spannable
@@ -500,7 +528,7 @@ internal object TextLayoutManager {
// a new spannable will be wiped out
val ops: MutableList<SetSpanOperation> = ArrayList()
buildSpannableFromFragments(context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS), sb, ops)
buildSpannableFromFragments(context, fragments, sb, ops, outputReactTags)
// TODO T31905686: add support for inline Images
// While setting the Spans on the final text, we also check whether any of them are images.
@@ -756,7 +784,11 @@ internal object TextLayoutManager {
heightYogaMeasureMode: YogaMeasureMode,
reactTextViewManagerCallback: ReactTextViewManagerCallback?
): PreparedLayout {
val text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback)
val fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS)
val reactTags = IntArray(fragments.count)
val text =
createSpannableFromAttributedString(
context, fragments, reactTextViewManagerCallback, reactTags)
val baseTextAttributes =
TextAttributeProps.fromMapBuffer(attributedString.getMapBuffer(AS_KEY_BASE_ATTRIBUTES))
val layout =
@@ -779,7 +811,7 @@ internal object TextLayoutManager {
getVerticalOffset(
layout, paragraphAttributes, height, heightYogaMeasureMode, maximumNumberOfLines)
return PreparedLayout(layout, maximumNumberOfLines, verticalOffset)
return PreparedLayout(layout, maximumNumberOfLines, verticalOffset, reactTags)
}
@JvmStatic
@@ -0,0 +1,14 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text.internal.span
/**
* Maps a section of the text to the index of the AttributedString fragment originally used to
* create it.
*/
internal class ReactFragmentIndexSpan(val fragmentIndex: Int) : ReactSpan
@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text.internal.span
import android.text.TextPaint
import android.text.style.ClickableSpan
import android.view.View
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.text.PreparedLayoutTextView
import com.facebook.react.views.text.TextLayoutManager
import com.facebook.react.views.view.ViewGroupClickEvent
/**
* This class is used in [TextLayoutManager] to linkify and style a span of text with
* accessibilityRole="link". This is needed to make nested Text components accessible.
*
* For example, if your React component looks like this:
* ```js
* <Text>
* Some text with
* <Text onPress={onPress} accessible={true} accessibilityRole="link">a link</Text>
* in the middle.
* </Text>
* ```
*/
internal class ReactLinkSpan(val fragmentIndex: Int) : ClickableSpan(), ReactSpan {
override fun onClick(view: View) {
val context = view.context as ReactContext
val textView = view as? PreparedLayoutTextView ?: return
val preparedLayout = textView.preparedLayout ?: return
val reactTag = preparedLayout.reactTags[fragmentIndex]
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, reactTag)
eventDispatcher?.dispatchEvent(
ViewGroupClickEvent(UIManagerHelper.getSurfaceId(context), reactTag))
}
override fun updateDrawState(ds: TextPaint) {
// no super call so we don't change the link color or add an underline by default, as the
// superclass does.
}
}
@@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting
import com.facebook.common.logging.FLog
import com.facebook.react.R
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.uimanager.ReactRoot
import com.facebook.react.views.scroll.VirtualView
import com.facebook.react.views.scroll.VirtualViewContainer
@@ -202,7 +203,7 @@ public class ReactVirtualViewExperimental(context: Context) :
}
internal inline fun debugLog(subtag: String, block: () -> String = { "" }) {
if (IS_DEBUG_BUILD) {
if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) {
FLog.d("$DEBUG_TAG:$subtag", "${block()} [$id][$nativeId]")
}
}
@@ -25,4 +25,6 @@ target_link_libraries(
reactnative
)
target_compile_reactnative_options(hermes_executor PRIVATE)
target_compile_options(hermes_executor PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermes_executor PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif()
@@ -27,4 +27,6 @@ target_link_libraries(hermesinstancejni
)
target_compile_reactnative_options(hermesinstancejni PRIVATE)
target_compile_options(hermesinstancejni PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermesinstancejni PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
@@ -17,7 +17,9 @@ add_library(rninstance
)
target_compile_reactnative_options(rninstance PRIVATE)
target_compile_options(rninstance PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(rninstance PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
target_merge_so(rninstance)
target_include_directories(rninstance PUBLIC .)
@@ -11,9 +11,6 @@ import android.app.Activity
import android.content.Context
import android.graphics.Rect
import android.util.DisplayMetrics
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.bridge.WritableMap
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
import com.facebook.react.uimanager.DisplayMetricsHolder
import com.facebook.react.uimanager.events.Event
@@ -21,6 +18,7 @@ import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.scroll.ReactScrollView
import com.facebook.react.views.virtual.VirtualViewMode
import com.facebook.react.views.virtual.VirtualViewModeChangeEvent
import com.facebook.testutils.shadows.ShadowArguments
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
@@ -32,8 +30,10 @@ import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
/** Tests [ReactVirtualView] */
@Config(shadows = [ShadowArguments::class])
@RunWith(RobolectricTestRunner::class)
class ReactVirtualViewTest {
@@ -45,9 +45,6 @@ class ReactVirtualViewTest {
context = Robolectric.buildActivity(Activity::class.java).create().get()
val arguments = mockStatic(Arguments::class.java)
arguments.`when`<WritableMap> { Arguments.createMap() }.thenAnswer { JavaOnlyMap() }
val displayMetricsHolder = mockStatic(DisplayMetricsHolder::class.java)
displayMetricsHolder
.`when`<DisplayMetrics> { DisplayMetricsHolder.getWindowDisplayMetrics() }
@@ -18,15 +18,18 @@ import org.robolectric.annotation.Implements
import org.robolectric.shadow.api.Shadow
@Implements(Arguments::class)
object ShadowArguments {
@JvmStatic @Implementation fun createArray(): WritableArray = JavaOnlyArray()
class ShadowArguments {
@JvmStatic @Implementation fun createMap(): WritableMap = JavaOnlyMap()
companion object {
@JvmStatic @Implementation fun createArray(): WritableArray = JavaOnlyArray()
@JvmStatic
@Implementation
fun fromJavaArgs(args: Array<Any?>): WritableNativeArray =
WritableNativeArray().apply {
(Shadow.extract(this) as ShadowNativeArray).backingArray = JavaOnlyArray.of(*args)
}
@JvmStatic @Implementation fun createMap(): WritableMap = JavaOnlyMap()
@JvmStatic
@Implementation
fun fromJavaArgs(args: Array<Any?>): WritableNativeArray =
WritableNativeArray().apply {
(Shadow.extract(this) as ShadowNativeArray).backingArray = JavaOnlyArray.of(*args)
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/CallInvoker.h>
#include <jsi/jsi.h>
#include <list>
#include <memory>
namespace facebook::react {
class TestCallInvoker : public CallInvoker {
public:
explicit TestCallInvoker(std::shared_ptr<facebook::jsi::Runtime> runtime)
: runtime_(runtime) {}
void invokeAsync(CallFunc&& func) noexcept override {
queue_.push_back(std::move(func));
}
void invokeSync(CallFunc&& func) override {
if (auto runtime = runtime_.lock()) {
func(*runtime);
}
}
void flushQueue() {
if (auto runtime = runtime_.lock()) {
while (!queue_.empty()) {
queue_.front()(*runtime);
queue_.pop_front();
runtime->drainMicrotasks(); // Run microtasks every cycle.
}
}
}
private:
std::list<CallFunc> queue_{};
std::weak_ptr<facebook::jsi::Runtime> runtime_{};
};
} // namespace facebook::react
@@ -53,9 +53,9 @@ class CxxModule {
class SyncTagType {};
public:
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
using Provider = std::function<std::unique_ptr<CxxModule>()>;
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
using Callback = std::function<void(std::vector<folly::dynamic>)>;
constexpr static AsyncTagType AsyncTag = AsyncTagType();
constexpr static SyncTagType SyncTag = SyncTagType();
@@ -68,8 +68,8 @@ namespace detail {
// only for types compatible with folly::dynamic.
template <typename T>
struct is_dynamic {
typedef typename std::
enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type type;
using type = typename std::
enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type;
};
} // end namespace detail
@@ -16,8 +16,9 @@ using namespace facebook::react;
namespace {
int tempFileFromString(std::string contents) {
const char* tmpDir = getenv("TMPDIR");
if (tmpDir == nullptr)
if (tmpDir == nullptr) {
tmpDir = "/tmp";
}
std::string tmp{tmpDir};
tmp += "/temp.XXXXXX";
@@ -26,7 +26,7 @@ target_link_libraries(hermes_executor_common
)
target_compile_reactnative_options(hermes_executor_common PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(
hermes_executor_common
PRIVATE
@@ -17,7 +17,7 @@ add_library(hermes_inspector_modern
target_compile_reactnative_options(hermes_inspector_modern PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(
hermes_inspector_modern
PRIVATE
@@ -28,7 +28,9 @@ target_link_libraries(jsinspector
reactperflogger
)
target_compile_reactnative_options(jsinspector PRIVATE)
target_compile_options(jsinspector PRIVATE
$<$<CONFIG:Debug>:-DREACT_NATIVE_DEBUGGER_ENABLED=1>
$<$<CONFIG:Debug>:-DREACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1>
)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(jsinspector PRIVATE
-DREACT_NATIVE_DEBUGGER_ENABLED=1
-DREACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1
)
endif ()
@@ -9,7 +9,6 @@
#include "UniqueMonostate.h"
#include <cinttypes>
#include <optional>
#include <string>
#include <unordered_set>
@@ -19,7 +18,7 @@ namespace facebook::react::jsinspector_modern {
struct ExecutionContextDescription {
int32_t id{};
std::string origin{""};
std::string origin;
std::string name{"<anonymous>"};
std::optional<std::string> uniqueId;
};
@@ -36,8 +36,8 @@ namespace facebook::react::jsinspector_modern {
class HostAgent::Impl final {
public:
explicit Impl(
HostAgent& hostAgent,
FrontendChannel frontendChannel,
HostAgent& /*hostAgent*/,
const FrontendChannel& frontendChannel,
HostTargetController& targetController,
HostTargetMetadata hostMetadata,
SessionState& sessionState,
@@ -126,11 +126,11 @@ class HostAgent::Impl final {
else if (req.method == "Page.reload") {
targetController_.getDelegate().onReload({
.ignoreCache =
req.params.isObject() && req.params.count("ignoreCache")
req.params.isObject() && (req.params.count("ignoreCache") != 0u)
? std::optional(req.params.at("ignoreCache").asBool())
: std::nullopt,
.scriptToEvaluateOnLoad = req.params.isObject() &&
req.params.count("scriptToEvaluateOnLoad")
(req.params.count("scriptToEvaluateOnLoad") != 0u)
? std::optional(
req.params.at("scriptToEvaluateOnLoad").asString())
: std::nullopt,
@@ -139,7 +139,8 @@ class HostAgent::Impl final {
shouldSendOKResponse = true;
isFinishedHandlingRequest = true;
} else if (req.method == "Overlay.setPausedInDebuggerMessage") {
auto message = req.params.isObject() && req.params.count("message")
auto message =
req.params.isObject() && (req.params.count("message") != 0u)
? std::optional(req.params.at("message").asString())
: std::nullopt;
if (!isPausedInDebuggerOverlayVisible_ && message.has_value()) {
@@ -45,13 +45,13 @@ class HostTargetSession {
targetController,
std::move(hostMetadata),
state_,
executor) {}
std::move(executor)) {}
/**
* Called by CallbackLocalConnection to send a message to this Session's
* Agent.
*/
void operator()(std::string message) {
void operator()(const std::string& message) {
cdp::PreparsedRequest request;
// Messages may be invalid JSON, or have unexpected types.
try {
@@ -91,7 +91,7 @@ class HostTargetSession {
* there's no current instance.
*/
void setCurrentInstance(InstanceTarget* instance) {
if (instance) {
if (instance != nullptr) {
hostAgent_.setCurrentInstanceAgent(
instance->createAgent(frontendChannel_, state_));
} else {
@@ -148,7 +148,7 @@ std::shared_ptr<HostTarget> HostTarget::create(
HostTargetDelegate& delegate,
VoidExecutor executor) {
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
hostTarget->setExecutor(executor);
hostTarget->setExecutor(std::move(executor));
return hostTarget;
}
@@ -166,7 +166,7 @@ std::unique_ptr<ILocalConnection> HostTarget::connect(
session->setCurrentInstance(currentInstance_.get());
sessions_.insert(std::weak_ptr(session));
return std::make_unique<CallbackLocalConnection>(
[session](std::string message) { (*session)(message); });
[session](const std::string& message) { (*session)(message); });
}
HostTarget::~HostTarget() {
@@ -228,10 +228,7 @@ void HostTargetController::incrementPauseOverlayCounter() {
bool HostTargetController::decrementPauseOverlayCounter() {
assert(pauseOverlayCounter_ > 0 && "Pause overlay counter underflow");
if (--pauseOverlayCounter_ == 0) {
return false;
}
return true;
return --pauseOverlayCounter_ != 0;
}
namespace {
@@ -10,8 +10,6 @@
#include <cassert>
#include <list>
#include <mutex>
#include <tuple>
#include <unordered_map>
namespace facebook::react::jsinspector_modern {
@@ -24,7 +22,7 @@ IRemoteConnection::~IRemoteConnection() {}
IInspector::~IInspector() {}
IPageStatusListener::~IPageStatusListener() {}
const folly::dynamic targetCapabilitiesToDynamic(
folly::dynamic targetCapabilitiesToDynamic(
const InspectorTargetCapabilities& capabilities) {
return folly::dynamic::object(
"nativePageReloads", capabilities.nativePageReloads)(
@@ -56,7 +54,7 @@ class InspectorImpl : public IInspector {
public:
Page(
int id,
const std::string& title,
const std::string& description,
const std::string& vm,
ConnectFunc connectFunc,
InspectorTargetCapabilities capabilities);
@@ -87,7 +85,7 @@ InspectorImpl::Page::Page(
description_(description),
vm_(vm),
connectFunc_(std::move(connectFunc)),
capabilities_(std::move(capabilities)) {}
capabilities_(capabilities) {}
InspectorImpl::Page::operator InspectorPageDescription() const {
return InspectorPageDescription{
@@ -124,7 +122,7 @@ void InspectorImpl::removePage(int pageId) {
std::scoped_lock lock(mutex_);
if (pages_.erase(pageId) != 0) {
for (auto listenerWeak : listeners_) {
for (auto& listenerWeak : listeners_) {
if (auto listener = listenerWeak.lock()) {
listener->onPageRemoved(pageId);
}
@@ -138,6 +136,7 @@ std::vector<InspectorPageDescription> InspectorImpl::getPages() const {
std::vector<InspectorPageDescription> inspectorPages;
// pages_ is a std::map keyed on an incremental id, so this is insertion
// ordered.
inspectorPages.reserve(pages_.size());
for (auto& it : pages_) {
inspectorPages.push_back(InspectorPageDescription(it.second));
}
@@ -39,7 +39,7 @@ struct InspectorTargetCapabilities {
bool prefersFuseboxFrontend = false;
};
const folly::dynamic targetCapabilitiesToDynamic(
folly::dynamic targetCapabilitiesToDynamic(
const InspectorTargetCapabilities& capabilities);
struct InspectorPageDescription {
@@ -16,7 +16,7 @@ InstanceAgent::InstanceAgent(
FrontendChannel frontendChannel,
InstanceTarget& target,
SessionState& sessionState)
: frontendChannel_(frontendChannel),
: frontendChannel_(std::move(frontendChannel)),
target_(target),
sessionState_(sessionState) {
(void)target_;
@@ -36,7 +36,7 @@ bool InstanceAgent::handleRequest(const cdp::PreparsedRequest& req) {
void InstanceAgent::setCurrentRuntime(RuntimeTarget* runtimeTarget) {
auto previousRuntimeAgent = std::move(runtimeAgent_);
if (runtimeTarget) {
if (runtimeTarget != nullptr) {
runtimeAgent_ = runtimeTarget->createAgent(frontendChannel_, sessionState_);
} else {
runtimeAgent_.reset();
@@ -18,7 +18,7 @@ std::shared_ptr<InstanceTarget> InstanceTarget::create(
VoidExecutor executor) {
std::shared_ptr<InstanceTarget> instanceTarget{
new InstanceTarget(executionContextManager, delegate)};
instanceTarget->setExecutor(executor);
instanceTarget->setExecutor(std::move(executor));
return instanceTarget;
}
@@ -16,9 +16,7 @@
#include <jsinspector-modern/InspectorInterfaces.h>
#include <jsinspector-modern/RuntimeAgent.h>
#include <list>
#include <memory>
#include <optional>
namespace facebook::react::jsinspector_modern {
@@ -14,7 +14,7 @@ enum ReactNativeLogLevel {
ReactNativeLogLevelFatal = 4
};
typedef void (*reactnativelogfunctype)(ReactNativeLogLevel, const char*);
using reactnativelogfunctype = void (*)(ReactNativeLogLevel, const char*);
#ifdef __cplusplus
extern "C" {
@@ -7,6 +7,7 @@
#pragma once
#include <ReactCommon/TestCallInvoker.h>
#include <gtest/gtest.h>
#include <hermes/hermes.h>
#include <react/bridging/Bridging.h>
@@ -15,34 +16,24 @@
namespace facebook::react {
class TestCallInvoker : public CallInvoker {
public:
void invokeAsync(CallFunc&& fn) noexcept override {
queue_.push_back(std::move(fn));
}
void invokeSync(CallFunc&&) override {
FAIL() << "JSCallInvoker does not support invokeSync()";
}
private:
friend class BridgingTest;
std::list<CallFunc> queue_;
};
class BridgingTest : public ::testing::Test {
public:
BridgingTest(BridgingTest& other) = delete;
BridgingTest& operator=(BridgingTest& other) = delete;
BridgingTest(BridgingTest&& other) = delete;
BridgingTest& operator=(BridgingTest&& other) = delete;
protected:
BridgingTest()
: invoker(std::make_shared<TestCallInvoker>()),
runtime(hermes::makeHermesRuntime(
: runtime(hermes::makeHermesRuntime(
::hermes::vm::RuntimeConfig::Builder()
// Make promises work with Hermes microtasks.
.withMicrotaskQueue(true)
.build())),
rt(*runtime) {}
rt(*runtime),
invoker(std::make_shared<TestCallInvoker>(runtime)) {}
~BridgingTest() {
~BridgingTest() override {
LongLivedObjectCollection::get(rt).clear();
}
@@ -62,16 +53,12 @@ class BridgingTest : public ::testing::Test {
}
void flushQueue() {
while (!invoker->queue_.empty()) {
invoker->queue_.front()(*runtime);
invoker->queue_.pop_front();
rt.drainMicrotasks(); // Run microtasks every cycle.
}
invoker->flushQueue();
}
std::shared_ptr<TestCallInvoker> invoker;
std::unique_ptr<jsi::Runtime> runtime;
std::shared_ptr<jsi::Runtime> runtime;
jsi::Runtime& rt;
std::shared_ptr<TestCallInvoker> invoker;
};
} // namespace facebook::react
@@ -21,6 +21,6 @@ endif()
target_compile_reactnative_options(react_debug PRIVATE)
target_compile_options(react_debug PRIVATE -Wpedantic)
if(NOT ${CMAKE_BUILD_TYPE} MATCHES Debug)
if(NOT ${CMAKE_BUILD_TYPE} MATCHES Debug AND NOT REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(react_debug PUBLIC -DNDEBUG)
endif()
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1de02178e1be302bb4b19501950b260a>>
* @generated SignedSource<<16c5fdf431579bbfd454a28c06f28c41>>
*/
/**
@@ -27,7 +27,9 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
public:
ReactNativeFeatureFlagsOverridesOSSExperimental() = default;
bool preventShadowTreeCommitExhaustionWithLocking() override {
return true;
}
};
} // namespace facebook::react
@@ -1,11 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
// This header is left here for compatibility reasons.
#include <react/bridging/CallbackWrapper.h>
@@ -1,11 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
// This header is left here for compatibility reasons.
#include <react/bridging/LongLivedObject.h>
@@ -0,0 +1,31 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/TestCallInvoker.h>
#include <gtest/gtest.h>
#include <hermes/hermes.h>
#include <memory>
namespace facebook::react {
template <typename T, typename... Args>
class TurboModuleTestFixture : public ::testing::Test {
public:
explicit TurboModuleTestFixture(Args... args)
: runtime_(facebook::hermes::makeHermesRuntime()),
jsInvoker_(std::make_shared<TestCallInvoker>(runtime_)),
module_(std::make_shared<T>(jsInvoker_, std::forward<Args>(args)...)) {}
protected:
std::shared_ptr<jsi::Runtime> runtime_{};
std::shared_ptr<TestCallInvoker> jsInvoker_{};
std::shared_ptr<T> module_;
};
} // namespace facebook::react
@@ -190,7 +190,8 @@ TextMeasurement TextLayoutManager::measure(
};
auto measurement =
ReactNativeFeatureFlags::disableTextLayoutManagerCacheAndroid()
(ReactNativeFeatureFlags::disableTextLayoutManagerCacheAndroid() ||
ReactNativeFeatureFlags::enablePreparedTextLayout())
? measureText()
: textMeasureCache_.get(
{.attributedString = attributedString,
@@ -310,14 +311,19 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
jfloat,
jfloat)>("prepareTextLayout");
return preparedTextCache_.get(
static auto reusePreparedLayoutWithNewReactTags =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<JPreparedLayout::javaobject(
JPreparedLayout::javaobject, jintArray)>(
"reusePreparedLayoutWithNewReactTags");
const auto [key, preparedText] = preparedTextCache_.getWithKey(
{.attributedString = attributedString,
.paragraphAttributes = paragraphAttributes,
.layoutConstraints = layoutConstraints},
[&] {
[&]() {
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
auto attributedStringMB = JReadableMapBuffer::createWithContents(
toMapBuffer(attributedString));
auto paragraphAttributesMB = JReadableMapBuffer::createWithContents(
@@ -336,6 +342,39 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
minimumSize.height,
maximumSize.height))};
});
// PreparedTextCacheKey allows equality of layouts which are the same
// display-wise, but ShadowView fragments (and thus react tags) may have
// changed.
const auto& fragments = attributedString.getFragments();
const auto& cacheKeyFragments = key->attributedString.getFragments();
bool needsNewReactTags = [&] {
for (size_t i = 0; i < fragments.size(); i++) {
if (fragments[i].parentShadowView.tag !=
cacheKeyFragments[i].parentShadowView.tag) {
return true;
}
}
return false;
}();
if (needsNewReactTags) {
std::vector<int> reactTags(fragments.size());
for (size_t i = 0; i < reactTags.size(); i++) {
reactTags[i] = fragments[i].parentShadowView.tag;
}
auto javaReactTags = jni::JArrayInt::newArray(fragments.size());
javaReactTags->setRegion(
0, static_cast<jsize>(reactTags.size()), reactTags.data());
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
return PreparedLayout{jni::make_global(reusePreparedLayoutWithNewReactTags(
fabricUIManager, preparedText->get(), javaReactTags.get()))};
} else {
return PreparedLayout{*preparedText};
}
}
TextMeasurement TextLayoutManager::measurePreparedLayout(
@@ -16,7 +16,9 @@ add_library(bridgeless
${bridgeless_SRC}
)
target_compile_reactnative_options(bridgeless PRIVATE)
target_compile_options(bridgeless PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(bridgeless PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
target_include_directories(bridgeless PUBLIC .)
react_native_android_selector(fabricjni fabricjni "")

Some files were not shown because too many files have changed in this diff Show More