Compare commits

...
41 Commits
Author SHA1 Message Date
Pieter De Baetsandmeta-codesync[bot] 693e9628e8 Mark mOnViewAttachMountItems as final in SurfaceMountingManager.java (#54344)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54344

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D85904478

fbshipit-source-id: 961d12bf1cbf04b9087a518531b20d87be3ccb63
2025-10-31 15:50:52 -07:00
David Vaccaandmeta-codesync[bot] 0e3a210be1 Add comprehensive KDoc documentation to FilterHelper (#54350)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54350

Added comprehensive KDoc documentation to the FilterHelper object and its primary public method for parsing and applying CSS filter effects on Android S+ (API 31+).

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85926889

fbshipit-source-id: e20bb0697753e710d66acc3ef4078ab009d6dc5d
2025-10-31 14:42:30 -07:00
David Vaccaandmeta-codesync[bot] b8f9d982f5 Add KDoc documentation to ComponentNameResolverBinding (#54349)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54349

Added comprehensive KDoc documentation to the ComponentNameResolverBinding object and its JNI method for installing component name resolvers into the JavaScript runtime.

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85926888

fbshipit-source-id: 253577d4c5d48bee22acb5666680becd4e632879
2025-10-31 14:42:30 -07:00
David Vaccaandmeta-codesync[bot] 7d7d843666 Add KDoc documentation to ComponentNameResolver interface (#54348)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54348

Added comprehensive KDoc documentation to the ComponentNameResolver interface and its property providing access to registered component names.

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85926887

fbshipit-source-id: ae12629e27f88555c7876d3343a146b1b5c3dec0
2025-10-31 14:42:30 -07:00
David Vaccaandmeta-codesync[bot] 89bcc02a07 Add KDoc documentation to BlendModeHelper (#54347)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54347

Added comprehensive KDoc documentation to the BlendModeHelper object and its public method for parsing CSS mix-blend-mode properties on Android Q+.

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85926891

fbshipit-source-id: bcafa6b02281d5265f59b34641847bc3b19d6604
2025-10-31 14:42:30 -07:00
David Vaccaandmeta-codesync[bot] ad0210c535 Add comprehensive KDoc documentation to BackgroundStyleApplicator (#54346)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54346

Added comprehensive KDoc documentation to the BackgroundStyleApplicator class and all 25 public methods covering backgrounds, borders, outlines, and box shadows.

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85926890

fbshipit-source-id: 5a398bf8d57d6b6dff6b012ac225bc96d56e2573
2025-10-31 14:42:30 -07:00
David Vaccaandmeta-codesync[bot] cc2101d063 Add KDoc documentation to FloatUtil class (#54345)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54345

Added comprehensive KDoc documentation to the `FloatUtil` class and its public methods to improve code readability and developer experience.

The documentation explains:
- The purpose of epsilon-based floating-point comparison and why it's necessary
- The role of the EPSILON constant in determining equality
- Special case handling for NaN values (both considered equal when both are NaN)
- Null value handling in the nullable overload
- Method parameters and return values

This documentation will help developers understand the rationale behind using this utility instead of direct float equality comparisons, which can be unreliable due to floating-point precision issues.

changelog: [internal] internal

Reviewed By: lenaic

Differential Revision: D85923748

fbshipit-source-id: d054099de3d9903801aadca4cfcd3127bb6d5297
2025-10-31 14:42:30 -07:00
Gianni Moschiniandmeta-codesync[bot] e2b62bc435 fix code snippet text container in AnsiHiglight (#54360)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54360

## Changelog:

[General] [Fixed] - Fixing an issue with the error LogBox formatting on windows causing text to wrap

Reviewed By: rickhanlonii, yannickl

Differential Revision: D76845667

fbshipit-source-id: 4b1ce705396f9818cbe9be80d521478b5eba632b
2025-10-31 13:24:51 -07:00
Luna Weiandmeta-codesync[bot] 025e0e47ef Remove clipping check on scrollview (#54357)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54357

Changelog: [Internal] - Ignore whether ScrollView enables subview clipping and just use the drawing rect for forwarding a clippingRect for VirtualView

Reviewed By: yungsters

Differential Revision: D85817030

fbshipit-source-id: e89298d106ec993ce1ca63131da002e0685c34dd
2025-10-31 13:23:26 -07:00
Rubén Norteandmeta-codesync[bot] 15589485c5 Remove legacy profiling for network requests (#54353)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54353

Changelog: [internal]

We have support for the network track in performance traces so we no longer need this custom integration.

Reviewed By: huntie

Differential Revision: D85946500

fbshipit-source-id: 4efa132a6524af6595ddf9f8c2b5651959fa1f94
2025-10-31 11:11:33 -07:00
Rubén Norteandmeta-codesync[bot] ad5ff285a9 Unify perf tracks under a React Native track group (#54354)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54354

Changelog: [internal]

This just renames the tracks for network, etc. to go under a "⚛ Native" track group.

Reviewed By: huntie

Differential Revision: D85946501

fbshipit-source-id: f7ea65bfd84ed4965749f7e22091bcaff38a5bc3
2025-10-31 11:11:33 -07:00
Rubén Norteandmeta-codesync[bot] 3806edc1bd Use try/finally for performance measure for device events (#54355)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54355

Changelog: [internal]

Small refactor to make sure profiling timespans don't break if there's an exception in one of the listeners.

Reviewed By: huntie

Differential Revision: D85951560

fbshipit-source-id: 162672a7e644893f46a3b0100284bc4125a83757
2025-10-31 11:11:33 -07:00
nishan (o^▽^o)andmeta-codesync[bot] 20ba98e00d feat(iOS) - Add contrast and hue rotate filter (#54294)
Summary:
This PR adds support for `contrast` and `hueRotate` filter support on iOS as discussed here - https://github.com/react-native-community/discussions-and-proposals/issues/927

## Changelog:

[IOS] [ADDED] - Add contrast and hue-rotate CSS filters

<!-- 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

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

Test Plan:
Test Filter screen in RNTester app. Checkout contrast and hue-rotate examples. Results are consistent on android and iOS.

<img width="300" height="auto" alt="Screenshot 2025-10-28 at 6 43 10 AM" src="https://github.com/user-attachments/assets/da7e4166-5723-4e4d-8d94-df9efb127672" />

<img width="300" height="auto" alt="Screenshot 2025-10-28 at 6 02 40 AM" src="https://github.com/user-attachments/assets/2d332202-6b2e-4269-93ed-3f80d3e03e4a" />

cc - joevilches thanks! remaining filters are `invert` and `sepia`. These are a bit tricky as they don't have direct SwiftUI equivalents. I am trying a few things, will do a PR when I get it working.

Reviewed By: jorge-cab

Differential Revision: D85961419

Pulled By: joevilches

fbshipit-source-id: f4628b17570e5b8fd3092392224f2d84b7d47d1d
2025-10-31 11:09:37 -07:00
Alex Huntandmeta-codesync[bot] a74387be66 Add Performance Issues to Perf Monitor (2/2) (#54266)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54266

Introduces the concept of **Performance Issues**, an experimental performance signals concept for React Native.

**Design**

Performance Issues are an **experimental** user space API via the User Timings `detail` object.

```
performance.measure({
  start,
  end,
  detail: {
    devtools: {
      ...
    },
   rnPerfIssue: {
     name: 'React: Cascading Update',
     severity: 'warning', // 'info' | 'warning' | 'error',
     description:
      'A cascading update is a update that is triggered by a previous update. This can lead to performance issues and should be avoided.',
    learnMoreUrl:
      'https://react.dev/reference/dev-tools/react-performance-tracks#cascading-updates',
    }
  }
});
```

When `rnPerfIssue` is present, we eagerly report an the event over CDP, regardless of an active performance trace, via the `"__react_native_perf_issues_reporter"` runtime binding.

**This diff**

- Updates the V2 Perf Monitor UI (Android) to display Performance Issues as a count.
    - (UI parts of this diff, including `HostTarget::installPerfIssuesBinding` are reinstated from the previous INP design, removed in D82208400).

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D85448199

fbshipit-source-id: 0cee0eff275219fa46ccf788a6208889d3a0506e
2025-10-31 09:39:10 -07:00
Alex Huntandmeta-codesync[bot] ee9ef3bf2d Add Performance Issues to Perf Monitor (1/2) (#54265)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54265

Introduces the concept of **Performance Issues**, an experimental performance signals concept for React Native.

**Design**

Performance Issues are an **experimental** user space API via the User Timings `detail` object.

```
performance.measure({
  start,
  end,
  detail: {
    devtools: {
      ...
    },
   rnPerfIssue: {
     name: 'React: Cascading Update',
     severity: 'warning', // 'info' | 'warning' | 'error',
     description:
      'A cascading update is a update that is triggered by a previous update. This can lead to performance issues and should be avoided.',
    learnMoreUrl:
      'https://react.dev/reference/dev-tools/react-performance-tracks#cascading-updates',
    }
  }
});
```

When `rnPerfIssue` is present, we eagerly report an the event over CDP, regardless of an active performance trace, via the `"__react_native_perf_issues_reporter"` runtime binding.

**This diff**

- Adds a `perfIssuesEnabled` feature flag.
- Initial implementation of the above API.
- Initially reports a "Cascading Render" issue, aligning 1:1 with the corresponding React performance track event (note: to be added in the React codebase, see https://github.com/facebook/react/pull/34961).

This feature is gated by the `perfMonitorV2Enabled` feature flag.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D85448200

fbshipit-source-id: e5aed5138682331e1cd3a713a5f09fb6745138bc
2025-10-31 09:39:10 -07:00
Alex Huntandmeta-codesync[bot] 6d68cdf5c3 Fix missing jsi dependency following #54262 (#54341)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54341

Changelog: [Internal]

{F1983141005}

Reviewed By: cipolleschi

Differential Revision: D85888301

fbshipit-source-id: 54cfaf2995bf0231e661fc11e115266578e1f692
2025-10-31 05:57:29 -07:00
Pieter De Baetsandmeta-codesync[bot] 6757e547c5 Mark private static fields as final in ReactHorizontalScrollView (#54343)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54343

Fixed NotWrittenPrivateField lint warnings in ReactHorizontalScrollView.java by marking three private static fields as final:

- DEBUG_MODE: Changed from `private static boolean` to `private static final boolean`
- TAG: Changed from `private static String` to `private static final String`
- NO_SCROLL_POSITION: Changed from `private static int` to `private static final int`

These fields are initialized once and never modified, making them compile-time constants. Marking them as final follows Java best practices and prevents potential issues with Redex code optimization.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D85904890

fbshipit-source-id: f83a19392a2418d8415936421f1e021316643056
2025-10-31 04:23:26 -07:00
Nolan O'Brienandmeta-codesync[bot] 3ff4ab2290 Fix missing enum values in switch statements (#54342)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54342

`-Wswitch-enum` was introduced in 2024 and is beneficial because it will err when switch statement is missing a case for an enum, even with `default:` present.  This helps alert developers when they add an enum value of all the switch statements that need updating.

These diffs are to help progress the codebase so that we can enable `-Wswitch-enum` by default in `fbobjc`

## Changelog:
[General] [Fixed] - Add missing value to switch for `-Wswitch-enum` builds

Differential Revision: D85835952

fbshipit-source-id: e56c45895a63325e565315a39ea59df769c4a3ad
2025-10-30 21:01:45 -07:00
Vladimir Makaevandmeta-codesync[bot] ae4d79d279 Daily arc lint --take CLANGFORMAT
Reviewed By: ionutluzinschi

Differential Revision: D85728900

fbshipit-source-id: 022ac989ce5519b5b065a9fa8a20ce6438309b4c
2025-10-30 15:56:46 -07:00
Alex Huntandmeta-codesync[bot] 7403e85233 Update debugger-frontend from 96e6bb8...7aa57d1 (#54340)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54340

Changelog: [Internal] - Update `react-native/debugger-frontend` from 96e6bb8...7aa57d1

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/facebook/react-native-devtools-frontend/compare/96e6bb8ecdff166816e2a469b7594f1276bda8c1...7aa57d13e50ce9d74a91c9315c9b0ded00fbc19f).

### Changelog

| Commit | Author | Date/Time | Subject |
| ------ | ------ | --------- | ------- |
| [7aa57d13e](https://github.com/facebook/react-native-devtools-frontend/commit/7aa57d13e) | Alex Hunt (hello@alexhunt.dev) | 2025-10-30T19:45:26+00:00 | [Add AGENTS.md (#215)](https://github.com/facebook/react-native-devtools-frontend/commit/7aa57d13e) |
| [d524f81db](https://github.com/facebook/react-native-devtools-frontend/commit/d524f81db) | Alex Hunt (hello@alexhunt.dev) | 2025-10-30T18:43:44+00:00 | [Enable Performance panel by default (#216)](https://github.com/facebook/react-native-devtools-frontend/commit/d524f81db) |
| [62f76dfbb](https://github.com/facebook/react-native-devtools-frontend/commit/62f76dfbb) | Alex Hunt (hello@alexhunt.dev) | 2025-10-30T17:01:50+00:00 | [Update project name in setup script (#214)](https://github.com/facebook/react-native-devtools-frontend/commit/62f76dfbb) |

Reviewed By: hoxyq

Differential Revision: D85888511

fbshipit-source-id: fbe4be107d8be6db7b91199dfe44b604b5be0036
2025-10-30 13:31:38 -07:00
Eric Rozellandmeta-codesync[bot] 600a8dc1da Allow customization of requestChildFocus scroll behavior in ScrollView (#54325)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54325

Apps that override ScrollView may need to customize the auto scroll behavior on focus.  This adds a protected method for apps that override ReactScrollView to customize focus behaviors.

## Changelog
[Internal]

Reviewed By: joevilches

Differential Revision: D85779339

fbshipit-source-id: 1ba978ed14ceb224ddc1d0da97727ff9702972e7
2025-10-30 12:22:50 -07:00
Alex Huntandmeta-codesync[bot] 9693a2b855 Support parsing of detail arg from console.timeStamp (#54262)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54262

Similar to `performance.measure()`, Chrome is adding an optional `detail` arg to `console.timeStamp`. Here we implement this for React Native, by direct passing to the `"TimeStamp"` trace event args.

[`console.timeStamp()`](https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp_static) remains an experimental, non-standard API.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D85437162

fbshipit-source-id: 36f5f6207cf205df5a216bde95013ea9540fc082
2025-10-30 11:42:24 -07:00
Rubén Norteandmeta-codesync[bot] e8fcde50e3 Simplify module after DOM API feature flag cleanup (#54323)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54323

Changelog: [internal]

We no longer need to lazily load these modules, because they're always used now.

Reviewed By: lunaleaps

Differential Revision: D85782126

fbshipit-source-id: 263eb234558b90d9cf5c6d55bccb39eb8d1c6a48
2025-10-30 08:37:04 -07:00
Rubén Norteandmeta-codesync[bot] 234b53d0f8 Merge tests for legacy node methods into ReactNativeElement test (#54321)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54321

Changelog: [internal]

Just moving some tests from `ReactFabricPublicInstance` to `ReactNativeElement` now that they're only used for that class.

Reviewed By: lunaleaps

Differential Revision: D85782130

fbshipit-source-id: 1445f61df2cbd691e09187b3b122032ad941cd67
2025-10-30 08:37:04 -07:00
Rubén Norteandmeta-codesync[bot] 8217165442 Delete unused legacy ReactFabricHostComponent class (#54320)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54320

Changelog: [internal]

This class is no longer used after we shipped DOM APIs.

Reviewed By: lunaleaps

Differential Revision: D85782129

fbshipit-source-id: 7ffa33aa1bade777d2b3fb7ae8b7c136376983a7
2025-10-30 08:37:04 -07:00
Rubén Norteandmeta-codesync[bot] 9c4601abec Clean up feature flag "enableAccessToHostTreeInFabric" (#54322)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54322

Changelog: [internal]

This just cleans up the feature flag, which is set to `true` by default and it's been enabled in `stable` already.

Reviewed By: lunaleaps, yungsters

Differential Revision: D85782127

fbshipit-source-id: 0a6174708066cfe597b4c6dbc8bb33829c6155e9
2025-10-30 08:37:04 -07:00
Rubén Norteandmeta-codesync[bot] beccee2164 Promote Web Performance APIs to stable (#54324)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54324

Changelog: [General][Added] - Enable Web Performance APIs

This promotes the Web Performance APIs from canary to stable.

Reviewed By: cipolleschi

Differential Revision: D85783108

fbshipit-source-id: 8d8b4565f28dc20d63e677c01366f4f37d5c46e4
2025-10-30 08:34:57 -07:00
Samuel Suslaandmeta-codesync[bot] 71f5c744db add name of command for systrace for easier debugging (#54328)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54328

changelog: [internal]

makes debugging easier if there is name of command.

Reviewed By: christophpurrer, javache

Differential Revision: D85784113

fbshipit-source-id: 14f89ea8b8401dce2dc84d14caaecc6cacda04b2
2025-10-30 06:04:42 -07:00
Harini Malothuandmeta-codesync[bot] f9754d3459 Ccomparison between signed and unsigned values, causing a error (#54318)
Summary:
We saw this error while integrating  changes in react native windows
https://github.com/microsoft/react-native-windows/issues/15292
Fixed type consistency in cloneMultipleRecursive by using size_t for loop iterator to properly match vector size type, improving type safety when iterating over children nodes.

## Changelog:
[GENERAL][FIXED] - Use size_t instead of int for vector iteration in cloneMultipleRecursive
<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

|GENERAL|FIXED|- Message

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

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

Reviewed By: christophpurrer

Differential Revision: D85800905

Pulled By: javache

fbshipit-source-id: 2fdb539e1c9af56eebdd5c7a900fa0e697bcab3a
2025-10-30 03:52:14 -07:00
Jorge Cabiedes Acostaandmeta-codesync[bot] d0015e0838 Fix visual artifacts when sometimes using decimal assymetric borderRadii (#54237)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54237

Before we were adding a single pixel for the stretchable area. in RCTViewComponentView we calculate what percentage is 1 pixel from the entire image to create the unit rectangle:
```
    CGRect contentsCenter = CGRect{
        CGPoint{imageCapInsets.left / imageSize.width, imageCapInsets.top / imageSize.height},
        CGSize{(CGFloat)1.0 / imageSize.width, (CGFloat)1.0 / imageSize.height}};
```

However sometimes when dividing `1.0 / imageSize.width` we would get a big enough float that causes a rounding error essentially having part of the border bleed into the stretchable region.

The easiest way to fix this is give the stretchable region a little more space so to prevent the bleeding.

We can do this by rounding the edgeInsets that give the border area its size

The alternative is some shockingly complex math to appropriately calculate the most convenient stretchable area size

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D85260693

fbshipit-source-id: 50eaba2189f3ff524be730211f8d19023522c68a
2025-10-29 20:31:35 -07:00
Peter Abbondanzoandmeta-codesync[bot] 26ad9492b3 Implement resizeMethod="resize" for image prefetching (#54228)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54228

## Issue
When prefetching images with the `resizeMethod` prop set, there's a brief period of time where the image has not laid out and the dimensions are 0x0. Since `ImageSource.h` only considers the `type` and `uri` properties for equality, two different `ImageSource` objects would be considered equal despite having different `size`. We would continue on to prefetch the image in its fullest quality and retain that image in `ImageState`, and upon subsequent requests once we have non-zero layouts, we would bail out early in the `ImageShadowNode::updateStateIfNeeded` method since both the old image source and image request params are equal in this case.

## Fix
Rather than adding `size` to the equality check, this change adds identical logic directly from `ReactImageView` to determine if we should postpone the image request: if the image is resizable and we don't have a width or a height, postpone!

## Additional Context
I noodled with a few spots of where this should ultimately live, but this seemed like the least invasive without making some larger refactors. The other approach I considered was to instead return an optional `ImageRequest` object from `ImageManager->requestImage call` to signal that no request was ever made, and using the `ImageRequest` response as part of the equality check.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D85200818

fbshipit-source-id: 82090feb500dafa47af14220a17262d715d20a7a
2025-10-29 16:52:18 -07:00
Zeya Pengandmeta-codesync[bot] b5e209181e Fix EXC_BAD_ACCESS introduced in #54211 (#54319)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54319

## Changelog:

[Internal] [Changed] - Fix EXC_BAD_ACCESS introduced in #54211

Reviewed By: sammy-SC

Differential Revision: D85779978

fbshipit-source-id: 7b240e1484fdcc57283325febf75faedaba585cd
2025-10-29 16:33:13 -07:00
Eric Rozellandmeta-codesync[bot] 8a3cc6411e Remove unnecessary local variable in for deferred focus (#54326)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54326

This variable was not needed, isAttachedToWindow was working as expected.

## Changelog
[Internal]

Reviewed By: Abbondanzo

Differential Revision: D85798355

fbshipit-source-id: 1716b323b4ac0d1d6f3e29370223db66540020d2
2025-10-29 16:18:32 -07:00
Rubén Norteandmeta-codesync[bot] b68329f7ca Implement Kotlin API for PerformanceTracer in RNDT (#54316)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54316

Changelog: [internal]

This defines an internal class in Kotlin to access `PerformanceTracer` methods, necessary for platform-specific integrations on Android.

Reviewed By: sbuggay

Differential Revision: D85689126

fbshipit-source-id: 84cb675984b90c677c3a322b191e24cc6b58c74f
2025-10-29 15:35:35 -07:00
Rubén Norteandmeta-codesync[bot] 712c544be6 Implement method to subscribe to tracing start/stop in RNDT (#54315)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54315

Changelog: [internal]

This adds methods to `PerformanceTracer` so consumers can be notified when tracing starts/stop. This is necessary to activate features necessary for tracing that would be costly to enable all the time.

Reviewed By: sbuggay

Differential Revision: D85689124

fbshipit-source-id: 9f9f79162dcb988ac4be02a8911401ce448461a9
2025-10-29 15:35:35 -07:00
Eric Rozellandmeta-codesync[bot] 9d498f676d Defer focus until attached (#54309)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54309

We can encounter cases where something is rendered by React but not yet attached to Android at the point focus is called.

If we defer focus until the View is attached to the window, we can avoid apps needing to add annoying timeouts or other creative mechanisms to wait for native mount before calling imperative focus.

The only caveat here is that if you call imperative focus on something offscreen initially that is attached later (e.g., due to clipping), apps could encounter undesirable bugs where focus jumps at the point the previously detached view is now attached. One might also argue it's  bug prone to call focus on something that is far offscreen / you don't plan to mount onscreen immediately.

## Changelog

[Android][Fixed] Defers focus until View is attached

Reviewed By: sbuggay

Differential Revision: D85727424

fbshipit-source-id: 677834aa2d9ba2d3247d1e71b3fe0cdd9a6ec4aa
2025-10-29 11:56:02 -07:00
Riccardo Cipolleschiandmeta-codesync[bot] da5f15a25b Update draft release creation with Hermes V1 (#54313)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54313

This change updates the draft release workflow to add dSYMS for Hermes V1 and also updating the url from where the dSYMs can be downloaded.

## Changelog:
[Internal] -

Reviewed By: j-piasecki

Differential Revision: D85758123

fbshipit-source-id: 7d771266de5b3829e9a39c649b610aaf79aa6206
2025-10-29 11:15:18 -07:00
Peter Abbondanzoandmeta-codesync[bot] b0b434469e Add feature flag for checking if components should send key presses (#54295)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54295

Sets up a feature flag for keydown/keyup events. Split out from actual work implementing the feature to avoid having to run codegen each commit

Changelog: [Internal]

Reviewed By: alanleedev

Differential Revision: D84938062

fbshipit-source-id: b556f347720b4a651cd321996f2914a9c670c7a1
2025-10-29 09:38:32 -07:00
Pieter De Baetsandmeta-codesync[bot] aec3294807 Enable k2 for all targets, remove compiler warnings (#54284)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54284

Changelog: [Internal]

Reviewed By: alanleedev

Differential Revision: D85563230

fbshipit-source-id: 6eb7c6479ad3d0f6d6249a6a320e7835dc298db6
2025-10-29 09:30:10 -07:00
Peter Abbondanzoandmeta-codesync[bot] 242728d6c1 Set sweepActiveTouchOnChildNativeGesturesAndroid default feature flag value to true (#54307)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54307

First of the remaining steps to fully rollout `sweepActiveTouchOnChildNativeGesturesAndroid`. It's already enabled by default everywhere an override is set, letting this bake for a week then going to remove

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D85700377

fbshipit-source-id: 81f5b8679e5562ba6c5dfae331052b181b5258ec
2025-10-29 08:28:49 -07:00
generatedunixname537391475639613andmeta-codesync[bot] a7973378ba xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java (#54311)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/54311

Reviewed By: javache

Differential Revision: D85546696

fbshipit-source-id: aec0b077bca68c4a9abef98ee9db41f80edb09d9
2025-10-29 04:39:18 -07:00
109 changed files with 2249 additions and 998 deletions
@@ -121,7 +121,7 @@ describe('Create Draft Release', () => {
});
describe('#_computeBody', () => {
it('computes body for release', async () => {
it('computes body for release when no hermes versions are passed', async () => {
const version = '0.77.1';
const changelog = `## v${version}
### Breaking Changes
@@ -134,20 +134,83 @@ describe('Create Draft Release', () => {
#### iOS
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`;
const body = _computeBody(version, changelog);
const body = _computeBody(changelog, version);
expect(body).toEqual(`${changelog}
---
Hermes dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-release.tar.gz)
Hermes V1 dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-framework-dSYM-release.tar.gz)
ReactNativeDependencies dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
ReactNative Core dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
---
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
---
To help you upgrade to this version, you can use the [Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) ⚛️.
---
View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/react-native/blob/main/CHANGELOG.md).`);
});
it('computes body for release when hermes versions are passed', async () => {
const version = '0.77.1';
const hermesVersion = '0.15.0';
const hermesV1Version = '250829098.0.2';
const changelog = `## v${version}
### Breaking Changes
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
- [PR #3457](https://github.com/facebook/react-native/pull/3457) - Some other change
#### iOS
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`;
const body = _computeBody(
changelog,
version,
hermesVersion,
hermesV1Version,
);
expect(body).toEqual(`${changelog}
---
Hermes dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-release.tar.gz)
Hermes V1 dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-release.tar.gz)
ReactNativeDependencies dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
ReactNative Core dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
---
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
+21 -5
View File
@@ -40,19 +40,29 @@ function _extractChangelog(version) {
return changelog.slice(changelogStarts, changelogEnds).join('\n').trim();
}
function _computeBody(version, changelog) {
function _computeBody(changelog, version, hermesVersion, hermesV1Version) {
hermesVersion = hermesVersion ?? version;
hermesV1Version = hermesV1Version ?? version;
return `${changelog}
---
Hermes dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesVersion}/hermes-ios-${hermesVersion}-hermes-framework-dSYM-release.tar.gz)
Hermes V1 dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${hermesV1Version}/hermes-ios-${hermesV1Version}-hermes-framework-dSYM-release.tar.gz)
ReactNativeDependencies dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
ReactNative Core dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-core-release.tar.gz)
---
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
@@ -113,7 +123,13 @@ function moveToChangelogBranch(version) {
run(`git checkout -b changelog/v${version}`);
}
async function createDraftRelease(version, latest, token) {
async function createDraftRelease(
version,
latest,
token,
hermesVersion,
hermesV1Version,
) {
if (version.startsWith('v')) {
version = version.substring(1);
}
@@ -121,7 +137,7 @@ async function createDraftRelease(version, latest, token) {
_verifyTagExists(version);
moveToChangelogBranch(version);
const changelog = _extractChangelog(version);
const body = _computeBody(version, changelog);
const body = _computeBody(changelog, version, hermesVersion, hermesV1Version);
const release = await _createDraftReleaseOnGitHub(
version,
body,
+10 -1
View File
@@ -2,6 +2,15 @@ name: Create Draft Release
on:
workflow_call:
inputs:
hermesVersion:
required: false
type: string
description: The version of Hermes to use for this release (eg. 0.15.0). If not specified, it will use React Native Version
hermesV1Version:
required: false
type: string
description: The version of Hermes V1 to use for this release (eg. 250829098.0.2). If not specified, it will use React Native Version
jobs:
create-draft-release:
@@ -27,7 +36,7 @@ jobs:
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
const version = '${{ github.ref_name }}';
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}')).id;
return (await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}', ${{ inputs.hermesVersion }}, ${{ inputs.hermesV1Version }})).id;
result-encoding: string
- name: Upload release assets for DotSlash
uses: actions/github-script@v6
+26 -1
View File
@@ -19,6 +19,28 @@ jobs:
echo "Setting release type to release"
echo "RELEASE_TYPE=release" >> $GITHUB_OUTPUT
set_hermes_versions:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
outputs:
HERMES_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}
HERMES_V1_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}
steps:
- name: Checkout
uses: actions/checkout@v4
- id: set_hermes_versions
run: |
echo "Setting hermes versions to latest"
hermes_version=$(grep -oE 'HERMES_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2)
hermes_v1_version=$(grep -oE 'HERMES_V1_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2)
echo "HERMES_VERSION=$hermes_version" >> $GITHUB_OUTPUT
echo "HERMES_V1_VERSION=$hermes_v1_version" >> $GITHUB_OUTPUT
- name: Print hermes versions
run: |
echo "HERMES_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}"
echo "HERMES_V1_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}"
prebuild_apple_dependencies:
if: github.repository == 'facebook/react-native'
uses: ./.github/workflows/prebuild-ios-dependencies.yml
@@ -117,6 +139,9 @@ jobs:
secrets: inherit
create_draft_release:
needs: generate_changelog
needs: [generate_changelog, set_hermes_versions]
uses: ./.github/workflows/create-draft-release.yml
secrets: inherit
with:
hermesVersion: ${{ needs.set_hermes_versions.outputs.HERMES_VERSION }}
hermesV1Version: ${{ needs.set_hermes_versions.outputs.HERMES_V1_VERSION }}
+2 -2
View File
@@ -1,5 +1,5 @@
@generated SignedSource<<dc58cf17e902cabe1ef88730ce9e056e>>
Git revision: 96e6bb8ecdff166816e2a469b7594f1276bda8c1
@generated SignedSource<<4bb67766e15e25a481c4c38873260a7a>>
Git revision: 7aa57d13e50ce9d74a91c9315c9b0ded00fbc19f
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebook/react-native-devtools-frontend
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import*as e from"../root/root.js";const t=e.Runtime.RNExperimentName,n={didInitializeExperiments:!1,isReactNativeEntryPoint:!1};class i{name;title;unstable;docLink;feedbackLink;enabledByDefault;constructor(e){this.name=e.name,this.title=e.title,this.unstable=e.unstable,this.docLink=e.docLink,this.feedbackLink=e.feedbackLink,this.enabledByDefault=function(e,t){if(null==e)return()=>t;if("boolean"==typeof e)return()=>e;return e}(e.enabledByDefault,!1)}}const r=new class{#e=new Map;#t=new Set;register(e){if(n.didInitializeExperiments)throw new Error("Experiments must be registered before constructing MainImpl");const{name:t}=e;if(this.#e.has(t))throw new Error(`React Native Experiment ${t} is already registered`);this.#e.set(t,new i(e))}enableExperimentsByDefault(e){if(n.didInitializeExperiments)throw new Error("Experiments must be configured before constructing MainImpl");for(const n of e)if(Object.prototype.hasOwnProperty.call(t,n)){const e=this.#e.get(n);if(!e)throw new Error(`React Native Experiment ${n} is not registered`);e.enabledByDefault=()=>!0}else this.#t.add(n)}copyInto(e,t=""){for(const[i,r]of this.#e)e.register(i,t+r.title,r.unstable,r.docLink,r.feedbackLink),r.enabledByDefault({isReactNativeEntryPoint:n.isReactNativeEntryPoint})&&e.enableExperimentsByDefault([i]);for(const t of this.#t)e.enableExperimentsByDefault([t]);n.didInitializeExperiments=!0}};r.register({name:t.JS_HEAP_PROFILER_ENABLE,title:"Enable Heap Profiler (Memory Panel)",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.REACT_NATIVE_SPECIFIC_UI,title:"Show React Native-specific UI",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>e}),r.register({name:t.ENABLE_PERFORMANCE_PANEL,title:"Enable Performance panel",unstable:!0,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.ENABLE_NETWORK_PANEL,title:"Enable Network panel",unstable:!0,enabledByDefault:()=>!1});var a=Object.freeze({__proto__:null,Instance:r,RNExperimentName:t,setIsReactNativeEntryPoint:function(e){if(n.didInitializeExperiments)throw new Error("setIsReactNativeEntryPoint must be called before constructing MainImpl");n.isReactNativeEntryPoint=e}});export{a as RNExperimentsImpl};
import*as e from"../root/root.js";const t=e.Runtime.RNExperimentName,i={didInitializeExperiments:!1,isReactNativeEntryPoint:!1};class n{name;title;unstable;docLink;feedbackLink;enabledByDefault;constructor(e){this.name=e.name,this.title=e.title,this.unstable=e.unstable,this.docLink=e.docLink,this.feedbackLink=e.feedbackLink,this.enabledByDefault=function(e,t){if(null==e)return()=>t;if("boolean"==typeof e)return()=>e;return e}(e.enabledByDefault,!1)}}const r=new class{#e=new Map;#t=new Set;register(e){if(i.didInitializeExperiments)throw new Error("Experiments must be registered before constructing MainImpl");const{name:t}=e;if(this.#e.has(t))throw new Error(`React Native Experiment ${t} is already registered`);this.#e.set(t,new n(e))}enableExperimentsByDefault(e){if(i.didInitializeExperiments)throw new Error("Experiments must be configured before constructing MainImpl");for(const i of e)if(Object.prototype.hasOwnProperty.call(t,i)){const e=this.#e.get(i);if(!e)throw new Error(`React Native Experiment ${i} is not registered`);e.enabledByDefault=()=>!0}else this.#t.add(i)}copyInto(e,t=""){for(const[n,r]of this.#e)e.register(n,t+r.title,r.unstable,r.docLink,r.feedbackLink),r.enabledByDefault({isReactNativeEntryPoint:i.isReactNativeEntryPoint})&&e.enableExperimentsByDefault([n]);for(const t of this.#t)e.enableExperimentsByDefault([t]);i.didInitializeExperiments=!0}};r.register({name:t.JS_HEAP_PROFILER_ENABLE,title:"Enable Heap Profiler (Memory Panel)",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>!e}),r.register({name:t.REACT_NATIVE_SPECIFIC_UI,title:"Show React Native-specific UI",unstable:!1,enabledByDefault:({isReactNativeEntryPoint:e})=>e}),r.register({name:t.ENABLE_NETWORK_PANEL,title:"Enable Network panel",unstable:!0,enabledByDefault:()=>!1});var a=Object.freeze({__proto__:null,Instance:r,RNExperimentName:t,setIsReactNativeEntryPoint:function(e){if(i.didInitializeExperiments)throw new Error("setIsReactNativeEntryPoint must be called before constructing MainImpl");i.isReactNativeEntryPoint=e}});export{a as RNExperimentsImpl};
@@ -1 +1 @@
import*as e from"../platform/platform.js";const t=new URLSearchParams(location.search);let n,r,s="";function a(){return window.location.pathname}function i(e){return["node_app","js_app"].some((t=>e.includes(t)))}class o{constructor(){}static instance(e={forceNew:null}){const{forceNew:t}=e;return n&&!t||(n=new o),n}static removeInstance(){n=void 0}static queryParam(e){return t.get(e)}static setQueryParamForTesting(e,n){t.set(e,n)}static isNode(){return void 0===r&&(r=i(a())),r}static setPlatform(e){s=e}static platform(){return s}static isDescriptorEnabled(e){const{experiment:t}=e;if("*"===t)return!0;if(t&&t.startsWith("!")&&h.isEnabled(t.substring(1)))return!1;if(t&&!t.startsWith("!")&&!h.isEnabled(t))return!1;const{condition:n}=e;return!n||n(_)}loadLegacyModule(e){console.log("Loading legacy module: "+e);return import(`../../${e}`).then((t=>(console.log("Loaded legacy module: "+e),t)))}}class l{#e=[];#t=new Set;#n=new Set;#r=new Set;#s=new Set;#a=new c;allConfigurableExperiments(){const e=[];for(const t of this.#e)this.#n.has(t.name)||e.push(t);return e}register(t,n,r,s,a){if(this.#t.has(t))throw new Error(`Duplicate registration of experiment '${t}'`);this.#t.add(t),this.#e.push(new m(this,t,n,Boolean(r),s??e.DevToolsPath.EmptyUrlString,a??e.DevToolsPath.EmptyUrlString))}isEnabled(e){return this.checkExperiment(e),!1!==this.#a.get(e)&&(!(!this.#n.has(e)&&!this.#r.has(e))||(!!this.#s.has(e)||Boolean(this.#a.get(e))))}setEnabled(e,t){this.checkExperiment(e),this.#a.set(e,t)}enableExperimentsTransiently(e){for(const t of e)this.checkExperiment(t),this.#n.add(t)}enableExperimentsByDefault(e){for(const t of e)this.checkExperiment(t),this.#r.add(t)}setServerEnabledExperiments(e){for(const t of e)this.checkExperiment(t),this.#s.add(t)}enableForTest(e){this.checkExperiment(e),this.#n.add(e)}disableForTest(e){this.checkExperiment(e),this.#n.delete(e)}clearForTest(){this.#e=[],this.#t.clear(),this.#n.clear(),this.#r.clear(),this.#s.clear()}cleanUpStaleExperiments(){this.#a.cleanUpStaleExperiments(this.#t)}checkExperiment(e){if(!this.#t.has(e))throw new Error(`Unknown experiment '${e}'`)}}class c{#e={};constructor(){try{const e=self.localStorage?.getItem("experiments");e&&(this.#e=JSON.parse(e))}catch{console.error("Failed to parse localStorage['experiments']")}}get(e){return this.#e[e]}set(e,t){this.#e[e]=t,this.#i()}cleanUpStaleExperiments(e){for(const[t]of Object.entries(this.#e))e.has(t)||delete this.#e[t];this.#i()}#i(){self.localStorage?.setItem("experiments",JSON.stringify(this.#e))}}class m{name;title;unstable;docLink;feedbackLink;#e;constructor(e,t,n,r,s,a){this.name=t,this.title=n,this.unstable=r,this.docLink=s,this.feedbackLink=a,this.#e=e}isEnabled(){return this.#e.isEnabled(this.name)}setEnabled(e){this.#e.setEnabled(this.name,e)}}const h=new l;var E,p,d,u;!function(e){e.REACT_NATIVE_SPECIFIC_UI="react-native-specific-ui",e.JS_HEAP_PROFILER_ENABLE="js-heap-profiler-enable",e.ENABLE_PERFORMANCE_PANEL="enable-performance-panel",e.ENABLE_NETWORK_PANEL="enable-network-panel"}(E||(E={})),function(e){e.NOT_SOURCES_HIDE_ADD_FOLDER="!sources.hide_add_folder",e.REACT_NATIVE_UNSTABLE_NETWORK_PANEL="unstable_enableNetworkPanel"}(p||(p={})),function(e){e[e.ALLOW=0]="ALLOW",e[e.ALLOW_WITHOUT_LOGGING=1]="ALLOW_WITHOUT_LOGGING",e[e.DISABLE=2]="DISABLE"}(d||(d={})),function(e){e.ALL_SCRIPTS="ALL_SCRIPTS",e.SIDE_EFFECT_FREE_SCRIPTS_ONLY="SIDE_EFFECT_FREE_SCRIPTS_ONLY",e.NO_SCRIPTS="NO_SCRIPTS"}(u||(u={}));const _=Object.create(null),f={canDock:()=>Boolean(o.queryParam("can_dock")),notSourcesHideAddFolder:()=>Boolean(o.queryParam(p.NOT_SOURCES_HIDE_ADD_FOLDER)),reactNativeUnstableNetworkPanel:()=>Boolean(o.queryParam(p.REACT_NATIVE_UNSTABLE_NETWORK_PANEL))||h.isEnabled("enable-network-panel")};var x=Object.freeze({__proto__:null,get ConditionName(){return p},Experiment:m,ExperimentsSupport:l,get GenAiEnterprisePolicyValue(){return d},get HostConfigFreestylerExecutionMode(){return u},get RNExperimentName(){return E},Runtime:o,conditions:f,experiments:h,getChromeVersion:()=>{const e=navigator.userAgent.match(/(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/);return e&&e.length>1?e[1]:""},getPathName:a,getRemoteBase:function(e=self.location.toString()){const t=new URL(e).searchParams.get("remoteBase");if(!t)return null;const n=/\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(t);return n?{base:`devtools://devtools/remote/serve_file/${n[1]}/`,version:n[1]}:null},hostConfig:_,isNodeEntry:i});export{x as Runtime};
import*as e from"../platform/platform.js";const t=new URLSearchParams(location.search);let n,r,s="";function a(){return window.location.pathname}function i(e){return["node_app","js_app"].some((t=>e.includes(t)))}class o{constructor(){}static instance(e={forceNew:null}){const{forceNew:t}=e;return n&&!t||(n=new o),n}static removeInstance(){n=void 0}static queryParam(e){return t.get(e)}static setQueryParamForTesting(e,n){t.set(e,n)}static isNode(){return void 0===r&&(r=i(a())),r}static setPlatform(e){s=e}static platform(){return s}static isDescriptorEnabled(e){const{experiment:t}=e;if("*"===t)return!0;if(t&&t.startsWith("!")&&h.isEnabled(t.substring(1)))return!1;if(t&&!t.startsWith("!")&&!h.isEnabled(t))return!1;const{condition:n}=e;return!n||n(_)}loadLegacyModule(e){console.log("Loading legacy module: "+e);return import(`../../${e}`).then((t=>(console.log("Loaded legacy module: "+e),t)))}}class l{#e=[];#t=new Set;#n=new Set;#r=new Set;#s=new Set;#a=new c;allConfigurableExperiments(){const e=[];for(const t of this.#e)this.#n.has(t.name)||e.push(t);return e}register(t,n,r,s,a){if(this.#t.has(t))throw new Error(`Duplicate registration of experiment '${t}'`);this.#t.add(t),this.#e.push(new m(this,t,n,Boolean(r),s??e.DevToolsPath.EmptyUrlString,a??e.DevToolsPath.EmptyUrlString))}isEnabled(e){return this.checkExperiment(e),!1!==this.#a.get(e)&&(!(!this.#n.has(e)&&!this.#r.has(e))||(!!this.#s.has(e)||Boolean(this.#a.get(e))))}setEnabled(e,t){this.checkExperiment(e),this.#a.set(e,t)}enableExperimentsTransiently(e){for(const t of e)this.checkExperiment(t),this.#n.add(t)}enableExperimentsByDefault(e){for(const t of e)this.checkExperiment(t),this.#r.add(t)}setServerEnabledExperiments(e){for(const t of e)this.checkExperiment(t),this.#s.add(t)}enableForTest(e){this.checkExperiment(e),this.#n.add(e)}disableForTest(e){this.checkExperiment(e),this.#n.delete(e)}clearForTest(){this.#e=[],this.#t.clear(),this.#n.clear(),this.#r.clear(),this.#s.clear()}cleanUpStaleExperiments(){this.#a.cleanUpStaleExperiments(this.#t)}checkExperiment(e){if(!this.#t.has(e))throw new Error(`Unknown experiment '${e}'`)}}class c{#e={};constructor(){try{const e=self.localStorage?.getItem("experiments");e&&(this.#e=JSON.parse(e))}catch{console.error("Failed to parse localStorage['experiments']")}}get(e){return this.#e[e]}set(e,t){this.#e[e]=t,this.#i()}cleanUpStaleExperiments(e){for(const[t]of Object.entries(this.#e))e.has(t)||delete this.#e[t];this.#i()}#i(){self.localStorage?.setItem("experiments",JSON.stringify(this.#e))}}class m{name;title;unstable;docLink;feedbackLink;#e;constructor(e,t,n,r,s,a){this.name=t,this.title=n,this.unstable=r,this.docLink=s,this.feedbackLink=a,this.#e=e}isEnabled(){return this.#e.isEnabled(this.name)}setEnabled(e){this.#e.setEnabled(this.name,e)}}const h=new l;var E,p,d,u;!function(e){e.REACT_NATIVE_SPECIFIC_UI="react-native-specific-ui",e.JS_HEAP_PROFILER_ENABLE="js-heap-profiler-enable",e.ENABLE_NETWORK_PANEL="enable-network-panel"}(E||(E={})),function(e){e.NOT_SOURCES_HIDE_ADD_FOLDER="!sources.hide_add_folder",e.REACT_NATIVE_UNSTABLE_NETWORK_PANEL="unstable_enableNetworkPanel"}(p||(p={})),function(e){e[e.ALLOW=0]="ALLOW",e[e.ALLOW_WITHOUT_LOGGING=1]="ALLOW_WITHOUT_LOGGING",e[e.DISABLE=2]="DISABLE"}(d||(d={})),function(e){e.ALL_SCRIPTS="ALL_SCRIPTS",e.SIDE_EFFECT_FREE_SCRIPTS_ONLY="SIDE_EFFECT_FREE_SCRIPTS_ONLY",e.NO_SCRIPTS="NO_SCRIPTS"}(u||(u={}));const _=Object.create(null),f={canDock:()=>Boolean(o.queryParam("can_dock")),notSourcesHideAddFolder:()=>Boolean(o.queryParam(p.NOT_SOURCES_HIDE_ADD_FOLDER)),reactNativeUnstableNetworkPanel:()=>Boolean(o.queryParam(p.REACT_NATIVE_UNSTABLE_NETWORK_PANEL))||h.isEnabled("enable-network-panel")};var x=Object.freeze({__proto__:null,get ConditionName(){return p},Experiment:m,ExperimentsSupport:l,get GenAiEnterprisePolicyValue(){return d},get HostConfigFreestylerExecutionMode(){return u},get RNExperimentName(){return E},Runtime:o,conditions:f,experiments:h,getChromeVersion:()=>{const e=navigator.userAgent.match(/(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/);return e&&e.length>1?e[1]:""},getPathName:a,getRemoteBase:function(e=self.location.toString()){const t=new URL(e).searchParams.get("remoteBase");if(!t)return null;const n=/\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(t);return n?{base:`devtools://devtools/remote/serve_file/${n[1]}/`,version:n[1]}:null},hostConfig:_,isNodeEntry:i});export{x as Runtime};
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
@@ -26,7 +26,9 @@ describe('console.timeStamp()', () => {
it("doesn't throw when additional arguments are specified", () => {
expect(() =>
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error'),
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error', {
tooltipText: 'Image processing failed',
}),
).not.toThrow();
});
@@ -30,8 +30,11 @@ class RCTDeviceEventEmitterImpl extends EventEmitter<RCTDeviceEventDefinitions>
...args: RCTDeviceEventDefinitions[TEvent]
): void {
beginEvent(() => `RCTDeviceEventEmitter.emit#${eventType}`);
super.emit(eventType, ...args);
endEvent();
try {
super.emit(eventType, ...args);
} finally {
endEvent();
}
}
}
const RCTDeviceEventEmitter: IEventEmitter<RCTDeviceEventDefinitions> =
+4 -1
View File
@@ -87,7 +87,7 @@ export default function Ansi({
<View style={styles.container}>
{parsedLines.map((items, i) => (
<View style={styles.line} key={i}>
<Text>
<Text style={styles.text}>
{items.map((bundle, key) => {
const textStyle =
bundle.fg && COLORS[bundle.fg]
@@ -122,4 +122,7 @@ const styles = StyleSheet.create({
line: {
flexDirection: 'row',
},
text: {
flexGrow: 1,
},
});
+1 -39
View File
@@ -14,7 +14,6 @@ import type {
EventCallback,
EventListener,
} from '../../src/private/webapis/dom/events/EventTarget';
import type Performance from '../../src/private/webapis/performance/Performance';
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import Event from '../../src/private/webapis/dom/events/Event';
@@ -34,13 +33,7 @@ const RCTNetworking = require('./RCTNetworking').default;
const base64 = require('base64-js');
const invariant = require('invariant');
const PERFORMANCE_TRACK_NAME = 'Network (JS-initiated only)';
const PERFORMANCE_TRACK_GROUP = 'Chrome DevTools Temp Compat';
declare var performance: Performance;
const DEBUG_NETWORK_SEND_DELAY: false = false; // Set to a number of milliseconds when debugging
const LABEL_FOR_MISSING_URL_FOR_PROFILING = 'Unknown URL';
export type NativeResponseType = 'base64' | 'blob' | 'text';
export type ResponseType =
@@ -141,7 +134,6 @@ class XMLHttpRequest extends EventTarget {
static DONE: number = DONE;
static _interceptor: ?XHRInterceptor = null;
static _profiling: boolean = false;
UNSENT: number = UNSENT;
OPENED: number = OPENED;
@@ -182,10 +174,6 @@ class XMLHttpRequest extends EventTarget {
XMLHttpRequest._interceptor = interceptor;
}
static enableProfiling(enableProfiling: boolean): void {
XMLHttpRequest._profiling = enableProfiling;
}
constructor() {
super();
this._reset();
@@ -389,23 +377,12 @@ class XMLHttpRequest extends EventTarget {
return;
}
const start = XMLHttpRequest._profiling ? performance.now() : undefined;
if (!this._response) {
this._response = responseText;
} else {
this._response += responseText;
}
if (XMLHttpRequest._profiling) {
console.timeStamp(
'Incremental Data: ' + this._getMeasureURL(),
start,
undefined,
PERFORMANCE_TRACK_NAME,
PERFORMANCE_TRACK_GROUP,
);
}
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.dataReceived(requestId, responseText);
@@ -450,16 +427,7 @@ class XMLHttpRequest extends EventTarget {
this._clearSubscriptions();
this._requestId = null;
this.setReadyState(this.DONE);
if (XMLHttpRequest._profiling && this._startTime != null) {
const start = this._startTime;
console.timeStamp(
this._getMeasureURL(),
start,
undefined,
PERFORMANCE_TRACK_NAME,
PERFORMANCE_TRACK_GROUP,
);
}
if (error) {
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.loadingFailed(requestId, error);
@@ -729,12 +697,6 @@ class XMLHttpRequest extends EventTarget {
super.addEventListener(type, listener);
}
_getMeasureURL(): string {
return (
this._trackingName ?? this._url ?? LABEL_FOR_MISSING_URL_FOR_PROFILING
);
}
/*
* `on<event>` event handling (without JS prototype magic).
*/
@@ -1,152 +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.
*
* @flow strict-local
* @format
*/
import type {
HostInstance,
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
NativeMethods,
} from '../../../src/private/types/HostInstance';
import type {
InternalInstanceHandle,
ViewConfig,
} from '../../Renderer/shims/ReactNativeTypes';
import TextInputState from '../../Components/TextInput/TextInputState';
import {getNodeFromInternalInstanceHandle} from '../../ReactNative/RendererProxy';
import {getFabricUIManager} from '../FabricUIManager';
import {create} from './ReactNativeAttributePayload';
import warnForStyleProps from './warnForStyleProps';
import nullthrows from 'nullthrows';
const {
measure: fabricMeasure,
measureInWindow: fabricMeasureInWindow,
measureLayout: fabricMeasureLayout,
getBoundingClientRect: fabricGetBoundingClientRect,
setNativeProps,
} = nullthrows(getFabricUIManager());
const noop = () => {};
/**
* This is used for refs on host components.
*/
export default class ReactFabricHostComponent implements NativeMethods {
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
__nativeTag: number;
__internalInstanceHandle: InternalInstanceHandle;
_viewConfig: ViewConfig;
constructor(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: InternalInstanceHandle,
) {
this.__nativeTag = tag;
this._viewConfig = viewConfig;
this.__internalInstanceHandle = internalInstanceHandle;
}
blur() {
// $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement
TextInputState.blurTextInput(this);
}
focus() {
// $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement
TextInputState.focusTextInput(this);
}
measure(callback: MeasureOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null) {
fabricMeasure(node, callback);
}
}
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null) {
fabricMeasureInWindow(node, callback);
}
}
measureLayout(
relativeToNativeNode: number | HostInstance,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void /* currently unused */,
) {
if (
typeof relativeToNativeNode === 'number' ||
!(relativeToNativeNode instanceof ReactFabricHostComponent)
) {
if (__DEV__) {
console.error(
'Warning: ref.measureLayout must be called with a ref to a native component.',
);
}
return;
}
const toStateNode = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
const fromStateNode = getNodeFromInternalInstanceHandle(
relativeToNativeNode.__internalInstanceHandle,
);
if (toStateNode != null && fromStateNode != null) {
fabricMeasureLayout(
toStateNode,
fromStateNode,
onFail != null ? onFail : noop,
onSuccess != null ? onSuccess : noop,
);
}
}
unstable_getBoundingClientRect(): DOMRect {
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null) {
const rect = fabricGetBoundingClientRect(node, true);
if (rect) {
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
}
}
// Empty rect if any of the above failed
return new DOMRect(0, 0, 0, 0);
}
setNativeProps(nativeProps: {...}): void {
if (__DEV__) {
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
}
const updatePayload = create(nativeProps, this._viewConfig.validAttributes);
const node = getNodeFromInternalInstanceHandle(
this.__internalInstanceHandle,
);
if (node != null && updatePayload != null) {
setNativeProps(node, updatePayload);
}
}
}
@@ -13,124 +13,62 @@
* instances and get some data from them (like their instance handle / fiber).
*/
import type ReactNativeDocumentT from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
import typeof * as ReactNativeDocumentModuleT from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
import type ReactNativeElementT from '../../../src/private/webapis/dom/nodes/ReactNativeElement';
import type ReadOnlyTextT from '../../../src/private/webapis/dom/nodes/ReadOnlyText';
import typeof * as RendererProxyT from '../../ReactNative/RendererProxy';
import type {
InternalInstanceHandle,
Node,
ViewConfig,
} from '../../Renderer/shims/ReactNativeTypes';
import type {RootTag} from '../RootTag';
import type ReactFabricHostComponentT from './ReactFabricHostComponent';
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
import ReactNativeDocument, {
createReactNativeDocument,
} from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
import ReactNativeElement from '../../../src/private/webapis/dom/nodes/ReactNativeElement';
import ReadOnlyText from '../../../src/private/webapis/dom/nodes/ReadOnlyText';
import * as RendererProxy from '../../ReactNative/RendererProxy';
export opaque type PublicRootInstance = mixed;
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
let ReactNativeDocumentModuleObject: ?ReactNativeDocumentModuleT;
let ReactFabricHostComponentClass: Class<ReactFabricHostComponentT>;
let ReactNativeElementClass: Class<ReactNativeElementT>;
let ReadOnlyTextClass: Class<ReadOnlyTextT>;
let RendererProxy: RendererProxyT;
function getReactNativeDocumentModule(): ReactNativeDocumentModuleT {
if (ReactNativeDocumentModuleObject == null) {
// We initialize this lazily to avoid a require cycle.
ReactNativeDocumentModuleObject = require('../../../src/private/webapis/dom/nodes/ReactNativeDocument');
}
return ReactNativeDocumentModuleObject;
}
function getReactNativeElementClass(): Class<ReactNativeElementT> {
if (ReactNativeElementClass == null) {
ReactNativeElementClass =
require('../../../src/private/webapis/dom/nodes/ReactNativeElement').default;
}
return ReactNativeElementClass;
}
function getReactFabricHostComponentClass(): Class<ReactFabricHostComponentT> {
if (ReactFabricHostComponentClass == null) {
ReactFabricHostComponentClass =
require('./ReactFabricHostComponent').default;
}
return ReactFabricHostComponentClass;
}
function getReadOnlyTextClass(): Class<ReadOnlyTextT> {
if (ReadOnlyTextClass == null) {
ReadOnlyTextClass =
require('../../../src/private/webapis/dom/nodes/ReadOnlyText').default;
}
return ReadOnlyTextClass;
}
export function createPublicRootInstance(rootTag: RootTag): PublicRootInstance {
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
const ReactNativeDocumentModule = getReactNativeDocumentModule();
// $FlowExpectedError[incompatible-return]
return ReactNativeDocumentModule.createReactNativeDocument(rootTag);
}
// $FlowExpectedError[incompatible-return]
return null;
return createReactNativeDocument(rootTag);
}
export function createPublicInstance(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocumentT,
): ReactFabricHostComponentT | ReactNativeElementT {
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
const ReactNativeElement = getReactNativeElementClass();
return new ReactNativeElement(
tag,
viewConfig,
internalInstanceHandle,
ownerDocument,
);
} else {
const ReactFabricHostComponent = getReactFabricHostComponentClass();
return new ReactFabricHostComponent(
tag,
viewConfig,
internalInstanceHandle,
);
}
ownerDocument: ReactNativeDocument,
): ReactNativeElement {
return new ReactNativeElement(
tag,
viewConfig,
internalInstanceHandle,
ownerDocument,
);
}
export function createPublicTextInstance(
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocumentT,
): ReadOnlyTextT {
const ReadOnlyText = getReadOnlyTextClass();
ownerDocument: ReactNativeDocument,
): ReadOnlyText {
return new ReadOnlyText(internalInstanceHandle, ownerDocument);
}
export function getNativeTagFromPublicInstance(
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
publicInstance: ReactNativeElement,
): number {
return publicInstance.__nativeTag;
}
export function getNodeFromPublicInstance(
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
publicInstance: ReactNativeElement,
): ?Node {
// Avoid loading ReactFabric if using an instance from the legacy renderer.
if (publicInstance.__internalInstanceHandle == null) {
return null;
}
if (RendererProxy == null) {
RendererProxy = require('../../ReactNative/RendererProxy');
}
return RendererProxy.getNodeFromInternalInstanceHandle(
// $FlowExpectedError[incompatible-type] __internalInstanceHandle is always an InternalInstanceHandle from React when we get here.
publicInstance.__internalInstanceHandle,
@@ -138,7 +76,7 @@ export function getNodeFromPublicInstance(
}
export function getInternalInstanceHandleFromPublicInstance(
publicInstance: ReactFabricHostComponentT | ReactNativeElementT,
publicInstance: ReactNativeElement,
): InternalInstanceHandle {
// TODO(T174762768): Remove this once OSS versions of renderers will be synced.
// $FlowExpectedError[prop-missing] Keeping this for backwards-compatibility with the renderers versions in open source.
@@ -1,75 +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.
*
* @flow strict-local
* @format
*/
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import type {
InternalInstanceHandle,
ViewConfig,
} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
import type ReactNativeDocument from 'react-native/src/private/webapis/dom/nodes/ReactNativeDocument';
import * as Fantom from '@react-native/fantom';
import ReactFabricHostComponent from 'react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
// Create fake parameters for the class.
const tag = 11;
const viewConfig: ViewConfig = {
uiViewClassName: 'test',
validAttributes: {
style: {},
},
};
// $FlowExpectedError[incompatible-type]
const internalInstanceHandle: InternalInstanceHandle = {};
// $FlowExpectedError[incompatible-type]
const ownerDocument: ReactNativeDocument = {};
const ITERATIONS = 100;
const now = Fantom.unstable_benchmark.now;
/* eslint-disable no-new */
Fantom.unstable_benchmark
.suite('ReactNativeElement vs. ReactFabricHostComponent', {
minIterations: 50000,
})
.test('ReactNativeElement', () => {
// This logic is very fast and if we only run it once the cost of calling
// the benchmark function itself is significant. Calling it in a loop and
// computing the average is more accurate to account for that.
const start = now();
for (let i = 0; i < ITERATIONS; i++) {
new ReactNativeElement(
tag,
viewConfig,
internalInstanceHandle,
ownerDocument,
);
}
const end = now();
return {
overriddenDuration: (end - start) / ITERATIONS,
};
})
.test('ReactFabricHostComponent', () => {
// This logic is very fast and if we only run it once the cost of calling
// the benchmark function itself is significant. Calling it in a loop and
// computing the average is more accurate to account for that.
const start = now();
for (let i = 0; i < ITERATIONS; i++) {
new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
}
const end = now();
return {
overriddenDuration: (end - start) / ITERATIONS,
};
});
@@ -1,353 +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.
*
* @flow strict-local
* @format
* @fantom_flags enableAccessToHostTreeInFabric:*
*/
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import type {HostInstance} from 'react-native';
import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement';
import TextInput from '../../../Components/TextInput/TextInput';
import TextInputState from '../../../Components/TextInput/TextInputState';
import View from '../../../Components/View/View';
import ReactFabricHostComponent from '../ReactFabricHostComponent';
import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {createRef} from 'react';
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
// TODO: move these tests to the test file for `ReactNativeElement` when the legacy implementation is removed.
describe('ReactFabricPublicInstance', () => {
it('should provide instances of the right class as refs in host components', () => {
const nodeRef = createRef<HostInstance>();
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<View ref={nodeRef} />);
});
const node = nullthrows(nodeRef.current);
expect(node).toBeInstanceOf(
ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()
? ReactNativeElement
: ReactFabricHostComponent,
);
});
describe('blur', () => {
test('blur() invokes TextInputState', () => {
const root = Fantom.createRoot();
const nodeRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(<TextInput ref={nodeRef} />);
});
const node = nullthrows(nodeRef.current);
const blurTextInput = jest.fn();
// We don't support view commands in Fantom yet, so we have to mock this.
TextInputState.blurTextInput = blurTextInput;
Fantom.runTask(() => {
node.blur();
});
expect(blurTextInput).toHaveBeenCalledTimes(1);
expect(blurTextInput.mock.calls).toEqual([[node]]);
});
});
describe('focus', () => {
test('focus() invokes TextInputState', () => {
const root = Fantom.createRoot();
const ref = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(<TextInput ref={ref} />);
});
const node = nullthrows(ref.current);
const focusTextInput = jest.fn();
// We don't support view commands in Fantom yet, so we have to mock this.
TextInputState.focusTextInput = focusTextInput;
Fantom.runTask(() => {
node.focus();
});
expect(focusTextInput).toHaveBeenCalledTimes(1);
expect(focusTextInput.mock.calls).toEqual([[node]]);
});
});
describe('measure', () => {
it('component.measure(...) invokes callback', () => {
const root = Fantom.createRoot();
const ref = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={ref}
/>,
);
});
const node = nullthrows(ref.current);
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 = Fantom.createRoot();
const ref = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={ref}
/>,
);
});
const node = nullthrows(ref.current);
Fantom.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
node.measure(callback);
expect(callback).not.toHaveBeenCalled();
});
});
describe('measureInWindow', () => {
it('component.measureInWindow(...) invokes callback', () => {
const root = Fantom.createRoot();
const ref = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={ref}
/>,
);
});
const node = nullthrows(ref.current);
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 = Fantom.createRoot();
const ref = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={ref}
/>,
);
});
const node = nullthrows(ref.current);
Fantom.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
node.measureInWindow(callback);
expect(callback).not.toHaveBeenCalled();
});
});
describe('measureLayout', () => {
it('component.measureLayout(component, ...) invokes callback', () => {
const root = Fantom.createRoot();
const parentRef = createRef<HostInstance>();
const childRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={parentRef}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={childRef}
/>
</View>,
);
});
const parentNode = nullthrows(parentRef.current);
const childNode = nullthrows(childRef.current);
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 = Fantom.createRoot();
const parentRef = createRef<HostInstance>();
const childRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={parentRef}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={childRef}
/>
</View>,
);
});
const parentNode = nullthrows(parentRef.current);
const childNode = nullthrows(childRef.current);
Fantom.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 = Fantom.createRoot();
const parentRef = createRef<HostInstance>();
const childRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={parentRef}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={childRef}
/>
</View>,
);
});
const parentNode = nullthrows(parentRef.current);
const childNode = nullthrows(childRef.current);
Fantom.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 = Fantom.createRoot();
const parentRef = createRef<HostInstance>();
const childRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
style={{width: 100, height: 100, left: 10, top: 10}}
ref={parentRef}>
<View
style={{width: 10, height: 10, left: 20, top: 20}}
ref={childRef}
/>
</View>,
);
});
const parentNode = nullthrows(parentRef.current);
const childNode = nullthrows(childRef.current);
Fantom.runTask(() => {
root.render(<></>);
});
const callback = jest.fn();
childNode.measureLayout(parentNode, callback);
expect(callback).not.toHaveBeenCalled();
});
});
describe('setNativeProps', () => {
it('should propagate changes to the host component', () => {
const root = Fantom.createRoot();
const nodeRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(<View ref={nodeRef} testID="first test id" />);
});
expect(
root
.getRenderedOutput({
props: ['testID'],
})
.toJSX(),
).toEqual(<rn-view testID={'first test id'} />);
const element = nullthrows(nodeRef.current);
Fantom.runTask(() => {
element.setNativeProps({testID: 'second test id'});
});
expect(
root
.getRenderedOutput({
props: ['testID'],
})
.toJSX(),
).toEqual(<rn-view testID={'second test id'} />);
});
});
});
@@ -1088,6 +1088,16 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
Float saturation = std::get<Float>(primitive.parameters);
[_swiftUIWrapper updateSaturation:@(saturation)];
}
} else if (primitive.type == FilterType::Contrast) {
if (_swiftUIWrapper != nullptr) {
Float contrast = std::get<Float>(primitive.parameters);
[_swiftUIWrapper updateContrast:@(contrast)];
}
} else if (primitive.type == FilterType::HueRotate) {
if (_swiftUIWrapper != nullptr) {
Float hueRotateDegrees = std::get<Float>(primitive.parameters);
[_swiftUIWrapper updateHueRotate:@(hueRotateDegrees)];
}
}
}
}
@@ -1545,7 +1555,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
if (!_props->filter.empty()) {
for (const auto &primitive : _props->filter) {
if (primitive.type == FilterType::Blur || primitive.type == FilterType::Grayscale ||
primitive.type == FilterType::DropShadow || primitive.type == FilterType::Saturate) {
primitive.type == FilterType::DropShadow || primitive.type == FilterType::Saturate ||
primitive.type == FilterType::Contrast || primitive.type == FilterType::HueRotate) {
return YES;
}
}
@@ -247,8 +247,12 @@ static UIImage *RCTGetSolidBorderImage(
const CGSize size = makeStretchable ? (CGSize){
// 1pt for the middle stretchable area along each axis
edgeInsets.left + 1 + edgeInsets.right,
edgeInsets.top + 1 + edgeInsets.bottom
// we also need to round the edge insets to avoid border bleeding
// this is because if the size is decimal, when calculating the unit
// rectangle for CALayer.contentsCenter we encounter rounding errors
// which causes visual glitches
ceil(edgeInsets.left) + 1 + ceil(edgeInsets.right),
ceil(edgeInsets.top) + 1 + ceil(edgeInsets.bottom),
} : viewSize;
UIGraphicsImageRenderer *const imageRenderer =
@@ -5651,6 +5651,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android
public fun pageScroll (I)Z
public fun reactSmoothScrollTo (II)V
public fun requestChildFocus (Landroid/view/View;Landroid/view/View;)V
protected fun requestChildFocusWithoutScroll (Landroid/view/View;Landroid/view/View;)V
protected fun restoreScrollTo (II)V
public fun scrollTo (II)V
public fun scrollToPreservingMomentum (II)V
@@ -5791,6 +5792,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc
public fun onTouchEvent (Landroid/view/MotionEvent;)Z
public fun reactSmoothScrollTo (II)V
public fun requestChildFocus (Landroid/view/View;Landroid/view/View;)V
protected fun requestChildFocusWithoutScroll (Landroid/view/View;Landroid/view/View;)V
public fun scrollTo (II)V
public fun scrollToPreservingMomentum (II)V
public fun setBackgroundColor (I)V
@@ -22,6 +22,7 @@ internal class PerfMonitorOverlayManager(
private var view: PerfMonitorOverlayView? = null
private var tracingState: TracingState = TracingState.ENABLEDINCDPMODE
private var perfIssueCount: Int = 0
/** Enable the Perf Monitor overlay. */
fun enable() {
@@ -72,8 +73,19 @@ internal class PerfMonitorOverlayManager(
override fun onRecordingStateChanged(state: TracingState) {
tracingState = state
if (state != TracingState.DISABLED) {
perfIssueCount = 0
}
UiThreadUtil.runOnUiThread {
view?.updateRecordingState(state)
view?.updatePerfIssueCount(perfIssueCount)
view?.show()
}
}
override fun onPerfIssueAdded(name: String) {
UiThreadUtil.runOnUiThread {
view?.updatePerfIssueCount(++perfIssueCount)
view?.show()
}
}
@@ -30,9 +30,11 @@ internal class PerfMonitorOverlayView(
private val onButtonPress: () -> Unit,
) {
private val dialog: Dialog
private lateinit var statusIndicator: TextView
private lateinit var statusLabel: TextView
private lateinit var tooltipLabel: TextView
private lateinit var statusIndicator: TextView
private lateinit var issuesContainer: LinearLayout
private lateinit var issueCountLabel: TextView
init {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
@@ -70,6 +72,11 @@ internal class PerfMonitorOverlayView(
dialog.show()
}
fun updatePerfIssueCount(count: Int) {
issueCountLabel.text = count.toString()
issuesContainer.visibility = if (count == 0) LinearLayout.GONE else LinearLayout.VISIBLE
}
private fun createToolbarDialog(): Dialog {
statusIndicator =
TextView(context).apply {
@@ -85,11 +92,7 @@ internal class PerfMonitorOverlayView(
val textContainer =
LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
layoutParams =
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
)
setPadding(dpToPx(2f).toInt(), 0, 0, 0)
}
statusLabel =
TextView(context).apply {
@@ -106,10 +109,35 @@ internal class PerfMonitorOverlayView(
textContainer.addView(statusLabel)
textContainer.addView(tooltipLabel)
issuesContainer =
LinearLayout(context).apply {
setPadding(dpToPx(8f).toInt(), 0, 0, 0)
visibility = LinearLayout.GONE
}
issueCountLabel =
TextView(context).apply {
textSize = TEXT_SIZE_PRIMARY
setTextColor(Color.WHITE)
typeface = TYPEFACE_BOLD
val alertDrawable =
context.getDrawable(android.R.drawable.ic_dialog_alert)?.apply {
setBounds(
0,
1,
dpToPx(TEXT_SIZE_PRIMARY).toInt(),
dpToPx(TEXT_SIZE_PRIMARY).toInt() + 1,
)
}
setCompoundDrawables(alertDrawable, null, null, null)
compoundDrawablePadding = dpToPx(6f).toInt()
}
issuesContainer.addView(issueCountLabel)
val containerLayout = createInnerLayout()
containerLayout.setOnClickListener { onButtonPress() }
containerLayout.addView(statusIndicator)
containerLayout.addView(textContainer)
containerLayout.addView(issuesContainer)
val dialog =
createAnchoredDialog(dpToPx(12f), dpToPx(12f)).apply { setContentView(containerLayout) }
@@ -175,7 +203,7 @@ internal class PerfMonitorOverlayView(
showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE
dividerDrawable =
object : ColorDrawable(Color.TRANSPARENT) {
override fun getIntrinsicWidth(): Int = dpToPx(8f).toInt()
override fun getIntrinsicWidth(): Int = dpToPx(10f).toInt()
}
}
}
@@ -4,6 +4,7 @@
* 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.devsupport.perfmonitor
import com.facebook.react.devsupport.interfaces.TracingState
@@ -12,4 +13,7 @@ import com.facebook.react.devsupport.interfaces.TracingState
internal interface PerfMonitorUpdateListener {
/** Called when the recording state of the background performance trace has changed. */
fun onRecordingStateChanged(state: TracingState)
/** Called when a new Performance Issue is added. */
fun onPerfIssueAdded(name: String)
}
@@ -80,7 +80,7 @@ public class SurfaceMountingManager {
// These are all non-null, until StopSurface is called
private ConcurrentHashMap<Integer, ViewState> mTagToViewState =
new ConcurrentHashMap<>(); // any thread
private Queue<MountItem> mOnViewAttachMountItems = new ArrayDeque<>();
private final Queue<MountItem> mOnViewAttachMountItems = new ArrayDeque<>();
private JSResponderHandler mJSResponderHandler;
private ViewManagerRegistry mViewManagerRegistry;
private RootViewManager mRootViewManager;
@@ -1344,7 +1344,6 @@ public class SurfaceMountingManager {
final boolean mIsRoot;
@Nullable ViewManager mViewManager = null;
@Nullable ReactStylesDiffMap mCurrentProps = null;
@Nullable ReadableMap mCurrentLocalData = null;
@Nullable StateWrapper mStateWrapper = null;
@Nullable EventEmitterWrapper mEventEmitter = null;
@@ -1374,8 +1373,6 @@ public class SurfaceMountingManager {
+ mIsRoot
+ " - props: "
+ mCurrentProps
+ " - localData: "
+ mCurrentLocalData
+ " - viewManager: "
+ mViewManager
+ " - isLayoutOnly: "
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<28000f9d4a36207875619060f2fa5713>>
* @generated SignedSource<<d8dc8c3a2efe8b7e870546e5749ef5ad>>
*/
/**
@@ -210,6 +210,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean = accessor.enableInteropViewManagerClassLookUpOptimizationIOS()
/**
* Enables key up/down/press events to be sent to JS from components
*/
@JvmStatic
public fun enableKeyEvents(): Boolean = accessor.enableKeyEvents()
/**
* When enabled, LayoutAnimations API will animate state changes on Android.
*/
@@ -306,6 +312,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableViewRecyclingForView(): Boolean = accessor.enableViewRecyclingForView()
/**
* Set clipping to drawingRect of ScrollView.
*/
@JvmStatic
public fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean = accessor.enableVirtualViewClippingWithoutScrollViewClipping()
/**
* Enables the experimental version of `VirtualViewContainerState`.
*/
@@ -366,6 +378,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = accessor.overrideBySynchronousMountPropsAtMountingAndroid()
/**
* Enable reporting Performance Issues (`detail.rnPerfIssue`). Displayed in the V2 Performance Monitor and the "Performance Issues" sub-panel in DevTools.
*/
@JvmStatic
public fun perfIssuesEnabled(): Boolean = accessor.perfIssuesEnabled()
/**
* Enable the V2 in-app Performance Monitor. This flag is global and should not be changed across React Host lifetimes.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ba1a322a9c7069cf63fd759090f9025d>>
* @generated SignedSource<<447ccc7271b71b0208a2297b7eba5995>>
*/
/**
@@ -50,6 +50,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableImperativeFocusCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableKeyEventsCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableMainQueueCoordinatorOnIOSCache: Boolean? = null
@@ -66,6 +67,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableViewRecyclingForScrollViewCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
private var enableViewRecyclingForViewCache: Boolean? = null
private var enableVirtualViewClippingWithoutScrollViewClippingCache: Boolean? = null
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
private var enableVirtualViewRenderStateCache: Boolean? = null
@@ -76,6 +78,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null
private var perfIssuesEnabledCache: Boolean? = null
private var perfMonitorV2EnabledCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
@@ -371,6 +374,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableKeyEvents(): Boolean {
var cached = enableKeyEventsCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableKeyEvents()
enableKeyEventsCache = cached
}
return cached
}
override fun enableLayoutAnimationsOnAndroid(): Boolean {
var cached = enableLayoutAnimationsOnAndroidCache
if (cached == null) {
@@ -515,6 +527,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean {
var cached = enableVirtualViewClippingWithoutScrollViewClippingCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableVirtualViewClippingWithoutScrollViewClipping()
enableVirtualViewClippingWithoutScrollViewClippingCache = cached
}
return cached
}
override fun enableVirtualViewContainerStateExperimental(): Boolean {
var cached = enableVirtualViewContainerStateExperimentalCache
if (cached == null) {
@@ -605,6 +626,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun perfIssuesEnabled(): Boolean {
var cached = perfIssuesEnabledCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.perfIssuesEnabled()
perfIssuesEnabledCache = cached
}
return cached
}
override fun perfMonitorV2Enabled(): Boolean {
var cached = perfMonitorV2EnabledCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<412a865e975214a0d794985e48fbbe4b>>
* @generated SignedSource<<d6403b67e269ad80ef3e2389ac923c8a>>
*/
/**
@@ -88,6 +88,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@DoNotStrip @JvmStatic public external fun enableKeyEvents(): Boolean
@DoNotStrip @JvmStatic public external fun enableLayoutAnimationsOnAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun enableLayoutAnimationsOnIOS(): Boolean
@@ -120,6 +122,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForView(): Boolean
@DoNotStrip @JvmStatic public external fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean
@DoNotStrip @JvmStatic public external fun enableVirtualViewContainerStateExperimental(): Boolean
@DoNotStrip @JvmStatic public external fun enableVirtualViewDebugFeatures(): Boolean
@@ -140,6 +144,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun perfIssuesEnabled(): Boolean
@DoNotStrip @JvmStatic public external fun perfMonitorV2Enabled(): Boolean
@DoNotStrip @JvmStatic public external fun preparedTextCacheSize(): Double
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<7987eba84c39c6e8cb6494092a94790e>>
* @generated SignedSource<<ab48ba86a7cf3cd35ec530a216aea774>>
*/
/**
@@ -83,6 +83,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean = false
override fun enableKeyEvents(): Boolean = false
override fun enableLayoutAnimationsOnAndroid(): Boolean = false
override fun enableLayoutAnimationsOnIOS(): Boolean = true
@@ -115,6 +117,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableViewRecyclingForView(): Boolean = true
override fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean = true
override fun enableVirtualViewContainerStateExperimental(): Boolean = false
override fun enableVirtualViewDebugFeatures(): Boolean = false
@@ -123,7 +127,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableVirtualViewWindowFocusDetection(): Boolean = false
override fun enableWebPerformanceAPIsByDefault(): Boolean = false
override fun enableWebPerformanceAPIsByDefault(): Boolean = true
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
@@ -135,6 +139,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = false
override fun perfIssuesEnabled(): Boolean = false
override fun perfMonitorV2Enabled(): Boolean = false
override fun preparedTextCacheSize(): Double = 200.0
@@ -147,7 +153,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun skipActivityIdentityAssertionOnHostPause(): Boolean = false
override fun sweepActiveTouchOnChildNativeGesturesAndroid(): Boolean = false
override fun sweepActiveTouchOnChildNativeGesturesAndroid(): Boolean = true
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<7f43cde1ba2bef2300c077f35b168d31>>
* @generated SignedSource<<6f6b5abe79764b88cf3ed62fe0230786>>
*/
/**
@@ -54,6 +54,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableImperativeFocusCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableKeyEventsCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableMainQueueCoordinatorOnIOSCache: Boolean? = null
@@ -70,6 +71,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableViewRecyclingForScrollViewCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
private var enableViewRecyclingForViewCache: Boolean? = null
private var enableVirtualViewClippingWithoutScrollViewClippingCache: Boolean? = null
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
private var enableVirtualViewRenderStateCache: Boolean? = null
@@ -80,6 +82,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null
private var perfIssuesEnabledCache: Boolean? = null
private var perfMonitorV2EnabledCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
@@ -405,6 +408,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableKeyEvents(): Boolean {
var cached = enableKeyEventsCache
if (cached == null) {
cached = currentProvider.enableKeyEvents()
accessedFeatureFlags.add("enableKeyEvents")
enableKeyEventsCache = cached
}
return cached
}
override fun enableLayoutAnimationsOnAndroid(): Boolean {
var cached = enableLayoutAnimationsOnAndroidCache
if (cached == null) {
@@ -565,6 +578,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean {
var cached = enableVirtualViewClippingWithoutScrollViewClippingCache
if (cached == null) {
cached = currentProvider.enableVirtualViewClippingWithoutScrollViewClipping()
accessedFeatureFlags.add("enableVirtualViewClippingWithoutScrollViewClipping")
enableVirtualViewClippingWithoutScrollViewClippingCache = cached
}
return cached
}
override fun enableVirtualViewContainerStateExperimental(): Boolean {
var cached = enableVirtualViewContainerStateExperimentalCache
if (cached == null) {
@@ -665,6 +688,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun perfIssuesEnabled(): Boolean {
var cached = perfIssuesEnabledCache
if (cached == null) {
cached = currentProvider.perfIssuesEnabled()
accessedFeatureFlags.add("perfIssuesEnabled")
perfIssuesEnabledCache = cached
}
return cached
}
override fun perfMonitorV2Enabled(): Boolean {
var cached = perfMonitorV2EnabledCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4464982256b8ce543d9235fccbf67054>>
* @generated SignedSource<<f82b5905d822f8009c054a9443f86e39>>
*/
/**
@@ -27,8 +27,6 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android : ReactN
override fun enableFabricRenderer(): Boolean = true
override fun enableWebPerformanceAPIsByDefault(): Boolean = true
override fun useNativeViewConfigsInBridgelessMode(): Boolean = true
override fun useTurboModuleInterop(): Boolean = true
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<692dcdb2af3c6af981e8f48686ca105e>>
* @generated SignedSource<<6b61490a8d6b1df1d6264016455382f8>>
*/
/**
@@ -83,6 +83,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@DoNotStrip public fun enableKeyEvents(): Boolean
@DoNotStrip public fun enableLayoutAnimationsOnAndroid(): Boolean
@DoNotStrip public fun enableLayoutAnimationsOnIOS(): Boolean
@@ -115,6 +117,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableViewRecyclingForView(): Boolean
@DoNotStrip public fun enableVirtualViewClippingWithoutScrollViewClipping(): Boolean
@DoNotStrip public fun enableVirtualViewContainerStateExperimental(): Boolean
@DoNotStrip public fun enableVirtualViewDebugFeatures(): Boolean
@@ -135,6 +139,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean
@DoNotStrip public fun perfIssuesEnabled(): Boolean
@DoNotStrip public fun perfMonitorV2Enabled(): Boolean
@DoNotStrip public fun preparedTextCacheSize(): Double
@@ -0,0 +1,109 @@
/*
* 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.internal.tracing
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.soloader.SoLoader
/**
* JNI interface to access PerformanceTracer methods from React Native Inspector Modern.
*
* This provides access to reportMark and reportMeasure methods which are used for performance
* tracing in the React Native DevTools.
*/
@DoNotStrip
public object PerformanceTracer {
init {
SoLoader.loadLibrary("react_performancetracerjni")
}
/** Callback interface for tracing state changes. */
@DoNotStrip
public interface TracingStateCallback {
/**
* Called when tracing state changes.
*
* @param isTracing true if tracing has started, false if tracing has stopped
*/
@DoNotStrip public fun onTracingStateChanged(isTracing: Boolean)
}
/**
* Report a Performance.mark() event - a labelled timestamp.
*
* @param name The name/label of the mark
* @param timestampNanos The timestamp in nanoseconds (monotonic time)
* @param detail Optional map with additional detail (pass null if not needed)
*/
@DoNotStrip
@JvmStatic
public external fun reportMark(name: String, timestampNanos: Long, detail: ReadableNativeMap?)
/**
* Report a Performance.measure() event - a labelled duration.
*
* @param name The name/label of the measure
* @param startTimestampNanos The start timestamp in nanoseconds (monotonic time)
* @param durationNanos The duration in nanoseconds
* @param detail Optional map with additional detail (pass null if not needed)
*/
@DoNotStrip
@JvmStatic
public external fun reportMeasure(
name: String,
startTimestampNanos: Long,
durationNanos: Long,
detail: ReadableNativeMap?,
)
/**
* Report a TimeStamp Trace Event - a labelled entry on Performance timeline.
*
* @param name The name/label of the timestamp
* @param startTimeNanos Start timestamp in nanoseconds (monotonic time)
* @param endTimeNanos End timestamp in nanoseconds (monotonic time)
* @param trackName Optional track name for organizing the timestamp
* @param trackGroup Optional track group for organizing the timestamp
* @param color Optional color name (e.g., "primary", "secondary", "error", "warning")
*/
@DoNotStrip
@JvmStatic
public external fun reportTimeStamp(
name: String,
startTimeNanos: Long,
endTimeNanos: Long,
trackName: String?,
trackGroup: String?,
color: String?,
)
/**
* Check if the tracer is currently tracing.
*
* @return true if tracing is active, false otherwise
*/
@DoNotStrip @JvmStatic public external fun isTracing(): Boolean
/**
* Subscribe to tracing state changes (start/stop events).
*
* @param callback The callback to invoke when tracing starts or stops
* @return A subscription ID that can be used to unsubscribe
*/
@DoNotStrip
@JvmStatic
public external fun subscribeToTracingStateChanges(callback: TracingStateCallback): Int
/**
* Unsubscribe from tracing state changes.
*
* @param subscriptionId The subscription ID returned from subscribeToTracingStateChanges
*/
@DoNotStrip @JvmStatic public external fun unsubscribeFromTracingStateChanges(subscriptionId: Int)
}
@@ -74,6 +74,12 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
}
}
fun handleNativePerfIssueAdded(
name: String,
) {
perfMonitorListeners.forEach { listener -> listener.onPerfIssueAdded(name) }
}
override fun close() {
mHybridData.resetNative()
}
@@ -45,12 +45,22 @@ import com.facebook.react.uimanager.style.LogicalEdge
import com.facebook.react.uimanager.style.OutlineStyle
/**
* BackgroundStyleApplicator is responsible for applying backgrounds, borders, and related effects,
* to an Android view
* Utility object responsible for applying backgrounds, borders, and related visual effects to
* Android views.
*
* This object provides methods to manage background colors, images, borders, outlines, and box
* shadows for React Native views. It handles the complex layering and composition of these visual
* properties by managing [CompositeBackgroundDrawable] instances.
*/
@OptIn(UnstableReactNativeAPI::class)
public object BackgroundStyleApplicator {
/**
* Sets the background color of the view.
*
* @param view The view to apply the background color to
* @param color The color to set, or null to remove the background color
*/
@JvmStatic
public fun setBackgroundColor(view: View, @ColorInt color: Int?): Unit {
// No color to set, and no color already set
@@ -64,6 +74,12 @@ public object BackgroundStyleApplicator {
ensureBackgroundDrawable(view).backgroundColor = color ?: Color.TRANSPARENT
}
/**
* Sets the background image layers for the view.
*
* @param view The view to apply the background images to
* @param backgroundImageLayers The list of background image layers to apply, or null to remove
*/
@JvmStatic
public fun setBackgroundImage(
view: View,
@@ -90,12 +106,25 @@ public object BackgroundStyleApplicator {
ensureBackgroundImageDrawable(view).backgroundRepeat = backgroundRepeats
}
/**
* Gets the background color of the view.
*
* @param view The view to get the background color from
* @return The background color, or null if no background color is set
*/
@JvmStatic
@ColorInt
public fun getBackgroundColor(view: View): Int? {
return getBackground(view)?.backgroundColor
}
/**
* Sets the border width for a specific edge of the view.
*
* @param view The view to apply the border width to
* @param edge The logical edge (start, end, top, bottom, etc.) to set the width for
* @param width The border width in DIPs, or null to remove
*/
@JvmStatic
public fun setBorderWidth(view: View, edge: LogicalEdge, width: Float?): Unit {
val composite = ensureCompositeBackgroundDrawable(view)
@@ -121,6 +150,13 @@ public object BackgroundStyleApplicator {
}
}
/**
* Gets the border width for a specific edge of the view.
*
* @param view The view to get the border width from
* @param edge The logical edge to get the width for
* @return The border width in DIPs, or null if not set
*/
@JvmStatic
public fun getBorderWidth(view: View, edge: LogicalEdge): Float? {
val width = getBorder(view)?.borderWidth?.getRaw(edge.toSpacingType())
@@ -131,17 +167,38 @@ public object BackgroundStyleApplicator {
}
}
/**
* Sets the border color for a specific edge of the view.
*
* @param view The view to apply the border color to
* @param edge The logical edge to set the color for
* @param color The border color, or null to remove
*/
@JvmStatic
public fun setBorderColor(view: View, edge: LogicalEdge, @ColorInt color: Int?): Unit {
ensureBorderDrawable(view).setBorderColor(edge, color)
}
/**
* Gets the border color for a specific edge of the view.
*
* @param view The view to get the border color from
* @param edge The logical edge to get the color for
* @return The border color, or null if not set
*/
@JvmStatic
@ColorInt
public fun getBorderColor(view: View, edge: LogicalEdge): Int? {
return getBorder(view)?.getBorderColor(edge)
}
/**
* Sets the border radius for a specific corner of the view.
*
* @param view The view to apply the border radius to
* @param corner The corner property to set the radius for
* @param radius The border radius value (length or percentage), or null to remove
*/
@JvmStatic
public fun setBorderRadius(
view: View,
@@ -183,22 +240,47 @@ public object BackgroundStyleApplicator {
compositeBackgroundDrawable.invalidateSelf()
}
/**
* Gets the border radius for a specific corner of the view.
*
* @param view The view to get the border radius from
* @param corner The corner property to get the radius for
* @return The border radius value, or null if not set
*/
@JvmStatic
public fun getBorderRadius(view: View, corner: BorderRadiusProp): LengthPercentage? {
return getCompositeBackgroundDrawable(view)?.borderRadius?.get(corner)
}
/**
* Sets the border style for the view.
*
* @param view The view to apply the border style to
* @param borderStyle The border style (solid, dashed, dotted), or null to remove
*/
@JvmStatic
public fun setBorderStyle(view: View, borderStyle: BorderStyle?) {
ensureBorderDrawable(view).borderStyle = borderStyle
}
/**
* Gets the border style of the view.
*
* @param view The view to get the border style from
* @return The border style, or null if not set
*/
@JvmStatic
public fun getBorderStyle(view: View): BorderStyle? {
return getBorder(view)?.borderStyle
}
/**
* Sets the outline color for the view (Fabric only).
*
* @param view The view to apply the outline color to
* @param outlineColor The outline color, or null to remove
*/
@JvmStatic
public fun setOutlineColor(view: View, @ColorInt outlineColor: Int?) {
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC) {
@@ -211,8 +293,20 @@ public object BackgroundStyleApplicator {
}
}
/**
* Gets the outline color of the view.
*
* @param view The view to get the outline color from
* @return The outline color, or null if not set
*/
@JvmStatic public fun getOutlineColor(view: View): Int? = getOutlineDrawable(view)?.outlineColor
/**
* Sets the outline offset for the view (Fabric only).
*
* @param view The view to apply the outline offset to
* @param outlineOffset The outline offset in DIPs
*/
@JvmStatic
public fun setOutlineOffset(view: View, outlineOffset: Float): Unit {
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC) {
@@ -223,8 +317,20 @@ public object BackgroundStyleApplicator {
outline.outlineOffset = outlineOffset.dpToPx()
}
/**
* Gets the outline offset of the view.
*
* @param view The view to get the outline offset from
* @return The outline offset in pixels, or null if not set
*/
public fun getOutlineOffset(view: View): Float? = getOutlineDrawable(view)?.outlineOffset
/**
* Sets the outline style for the view (Fabric only).
*
* @param view The view to apply the outline style to
* @param outlineStyle The outline style (solid, dashed, dotted), or null to remove
*/
@JvmStatic
public fun setOutlineStyle(view: View, outlineStyle: OutlineStyle?): Unit {
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC) {
@@ -237,8 +343,20 @@ public object BackgroundStyleApplicator {
}
}
/**
* Gets the outline style of the view.
*
* @param view The view to get the outline style from
* @return The outline style, or null if not set
*/
public fun getOutlineStyle(view: View): OutlineStyle? = getOutlineDrawable(view)?.outlineStyle
/**
* Sets the outline width for the view (Fabric only).
*
* @param view The view to apply the outline width to
* @param width The outline width in DIPs
*/
@JvmStatic
public fun setOutlineWidth(view: View, width: Float) {
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC) {
@@ -249,8 +367,20 @@ public object BackgroundStyleApplicator {
outline.outlineWidth = width.dpToPx()
}
/**
* Gets the outline width of the view.
*
* @param view The view to get the outline width from
* @return The outline width in pixels, or null if not set
*/
public fun getOutlineWidth(view: View): Float? = getOutlineDrawable(view)?.outlineOffset
/**
* Sets box shadows for the view (Fabric only).
*
* @param view The view to apply box shadows to
* @param shadows The list of box shadow styles to apply
*/
@JvmStatic
public fun setBoxShadow(view: View, shadows: List<BoxShadow>) {
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC) {
@@ -309,6 +439,12 @@ public object BackgroundStyleApplicator {
.withNewShadows(outerShadows = outerShadows, innerShadows = innerShadows)
}
/**
* Sets box shadows for the view from a ReadableArray (Fabric only).
*
* @param view The view to apply box shadows to
* @param shadows The array of box shadow definitions, or null to remove all shadows
*/
@JvmStatic
public fun setBoxShadow(view: View, shadows: ReadableArray?) {
if (shadows == null) {
@@ -323,11 +459,26 @@ public object BackgroundStyleApplicator {
BackgroundStyleApplicator.setBoxShadow(view, shadowStyles)
}
/**
* Sets a feedback underlay drawable for the view.
*
* @param view The view to apply the feedback underlay to
* @param drawable The drawable to use as feedback underlay, or null to remove
*/
@JvmStatic
public fun setFeedbackUnderlay(view: View, drawable: Drawable?) {
ensureCompositeBackgroundDrawable(view).withNewFeedbackUnderlay(drawable)
}
/**
* Clips the canvas to the padding box of the view.
*
* The padding box is the area within the borders of the view, accounting for border radius if
* present.
*
* @param view The view whose padding box defines the clipping region
* @param canvas The canvas to clip
*/
@JvmStatic
public fun clipToPaddingBox(view: View, canvas: Canvas) {
val drawingRect = Rect()
@@ -366,6 +517,13 @@ public object BackgroundStyleApplicator {
}
}
/**
* Resets the background styling of the view to its original state.
*
* This removes any CompositeBackgroundDrawable and restores the original background.
*
* @param view The view to reset
*/
@JvmStatic
public fun reset(view: View) {
if (view.background is CompositeBackgroundDrawable) {
@@ -14,10 +14,23 @@ import android.view.ViewGroup
import androidx.core.view.children
import com.facebook.react.R
/**
* Helper object for parsing and handling CSS mix-blend-mode properties on Android Q+ (API 29+).
*
* This object provides utilities to convert CSS mix-blend-mode string values into Android's
* [BlendMode] enumeration and to determine when views need isolated layers for proper blending.
*/
@TargetApi(29)
internal object BlendModeHelper {
/** @see <a href="https://www.w3.org/TR/compositing-1/#mix-blend-mode">mix-blend-mode</a> */
/**
* Parses a CSS mix-blend-mode string value into an Android BlendMode.
*
* @param mixBlendMode The CSS mix-blend-mode value (e.g., "multiply", "screen", "overlay")
* @return The corresponding [BlendMode], or null if "normal" or on unsupported Android versions
* @throws IllegalArgumentException if the mix-blend-mode name is invalid
* @see <a href="https://www.w3.org/TR/compositing-1/#mix-blend-mode">mix-blend-mode</a>
*/
@JvmStatic
fun parseMixBlendMode(mixBlendMode: String?): BlendMode? {
if (mixBlendMode == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
@@ -9,8 +9,18 @@ package com.facebook.react.uimanager
import com.facebook.proguard.annotations.DoNotStripAny
/**
* Interface for resolving component names registered in React Native.
*
* This interface provides access to all registered component names in the React Native runtime,
* which is useful for debugging and inspection purposes.
*/
@DoNotStripAny
internal interface ComponentNameResolver {
/* returns a list of all the component names that are registered in React Native. */
/**
* Gets the list of all component names registered in React Native.
*
* @return An array of all registered component names
*/
val componentNames: Array<String>
}
@@ -12,11 +12,23 @@ import com.facebook.react.bridge.RuntimeExecutor
import com.facebook.soloader.SoLoader
import kotlin.jvm.JvmStatic
/**
* JNI binding for installing a ComponentNameResolver into the JavaScript runtime.
*
* This object provides a native binding to install a [ComponentNameResolver] into the React Native
* JavaScript runtime, making component name information available to the native side.
*/
@DoNotStripAny
internal object ComponentNameResolverBinding {
init {
SoLoader.loadLibrary("uimanagerjni")
}
/**
* Installs a ComponentNameResolver into the JavaScript runtime.
*
* @param runtimeExecutor The runtime executor for the JavaScript runtime
* @param componentNameResolver The component name resolver to install
*/
@JvmStatic external fun install(runtimeExecutor: RuntimeExecutor, componentNameResolver: Any)
}
@@ -21,9 +21,25 @@ import com.facebook.react.uimanager.PixelUtil.dpToPx
import kotlin.math.cos
import kotlin.math.sin
/**
* Helper object for parsing and applying CSS filter effects on Android S+ (API 31+).
*
* This object provides utilities to convert CSS filter arrays into Android [RenderEffect] chains
* and [ColorMatrixColorFilter] objects. Supports filters like blur, brightness, contrast,
* grayscale, sepia, saturate, hue-rotate, invert, opacity, and drop-shadow.
*
* @see <a href="https://www.w3.org/TR/filter-effects-1/">CSS Filter Effects Module Level 1</a>
*/
@TargetApi(31)
internal object FilterHelper {
/**
* Parses a ReadableArray of CSS filter definitions into a chained [RenderEffect].
*
* @param filters The array of filter definitions, or null
* @return A chained RenderEffect representing all filters, or null if no filters provided
* @throws IllegalArgumentException if an invalid filter name is encountered
*/
@JvmStatic
fun parseFilters(filters: ReadableArray?): RenderEffect? {
filters ?: return null
@@ -9,9 +9,30 @@ package com.facebook.react.uimanager
import kotlin.math.abs
/**
* Utility class for comparing floating-point numbers with epsilon-based equality.
*
* This class provides methods to compare float values while accounting for floating-point precision
* issues. Direct equality comparison of floats can be unreliable due to rounding errors in
* floating-point arithmetic. This utility uses an epsilon threshold to determine if two float
* values are "close enough" to be considered equal.
*/
public object FloatUtil {
private const val EPSILON = .00001f
/**
* Compares two float values for equality using an epsilon threshold.
*
* This method handles special cases:
* - If both values are NaN, they are considered equal
* - If only one value is NaN, they are not equal
* - Otherwise, values are equal if their absolute difference is less than [EPSILON]
*
* @param f1 the first float value to compare
* @param f2 the second float value to compare
* @return `true` if the values are equal within the epsilon threshold, `false` otherwise
*/
@JvmStatic
public fun floatsEqual(f1: Float, f2: Float): Boolean {
return if (f1.isNaN() || f2.isNaN()) {
@@ -19,6 +40,18 @@ public object FloatUtil {
} else abs(f2 - f1) < EPSILON
}
/**
* Compares two nullable float values for equality using an epsilon threshold.
*
* This method handles null values:
* - If both values are null, they are considered equal
* - If only one value is null, they are not equal
* - If both values are non-null, delegates to [floatsEqual] with non-null parameters
*
* @param f1 the first nullable float value to compare
* @param f2 the second nullable float value to compare
* @return `true` if the values are equal (both null or equal within epsilon), `false` otherwise
*/
@JvmStatic
public fun floatsEqual(f1: Float?, f2: Float?): Boolean {
if (f1 == null) {
@@ -14,15 +14,15 @@ import androidx.annotation.ColorInt
import com.facebook.react.modules.i18nmanager.I18nUtil
internal data class ColorEdges(
@ColorInt val left: Int = Color.BLACK,
@ColorInt val top: Int = Color.BLACK,
@ColorInt val right: Int = Color.BLACK,
@ColorInt val bottom: Int = Color.BLACK,
@param:ColorInt val left: Int = Color.BLACK,
@param:ColorInt val top: Int = Color.BLACK,
@param:ColorInt val right: Int = Color.BLACK,
@param:ColorInt val bottom: Int = Color.BLACK,
)
@JvmInline
internal value class BorderColors(
@ColorInt val edgeColors: Array<Int?> = arrayOfNulls<Int?>(LogicalEdge.values().size)
@param:ColorInt val edgeColors: Array<Int?> = arrayOfNulls<Int?>(LogicalEdge.values().size)
) {
fun resolve(layoutDirection: Int, context: Context): ColorEdges {
@@ -18,7 +18,7 @@ import com.facebook.react.bridge.ReadableType
public data class BoxShadow(
val offsetX: Float,
val offsetY: Float,
@ColorInt val color: Int? = null,
@param:ColorInt val color: Int? = null,
val blurRadius: Float? = null,
val spreadDistance: Float? = null,
val inset: Boolean? = null,
@@ -83,10 +83,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
HasSmoothScroll,
VirtualViewContainer {
private static boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG;
private static String TAG = ReactHorizontalScrollView.class.getSimpleName();
private static final boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG;
private static final String TAG = ReactHorizontalScrollView.class.getSimpleName();
private static int NO_SCROLL_POSITION = Integer.MIN_VALUE;
private static final int NO_SCROLL_POSITION = Integer.MIN_VALUE;
private static @Nullable Field sScrollerField;
private static boolean sTriedToGetScrollerField = false;
@@ -542,6 +542,15 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
if (focused != null && !mPagingEnabled) {
scrollToChild(focused);
}
requestChildFocusWithoutScroll(child, focused);
}
/**
* In rare cases where an app overrides the built-in ReactScrollView by overriding it, and also
* needs to customize scroll into view on focus behaviors, this protected method can be used to
* unblocks such customization.
*/
protected void requestChildFocusWithoutScroll(View child, View focused) {
super.requestChildFocus(child, focused);
}
@@ -511,6 +511,15 @@ public class ReactScrollView extends ScrollView
if (focused != null) {
scrollToChild(focused);
}
requestChildFocusWithoutScroll(child, focused);
}
/**
* In rare cases where an app overrides the built-in ReactScrollView by overriding it, and also
* needs to customize scroll into view on focus behaviors, this protected method can be used to
* unblocks such customization.
*/
protected void requestChildFocusWithoutScroll(View child, View focused) {
super.requestChildFocus(child, focused);
}
@@ -153,6 +153,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
private var accessibilityStateChangeListener:
AccessibilityManager.AccessibilityStateChangeListener? =
null
private var focusOnAttach = false
init {
initView()
@@ -212,6 +213,9 @@ public open class ReactViewGroup public constructor(context: Context?) :
updateBackgroundDrawable(null)
resetPointerEvents()
// In case a focus was attempted but the view never attached, reset to false
focusOnAttach = false
}
private var _drawingOrderHelper: ViewGroupDrawingOrderHelper? = null
@@ -417,10 +421,15 @@ public open class ReactViewGroup public constructor(context: Context?) :
}
internal fun requestFocusFromJS() {
super.requestFocus(FOCUS_DOWN, null)
if (isAttachedToWindow) {
super.requestFocus(FOCUS_DOWN, null)
} else {
focusOnAttach = true
}
}
internal fun clearFocusFromJS() {
focusOnAttach = false
super.clearFocus()
}
@@ -572,6 +581,11 @@ public open class ReactViewGroup public constructor(context: Context?) :
if (_removeClippedSubviews) {
updateClippingRect()
}
if (focusOnAttach) {
requestFocusFromJS()
focusOnAttach = false
}
}
private fun customDrawOrderDisabled(): Boolean {
@@ -187,17 +187,28 @@ public class ReactVirtualView(context: Context) :
}
// If no ScrollView, or ScrollView has disabled removeClippedSubviews, use default behavior
if (
parentScrollView == null ||
!((parentScrollView as ReactClippingViewGroup).removeClippedSubviews ?: false)
) {
if (parentScrollView == null) {
super.updateClippingRect(excludedViews)
return
}
val clippingRect = checkNotNull(clippingRect)
val parentScrollView = checkNotNull(parentScrollView) as ReactClippingViewGroup
if (ReactNativeFeatureFlags.enableVirtualViewClippingWithoutScrollViewClipping()) {
if (parentScrollView.removeClippedSubviews) {
parentScrollView.getClippingRect(clippingRect)
} else {
(parentScrollView as View).getDrawingRect(clippingRect)
}
} else {
if (!(parentScrollView.removeClippedSubviews ?: false)) {
super.updateClippingRect(excludedViews)
return
}
parentScrollView.getClippingRect(clippingRect)
}
(parentScrollView as ReactClippingViewGroup).getClippingRect(clippingRect)
clippingRect.intersect(targetRect)
clippingRect.offset(-targetRect.left, -targetRect.top)
@@ -208,17 +208,29 @@ public class ReactVirtualViewExperimental(context: Context) :
}
// If no ScrollView, or ScrollView has disabled removeClippedSubviews, use default behavior
if (
scrollView == null ||
!((scrollView as ReactClippingViewGroup).removeClippedSubviews ?: false)
) {
if (scrollView == null) {
super.updateClippingRect(excludedViews)
return
}
val clippingRect = checkNotNull(clippingRect)
val scrollView = checkNotNull(scrollView) as ReactClippingViewGroup
if (ReactNativeFeatureFlags.enableVirtualViewClippingWithoutScrollViewClipping()) {
if (scrollView.removeClippedSubviews) {
scrollView.getClippingRect(clippingRect)
} else {
(scrollView as View).getDrawingRect(clippingRect)
}
} else {
if (!(scrollView.removeClippedSubviews ?: false)) {
super.updateClippingRect(excludedViews)
return
}
scrollView.getClippingRect(clippingRect)
}
(scrollView as ReactClippingViewGroup).getClippingRect(clippingRect)
clippingRect.intersect(containerRelativeRect)
clippingRect.offset(-containerRelativeRect.left, -containerRelativeRect.top)
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<48a05f5360c927150021af873e6d52b8>>
* @generated SignedSource<<13ed11b5c260fae79048ea80745d22fe>>
*/
/**
@@ -219,6 +219,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableKeyEvents() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableKeyEvents");
return method(javaProvider_);
}
bool enableLayoutAnimationsOnAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableLayoutAnimationsOnAndroid");
@@ -315,6 +321,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableVirtualViewClippingWithoutScrollViewClipping() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableVirtualViewClippingWithoutScrollViewClipping");
return method(javaProvider_);
}
bool enableVirtualViewContainerStateExperimental() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableVirtualViewContainerStateExperimental");
@@ -375,6 +387,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool perfIssuesEnabled() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("perfIssuesEnabled");
return method(javaProvider_);
}
bool perfMonitorV2Enabled() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("perfMonitorV2Enabled");
@@ -673,6 +691,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableInteropViewManagerClassLookUpOpti
return ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS();
}
bool JReactNativeFeatureFlagsCxxInterop::enableKeyEvents(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableKeyEvents();
}
bool JReactNativeFeatureFlagsCxxInterop::enableLayoutAnimationsOnAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid();
@@ -753,6 +776,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForView(
return ReactNativeFeatureFlags::enableViewRecyclingForView();
}
bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewClippingWithoutScrollViewClipping(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableVirtualViewClippingWithoutScrollViewClipping();
}
bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperimental(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental();
@@ -803,6 +831,11 @@ bool JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMounti
return ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::perfIssuesEnabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::perfIssuesEnabled();
}
bool JReactNativeFeatureFlagsCxxInterop::perfMonitorV2Enabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::perfMonitorV2Enabled();
@@ -1044,6 +1077,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableInteropViewManagerClassLookUpOptimizationIOS",
JReactNativeFeatureFlagsCxxInterop::enableInteropViewManagerClassLookUpOptimizationIOS),
makeNativeMethod(
"enableKeyEvents",
JReactNativeFeatureFlagsCxxInterop::enableKeyEvents),
makeNativeMethod(
"enableLayoutAnimationsOnAndroid",
JReactNativeFeatureFlagsCxxInterop::enableLayoutAnimationsOnAndroid),
@@ -1092,6 +1128,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableViewRecyclingForView",
JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForView),
makeNativeMethod(
"enableVirtualViewClippingWithoutScrollViewClipping",
JReactNativeFeatureFlagsCxxInterop::enableVirtualViewClippingWithoutScrollViewClipping),
makeNativeMethod(
"enableVirtualViewContainerStateExperimental",
JReactNativeFeatureFlagsCxxInterop::enableVirtualViewContainerStateExperimental),
@@ -1122,6 +1161,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"overrideBySynchronousMountPropsAtMountingAndroid",
JReactNativeFeatureFlagsCxxInterop::overrideBySynchronousMountPropsAtMountingAndroid),
makeNativeMethod(
"perfIssuesEnabled",
JReactNativeFeatureFlagsCxxInterop::perfIssuesEnabled),
makeNativeMethod(
"perfMonitorV2Enabled",
JReactNativeFeatureFlagsCxxInterop::perfMonitorV2Enabled),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e8bbe1de8eccb3ecea6b9f001de07fd2>>
* @generated SignedSource<<1e57bf8252c16fc10bbfcae08710af85>>
*/
/**
@@ -120,6 +120,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableInteropViewManagerClassLookUpOptimizationIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableKeyEvents(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableLayoutAnimationsOnAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -168,6 +171,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableViewRecyclingForView(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableVirtualViewClippingWithoutScrollViewClipping(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableVirtualViewContainerStateExperimental(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -198,6 +204,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool overrideBySynchronousMountPropsAtMountingAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool perfIssuesEnabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool perfMonitorV2Enabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -120,6 +120,13 @@ void JReactHostInspectorTarget::onSetPausedInDebuggerMessage(
}
}
void JReactHostInspectorTarget::unstable_onPerfIssueAdded(
const PerfIssuePayload& issue) {
static auto method = javaClassStatic()->getMethod<void(local_ref<jstring>)>(
"handleNativePerfIssueAdded");
method(jobj_, make_jstring(issue.name));
}
void JReactHostInspectorTarget::loadNetworkResource(
const jsinspector_modern::LoadNetworkResourceRequest& params,
jsinspector_modern::ScopedExecutor<
@@ -103,6 +103,7 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
jsinspector_modern::HostTargetMetadata getMetadata() override;
void onReload(const PageReloadRequest &request) override;
void onSetPausedInDebuggerMessage(const OverlaySetPausedInDebuggerMessageRequest &request) override;
void unstable_onPerfIssueAdded(const jsinspector_modern::PerfIssuePayload &issue) override;
void loadNetworkResource(
const jsinspector_modern::LoadNetworkResourceRequest &params,
jsinspector_modern::ScopedExecutor<jsinspector_modern::NetworkRequestListener> executor) override;
@@ -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.
*/
#include "PerformanceTracerCxxInterop.h"
#include <fbjni/fbjni.h>
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*unused*/) {
return facebook::jni::initialize(vm, [] {
facebook::react::PerformanceTracerCxxInterop::registerNatives();
});
}
@@ -0,0 +1,241 @@
/*
* 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.
*/
#include "PerformanceTracerCxxInterop.h"
#include <folly/dynamic.h>
#include <jsinspector-modern/tracing/PerformanceTracer.h>
#include <react/jni/NativeMap.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/timing/primitives.h>
#include <map>
#include <memory>
#include <mutex>
namespace facebook::react {
void TracingStateCallback::onTracingStateChanged(bool isTracing) const {
static const auto method =
javaClassStatic()->getMethod<void(jboolean)>("onTracingStateChanged");
method(self(), static_cast<jboolean>(isTracing));
}
namespace {
class TracingCallbackRegistry {
public:
static TracingCallbackRegistry& getInstance() {
static TracingCallbackRegistry instance;
return instance;
}
void registerCallback(
uint32_t subscriptionId,
jni::global_ref<jobject> callback) {
std::lock_guard<std::mutex> lock(mutex_);
callbacks_[subscriptionId] = std::move(callback);
}
void unregisterCallback(uint32_t subscriptionId) {
std::lock_guard<std::mutex> lock(mutex_);
callbacks_.erase(subscriptionId);
}
jni::global_ref<jobject> getCallback(uint32_t subscriptionId) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = callbacks_.find(subscriptionId);
if (it != callbacks_.end()) {
return it->second;
}
return nullptr;
}
private:
std::mutex mutex_;
std::map<uint32_t, jni::global_ref<jobject>> callbacks_;
};
} // namespace
void PerformanceTracerCxxInterop::reportMark(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong timestampNanos,
jni::alias_ref<ReadableNativeMap::javaobject> detail) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
HighResTimeStamp timestamp = HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(timestampNanos)));
folly::dynamic detailDynamic = nullptr;
if (detail) {
detailDynamic = detail->cthis()->consume();
}
tracer.reportMark(nameStr, timestamp, std::move(detailDynamic));
}
void PerformanceTracerCxxInterop::reportMeasure(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong startTimestampNanos,
jlong durationNanos,
jni::alias_ref<ReadableNativeMap::javaobject> detail) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
HighResTimeStamp startTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(startTimestampNanos)));
HighResDuration duration = HighResDuration::fromNanoseconds(durationNanos);
folly::dynamic detailDynamic = nullptr;
if (detail) {
detailDynamic = detail->cthis()->consume();
}
tracer.reportMeasure(
nameStr, startTimestamp, duration, std::move(detailDynamic));
}
void PerformanceTracerCxxInterop::reportTimeStamp(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong startTimeNanos,
jlong endTimeNanos,
jni::alias_ref<jstring> trackName,
jni::alias_ref<jstring> trackGroup,
jni::alias_ref<jstring> color) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
// Convert start timestamp
HighResTimeStamp startTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(startTimeNanos)));
// Convert end timestamp
HighResTimeStamp endTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(endTimeNanos)));
// Handle optional string parameters
std::optional<std::string> trackNameOpt = std::nullopt;
if (trackName) {
trackNameOpt = trackName->toStdString();
}
std::optional<std::string> trackGroupOpt = std::nullopt;
if (trackGroup) {
trackGroupOpt = trackGroup->toStdString();
}
// Handle optional color parameter
std::optional<jsinspector_modern::tracing::ConsoleTimeStampColor> colorOpt =
std::nullopt;
if (color) {
colorOpt = jsinspector_modern::tracing::getConsoleTimeStampColorFromString(
color->toStdString());
}
tracer.reportTimeStamp(
nameStr,
startTimestamp,
endTimestamp,
trackNameOpt,
trackGroupOpt,
colorOpt);
}
jboolean PerformanceTracerCxxInterop::isTracing(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/) {
return static_cast<jboolean>(
jsinspector_modern::tracing::PerformanceTracer::getInstance()
.isTracing());
}
jint PerformanceTracerCxxInterop::subscribeToTracingStateChanges(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<TracingStateCallback::javaobject> callback) {
auto globalCallback = jni::make_global(callback);
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
auto callbackWrapper = std::make_shared<uint32_t>(0);
uint32_t subscriptionId =
tracer.subscribeToTracingStateChanges([callbackWrapper](bool isTracing) {
auto callback = TracingCallbackRegistry::getInstance().getCallback(
*callbackWrapper);
if (callback) {
try {
jni::ThreadScope::WithClassLoader([&] {
auto callbackClass = callback->getClass();
auto onTracingStateChangedMethod =
callbackClass->getMethod<void(jboolean)>(
"onTracingStateChanged");
onTracingStateChangedMethod(
callback, static_cast<jboolean>(isTracing));
});
} catch (const std::exception& e) {
}
}
});
*callbackWrapper = subscriptionId;
TracingCallbackRegistry::getInstance().registerCallback(
subscriptionId, globalCallback);
return static_cast<jint>(subscriptionId);
}
void PerformanceTracerCxxInterop::unsubscribeFromTracingStateChanges(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jint subscriptionId) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
tracer.unsubscribeFromTracingStateChanges(
static_cast<uint32_t>(subscriptionId));
TracingCallbackRegistry::getInstance().unregisterCallback(
static_cast<uint32_t>(subscriptionId));
}
void PerformanceTracerCxxInterop::registerNatives() {
javaClassLocal()->registerNatives(
{makeNativeMethod("reportMark", PerformanceTracerCxxInterop::reportMark),
makeNativeMethod(
"reportMeasure", PerformanceTracerCxxInterop::reportMeasure),
makeNativeMethod(
"reportTimeStamp", PerformanceTracerCxxInterop::reportTimeStamp),
makeNativeMethod("isTracing", PerformanceTracerCxxInterop::isTracing),
makeNativeMethod(
"subscribeToTracingStateChanges",
PerformanceTracerCxxInterop::subscribeToTracingStateChanges),
makeNativeMethod(
"unsubscribeFromTracingStateChanges",
PerformanceTracerCxxInterop::unsubscribeFromTracingStateChanges)});
}
} // namespace facebook::react
@@ -0,0 +1,63 @@
/*
* 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 <fbjni/fbjni.h>
#include <jni.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook::react {
class TracingStateCallback : public facebook::jni::JavaClass<TracingStateCallback> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/internal/tracing/PerformanceTracer$TracingStateCallback;";
void onTracingStateChanged(bool isTracing) const;
};
class PerformanceTracerCxxInterop : public facebook::jni::JavaClass<PerformanceTracerCxxInterop> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/internal/tracing/PerformanceTracer;";
static void reportMark(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong timestampNanos,
facebook::jni::alias_ref<ReadableNativeMap::javaobject> detail);
static void reportMeasure(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong startTimestampNanos,
jlong durationNanos,
facebook::jni::alias_ref<ReadableNativeMap::javaobject> detail);
static void reportTimeStamp(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong startTimeNanos,
jlong endTimeNanos,
facebook::jni::alias_ref<jstring> trackName,
facebook::jni::alias_ref<jstring> trackGroup,
facebook::jni::alias_ref<jstring> color);
static jboolean isTracing(facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis);
static jint subscribeToTracingStateChanges(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<TracingStateCallback::javaobject> callback);
static void unsubscribeFromTracingStateChanges(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
jint subscriptionId);
static void registerNatives();
};
} // namespace facebook::react
@@ -53,6 +53,14 @@ import UIKit
containerViewModel.saturationAmount = CGFloat(saturation.floatValue)
}
@objc public func updateContrast(_ contrast: NSNumber) {
containerViewModel.contrastAmount = CGFloat(contrast.floatValue)
}
@objc public func updateHueRotate(_ degrees: NSNumber) {
containerViewModel.hueRotationDegrees = CGFloat(degrees.floatValue)
}
@objc public func updateLayout(withBounds bounds: CGRect) {
hostingController?.view.frame = bounds
containerViewModel.contentView?.frame = bounds
@@ -66,6 +74,8 @@ import UIKit
containerViewModel.shadowY = 0
containerViewModel.shadowColor = Color.clear
containerViewModel.saturationAmount = 1
containerViewModel.contrastAmount = 1
containerViewModel.hueRotationDegrees = 0
}
}
@@ -85,6 +95,12 @@ class ContainerViewModel: ObservableObject {
// saturation filter properties
@Published var saturationAmount: CGFloat = 1
// contrast filter properties
@Published var contrastAmount: CGFloat = 1
// hue-rotate filter properties
@Published var hueRotationDegrees: CGFloat = 0
@Published var contentView: UIView?
}
@@ -98,6 +114,8 @@ struct SwiftUIContainerView: View {
.grayscale(viewModel.grayscale)
.shadow(color: viewModel.shadowColor, radius: viewModel.shadowRadius, x: viewModel.shadowX, y: viewModel.shadowY)
.saturation(viewModel.saturationAmount)
.contrast(viewModel.contrastAmount)
.hueRotation(.degrees(viewModel.hueRotationDegrees))
}
}
}
@@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateGrayscale:(NSNumber *)grayscale;
- (void)updateDropShadow:(NSNumber *)standardDeviation x:(NSNumber *)x y:(NSNumber *)y color:(UIColor *)color;
- (void)updateSaturation:(NSNumber *)saturation;
- (void)updateContrast:(NSNumber *)contrast;
- (void)updateHueRotate:(NSNumber *)degrees;
- (void)updateContentView:(UIView *)view;
- (UIView *_Nullable)hostingView;
- (void)resetStyles;
@@ -58,6 +58,16 @@
[self.swiftContainerView updateSaturation:saturation];
}
- (void)updateContrast:(NSNumber *)contrast
{
[self.swiftContainerView updateContrast:contrast];
}
- (void)updateHueRotate:(NSNumber *)degrees
{
[self.swiftContainerView updateHueRotate:degrees];
}
- (void)updateDropShadow:(NSNumber *)standardDeviation x:(NSNumber *)x y:(NSNumber *)y color:(UIColor *)color
{
[self.swiftContainerView updateDropShadowWithStandardDeviation:standardDeviation x:x y:y color:color];
@@ -1523,6 +1523,7 @@ jsi::Value JSCRuntime::createValue(JSValueRef value) const {
}
case kJSTypeSymbol:
return jsi::Value(createSymbol(value));
case kJSTypeBigInt:
default:
// WHAT ARE YOU
abort();
@@ -206,6 +206,9 @@ std::shared_ptr<HostTarget> HostTarget::create(
VoidExecutor executor) {
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
hostTarget->setExecutor(std::move(executor));
if (InspectorFlags::getInstance().getPerfIssuesEnabled()) {
hostTarget->installPerfIssuesBinding();
}
return hostTarget;
}
@@ -289,6 +292,17 @@ void HostTarget::sendCommand(HostCommand command) {
});
}
void HostTarget::installPerfIssuesBinding() {
perfMonitorUpdateHandler_ =
std::make_unique<PerfMonitorUpdateHandler>(delegate_);
perfMetricsBinding_ = std::make_unique<HostRuntimeBinding>(
*this, // Used immediately
"__react_native_perf_issues_reporter",
[this](const std::string& message) {
perfMonitorUpdateHandler_->handlePerfIssueAdded(message);
});
}
HostTargetController::HostTargetController(HostTarget& target)
: target_(target) {}
@@ -12,6 +12,7 @@
#include "InspectorInterfaces.h"
#include "InstanceTarget.h"
#include "NetworkIOAgent.h"
#include "PerfMonitorV2.h"
#include "ScopedExecutor.h"
#include "WeakList.h"
@@ -126,6 +127,12 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
*/
virtual void onSetPausedInDebuggerMessage(const OverlaySetPausedInDebuggerMessageRequest &request) = 0;
/**
* [Experimental] Called when the runtime has new data for the V2 Perf
* Monitor overlay. This is called on the inspector thread.
*/
virtual void unstable_onPerfIssueAdded(const PerfIssuePayload & /*issue*/) {}
/**
* Called by NetworkIOAgent on handling a `Network.loadNetworkResource` CDP
* request. Platform implementations should override this to perform a
@@ -166,6 +173,13 @@ class HostTargetController final {
bool hasInstance() const;
/**
* [Experimental] Install a runtime binding subscribing to new Performance
* Issues, which we broadcast to the V2 Perf Monitor overlay via
* \ref HostTargetDelegate::unstable_onPerfIssueAdded.
*/
void installPerfIssuesBinding();
/**
* Increments the target's pause overlay counter. The counter represents the
* exact number of Agents that have (concurrently) requested the pause
@@ -325,6 +339,7 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
std::shared_ptr<ExecutionContextManager> executionContextManager_;
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
std::unique_ptr<HostCommandSender> commandSender_;
std::unique_ptr<PerfMonitorUpdateHandler> perfMonitorUpdateHandler_;
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
/**
@@ -345,6 +360,13 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
return currentInstance_ != nullptr;
}
/**
* [Experimental] Install a runtime binding subscribing to new Peformance
* Issues, which we broadcast to the V2 Perf Monitor overlay via
* \ref HostTargetDelegate::unstable_onPerfMonitorUpdate.
*/
void installPerfIssuesBinding();
// Necessary to allow HostAgent to access HostTarget's internals in a
// controlled way (i.e. only HostTargetController gets friend access, while
// HostAgent itself doesn't).
@@ -33,6 +33,10 @@ bool InspectorFlags::getNetworkInspectionEnabled() const {
return loadFlagsAndAssertUnchanged().networkInspectionEnabled;
}
bool InspectorFlags::getPerfIssuesEnabled() const {
return loadFlagsAndAssertUnchanged().perfIssuesEnabled;
}
void InspectorFlags::dangerouslyResetFlags() {
*this = InspectorFlags{};
}
@@ -59,6 +63,7 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
.networkInspectionEnabled =
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(),
.perfIssuesEnabled = ReactNativeFeatureFlags::perfIssuesEnabled(),
};
if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) {
@@ -35,6 +35,11 @@ class InspectorFlags {
*/
bool getNetworkInspectionEnabled() const;
/**
* Flag determining if the V2 in-app Performance Monitor is enabled.
*/
bool getPerfIssuesEnabled() const;
/**
* Forcibly disable the main `getFuseboxEnabled()` flag. This should ONLY be
* used by `ReactInstanceIntegrationTest`.
@@ -52,6 +57,7 @@ class InspectorFlags {
bool fuseboxEnabled;
bool isProfilingBuild;
bool networkInspectionEnabled;
bool perfIssuesEnabled;
bool operator==(const Values &) const = default;
};
@@ -0,0 +1,29 @@
/*
* 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.
*/
#include "PerfMonitorV2.h"
#include "HostTarget.h"
#include <folly/json.h>
#include <react/timing/primitives.h>
namespace facebook::react::jsinspector_modern {
void PerfMonitorUpdateHandler::handlePerfIssueAdded(
const std::string& message) {
auto payload = folly::parseJson(message);
if (payload.isObject()) {
delegate_.unstable_onPerfIssueAdded(
PerfIssuePayload{
.name = payload["name"].asString(),
.severity = payload["severity"].asString(),
});
}
}
} // namespace facebook::react::jsinspector_modern
@@ -0,0 +1,38 @@
/*
* 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>
namespace facebook::react::jsinspector_modern {
class HostTargetDelegate;
struct PerfIssuePayload {
std::string name;
std::string severity;
};
/**
* [Experimental] Utility to handle performance metrics updates received from
* the runtime and forward update events to the V2 Perf Monitor UI.
*/
class PerfMonitorUpdateHandler {
public:
explicit PerfMonitorUpdateHandler(HostTargetDelegate &delegate) : delegate_(delegate) {}
/**
* Handle a new "__react_native_perf_issues_reporter" message.
*/
void handlePerfIssueAdded(const std::string &message);
private:
HostTargetDelegate &delegate_;
};
} // namespace facebook::react::jsinspector_modern
@@ -342,10 +342,7 @@ class JSINSPECTOR_EXPORT RuntimeTarget : public EnableExecutorFromThis<RuntimeTa
* sessions, or when the only Agent that had this domain enabled has
* disconnected.
*/
std::pair<bool, bool> processDomainChange(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent);
std::pair<bool, bool> processDomainChange(Domain domain, bool enabled, const RuntimeAgent &notifyingAgent);
/**
* Checks whether the given domain is enabled in at least one session
@@ -467,9 +467,18 @@ void consoleTimeStamp(
}
}
std::optional<folly::dynamic> detail;
if (performanceTracer.isTracing() && argumentsCount >= 7) {
const jsi::Value& detailArgument = arguments[6];
if (detailArgument.isObject()) {
detail =
tracing::getConsoleTimeStampDetailFromObject(runtime, detailArgument);
}
}
if (performanceTracer.isTracing()) {
performanceTracer.reportTimeStamp(
label, start, end, trackName, trackGroup, color);
label, start, end, trackName, trackGroup, color, std::move(detail));
}
if (ReactPerfettoLogger::isTracing()) {
@@ -18,6 +18,7 @@ target_include_directories(jsinspector_tracing PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(jsinspector_tracing
folly_runtime
jsi
jsinspector_network
oscompat
react_timing
@@ -0,0 +1,22 @@
/*
* 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.
*/
#include "ConsoleTimeStamp.h"
namespace facebook::react::jsinspector_modern::tracing {
std::optional<folly::dynamic> getConsoleTimeStampDetailFromObject(
jsi::Runtime& runtime,
const jsi::Value& detailValue) {
try {
return jsi::dynamicFromValue(runtime, detailValue);
} catch (jsi::JSIException&) {
return std::nullopt;
}
}
} // namespace facebook::react::jsinspector_modern::tracing
@@ -7,6 +7,8 @@
#pragma once
#include <jsi/JSIDynamic.h>
#include <jsi/jsi.h>
#include <react/timing/primitives.h>
#include <cassert>
@@ -96,4 +98,6 @@ inline std::optional<ConsoleTimeStampColor> getConsoleTimeStampColorFromString(c
}
};
std::optional<folly::dynamic> getConsoleTimeStampDetailFromObject(jsi::Runtime &runtime, const jsi::Value &detailValue);
}; // namespace facebook::react::jsinspector_modern::tracing
@@ -57,26 +57,39 @@ bool PerformanceTracer::startTracing(HighResDuration maxDuration) {
bool PerformanceTracer::startTracingImpl(
std::optional<HighResDuration> maxDuration) {
std::lock_guard lock(mutex_);
std::vector<TracingStateCallback> callbacksToNotify;
if (tracingAtomic_) {
return false;
{
std::lock_guard lock(mutex_);
if (tracingAtomic_) {
return false;
}
tracingAtomic_ = true;
if (maxDuration && *maxDuration < MIN_VALUE_FOR_MAX_TRACE_DURATION_OPTION) {
throw std::invalid_argument("maxDuration should be at least 1 second");
}
currentTraceStartTime_ = HighResTimeStamp::now();
currentTraceMaxDuration_ = maxDuration;
for (const auto& [id, callback] : tracingStateCallbacks_) {
callbacksToNotify.push_back(callback);
}
}
tracingAtomic_ = true;
if (maxDuration && *maxDuration < MIN_VALUE_FOR_MAX_TRACE_DURATION_OPTION) {
throw std::invalid_argument("maxDuration should be at least 1 second");
for (const auto& callback : callbacksToNotify) {
callback(true);
}
currentTraceStartTime_ = HighResTimeStamp::now();
currentTraceMaxDuration_ = maxDuration;
return true;
}
std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
std::vector<TraceEvent> events;
std::vector<TracingStateCallback> callbacksToNotify;
auto currentTraceEndTime = HighResTimeStamp::now();
@@ -87,6 +100,21 @@ std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
return std::nullopt;
}
// Collect callbacks before disabling tracing
for (const auto& [id, callback] : tracingStateCallbacks_) {
callbacksToNotify.push_back(callback);
}
}
// Notify callbacks while tracing is still enabled, so they can send final
// events
for (const auto& callback : callbacksToNotify) {
callback(false);
}
{
std::lock_guard lock(mutex_);
tracingAtomic_ = false;
performanceMeasureCount_ = 0;
@@ -129,6 +157,7 @@ std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
});
currentTraceMaxDuration_ = std::nullopt;
return events;
}
@@ -184,7 +213,8 @@ void PerformanceTracer::reportTimeStamp(
std::optional<ConsoleTimeStampEntry> end,
std::optional<std::string> trackName,
std::optional<std::string> trackGroup,
std::optional<ConsoleTimeStampColor> color) {
std::optional<ConsoleTimeStampColor> color,
std::optional<folly::dynamic> detail) {
if (!tracingAtomic_) {
return;
}
@@ -202,6 +232,7 @@ void PerformanceTracer::reportTimeStamp(
.trackName = std::move(trackName),
.trackGroup = std::move(trackGroup),
.color = std::move(color),
.detail = std::move(detail),
.threadId = getCurrentThreadId(),
});
}
@@ -622,6 +653,13 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
if (event.color) {
data["color"] = consoleTimeStampColorToString(*event.color);
}
if (event.detail) {
folly::dynamic devtoolsDetail = folly::dynamic::object();
for (const auto& [key, value] : event.detail->items()) {
devtoolsDetail[key] = value;
}
data["devtools"] = folly::toJson(devtoolsDetail);
}
events.emplace_back(
TraceEvent{
@@ -703,4 +741,18 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
std::move(event));
}
uint32_t PerformanceTracer::subscribeToTracingStateChanges(
TracingStateCallback callback) {
std::lock_guard lock(mutex_);
uint32_t subscriptionId = nextCallbackId_++;
tracingStateCallbacks_[subscriptionId] = std::move(callback);
return subscriptionId;
}
void PerformanceTracer::unsubscribeFromTracingStateChanges(
uint32_t subscriptionId) {
std::lock_guard lock(mutex_);
tracingStateCallbacks_.erase(subscriptionId);
}
} // namespace facebook::react::jsinspector_modern::tracing
@@ -15,6 +15,7 @@
#include <folly/dynamic.h>
#include <atomic>
#include <functional>
#include <map>
#include <mutex>
#include <optional>
@@ -95,7 +96,8 @@ class PerformanceTracer {
std::optional<ConsoleTimeStampEntry> end = std::nullopt,
std::optional<std::string> trackName = std::nullopt,
std::optional<std::string> trackGroup = std::nullopt,
std::optional<ConsoleTimeStampColor> color = std::nullopt);
std::optional<ConsoleTimeStampColor> color = std::nullopt,
std::optional<folly::dynamic> detail = std::nullopt);
/**
* Record an Event Loop tick, which will be represented as an Event Loop task
@@ -182,6 +184,29 @@ class PerformanceTracer {
HighResTimeStamp chunkTimestamp,
TraceEventProfileChunk &&traceEventProfileChunk);
/**
* Callback function type for tracing state changes.
* @param isTracing true if tracing has started, false if tracing has stopped
*/
using TracingStateCallback = std::function<void(bool isTracing)>;
/**
* Subscribe to tracing state changes (start/stop events).
* Tracing start state is reported after tracing has started, so callbacks can
* report events immediately.
* Tracing stop state is reported before tracing has stopped, so callbacks
* can report final events.
* @param callback Function to call when tracing starts or stops
* @return A unique identifier for the subscription that can be used to unsubscribe
*/
uint32_t subscribeToTracingStateChanges(TracingStateCallback callback);
/**
* Unsubscribe from tracing state changes.
* @param subscriptionId The identifier returned from subscribeToTracingStateChanges
*/
void unsubscribeFromTracingStateChanges(uint32_t subscriptionId);
private:
PerformanceTracer();
PerformanceTracer(const PerformanceTracer &) = delete;
@@ -228,6 +253,7 @@ class PerformanceTracer {
std::optional<std::string> trackName;
std::optional<std::string> trackGroup;
std::optional<ConsoleTimeStampColor> color;
std::optional<folly::dynamic> detail;
ThreadId threadId;
HighResTimeStamp createdAt = HighResTimeStamp::now();
};
@@ -316,6 +342,10 @@ class PerformanceTracer {
*/
std::mutex mutex_;
// Callback management
std::map<uint32_t, TracingStateCallback> tracingStateCallbacks_;
uint32_t nextCallbackId_{0};
bool startTracingImpl(std::optional<HighResDuration> maxDuration = std::nullopt);
std::vector<TraceEvent> collectEventsAndClearBuffers(HighResTimeStamp currentTraceEndTime);
@@ -44,6 +44,7 @@ Pod::Spec.new do |s|
resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: module_name)
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
s.dependency "React-jsi"
s.dependency "React-oscompat"
s.dependency "React-timing"
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0554340092d31b5431e201f570599ab5>>
* @generated SignedSource<<5d596cc470e66861d7c1b2ffbb15b268>>
*/
/**
@@ -146,6 +146,10 @@ bool ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS
return getAccessor().enableInteropViewManagerClassLookUpOptimizationIOS();
}
bool ReactNativeFeatureFlags::enableKeyEvents() {
return getAccessor().enableKeyEvents();
}
bool ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid() {
return getAccessor().enableLayoutAnimationsOnAndroid();
}
@@ -210,6 +214,10 @@ bool ReactNativeFeatureFlags::enableViewRecyclingForView() {
return getAccessor().enableViewRecyclingForView();
}
bool ReactNativeFeatureFlags::enableVirtualViewClippingWithoutScrollViewClipping() {
return getAccessor().enableVirtualViewClippingWithoutScrollViewClipping();
}
bool ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental() {
return getAccessor().enableVirtualViewContainerStateExperimental();
}
@@ -250,6 +258,10 @@ bool ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid()
return getAccessor().overrideBySynchronousMountPropsAtMountingAndroid();
}
bool ReactNativeFeatureFlags::perfIssuesEnabled() {
return getAccessor().perfIssuesEnabled();
}
bool ReactNativeFeatureFlags::perfMonitorV2Enabled() {
return getAccessor().perfMonitorV2Enabled();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<98a77cdecfd3e6d42f03b2b4ba3d8c88>>
* @generated SignedSource<<41492e85e1feeb20d2e3e60b6cc6cbe7>>
*/
/**
@@ -189,6 +189,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableInteropViewManagerClassLookUpOptimizationIOS();
/**
* Enables key up/down/press events to be sent to JS from components
*/
RN_EXPORT static bool enableKeyEvents();
/**
* When enabled, LayoutAnimations API will animate state changes on Android.
*/
@@ -269,6 +274,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableViewRecyclingForView();
/**
* Set clipping to drawingRect of ScrollView.
*/
RN_EXPORT static bool enableVirtualViewClippingWithoutScrollViewClipping();
/**
* Enables the experimental version of `VirtualViewContainerState`.
*/
@@ -319,6 +329,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool overrideBySynchronousMountPropsAtMountingAndroid();
/**
* Enable reporting Performance Issues (`detail.rnPerfIssue`). Displayed in the V2 Performance Monitor and the "Performance Issues" sub-panel in DevTools.
*/
RN_EXPORT static bool perfIssuesEnabled();
/**
* Enable the V2 in-app Performance Monitor. This flag is global and should not be changed across React Host lifetimes.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4eba223705b2d24f0360c6da1239b9a0>>
* @generated SignedSource<<cf152ea3a4e9635a71d2794a3139dadb>>
*/
/**
@@ -569,6 +569,24 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() {
auto flagValue = enableKeyEvents_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(30, "enableKeyEvents");
flagValue = currentProvider_->enableKeyEvents();
enableKeyEvents_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() {
auto flagValue = enableLayoutAnimationsOnAndroid_.load();
@@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(30, "enableLayoutAnimationsOnAndroid");
markFlagAsAccessed(31, "enableLayoutAnimationsOnAndroid");
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
enableLayoutAnimationsOnAndroid_ = flagValue;
@@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "enableLayoutAnimationsOnIOS");
markFlagAsAccessed(32, "enableLayoutAnimationsOnIOS");
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
enableLayoutAnimationsOnIOS_ = flagValue;
@@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(32, "enableMainQueueCoordinatorOnIOS");
markFlagAsAccessed(33, "enableMainQueueCoordinatorOnIOS");
flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS();
enableMainQueueCoordinatorOnIOS_ = flagValue;
@@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(33, "enableModuleArgumentNSNullConversionIOS");
markFlagAsAccessed(34, "enableModuleArgumentNSNullConversionIOS");
flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS();
enableModuleArgumentNSNullConversionIOS_ = flagValue;
@@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(34, "enableNativeCSSParsing");
markFlagAsAccessed(35, "enableNativeCSSParsing");
flagValue = currentProvider_->enableNativeCSSParsing();
enableNativeCSSParsing_ = flagValue;
@@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(35, "enableNetworkEventReporting");
markFlagAsAccessed(36, "enableNetworkEventReporting");
flagValue = currentProvider_->enableNetworkEventReporting();
enableNetworkEventReporting_ = flagValue;
@@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(36, "enablePreparedTextLayout");
markFlagAsAccessed(37, "enablePreparedTextLayout");
flagValue = currentProvider_->enablePreparedTextLayout();
enablePreparedTextLayout_ = flagValue;
@@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(37, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(38, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(38, "enableResourceTimingAPI");
markFlagAsAccessed(39, "enableResourceTimingAPI");
flagValue = currentProvider_->enableResourceTimingAPI();
enableResourceTimingAPI_ = flagValue;
@@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(39, "enableSwiftUIBasedFilters");
markFlagAsAccessed(40, "enableSwiftUIBasedFilters");
flagValue = currentProvider_->enableSwiftUIBasedFilters();
enableSwiftUIBasedFilters_ = flagValue;
@@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(40, "enableViewCulling");
markFlagAsAccessed(41, "enableViewCulling");
flagValue = currentProvider_->enableViewCulling();
enableViewCulling_ = flagValue;
@@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(41, "enableViewRecycling");
markFlagAsAccessed(42, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(42, "enableViewRecyclingForImage");
markFlagAsAccessed(43, "enableViewRecyclingForImage");
flagValue = currentProvider_->enableViewRecyclingForImage();
enableViewRecyclingForImage_ = flagValue;
@@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(43, "enableViewRecyclingForScrollView");
markFlagAsAccessed(44, "enableViewRecyclingForScrollView");
flagValue = currentProvider_->enableViewRecyclingForScrollView();
enableViewRecyclingForScrollView_ = flagValue;
@@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(44, "enableViewRecyclingForText");
markFlagAsAccessed(45, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(45, "enableViewRecyclingForView");
markFlagAsAccessed(46, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -857,6 +875,24 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableVirtualViewClippingWithoutScrollViewClipping() {
auto flagValue = enableVirtualViewClippingWithoutScrollViewClipping_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(47, "enableVirtualViewClippingWithoutScrollViewClipping");
flagValue = currentProvider_->enableVirtualViewClippingWithoutScrollViewClipping();
enableVirtualViewClippingWithoutScrollViewClipping_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimental() {
auto flagValue = enableVirtualViewContainerStateExperimental_.load();
@@ -866,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(46, "enableVirtualViewContainerStateExperimental");
markFlagAsAccessed(48, "enableVirtualViewContainerStateExperimental");
flagValue = currentProvider_->enableVirtualViewContainerStateExperimental();
enableVirtualViewContainerStateExperimental_ = flagValue;
@@ -884,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(47, "enableVirtualViewDebugFeatures");
markFlagAsAccessed(49, "enableVirtualViewDebugFeatures");
flagValue = currentProvider_->enableVirtualViewDebugFeatures();
enableVirtualViewDebugFeatures_ = flagValue;
@@ -902,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(48, "enableVirtualViewRenderState");
markFlagAsAccessed(50, "enableVirtualViewRenderState");
flagValue = currentProvider_->enableVirtualViewRenderState();
enableVirtualViewRenderState_ = flagValue;
@@ -920,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(49, "enableVirtualViewWindowFocusDetection");
markFlagAsAccessed(51, "enableVirtualViewWindowFocusDetection");
flagValue = currentProvider_->enableVirtualViewWindowFocusDetection();
enableVirtualViewWindowFocusDetection_ = flagValue;
@@ -938,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableWebPerformanceAPIsByDefault() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(50, "enableWebPerformanceAPIsByDefault");
markFlagAsAccessed(52, "enableWebPerformanceAPIsByDefault");
flagValue = currentProvider_->enableWebPerformanceAPIsByDefault();
enableWebPerformanceAPIsByDefault_ = flagValue;
@@ -956,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(51, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(53, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -974,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(52, "fuseboxEnabledRelease");
markFlagAsAccessed(54, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -992,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "fuseboxNetworkInspectionEnabled");
markFlagAsAccessed(55, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -1010,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(54, "hideOffscreenVirtualViewsOnIOS");
markFlagAsAccessed(56, "hideOffscreenVirtualViewsOnIOS");
flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS();
hideOffscreenVirtualViewsOnIOS_ = flagValue;
@@ -1028,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(55, "overrideBySynchronousMountPropsAtMountingAndroid");
markFlagAsAccessed(57, "overrideBySynchronousMountPropsAtMountingAndroid");
flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid();
overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue;
@@ -1037,6 +1073,24 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() {
auto flagValue = perfIssuesEnabled_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "perfIssuesEnabled");
flagValue = currentProvider_->perfIssuesEnabled();
perfIssuesEnabled_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() {
auto flagValue = perfMonitorV2Enabled_.load();
@@ -1046,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(56, "perfMonitorV2Enabled");
markFlagAsAccessed(59, "perfMonitorV2Enabled");
flagValue = currentProvider_->perfMonitorV2Enabled();
perfMonitorV2Enabled_ = flagValue;
@@ -1064,7 +1118,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(57, "preparedTextCacheSize");
markFlagAsAccessed(60, "preparedTextCacheSize");
flagValue = currentProvider_->preparedTextCacheSize();
preparedTextCacheSize_ = flagValue;
@@ -1082,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "preventShadowTreeCommitExhaustion");
markFlagAsAccessed(61, "preventShadowTreeCommitExhaustion");
flagValue = currentProvider_->preventShadowTreeCommitExhaustion();
preventShadowTreeCommitExhaustion_ = flagValue;
@@ -1100,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(59, "shouldPressibilityUseW3CPointerEventsForHover");
markFlagAsAccessed(62, "shouldPressibilityUseW3CPointerEventsForHover");
flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover();
shouldPressibilityUseW3CPointerEventsForHover_ = flagValue;
@@ -1118,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(60, "shouldTriggerResponderTransferOnScrollAndroid");
markFlagAsAccessed(63, "shouldTriggerResponderTransferOnScrollAndroid");
flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid();
shouldTriggerResponderTransferOnScrollAndroid_ = flagValue;
@@ -1136,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(61, "skipActivityIdentityAssertionOnHostPause");
markFlagAsAccessed(64, "skipActivityIdentityAssertionOnHostPause");
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
skipActivityIdentityAssertionOnHostPause_ = flagValue;
@@ -1154,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(62, "sweepActiveTouchOnChildNativeGesturesAndroid");
markFlagAsAccessed(65, "sweepActiveTouchOnChildNativeGesturesAndroid");
flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid();
sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue;
@@ -1172,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(63, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(66, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -1190,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit(
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(64, "updateRuntimeShadowNodeReferencesOnCommit");
markFlagAsAccessed(67, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -1208,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(65, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(68, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -1226,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(66, "useFabricInterop");
markFlagAsAccessed(69, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -1244,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(67, "useNativeEqualsInNativeReadableArrayAndroid");
markFlagAsAccessed(70, "useNativeEqualsInNativeReadableArrayAndroid");
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
@@ -1262,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(68, "useNativeTransformHelperAndroid");
markFlagAsAccessed(71, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
@@ -1280,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(69, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(72, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1298,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(70, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(73, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1316,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(71, "useRawPropsJsiValue");
markFlagAsAccessed(74, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1334,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(72, "useShadowNodeStateOnClone");
markFlagAsAccessed(75, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1352,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(73, "useSharedAnimatedBackend");
markFlagAsAccessed(76, "useSharedAnimatedBackend");
flagValue = currentProvider_->useSharedAnimatedBackend();
useSharedAnimatedBackend_ = flagValue;
@@ -1370,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(74, "useTraitHiddenOnAndroid");
markFlagAsAccessed(77, "useTraitHiddenOnAndroid");
flagValue = currentProvider_->useTraitHiddenOnAndroid();
useTraitHiddenOnAndroid_ = flagValue;
@@ -1388,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(75, "useTurboModuleInterop");
markFlagAsAccessed(78, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1406,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(76, "useTurboModules");
markFlagAsAccessed(79, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1424,7 +1478,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(77, "viewCullingOutsetRatio");
markFlagAsAccessed(80, "viewCullingOutsetRatio");
flagValue = currentProvider_->viewCullingOutsetRatio();
viewCullingOutsetRatio_ = flagValue;
@@ -1442,7 +1496,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(78, "virtualViewHysteresisRatio");
markFlagAsAccessed(81, "virtualViewHysteresisRatio");
flagValue = currentProvider_->virtualViewHysteresisRatio();
virtualViewHysteresisRatio_ = flagValue;
@@ -1460,7 +1514,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(79, "virtualViewPrerenderRatio");
markFlagAsAccessed(82, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<de758de45c903f7bf895bf03e049a20a>>
* @generated SignedSource<<e7e8d311c0d1cca4e28bee7ddcf5065c>>
*/
/**
@@ -62,6 +62,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableImmediateUpdateModeForContentOffsetChanges();
bool enableImperativeFocus();
bool enableInteropViewManagerClassLookUpOptimizationIOS();
bool enableKeyEvents();
bool enableLayoutAnimationsOnAndroid();
bool enableLayoutAnimationsOnIOS();
bool enableMainQueueCoordinatorOnIOS();
@@ -78,6 +79,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableViewRecyclingForScrollView();
bool enableViewRecyclingForText();
bool enableViewRecyclingForView();
bool enableVirtualViewClippingWithoutScrollViewClipping();
bool enableVirtualViewContainerStateExperimental();
bool enableVirtualViewDebugFeatures();
bool enableVirtualViewRenderState();
@@ -88,6 +90,7 @@ class ReactNativeFeatureFlagsAccessor {
bool fuseboxNetworkInspectionEnabled();
bool hideOffscreenVirtualViewsOnIOS();
bool overrideBySynchronousMountPropsAtMountingAndroid();
bool perfIssuesEnabled();
bool perfMonitorV2Enabled();
double preparedTextCacheSize();
bool preventShadowTreeCommitExhaustion();
@@ -123,7 +126,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 80> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 83> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> cdpInteractionMetricsEnabled_;
@@ -155,6 +158,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableImmediateUpdateModeForContentOffsetChanges_;
std::atomic<std::optional<bool>> enableImperativeFocus_;
std::atomic<std::optional<bool>> enableInteropViewManagerClassLookUpOptimizationIOS_;
std::atomic<std::optional<bool>> enableKeyEvents_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnAndroid_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnIOS_;
std::atomic<std::optional<bool>> enableMainQueueCoordinatorOnIOS_;
@@ -171,6 +175,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableViewRecyclingForScrollView_;
std::atomic<std::optional<bool>> enableViewRecyclingForText_;
std::atomic<std::optional<bool>> enableViewRecyclingForView_;
std::atomic<std::optional<bool>> enableVirtualViewClippingWithoutScrollViewClipping_;
std::atomic<std::optional<bool>> enableVirtualViewContainerStateExperimental_;
std::atomic<std::optional<bool>> enableVirtualViewDebugFeatures_;
std::atomic<std::optional<bool>> enableVirtualViewRenderState_;
@@ -181,6 +186,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> fuseboxNetworkInspectionEnabled_;
std::atomic<std::optional<bool>> hideOffscreenVirtualViewsOnIOS_;
std::atomic<std::optional<bool>> overrideBySynchronousMountPropsAtMountingAndroid_;
std::atomic<std::optional<bool>> perfIssuesEnabled_;
std::atomic<std::optional<bool>> perfMonitorV2Enabled_;
std::atomic<std::optional<double>> preparedTextCacheSize_;
std::atomic<std::optional<bool>> preventShadowTreeCommitExhaustion_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<21049b33fc72200390c6dd6d0ca27c19>>
* @generated SignedSource<<a0a1898f3a4c14179bd03d5dbe97adfa>>
*/
/**
@@ -147,6 +147,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableKeyEvents() override {
return false;
}
bool enableLayoutAnimationsOnAndroid() override {
return false;
}
@@ -211,6 +215,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return true;
}
bool enableVirtualViewClippingWithoutScrollViewClipping() override {
return true;
}
bool enableVirtualViewContainerStateExperimental() override {
return false;
}
@@ -228,7 +236,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
}
bool enableWebPerformanceAPIsByDefault() override {
return false;
return true;
}
bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override {
@@ -251,6 +259,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool perfIssuesEnabled() override {
return false;
}
bool perfMonitorV2Enabled() override {
return false;
}
@@ -276,7 +288,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
}
bool sweepActiveTouchOnChildNativeGesturesAndroid() override {
return false;
return true;
}
bool traceTurboModulePromiseRejectionsOnAndroid() override {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cc71bfa62258ec91b00c4646441fa7fc>>
* @generated SignedSource<<fc8f8541827a039fa5bc4a113e75fdf6>>
*/
/**
@@ -315,6 +315,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableInteropViewManagerClassLookUpOptimizationIOS();
}
bool enableKeyEvents() override {
auto value = values_["enableKeyEvents"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableKeyEvents();
}
bool enableLayoutAnimationsOnAndroid() override {
auto value = values_["enableLayoutAnimationsOnAndroid"];
if (!value.isNull()) {
@@ -459,6 +468,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableViewRecyclingForView();
}
bool enableVirtualViewClippingWithoutScrollViewClipping() override {
auto value = values_["enableVirtualViewClippingWithoutScrollViewClipping"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableVirtualViewClippingWithoutScrollViewClipping();
}
bool enableVirtualViewContainerStateExperimental() override {
auto value = values_["enableVirtualViewContainerStateExperimental"];
if (!value.isNull()) {
@@ -549,6 +567,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::overrideBySynchronousMountPropsAtMountingAndroid();
}
bool perfIssuesEnabled() override {
auto value = values_["perfIssuesEnabled"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::perfIssuesEnabled();
}
bool perfMonitorV2Enabled() override {
auto value = values_["perfMonitorV2Enabled"];
if (!value.isNull()) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<32a03e199994c91c022013d7929bfea5>>
* @generated SignedSource<<db8626cf78a3ed226bbbe6bf20cc03a2>>
*/
/**
@@ -35,10 +35,6 @@ class ReactNativeFeatureFlagsOverridesOSSCanary : public ReactNativeFeatureFlags
return true;
}
bool enableWebPerformanceAPIsByDefault() override {
return true;
}
bool useNativeViewConfigsInBridgelessMode() override {
return true;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4b639e1b0211b16826bcfd1a19dd6658>>
* @generated SignedSource<<5dcaab263795b1605da54dbbb617264f>>
*/
/**
@@ -55,6 +55,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0;
virtual bool enableImperativeFocus() = 0;
virtual bool enableInteropViewManagerClassLookUpOptimizationIOS() = 0;
virtual bool enableKeyEvents() = 0;
virtual bool enableLayoutAnimationsOnAndroid() = 0;
virtual bool enableLayoutAnimationsOnIOS() = 0;
virtual bool enableMainQueueCoordinatorOnIOS() = 0;
@@ -71,6 +72,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableViewRecyclingForScrollView() = 0;
virtual bool enableViewRecyclingForText() = 0;
virtual bool enableViewRecyclingForView() = 0;
virtual bool enableVirtualViewClippingWithoutScrollViewClipping() = 0;
virtual bool enableVirtualViewContainerStateExperimental() = 0;
virtual bool enableVirtualViewDebugFeatures() = 0;
virtual bool enableVirtualViewRenderState() = 0;
@@ -81,6 +83,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool fuseboxNetworkInspectionEnabled() = 0;
virtual bool hideOffscreenVirtualViewsOnIOS() = 0;
virtual bool overrideBySynchronousMountPropsAtMountingAndroid() = 0;
virtual bool perfIssuesEnabled() = 0;
virtual bool perfMonitorV2Enabled() = 0;
virtual double preparedTextCacheSize() = 0;
virtual bool preventShadowTreeCommitExhaustion() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<81b1cc08f5f6aa5f44c002eb7992811f>>
* @generated SignedSource<<6bd64914d0e2f0c727490a420496fbb0>>
*/
/**
@@ -194,6 +194,11 @@ bool NativeReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizat
return ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS();
}
bool NativeReactNativeFeatureFlags::enableKeyEvents(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableKeyEvents();
}
bool NativeReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid();
@@ -274,6 +279,11 @@ bool NativeReactNativeFeatureFlags::enableViewRecyclingForView(
return ReactNativeFeatureFlags::enableViewRecyclingForView();
}
bool NativeReactNativeFeatureFlags::enableVirtualViewClippingWithoutScrollViewClipping(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableVirtualViewClippingWithoutScrollViewClipping();
}
bool NativeReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableVirtualViewContainerStateExperimental();
@@ -324,6 +334,11 @@ bool NativeReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAnd
return ReactNativeFeatureFlags::overrideBySynchronousMountPropsAtMountingAndroid();
}
bool NativeReactNativeFeatureFlags::perfIssuesEnabled(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::perfIssuesEnabled();
}
bool NativeReactNativeFeatureFlags::perfMonitorV2Enabled(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::perfMonitorV2Enabled();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2ac15ead28cac3a969854f339d625dca>>
* @generated SignedSource<<99624bd7682ad34476ac395e8acc43c7>>
*/
/**
@@ -96,6 +96,8 @@ class NativeReactNativeFeatureFlags
bool enableInteropViewManagerClassLookUpOptimizationIOS(jsi::Runtime& runtime);
bool enableKeyEvents(jsi::Runtime& runtime);
bool enableLayoutAnimationsOnAndroid(jsi::Runtime& runtime);
bool enableLayoutAnimationsOnIOS(jsi::Runtime& runtime);
@@ -128,6 +130,8 @@ class NativeReactNativeFeatureFlags
bool enableViewRecyclingForView(jsi::Runtime& runtime);
bool enableVirtualViewClippingWithoutScrollViewClipping(jsi::Runtime& runtime);
bool enableVirtualViewContainerStateExperimental(jsi::Runtime& runtime);
bool enableVirtualViewDebugFeatures(jsi::Runtime& runtime);
@@ -148,6 +152,8 @@ class NativeReactNativeFeatureFlags
bool overrideBySynchronousMountPropsAtMountingAndroid(jsi::Runtime& runtime);
bool perfIssuesEnabled(jsi::Runtime& runtime);
bool perfMonitorV2Enabled(jsi::Runtime& runtime);
double preparedTextCacheSize(jsi::Runtime& runtime);
@@ -19,11 +19,11 @@ namespace facebook::react {
* [Experimental] Reports CDP interaction events via the
* "__chromium_devtools_metrics_reporter" runtime binding.
*
* This populates the Interaction to Next Paint (INP) live metric in Chrome
* DevTools and the V2 Perf Monitor. Events are reported immediately and do
* not require an active CDP Tracing session.
* This populates the Interaction to Next Paint (INP) live metric in React
* Native DevTools. Events are reported immediately and do not require an
* active CDP Tracing session.
*/
class CdpMetricsReporter : public PerformanceEntryReporterEventTimingListener {
class CdpMetricsReporter : public PerformanceEntryReporterEventListener {
public:
explicit CdpMetricsReporter(RuntimeExecutor runtimeExecutor);
@@ -0,0 +1,60 @@
/*
* 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.
*/
#include "CdpPerfIssuesReporter.h"
#include <folly/dynamic.h>
#include <folly/json.h>
#include <string_view>
namespace facebook::react {
namespace {
constexpr std::string_view issuesReporterName =
"__react_native_perf_issues_reporter";
} // namespace
CdpPerfIssuesReporter::CdpPerfIssuesReporter(RuntimeExecutor runtimeExecutor)
: runtimeExecutor_(std::move(runtimeExecutor)) {}
void CdpPerfIssuesReporter::onMeasureEntry(
const PerformanceMeasure& entry,
const std::optional<UserTimingDetailProvider>& detailProvider) {
std::optional<folly::dynamic> maybeDetail = nullptr;
if (detailProvider.has_value()) {
maybeDetail = (*detailProvider)();
}
if (!maybeDetail.has_value() || !maybeDetail->isObject()) {
return;
}
runtimeExecutor_([entry,
detail = std::move(*maybeDetail)](jsi::Runtime& runtime) {
auto global = runtime.global();
if (!global.hasProperty(runtime, issuesReporterName.data())) {
return;
}
if (detail.count("rnPerfIssue") != 0 && detail["rnPerfIssue"].isObject()) {
auto& perfIssue = detail["rnPerfIssue"];
if (perfIssue.count("name") != 0 && perfIssue.count("severity") != 0 &&
perfIssue.count("description") != 0) {
auto jsonString = folly::toJson(perfIssue);
auto jsiString = jsi::String::createFromUtf8(runtime, jsonString);
auto issuesReporter =
global.getPropertyAsFunction(runtime, issuesReporterName.data());
issuesReporter.call(runtime, jsiString);
}
}
});
}
} // namespace facebook::react
@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/RuntimeExecutor.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/performance/timeline/PerformanceEntry.h>
#include <react/performance/timeline/PerformanceEntryReporterListeners.h>
#include <react/timing/primitives.h>
namespace facebook::react {
/**
* [Experimental] Reports new Performance Issues via the
* "__react_native_perf_issues_reporter" runtime binding.
*
* This populates the Perf Issues indicator in the V2 Perf Monitor. Events are
* reported immediately and do not require an active CDP Tracing session.
*/
class CdpPerfIssuesReporter : public PerformanceEntryReporterEventListener {
public:
explicit CdpPerfIssuesReporter(RuntimeExecutor runtimeExecutor);
void onMeasureEntry(const PerformanceMeasure &entry, const std::optional<UserTimingDetailProvider> &detailProvider)
override;
private:
const RuntimeExecutor runtimeExecutor_{};
};
} // namespace facebook::react
@@ -88,19 +88,18 @@ PerformanceEntryReporter::PerformanceEntryReporter()
#endif
}
void PerformanceEntryReporter::addEventTimingListener(
PerformanceEntryReporterEventTimingListener* listener) {
void PerformanceEntryReporter::addEventListener(
PerformanceEntryReporterEventListener* listener) {
std::unique_lock lock(listenersMutex_);
eventTimingListeners_.push_back(listener);
eventListeners_.push_back(listener);
}
void PerformanceEntryReporter::removeEventTimingListener(
PerformanceEntryReporterEventTimingListener* listener) {
void PerformanceEntryReporter::removeEventListener(
PerformanceEntryReporterEventListener* listener) {
std::unique_lock lock(listenersMutex_);
auto it = std::find(
eventTimingListeners_.begin(), eventTimingListeners_.end(), listener);
if (it != eventTimingListeners_.end()) {
eventTimingListeners_.erase(it);
auto it = std::find(eventListeners_.begin(), eventListeners_.end(), listener);
if (it != eventListeners_.end()) {
eventListeners_.erase(it);
}
}
@@ -208,13 +207,13 @@ void PerformanceEntryReporter::reportMeasure(
const std::string& name,
HighResTimeStamp startTime,
HighResDuration duration,
UserTimingDetailProvider&& detailProvider) {
const std::optional<UserTimingDetailProvider>& detailProvider) {
const auto entry = PerformanceMeasure{
{.name = std::string(name),
.startTime = startTime,
.duration = duration}};
traceMeasure(entry, std::move(detailProvider));
traceMeasure(entry, detailProvider);
// Add to buffers & notify observers
{
@@ -223,6 +222,16 @@ void PerformanceEntryReporter::reportMeasure(
}
observerRegistry_->queuePerformanceEntry(entry);
std::vector<PerformanceEntryReporterEventListener*> listenersCopy;
{
std::shared_lock lock(listenersMutex_);
listenersCopy = eventListeners_;
}
for (auto* listener : listenersCopy) {
listener->onMeasureEntry(entry, detailProvider);
}
}
void PerformanceEntryReporter::clearEventCounts() {
@@ -272,10 +281,10 @@ void PerformanceEntryReporter::reportEvent(
// TODO(T198982346): Log interaction events to jsinspector_modern
observerRegistry_->queuePerformanceEntry(entry);
std::vector<PerformanceEntryReporterEventTimingListener*> listenersCopy;
std::vector<PerformanceEntryReporterEventListener*> listenersCopy;
{
std::shared_lock lock(listenersMutex_);
listenersCopy = eventTimingListeners_;
listenersCopy = eventListeners_;
}
for (auto* listener : listenersCopy) {
@@ -354,11 +363,13 @@ void PerformanceEntryReporter::traceMark(
void PerformanceEntryReporter::traceMeasure(
const PerformanceMeasure& entry,
UserTimingDetailProvider&& detailProvider) const {
const std::optional<UserTimingDetailProvider>& detailProvider) const {
auto& performanceTracer =
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) {
auto detail = detailProvider != nullptr ? detailProvider() : nullptr;
auto detail = detailProvider && detailProvider.has_value()
? (*detailProvider)()
: nullptr;
if (ReactPerfettoLogger::isTracing()) {
ReactPerfettoLogger::measure(
@@ -59,8 +59,8 @@ class PerformanceEntryReporter {
void clearEntries(PerformanceEntryType entryType);
void clearEntries(PerformanceEntryType entryType, const std::string &entryName);
void addEventTimingListener(PerformanceEntryReporterEventTimingListener *listener);
void removeEventTimingListener(PerformanceEntryReporterEventTimingListener *listener);
void addEventListener(PerformanceEntryReporterEventListener *listener);
void removeEventListener(PerformanceEntryReporterEventListener *listener);
static std::vector<PerformanceEntryType> getSupportedEntryTypes();
@@ -84,7 +84,7 @@ class PerformanceEntryReporter {
const std::string &name,
HighResTimeStamp startTime,
HighResDuration duration,
UserTimingDetailProvider &&detailProvider = nullptr);
const std::optional<UserTimingDetailProvider> &detailProvider = std::nullopt);
void reportEvent(
const std::string &name,
@@ -123,7 +123,7 @@ class PerformanceEntryReporter {
std::unordered_map<std::string, uint32_t> eventCounts_;
mutable std::shared_mutex listenersMutex_;
std::vector<PerformanceEntryReporterEventTimingListener *> eventTimingListeners_{};
std::vector<PerformanceEntryReporterEventListener *> eventListeners_{};
const inline PerformanceEntryBuffer &getBuffer(PerformanceEntryType entryType) const
{
@@ -166,7 +166,8 @@ class PerformanceEntryReporter {
}
void traceMark(const PerformanceMark &entry, UserTimingDetailProvider &&detailProvider) const;
void traceMeasure(const PerformanceMeasure &entry, UserTimingDetailProvider &&detailProvider) const;
void traceMeasure(const PerformanceMeasure &entry, const std::optional<UserTimingDetailProvider> &detailProvider)
const;
void traceResourceTiming(
const PerformanceResourceTiming &entry,
const std::optional<std::string> &devtoolsRequestId,
@@ -14,9 +14,17 @@
namespace facebook::react {
class PerformanceEntryReporterEventTimingListener {
using UserTimingDetailProvider = std::function<folly::dynamic()>;
class PerformanceEntryReporterEventListener {
public:
virtual ~PerformanceEntryReporterEventTimingListener() = default;
virtual ~PerformanceEntryReporterEventListener() = default;
virtual void onMeasureEntry(
const PerformanceMeasure & /*entry*/,
const std::optional<UserTimingDetailProvider> & /*detailProvider*/)
{
}
virtual void onEventTimingEntry(const PerformanceEventTiming & /*entry*/) {}
};
@@ -196,7 +196,8 @@ TEST(PerformanceObserver, PerformanceObserverTestObserveTakeRecords) {
reporter->reportMeasure(
"off",
timeOrigin + HighResDuration::fromMilliseconds(10),
HighResDuration::fromMilliseconds(20));
HighResDuration::fromMilliseconds(20),
std::nullopt);
reporter->reportMark(
"test2", timeOrigin + HighResDuration::fromMilliseconds(20));
reporter->reportMark(
@@ -391,7 +392,10 @@ TEST(PerformanceObserver, PerformanceObserverTestMultiple) {
{.durationThreshold = HighResDuration::fromMilliseconds(80)});
reporter->reportMeasure(
"measure", timeOrigin, HighResDuration::fromMilliseconds(50));
"measure",
timeOrigin,
HighResDuration::fromMilliseconds(50),
std::nullopt);
reporter->reportEvent(
"event1",
timeOrigin,
@@ -74,11 +74,13 @@ NativeAnimatedNodesManager::NativeAnimatedNodesManager(
DirectManipulationCallback&& directManipulationCallback,
FabricCommitCallback&& fabricCommitCallback,
StartOnRenderCallback&& startOnRenderCallback,
StopOnRenderCallback&& stopOnRenderCallback) noexcept
StopOnRenderCallback&& stopOnRenderCallback,
FrameRateListenerCallback&& frameRateListenerCallback) noexcept
: directManipulationCallback_(std::move(directManipulationCallback)),
fabricCommitCallback_(std::move(fabricCommitCallback)),
startOnRenderCallback_(std::move(startOnRenderCallback)),
stopOnRenderCallback_(std::move(stopOnRenderCallback)) {
stopOnRenderCallback_(std::move(stopOnRenderCallback)),
frameRateListenerCallback_(std::move(frameRateListenerCallback)) {
if (!fabricCommitCallback_) {
LOG(WARNING)
<< "C++ Animated was setup without commit callback. This may lead to issue where buttons are not tappable when animation is driven by onScroll event.";
@@ -541,7 +543,7 @@ void NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool isAsync) {
}
if (startOnRenderCallback_) {
startOnRenderCallback_(isAsync);
startOnRenderCallback_([this]() { onRender(); }, isAsync);
}
}
@@ -563,6 +565,10 @@ void NativeAnimatedNodesManager::stopRenderCallbackIfNeeded(
if (stopOnRenderCallback_) {
stopOnRenderCallback_(isAsync);
}
if (frameRateListenerCallback_) {
frameRateListenerCallback_(false);
}
}
}
@@ -1030,6 +1036,10 @@ void NativeAnimatedNodesManager::onRender() {
"numActiveAnimations",
activeAnimations_.size());
if (frameRateListenerCallback_) {
frameRateListenerCallback_(true);
}
isOnRenderThread_ = true;
{
@@ -55,14 +55,16 @@ class NativeAnimatedNodesManager {
public:
using DirectManipulationCallback = std::function<void(Tag, const folly::dynamic &)>;
using FabricCommitCallback = std::function<void(std::unordered_map<Tag, folly::dynamic> &)>;
using StartOnRenderCallback = std::function<void(bool isAsync)>;
using StartOnRenderCallback = std::function<void(std::function<void()> &&, bool isAsync)>;
using StopOnRenderCallback = std::function<void(bool isAsync)>;
using FrameRateListenerCallback = std::function<void(bool /* shouldEnableListener */)>;
explicit NativeAnimatedNodesManager(
DirectManipulationCallback &&directManipulationCallback,
FabricCommitCallback &&fabricCommitCallback,
StartOnRenderCallback &&startOnRenderCallback = nullptr,
StopOnRenderCallback &&stopOnRenderCallback = nullptr) noexcept;
StopOnRenderCallback &&stopOnRenderCallback = nullptr,
FrameRateListenerCallback &&frameRateListenerCallback = nullptr) noexcept;
explicit NativeAnimatedNodesManager(std::shared_ptr<UIManagerAnimationBackend> animationBackend) noexcept;
@@ -237,6 +239,7 @@ class NativeAnimatedNodesManager {
std::atomic_bool isRenderCallbackStarted_{false};
const StartOnRenderCallback startOnRenderCallback_;
const StopOnRenderCallback stopOnRenderCallback_;
const FrameRateListenerCallback frameRateListenerCallback_;
std::shared_ptr<EventEmitterListener> eventEmitterListener_{nullptr};
@@ -19,41 +19,26 @@
namespace facebook::react {
UIManagerNativeAnimatedDelegateImpl::UIManagerNativeAnimatedDelegateImpl(
NativeAnimatedNodesManagerProvider::FrameRateListenerCallback
frameRateListenerCallback)
: frameRateListenerCallback_(std::move(frameRateListenerCallback)) {}
std::weak_ptr<NativeAnimatedNodesManager> manager)
: nativeAnimatedNodesManager_(manager) {}
void UIManagerNativeAnimatedDelegateImpl::runAnimationFrame() {
if (auto nativeAnimatedNodesManagerStrong =
nativeAnimatedNodesManager_.lock()) {
if (frameRateListenerCallback_) {
frameRateListenerCallback_(true);
}
nativeAnimatedNodesManagerStrong->onRender();
}
}
NativeAnimatedNodesManagerProvider::NativeAnimatedNodesManagerProvider(
StartOnRenderCallback startOnRenderCallback,
StopOnRenderCallback stopOnRenderCallback,
FrameRateListenerCallback frameRateListenerCallback)
NativeAnimatedNodesManager::StartOnRenderCallback startOnRenderCallback,
NativeAnimatedNodesManager::StopOnRenderCallback stopOnRenderCallback,
NativeAnimatedNodesManager::FrameRateListenerCallback
frameRateListenerCallback)
: eventEmitterListenerContainer_(
std::make_shared<EventEmitterListenerContainer>()),
frameRateListenerCallback_(std::move(frameRateListenerCallback)),
startOnRenderCallback_(std::move(startOnRenderCallback)) {
if (frameRateListenerCallback_) {
stopOnRenderCallback_ = [this, stopOnRenderCallback](bool isAsync) {
if (stopOnRenderCallback) {
stopOnRenderCallback(isAsync);
}
if (frameRateListenerCallback_) {
frameRateListenerCallback_(false);
}
};
} else {
stopOnRenderCallback_ = std::move(stopOnRenderCallback);
}
}
startOnRenderCallback_(std::move(startOnRenderCallback)),
stopOnRenderCallback_(std::move(stopOnRenderCallback)),
frameRateListenerCallback_(std::move(frameRateListenerCallback)) {}
std::shared_ptr<NativeAnimatedNodesManager>
NativeAnimatedNodesManagerProvider::getOrCreate(
@@ -85,10 +70,6 @@ NativeAnimatedNodesManagerProvider::getOrCreate(
uiManager->synchronouslyUpdateViewOnUIThread(viewTag, props);
};
nativeAnimatedDelegate_ =
std::make_shared<UIManagerNativeAnimatedDelegateImpl>(
frameRateListenerCallback_);
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
#ifdef RN_USE_ANIMATION_BACKEND
// TODO: this should be initialized outside of animated, but for now it
@@ -106,31 +87,14 @@ NativeAnimatedNodesManagerProvider::getOrCreate(
uiManager->unstable_setAnimationBackend(animationBackend_);
} else {
auto startOnRenderCallback =
[this, startOnRenderCallbackFn = std::move(startOnRenderCallback_)](
bool isAsync) {
if (startOnRenderCallbackFn) {
startOnRenderCallbackFn(
[this]() {
if (nativeAnimatedDelegate_) {
nativeAnimatedDelegate_->runAnimationFrame();
}
},
isAsync);
}
};
nativeAnimatedNodesManager_ =
std::make_shared<NativeAnimatedNodesManager>(
std::move(directManipulationCallback),
std::move(fabricCommitCallback),
std::move(startOnRenderCallback),
std::move(startOnRenderCallback_),
std::move(stopOnRenderCallback_));
}
std::static_pointer_cast<UIManagerNativeAnimatedDelegateImpl>(
nativeAnimatedDelegate_)
->setNativeAnimatedNodesManager(nativeAnimatedNodesManager_);
addEventEmitterListener(
nativeAnimatedNodesManager_->getEventEmitterListener());
@@ -153,6 +117,10 @@ NativeAnimatedNodesManagerProvider::getOrCreate(
return false;
}));
nativeAnimatedDelegate_ =
std::make_shared<UIManagerNativeAnimatedDelegateImpl>(
nativeAnimatedNodesManager_);
uiManager->setNativeAnimatedDelegate(nativeAnimatedDelegate_);
// TODO: remove force casting.
@@ -17,16 +17,10 @@ class AnimatedMountingOverrideDelegate;
class NativeAnimatedNodesManagerProvider {
public:
using FrameRateListenerCallback = std::function<void(bool /* shouldEnableListener */)>;
// when isAsync is true, it means StartOnRenderCallback is invoked from js
// thread, otherwise from main thread
using StartOnRenderCallback = std::function<void(std::function<void()> &&, bool /* isAsync */)>;
using StopOnRenderCallback = NativeAnimatedNodesManager::StopOnRenderCallback;
NativeAnimatedNodesManagerProvider(
StartOnRenderCallback startOnRenderCallback = nullptr,
StopOnRenderCallback stopOnRenderCallback = nullptr,
FrameRateListenerCallback frameRateListenerCallback = nullptr);
NativeAnimatedNodesManager::StartOnRenderCallback startOnRenderCallback = nullptr,
NativeAnimatedNodesManager::StopOnRenderCallback stopOnRenderCallback = nullptr,
NativeAnimatedNodesManager::FrameRateListenerCallback frameRateListenerCallback = nullptr);
std::shared_ptr<NativeAnimatedNodesManager> getOrCreate(
jsi::Runtime &runtime,
@@ -47,29 +41,21 @@ class NativeAnimatedNodesManagerProvider {
std::shared_ptr<UIManagerNativeAnimatedDelegate> nativeAnimatedDelegate_;
std::shared_ptr<AnimatedMountingOverrideDelegate> animatedMountingOverrideDelegate_;
FrameRateListenerCallback frameRateListenerCallback_;
StartOnRenderCallback startOnRenderCallback_;
StopOnRenderCallback stopOnRenderCallback_;
NativeAnimatedNodesManager::StartOnRenderCallback startOnRenderCallback_;
NativeAnimatedNodesManager::StopOnRenderCallback stopOnRenderCallback_;
NativeAnimatedNodesManager::FrameRateListenerCallback frameRateListenerCallback_;
std::unique_ptr<MergedValueDispatcher> mergedValueDispatcher_;
};
class UIManagerNativeAnimatedDelegateImpl : public UIManagerNativeAnimatedDelegate {
public:
explicit UIManagerNativeAnimatedDelegateImpl(
NativeAnimatedNodesManagerProvider::FrameRateListenerCallback frameRateListenerCallback);
explicit UIManagerNativeAnimatedDelegateImpl(std::weak_ptr<NativeAnimatedNodesManager> manager);
void runAnimationFrame() override;
void setNativeAnimatedNodesManager(std::weak_ptr<NativeAnimatedNodesManager> manager)
{
nativeAnimatedNodesManager_ = manager;
}
private:
std::weak_ptr<NativeAnimatedNodesManager> nativeAnimatedNodesManager_;
NativeAnimatedNodesManagerProvider::FrameRateListenerCallback frameRateListenerCallback_;
};
} // namespace facebook::react
@@ -74,6 +74,24 @@ void ImageShadowNode::updateStateIfNeeded() {
return;
}
#ifdef ANDROID
// Check if we should skip prefetching based on shouldResize logic
if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
const auto& resizeMethod = imageProps.resizeMethod;
const auto& uri = newImageSource.uri;
bool shouldResize = (resizeMethod == "resize") ||
// Only resize for local content/file URIs
(resizeMethod == "auto" &&
(uri.starts_with("content://") || uri.starts_with("file://")));
// If we would resize but have no dimensions, skip creating the request
if (shouldResize &&
(newImageSource.size.width == 0 || newImageSource.size.height == 0)) {
// Keep the old state - don't create a new image request
return;
}
}
#endif
ImageState state{
newImageSource,
imageManager_->requestImage(
@@ -421,7 +421,7 @@ std::shared_ptr<ShadowNode> cloneMultipleRecursive(
std::shared_ptr<std::vector<std::shared_ptr<const ShadowNode>>> newChildren;
auto count = childrenCount.at(family);
for (int i = 0; count > 0 && i < children.size(); i++) {
for (size_t i = 0; count > 0 && i < children.size(); i++) {
const auto childFamily = &children[i]->getFamily();
if (childrenCount.contains(childFamily)) {
count--;
@@ -41,7 +41,12 @@ Scheduler::Scheduler(
if (ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
ReactNativeFeatureFlags::cdpInteractionMetricsEnabled()) {
cdpMetricsReporter_.emplace(CdpMetricsReporter{runtimeExecutor_});
performanceEntryReporter_->addEventTimingListener(&*cdpMetricsReporter_);
performanceEntryReporter_->addEventListener(&*cdpMetricsReporter_);
}
if (ReactNativeFeatureFlags::perfIssuesEnabled()) {
cdpPerfIssuesReporter_.emplace(CdpPerfIssuesReporter{runtimeExecutor_});
performanceEntryReporter_->addEventListener(&*cdpPerfIssuesReporter_);
}
eventPerformanceLogger_ =
@@ -172,7 +177,10 @@ Scheduler::~Scheduler() {
uiManager_->setAnimationDelegate(nullptr);
if (cdpMetricsReporter_) {
performanceEntryReporter_->removeEventTimingListener(&*cdpMetricsReporter_);
performanceEntryReporter_->removeEventListener(&*cdpMetricsReporter_);
}
if (cdpPerfIssuesReporter_) {
performanceEntryReporter_->removeEventListener(&*cdpPerfIssuesReporter_);
}
// Then, let's verify that the requirement was satisfied.
@@ -283,8 +291,8 @@ void Scheduler::uiManagerDidDispatchCommand(
const std::shared_ptr<const ShadowNode>& shadowNode,
const std::string& commandName,
const folly::dynamic& args) {
TraceSection s("Scheduler::uiManagerDispatchCommand");
TraceSection s(
"Scheduler::uiManagerDispatchCommand", "commandName", commandName);
if (delegate_ != nullptr) {
auto shadowView = ShadowView(*shadowNode);
runtimeScheduler_->scheduleRenderingUpdate(
@@ -11,6 +11,7 @@
#include <ReactCommon/RuntimeExecutor.h>
#include <react/performance/cdpmetrics/CdpMetricsReporter.h>
#include <react/performance/cdpmetrics/CdpPerfIssuesReporter.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
#include <react/renderer/core/ComponentDescriptor.h>
@@ -134,6 +135,7 @@ class Scheduler final : public UIManagerDelegate {
std::shared_ptr<PerformanceEntryReporter> performanceEntryReporter_;
std::optional<CdpMetricsReporter> cdpMetricsReporter_;
std::optional<CdpPerfIssuesReporter> cdpPerfIssuesReporter_;
std::shared_ptr<EventPerformanceLogger> eventPerformanceLogger_;
/**

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