Compare commits

..
Author SHA1 Message Date
Vojtech Novak f499de9031 fix hermes param handling in test-e2e-local.js 2024-12-03 16:42:57 +01:00
Dmitry Rykun 318db8eedf Add Android-specific ImageRequestParams (#47930)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47930

This diff adds a list of props that will be used by the Android `ImagePrefetcher` to create an `ImageRequest`. This list is derived from all the props that `ReactImageView` uses to create its `ImageOptions` and `ImageRequest` objects.
Changelog: [Internal]

Reviewed By: javache

Differential Revision: D66453306

fbshipit-source-id: ca4f59784c81f2b94ed4b052f6fbe5e8c6b97a2a
2024-12-03 04:09:42 -08:00
Richard Barnes 071d223ee0 Remove unused-variable in ../xplat/compphoto/gpuEngine/tools/ToolHelpers.cpp +3 (#48052)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48052

LLVM-15 has a warning `-Wunused-variable` which we treat as an error because it's so often diagnostic of a code issue. Unused variables can compromise readability or, worse, performance.

This diff either (a) removes an unused variable and, possibly, it's associated code or (b) qualifies the variable with `[[maybe_unused]]`.

#buildsonlynotests - Builds are sufficient

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Reviewed By: javache, wuyuoss

Differential Revision: D66143498

fbshipit-source-id: a461f115610258777bd1173f91cf4d4472e2fc5e
2024-12-03 04:07:49 -08:00
David Vacca 67bff8734f Delete GuardedResultAsyncTask (#48058)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48058

Delete unused abstract class GuardedResultAsyncTask

I'm deleting without depreacting becuase I didn't find usages internally or externally

changelog: [Android][Breaking] Delete unused abstract class GuardedResultAsyncTask

Reviewed By: javache

Differential Revision: D66416570

fbshipit-source-id: 8b369dfcd3e99b24f97c62cec9229f82ba5eed77
2024-12-02 18:45:27 -08:00
David Vacca b25b65ba19 Delete deprecated class FabricViewStateManager (#48057)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48057

FabricViewStateManager has been deprecated for a long time and it's unused, let's delete it

changelog: [Android][Breaking] Delete deprecated class FabricViewStateManager

Reviewed By: javache, cortinico

Differential Revision: D66403219

fbshipit-source-id: e8f893b6a240ca09c0e86821c0a15fa345ffd221
2024-12-02 18:45:27 -08:00
David Vacca a4849cb3d6 Reduce visibility of ComponentNameResolver to internal (#48056)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48056

ComponentNameResolver is meant to be used only internaly, we reduce its visbility to internal

changelog: [Android][Breaking] Removed ComponentNameResolver from public API

Reviewed By: javache

Differential Revision: D66403218

fbshipit-source-id: bf08284400a6dc6446b771c894488ed3fb371e25
2024-12-02 18:45:27 -08:00
David Vacca 385b9f4265 Migrate ComponentNameResolver to kotlin (#47919)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47919

Migrate ComponentNameResolver to kotlin

changelog: [Android][Changed] Migrate ComponentNameResolver to kotlin

Reviewed By: javache

Differential Revision: D66403041

fbshipit-source-id: 7af1a89988373014c5aeb1f6145324cf889a1a36
2024-12-02 18:45:27 -08:00
Mateo Guzmán bfc8b3391e Migrate BatchEventDispatchedListener to kotlin (#48038)
Summary:
Migrating `BatchEventDispatchedListener` from Java to Kotlin

## Changelog:

[INTERNAL] - Migrating `BatchEventDispatchedListener` from Java to Kotlin

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

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

Reviewed By: tdn120, cortinico

Differential Revision: D66653052

Pulled By: javache

fbshipit-source-id: 494866c54d349587e313c18717c6c587a2228aa8
2024-12-02 17:53:59 -08:00
Joe Vilches 2df7552fa2 Back out "Back out "[yoga][intrinsic sizing] Update public API for intrinsic sizing setters""
Summary:
Original commit changeset: 793f77dad021

Original Phabricator Diff: D66332309

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D66662661

fbshipit-source-id: 22ed3ac9492f0a563c041ce4cb5fba4b65b53211
2024-12-02 17:29:49 -08:00
Joe Vilches e1623b7525 Back out "Back out "[yoga][intrinsic sizing] Modify private apis to set, store, and get intrinsic sizing keywords"" (#48049)
Summary:
X-link: https://github.com/facebook/yoga/pull/1756

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

Changelog: [Internal]

Original commit changeset: 1d596964e0c8

Original Phabricator Diff: D66332307

Reviewed By: NickGerleman

Differential Revision: D66662662

fbshipit-source-id: 4f9ac2b1557b848f519dcd728d7097b52f1190b3
2024-12-02 17:29:49 -08:00
Eli White fa8a25eb6b Make enum types annotation objects instead of literal strings and numbers (#47349)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47349

This is needed to be able to recurse into the literals and compare them.

I'm primarily unsure if there is a problem representing doubles/floats as numbers instead of strings though.

Changelog: [Internal]

Reviewed By: makovkastar

Differential Revision: D65284058

fbshipit-source-id: b2de9ed5fb7f079a432c94aaea69027863879909
2024-12-02 14:32:35 -08:00
Eli White 96c2be8567 Don't store pretty printed json in schema
Summary: Changelog: [Internal]

Reviewed By: javache

Differential Revision: D66603960

fbshipit-source-id: 2d7844df76f168b99eb76bb19359ee71e953d0b2
2024-12-02 14:32:35 -08:00
Thomas Nardone 53bc661138 ReactViewGroup - track clipped views (#47987)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47987

Store a tag value for whether the view is added or removed, to better track the state instead of checking view.getParent().

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D66383241

fbshipit-source-id: 16521eb4052e9473be058a00cfe29d7f198b7861
2024-12-02 14:28:35 -08:00
Ramanpreet Nara 69ecaef068 Remove native -> js call noop-ing after early js error (#47915)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47915

## Purpose of this noop-ing
If an fatal js error happens during js runtime init, the js thread continues executing and raises **yet another** fatal error.

This noop-ing was an **attempt** to prevent that second inactionable fatal from happening: That fatal is usually inactionable.

## Problems with this noop-ing
I don't think this is the right approach: There could be legitimate reasons to continue executing native -> js calls post first js fatal.

I don't think it does *much*: it doesn't noop native -> js calls executed on the runtime scheduler, which should be most of them.

## Changes

Instead of trying to prevent that fatal, just let it happen. Then, don't report the second fatal: D66193194 and D66392706.

## Safetly
The production impact is negligible: This codepath is executed only after early js errors. There shouldn't be any in production right now.

We've spent a lot of time making our javascript error handling pipeline's coverage compresive. So, after an early js fatal error happens, subsequent js fatals should get handled properly.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D66394278

fbshipit-source-id: ef342fc2eba9ae9f27b15a0f412fb69bd92aed43
2024-12-02 14:18:55 -08:00
Alex Hunt 215b0a50cf Update debugger-frontend from 6b80704...486803f (#48051)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48051

Changelog: [Internal] - Update `react-native/debugger-frontend` from 6b80704...486803f

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/facebookexperimental/rn-chrome-devtools-frontend/compare/6b80704fd50ea0bf10f5f5da5a4343de29aff8b2...486803f6bf272e0629297265dee8048a2f1269dd).

Reviewed By: hoxyq

Differential Revision: D66664563

fbshipit-source-id: 8c04c0f99e203f594d4e86a52aca99803c4b005f
2024-12-02 12:02:07 -08:00
Alex Hunt 11c214d0c9 Update FuseboxTracer to closer align with Chrome trace events (#48020)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48020

Progress towards Performance panel display parity for User Timing events in Chrome DevTools.

- Support nullable `track` name in `ReactPerfLogger`, passing track name directly to `FuseboxTracer`.
- Update `FuseboxTracer` to register "Main" process and send V8-aligned `blink.user_timing`-categorised tracing events.

The previous track naming strategy continues to be used under Perfetto.

Changelog: [Internal]

**Better, but not perfect yet**

For now, this is probably the upper limit of how aligned we can be with Chrome on web, since our forked DevTools frontend is 6mo+ behind `main`. Notably, it does not include equivalent custom track handling today: https://github.com/ChromeDevTools/devtools-frontend/commit/4b4435feef14c5c0ac71d932940c6ea7613f8afe

{F1969750671}

> Importing an exact trace from Chrome into RNDT is unable to sub-group the "Timings" track.

Reviewed By: rubennorte

Differential Revision: D66579308

fbshipit-source-id: fa57151d2be477eaa15f62e5c19ee09b1a0ef43a
2024-12-02 11:24:34 -08:00
Andrew Datsenko dc88683798 Corrently symbolicate message (#48048)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48048

Changelog: [Internal]
Some messages may contains multi line content, ie snapshot comparison, multiline strings comparison.

Fixing this by checking first code pointer in the stack and slicing from there.

Reviewed By: christophpurrer, rubennorte

Differential Revision: D66660107

fbshipit-source-id: 57cea02cf6aae3c24f351504c2e077b5a2de0761
2024-12-02 11:16:03 -08:00
Alex Hunt 1a9780f0e3 Remove FuseboxClient CDP domain (#48004)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48004

Follows https://github.com/facebook/react-native/pull/47962 and depends on https://github.com/facebookexperimental/rn-chrome-devtools-frontend/pull/139.

Updates the modern debugger server to no longer respond to `FuseboxClient` messages — namely `FuseboxClient.setClientMetadata`. This method is replaced by `ReactNativeApplication.enable` for identifying the React Native DevTools frontend.

Changelog:
[General][Breaking] - The `FuseboxClient.setClientMetadata` CDP method is removed. Instead, use `ReactNativeApplication.enable`.

Reviewed By: rubennorte

Differential Revision: D66575324

fbshipit-source-id: f2b4cbacd857931832d89305510f5aaf51df483a
2024-12-02 08:27:52 -08:00
Peter Abbondanzo 72bb2f4089 Bump Fresco to 3.5.0
Summary:
Updates Fresco from 3.4.0 to 3.5.0. Picks up a few new features, including experimental support for XML-based drawable resource types

Changelog:
[Android][Changed] - Update Fresco to 3.5.0

Reviewed By: cortinico

Differential Revision: D66553841

fbshipit-source-id: d0e630c73ba73ea9bbf96f7d630471c5383145f0
2024-12-02 07:33:37 -08:00
Pieter De Baets 70a957452c Restore deprecated TurboReactPackage (#48039)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48039

This class was removed in D66127067 but was marked as DeprecatedInNewArchitecture and not Deprecated, which limited the signal we gave to developers to move away from this.

Restore for now to e

Changelog: [Android][Fixed] Reverted removal of TurboReactPackage

Reviewed By: rshest

Differential Revision: D66648209

fbshipit-source-id: 165f9390b4874e69353612b929d87b0c495588af
2024-12-02 07:30:52 -08:00
Alex Hunt a4310ede9c Update debugger-frontend from b61aae3...6b80704 (#48042)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48042

Changelog: [Internal] - Update `react-native/debugger-frontend` from b61aae3...6b80704

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/facebookexperimental/rn-chrome-devtools-frontend/compare/b61aae3ccc6e2684dfbf1e2a06b0f985b459f11f...6b80704fd50ea0bf10f5f5da5a4343de29aff8b2).

Reviewed By: blakef

Differential Revision: D66651149

fbshipit-source-id: 6848eebb4b7c04c7c04ae1f784fc39785945bf7b
2024-12-02 06:25:54 -08:00
Rubén Norte 0580e88aa5 Allow tests to specify opt/dev mode (#48022)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48022

Changelog: [internal]

This adds support for Fantom options in tests to configure different aspects of the test execution.

For now, it only supports specifying the mode (dev or opt) so we can try things without having to change the runner (watch mode still works if you change mode :D).

Fantom options are specified as pragmas in the docblock of the test. E.g.:

```
/**
 * flow strict-local
 * format
 * fantom_mode opt
 */
```

We expect this is mostly going to be used for one-time tests and that regular tests won't specify the mode (they'll just run in dev mode).

Maybe we can evolve this in the future to specify that you want a test to be executed in both modes, to ensure the behavior is consistent in dev/prod.

Reviewed By: rshest

Differential Revision: D66597626

fbshipit-source-id: b12325fc2235740cc2a3e0283d6a556091c1794c
2024-12-02 06:06:26 -08:00
Rubén Norte 762389f775 Extract logic to debug command errors to shared function (#48019)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48019

Changelog: [internal]

Just a minor refactor to reduce code duplication.

Reviewed By: rshest

Differential Revision: D66596730

fbshipit-source-id: 2afa501cd5664402adf4aceba059a4d987c3ed62
2024-12-02 06:06:26 -08:00
Rubén Norte 4bced9099a Implement warm up step to remove costly builds from individual test running time (#48015)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48015

Changelog: [internal]

Right now, when we run individual Fantom tests, we compile Hermes and the RN Tester CLI as part of the test, which causes the first test to run to be very slow and the remaining tests in the same run to be very fast.

This is misleading because it makes it look like the test itself is slow, when it's actually paying a price for everyone.

Fortunately, Jest has an option to do a global setup before any tests in the project run (and it doesn't run if none of the tests in the project run, in multi-project setups), so we can use it to do the necessary warmup so it doesn't end up being attributed to individual tests.

Reviewed By: javache

Differential Revision: D66595406

fbshipit-source-id: 496aa2b248da661f7504c8445fed1edad0301803
2024-12-02 06:06:26 -08:00
Rubén Norte 21cc745d51 Extract some logic from runner to utils module (#48014)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48014

Changelog: [internal]

Just a small refactor in preparation for a following change that will add more usages for these utilities. It also cleans up the runner file which is good too.

Reviewed By: javache

Differential Revision: D66595405

fbshipit-source-id: e734d76006ce937fadd1cb673035db85a3e838dd
2024-12-02 06:06:26 -08:00
Vojtech Novak 9147b0753a fix IOException in BuildCodegenCLITask (#48008)
Summary:
building RN tester with 0.77 rc-0 doesn't work now because of `java.io.IOException:  No such file or directory` on line 48.

`buildDirectory` is a Gradle property representing a file

https://github.com/facebook/react-native/pull/47552 removes this file altogether so feel free to close if that one is the "right one"

## Changelog:

[ANDROID] [FIXED] - fix IOException in `BuildCodegenCLITask`

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

Test Plan: After this change, building RN tester works.

Reviewed By: cortinico

Differential Revision: D66650038

Pulled By: robhogan

fbshipit-source-id: 11cd83493fa118c6b79d11c9113228dd3971a803
2024-12-02 05:09:54 -08:00
Rubén Norte 2480de6ffb Add Fantom test for LongTask API (#48041)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48041

Changelog: [internal]

This adds a Fantom test for the LongTask API, testing it using public APIs :D

Reviewed By: javache

Differential Revision: D66601861

fbshipit-source-id: f3531e8b58ffa044dcb5cec2f462ae6a31c27790
2024-12-02 04:47:17 -08:00
Rubén Norte b6f2b148f4 Add suppor for toBeLessThanOrEqual and toBeGreaterThanOrEqual (#48040)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48040

Changelog: [internal]

We need more expectations for a new test we're writing. This just adds:
* `expect(received).toBeLessThanOrEqual(expected)`
* `expect(received).toBeGreaterThanOrEqual(expected)`

Reviewed By: sammy-SC

Differential Revision: D66601921

fbshipit-source-id: 0a73f7757117ed790b95796b259244c8259136b7
2024-12-02 04:47:17 -08:00
Rubén Norte 60a4d22307 Migrate ReactFabricPublicInstance tests to Fantom (#48025)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48025

Changelog: [internal]

This migrates the existing tests we have for the current public API for host component refs to Fantom.

After this, the only remaining test to migrate before we can clean up the legacy mocks for Fabric, etc. is the one for IntersectionObserer.

Reviewed By: sammy-SC

Differential Revision: D66599070

fbshipit-source-id: 67da1cd3b360ac79aed6fe6ad2a8bd5273754174
2024-12-02 04:47:17 -08:00
Rubén Norte 09f6d165ec Export ReactNativeTester (#48024)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48024

Changelog: [internal]

Reviewed By: rshest

Differential Revision: D66599071

fbshipit-source-id: 52585e030642e5b6bd8921d2c9f9d14ee5d6ce71
2024-12-02 04:47:17 -08:00
Nicola Corti 88c9a42fca Fix eslint warnings in react-native (#48016)
Summary:
There are currently 2 warnings firing for every PR (e.g. look here https://github.com/facebook/react-native/pull/48013/files).

Those are annoying so I'm fixing them here.

## Changelog:

[INTERNAL] - Fix eslint warnings in react-native

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

Test Plan: CI

Reviewed By: NickGerleman

Differential Revision: D66595842

Pulled By: cortinico

fbshipit-source-id: 0fd39629a97dbbe5d75a78c8eaa50241faf6bf1e
2024-12-02 04:11:22 -08:00
zhongwuzw 28ced2e558 Fabric: Fixes Modal onRequestClose not called (#48037)
Summary:
Fixes https://github.com/facebook/react-native/issues/48030 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes Modal onRequestClose not called

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

Test Plan: Repro please see  https://github.com/facebook/react-native/issues/48030.

Reviewed By: cortinico

Differential Revision: D66647232

Pulled By: cipolleschi

fbshipit-source-id: 773517dfe45f6f2e6348cda225e972fbac05edc2
2024-12-02 04:06:40 -08:00
CHOIMINSEOK b8095f4692 Avoid NPE when touch event is triggered before SurfaceManager is initiated (#48007)
Summary:
A NPE can occur when a user touches the screen before the `SurfaceMountingManager` is initialized. Below is an example of the error log from our production service. This issue can also be reproduced using RNTester. To prevent invalid touch events during init time of rn app from causing an NPE, add a null check for SurfaceMountingManager before calling mark/sweepActiveTouchForTag.

```
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.facebook.react.fabric.mounting.SurfaceMountingManager.markActiveTouchForTag(int)' on a null object reference
       at com.facebook.react.fabric.FabricUIManager.markActiveTouchForTag(FabricUIManager.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.markActiveTouchForTag(JSTouchDispatcher.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java)
       at com.facebook.react.runtime.ReactSurfaceView.dispatchJSTouchEvent(ReactSurfaceView.java)
       at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2870)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:794)
       at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1967)
       at android.app.Activity.dispatchTouchEvent(Activity.java:4571)
       at com.rainist.banksalad2.feature.common.BaseActivity.dispatchTouchEvent(BaseActivity.java)
       at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:70)
       at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:752)
       at android.view.View.dispatchPointerEvent(View.java:16498)
       at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8676)
       at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8423)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7752)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7978)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:8035)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11343)
       at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:11212)
       at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:11168)
       at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11477)
       at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:337)
       at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
       at android.os.MessageQueue.next(MessageQueue.java:335)
       at android.os.Looper.loopOnce(Looper.java:187)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:9063)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
```
https://github.com/user-attachments/assets/e9c6ff84-c94d-4392-9042-8e635197202e

## Changelog:

[Android] [Fixed] - Avoid NPE when touch event is triggered before SurfaceManager is initiated

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

Test Plan:
I checked the crashed being fixed on RNTester.

https://github.com/user-attachments/assets/71f7e359-707a-494c-ae34-fef8d432e612

Reviewed By: cortinico

Differential Revision: D66594576

Pulled By: javache

fbshipit-source-id: b1559d94866bdb021e0374f1953684849603033c
2024-12-02 03:54:34 -08:00
Wojciech Lewicki 44b04b6d42 fix: add mising subpsec to React-Fabric podspec (#48023)
Summary:
Running app with static linking fails to compile. Probably during https://github.com/facebook/react-native/pull/43581 adding that code was overlooked since the analogous thing seems to be added: https://github.com/facebook/react-native/pull/43581/files#diff-6680f6849631e3dcc5897ee3961a9d6d2bc57aff3eccb79a9d9c634183276202R566.

cc rubennorte since you made the linked PR.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[IOS] [FIXED] - Add missing subpsec to React-Fabric podspec.

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

Test Plan: Run https://github.com/WoLewicki/reproducer-react-native/tree/%40wolewicki/static-linking-with-live-markdown and see that it won't compile without this change.

Reviewed By: cipolleschi

Differential Revision: D66600486

Pulled By: cortinico

fbshipit-source-id: 9de64541e49d27cdf00f37942adab6620476f15a
2024-12-02 03:23:45 -08:00
Wojciech Lewicki fa03840e68 fix: typo in inherited (#48013)
Summary:
fix typo in `inherithed`

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[IOS] [CHANGED] - fix typo in utils.rb

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS] [CHANGED] - fix typo in utils.rb

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

Test Plan: I did not spot any changes when switching it but in some specific build settings it could destroy some behaviors probably.

Reviewed By: cipolleschi

Differential Revision: D66595786

Pulled By: cortinico

fbshipit-source-id: d1607fd1127352533fb2977bdfcafec1edd1aef7
2024-12-02 03:23:01 -08:00
Nicola Corti 490db92562 Gradle to 8.11.1 (#48026)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48026

This should mitigate this particular issue we're seeing on Windows:
- https://github.com/facebook/react-native/issues/46210

Changelog:
[Android] [Changed] - Gradle to 8.11.1

Reviewed By: javache

Differential Revision: D66600321

fbshipit-source-id: d58437485222e189d90bcf4d6b41ca956449ed22
2024-12-02 03:22:32 -08:00
Rob Hogan e996b3f346 Fix Animated on JSC: Object.hasOwn -> obj.hasOwnProperty (#48035)
Summary:
https://github.com/facebook/react-native/pull/46385 introduced use of `Object.hasOwn` as an incidental detail of some `Animated` performance improvements.

Unfortunately, `Object.hasOwn` is not present in the version of JSC shipped with Android, nor the built in iOS JSC until iOS 15.4, which is greater than React Native's minimum version (13.4).

Instead:
 - Use `obj.hasOwnProperty(prop)` for known objects that have the `Object` prototype.
 - Otherwise, use `Object.hasOwn` where it is defined.
 - Lastly, fall back to `Object.prototype.hasOwnProperty.call(obj, prop)`, which is compatible with passed `null`-prototype objects.

Fixes https://github.com/facebook/react-native/issues/47963

Intend to pick for RN 0.77.

## Changelog:

[GENERAL][FIXED] Replace Object.hasOwn usages to fix Animated on JSC

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

Test Plan:
- Run `rn-tester` on Android with Hermes disabled.
 - Verify the FlatList->Basic example redboxes before this change, and works after it.

Reviewed By: yungsters

Differential Revision: D66638379

Pulled By: robhogan

fbshipit-source-id: 51ac525851b41adea3bf3cc41349225138e1f2fe
2024-12-02 03:16:28 -08:00
Hugo FOYART f791fb9e66 fix: FormData filename in content-disposition (#46543)
Summary:
This Pull Request fixes a regression introduced in https://github.com/facebook/react-native/commit/7c7e9e6571c1f702213e9ffbb40921cd5a1a786b, which adds a `filename*` attribute to the `content-disposition` of a FormData part. However, as the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives) states, there is no `filename*` attribute for the `content-disposition` header in case of a form data.

The `filename*` attribute would break the parsing of form data in the request, such as in frameworks like `Next.js` which uses the web implementation of [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request).

Fixes https://github.com/facebook/react-native/issues/44737

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[General] [Fixed] - Remove non compliant `filename*` attribute in a FormData `content-disposition` header

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

Test Plan:
- Clone the `react-native` repo
- Create a simple JS file that will act as a node server and execute it

```javascript
const http = require('http');

const server = http.createServer(async function (r, res) {
    const req = new Request(new URL(r.url, 'http://localhost:3000'), {
      headers: r.headers,
      method: r.method,
      body: r,
      duplex: 'half',
    });

    const fileData = await req.formData();

    console.log(fileData);
    res.writeHead(200);
    res.end();
});
server.listen(3000);
```

- Go to `packages/rn-tester`
- Add a `useEffect` in `js/RNTesterAppShared.js`

```javascript
React.useEffect(() => {
    const formData = new FormData();
    formData.append('file', {
      uri: 'https://www.gravatar.com/avatar',
      name: '测试photo/1.jpg',
      type: 'image/jpeg',
    });

    fetch('http://localhost:3000', {
      method: 'POST',
      body: formData,
    }).then(res => console.log(res.ok));
  });
```

- Run the app on iOS or Android
- The node server should output the file added to the FormData with an encoded name

Reviewed By: robhogan

Differential Revision: D66643317

Pulled By: yungsters

fbshipit-source-id: 0d531528005025bff303505363671e854c0a2b63
2024-12-02 03:14:28 -08:00
Kacper Rozniata b886bc4db9 feat(android): migrate ReactSwitchManager to Kotlin (#48003)
Summary:
This PR migrates `ReactSwitchManager` to Kotlin

Also it moves it's shadow node to a separate file (`ReactSwitchShadowNode.kt`)

## Changelog:

[ANDROID] [CHANGED] - Migrate `ReactSwitchManager` to Kotlin

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

Test Plan: Make sure that `Switch` example in `RNTester` works correctly

Reviewed By: cortinico

Differential Revision: D66594606

Pulled By: javache

fbshipit-source-id: 774641c4cf57d6d5f770df1fed4fcafef2af7ceb
2024-12-02 02:41:31 -08:00
zhongwuzw 91e217ff54 Add completion block when call js module function (#47998)
Summary:
Fabric bridgeless don't call completion block when call js module method, so let's support it :).

## Changelog:

[IOS] [FIXED] - [Fabric] Add completion block when call js module function

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

Test Plan: Module call js module method when passed `onComplete` block can be called successfully in bridgeless mode.

Reviewed By: fabriziocucci

Differential Revision: D66594514

Pulled By: javache

fbshipit-source-id: 74644b1f359a24cfa93389451e172fbc6a8ee1a1
2024-11-29 04:08:52 -08:00
Andrew Datsenko 5ff59b448b Add defaults to ParagraphAttributes::getDebugProps (#47986)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47986

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D66542033

fbshipit-source-id: d1f3d1776138d076636765262e27ae8e5e7342c5
2024-11-28 12:07:56 -08:00
Andrew Datsenko de30f408e5 Add defaults to TextAttributes::getDebugProps (#47985)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47985

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D66542032

fbshipit-source-id: ccecd660f00d3d32909a57534897ba110d53d7b3
2024-11-28 12:07:56 -08:00
Andrew Datsenko 212a743fdf Add float comparison to debugStringConvertilbeUtils (#47984)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47984

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D66541322

fbshipit-source-id: 1adb52de5c9b0321e328532c4394d8067ffce3d8
2024-11-28 12:07:56 -08:00
Andrew Datsenko b27bd00a38 add jest-diff to dependencies (#47990)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47990

Changelog:
[General][Added] - add `jest-diff v29.7.0` to devDependencies

Reviewed By: NickGerleman

Differential Revision: D66541001

fbshipit-source-id: 01c59a936b66f85ce034b59c7928df3c3f8c2a01
2024-11-28 10:04:46 -08:00
lihaitao 2aa79979d3 fix:setColorScheme exception when activity recreate (#47955)
Summary:
solve the exception  on android when `Appearance.setColorScheme` and activity recreate()
Fixes https://github.com/facebook/react-native/issues/47954

## Changelog:

[Android][Fixed] setColorScheme should be called on the UI thread

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

Reviewed By: cortinico

Differential Revision: D66573373

Pulled By: javache

fbshipit-source-id: 97808e163e1c53bb94f4be7269d9cb9e212f2e95
2024-11-28 09:37:07 -08:00
Alex Hunt 2fcf7b1f49 Allow fuseboxClientType_ detection from ReactNativeApplication.enable method (#47962)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47962

Updates `HostAgent` to respond equivalently if either `FuseboxClient.setClientMetadata` (outgoing) or `ReactNativeApplication.enable` (incoming) are sent by the CDP frontend.

This is a partial migration, to be followed by removing the `FuseboxClient.setClientMetadata` method later.

Changelog: [Internal]

Reviewed By: robhogan

Differential Revision: D66501027

fbshipit-source-id: 1ff669c24667f51d240311e75f95747efe577e2d
2024-11-28 09:26:03 -08:00
Rubén Norte 7ccc5934d0 Implement symbolication of error stack traces (#48006)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48006

Changelog: [internal]

This implements symbolication of error stack traces in Fantom tests. We just needed to ask Metro to generate source maps and use the `source-map` package to process the stack traces that we get back from the runtime.

Reviewed By: sammy-SC

Differential Revision: D66577818

fbshipit-source-id: 672c66c246ad8646646d5ed31cabca39eb4f7aca
2024-11-28 07:59:26 -08:00
Pieter De Baets bc9e4db9e9 Demonstrate bug in differentiator where reparented nodes reference non-existent nodes (#48002)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48002

Demonstrates the issue identified in https://github.com/facebook/react-native/issues/47960 and a crash we've been seeing internally around `getViewState` referencing a view that does not exist.

When reparenting unflattened nodes, Differentiator may emit an `update` with a `parentShadowView` that does not exist on the native side yet, thereby crashing Android.

Landing the test-case first (with some test cleanup), so the diff for the actual fix is clearer.

Changelog: [Internal]

Reviewed By: lenaic

Differential Revision: D66557919

fbshipit-source-id: 5428c32e5f0200a8e98568cabeedb0c61aafbe23
2024-11-28 06:06:59 -08:00
120 changed files with 3217 additions and 1203 deletions
+40
View File
@@ -0,0 +1,40 @@
/**
* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
*
* @flow strict
* @format
* @oncall react_native
*/
declare module 'jest-diff' {
import type {CompareKeys} from 'pretty-format';
declare export type DiffOptionsColor = (arg: string) => string; // subset of Chalk type
declare export type DiffOptions = {
aAnnotation?: string,
aColor?: DiffOptionsColor,
aIndicator?: string,
bAnnotation?: string,
bColor?: DiffOptionsColor,
bIndicator?: string,
changeColor?: DiffOptionsColor,
changeLineTrailingSpaceColor?: DiffOptionsColor,
commonColor?: DiffOptionsColor,
commonIndicator?: string,
commonLineTrailingSpaceColor?: DiffOptionsColor,
contextLines?: number,
emptyFirstOrLastLinePlaceholder?: string,
expand?: boolean,
includeChangeCounts?: boolean,
omitAnnotationLines?: boolean,
patchColor?: DiffOptionsColor,
compareKeys?: CompareKeys,
};
declare export function diff(
a: mixed,
b: mixed,
options?: DiffOptions,
): string | null;
}
+4 -1
View File
@@ -19,7 +19,6 @@ declare type Colors = {
tag: {close: string, open: string},
value: {close: string, open: string},
};
declare type CompareKeys = ((a: string, b: string) => number) | null | void;
declare type PrettyFormatPlugin =
| {
@@ -38,6 +37,10 @@ declare type PrettyFormatPlugin =
};
declare module 'pretty-format' {
declare export type CompareKeys =
| ((a: string, b: string) => number)
| null
| void;
declare export function format(
value: mixed,
options?: ?{
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+1
View File
@@ -25,4 +25,5 @@ module.exports = {
transformIgnorePatterns: ['.*'],
testRunner: './jest/integration/runner/index.js',
watchPathIgnorePatterns: ['<rootDir>/jest/integration/build/'],
globalSetup: './jest/integration/runner/warmup/index.js',
};
+40 -101
View File
@@ -12,25 +12,29 @@
import type {TestSuiteResult} from '../runtime/setup';
import entrypointTemplate from './entrypoint-template';
import {spawnSync} from 'child_process';
import crypto from 'crypto';
import {
getBuckModeForPlatform,
getDebugInfoFromCommandResult,
getFantomTestConfig,
getShortHash,
runBuck2,
symbolicateStackTrace,
} from './utils';
import fs from 'fs';
// $FlowExpectedError[untyped-import]
import {formatResultsErrors} from 'jest-message-util';
import Metro from 'metro';
import nullthrows from 'nullthrows';
import os from 'os';
import path from 'path';
const BUILD_OUTPUT_PATH = path.resolve(__dirname, '..', 'build');
const ENABLE_OPTIMIZED_MODE: false = false;
const PRINT_FANTOM_OUTPUT: false = false;
function parseRNTesterCommandResult(
commandArgs: $ReadOnlyArray<string>,
result: ReturnType<typeof spawnSync>,
): {logs: string, testResult: TestSuiteResult} {
function parseRNTesterCommandResult(result: ReturnType<typeof runBuck2>): {
logs: string,
testResult: TestSuiteResult,
} {
const stdout = result.stdout.toString();
const outputArray = stdout
@@ -46,51 +50,26 @@ function parseRNTesterCommandResult(
testResult = JSON.parse(nullthrows(testResultJSON));
} catch (error) {
throw new Error(
[
'Failed to parse test results from RN tester binary result. Full output:',
'buck2 ' + commandArgs.join(' '),
'stdout:',
stdout,
'stderr:',
result.stderr.toString(),
].join('\n'),
'Failed to parse test results from RN tester binary result.\n' +
getDebugInfoFromCommandResult(result),
);
}
return {logs: outputArray.join('\n'), testResult};
}
function getBuckModeForPlatform() {
const mode = ENABLE_OPTIMIZED_MODE ? 'opt' : 'dev';
switch (os.platform()) {
case 'linux':
return `@//arvr/mode/linux/${mode}`;
case 'darwin':
return os.arch() === 'arm64'
? `@//arvr/mode/mac-arm/${mode}`
: `@//arvr/mode/mac/${mode}`;
case 'win32':
return `@//arvr/mode/win/${mode}`;
default:
throw new Error(`Unsupported platform: ${os.platform()}`);
}
}
function getShortHash(contents: string): string {
return crypto.createHash('md5').update(contents).digest('hex').slice(0, 8);
}
function generateBytecodeBundle({
sourcePath,
bytecodePath,
isOptimizedMode,
}: {
sourcePath: string,
bytecodePath: string,
isOptimizedMode: boolean,
}): void {
const hermesCompilerCommandArgs = [
const hermesCompilerCommandResult = runBuck2([
'run',
getBuckModeForPlatform(),
getBuckModeForPlatform(isOptimizedMode),
'//xplat/hermes/tools/hermesc:hermesc',
'--',
'-emit-binary',
@@ -100,33 +79,10 @@ function generateBytecodeBundle({
'-out',
bytecodePath,
sourcePath,
];
const hermesCompilerCommandResult = spawnSync(
'buck2',
hermesCompilerCommandArgs,
{
encoding: 'utf8',
env: {
...process.env,
PATH: `/usr/local/bin:${process.env.PATH ?? ''}`,
},
},
);
]);
if (hermesCompilerCommandResult.status !== 0) {
throw new Error(
[
'Failed to run Hermes compiler. Full output:',
'buck2 ' + hermesCompilerCommandArgs.join(' '),
'stdout:',
hermesCompilerCommandResult.stdout,
'stderr:',
hermesCompilerCommandResult.stderr,
'error:',
hermesCompilerCommandResult.error,
].join('\n'),
);
throw new Error(getDebugInfoFromCommandResult(hermesCompilerCommandResult));
}
}
@@ -139,7 +95,9 @@ module.exports = async function runTest(
): mixed {
const startTime = Date.now();
const isOptimizedMode = ENABLE_OPTIMIZED_MODE;
const testConfig = getFantomTestConfig(testPath);
const isOptimizedMode = testConfig.mode === 'opt';
const metroConfig = await Metro.loadConfig({
config: path.resolve(__dirname, '..', 'config', 'metro.config.js'),
@@ -163,76 +121,54 @@ module.exports = async function runTest(
fs.mkdirSync(path.dirname(entrypointPath), {recursive: true});
fs.writeFileSync(entrypointPath, entrypointContents, 'utf8');
const sourceMapPath = path.join(
path.dirname(testJSBundlePath),
path.basename(testJSBundlePath, '.js') + '.map',
);
await Metro.runBuild(metroConfig, {
entry: entrypointPath,
out: testJSBundlePath,
platform: 'android',
minify: isOptimizedMode,
dev: !isOptimizedMode,
sourceMap: true,
sourceMapUrl: sourceMapPath,
});
if (isOptimizedMode) {
generateBytecodeBundle({
sourcePath: testJSBundlePath,
bytecodePath: testBytecodeBundlePath,
isOptimizedMode,
});
}
const rnTesterCommandArgs = [
const rnTesterCommandResult = runBuck2([
'run',
getBuckModeForPlatform(),
getBuckModeForPlatform(isOptimizedMode),
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
'--',
'--bundlePath',
testBundlePath,
];
const rnTesterCommandResult = spawnSync('buck2', rnTesterCommandArgs, {
encoding: 'utf8',
env: {
...process.env,
PATH: `/usr/local/bin:${process.env.PATH ?? ''}`,
},
});
]);
if (rnTesterCommandResult.status !== 0) {
throw new Error(
[
'Failed to run test in RN tester binary. Full output:',
'buck2 ' + rnTesterCommandArgs.join(' '),
'stdout:',
rnTesterCommandResult.stdout,
'stderr:',
rnTesterCommandResult.stderr,
'error:',
rnTesterCommandResult.error,
].join('\n'),
);
throw new Error(getDebugInfoFromCommandResult(rnTesterCommandResult));
}
if (PRINT_FANTOM_OUTPUT) {
console.log(
[
'RN tester binary. Full output:',
'buck2 ' + rnTesterCommandArgs.join(' '),
'stdout:',
rnTesterCommandResult.stdout,
'stderr:',
rnTesterCommandResult.stderr,
'error:',
rnTesterCommandResult.error,
].join('\n'),
);
console.log(getDebugInfoFromCommandResult(rnTesterCommandResult));
}
const rnTesterParsedOutput = parseRNTesterCommandResult(
rnTesterCommandArgs,
rnTesterCommandResult,
);
const testResultError = rnTesterParsedOutput.testResult.error;
if (testResultError) {
const error = new Error(testResultError.message);
error.stack = testResultError.stack;
error.stack = symbolicateStackTrace(sourceMapPath, testResultError.stack);
throw error;
}
@@ -248,6 +184,9 @@ module.exports = async function runTest(
failureDetails: [] as Array<string>,
testFilePath: testPath,
...testResult,
failureMessages: testResult.failureMessages.map(maybeStackTrace =>
symbolicateStackTrace(sourceMapPath, maybeStackTrace),
),
})) ?? [];
return {
+158
View File
@@ -0,0 +1,158 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import {spawnSync} from 'child_process';
import crypto from 'crypto';
import fs from 'fs';
// $FlowExpectedError[untyped-import]
import {extract, parse} from 'jest-docblock';
import os from 'os';
// $FlowExpectedError[untyped-import]
import {SourceMapConsumer} from 'source-map';
type DocblockPragmas = {[key: string]: string | string[]};
type FantomTestMode = 'dev' | 'opt';
type FantomTestConfig = {
mode: FantomTestMode,
};
const DEFAULT_MODE: FantomTestMode = 'dev';
/**
* Extracts the Fantom configuration from the test file, specified as part of
* the docblock comment. E.g.:
*
* ```
* /**
* * @flow strict-local
* * @fantom mode:opt
* *
* ```
*
* So far the only supported option is `mode`, which can be 'dev' or 'opt'.
*/
export function getFantomTestConfig(testPath: string): FantomTestConfig {
const docblock = extract(fs.readFileSync(testPath, 'utf8'));
const pragmas = parse(docblock) as DocblockPragmas;
const config = {
mode: DEFAULT_MODE,
};
const maybeMode = pragmas.fantom_mode;
if (maybeMode != null) {
if (Array.isArray(maybeMode)) {
throw new Error('Expected a single value for @fantom_mode');
}
const mode = maybeMode;
if (mode === 'dev' || mode === 'opt') {
config.mode = mode;
} else {
throw new Error(`Invalid Fantom mode: ${mode}`);
}
}
return config;
}
export function getBuckModeForPlatform(enableRelease: boolean = false): string {
const mode = enableRelease ? 'opt' : 'dev';
switch (os.platform()) {
case 'linux':
return `@//arvr/mode/linux/${mode}`;
case 'darwin':
return os.arch() === 'arm64'
? `@//arvr/mode/mac-arm/${mode}`
: `@//arvr/mode/mac/${mode}`;
case 'win32':
return `@//arvr/mode/win/${mode}`;
default:
throw new Error(`Unsupported platform: ${os.platform()}`);
}
}
type SpawnResultWithOriginalCommand = {
...ReturnType<typeof spawnSync>,
originalCommand: string,
...
};
export function runBuck2(args: Array<string>): SpawnResultWithOriginalCommand {
const result = spawnSync('buck2', args, {
encoding: 'utf8',
env: {
...process.env,
PATH: `/usr/local/bin:${process.env.PATH ?? ''}`,
},
});
return {
...result,
originalCommand: `buck2 ${args.join(' ')}`,
};
}
export function getDebugInfoFromCommandResult(
commandResult: SpawnResultWithOriginalCommand,
): string {
const logLines = [
`Command ${commandResult.status === 0 ? 'succeeded' : 'failed'}: ${commandResult.originalCommand}`,
'',
'stdout:',
commandResult.stdout,
'',
'stderr:',
commandResult.stderr,
];
if (commandResult.error) {
logLines.push('', 'error:', String(commandResult.error));
}
return logLines.join('\n');
}
export function getShortHash(contents: string): string {
return crypto.createHash('md5').update(contents).digest('hex').slice(0, 8);
}
export function symbolicateStackTrace(
sourceMapPath: string,
stackTrace: string,
): string {
const sourceMapData = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8'));
const consumer = new SourceMapConsumer(sourceMapData);
return stackTrace
.split('\n')
.map(line => {
const match = line.match(/at (.*) \((.*):(\d+):(\d+)\)/);
if (match) {
const functionName = match[1];
// const fileName = match[2];
const lineNumber = parseInt(match[3], 10);
const columnNumber = parseInt(match[4], 10);
// Get the original position
const originalPosition = consumer.originalPositionFor({
line: lineNumber,
column: columnNumber,
});
return `at ${originalPosition.name ?? functionName} (${originalPosition.source}:${originalPosition.line}:${originalPosition.column})`;
} else {
return line;
}
})
.join('\n');
}
+13
View File
@@ -0,0 +1,13 @@
/**
* 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
* @oncall react_native
*/
require('../../../../scripts/build/babel-register').registerForMonorepo();
module.exports = require('./warmup');
+97
View File
@@ -0,0 +1,97 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import {
getBuckModeForPlatform,
getDebugInfoFromCommandResult,
runBuck2,
} from '../utils';
// $FlowExpectedError[untyped-import]
import fs from 'fs';
import Metro from 'metro';
import os from 'os';
import path from 'path';
export default async function warmUp(
globalConfig: {...},
projectConfig: {...},
): Promise<void> {
try {
warmUpHermesCompiler();
warmUpRNTesterCLI();
await warmUpMetro();
} catch (e) {
// Sandcastle fails to parse the test output if we log stuff to stdout/stderr.
if ((process.env.SANDCASTLE ?? '') !== '') {
console.error(
'Global warmup failed. Tests will continue to run but will likely fail. Details:\n',
e,
);
}
}
}
async function warmUpMetro(): Promise<void> {
const metroConfig = await Metro.loadConfig({
config: path.resolve(__dirname, '..', '..', 'config', 'metro.config.js'),
});
const entrypointPath = path.resolve(
__dirname,
'..',
'..',
'runtime',
'WarmUpEntryPoint.js',
);
const bundlePath = path.join(
os.tmpdir(),
`fantom-warmup-bundle-${Date.now()}.js`,
);
await Metro.runBuild(metroConfig, {
entry: entrypointPath,
out: bundlePath,
platform: 'android',
minify: false,
dev: true,
});
try {
fs.unlinkSync(bundlePath);
} catch {}
}
function warmUpHermesCompiler(): void {
const buildHermesCompilerCommandResult = runBuck2([
'build',
getBuckModeForPlatform(),
'//xplat/hermes/tools/hermesc:hermesc',
]);
if (buildHermesCompilerCommandResult.status !== 0) {
throw new Error(
getDebugInfoFromCommandResult(buildHermesCompilerCommandResult),
);
}
}
function warmUpRNTesterCLI(): void {
const buildRNTesterCommandResult = runBuck2([
'build',
getBuckModeForPlatform(),
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
]);
if (buildRNTesterCommandResult.status !== 0) {
throw new Error(getDebugInfoFromCommandResult(buildRNTesterCommandResult));
}
}
@@ -0,0 +1,17 @@
/**
* 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
* @oncall react_native
*/
/**
* This is just an entrypoint to warm up the Metro cache before the tests run.
*/
import 'react-native/Libraries/Core/InitializeCore.js';
import 'react-native/src/private/__tests__/ReactNativeTester';
import './setup';
+46 -1
View File
@@ -194,7 +194,10 @@ class ErrorWithCustomBlame extends Error {
this.#cachedProcessedStack = originalStack;
} else {
const lines = originalStack.split('\n');
lines.splice(1, this.#ignoredFrameCount);
const index = lines.findIndex(line =>
/at (.*) \((.*):(\d+):(\d+)\)/.test(line),
);
lines.splice(index > -1 ? index : 1, this.#ignoredFrameCount);
this.#cachedProcessedStack = lines.join('\n');
}
}
@@ -307,6 +310,48 @@ class Expect {
}
}
toBeGreaterThanOrEqual(expected: number): void {
if (typeof this.#received !== 'number') {
throw new ErrorWithCustomBlame(
`Expected ${String(this.#received)} to be a number but it was a ${typeof this.#received}`,
).blameToPreviousFrame();
}
if (typeof expected !== 'number') {
throw new ErrorWithCustomBlame(
`Expected ${String(expected)} to be a number but it was a ${typeof expected}`,
).blameToPreviousFrame();
}
const pass = this.#received >= expected;
if (!this.#isExpectedResult(pass)) {
throw new ErrorWithCustomBlame(
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to be greater than or equal to ${expected}`,
).blameToPreviousFrame();
}
}
toBeLessThanOrEqual(expected: number): void {
if (typeof this.#received !== 'number') {
throw new ErrorWithCustomBlame(
`Expected ${String(this.#received)} to be a number but it was a ${typeof this.#received}`,
).blameToPreviousFrame();
}
if (typeof expected !== 'number') {
throw new ErrorWithCustomBlame(
`Expected ${String(expected)} to be a number but it was a ${typeof expected}`,
).blameToPreviousFrame();
}
const pass = this.#received <= expected;
if (!this.#isExpectedResult(pass)) {
throw new ErrorWithCustomBlame(
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to be less than or equal to ${expected}`,
).blameToPreviousFrame();
}
}
#isExpectedResult(pass: boolean): boolean {
return this.#isNot ? !pass : pass;
}
+1
View File
@@ -83,6 +83,7 @@
"hermes-transform": "0.25.1",
"inquirer": "^7.1.0",
"jest": "^29.6.3",
"jest-diff": "^29.7.0",
"jest-junit": "^10.0.0",
"jscodeshift": "^0.14.0",
"metro-babel-register": "^0.81.0",
+2 -2
View File
@@ -1,5 +1,5 @@
@generated SignedSource<<6b92b66e59525cef52902139f863f175>>
Git revision: b61aae3ccc6e2684dfbf1e2a06b0f985b459f11f
@generated SignedSource<<cecad43da3fd917e15322ee3efbed875>>
Git revision: 486803f6bf272e0629297265dee8048a2f1269dd
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebookexperimental/rn-chrome-devtools-frontend
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -42,7 +42,7 @@ import*as e from"../../ui/legacy/legacy.js";import*as t from"../../core/host/hos
<p>${d(a.docsDebuggingBasicsDetail)}</p>
</div>
</button>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#o.bind(this,"https://reactnative.dev/docs/react-devtools")} title=${d(a.docsReactNativeDevTools)}>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#o.bind(this,"https://reactnative.dev/docs/react-native-devtools")} title=${d(a.docsReactNativeDevTools)}>
<div class="rn-welcome-image" style="background-image: url('${c}')"></div>
<div>
<p class="devtools-link">${d(a.docsReactNativeDevTools)}</p>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -12,6 +12,7 @@ import com.facebook.react.utils.detectOSAwareHermesCommand
import com.facebook.react.utils.moveTo
import com.facebook.react.utils.windowsAwareCommandLine
import java.io.File
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.DirectoryProperty
@@ -19,6 +20,7 @@ import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
abstract class BundleHermesCTask : DefaultTask() {
@@ -26,6 +28,8 @@ abstract class BundleHermesCTask : DefaultTask() {
group = "react"
}
@get:Inject abstract val execOperations: ExecOperations
@get:Internal abstract val root: DirectoryProperty
@get:InputFiles
@@ -127,9 +131,9 @@ abstract class BundleHermesCTask : DefaultTask() {
File(jsIntermediateSourceMapsDir.get().asFile, "$bundleAssetName.compiler.map")
private fun runCommand(command: List<Any>) {
project.exec {
it.workingDir(root.get().asFile)
it.commandLine(command)
execOperations.exec { exec ->
exec.workingDir(root.get().asFile)
exec.commandLine(command)
}
}
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+1 -1
View File
@@ -327,7 +327,7 @@ export interface NativeModuleBooleanTypeAnnotation {
export type NativeModuleEnumMember = {
readonly name: string;
readonly value: string | number;
readonly value: NativeModuleStringLiteralTypeAnnotation | NativeModuleNumberLiteralTypeAnnotation,
};
export type NativeModuleEnumMemberType =
+1 -1
View File
@@ -311,7 +311,7 @@ export type NativeModuleNumberTypeAnnotation = $ReadOnly<{
export type NativeModuleEnumMember = {
name: string,
value: string | number,
value: StringLiteralTypeAnnotation | NumberLiteralTypeAnnotation,
};
export type NativeModuleEnumMemberType =
@@ -96,7 +96,7 @@ function combineSchemasInFileListAndWriteToFile(
exclude: ?RegExp,
): void {
const combined = combineSchemasInFileList(fileList, platform, exclude);
const formattedSchema = JSON.stringify(combined, null, 2);
const formattedSchema = JSON.stringify(combined);
fs.writeFileSync(outfile, formattedSchema);
}
@@ -84,4 +84,4 @@ for (const file of schemaFiles) {
}
}
fs.writeFileSync(output, JSON.stringify({modules}, null, 2));
fs.writeFileSync(output, JSON.stringify({modules}));
@@ -406,6 +406,14 @@ struct Bridging<${enumName}> {
};`;
};
function getMemberValueAppearance(member: NativeModuleEnumMember['value']) {
if (member.type === 'StringLiteralTypeAnnotation') {
return `"${member.value}"`;
} else {
return member.value;
}
}
function generateEnum(
hasteModuleName: string,
origEnumName: string,
@@ -417,9 +425,6 @@ function generateEnum(
const nativeEnumMemberType: NativeEnumMemberValueType =
memberType === 'StringTypeAnnotation' ? 'std::string' : 'int32_t';
const getMemberValueAppearance = (value: string | number) =>
memberType === 'StringTypeAnnotation' ? `"${value}"` : `${value}`;
const fromCases =
members
.map(
@@ -163,11 +163,17 @@ const SIMPLE_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'ONE',
value: '1',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1,
},
},
{
name: 'TWO',
value: '2',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 2,
},
},
],
},
@@ -178,15 +184,24 @@ const SIMPLE_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'POINT_ZERO',
value: '0.0',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0.0,
},
},
{
name: 'POINT_ONE',
value: '0.1',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0.1,
},
},
{
name: 'POINT_TWO',
value: '0.2',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0.2,
},
},
],
},
@@ -197,11 +212,17 @@ const SIMPLE_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'HELLO',
value: 'hello',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'hello',
},
},
{
name: 'GoodBye',
value: 'goodbye',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'goodbye',
},
},
],
},
@@ -1926,11 +1947,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'IA',
value: '23',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 23,
},
},
{
name: 'IB',
value: '42',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 42,
},
},
],
},
@@ -1941,11 +1968,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'FA',
value: '1.23',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1.23,
},
},
{
name: 'FB',
value: '4.56',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 4.56,
},
},
],
},
@@ -1956,11 +1989,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'NA',
value: 'NA',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'NA',
},
},
{
name: 'NB',
value: 'NB',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'NB',
},
},
],
},
@@ -1971,11 +2010,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
members: [
{
name: 'SA',
value: 's---a',
value: {
type: 'StringLiteralTypeAnnotation',
value: 's---a',
},
},
{
name: 'SB',
value: 's---b',
value: {
type: 'StringLiteralTypeAnnotation',
value: 's---b',
},
},
],
},
@@ -2047,7 +2047,7 @@ template <>
struct Bridging<NativeSampleTurboModuleFloatEnum> {
static NativeSampleTurboModuleFloatEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
double value = (double)rawValue.asNumber();
if (value == 0.0) {
if (value == 0) {
return NativeSampleTurboModuleFloatEnum::POINT_ZERO;
} else if (value == 0.1) {
return NativeSampleTurboModuleFloatEnum::POINT_ONE;
@@ -2060,7 +2060,7 @@ struct Bridging<NativeSampleTurboModuleFloatEnum> {
static jsi::Value toJs(jsi::Runtime &rt, NativeSampleTurboModuleFloatEnum value) {
if (value == NativeSampleTurboModuleFloatEnum::POINT_ZERO) {
return bridging::toJs(rt, 0.0);
return bridging::toJs(rt, 0);
} else if (value == NativeSampleTurboModuleFloatEnum::POINT_ONE) {
return bridging::toJs(rt, 0.1);
} else if (value == NativeSampleTurboModuleFloatEnum::POINT_TWO) {
@@ -478,11 +478,17 @@ describe('typeEnumResolution', () => {
members: [
{
name: 'Hello',
value: 'hello',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'hello',
},
},
{
name: 'Goodbye',
value: 'goodbye',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'goodbye',
},
},
],
},
@@ -521,11 +527,17 @@ describe('typeEnumResolution', () => {
members: [
{
name: 'On',
value: '1',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1,
},
},
{
name: 'Off',
value: '0',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0,
},
},
],
},
@@ -147,11 +147,17 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'SD',
'value': 'SD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'SD'
}
},
{
'name': 'HD',
'value': 'HD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'HD'
}
}
]
},
@@ -162,15 +168,24 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'Corrupted',
'value': -1
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': -1
}
},
{
'name': 'Low',
'value': 720
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 720
}
},
{
'name': 'High',
'value': 1080
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 1080
}
}
]
},
@@ -181,15 +196,24 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'One',
'value': 'one'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'one'
}
},
{
'name': 'Two',
'value': 'two'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'two'
}
},
{
'name': 'Three',
'value': 'three'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'three'
}
}
]
}
@@ -471,11 +495,17 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'SD',
'value': 'SD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'SD'
}
},
{
'name': 'HD',
'value': 'HD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'HD'
}
}
]
},
@@ -486,11 +516,17 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'Low',
'value': 720
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 720
}
},
{
'name': 'High',
'value': 1080
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 1080
}
}
]
},
@@ -501,15 +537,24 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`]
'members': [
{
'name': 'One',
'value': 'one'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'one'
}
},
{
'name': 'Two',
'value': 'two'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'two'
}
},
{
'name': 'Three',
'value': 'three'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'three'
}
}
]
}
+22 -4
View File
@@ -230,10 +230,28 @@ class FlowParser implements Parser {
parseEnumMembers(
typeAnnotation: $FlowFixMe,
): $ReadOnlyArray<NativeModuleEnumMember> {
return typeAnnotation.members.map(member => ({
name: member.id.name,
value: member.init?.value ?? member.id.name,
}));
return typeAnnotation.members.map(member => {
const value =
typeof member.init?.value === 'number'
? {
type: 'NumberLiteralTypeAnnotation',
value: member.init.value,
}
: typeof member.init?.value === 'string'
? {
type: 'StringLiteralTypeAnnotation',
value: member.init.value,
}
: {
type: 'StringLiteralTypeAnnotation',
value: member.id.name,
};
return {
name: member.id.name,
value: value,
};
});
}
isModuleInterface(node: $FlowFixMe): boolean {
+16 -4
View File
@@ -175,21 +175,33 @@ export class MockedParser implements Parser {
? [
{
name: 'Hello',
value: 'hello',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'hello',
},
},
{
name: 'Goodbye',
value: 'goodbye',
value: {
type: 'StringLiteralTypeAnnotation',
value: 'goodbye',
},
},
]
: [
{
name: 'On',
value: '1',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1,
},
},
{
name: 'Off',
value: '0',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0,
},
},
];
}
@@ -134,11 +134,17 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
'members': [
{
'name': 'SD',
'value': 'SD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'SD'
}
},
{
'name': 'HD',
'value': 'HD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'HD'
}
}
]
},
@@ -149,15 +155,24 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
'members': [
{
'name': 'Corrupted',
'value': -1
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': -1
}
},
{
'name': 'Low',
'value': 720
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 720
}
},
{
'name': 'High',
'value': 1080
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 1080
}
}
]
},
@@ -168,15 +183,24 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
'members': [
{
'name': 'One',
'value': 'one'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'one'
}
},
{
'name': 'Two',
'value': 'two'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'two'
}
},
{
'name': 'Three',
'value': 'three'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'three'
}
}
]
}
@@ -458,11 +482,17 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL
'members': [
{
'name': 'SD',
'value': 'SD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'SD'
}
},
{
'name': 'HD',
'value': 'HD'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'HD'
}
}
]
},
@@ -473,11 +503,17 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL
'members': [
{
'name': 'Low',
'value': 720
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 720
}
},
{
'name': 'High',
'value': 1080
'value': {
'type': 'NumberLiteralTypeAnnotation',
'value': 1080
}
}
]
},
@@ -488,15 +524,24 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL
'members': [
{
'name': 'One',
'value': 'one'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'one'
}
},
{
'name': 'Two',
'value': 'two'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'two'
}
},
{
'name': 'Three',
'value': 'three'
'value': {
'type': 'StringLiteralTypeAnnotation',
'value': 'three'
}
}
]
}
@@ -124,9 +124,27 @@ describe('TypeScript Module Parser', () => {
expect(parser).not.toThrow();
expect(parser().enumMap.MyEnum.members).toEqual([
{name: 'ZERO', value: 0},
{name: 'POSITIVE', value: 1},
{name: 'NEGATIVE', value: -1},
{
name: 'ZERO',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0,
},
},
{
name: 'POSITIVE',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1,
},
},
{
name: 'NEGATIVE',
value: {
type: 'NumberLiteralTypeAnnotation',
value: -1,
},
},
]);
});
@@ -147,8 +165,20 @@ describe('TypeScript Module Parser', () => {
expect(parser).not.toThrow();
expect(parser().enumMap.MyEnum.members).toEqual([
{name: 'ZERO', value: 0},
{name: 'POSITIVE', value: 1},
{
name: 'ZERO',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 0,
},
},
{
name: 'POSITIVE',
value: {
type: 'NumberLiteralTypeAnnotation',
value: 1,
},
},
]);
});
@@ -250,17 +250,30 @@ class TypeScriptParser implements Parser {
typeAnnotation: $FlowFixMe,
): $ReadOnlyArray<NativeModuleEnumMember> {
return typeAnnotation.members.map(member => {
// Handle negative values
if (member.initializer?.operator === '-') {
return {
name: member.id.name,
value: -member.initializer?.argument?.value ?? member.id.name,
};
}
const value =
member.initializer?.operator === '-'
? {
type: 'NumberLiteralTypeAnnotation',
value: -1 * member.initializer?.argument?.value,
}
: typeof member.initializer?.value === 'number'
? {
type: 'NumberLiteralTypeAnnotation',
value: member.initializer?.value,
}
: typeof member.initializer?.value === 'string'
? {
type: 'StringLiteralTypeAnnotation',
value: member.initializer?.value,
}
: {
type: 'StringLiteralTypeAnnotation',
value: member.id.name,
};
return {
name: member.id.name,
value: member.initializer?.value ?? member.id.name,
value,
};
});
}
@@ -106,17 +106,17 @@ export function allowTransformProp(prop: string): void {
}
export function isSupportedColorStyleProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_COLOR_STYLES, prop);
return SUPPORTED_COLOR_STYLES.hasOwnProperty(prop);
}
export function isSupportedInterpolationParam(param: string): boolean {
return Object.hasOwn(SUPPORTED_INTERPOLATION_PARAMS, param);
return SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(param);
}
export function isSupportedStyleProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_STYLES, prop);
return SUPPORTED_STYLES.hasOwnProperty(prop);
}
export function isSupportedTransformProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_TRANSFORMS, prop);
return SUPPORTED_TRANSFORMS.hasOwnProperty(prop);
}
@@ -37,7 +37,7 @@ function createAnimatedProps(
const key = keys[ii];
const value = inputProps[key];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
let node;
if (key === 'style') {
node = AnimatedStyle.from(value, allowlist?.style);
@@ -271,3 +271,11 @@ export default class AnimatedProps extends AnimatedNode {
};
}
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowIgnore[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
// $FlowIgnore[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
@@ -35,7 +35,7 @@ function createAnimatedStyle(
const key = keys[ii];
const value = inputStyle[key];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
let node;
if (value != null && key === 'transform') {
node = ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform()
@@ -241,3 +241,11 @@ export default class AnimatedStyle extends AnimatedWithChildren {
};
}
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowIgnore[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
// $FlowIgnore[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
+11 -3
View File
@@ -28,6 +28,15 @@ type FormDataPart =
...
};
/**
* Encode a FormData filename compliant with RFC 2183
*
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
*/
function encodeFilename(filename: string): string {
return encodeURIComponent(filename.replace(/\//g, '_'));
}
/**
* Polyfill for XMLHttpRequest2 FormData API, allowing multipart POST requests
* with mixed data (string, native files) to be submitted via XMLHttpRequest.
@@ -82,9 +91,8 @@ class FormData {
// content type (cf. web Blob interface.)
if (typeof value === 'object' && !Array.isArray(value) && value) {
if (typeof value.name === 'string') {
headers['content-disposition'] += `; filename="${
value.name
}"; filename*=utf-8''${encodeURI(value.name)}`;
headers['content-disposition'] +=
`; filename="${encodeFilename(value.name)}"`;
}
if (typeof value.type === 'string') {
headers['content-type'] = value.type;
@@ -48,8 +48,7 @@ describe('FormData', function () {
type: 'image/jpeg',
name: 'photo.jpg',
headers: {
'content-disposition':
'form-data; name="photo"; filename="photo.jpg"; filename*=utf-8\'\'photo.jpg',
'content-disposition': 'form-data; name="photo"; filename="photo.jpg"',
'content-type': 'image/jpeg',
},
fieldName: 'photo',
@@ -70,7 +69,7 @@ describe('FormData', function () {
name: '测试photo.jpg',
headers: {
'content-disposition':
'form-data; name="photo"; filename="测试photo.jpg"; filename*=utf-8\'\'%E6%B5%8B%E8%AF%95photo.jpg',
'form-data; name="photo"; filename="%E6%B5%8B%E8%AF%95photo.jpg"',
'content-type': 'image/jpeg',
},
fieldName: 'photo',
@@ -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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import setUpReactFabricPublicInstanceFantomTests from './setUpReactFabricPublicInstanceFantomTests';
setUpReactFabricPublicInstanceFantomTests({isModern: false});
@@ -0,0 +1,15 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import './setUpFeatureFlags';
import setUpReactFabricPublicInstanceFantomTests from './setUpReactFabricPublicInstanceFantomTests';
setUpReactFabricPublicInstanceFantomTests({isModern: true});
@@ -0,0 +1,16 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import * as ReactNativeFeatureFlags from '../../../../src/private/featureflags/ReactNativeFeatureFlags';
ReactNativeFeatureFlags.override({
enableAccessToHostTreeInFabric: () => true,
});
@@ -0,0 +1,365 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import '../../../Core/InitializeCore.js';
import * as ReactNativeTester from '../../../../src/private/__tests__/ReactNativeTester';
import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement';
import TextInputState from '../../../Components/TextInput/TextInputState';
import View from '../../../Components/View/View';
import ReactFabricHostComponent from '../ReactFabricHostComponent';
import nullthrows from 'nullthrows';
import * as React from 'react';
export default function setUpTests({isModern}: {isModern: boolean}) {
// TODO: move these tests to the test file for `ReactNativeElement` when the legacy implementation is removed.
describe(`ReactFabricPublicInstance (${isModern ? 'modern' : 'legacy'})`, () => {
it('should provide instances of the right class as refs in host components', () => {
let node;
const root = ReactNativeTester.createRoot();
ReactNativeTester.runTask(() => {
root.render(
<View
ref={receivedNode => {
node = receivedNode;
}}
/>,
);
});
expect(node).toBeInstanceOf(
isModern ? ReactNativeElement : ReactFabricHostComponent,
);
});
describe('blur', () => {
test('blur() invokes TextInputState', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
const blurTextInput = jest.fn();
// We don't support view commands in Fantom yet, so we have to mock this.
TextInputState.blurTextInput = blurTextInput;
ReactNativeTester.runTask(() => {
node.blur();
});
expect(blurTextInput).toHaveBeenCalledTimes(1);
expect(blurTextInput.mock.calls).toEqual([[node]]);
});
});
describe('focus', () => {
test('focus() invokes TextInputState', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
const focusTextInput = jest.fn();
// We don't support view commands in Fantom yet, so we have to mock this.
TextInputState.focusTextInput = focusTextInput;
ReactNativeTester.runTask(() => {
node.focus();
});
expect(focusTextInput).toHaveBeenCalledTimes(1);
expect(focusTextInput.mock.calls).toEqual([[node]]);
});
});
describe('measure', () => {
it('component.measure(...) invokes callback', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
const callback = jest.fn();
node.measure(callback);
expect(callback).toHaveBeenCalledTimes(1);
expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 10, 10]]);
});
it('unmounted.measure(...) does nothing', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
ReactNativeTester.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
node.measure(callback);
expect(callback).not.toHaveBeenCalled();
});
});
describe('measureInWindow', () => {
it('component.measureInWindow(...) invokes callback', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
const callback = jest.fn();
node.measureInWindow(callback);
expect(callback).toHaveBeenCalledTimes(1);
expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
});
it('unmounted.measureInWindow(...) does nothing', () => {
const root = ReactNativeTester.createRoot();
let maybeNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeNode = node;
}}
/>,
);
});
const node = nullthrows(maybeNode);
ReactNativeTester.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
node.measureInWindow(callback);
expect(callback).not.toHaveBeenCalled();
});
});
describe('measureLayout', () => {
it('component.measureLayout(component, ...) invokes callback', () => {
const root = ReactNativeTester.createRoot();
let maybeParentNode;
let maybeChildNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeParentNode = node;
}}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={node => {
maybeChildNode = node;
}}
/>
</View>,
);
});
const parentNode = nullthrows(maybeParentNode);
const childNode = nullthrows(maybeChildNode);
const callback = jest.fn();
childNode.measureLayout(parentNode, callback);
expect(callback).toHaveBeenCalledTimes(1);
expect(callback.mock.calls).toEqual([[20, 20, 10, 10]]);
});
it('unmounted.measureLayout(component, ...) does nothing', () => {
const root = ReactNativeTester.createRoot();
let maybeParentNode;
let maybeChildNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeParentNode = node;
}}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={node => {
maybeChildNode = node;
}}
/>
</View>,
);
});
const parentNode = nullthrows(maybeParentNode);
const childNode = nullthrows(maybeChildNode);
ReactNativeTester.runTask(() => {
root.render(
<View style={{width: 100, height: 100, left: 10, top: 10}} />,
);
});
const callback = jest.fn();
childNode.measureLayout(parentNode, callback);
expect(callback).not.toHaveBeenCalled();
});
it('component.measureLayout(unmounted, ...) does nothing', () => {
const root = ReactNativeTester.createRoot();
let maybeParentNode;
let maybeChildNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeParentNode = node;
}}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={node => {
maybeChildNode = node;
}}
/>
</View>,
);
});
const parentNode = nullthrows(maybeParentNode);
const childNode = nullthrows(maybeChildNode);
ReactNativeTester.runTask(() => {
root.render(
<View style={{width: 100, height: 100, left: 10, top: 10}} />,
);
});
const callback = jest.fn();
parentNode.measureLayout(childNode, callback);
expect(callback).not.toHaveBeenCalled();
});
it('unmounted.measureLayout(unmounted, ...) does nothing', () => {
const root = ReactNativeTester.createRoot();
let maybeParentNode;
let maybeChildNode;
ReactNativeTester.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={node => {
maybeParentNode = node;
}}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={node => {
maybeChildNode = node;
}}
/>
</View>,
);
});
const parentNode = nullthrows(maybeParentNode);
const childNode = nullthrows(maybeChildNode);
ReactNativeTester.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
childNode.measureLayout(parentNode, callback);
expect(callback).not.toHaveBeenCalled();
});
});
});
}
@@ -468,8 +468,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init);
- (dispatch_queue_t)methodQueue
{
if (_bridge.valid) {
id instance = self.instance;
RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@)", self, instance);
RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@)", self, self.instance);
}
return _methodQueue;
}
@@ -10,7 +10,7 @@
/**
* UIView class for root <ModalHostView> component.
*/
@interface RCTModalHostViewComponentView : RCTViewComponentView
@interface RCTModalHostViewComponentView : RCTViewComponentView <UIAdaptivePresentationControllerDelegate>
/**
* Subclasses may override this method and present the modal on different view controller.
@@ -149,6 +149,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
{
BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window;
if (shouldBePresented) {
self.viewController.presentationController.delegate = self;
_isPresented = YES;
[self presentViewController:self.viewController
animated:_shouldAnimatePresentation
@@ -274,6 +276,16 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
[childComponentView removeFromSuperview];
}
#pragma mark - UIAdaptivePresentationControllerDelegate
- (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller
{
auto eventEmitter = [self modalEventEmitter];
if (eventEmitter) {
eventEmitter->onRequestClose({});
}
}
@end
#ifdef __cplusplus
@@ -121,8 +121,8 @@ using namespace facebook::react;
auto &props = *sharedProps;
props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}};
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
.children({
@@ -136,8 +136,8 @@ using namespace facebook::react;
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(0));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
.children({
@@ -216,8 +216,8 @@ using namespace facebook::react;
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(30));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(50));
return sharedProps;
})
.children({
@@ -260,8 +260,8 @@ using namespace facebook::react;
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(50));
return sharedProps;
})
.children({
@@ -418,8 +418,8 @@ static ParagraphShadowNode::ConcreteState::Shared stateWithShadowNode(
auto &props = *sharedProps;
props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}};
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
.children({
@@ -434,8 +434,8 @@ static ParagraphShadowNode::ConcreteState::Shared stateWithShadowNode(
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(20));
return sharedProps;
})
.children({
@@ -87,6 +87,9 @@ CGFloat RCTCoreGraphicsFloatFromYogaValue(YGValue value, CGFloat baseFloatValue)
return RCTCoreGraphicsFloatFromYogaFloat(value.value) * baseFloatValue;
case YGUnitAuto:
case YGUnitUndefined:
case YGUnitMaxContent:
case YGUnitFitContent:
case YGUnitStretch:
return baseFloatValue;
}
}
@@ -63,6 +63,9 @@ typedef NS_ENUM(unsigned int, meta_prop_t) {
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
case YGUnitMaxContent: \
case YGUnitFitContent: \
case YGUnitStretch: \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
@@ -88,6 +91,35 @@ typedef NS_ENUM(unsigned int, meta_prop_t) {
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitMaxContent: \
case YGUnitFitContent: \
case YGUnitStretch: \
break; \
}
#define RCT_SET_YGVALUE_AUTO_INTRINSIC(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
setter##Auto(__VA_ARGS__); \
break; \
case YGUnitMaxContent: \
setter##MaxContent(__VA_ARGS__); \
break; \
case YGUnitFitContent: \
setter##FitContent(__VA_ARGS__); \
break; \
case YGUnitStretch: \
setter##Stretch(__VA_ARGS__); \
break; \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
}
static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
@@ -483,14 +515,14 @@ RCT_BORDER_PROPERTY(Start, START)
RCT_BORDER_PROPERTY(End, END)
// Dimensions
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
@@ -634,7 +666,7 @@ RCTShadowViewMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode,
- (void)setFlexBasis:(YGValue)value
{
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode);
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSetFlexBasis, _yogaNode);
}
- (YGValue)flexBasis
@@ -426,6 +426,10 @@ public abstract interface class com/facebook/react/ReactRootView$ReactRootViewEv
public abstract fun onAttachedToReactInstance (Lcom/facebook/react/ReactRootView;)V
}
public abstract class com/facebook/react/TurboReactPackage : com/facebook/react/BaseReactPackage {
public fun <init> ()V
}
public abstract interface class com/facebook/react/ViewManagerOnDemandReactPackage {
public abstract fun createViewManager (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/lang/String;)Lcom/facebook/react/uimanager/ViewManager;
public abstract fun getViewManagerNames (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/Collection;
@@ -776,16 +780,6 @@ public abstract class com/facebook/react/bridge/GuardedAsyncTask : android/os/As
protected abstract fun doInBackgroundGuarded ([Ljava/lang/Object;)V
}
public abstract class com/facebook/react/bridge/GuardedResultAsyncTask : android/os/AsyncTask {
protected fun <init> (Lcom/facebook/react/bridge/JSExceptionHandler;)V
protected fun <init> (Lcom/facebook/react/bridge/ReactContext;)V
protected synthetic fun doInBackground ([Ljava/lang/Object;)Ljava/lang/Object;
protected final fun doInBackground ([Ljava/lang/Void;)Ljava/lang/Object;
protected abstract fun doInBackgroundGuarded ()Ljava/lang/Object;
protected final fun onPostExecute (Ljava/lang/Object;)V
protected abstract fun onPostExecuteGuarded (Ljava/lang/Object;)V
}
public abstract class com/facebook/react/bridge/GuardedRunnable : java/lang/Runnable {
public fun <init> (Lcom/facebook/react/bridge/JSExceptionHandler;)V
public fun <init> (Lcom/facebook/react/bridge/ReactContext;)V
@@ -4091,10 +4085,6 @@ public abstract class com/facebook/react/uimanager/BaseViewManagerDelegate : com
public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V
}
public abstract interface class com/facebook/react/uimanager/ComponentNameResolver {
public abstract fun getComponentNames ()[Ljava/lang/String;
}
public final class com/facebook/react/uimanager/DisplayMetricsHolder {
public static final field INSTANCE Lcom/facebook/react/uimanager/DisplayMetricsHolder;
public static final fun getDisplayMetricsWritableMap (D)Lcom/facebook/react/bridge/WritableMap;
@@ -4106,22 +4096,6 @@ public final class com/facebook/react/uimanager/DisplayMetricsHolder {
public static final fun setWindowDisplayMetrics (Landroid/util/DisplayMetrics;)V
}
public class com/facebook/react/uimanager/FabricViewStateManager {
public fun <init> ()V
public fun getStateData ()Lcom/facebook/react/bridge/ReadableMap;
public fun hasStateWrapper ()Z
public fun setState (Lcom/facebook/react/uimanager/FabricViewStateManager$StateUpdateCallback;)V
public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V
}
public abstract interface class com/facebook/react/uimanager/FabricViewStateManager$HasFabricViewStateManager {
public abstract fun getFabricViewStateManager ()Lcom/facebook/react/uimanager/FabricViewStateManager;
}
public abstract interface class com/facebook/react/uimanager/FabricViewStateManager$StateUpdateCallback {
public abstract fun getStateUpdate ()Lcom/facebook/react/bridge/WritableMap;
}
public final class com/facebook/react/uimanager/FloatUtil {
public static final field INSTANCE Lcom/facebook/react/uimanager/FloatUtil;
public static final fun floatsEqual (FF)Z
@@ -7137,45 +7111,6 @@ public final class com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout
public fun setRefreshing (Z)V
}
public class com/facebook/react/views/switchview/ReactSwitchManager : com/facebook/react/uimanager/SimpleViewManager, com/facebook/react/viewmanagers/AndroidSwitchManagerInterface {
public static final field REACT_CLASS Ljava/lang/String;
public fun <init> ()V
protected synthetic fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)V
protected fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/switchview/ReactSwitch;)V
public fun createShadowNodeInstance ()Lcom/facebook/react/uimanager/LayoutShadowNode;
public synthetic fun createShadowNodeInstance ()Lcom/facebook/react/uimanager/ReactShadowNode;
protected synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View;
protected fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/switchview/ReactSwitch;
protected fun getDelegate ()Lcom/facebook/react/uimanager/ViewManagerDelegate;
public fun getName ()Ljava/lang/String;
public fun getShadowNodeClass ()Ljava/lang/Class;
public fun measure (Landroid/content/Context;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J
public synthetic fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
public fun receiveCommand (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
public synthetic fun setBackgroundColor (Landroid/view/View;I)V
public fun setBackgroundColor (Lcom/facebook/react/views/switchview/ReactSwitch;I)V
public synthetic fun setDisabled (Landroid/view/View;Z)V
public fun setDisabled (Lcom/facebook/react/views/switchview/ReactSwitch;Z)V
public synthetic fun setEnabled (Landroid/view/View;Z)V
public fun setEnabled (Lcom/facebook/react/views/switchview/ReactSwitch;Z)V
public synthetic fun setNativeValue (Landroid/view/View;Z)V
public fun setNativeValue (Lcom/facebook/react/views/switchview/ReactSwitch;Z)V
public synthetic fun setOn (Landroid/view/View;Z)V
public fun setOn (Lcom/facebook/react/views/switchview/ReactSwitch;Z)V
public synthetic fun setThumbColor (Landroid/view/View;Ljava/lang/Integer;)V
public fun setThumbColor (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/Integer;)V
public synthetic fun setThumbTintColor (Landroid/view/View;Ljava/lang/Integer;)V
public fun setThumbTintColor (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/Integer;)V
public synthetic fun setTrackColorForFalse (Landroid/view/View;Ljava/lang/Integer;)V
public fun setTrackColorForFalse (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/Integer;)V
public synthetic fun setTrackColorForTrue (Landroid/view/View;Ljava/lang/Integer;)V
public fun setTrackColorForTrue (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/Integer;)V
public synthetic fun setTrackTintColor (Landroid/view/View;Ljava/lang/Integer;)V
public fun setTrackTintColor (Lcom/facebook/react/views/switchview/ReactSwitch;Ljava/lang/Integer;)V
public synthetic fun setValue (Landroid/view/View;Z)V
public fun setValue (Lcom/facebook/react/views/switchview/ReactSwitch;Z)V
}
public final class com/facebook/react/views/text/DefaultStyleValuesUtil {
public static final field INSTANCE Lcom/facebook/react/views/text/DefaultStyleValuesUtil;
public static final fun getDefaultTextColor (Landroid/content/Context;)Landroid/content/res/ColorStateList;
@@ -591,7 +591,8 @@ android {
"src/main/res/shell",
"src/main/res/views/alert",
"src/main/res/views/modal",
"src/main/res/views/uimanager"))
"src/main/res/views/uimanager",
"src/main/res/views/view"))
java.exclude("com/facebook/react/processing")
java.exclude("com/facebook/react/module/processing")
}
@@ -0,0 +1,13 @@
/*
* 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
@Deprecated(
message = "Use BaseReactPackage instead",
replaceWith = ReplaceWith(expression = "BaseReactPackage"))
public abstract class TurboReactPackage : BaseReactPackage() {}
@@ -1,50 +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.AsyncTask;
/**
* Abstract base for a AsyncTask with result support that should have any RuntimeExceptions it
* throws handled by the {@link JSExceptionHandler} registered if the app is in dev mode.
*/
public abstract class GuardedResultAsyncTask<Result> extends AsyncTask<Void, Void, Result> {
private final JSExceptionHandler mExceptionHandler;
protected GuardedResultAsyncTask(ReactContext reactContext) {
this(reactContext.getExceptionHandler());
}
protected GuardedResultAsyncTask(JSExceptionHandler exceptionHandler) {
mExceptionHandler = exceptionHandler;
}
@Override
protected final Result doInBackground(Void... params) {
try {
return doInBackgroundGuarded();
} catch (RuntimeException e) {
mExceptionHandler.handleException(e);
throw e;
}
}
@Override
protected final void onPostExecute(Result result) {
try {
onPostExecuteGuarded(result);
} catch (RuntimeException e) {
mExceptionHandler.handleException(e);
}
}
protected abstract Result doInBackgroundGuarded();
protected abstract void onPostExecuteGuarded(Result result);
}
@@ -445,12 +445,18 @@ public class FabricUIManager
@Override
public void markActiveTouchForTag(int surfaceId, int reactTag) {
mMountingManager.getSurfaceManager(surfaceId).markActiveTouchForTag(reactTag);
SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
if (surfaceMountingManager != null) {
surfaceMountingManager.markActiveTouchForTag(reactTag);
}
}
@Override
public void sweepActiveTouchForTag(int surfaceId, int reactTag) {
mMountingManager.getSurfaceManager(surfaceId).sweepActiveTouchForTag(reactTag);
SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
if (surfaceMountingManager != null) {
surfaceMountingManager.sweepActiveTouchForTag(reactTag);
}
}
/**
@@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatDelegate
import com.facebook.fbreact.specs.NativeAppearanceSpec
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.module.annotations.ReactModule
/** Module that exposes the user's preferred color scheme. */
@@ -58,11 +59,13 @@ constructor(
}
public override fun setColorScheme(style: String) {
when (style) {
"dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
"light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"unspecified" ->
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
UiThreadUtil.runOnUiThread {
when (style) {
"dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
"light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"unspecified" ->
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
}
}
@@ -84,6 +84,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
view.setTag(R.id.accessibility_actions, null);
view.setTag(R.id.accessibility_value, null);
view.setTag(R.id.accessibility_state_expanded, null);
view.setTag(R.id.view_clipped, null);
// This indirectly calls (and resets):
// setTranslationX
@@ -5,13 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager;
package com.facebook.react.uimanager
import com.facebook.proguard.annotations.DoNotStripAny;
import com.facebook.proguard.annotations.DoNotStripAny
@DoNotStripAny
public interface ComponentNameResolver {
internal interface ComponentNameResolver {
/* returns a list of all the component names that are registered in React Native. */
String[] getComponentNames();
public val componentNames: Array<String>?
}
@@ -1,98 +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.uimanager;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
/**
* This is a helper base class for ViewGroups that use Fabric State.
*
* <p>Reason to use this: UpdateState calls from the View layer to the Fabric core can fail, and
* optionally Fabric will call a "failure callback" if that happens. This class abstracts that and
* makes it easier ensure that State in Fabric is always up-to-date.
*
* <p>1. Whenever ViewManager.updateState is called, call View.setStateWrapper. 2. Instead of
* calling StateWrapper.updateState directly, call View.setState and it will automatically keep
* retrying the UpdateState call until it succeeds; or you call setState again; or the View layer is
* updated with a newer StateWrapper.
*/
@Nullsafe(Nullsafe.Mode.LOCAL)
@Deprecated(
since =
"Deprecated class since v0.73.0, please use com.facebook.react.uimanager.StateWrapper"
+ " instead.",
forRemoval = true)
public class FabricViewStateManager {
private static final String TAG = "FabricViewStateManager";
@Deprecated
public interface HasFabricViewStateManager {
FabricViewStateManager getFabricViewStateManager();
}
@Deprecated
public interface StateUpdateCallback {
WritableMap getStateUpdate();
}
@Nullable private StateWrapper mStateWrapper = null;
@Deprecated
public void setStateWrapper(StateWrapper stateWrapper) {
mStateWrapper = stateWrapper;
}
@Deprecated
public boolean hasStateWrapper() {
return mStateWrapper != null;
}
private void setState(
@Nullable final StateWrapper stateWrapper,
final StateUpdateCallback stateUpdateCallback,
final int numTries) {
// The StateWrapper will change, breaking the async loop, whenever the UpdateState MountItem
// is executed.
// The caller is responsible for detecting if data is up-to-date, and doing nothing, or
// detecting if state is stale and calling setState again.
if (stateWrapper == null) {
FLog.e(TAG, "setState called without a StateWrapper");
return;
}
if (stateWrapper != mStateWrapper) {
return;
}
// We bail out after an arbitrary number of tries. In practice this should never go higher
// than 2 or 3, but there's nothing guaranteeing that.
if (numTries > 60) {
return;
}
@Nullable WritableMap stateUpdate = stateUpdateCallback.getStateUpdate();
if (stateUpdate == null) {
return;
}
// TODO: State update cannot fail; remove `failureRunnable` and custom retrying logic.
stateWrapper.updateState(stateUpdate);
}
@Deprecated
public void setState(final StateUpdateCallback stateUpdateCallback) {
setState(mStateWrapper, stateUpdateCallback, 0);
}
@Deprecated
public @Nullable ReadableMap getStateData() {
return mStateWrapper != null ? mStateWrapper.getStateData() : null;
}
}
@@ -5,10 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager.events;
package com.facebook.react.uimanager.events
public interface BatchEventDispatchedListener {
public fun interface BatchEventDispatchedListener {
/** Called after a batch of low priority events has been dispatched. */
void onBatchEventDispatched();
public fun onBatchEventDispatched()
}
@@ -1,232 +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.
*/
// switchview because switch is a keyword
package com.facebook.react.views.switchview;
import android.content.Context;
import android.view.View;
import android.widget.CompoundButton;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.AndroidSwitchManagerDelegate;
import com.facebook.react.viewmanagers.AndroidSwitchManagerInterface;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
/** View manager for {@link ReactSwitch} components. */
public class ReactSwitchManager extends SimpleViewManager<ReactSwitch>
implements AndroidSwitchManagerInterface<ReactSwitch> {
public static final String REACT_CLASS = "AndroidSwitch";
static class ReactSwitchShadowNode extends LayoutShadowNode implements YogaMeasureFunction {
private int mWidth;
private int mHeight;
private boolean mMeasured;
private ReactSwitchShadowNode() {
initMeasureFunction();
}
private void initMeasureFunction() {
setMeasureFunction(this);
}
@Override
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
if (!mMeasured) {
// Create a switch with the default config and measure it; since we don't (currently)
// support setting custom switch text, this is fine, as all switches will measure the same
// on a specific device/theme/locale combination.
ReactSwitch reactSwitch = new ReactSwitch(getThemedContext());
reactSwitch.setShowText(false);
final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
reactSwitch.measure(spec, spec);
mWidth = reactSwitch.getMeasuredWidth();
mHeight = reactSwitch.getMeasuredHeight();
mMeasured = true;
}
return YogaMeasureOutput.make(mWidth, mHeight);
}
}
private static final CompoundButton.OnCheckedChangeListener ON_CHECKED_CHANGE_LISTENER =
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
ReactContext reactContext = (ReactContext) buttonView.getContext();
int reactTag = buttonView.getId();
UIManagerHelper.getEventDispatcherForReactTag(reactContext, reactTag)
.dispatchEvent(
new ReactSwitchEvent(
UIManagerHelper.getSurfaceId(reactContext), reactTag, isChecked));
}
};
private final ViewManagerDelegate<ReactSwitch> mDelegate;
public ReactSwitchManager() {
mDelegate = new AndroidSwitchManagerDelegate<>(this);
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public LayoutShadowNode createShadowNodeInstance() {
return new ReactSwitchShadowNode();
}
@Override
public Class getShadowNodeClass() {
return ReactSwitchShadowNode.class;
}
@Override
protected ReactSwitch createViewInstance(ThemedReactContext context) {
ReactSwitch view = new ReactSwitch(context);
view.setShowText(false);
return view;
}
@Override
public void setBackgroundColor(ReactSwitch view, @ColorInt int backgroundColor) {
view.setBackgroundColor(backgroundColor);
}
@Override
@ReactProp(name = "disabled", defaultBoolean = false)
public void setDisabled(ReactSwitch view, boolean disabled) {
view.setEnabled(!disabled);
}
@Override
@ReactProp(name = ViewProps.ENABLED, defaultBoolean = true)
public void setEnabled(ReactSwitch view, boolean enabled) {
view.setEnabled(enabled);
}
@Override
@ReactProp(name = ViewProps.ON)
public void setOn(ReactSwitch view, boolean on) {
setValueInternal(view, on);
}
@Override
@ReactProp(name = "value")
public void setValue(ReactSwitch view, boolean value) {
setValueInternal(view, value);
}
@Override
@ReactProp(name = "thumbTintColor", customType = "Color")
public void setThumbTintColor(ReactSwitch view, @Nullable Integer color) {
this.setThumbColor(view, color);
}
@Override
@ReactProp(name = "thumbColor", customType = "Color")
public void setThumbColor(ReactSwitch view, @Nullable Integer color) {
view.setThumbColor(color);
}
@Override
@ReactProp(name = "trackColorForFalse", customType = "Color")
public void setTrackColorForFalse(ReactSwitch view, @Nullable Integer color) {
view.setTrackColorForFalse(color);
}
@Override
@ReactProp(name = "trackColorForTrue", customType = "Color")
public void setTrackColorForTrue(ReactSwitch view, @Nullable Integer color) {
view.setTrackColorForTrue(color);
}
@Override
@ReactProp(name = "trackTintColor", customType = "Color")
public void setTrackTintColor(ReactSwitch view, @Nullable Integer color) {
view.setTrackColor(color);
}
@Override
public void setNativeValue(ReactSwitch view, boolean value) {
setValueInternal(view, value);
}
@Override
public void receiveCommand(
@NonNull ReactSwitch view, String commandId, @Nullable ReadableArray args) {
switch (commandId) {
case "setNativeValue":
setValueInternal(view, args != null && args.getBoolean(0));
break;
}
}
@Override
protected void addEventEmitters(final ThemedReactContext reactContext, final ReactSwitch view) {
view.setOnCheckedChangeListener(ON_CHECKED_CHANGE_LISTENER);
}
@Override
protected ViewManagerDelegate<ReactSwitch> getDelegate() {
return mDelegate;
}
@Override
public long measure(
Context context,
ReadableMap localData,
ReadableMap props,
ReadableMap state,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode,
@Nullable float[] attachmentsPositions) {
ReactSwitch view = new ReactSwitch(context);
view.setShowText(false);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);
return YogaMeasureOutput.make(
PixelUtil.toDIPFromPixel(view.getMeasuredWidth()),
PixelUtil.toDIPFromPixel(view.getMeasuredHeight()));
}
private static void setValueInternal(ReactSwitch view, boolean value) {
// we set the checked change listener to null and then restore it so that we don't fire an
// onChange event to JS when JS itself is updating the value of the switch
view.setOnCheckedChangeListener(null);
view.setOn(value);
view.setOnCheckedChangeListener(ON_CHECKED_CHANGE_LISTENER);
}
}
@@ -0,0 +1,152 @@
/*
* 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.switchview
import android.content.Context
import android.view.View
import android.widget.CompoundButton
import androidx.annotation.ColorInt
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.BaseViewManager
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.uimanager.ViewProps
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.viewmanagers.AndroidSwitchManagerDelegate
import com.facebook.react.viewmanagers.AndroidSwitchManagerInterface
import com.facebook.yoga.YogaMeasureMode
import com.facebook.yoga.YogaMeasureOutput
internal class ReactSwitchManager :
BaseViewManager<ReactSwitch, ReactSwitchShadowNode>(),
AndroidSwitchManagerInterface<ReactSwitch> {
private val delegate: ViewManagerDelegate<ReactSwitch> = AndroidSwitchManagerDelegate(this)
override fun getName(): String = REACT_CLASS
override fun createShadowNodeInstance(): ReactSwitchShadowNode = ReactSwitchShadowNode()
override fun getShadowNodeClass(): Class<ReactSwitchShadowNode> =
ReactSwitchShadowNode::class.java
override fun createViewInstance(context: ThemedReactContext): ReactSwitch =
ReactSwitch(context).apply { showText = false }
override fun setBackgroundColor(view: ReactSwitch, @ColorInt backgroundColor: Int) {
view.setBackgroundColor(backgroundColor)
}
@ReactProp(name = "disabled", defaultBoolean = false)
override fun setDisabled(view: ReactSwitch, disabled: Boolean) {
view.isEnabled = !disabled
}
@ReactProp(name = ViewProps.ENABLED, defaultBoolean = true)
override fun setEnabled(view: ReactSwitch, enabled: Boolean) {
view.isEnabled = enabled
}
@ReactProp(name = ViewProps.ON)
override fun setOn(view: ReactSwitch, on: Boolean) {
setValueInternal(view, on)
}
@ReactProp(name = "value")
override fun setValue(view: ReactSwitch, value: Boolean) {
setValueInternal(view, value)
}
@ReactProp(name = "thumbTintColor", customType = "Color")
override fun setThumbTintColor(view: ReactSwitch, color: Int?) {
setThumbColor(view, color)
}
@ReactProp(name = "thumbColor", customType = "Color")
override fun setThumbColor(view: ReactSwitch, color: Int?) {
view.setThumbColor(color)
}
@ReactProp(name = "trackColorForFalse", customType = "Color")
override fun setTrackColorForFalse(view: ReactSwitch, color: Int?) {
view.setTrackColorForFalse(color)
}
@ReactProp(name = "trackColorForTrue", customType = "Color")
override fun setTrackColorForTrue(view: ReactSwitch, color: Int?) {
view.setTrackColorForTrue(color)
}
@ReactProp(name = "trackTintColor", customType = "Color")
override fun setTrackTintColor(view: ReactSwitch, color: Int?) {
view.setTrackColor(color)
}
override fun setNativeValue(view: ReactSwitch, value: Boolean) {
setValueInternal(view, value)
}
override fun receiveCommand(view: ReactSwitch, commandId: String, args: ReadableArray?) {
when (commandId) {
"setNativeValue" -> setValueInternal(view, args?.getBoolean(0) ?: false)
}
}
override fun addEventEmitters(reactContext: ThemedReactContext, view: ReactSwitch) {
view.setOnCheckedChangeListener(ON_CHECKED_CHANGE_LISTENER)
}
override fun updateExtraData(root: ReactSwitch, extraData: Any) {
// Do nothing
}
override fun getDelegate(): ViewManagerDelegate<ReactSwitch> = delegate
override fun measure(
context: Context,
localData: ReadableMap?,
props: ReadableMap?,
state: ReadableMap?,
width: Float,
widthMode: YogaMeasureMode,
height: Float,
heightMode: YogaMeasureMode,
attachmentsPositions: FloatArray?
): Long {
val view = ReactSwitch(context).apply { showText = false }
val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(measureSpec, measureSpec)
return YogaMeasureOutput.make(
PixelUtil.toDIPFromPixel(view.measuredWidth.toFloat()),
PixelUtil.toDIPFromPixel(view.measuredHeight.toFloat()))
}
private fun setValueInternal(view: ReactSwitch, value: Boolean) {
// Temporarily remove the listener to avoid triggering JS events
view.setOnCheckedChangeListener(null)
view.setOn(value)
view.setOnCheckedChangeListener(ON_CHECKED_CHANGE_LISTENER)
}
internal companion object {
public const val REACT_CLASS: String = "AndroidSwitch"
private val ON_CHECKED_CHANGE_LISTENER =
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
val reactContext = buttonView.context as ReactContext
val reactTag = buttonView.id
UIManagerHelper.getEventDispatcherForReactTag(reactContext, reactTag)
?.dispatchEvent(
ReactSwitchEvent(UIManagerHelper.getSurfaceId(reactContext), reactTag, isChecked))
}
}
}
@@ -0,0 +1,52 @@
/*
* 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.switchview
import android.view.View
import com.facebook.react.uimanager.LayoutShadowNode
import com.facebook.yoga.YogaMeasureFunction
import com.facebook.yoga.YogaMeasureMode
import com.facebook.yoga.YogaMeasureOutput
import com.facebook.yoga.YogaNode
internal class ReactSwitchShadowNode : LayoutShadowNode(), YogaMeasureFunction {
private var width = 0
private var height = 0
private var measured = false
init {
initMeasureFunction()
}
private fun initMeasureFunction() {
setMeasureFunction(this)
}
override fun measure(
node: YogaNode,
width: Float,
widthMode: YogaMeasureMode,
height: Float,
heightMode: YogaMeasureMode
): Long {
if (!measured) {
// Create a switch with the default config and measure it; since we don't (currently)
// support setting custom switch text, this is fine, as all switches will measure the same
// on a specific device/theme/locale combination.
val reactSwitch = ReactSwitch(themedContext)
reactSwitch.showText = false
val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
reactSwitch.measure(spec, spec)
this.width = reactSwitch.measuredWidth
this.height = reactSwitch.measuredHeight
measured = true
}
return YogaMeasureOutput.make(this.width, this.height)
}
}
@@ -125,6 +125,7 @@ public class ReactViewGroup extends ViewGroup
// {@link ViewGroup#getChildCount} so those methods may return views that are not attached.
// This is risky but allows us to perform a correct cleanup in {@link NativeViewHierarchyManager}.
private boolean mRemoveClippedSubviews;
private volatile boolean mInSubviewClippingLoop;
private @Nullable View[] mAllChildren;
private int mAllChildrenCount;
private @Nullable Rect mClippingRect;
@@ -158,6 +159,7 @@ public class ReactViewGroup extends ViewGroup
setClipChildren(false);
mRemoveClippedSubviews = false;
mInSubviewClippingLoop = false;
mAllChildren = null;
mAllChildrenCount = 0;
mClippingRect = null;
@@ -363,6 +365,7 @@ public class ReactViewGroup extends ViewGroup
View child = getChildAt(i);
mAllChildren[i] = child;
child.addOnLayoutChangeListener(mChildrenLayoutChangeListener);
setViewClipped(child, false);
}
updateClippingRect();
} else {
@@ -407,6 +410,7 @@ public class ReactViewGroup extends ViewGroup
private void updateClippingToRect(Rect clippingRect) {
Assertions.assertNotNull(mAllChildren);
mInSubviewClippingLoop = true;
int clippedSoFar = 0;
for (int i = 0; i < mAllChildrenCount; i++) {
try {
@@ -415,7 +419,7 @@ public class ReactViewGroup extends ViewGroup
int realClippedSoFar = 0;
Set<View> uniqueViews = new HashSet<>();
for (int j = 0; j < i; j++) {
realClippedSoFar += isViewClipped(mAllChildren[j]) ? 1 : 0;
realClippedSoFar += isViewClipped(mAllChildren[j], null) ? 1 : 0;
uniqueViews.add(mAllChildren[j]);
}
@@ -436,10 +440,11 @@ public class ReactViewGroup extends ViewGroup
+ uniqueViews.size(),
e);
}
if (isViewClipped(mAllChildren[i])) {
if (isViewClipped(mAllChildren[i], i)) {
clippedSoFar++;
}
}
mInSubviewClippingLoop = false;
}
private void updateSubviewClipStatus(Rect clippingRect, int idx, int clippedSoFar) {
@@ -458,14 +463,16 @@ public class ReactViewGroup extends ViewGroup
// it won't be size and located properly.
Animation animation = child.getAnimation();
boolean isAnimating = animation != null && !animation.hasEnded();
if (!intersects && !isViewClipped(child) && !isAnimating) {
if (!intersects && !isViewClipped(child, idx) && !isAnimating) {
setViewClipped(child, true);
// We can try saving on invalidate call here as the view that we remove is out of visible area
// therefore invalidation is not necessary.
removeViewInLayout(child);
needUpdateClippingRecursive = true;
} else if (intersects && isViewClipped(child)) {
} else if (intersects && isViewClipped(child, idx)) {
int adjustedIdx = idx - clippedSoFar;
Assertions.assertCondition(adjustedIdx >= 0);
setViewClipped(child, false);
addViewInLayout(child, adjustedIdx, sDefaultLayoutParam, true);
invalidate();
needUpdateClippingRecursive = true;
@@ -497,19 +504,21 @@ public class ReactViewGroup extends ViewGroup
subview.getLeft(), subview.getTop(), subview.getRight(), subview.getBottom());
// If it was intersecting before, should be attached to the parent
boolean oldIntersects = !isViewClipped(subview);
boolean oldIntersects = !isViewClipped(subview, null);
if (intersects != oldIntersects) {
mInSubviewClippingLoop = true;
int clippedSoFar = 0;
for (int i = 0; i < mAllChildrenCount; i++) {
if (mAllChildren[i] == subview) {
updateSubviewClipStatus(mClippingRect, i, clippedSoFar);
break;
}
if (isViewClipped(mAllChildren[i])) {
if (isViewClipped(mAllChildren[i], i)) {
clippedSoFar++;
}
}
mInSubviewClippingLoop = false;
}
}
@@ -541,7 +550,7 @@ public class ReactViewGroup extends ViewGroup
@Override
public void onViewAdded(View child) {
UiThreadUtil.assertOnUiThread();
checkViewClippingTag(child, Boolean.FALSE);
if (!customDrawOrderDisabled()) {
getDrawingOrderHelper().handleAddView(child);
setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder());
@@ -554,7 +563,7 @@ public class ReactViewGroup extends ViewGroup
@Override
public void onViewRemoved(View child) {
UiThreadUtil.assertOnUiThread();
checkViewClippingTag(child, Boolean.TRUE);
if (!customDrawOrderDisabled()) {
if (indexOfChild(child) == -1) {
return;
@@ -567,6 +576,21 @@ public class ReactViewGroup extends ViewGroup
super.onViewRemoved(child);
}
private void checkViewClippingTag(View child, Boolean expectedTag) {
if (mInSubviewClippingLoop) {
Object tag = child.getTag(R.id.view_clipped);
if (!expectedTag.equals(tag)) {
ReactSoftExceptionLogger.logSoftException(
"ReactViewGroup.onViewRemoved",
new ReactNoCrashSoftException(
"View clipping tag mismatch: tag=" + tag + " expected=" + expectedTag));
}
}
if (mRemoveClippedSubviews) {
child.setTag(R.id.view_clipped, expectedTag);
}
}
@Override
protected int getChildDrawingOrder(int childCount, int index) {
UiThreadUtil.assertOnUiThread();
@@ -638,19 +662,22 @@ public class ReactViewGroup extends ViewGroup
/*package*/ void addViewWithSubviewClippingEnabled(
final View child, int index, ViewGroup.LayoutParams params) {
Assertions.assertCondition(mRemoveClippedSubviews);
setViewClipped(child, true); // the view has not been added, so it is "clipped"
addInArray(child, index);
// we add view as "clipped" and then run {@link #updateSubviewClipStatus} to conditionally
// attach it
Rect clippingRect = Assertions.assertNotNull(mClippingRect);
View[] childArray = Assertions.assertNotNull(mAllChildren);
mInSubviewClippingLoop = true;
int clippedSoFar = 0;
for (int i = 0; i < index; i++) {
if (isViewClipped(childArray[i])) {
if (isViewClipped(childArray[i], i)) {
clippedSoFar++;
}
}
updateSubviewClipStatus(clippingRect, index, clippedSoFar);
mInSubviewClippingLoop = false;
child.addOnLayoutChangeListener(mChildrenLayoutChangeListener);
if (child instanceof ReactClippingProhibitedView) {
@@ -685,10 +712,10 @@ public class ReactViewGroup extends ViewGroup
View[] childArray = Assertions.assertNotNull(mAllChildren);
view.removeOnLayoutChangeListener(mChildrenLayoutChangeListener);
int index = indexOfChildInAllChildren(view);
if (!isViewClipped(childArray[index])) {
if (!isViewClipped(childArray[index], index)) {
int clippedSoFar = 0;
for (int i = 0; i < index; i++) {
if (isViewClipped(childArray[i])) {
if (isViewClipped(childArray[i], i)) {
clippedSoFar++;
}
}
@@ -709,9 +736,27 @@ public class ReactViewGroup extends ViewGroup
/**
* @return {@code true} if the view has been removed from the ViewGroup.
* @param index For logging - index of the view in {@code mAllChildren}, or {@code null} to skip
* logging.
*/
private boolean isViewClipped(View view) {
private boolean isViewClipped(View view, @Nullable Integer index) {
Object tag = view.getTag(R.id.view_clipped);
if (tag != null) {
return (boolean) tag;
}
ViewParent parent = view.getParent();
if (index != null) {
ReactSoftExceptionLogger.logSoftException(
"ReactViewGroup.isViewClipped",
new ReactNoCrashSoftException(
"View missing clipping tag: index="
+ index
+ " parentNull="
+ (parent == null)
+ " parentThis="
+ (parent == this)));
}
// fallback - parent *should* be null if the view was removed
if (parent == null) {
return true;
} else {
@@ -720,6 +765,10 @@ public class ReactViewGroup extends ViewGroup
}
}
private static void setViewClipped(View view, boolean clipped) {
view.setTag(R.id.view_clipped, clipped);
}
private int indexOfChildInAllChildren(View child) {
final int count = mAllChildrenCount;
final View[] childArray = Assertions.assertNotNull(mAllChildren);
@@ -74,6 +74,9 @@ public class YogaNative {
static native void jni_YGNodeStyleSetFlexBasisJNI(long nativePointer, float flexBasis);
static native void jni_YGNodeStyleSetFlexBasisPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetFlexBasisAutoJNI(long nativePointer);
static native void jni_YGNodeStyleSetFlexBasisMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetFlexBasisFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetFlexBasisStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetMarginJNI(long nativePointer, int edge);
static native void jni_YGNodeStyleSetMarginJNI(long nativePointer, int edge, float margin);
static native void jni_YGNodeStyleSetMarginPercentJNI(long nativePointer, int edge, float percent);
@@ -91,22 +94,40 @@ public class YogaNative {
static native void jni_YGNodeStyleSetWidthJNI(long nativePointer, float width);
static native void jni_YGNodeStyleSetWidthPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetWidthAutoJNI(long nativePointer);
static native void jni_YGNodeStyleSetWidthMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetWidthFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetWidthStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetHeightJNI(long nativePointer);
static native void jni_YGNodeStyleSetHeightJNI(long nativePointer, float height);
static native void jni_YGNodeStyleSetHeightPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetHeightAutoJNI(long nativePointer);
static native void jni_YGNodeStyleSetHeightMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetHeightFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetHeightStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetMinWidthJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinWidthJNI(long nativePointer, float minWidth);
static native void jni_YGNodeStyleSetMinWidthPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetMinWidthMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinWidthFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinWidthStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetMinHeightJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinHeightJNI(long nativePointer, float minHeight);
static native void jni_YGNodeStyleSetMinHeightPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetMinHeightMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinHeightFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMinHeightStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetMaxWidthJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxWidthJNI(long nativePointer, float maxWidth);
static native void jni_YGNodeStyleSetMaxWidthPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetMaxWidthMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxWidthFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxWidthStretchJNI(long nativePointer);
static native long jni_YGNodeStyleGetMaxHeightJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxHeightJNI(long nativePointer, float maxheight);
static native void jni_YGNodeStyleSetMaxHeightPercentJNI(long nativePointer, float percent);
static native void jni_YGNodeStyleSetMaxHeightMaxContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxHeightFitContentJNI(long nativePointer);
static native void jni_YGNodeStyleSetMaxHeightStretchJNI(long nativePointer);
static native float jni_YGNodeStyleGetAspectRatioJNI(long nativePointer);
static native void jni_YGNodeStyleSetAspectRatioJNI(long nativePointer, float aspectRatio);
static native float jni_YGNodeStyleGetGapJNI(long nativePointer, int gutter);
@@ -124,6 +124,12 @@ public abstract class YogaNode implements YogaProps {
public abstract void setFlexBasisAuto();
public abstract void setFlexBasisMaxContent();
public abstract void setFlexBasisFitContent();
public abstract void setFlexBasisStretch();
public abstract YogaValue getMargin(YogaEdge edge);
public abstract void setMargin(YogaEdge edge, float margin);
@@ -158,6 +164,12 @@ public abstract class YogaNode implements YogaProps {
public abstract void setWidthAuto();
public abstract void setWidthMaxContent();
public abstract void setWidthFitContent();
public abstract void setWidthStretch();
public abstract YogaValue getHeight();
public abstract void setHeight(float height);
@@ -166,30 +178,60 @@ public abstract class YogaNode implements YogaProps {
public abstract void setHeightAuto();
public abstract void setHeightMaxContent();
public abstract void setHeightFitContent();
public abstract void setHeightStretch();
public abstract YogaValue getMinWidth();
public abstract void setMinWidth(float minWidth);
public abstract void setMinWidthPercent(float percent);
public abstract void setMinWidthMaxContent();
public abstract void setMinWidthFitContent();
public abstract void setMinWidthStretch();
public abstract YogaValue getMinHeight();
public abstract void setMinHeight(float minHeight);
public abstract void setMinHeightPercent(float percent);
public abstract void setMinHeightMaxContent();
public abstract void setMinHeightFitContent();
public abstract void setMinHeightStretch();
public abstract YogaValue getMaxWidth();
public abstract void setMaxWidth(float maxWidth);
public abstract void setMaxWidthPercent(float percent);
public abstract void setMaxWidthMaxContent();
public abstract void setMaxWidthFitContent();
public abstract void setMaxWidthStretch();
public abstract YogaValue getMaxHeight();
public abstract void setMaxHeight(float maxheight);
public abstract void setMaxHeightPercent(float percent);
public abstract void setMaxHeightMaxContent();
public abstract void setMaxHeightFitContent();
public abstract void setMaxHeightStretch();
public abstract float getAspectRatio();
public abstract void setAspectRatio(float aspectRatio);
@@ -373,6 +373,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetFlexBasisAutoJNI(mNativePointer);
}
public void setFlexBasisMaxContent() {
YogaNative.jni_YGNodeStyleSetFlexBasisMaxContentJNI(mNativePointer);
}
public void setFlexBasisFitContent() {
YogaNative.jni_YGNodeStyleSetFlexBasisFitContentJNI(mNativePointer);
}
public void setFlexBasisStretch() {
YogaNative.jni_YGNodeStyleSetFlexBasisStretchJNI(mNativePointer);
}
public YogaValue getMargin(YogaEdge edge) {
return valueFromLong(YogaNative.jni_YGNodeStyleGetMarginJNI(mNativePointer, edge.intValue()));
}
@@ -441,6 +453,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetWidthAutoJNI(mNativePointer);
}
public void setWidthMaxContent() {
YogaNative.jni_YGNodeStyleSetWidthMaxContentJNI(mNativePointer);
}
public void setWidthFitContent() {
YogaNative.jni_YGNodeStyleSetWidthFitContentJNI(mNativePointer);
}
public void setWidthStretch() {
YogaNative.jni_YGNodeStyleSetWidthStretchJNI(mNativePointer);
}
public YogaValue getHeight() {
return valueFromLong(YogaNative.jni_YGNodeStyleGetHeightJNI(mNativePointer));
}
@@ -457,6 +481,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetHeightAutoJNI(mNativePointer);
}
public void setHeightMaxContent() {
YogaNative.jni_YGNodeStyleSetHeightMaxContentJNI(mNativePointer);
}
public void setHeightFitContent() {
YogaNative.jni_YGNodeStyleSetHeightFitContentJNI(mNativePointer);
}
public void setHeightStretch() {
YogaNative.jni_YGNodeStyleSetHeightStretchJNI(mNativePointer);
}
public YogaValue getMinWidth() {
return valueFromLong(YogaNative.jni_YGNodeStyleGetMinWidthJNI(mNativePointer));
}
@@ -469,6 +505,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetMinWidthPercentJNI(mNativePointer, percent);
}
public void setMinWidthMaxContent() {
YogaNative.jni_YGNodeStyleSetMinWidthMaxContentJNI(mNativePointer);
}
public void setMinWidthFitContent() {
YogaNative.jni_YGNodeStyleSetMinWidthFitContentJNI(mNativePointer);
}
public void setMinWidthStretch() {
YogaNative.jni_YGNodeStyleSetMinWidthStretchJNI(mNativePointer);
}
public YogaValue getMinHeight() {
return valueFromLong(YogaNative.jni_YGNodeStyleGetMinHeightJNI(mNativePointer));
}
@@ -481,6 +529,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetMinHeightPercentJNI(mNativePointer, percent);
}
public void setMinHeightMaxContent() {
YogaNative.jni_YGNodeStyleSetMinHeightMaxContentJNI(mNativePointer);
}
public void setMinHeightFitContent() {
YogaNative.jni_YGNodeStyleSetMinHeightFitContentJNI(mNativePointer);
}
public void setMinHeightStretch() {
YogaNative.jni_YGNodeStyleSetMinHeightStretchJNI(mNativePointer);
}
public YogaValue getMaxWidth() {
return valueFromLong(YogaNative.jni_YGNodeStyleGetMaxWidthJNI(mNativePointer));
}
@@ -493,6 +553,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetMaxWidthPercentJNI(mNativePointer, percent);
}
public void setMaxWidthMaxContent() {
YogaNative.jni_YGNodeStyleSetMaxWidthMaxContentJNI(mNativePointer);
}
public void setMaxWidthFitContent() {
YogaNative.jni_YGNodeStyleSetMaxWidthFitContentJNI(mNativePointer);
}
public void setMaxWidthStretch() {
YogaNative.jni_YGNodeStyleSetMaxWidthStretchJNI(mNativePointer);
}
public YogaValue getMaxHeight() {
return valueFromLong(YogaNative.jni_YGNodeStyleGetMaxHeightJNI(mNativePointer));
}
@@ -505,6 +577,18 @@ public abstract class YogaNodeJNIBase extends YogaNode implements Cloneable {
YogaNative.jni_YGNodeStyleSetMaxHeightPercentJNI(mNativePointer, percent);
}
public void setMaxHeightMaxContent() {
YogaNative.jni_YGNodeStyleSetMaxHeightMaxContentJNI(mNativePointer);
}
public void setMaxHeightFitContent() {
YogaNative.jni_YGNodeStyleSetMaxHeightFitContentJNI(mNativePointer);
}
public void setMaxHeightStretch() {
YogaNative.jni_YGNodeStyleSetMaxHeightStretchJNI(mNativePointer);
}
public float getAspectRatio() {
return YogaNative.jni_YGNodeStyleGetAspectRatioJNI(mNativePointer);
}
@@ -15,15 +15,33 @@ public interface YogaProps {
void setWidthPercent(float percent);
void setWidthAuto();
void setWidthMaxContent();
void setWidthFitContent();
void setWidthStretch();
void setMinWidth(float minWidth);
void setMinWidthPercent(float percent);
void setMinWidthMaxContent();
void setMinWidthFitContent();
void setMinWidthStretch();
void setMaxWidth(float maxWidth);
void setMaxWidthPercent(float percent);
void setWidthAuto();
void setMaxWidthMaxContent();
void setMaxWidthFitContent();
void setMaxWidthStretch();
/* Height properties */
@@ -31,15 +49,33 @@ public interface YogaProps {
void setHeightPercent(float percent);
void setHeightAuto();
void setHeightMaxContent();
void setHeightFitContent();
void setHeightStretch();
void setMinHeight(float minHeight);
void setMinHeightPercent(float percent);
void setMinHeightMaxContent();
void setMinHeightFitContent();
void setMinHeightStretch();
void setMaxHeight(float maxHeight);
void setMaxHeightPercent(float percent);
void setHeightAuto();
void setMaxHeightMaxContent();
void setMaxHeightFitContent();
void setMaxHeightStretch();
/* Margin properties */
@@ -81,6 +117,12 @@ public interface YogaProps {
void setFlexBasis(float flexBasis);
void setFlexBasisMaxContent();
void setFlexBasisFitContent();
void setFlexBasisStretch();
void setFlexDirection(YogaFlexDirection direction);
void setFlexGrow(float flexGrow);
@@ -13,7 +13,10 @@ public enum YogaUnit {
UNDEFINED(0),
POINT(1),
PERCENT(2),
AUTO(3);
AUTO(3),
MAX_CONTENT(4),
FIT_CONTENT(5),
STRETCH(6);
private final int mIntValue;
@@ -31,6 +34,9 @@ public enum YogaUnit {
case 1: return POINT;
case 2: return PERCENT;
case 3: return AUTO;
case 4: return MAX_CONTENT;
case 5: return FIT_CONTENT;
case 6: return STRETCH;
default: throw new IllegalArgumentException("Unknown enum value: " + value);
}
}
@@ -429,6 +429,28 @@ static void jni_YGNodeCopyStyleJNI(
YGNodeStyleSet##name##Auto(_jlong2YGNodeRef(nativePointer)); \
}
#define YG_NODE_JNI_STYLE_UNIT_PROP_AUTO_INTRINSIC(name) \
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO(name) \
YG_NODE_JNI_STYLE_UNIT_INTRINSIC(name)
#define YG_NODE_JNI_STYLE_UNIT_PROP_INTRINSIC(name) \
YG_NODE_JNI_STYLE_UNIT_PROP(name) \
YG_NODE_JNI_STYLE_UNIT_INTRINSIC(name)
#define YG_NODE_JNI_STYLE_UNIT_INTRINSIC(name) \
static void jni_YGNodeStyleSet##name##MaxContentJNI( \
JNIEnv* /*env*/, jobject /*obj*/, jlong nativePointer) { \
YGNodeStyleSet##name##MaxContent(_jlong2YGNodeRef(nativePointer)); \
} \
static void jni_YGNodeStyleSet##name##FitContentJNI( \
JNIEnv* /*env*/, jobject /*obj*/, jlong nativePointer) { \
YGNodeStyleSet##name##FitContent(_jlong2YGNodeRef(nativePointer)); \
} \
static void jni_YGNodeStyleSet##name##StretchJNI( \
JNIEnv* /*env*/, jobject /*obj*/, jlong nativePointer) { \
YGNodeStyleSet##name##Stretch(_jlong2YGNodeRef(nativePointer)); \
}
#define YG_NODE_JNI_STYLE_EDGE_UNIT_PROP(name) \
static jlong jni_YGNodeStyleGet##name##JNI( \
JNIEnv* /*env*/, jobject /*obj*/, jlong nativePointer, jint edge) { \
@@ -483,13 +505,13 @@ YG_NODE_JNI_STYLE_PROP(jfloat, float, Flex);
YG_NODE_JNI_STYLE_PROP(jfloat, float, FlexGrow);
YG_NODE_JNI_STYLE_PROP(jfloat, float, FlexShrink);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO(FlexBasis);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO(Width);
YG_NODE_JNI_STYLE_UNIT_PROP(MinWidth);
YG_NODE_JNI_STYLE_UNIT_PROP(MaxWidth);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO(Height);
YG_NODE_JNI_STYLE_UNIT_PROP(MinHeight);
YG_NODE_JNI_STYLE_UNIT_PROP(MaxHeight);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO_INTRINSIC(FlexBasis);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO_INTRINSIC(Width);
YG_NODE_JNI_STYLE_UNIT_PROP_INTRINSIC(MinWidth);
YG_NODE_JNI_STYLE_UNIT_PROP_INTRINSIC(MaxWidth);
YG_NODE_JNI_STYLE_UNIT_PROP_AUTO_INTRINSIC(Height);
YG_NODE_JNI_STYLE_UNIT_PROP_INTRINSIC(MinHeight);
YG_NODE_JNI_STYLE_UNIT_PROP_INTRINSIC(MaxHeight);
YG_NODE_JNI_STYLE_EDGE_UNIT_PROP_AUTO(Position);
@@ -870,6 +892,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetFlexBasisAutoJNI",
"(J)V",
(void*)jni_YGNodeStyleSetFlexBasisAutoJNI},
{"jni_YGNodeStyleSetFlexBasisMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetFlexBasisMaxContentJNI},
{"jni_YGNodeStyleSetFlexBasisFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetFlexBasisFitContentJNI},
{"jni_YGNodeStyleSetFlexBasisStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetFlexBasisStretchJNI},
{"jni_YGNodeStyleGetMarginJNI",
"(JI)J",
(void*)jni_YGNodeStyleGetMarginJNI},
@@ -917,6 +948,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetWidthAutoJNI",
"(J)V",
(void*)jni_YGNodeStyleSetWidthAutoJNI},
{"jni_YGNodeStyleSetWidthMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetWidthMaxContentJNI},
{"jni_YGNodeStyleSetWidthFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetWidthFitContentJNI},
{"jni_YGNodeStyleSetWidthStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetWidthStretchJNI},
{"jni_YGNodeStyleGetHeightJNI", "(J)J", (void*)jni_YGNodeStyleGetHeightJNI},
{"jni_YGNodeStyleSetHeightJNI",
"(JF)V",
@@ -927,6 +967,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetHeightAutoJNI",
"(J)V",
(void*)jni_YGNodeStyleSetHeightAutoJNI},
{"jni_YGNodeStyleSetHeightMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetHeightMaxContentJNI},
{"jni_YGNodeStyleSetHeightFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetHeightFitContentJNI},
{"jni_YGNodeStyleSetHeightStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetHeightStretchJNI},
{"jni_YGNodeStyleGetMinWidthJNI",
"(J)J",
(void*)jni_YGNodeStyleGetMinWidthJNI},
@@ -936,6 +985,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetMinWidthPercentJNI",
"(JF)V",
(void*)jni_YGNodeStyleSetMinWidthPercentJNI},
{"jni_YGNodeStyleSetMinWidthMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinWidthMaxContentJNI},
{"jni_YGNodeStyleSetMinWidthFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinWidthFitContentJNI},
{"jni_YGNodeStyleSetMinWidthStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinWidthStretchJNI},
{"jni_YGNodeStyleGetMinHeightJNI",
"(J)J",
(void*)jni_YGNodeStyleGetMinHeightJNI},
@@ -945,6 +1003,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetMinHeightPercentJNI",
"(JF)V",
(void*)jni_YGNodeStyleSetMinHeightPercentJNI},
{"jni_YGNodeStyleSetMinHeightMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinHeightMaxContentJNI},
{"jni_YGNodeStyleSetMinHeightFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinHeightFitContentJNI},
{"jni_YGNodeStyleSetMinHeightStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMinHeightStretchJNI},
{"jni_YGNodeStyleGetMaxWidthJNI",
"(J)J",
(void*)jni_YGNodeStyleGetMaxWidthJNI},
@@ -954,6 +1021,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetMaxWidthPercentJNI",
"(JF)V",
(void*)jni_YGNodeStyleSetMaxWidthPercentJNI},
{"jni_YGNodeStyleSetMaxWidthMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxWidthMaxContentJNI},
{"jni_YGNodeStyleSetMaxWidthFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxWidthFitContentJNI},
{"jni_YGNodeStyleSetMaxWidthStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxWidthStretchJNI},
{"jni_YGNodeStyleGetMaxHeightJNI",
"(J)J",
(void*)jni_YGNodeStyleGetMaxHeightJNI},
@@ -963,6 +1039,15 @@ static JNINativeMethod methods[] = {
{"jni_YGNodeStyleSetMaxHeightPercentJNI",
"(JF)V",
(void*)jni_YGNodeStyleSetMaxHeightPercentJNI},
{"jni_YGNodeStyleSetMaxHeightMaxContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxHeightMaxContentJNI},
{"jni_YGNodeStyleSetMaxHeightFitContentJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxHeightFitContentJNI},
{"jni_YGNodeStyleSetMaxHeightStretchJNI",
"(J)V",
(void*)jni_YGNodeStyleSetMaxHeightStretchJNI},
{"jni_YGNodeStyleGetAspectRatioJNI",
"(J)F",
(void*)jni_YGNodeStyleGetAspectRatioJNI},
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- tag used to store state of ReactViewGroup subview clipping -->
<item type="id" name="view_clipped"/>
</resources>
@@ -221,6 +221,13 @@ Pod::Spec.new do |s|
end
s.subspec "consistency" do |ss|
ss.dependency folly_dep_name, folly_version
ss.compiler_flags = folly_compiler_flags
ss.source_files = "react/renderer/consistency/**/*.{m,mm,cpp,h}"
ss.header_dir = "react/renderer/consistency"
end
s.subspec "uimanager" do |ss|
ss.subspec "consistency" do |sss|
sss.dependency folly_dep_name, folly_version
@@ -125,18 +125,14 @@ void HostAgent::handleRequest(const cdp::PreparsedRequest& req) {
shouldSendOKResponse = true;
isFinishedHandlingRequest = true;
} else if (req.method == "FuseboxClient.setClientMetadata") {
} else if (req.method == "ReactNativeApplication.enable") {
sessionState_.isReactNativeApplicationDomainEnabled = true;
fuseboxClientType_ = FuseboxClientType::Fusebox;
if (sessionState_.isLogDomainEnabled) {
sendFuseboxNotice();
}
shouldSendOKResponse = true;
isFinishedHandlingRequest = true;
} else if (req.method == "ReactNativeApplication.enable") {
sessionState_.isReactNativeApplicationDomainEnabled = true;
frontendChannel_(cdp::jsonNotification(
"ReactNativeApplication.metadataUpdated",
createHostMetadataPayload(hostMetadata_)));
@@ -348,21 +348,6 @@ TYPED_TEST(JsiIntegrationPortableTest, ExceptionDuringAddBindingIsIgnored) {
EXPECT_TRUE(this->eval("globalThis.foo === 42").getBool());
}
TYPED_TEST(JsiIntegrationPortableTest, FuseboxSetClientMetadata) {
this->connect();
this->expectMessageFromPage(JsonEq(R"({
"id": 1,
"result": {}
})"));
this->toPage_->sendMessage(R"({
"id": 1,
"method": "FuseboxClient.setClientMetadata",
"params": {}
})");
}
TYPED_TEST(JsiIntegrationPortableTest, ReactNativeApplicationEnable) {
this->connect();
@@ -45,10 +45,8 @@ namespace {
#endif
NO_DESTROY const std::string TRACK_PREFIX = "Track:";
NO_DESTROY const std::string DEFAULT_TRACK_NAME = "# Web Performance";
NO_DESTROY const std::string CUSTOM_TRACK_NAME_PREFIX = "# Web Performance: ";
std::tuple<std::string, std::string_view> parseTrackName(
std::tuple<std::optional<std::string>, std::string_view> parseTrackName(
const std::string& name) {
// Until there's a standard way to pass through track information, parse it
// manually, e.g., "Track:Foo:Event name"
@@ -58,16 +56,13 @@ std::tuple<std::string, std::string_view> parseTrackName(
if (name.starts_with(TRACK_PREFIX)) {
const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length());
if (trackNameDelimiter != std::string::npos) {
trackName = CUSTOM_TRACK_NAME_PREFIX +
name.substr(
TRACK_PREFIX.length(),
trackNameDelimiter - TRACK_PREFIX.length());
trackName = name.substr(
TRACK_PREFIX.length(), trackNameDelimiter - TRACK_PREFIX.length());
eventName = std::string_view(name).substr(trackNameDelimiter + 1);
}
}
auto& trackNameRef = trackName.has_value() ? *trackName : DEFAULT_TRACK_NAME;
return std::make_tuple(trackNameRef, eventName);
return std::make_tuple(trackName, eventName);
}
class PerformanceObserverWrapper : public jsi::NativeState {
@@ -41,16 +41,38 @@ bool ParagraphAttributes::operator!=(const ParagraphAttributes& rhs) const {
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
ParagraphAttributes paragraphAttributes{};
return {
debugStringConvertibleItem("maximumNumberOfLines", maximumNumberOfLines),
debugStringConvertibleItem("ellipsizeMode", ellipsizeMode),
debugStringConvertibleItem("textBreakStrategy", textBreakStrategy),
debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit),
debugStringConvertibleItem("minimumFontSize", minimumFontSize),
debugStringConvertibleItem("maximumFontSize", maximumFontSize),
debugStringConvertibleItem("includeFontPadding", includeFontPadding),
debugStringConvertibleItem(
"android_hyphenationFrequency", android_hyphenationFrequency)};
"maximumNumberOfLines",
maximumNumberOfLines,
paragraphAttributes.maximumNumberOfLines),
debugStringConvertibleItem(
"ellipsizeMode", ellipsizeMode, paragraphAttributes.ellipsizeMode),
debugStringConvertibleItem(
"textBreakStrategy",
textBreakStrategy,
paragraphAttributes.textBreakStrategy),
debugStringConvertibleItem(
"adjustsFontSizeToFit",
adjustsFontSizeToFit,
paragraphAttributes.adjustsFontSizeToFit),
debugStringConvertibleItem(
"minimumFontSize",
minimumFontSize,
paragraphAttributes.minimumFontSize),
debugStringConvertibleItem(
"maximumFontSize",
maximumFontSize,
paragraphAttributes.maximumFontSize),
debugStringConvertibleItem(
"includeFontPadding",
includeFontPadding,
paragraphAttributes.includeFontPadding),
debugStringConvertibleItem(
"android_hyphenationFrequency",
android_hyphenationFrequency,
paragraphAttributes.android_hyphenationFrequency)};
}
#endif
@@ -197,49 +197,97 @@ TextAttributes TextAttributes::defaultTextAttributes() {
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
const auto& textAttributes = TextAttributes::defaultTextAttributes();
return {
// Color
debugStringConvertibleItem("backgroundColor", backgroundColor),
debugStringConvertibleItem("foregroundColor", foregroundColor),
debugStringConvertibleItem("opacity", opacity),
debugStringConvertibleItem(
"backgroundColor", backgroundColor, textAttributes.backgroundColor),
debugStringConvertibleItem(
"foregroundColor", foregroundColor, textAttributes.foregroundColor),
debugStringConvertibleItem("opacity", opacity, textAttributes.opacity),
// Font
debugStringConvertibleItem("fontFamily", fontFamily),
debugStringConvertibleItem("fontSize", fontSize),
debugStringConvertibleItem("fontSizeMultiplier", fontSizeMultiplier),
debugStringConvertibleItem("fontWeight", fontWeight),
debugStringConvertibleItem("fontStyle", fontStyle),
debugStringConvertibleItem("fontVariant", fontVariant),
debugStringConvertibleItem("allowFontScaling", allowFontScaling),
debugStringConvertibleItem("dynamicTypeRamp", dynamicTypeRamp),
debugStringConvertibleItem("letterSpacing", letterSpacing),
debugStringConvertibleItem(
"fontFamily", fontFamily, textAttributes.fontFamily),
debugStringConvertibleItem("fontSize", fontSize, textAttributes.fontSize),
debugStringConvertibleItem(
"fontSizeMultiplier",
fontSizeMultiplier,
textAttributes.fontSizeMultiplier),
debugStringConvertibleItem(
"fontWeight", fontWeight, textAttributes.fontWeight),
debugStringConvertibleItem(
"fontStyle", fontStyle, textAttributes.fontStyle),
debugStringConvertibleItem(
"fontVariant", fontVariant, textAttributes.fontVariant),
debugStringConvertibleItem(
"allowFontScaling",
allowFontScaling,
textAttributes.allowFontScaling),
debugStringConvertibleItem(
"dynamicTypeRamp", dynamicTypeRamp, textAttributes.dynamicTypeRamp),
debugStringConvertibleItem(
"letterSpacing", letterSpacing, textAttributes.letterSpacing),
// Paragraph Styles
debugStringConvertibleItem("lineHeight", lineHeight),
debugStringConvertibleItem("alignment", alignment),
debugStringConvertibleItem("baseWritingDirection", baseWritingDirection),
debugStringConvertibleItem("lineBreakStrategyIOS", lineBreakStrategy),
debugStringConvertibleItem("lineBreakModeIOS", lineBreakMode),
debugStringConvertibleItem(
"lineHeight", lineHeight, textAttributes.lineHeight),
debugStringConvertibleItem(
"alignment", alignment, textAttributes.alignment),
debugStringConvertibleItem(
"baseWritingDirection",
baseWritingDirection,
textAttributes.baseWritingDirection),
debugStringConvertibleItem(
"lineBreakStrategyIOS",
lineBreakStrategy,
textAttributes.lineBreakStrategy),
debugStringConvertibleItem(
"lineBreakModeIOS", lineBreakMode, textAttributes.lineBreakMode),
// Decoration
debugStringConvertibleItem("textDecorationColor", textDecorationColor),
debugStringConvertibleItem(
"textDecorationLineType", textDecorationLineType),
debugStringConvertibleItem("textDecorationStyle", textDecorationStyle),
"textDecorationColor",
textDecorationColor,
textAttributes.textDecorationColor),
debugStringConvertibleItem(
"textDecorationLineType",
textDecorationLineType,
textAttributes.textDecorationLineType),
debugStringConvertibleItem(
"textDecorationStyle",
textDecorationStyle,
textAttributes.textDecorationStyle),
// Shadow
debugStringConvertibleItem("textShadowOffset", textShadowOffset),
debugStringConvertibleItem("textShadowRadius", textShadowRadius),
debugStringConvertibleItem("textShadowColor", textShadowColor),
debugStringConvertibleItem(
"textShadowOffset",
textShadowOffset,
textAttributes.textShadowOffset),
debugStringConvertibleItem(
"textShadowRadius",
textShadowRadius,
textAttributes.textShadowRadius),
debugStringConvertibleItem(
"textShadowColor", textShadowColor, textAttributes.textShadowColor),
// Special
debugStringConvertibleItem("isHighlighted", isHighlighted),
debugStringConvertibleItem("isPressable", isPressable),
debugStringConvertibleItem("layoutDirection", layoutDirection),
debugStringConvertibleItem("accessibilityRole", accessibilityRole),
debugStringConvertibleItem("role", role),
debugStringConvertibleItem(
"isHighlighted", isHighlighted, textAttributes.isHighlighted),
debugStringConvertibleItem(
"isPressable", isPressable, textAttributes.isPressable),
debugStringConvertibleItem(
"layoutDirection", layoutDirection, textAttributes.layoutDirection),
debugStringConvertibleItem(
"accessibilityRole",
accessibilityRole,
textAttributes.accessibilityRole),
debugStringConvertibleItem("role", role, textAttributes.role),
debugStringConvertibleItem("textAlignVertical", textAlignVertical),
debugStringConvertibleItem(
"textAlignVertical",
textAlignVertical,
textAttributes.textAlignVertical),
};
}
#endif
@@ -30,7 +30,24 @@ void ImageShadowNode::updateStateIfNeeded() {
auto newImageSource = getImageSource();
const auto& oldImageRequestParams = savedState.getImageRequestParams();
const auto& imageProps = getConcreteProps();
const auto& newImageRequestParams = ImageRequestParams(imageProps.blurRadius);
const auto& newImageRequestParams = ImageRequestParams(
imageProps.blurRadius
#ifdef ANDROID
,
imageProps.defaultSource,
imageProps.resizeMode,
imageProps.resizeMethod,
// TODO: should we resizeMultiplier * imageSource.scale ?
imageProps.resizeMultiplier,
imageProps.shouldNotify,
imageProps.overlayColor,
imageProps.tintColor,
imageProps.fadeDuration,
imageProps.progressiveRenderingEnabled,
imageProps.loadingIndicatorSource,
imageProps.internal_analyticTag
#endif
);
if (oldImageSource == newImageSource &&
oldImageRequestParams == newImageRequestParams) {
@@ -538,9 +538,9 @@ void YogaLayoutableShadowNode::setSize(Size size) const {
auto style = yogaNode_.style();
style.setDimension(
yoga::Dimension::Width, yoga::StyleLength::points(size.width));
yoga::Dimension::Width, yoga::StyleSizeLength::points(size.width));
style.setDimension(
yoga::Dimension::Height, yoga::StyleLength::points(size.height));
yoga::Dimension::Height, yoga::StyleSizeLength::points(size.height));
yogaNode_.setStyle(style);
yogaNode_.setDirty(true);
}
@@ -631,16 +631,18 @@ void YogaLayoutableShadowNode::layoutTree(
auto ownerHeight = yogaFloatFromFloat(maximumSize.height);
yogaStyle.setMaxDimension(
yoga::Dimension::Width, yoga::StyleLength::points(maximumSize.width));
yoga::Dimension::Width, yoga::StyleSizeLength::points(maximumSize.width));
yogaStyle.setMaxDimension(
yoga::Dimension::Height, yoga::StyleLength::points(maximumSize.height));
yoga::Dimension::Height,
yoga::StyleSizeLength::points(maximumSize.height));
yogaStyle.setMinDimension(
yoga::Dimension::Width, yoga::StyleLength::points(minimumSize.width));
yoga::Dimension::Width, yoga::StyleSizeLength::points(minimumSize.width));
yogaStyle.setMinDimension(
yoga::Dimension::Height, yoga::StyleLength::points(minimumSize.height));
yoga::Dimension::Height,
yoga::StyleSizeLength::points(minimumSize.height));
auto direction =
yogaDirectionFromLayoutDirection(layoutConstraints.layoutDirection);
@@ -93,18 +93,15 @@ inline yoga::FloatOptional yogaOptionalFloatFromFloat(Float value) {
inline std::optional<Float> optionalFloatFromYogaValue(
const yoga::Style::Length& length,
std::optional<Float> base = {}) {
switch (length.unit()) {
case yoga::Unit::Undefined:
return {};
case yoga::Unit::Point:
return floatFromYogaOptionalFloat(length.value());
case yoga::Unit::Percent:
return base.has_value()
? std::optional<Float>(
base.value() * floatFromYogaOptionalFloat(length.value()))
: std::optional<Float>();
case yoga::Unit::Auto:
return {};
if (length.isPoints()) {
return floatFromYogaOptionalFloat(length.value());
} else if (length.isPercent()) {
return base.has_value()
? std::optional<Float>(
base.value() * floatFromYogaOptionalFloat(length.value()))
: std::optional<Float>();
} else {
return {};
}
}
@@ -447,6 +444,47 @@ inline void fromRawValue(
LOG(ERROR) << "Could not parse yoga::Display: " << stringValue;
}
inline void fromRawValue(
const PropsParserContext& /*context*/,
const RawValue& value,
yoga::Style::SizeLength& result) {
if (value.hasType<Float>()) {
result = yoga::StyleSizeLength::points((float)value);
return;
} else if (value.hasType<std::string>()) {
const auto stringValue = (std::string)value;
if (stringValue == "auto") {
result = yoga::StyleSizeLength::ofAuto();
return;
} else if (stringValue == "max-content") {
result = yoga::StyleSizeLength::ofMaxContent();
return;
} else if (stringValue == "stretch") {
result = yoga::StyleSizeLength::ofStretch();
return;
} else if (stringValue == "fit-content") {
result = yoga::StyleSizeLength::ofFitContent();
return;
} else {
if (stringValue.back() == '%') {
auto tryValue = folly::tryTo<float>(
std::string_view(stringValue).substr(0, stringValue.length() - 1));
if (tryValue.hasValue()) {
result = yoga::StyleSizeLength::percent(tryValue.value());
return;
}
} else {
auto tryValue = folly::tryTo<float>(stringValue);
if (tryValue.hasValue()) {
result = yoga::StyleSizeLength::points(tryValue.value());
return;
}
}
}
}
result = yoga::StyleSizeLength::undefined();
}
inline void fromRawValue(
const PropsParserContext& context,
const RawValue& value,
@@ -1376,15 +1414,36 @@ inline std::string toString(const yoga::Display& value) {
}
inline std::string toString(const yoga::Style::Length& length) {
switch (length.unit()) {
case yoga::Unit::Undefined:
return "undefined";
case yoga::Unit::Point:
return std::to_string(length.value().unwrap());
case yoga::Unit::Percent:
return std::to_string(length.value().unwrap()) + "%";
case yoga::Unit::Auto:
return "auto";
if (length.isUndefined()) {
return "undefined";
} else if (length.isAuto()) {
return "auto";
} else if (length.isPoints()) {
return std::to_string(length.value().unwrap());
} else if (length.isPercent()) {
return std::to_string(length.value().unwrap()) + "%";
} else {
return "unknown";
}
}
inline std::string toString(const yoga::Style::SizeLength& length) {
if (length.isUndefined()) {
return "undefined";
} else if (length.isAuto()) {
return "auto";
} else if (length.isPoints()) {
return std::to_string(length.value().unwrap());
} else if (length.isPercent()) {
return std::to_string(length.value().unwrap()) + "%";
} else if (length.isMaxContent()) {
return "max-content";
} else if (length.isFitContent()) {
return "fit-content";
} else if (length.isStretch()) {
return "stretch";
} else {
return "unknown";
}
}
@@ -77,8 +77,8 @@ class LayoutTest : public ::testing::Test {
auto &props = *sharedProps;
props.layoutConstraints = LayoutConstraints{{0,0}, {500, 500}};
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
.children({
@@ -90,8 +90,8 @@ class LayoutTest : public ::testing::Test {
auto &props = *sharedProps;
auto &yogaStyle = props.yogaStyle;
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(50));
return sharedProps;
})
.children({
@@ -105,8 +105,8 @@ class LayoutTest : public ::testing::Test {
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(10));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(10));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(30));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(90));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(30));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(90));
if (testCase == TRANSFORM_SCALE) {
props.transform = props.transform * Transform::Scale(2, 2, 1);
@@ -138,8 +138,8 @@ class LayoutTest : public ::testing::Test {
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(10));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(10));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(110));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(110));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(20));
return sharedProps;
})
.children({
@@ -153,8 +153,8 @@ class LayoutTest : public ::testing::Test {
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(70));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(-50));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(30));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(60));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(30));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(60));
return sharedProps;
})
}),
@@ -168,8 +168,8 @@ class LayoutTest : public ::testing::Test {
yogaStyle.setPositionType(yoga::PositionType::Absolute);
yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(-60));
yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(50));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(70));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(70));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(20));
return sharedProps;
})
})
@@ -15,14 +15,29 @@
#include <react/renderer/debug/DebugStringConvertible.h>
#include <react/renderer/debug/DebugStringConvertibleItem.h>
#include <react/utils/FloatComparison.h>
namespace facebook::react {
#if RN_DEBUG_STRING_CONVERTIBLE
inline SharedDebugStringConvertible debugStringConvertibleItem(
const std::string& name,
float value,
float defaultValue = {}) {
if (floatEquality(value, defaultValue)) {
return nullptr;
}
return std::make_shared<DebugStringConvertibleItem>(
name, facebook::react::toString(value));
}
template <typename T>
inline SharedDebugStringConvertible
debugStringConvertibleItem(std::string name, T value, T defaultValue = {}) {
inline SharedDebugStringConvertible debugStringConvertibleItem(
const std::string& name,
T value,
T defaultValue = {}) {
if (value == defaultValue) {
return nullptr;
}
@@ -33,7 +48,7 @@ debugStringConvertibleItem(std::string name, T value, T defaultValue = {}) {
template <typename T>
inline SharedDebugStringConvertible debugStringConvertibleItem(
std::string name,
const std::string& name,
std::optional<T> value,
T defaultValue = {}) {
if (!value.has_value()) {
@@ -54,7 +69,7 @@ inline SharedDebugStringConvertibleList operator+(
}
inline SharedDebugStringConvertible debugStringConvertibleItem(
std::string name,
const std::string& name,
DebugStringConvertible value,
std::string defaultValue) {
return debugStringConvertibleItem(
@@ -16,7 +16,7 @@ add_compile_options(
file(GLOB react_render_imagemanager_SRC CONFIGURE_DEPENDS
*.cpp
platform/cxx/react/renderer/imagemanager/*.cpp)
platform/android/react/renderer/imagemanager/*.cpp)
add_library(react_render_imagemanager
OBJECT
@@ -25,7 +25,7 @@ add_library(react_render_imagemanager
target_include_directories(react_render_imagemanager
PUBLIC
${REACT_COMMON_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
@@ -1,30 +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
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
class ImageRequestParams {
public:
ImageRequestParams() = default;
ImageRequestParams(Float blurRadius) : blurRadius(blurRadius) {}
Float blurRadius{};
bool operator==(const ImageRequestParams& rhs) const {
return this->blurRadius == rhs.blurRadius;
}
bool operator!=(const ImageRequestParams& rhs) const {
return !(*this == rhs);
}
};
} // namespace facebook::react
@@ -0,0 +1,94 @@
/*
* 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 <utility>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/imagemanager/primitives.h>
namespace facebook::react {
class ImageRequestParams {
public:
ImageRequestParams() = default;
ImageRequestParams(
Float blurRadius,
ImageSource defaultSource,
ImageResizeMode resizeMode,
std::string resizeMethod,
Float resizeMultiplier,
bool shouldNotify,
SharedColor overlayColor,
SharedColor tintColor,
Float fadeDuration,
bool progressiveRenderingEnabled,
ImageSource loadingIndicatorSource,
std::string analyticTag)
: blurRadius(blurRadius),
defaultSource(std::move(defaultSource)),
resizeMode(resizeMode),
resizeMethod(std::move(resizeMethod)),
resizeMultiplier(resizeMultiplier),
shouldNotify(shouldNotify),
overlayColor(overlayColor),
tintColor(tintColor),
fadeDuration(fadeDuration),
progressiveRenderingEnabled(progressiveRenderingEnabled),
loadingIndicatorSource(std::move(loadingIndicatorSource)),
analyticTag(std::move(analyticTag)) {}
Float blurRadius{};
ImageSource defaultSource{};
ImageResizeMode resizeMode{ImageResizeMode::Stretch};
std::string resizeMethod{};
Float resizeMultiplier{};
bool shouldNotify{};
SharedColor overlayColor{};
SharedColor tintColor{};
Float fadeDuration{};
bool progressiveRenderingEnabled{};
ImageSource loadingIndicatorSource{};
std::string analyticTag{};
bool operator==(const ImageRequestParams& rhs) const {
return std::tie(
this->blurRadius,
this->defaultSource,
this->resizeMode,
this->resizeMethod,
this->resizeMultiplier,
this->shouldNotify,
this->overlayColor,
this->tintColor,
this->fadeDuration,
this->progressiveRenderingEnabled,
this->loadingIndicatorSource,
this->analyticTag) ==
std::tie(
rhs.blurRadius,
rhs.defaultSource,
rhs.resizeMode,
rhs.resizeMethod,
rhs.resizeMultiplier,
rhs.shouldNotify,
rhs.overlayColor,
rhs.tintColor,
rhs.fadeDuration,
rhs.progressiveRenderingEnabled,
rhs.loadingIndicatorSource,
rhs.analyticTag);
}
bool operator!=(const ImageRequestParams& rhs) const {
return !(*this == rhs);
}
};
} // namespace facebook::react
@@ -0,0 +1,100 @@
/*
* 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 <string>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
namespace facebook::react {
inline std::string toString(const ImageResizeMode& value) {
switch (value) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
constexpr static MapBuffer::Key IS_KEY_URI = 0;
constexpr static MapBuffer::Key IS_KEY_DEFAULT_SRC = 1;
constexpr static MapBuffer::Key IS_KEY_RESIZE_MODE = 2;
constexpr static MapBuffer::Key IS_KEY_RESIZE_METHOD = 3;
constexpr static MapBuffer::Key IS_KEY_BLUR_RADIUS = 4;
constexpr static MapBuffer::Key IS_KEY_VIEW_WIDTH = 5;
constexpr static MapBuffer::Key IS_KEY_VIEW_HEIGHT = 6;
constexpr static MapBuffer::Key IS_KEY_RESIZE_MULTIPLIER = 7;
constexpr static MapBuffer::Key IS_KEY_SHOULD_NOTIFY_LOAD_EVENTS = 8;
constexpr static MapBuffer::Key IS_KEY_OVERLAY_COLOR = 9;
constexpr static MapBuffer::Key IS_KEY_TINT_COLOR = 10;
constexpr static MapBuffer::Key IS_KEY_FADE_DURATION = 11;
constexpr static MapBuffer::Key IS_KEY_PROGRESSIVE_RENDERING_ENABLED = 12;
constexpr static MapBuffer::Key IS_KEY_LOADING_INDICATOR_SRC = 13;
constexpr static MapBuffer::Key IS_KEY_ANALYTIC_TAG = 14;
inline void serializeImageSource(
MapBufferBuilder& builder,
const ImageSource& imageSource) {
builder.putString(IS_KEY_URI, imageSource.uri);
builder.putDouble(IS_KEY_VIEW_WIDTH, imageSource.size.width);
builder.putDouble(IS_KEY_VIEW_HEIGHT, imageSource.size.height);
}
inline void serializeImageRequestParams(
MapBufferBuilder& builder,
const ImageRequestParams& imageRequestParams) {
builder.putString(IS_KEY_DEFAULT_SRC, imageRequestParams.defaultSource.uri);
builder.putString(
IS_KEY_RESIZE_MODE, toString(imageRequestParams.resizeMode));
builder.putString(IS_KEY_RESIZE_METHOD, imageRequestParams.resizeMethod);
builder.putDouble(IS_KEY_BLUR_RADIUS, imageRequestParams.blurRadius);
builder.putDouble(
IS_KEY_RESIZE_MULTIPLIER, imageRequestParams.resizeMultiplier);
builder.putBool(
IS_KEY_SHOULD_NOTIFY_LOAD_EVENTS, imageRequestParams.shouldNotify);
if (isColorMeaningful(imageRequestParams.overlayColor)) {
builder.putInt(
IS_KEY_OVERLAY_COLOR, toAndroidRepr(imageRequestParams.overlayColor));
}
if (isColorMeaningful(imageRequestParams.tintColor)) {
builder.putInt(
IS_KEY_TINT_COLOR, toAndroidRepr(imageRequestParams.tintColor));
}
builder.putDouble(IS_KEY_FADE_DURATION, imageRequestParams.fadeDuration);
builder.putBool(
IS_KEY_PROGRESSIVE_RENDERING_ENABLED,
imageRequestParams.progressiveRenderingEnabled);
builder.putString(
IS_KEY_LOADING_INDICATOR_SRC,
imageRequestParams.loadingIndicatorSource.uri);
builder.putString(IS_KEY_ANALYTIC_TAG, imageRequestParams.analyticTag);
}
inline MapBuffer serializeImageRequest(
const ImageSource& imageSource,
const ImageRequestParams& imageRequestParams) {
auto builder = MapBufferBuilder();
serializeImageSource(builder, imageSource);
serializeImageRequestParams(builder, imageRequestParams);
return builder.build();
}
} // namespace facebook::react
@@ -45,17 +45,22 @@ static SharedViewProps nonFlattenedDefaultProps(
static ShadowNode::Shared makeNode(
const ComponentDescriptor& componentDescriptor,
int tag,
const ShadowNode::ListOfShared& children,
std::shared_ptr<ShadowNode::ListOfShared> children,
bool flattened = false) {
auto props = flattened ? generateDefaultProps(componentDescriptor)
: nonFlattenedDefaultProps(componentDescriptor);
return componentDescriptor.createShadowNode(
ShadowNodeFragment{
props, std::make_shared<ShadowNode::ListOfShared>(children)},
ShadowNodeFragment{std::move(props), std::move(children)},
componentDescriptor.createFamily({tag, SurfaceId(1), nullptr}));
}
static std::shared_ptr<ShadowNode::ListOfShared> listOfChildren(
std::initializer_list<ShadowNode::Shared> list) {
return std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{list});
}
/**
* Test reordering of views with the same parent:
*
@@ -117,25 +122,20 @@ TEST(MountingTest, testReorderingInstructionGeneration) {
auto shadowNodeV1 = viewComponentDescriptor.createShadowNode(
ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childB, childC, childD})},
listOfChildren({childB, childC, childD})},
family);
auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childA, childB, childC, childD})});
listOfChildren({childA, childB, childC, childD})});
auto shadowNodeV3 = shadowNodeV2->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childB, childC, childD})});
listOfChildren({childB, childC, childD})});
auto shadowNodeV4 = shadowNodeV3->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childB, childD, childE})});
listOfChildren({childB, childD, childE})});
auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childB, childA, childE, childC})});
listOfChildren({childB, childA, childE, childC})});
auto shadowNodeV6 = shadowNodeV5->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(ShadowNode::ListOfShared{
@@ -157,38 +157,31 @@ TEST(MountingTest, testReorderingInstructionGeneration) {
auto rootNodeV1 = std::static_pointer_cast<const RootShadowNode>(
emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV1})}));
listOfChildren({shadowNodeV1})}));
auto rootNodeV2 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV1->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV2})}));
listOfChildren({shadowNodeV2})}));
auto rootNodeV3 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV2->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV3})}));
listOfChildren({shadowNodeV3})}));
auto rootNodeV4 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV3->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV4})}));
listOfChildren({shadowNodeV4})}));
auto rootNodeV5 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV4->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV5})}));
listOfChildren({shadowNodeV5})}));
auto rootNodeV6 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV5->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV6})}));
listOfChildren({shadowNodeV6})}));
auto rootNodeV7 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV6->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV7})}));
listOfChildren({shadowNodeV7})}));
// Layout
std::vector<const LayoutableShadowNode*> affectedLayoutableNodesV1{};
@@ -289,7 +282,6 @@ TEST(MountingTest, testReorderingInstructionGeneration) {
// Calculating mutations.
auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4);
LOG(ERROR) << "Num mutations IN OLD TEST mutations3: " << mutations3.size();
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -425,172 +417,107 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) {
auto family =
viewComponentDescriptor.createFamily({10, SurfaceId(1), nullptr});
auto reparentedViewA = makeNode(
viewComponentDescriptor,
1000,
ShadowNode::ListOfShared{
childC->clone({}), childA->clone({}), childB->clone({})});
auto reparentedViewA_ = makeNode(
viewComponentDescriptor, 1000, listOfChildren({childC, childA, childB}));
auto reparentedViewA = reparentedViewA_->clone(
ShadowNodeFragment{nonFlattenedDefaultProps(viewComponentDescriptor)});
auto reparentedViewB = makeNode(
viewComponentDescriptor,
2000,
ShadowNode::ListOfShared{
childF->clone({}), childE->clone({}), childD->clone({})});
viewComponentDescriptor, 2000, listOfChildren({childF, childE, childD}));
// Root -> G* -> H -> I -> J -> A* [nodes with * are _not_ flattened]
auto shadowNodeV1 = viewComponentDescriptor.createShadowNode(
ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childH->clone(ShadowNodeFragment{
listOfChildren({childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childH->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({childI->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({childJ->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childI->clone(ShadowNodeFragment{
generateDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childJ->clone(ShadowNodeFragment{
generateDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
reparentedViewA->clone(
{})})})})})})})})})})},
listOfChildren({reparentedViewA_})})})})})})})})})},
family);
// Root -> G* -> H* -> I -> J -> A* [nodes with * are _not_ flattened]
// Force an update with A with new props
auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childG->clone(ShadowNodeFragment{
listOfChildren({childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childH->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childH->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(ShadowNode::ListOfShared{
childI->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childJ->clone(ShadowNodeFragment{
generateDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
reparentedViewA->clone(
{})})})})})})})})})})});
listOfChildren({childI->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({childJ->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({reparentedViewA})})})})})})})})})});
// Root -> G* -> H -> I -> J -> A* [nodes with * are _not_ flattened]
auto shadowNodeV3 = shadowNodeV2->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childH->clone(ShadowNodeFragment{
listOfChildren({childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childH->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({childI->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren({childJ->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(ShadowNode::ListOfShared{
childI->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childJ->clone(ShadowNodeFragment{
generateDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
reparentedViewA->clone(
{})})})})})})})})})})});
listOfChildren({reparentedViewA})})})})})})})})})});
// The view is reparented 1 level down with a different sibling
// Root -> G* -> H* -> I* -> J -> [B*, A*] [nodes with * are _not_ flattened]
auto shadowNodeV4 = shadowNodeV3->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childG->clone(ShadowNodeFragment{
listOfChildren({childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childH->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childH->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(ShadowNode::ListOfShared{
childI->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childJ->clone(ShadowNodeFragment{
generateDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
reparentedViewB->clone({}),
reparentedViewA->clone(
{})})})})})})})})})})});
listOfChildren({childI->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childJ->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
listOfChildren(
{reparentedViewB, reparentedViewA})})})})})})})})})});
// The view is reparented 1 level further down with its order with the sibling
// swapped
// Root -> G* -> H* -> I* -> J* -> [A*, B*] [nodes with * are _not_ flattened]
auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{
generateDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childG->clone(ShadowNodeFragment{
listOfChildren({childG->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childH->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{childH->clone(ShadowNodeFragment{
listOfChildren({childI->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
listOfChildren({childJ->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(ShadowNode::ListOfShared{
childI->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(viewComponentDescriptor),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
childJ->clone(ShadowNodeFragment{
nonFlattenedDefaultProps(
viewComponentDescriptor),
std::make_shared<
ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{
reparentedViewA->clone({}),
reparentedViewB->clone(
{})})})})})})})})})})});
listOfChildren(
{reparentedViewA, reparentedViewB})})})})})})})})})});
// Injecting a tree into the root node.
auto rootNodeV1 = std::static_pointer_cast<const RootShadowNode>(
emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV1})}));
listOfChildren({shadowNodeV1})}));
auto rootNodeV2 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV1->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV2})}));
listOfChildren({shadowNodeV2})}));
auto rootNodeV3 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV2->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV3})}));
listOfChildren({shadowNodeV3})}));
auto rootNodeV4 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV3->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV4})}));
listOfChildren({shadowNodeV4})}));
auto rootNodeV5 = std::static_pointer_cast<const RootShadowNode>(
rootNodeV4->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<ShadowNode::ListOfShared>(
ShadowNode::ListOfShared{shadowNodeV5})}));
listOfChildren({shadowNodeV5})}));
// Layout
std::vector<const LayoutableShadowNode*> affectedLayoutableNodesV1{};
@@ -626,33 +553,38 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) {
// Calculating mutations.
auto mutations1 = calculateShadowViewMutations(*rootNodeV1, *rootNodeV2);
EXPECT_EQ(mutations1.size(), 5);
EXPECT_EQ(mutations1.size(), 6);
EXPECT_EQ(mutations1[0].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations1[0].oldChildShadowView.tag, 106);
EXPECT_EQ(mutations1[1].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations1[1].oldChildShadowView.tag, 1000);
EXPECT_EQ(mutations1[2].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations1[2].newChildShadowView.tag, 107);
EXPECT_EQ(mutations1[3].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations1[3].newChildShadowView.tag, 107);
EXPECT_EQ(mutations1[0].oldChildShadowView.tag, childG->getTag());
EXPECT_EQ(mutations1[1].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations1[1].oldChildShadowView.tag, reparentedViewA->getTag());
// This is incorrect! ChildH does not exist yet at this point
EXPECT_EQ(mutations1[1].parentShadowView.tag, childH->getTag());
EXPECT_EQ(mutations1[2].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations1[2].oldChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations1[3].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations1[3].newChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations1[4].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations1[4].newChildShadowView.tag, 1000);
EXPECT_EQ(mutations1[4].newChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations1[5].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations1[5].newChildShadowView.tag, reparentedViewA->getTag());
auto mutations2 = calculateShadowViewMutations(*rootNodeV2, *rootNodeV3);
EXPECT_EQ(mutations2.size(), 5);
EXPECT_EQ(mutations2[0].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations2[0].oldChildShadowView.tag, 106);
EXPECT_EQ(mutations2[0].oldChildShadowView.tag, childG->getTag());
EXPECT_EQ(mutations2[0].parentShadowView.tag, emptyRootNode->getTag());
EXPECT_EQ(mutations2[1].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations2[1].oldChildShadowView.tag, 1000);
EXPECT_EQ(mutations2[1].oldChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations2[2].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations2[2].oldChildShadowView.tag, 107);
EXPECT_EQ(mutations2[2].oldChildShadowView.tag, childH->getTag());
EXPECT_EQ(
mutations2[3].type,
ShadowViewMutation::Delete); // correct, 107 is removed from tree entirely
EXPECT_EQ(mutations2[3].oldChildShadowView.tag, 107);
ShadowViewMutation::Delete); // correct, H is removed from tree entirely
EXPECT_EQ(mutations2[3].oldChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations2[4].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations2[4].newChildShadowView.tag, 1000);
EXPECT_EQ(mutations2[4].newChildShadowView.tag, reparentedViewA->getTag());
auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4);
@@ -661,57 +593,58 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) {
EXPECT_EQ(mutations3.size(), 15);
EXPECT_EQ(mutations3[0].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations3[0].oldChildShadowView.tag, 106);
EXPECT_EQ(mutations3[0].oldChildShadowView.tag, childG->getTag());
EXPECT_EQ(mutations3[0].parentShadowView.tag, emptyRootNode->getTag());
EXPECT_EQ(mutations3[1].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations3[1].oldChildShadowView.tag, 1000);
EXPECT_EQ(mutations3[1].oldChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations3[2].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[2].newChildShadowView.tag, 107);
EXPECT_EQ(mutations3[2].newChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations3[3].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[3].newChildShadowView.tag, 2000);
EXPECT_EQ(mutations3[3].newChildShadowView.tag, reparentedViewB->getTag());
EXPECT_EQ(mutations3[4].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[4].newChildShadowView.tag, 108);
EXPECT_EQ(mutations3[4].newChildShadowView.tag, childI->getTag());
EXPECT_EQ(mutations3[5].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[5].newChildShadowView.tag, 105);
EXPECT_EQ(mutations3[5].newChildShadowView.tag, childF->getTag());
EXPECT_EQ(mutations3[6].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[6].newChildShadowView.tag, 104);
EXPECT_EQ(mutations3[6].newChildShadowView.tag, childE->getTag());
EXPECT_EQ(mutations3[7].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations3[7].newChildShadowView.tag, 103);
EXPECT_EQ(mutations3[7].newChildShadowView.tag, childD->getTag());
EXPECT_EQ(mutations3[8].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[8].newChildShadowView.tag, 105);
EXPECT_EQ(mutations3[8].newChildShadowView.tag, childF->getTag());
EXPECT_EQ(mutations3[9].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[9].newChildShadowView.tag, 104);
EXPECT_EQ(mutations3[9].newChildShadowView.tag, childE->getTag());
EXPECT_EQ(mutations3[10].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[10].newChildShadowView.tag, 103);
EXPECT_EQ(mutations3[10].newChildShadowView.tag, childD->getTag());
EXPECT_EQ(mutations3[11].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[11].newChildShadowView.tag, 107);
EXPECT_EQ(mutations3[11].newChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations3[12].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[12].newChildShadowView.tag, 108);
EXPECT_EQ(mutations3[12].newChildShadowView.tag, childI->getTag());
EXPECT_EQ(mutations3[13].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[13].newChildShadowView.tag, 2000);
EXPECT_EQ(mutations3[13].newChildShadowView.tag, reparentedViewB->getTag());
EXPECT_EQ(mutations3[14].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations3[14].newChildShadowView.tag, 1000);
EXPECT_EQ(mutations3[14].newChildShadowView.tag, reparentedViewA->getTag());
auto mutations4 = calculateShadowViewMutations(*rootNodeV4, *rootNodeV5);
EXPECT_EQ(mutations4.size(), 9);
EXPECT_EQ(mutations4[0].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations4[0].oldChildShadowView.tag, 106);
EXPECT_EQ(mutations4[0].oldChildShadowView.tag, childG->getTag());
EXPECT_EQ(mutations4[1].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations4[1].oldChildShadowView.tag, 107);
EXPECT_EQ(mutations4[1].oldChildShadowView.tag, childH->getTag());
EXPECT_EQ(mutations4[2].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations4[2].oldChildShadowView.tag, 108);
EXPECT_EQ(mutations4[2].oldChildShadowView.tag, childI->getTag());
EXPECT_EQ(mutations4[3].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations4[3].oldChildShadowView.tag, 1000);
EXPECT_EQ(mutations4[3].oldChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations4[4].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations4[4].oldChildShadowView.tag, 2000);
EXPECT_EQ(mutations4[4].oldChildShadowView.tag, reparentedViewB->getTag());
EXPECT_EQ(mutations4[5].type, ShadowViewMutation::Create);
EXPECT_EQ(mutations4[5].newChildShadowView.tag, 109);
EXPECT_EQ(mutations4[5].newChildShadowView.tag, childJ->getTag());
EXPECT_EQ(mutations4[6].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations4[6].newChildShadowView.tag, 109);
EXPECT_EQ(mutations4[6].newChildShadowView.tag, childJ->getTag());
EXPECT_EQ(mutations4[7].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations4[7].newChildShadowView.tag, 1000);
EXPECT_EQ(mutations4[7].newChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations4[8].type, ShadowViewMutation::Insert);
EXPECT_EQ(mutations4[8].newChildShadowView.tag, 2000);
EXPECT_EQ(mutations4[8].newChildShadowView.tag, reparentedViewB->getTag());
}
} // namespace facebook::react
@@ -87,8 +87,8 @@ class PointerEventsProcessorTest : public ::testing::Test {
listenToAllPointerEvents(props);
props.layoutConstraints = LayoutConstraints{{0,0}, {500, 500}};
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(400));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(400));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(400));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(400));
yogaStyle.setDisplay(yoga::Display::Flex);
yogaStyle.setFlexDirection(yoga::FlexDirection::Row);
yogaStyle.setAlignItems(yoga::Align::Center);
@@ -109,8 +109,8 @@ class PointerEventsProcessorTest : public ::testing::Test {
yogaStyle.setFlexDirection(yoga::FlexDirection::Column);
yogaStyle.setAlignItems(yoga::Align::FlexEnd);
yogaStyle.setJustifyContent(yoga::Justify::Center);
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(150));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(300));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(150));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(300));
return sharedProps;
})
.children({
@@ -123,8 +123,8 @@ class PointerEventsProcessorTest : public ::testing::Test {
auto &props = *sharedProps;
listenToAllPointerEvents(props);
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(100));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(100));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
}),
@@ -141,8 +141,8 @@ class PointerEventsProcessorTest : public ::testing::Test {
yogaStyle.setFlexDirection(yoga::FlexDirection::Column);
yogaStyle.setAlignItems(yoga::Align::FlexStart);
yogaStyle.setJustifyContent(yoga::Justify::Center);
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(150));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(300));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(150));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(300));
return sharedProps;
})
.children({
@@ -155,8 +155,8 @@ class PointerEventsProcessorTest : public ::testing::Test {
auto &props = *sharedProps;
listenToAllPointerEvents(props);
auto &yogaStyle = props.yogaStyle;
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(100));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200));
yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleSizeLength::points(100));
yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleSizeLength::points(200));
return sharedProps;
})
})
@@ -57,62 +57,48 @@ ReactInstance::ReactInstance(
timerManager_(std::move(timerManager)),
jsErrorHandler_(std::make_shared<JsErrorHandler>(std::move(onJsError))),
parentInspectorTarget_(parentInspectorTarget) {
RuntimeExecutor runtimeExecutor = [weakRuntime = std::weak_ptr(runtime_),
weakTimerManager =
std::weak_ptr(timerManager_),
weakJsThread =
std::weak_ptr(jsMessageQueueThread_),
jsErrorHandler =
jsErrorHandler_](auto callback) {
if (weakRuntime.expired()) {
return;
}
/**
* If a fatal error was caught while executing the main bundle, assume the
* js runtime is invalid. And stop executing any more js.
*/
if (!jsErrorHandler->isRuntimeReady() &&
jsErrorHandler->hasHandledFatalError()) {
LOG(INFO)
<< "RuntimeExecutor: Detected fatal error. Dropping work on non-js thread."
<< std::endl;
return;
}
if (auto jsThread = weakJsThread.lock()) {
jsThread->runOnQueue([jsErrorHandler,
weakRuntime,
weakTimerManager,
callback = std::move(callback)]() {
auto runtime = weakRuntime.lock();
if (!runtime) {
RuntimeExecutor runtimeExecutor =
[weakRuntime = std::weak_ptr(runtime_),
weakTimerManager = std::weak_ptr(timerManager_),
weakJsThread = std::weak_ptr(jsMessageQueueThread_),
jsErrorHandler = jsErrorHandler_](auto callback) {
if (weakRuntime.expired()) {
return;
}
jsi::Runtime& jsiRuntime = runtime->getRuntime();
SystraceSection s("ReactInstance::_runtimeExecutor[Callback]");
try {
ShadowNode::setUseRuntimeShadowNodeReferenceUpdateOnThread(true);
callback(jsiRuntime);
// If we have first-class support for microtasks,
// they would've been called as part of the previous callback.
if (ReactNativeFeatureFlags::disableEventLoopOnBridgeless()) {
if (auto timerManager = weakTimerManager.lock()) {
timerManager->callReactNativeMicrotasks(jsiRuntime);
if (auto jsThread = weakJsThread.lock()) {
jsThread->runOnQueue([jsErrorHandler,
weakRuntime,
weakTimerManager,
callback = std::move(callback)]() {
auto runtime = weakRuntime.lock();
if (!runtime) {
return;
}
}
} catch (jsi::JSError& originalError) {
jsErrorHandler->handleError(jsiRuntime, originalError, true);
} catch (std::exception& ex) {
jsi::JSError error(
jsiRuntime, std::string("Non-js exception: ") + ex.what());
jsErrorHandler->handleError(jsiRuntime, error, true);
jsi::Runtime& jsiRuntime = runtime->getRuntime();
SystraceSection s("ReactInstance::_runtimeExecutor[Callback]");
try {
ShadowNode::setUseRuntimeShadowNodeReferenceUpdateOnThread(true);
callback(jsiRuntime);
// If we have first-class support for microtasks,
// they would've been called as part of the previous callback.
if (ReactNativeFeatureFlags::disableEventLoopOnBridgeless()) {
if (auto timerManager = weakTimerManager.lock()) {
timerManager->callReactNativeMicrotasks(jsiRuntime);
}
}
} catch (jsi::JSError& originalError) {
jsErrorHandler->handleError(jsiRuntime, originalError, true);
} catch (std::exception& ex) {
jsi::JSError error(
jsiRuntime, std::string("Non-js exception: ") + ex.what());
jsErrorHandler->handleError(jsiRuntime, error, true);
}
});
}
});
}
};
};
if (parentInspectorTarget_) {
auto executor = parentInspectorTarget_->executorFromThis();
@@ -121,8 +121,11 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
[_bridgeModuleDecorator.callableJSModules
setBridgelessJSModuleMethodInvoker:^(
NSString *moduleName, NSString *methodName, NSArray *args, dispatch_block_t onComplete) {
// TODO: Make RCTInstance call onComplete
[weakSelf callFunctionOnJSModule:moduleName method:methodName args:args];
if (onComplete) {
[weakSelf
callFunctionOnBufferedRuntimeExecutor:[onComplete](facebook::jsi::Runtime &_) { onComplete(); }];
}
}];
}
_launchOptions = launchOptions;
@@ -14,6 +14,13 @@
namespace facebook::react {
namespace {
/** Process ID for all emitted events. */
const uint64_t PID = 1000;
} // namespace
bool FuseboxTracer::isTracing() {
std::lock_guard lock(mutex_);
return tracing_;
@@ -43,34 +50,43 @@ bool FuseboxTracer::stopTracing(
}
auto traceEvents = folly::dynamic::array();
// Register "Main" process
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", "Main"))("cat", "__metadata")(
"name", "process_name")("ph", "M")("pid", PID)("tid", 0)("ts", 0));
// Register "Timings" track
// NOTE: This is a hack to make the trace viewer show a "Timings" track
// adjacent to custom tracks in our current build of Chrome DevTools.
// In future, we should align events exactly.
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", "Timings"))("cat", "__metadata")(
"name", "thread_name")("ph", "M")("pid", PID)("tid", 1000)("ts", 0));
auto savedBuffer = std::move(buffer_);
buffer_.clear();
std::unordered_map<std::string, uint64_t> trackIdMap;
uint64_t nextTrack = 1000;
// Name the main process. Only one process is supported currently.
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", "Main App"))("cat", "__metadata")(
"name", "process_name")("ph", "M")("pid", 1000)("tid", 0)("ts", 0));
uint64_t nextTrack = 1001;
for (auto& event : savedBuffer) {
if (!trackIdMap.contains(event.track)) {
// For events with a custom track name, register track
if (event.track.length() && !trackIdMap.contains(event.track)) {
auto trackId = nextTrack++;
trackIdMap[event.track] = trackId;
// New track
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", event.track))(
"cat", "__metadata")("name", "thread_name")("ph", "M")("pid", 1000)(
"cat", "__metadata")("name", "thread_name")("ph", "M")("pid", PID)(
"tid", trackId)("ts", 0));
}
auto trackId = trackIdMap[event.track];
// New event
auto trackId =
trackIdMap.contains(event.track) ? trackIdMap[event.track] : 1000;
// Emit "blink.user_timing" trace event
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object())("cat", "react.native")(
"args", folly::dynamic::object())("cat", "blink.user_timing")(
"dur", (event.end - event.start) * 1000)("name", event.name)("ph", "X")(
"ts", event.start * 1000)("pid", 1000)("tid", trackId));
"ts", event.start * 1000)("pid", PID)("tid", trackId));
if (traceEvents.size() >= 1000) {
resultCallback(traceEvents);
@@ -88,13 +104,13 @@ void FuseboxTracer::addEvent(
const std::string_view& name,
uint64_t start,
uint64_t end,
const std::string_view& track) {
const std::optional<std::string_view>& track) {
std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
return;
}
buffer_.push_back(
BufferEvent{start, end, std::string(name), std::string(track)});
buffer_.push_back(BufferEvent{
start, end, std::string(name), std::string(track.value_or(""))});
}
bool FuseboxTracer::stopTracingAndWriteToFile(const std::string& path) {
@@ -7,10 +7,12 @@
#pragma once
#include <functional>
#include <vector>
#include "folly/dynamic.h"
#include <functional>
#include <optional>
#include <vector>
namespace facebook::react {
struct BufferEvent {
@@ -38,7 +40,7 @@ class FuseboxTracer {
const std::string_view& name,
uint64_t start,
uint64_t end,
const std::string_view& track);
const std::optional<std::string_view>& track);
static FuseboxTracer& getFuseboxTracer();
@@ -14,15 +14,33 @@
#endif
#include <chrono>
#ifdef WITH_PERFETTO
#include "ReactPerfetto.h"
#endif
namespace facebook::react {
#ifdef WITH_PERFETTO
namespace {
const std::string PERFETTO_DEFAULT_TRACK_NAME = "# Web Performance";
const std::string PERFETTO_TRACK_NAME_PREFIX = "# Web Performance: ";
std::string toPerfettoTrackName(
const std::optional<std::string_view>& trackName) {
return trackName.has_value()
? PERFETTO_TRACK_NAME_PREFIX + std::string(trackName.value())
: PERFETTO_DEFAULT_TRACK_NAME;
}
} // namespace
#endif
/* static */ void ReactPerfLogger::measure(
const std::string_view& eventName,
double startTime,
double endTime,
const std::string_view& trackName) {
const std::optional<std::string_view>& trackName) {
#ifdef HAS_FUSEBOX
FuseboxTracer::getFuseboxTracer().addEvent(
eventName, (uint64_t)startTime, (uint64_t)endTime, trackName);
@@ -30,7 +48,7 @@ namespace facebook::react {
#ifdef WITH_PERFETTO
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
auto track = getPerfettoWebPerfTrackAsync(std::string(trackName));
auto track = getPerfettoWebPerfTrackAsync(toPerfettoTrackName(trackName));
TRACE_EVENT_BEGIN(
"react-native",
perfetto::DynamicString(eventName.data(), eventName.size()),
@@ -45,7 +63,7 @@ namespace facebook::react {
/* static */ void ReactPerfLogger::mark(
const std::string_view& eventName,
double startTime,
const std::string_view& trackName) {
const std::optional<std::string_view>& trackName) {
// TODO(T203046480) Support mark in FuseboxTracer
#ifdef WITH_PERFETTO
@@ -53,7 +71,7 @@ namespace facebook::react {
TRACE_EVENT_INSTANT(
"react-native",
perfetto::DynamicString(eventName.data(), eventName.size()),
getPerfettoWebPerfTrackSync(std::string(trackName)),
getPerfettoWebPerfTrackSync(toPerfettoTrackName(trackName)),
performanceNowToPerfettoTraceTime(startTime));
}
#endif
@@ -62,4 +80,5 @@ namespace facebook::react {
/* static */ double ReactPerfLogger::performanceNow() {
return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now());
}
} // namespace facebook::react
@@ -8,26 +8,30 @@
#pragma once
#include <reactperflogger/ReactPerfettoCategories.h>
#include <optional>
#include <string>
namespace facebook::react {
/**
* An internal interface for logging performance events to configured React
* Native performance tools, such as Perfetto or React Native DevTools.
*
* Approximates https://w3c.github.io/user-timing/.
*/
class ReactPerfLogger {
public:
static void mark(
const std::string_view& eventName,
double startTime,
const std::string_view& trackName);
const std::optional<std::string_view>& trackName);
/**
* This accepts performance events that should go to internal tracing
* frameworks like Perfetto, and should go to DevTools like Fusebox.
*/
static void measure(
const std::string_view& eventName,
double startTime,
double endTime,
const std::string_view& trackName);
const std::optional<std::string_view>& trackName);
static double performanceNow();
};
@@ -245,6 +245,12 @@ const char* YGUnitToString(const YGUnit value) {
return "percent";
case YGUnitAuto:
return "auto";
case YGUnitMaxContent:
return "max-content";
case YGUnitFitContent:
return "fit-content";
case YGUnitStretch:
return "stretch";
}
return "unknown";
}
@@ -131,7 +131,10 @@ YG_ENUM_DECL(
YGUnitUndefined,
YGUnitPoint,
YGUnitPercent,
YGUnitAuto)
YGUnitAuto,
YGUnitMaxContent,
YGUnitFitContent,
YGUnitStretch)
YG_ENUM_DECL(
YGWrap,

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