Compare commits

..
Author SHA1 Message Date
Riccardo Cipolleschi 7f13eb0a68 [RN][Releases] Fix draft creation 2025-05-07 14:04:45 +01:00
React Native Bot f70a146699 [LOCAL] Bump Podfile.lock 2025-05-07 11:41:41 +00:00
React Native Bot d9c2857e59 Release 0.80.0-rc.0
#publish-packages-to-npm&next
2025-05-07 10:17:06 +00:00
Jorge Cohen cbf1710fd5 Bump hermes version 2025-05-06 11:21:57 +01:00
Christian Falch 3ac16dd6a8 replaced use of rsync with cp in prebuild scripts (#51095)
Summary:
When building the RNDependencies XCFrameworks we saw some errors in the CI servers about rsync failing:

`rsync(3031): error: poll: hangup on nonblocking write`

We decided to fix this by changing from using rsync to cp.

This commit fixes this by replacing rsync with cp, and adding a cleanup step after copying the XCFramework files.

## Changelog:

[INTERNAL] [FIXED] - Replacing rsync with cp, and adding a cleanup step after copying the XCFramework files.

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

Test Plan: Run RNDependencies scripts on CI and verify that they're working as expected.

Reviewed By: fabriziocucci

Differential Revision: D74236114

Pulled By: cipolleschi

fbshipit-source-id: accd84abf7601919cfdbc1b6a8681a61d4293ca5
2025-05-06 02:19:54 -07:00
David Vacca 3ad0b8244a Throw if getCatalystInstance() is executed when running on bridgless mode + legacy arch minification (#51112)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51112

Throw if getCatalystInstance() is executed when running on bridgless mode + legacy arch minification

This should be safe given that all the methods of BridgelessCatalystInstance throw exceptions

changelog: [internal] internal

Reviewed By: alanleedev

Differential Revision: D74200099

fbshipit-source-id: a888061250f19348a22895c3684aa2333280c916
2025-05-05 23:53:10 -07:00
David Vacca a02052d37f Mark BridgelessCatalystInstance as LegacyArchitecture and remove DoNotStrip (#51111)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51111

BridgelessCatalystInstance is unused in bridgeless, any usage will lead to an exception / crash.
This diffs marks BridgelessCatalystInstance as LegacyArchitecture and removes DoNotStrip annotation to enable further optimizations

changelog: [internal] internal

Reviewed By: alanleedev

Differential Revision: D74200097

fbshipit-source-id: f99b6fa74c30bdf0e1cd6758e223601b3ce5826c
2025-05-05 23:53:10 -07:00
Tim Yung e69e35e370 Fantom: Create Shadow Node Reference Counter (#51088)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51088

Creates `ShadowNodeReferenceCounter`, a module with utilities for writing Fantom tests that make assertions about the reference count for a `ShadowNode` object.

Changelog:
[Internal]

Reviewed By: lunaleaps

Differential Revision: D74131710

fbshipit-source-id: a949a402ee52f40445ce99c712540e80c8a05065
2025-05-05 23:43:41 -07:00
Nick Gerleman f85b30b24b Underpinnings for Caching Text Layouts (#51065)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51065

This adds infrastructure to let us start storing cached Android text layouts as part of a `ParagraphShadowNode`. After this, we will clear them out, and propagate them to state. Right now, the flag doesn't do much, apart from extra work.

This is done by adding `TextLayoutManagerExtended::supportsPreparedLayout()`, and `TextLayoutManager::PreparedLayout` types, to shim between platforms, then on Android, we add a `PreparedLayout`, which is for now just an Android layout, with extra field (`maxNumberOfLines`, for some reason not exposed on recent versions).

Android `TextLayoutManager` java side is split a little bit, so that we reuse all the existing logic for prepared layouts. I tried to set up the boundary, so that we don't reserialize a MapBuffer after preparation, and for simplicity, this means source of truth for attachment count, and attachment sizes, now lives on the layout. This means we need to change boundary a bit, where we are no longer able to pass in a buffer to fill from C++ side of attachment positions.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D73970149

fbshipit-source-id: ff71c227e062c16fe52a4eb3ba2acbebf3d96e56
2025-05-05 16:48:16 -07:00
Aaron Coplan 2562440385 Construct FBImagePerfImageInfo and pass to completion handler from within FBIconModule (#51110)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51110

Changelog: [iOS][Changed] Update RCTImageLoader.mm to cast loadHandler to RCTImageLoaderLoggable before calling shouldEnablePerfLogging

Replacing cast from RCTImageURLLoaderWithAttribution to RCTImageLoaderLoggable

Reviewed By: philIip

Differential Revision: D73148595

fbshipit-source-id: 01f61bc60624e2bb9cb4ff9b45094e38ea77dcaa
2025-05-05 16:37:56 -07:00
Aaron Coplan 43c9a609de Update loadImageURL signature in RCTImageURLLoader to include metadata in completion callback (#51107)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51107

Changelog: [iOS][Deprecated] Deprecate loadImageForURL in favor of new signature which uses completionHandlerWithMetadata

Updating the signature of completionHandlerWithMetadata to allow passing metadata through the completionHandlerWithMetadata in order to enable better instrumentation of image loading.

Reviewed By: philIip

Differential Revision: D73162738

fbshipit-source-id: 8e7e4ac35c9685a362a2efe08ebebbf0dd249ff9
2025-05-05 16:37:56 -07:00
Nick Gerleman e565c662d7 Assume full container width when ellipsizing line (#51108)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51108

We calculate the width of text to be the full width of the container, if the text overflows the bounds of the container, instead of the wrapped width, to match the behavior on web.

Incorporating line count into layout defeats this logic, since the final layout never overflows. We need to also check to see if we are ellipsized (had overflow) instead.

Changelog:
[Android][Fixed] - Assume full container width when ellipsizing line

Reviewed By: joevilches

Differential Revision: D74204041

fbshipit-source-id: 1c2a7fc50f101bd4c9ed2f979534731992b5310b
2025-05-05 15:47:10 -07:00
Joe Vilches f1e7bbb88b Fix "detected text" annoucement (#51106)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51106

There was a strange bug reported to us with `accessibilityOrder` where an OCR model would announce text on the screen sometimes. This would happen if a focused `View` without a label would wrap `Text` that was was not included in the `accessibilityOrder` array. What happens under the hood is we have a focused `View` trying to coopt a label. It finds no accessibility nodes under it (because the `Text` has `importantForAccessibility` set to `NO`) so it falls back to this weird TalkBack behavior. It both reads the text from the TextView and announces an OCR announcement - leading to repeating the text.

To fix this we just check if we are going to coopt text and if we do then we do not set `importantForAccessibility`. Behavior here changes a bit. Links are not accessible without having to reference the Text, setting `accessible={true}` on non-referenced `Text` will lead to be focusable.

These are both less bad than what we had before though, so I think this tradeoff is fine.

Changelog: [Internal]

Reviewed By: jorge-cab

Differential Revision: D74101530

fbshipit-source-id: 0678915efdc46a6273f79927b87f22bc9017814a
2025-05-05 12:58:18 -07:00
Rob Hogan f4936894f0 Miscellaneous lint fixes (#51099)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51099

Fix up some misc broken lints and add temporary suppressions to reduce noise in the subsequent stack.

Changelog: [Internal]

Reviewed By: huntie

Differential Revision: D74141669

fbshipit-source-id: dc19378e41f6d8d45fc7d2ecf18c1c01a1e501be
2025-05-05 09:48:46 -07:00
Eric Rozell d9823d80bb Delete ShadowTreeRevision on background thread (#50997)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50997

In some apps, we spend a non-trivial amount of time calling ShadowNode destructors on the UI thread.

A simple way to avoid stalling the UI thread is to move the `baseRevision_` instance to a data structure that is cleared on a background thread, so it's tree of ShadowNode shared_ptrs are released (and in most cases destroyed) on the background thread.

Rather than using std::thread, this change introduces the LowPriorityExecutor abstraction that should be supplied by host platforms. The implementation of this LowPriorityExecutor for each platform is as follows:
- iOS: uses dispatch_async to a low priority dispatch queue
- Android: uses a pthread with SCHED_OTHER and priority = 19

Moving the ShadowTreeRevision into a lambda capture and punting the lambda to the LowPriorityExecutor moves the destructor calls of the ShadowNodes to the host platform implementation of the LowPriorityExecutor.

This change is also guarded by a feature flag so we can keep an eye out for potential memory leaks.

## Changelog

[Internal]

Reviewed By: NickGerleman

Differential Revision: D73688009

fbshipit-source-id: 6a66da248e6fe5c38375bf026499346e8381e75a
2025-05-05 07:44:02 -07:00
86 changed files with 1658 additions and 511 deletions
+1 -1
View File
@@ -24,6 +24,6 @@ jobs:
with:
script: |
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
const version = ${{ github.ref_name }}';
const version = '${{ github.ref_name }}';
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
+2 -2
View File
@@ -52,8 +52,8 @@
"@babel/preset-flow": "^7.24.7",
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/metro-babel-transformer": "0.80.0-main",
"@react-native/metro-config": "0.80.0-main",
"@react-native/metro-babel-transformer": "0.80.0-rc.0",
"@react-native/metro-config": "0.80.0-rc.0",
"@tsconfig/node18": "1.0.1",
"@types/react": "^19.0.0",
"@typescript-eslint/parser": "^7.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.80.0-main"
"@react-native/codegen": "0.80.0-rc.0"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,7 +22,7 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.80.0-main",
"@react-native/dev-middleware": "0.80.0-rc.0",
"chalk": "^4.0.0",
"debug": "^4.4.0",
"invariant": "^2.2.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,7 +23,7 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.80.0-main",
"@react-native/debugger-frontend": "0.80.0-rc.0",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.80.0-main",
"@react-native/eslint-plugin": "0.80.0-rc.0",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.80.0-main",
"@react-native/codegen": "0.80.0-rc.0",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "helloworld",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"private": true,
"scripts": {
"bootstrap": "node ./cli.js bootstrap",
@@ -13,16 +13,16 @@
},
"dependencies": {
"react": "19.1.0",
"react-native": "1000.0.0"
"react-native": "0.80.0-rc.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.80.0-main",
"@react-native/core-cli-utils": "0.80.0-main",
"@react-native/eslint-config": "0.80.0-main",
"@react-native/metro-config": "0.80.0-main",
"@react-native/babel-preset": "0.80.0-rc.0",
"@react-native/core-cli-utils": "0.80.0-rc.0",
"@react-native/eslint-config": "0.80.0-rc.0",
"@react-native/metro-config": "0.80.0-rc.0",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"eslint": "^8.19.0",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.80.0-main",
"@react-native/metro-babel-transformer": "0.80.0-main",
"@react-native/js-polyfills": "0.80.0-rc.0",
"@react-native/metro-babel-transformer": "0.80.0-rc.0",
"metro-config": "^0.82.2",
"metro-runtime": "^0.82.2"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/new-app-screen",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "NewAppScreen component for React Native",
"keywords": [
"react-native"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Babel preset for React Native applications",
"repository": {
"type": "git",
@@ -66,7 +66,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.80.0-main",
"@react-native/babel-plugin-codegen": "0.80.0-rc.0",
"babel-plugin-syntax-hermes-parser": "0.28.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Babel transformer for React Native applications.",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
],
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.80.0-main",
"@react-native/babel-preset": "0.80.0-rc.0",
"hermes-parser": "0.28.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@react-native/bots",
"description": "React Native Bots",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"private": true,
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"private": true,
"description": "TypeScript related unit test for @react-native/codegen",
"license": "MIT",
@@ -19,7 +19,7 @@
"prepare": "yarn run build"
},
"dependencies": {
"@react-native/codegen": "0.80.0-main"
"@react-native/codegen": "0.80.0-rc.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/compatibility-check",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Check a React Native app's boundary between JS and Native for incompatibilities",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"dist"
],
"dependencies": {
"@react-native/codegen": "0.80.0-main"
"@react-native/codegen": "0.80.0-rc.0"
},
"devDependencies": {
"flow-remove-types": "^2.237.2",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@react-native/fantom",
"private": true,
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"main": "src/index.js",
"description": "Internal integration testing and benchmarking tool for React Native",
"peerDependencies": {
+14
View File
@@ -566,6 +566,20 @@ if (typeof global.EventTarget === 'undefined') {
);
}
/**
* Returns a function that returns the current reference count for the supplied
* element's shadow node. If the reference count is zero, that means the shadow
* node has been deallocated.
*
* @param node The node for which to create a reference counting function.
*/
export function createShadowNodeReferenceCounter(
node: ReactNativeElement,
): () => number {
let shadowNode = getNativeNodeReference(node);
return NativeFantom.createShadowNodeReferenceCounter(shadowNode);
}
/**
* Saves a heap snapshot after forcing garbage collection.
*
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -21,7 +21,7 @@
},
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.80.0-main"
"@react-native/codegen": "0.80.0-rc.0"
},
"peerDependencies": {
"@types/react": "^19.0.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"private": true,
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
"license": "MIT",
@@ -26,8 +26,8 @@
],
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.80.0-main",
"react-native": "1000.0.0"
"@react-native/babel-preset": "0.80.0-rc.0",
"react-native": "0.80.0-rc.0"
},
"peerDependencies": {
"react": "*",
+3 -3
View File
@@ -14,8 +14,8 @@ export const version: $ReadOnly<{
patch: number,
prerelease: string | null,
}> = {
major: 1000,
minor: 0,
major: 0,
minor: 80,
patch: 0,
prerelease: null,
prerelease: 'rc.0',
};
@@ -317,6 +317,45 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
return image;
}
/*
* This function abstracts away the migration from loadImageForURL to loadImageURL by handling checking whether
* the interface responds to the new function signature and calling the appropriate function based on the result.
*/
static RCTImageLoaderCancellationBlock RCTLoadImageURLFromLoader(
id<RCTImageURLLoader> loadHandler,
NSURL *imageURL,
CGSize size,
CGFloat scale,
RCTResizeMode resizeMode,
RCTImageLoaderProgressBlock progressHandler,
RCTImageLoaderPartialLoadBlock partialLoadHandler,
RCTImageLoaderCompletionBlockWithMetadata completionHandler)
{
if ([loadHandler respondsToSelector:@selector(loadImageForURL:
size:scale:resizeMode:progressHandler:partialLoadHandler
:completionHandlerWithMetadata:)]) {
return [loadHandler loadImageForURL:imageURL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandlerWithMetadata:^(NSError *error, UIImage *image, id metadata) {
completionHandler(error, image, metadata);
}];
} else {
return [loadHandler loadImageForURL:imageURL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image) {
completionHandler(error, image, nil);
}];
}
}
#pragma mark - RCTImageLoaderProtocol 2/3
- (nullable RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
@@ -566,15 +605,18 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
completionHandler(error, image, metadata, nil);
}];
}
RCTImageLoaderCancellationBlock cb = [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image) {
completionHandler(error, image, nil, nil);
}];
RCTImageLoaderCancellationBlock cb = RCTLoadImageURLFromLoader(
loadHandler,
request.URL,
size,
scale,
resizeMode,
progressHandler,
partialLoadHandler,
^(NSError *error, UIImage *image, id metadata) {
completionHandler(error, image, metadata, nil);
});
return [[RCTImageURLLoaderRequest alloc] initWithRequestId:nil imageURL:request.URL cancellationBlock:cb];
}
@@ -608,15 +650,17 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
}];
cancelLoadLocal = loaderRequest.cancellationBlock;
} else {
cancelLoadLocal = [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image) {
completionHandler(error, image, nil, nil);
}];
cancelLoadLocal = RCTLoadImageURLFromLoader(
loadHandler,
request.URL,
size,
scale,
resizeMode,
progressHandler,
partialLoadHandler,
^(NSError *error, UIImage *image, id metadata) {
completionHandler(error, image, metadata, nil);
});
}
[cancelLoadLock lock];
cancelLoad = cancelLoadLocal;
@@ -867,7 +911,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
{
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:url];
if ([loadHandler respondsToSelector:@selector(shouldEnablePerfLogging)]) {
return [(id<RCTImageURLLoaderWithAttribution>)loadHandler shouldEnablePerfLogging];
return [(id<RCTImageLoaderLoggable>)loadHandler shouldEnablePerfLogging];
}
return NO;
}
@@ -38,6 +38,8 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
- (BOOL)canLoadImageURL:(NSURL *)requestURL;
/**
* DEPRECATED: Please use updated signature for loadImageForURL which uses
* completionHandlerWithMetadata instead.
* Send a network request to load the request URL. The method should call the
* progressHandler (if applicable) and the completionHandler when the request
* has finished. The method should also return a cancellation block, if
@@ -49,10 +51,26 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
RCT_DEPRECATED;
@optional
/*
* Send a network request to load the request URL. The method should call the
* progressHandler (if applicable) and the completionHandler when the request
* has finished. The method should also return a cancellation block, if
* applicable.
*/
- (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandlerWithMetadata:
(RCTImageLoaderCompletionBlockWithMetadata)completionHandlerWithMetadata;
/**
* If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:`
* then `loaderPriority` is used to determine which one to use. The loader
@@ -21,10 +21,10 @@ NSDictionary* RCTGetReactNativeVersion(void)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(void){
__rnVersion = @{
RCTVersionMajor: @(1000),
RCTVersionMinor: @(0),
RCTVersionMajor: @(0),
RCTVersionMinor: @(80),
RCTVersionPatch: @(0),
RCTVersionPrerelease: [NSNull null],
RCTVersionPrerelease: @"rc.0",
};
});
return __rnVersion;
@@ -2392,12 +2392,14 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid
public fun initialize ()V
public fun invalidate ()V
public fun markActiveTouchForTag (II)V
public fun measurePreparedLayout (Lcom/facebook/react/views/text/PreparedLayout;FFFF)[F
public fun onAllAnimationsComplete ()V
public fun onAnimationStarted ()V
public fun onHostDestroy ()V
public fun onHostPause ()V
public fun onHostResume ()V
public fun onRequestEventBeat ()V
public fun prepareLayout (ILcom/facebook/react/common/mapbuffer/ReadableMapBuffer;Lcom/facebook/react/common/mapbuffer/ReadableMapBuffer;FF)Lcom/facebook/react/views/text/PreparedLayout;
public fun prependUIBlock (Lcom/facebook/react/fabric/interop/UIBlock;)V
public fun profileNextBatch ()V
public fun receiveEvent (IILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0
VERSION_NAME=0.80.0-rc.0
react.internal.publishingGroup=com.facebook.react
android.useAndroidX=true
@@ -22,11 +22,13 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Point;
import android.os.SystemClock;
import android.text.Layout;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.util.Preconditions;
import androidx.core.view.ViewCompat.FocusRealDirection;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
@@ -49,6 +51,7 @@ import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.UIManagerListener;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.UnstableReactNativeAPI;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
@@ -86,6 +89,7 @@ import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.FabricEventDispatcher;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.uimanager.events.SynchronousEventReceiver;
import com.facebook.react.views.text.PreparedLayout;
import com.facebook.react.views.text.TextLayoutManager;
import java.util.ArrayList;
import java.util.HashMap;
@@ -648,6 +652,49 @@ public class FabricUIManager
attachmentsPositions);
}
@AnyThread
@ThreadConfined(ANY)
public PreparedLayout prepareLayout(
int surfaceId,
ReadableMapBuffer attributedString,
ReadableMapBuffer paragraphAttributes,
float maxWidth,
float maxHeight) {
SurfaceMountingManager surfaceMountingManager =
mMountingManager.getSurfaceManagerEnforced(surfaceId, "prepareLayout");
Layout layout =
TextLayoutManager.createLayout(
Preconditions.checkNotNull(surfaceMountingManager.getContext()),
attributedString,
paragraphAttributes,
PixelUtil.toPixelFromDIP(maxWidth),
PixelUtil.toPixelFromDIP(maxHeight),
null /* T219881133: Migrate away from ReactTextViewManagerCallback */);
int maximumNumberOfLines =
paragraphAttributes.contains(TextLayoutManager.PA_KEY_MAX_NUMBER_OF_LINES)
? paragraphAttributes.getInt(TextLayoutManager.PA_KEY_MAX_NUMBER_OF_LINES)
: ReactConstants.UNSET;
return new PreparedLayout(layout, maximumNumberOfLines);
}
@AnyThread
@ThreadConfined(ANY)
public float[] measurePreparedLayout(
PreparedLayout preparedLayout,
float minWidth,
float maxWidth,
float minHeight,
float maxHeight) {
return TextLayoutManager.measurePreparedLayout(
preparedLayout,
getYogaSize(minWidth, maxWidth),
getYogaMeasureMode(minWidth, maxWidth),
getYogaSize(minHeight, maxHeight),
getYogaMeasureMode(minHeight, maxHeight));
}
/**
* @param surfaceId {@link int} surface ID
* @param defaultTextInputPadding {@link float[]} output parameter will contain the default theme
@@ -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<<b86b8bb1d53ca03240c25adb8b423040>>
* @generated SignedSource<<453f8c0a593b173c197fcf54ed834a1b>>
*/
/**
@@ -90,6 +90,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = accessor.enableCustomFocusSearchOnClippedElementsAndroid()
/**
* Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.
*/
@JvmStatic
public fun enableDestroyShadowTreeRevisionAsync(): Boolean = accessor.enableDestroyShadowTreeRevisionAsync()
/**
* When enabled a subset of components will avoid double measurement on Android.
*/
@@ -174,6 +180,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableNewBackgroundAndBorderDrawables(): Boolean = accessor.enableNewBackgroundAndBorderDrawables()
/**
* Enables caching text layout artifacts for later reuse
*/
@JvmStatic
public fun enablePreparedTextLayout(): Boolean = accessor.enablePreparedTextLayout()
/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
@@ -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<<9c1e0e7e87aead96e2554ef5ecf9e7af>>
* @generated SignedSource<<a51441451ec25033040ba044ee3371fc>>
*/
/**
@@ -30,6 +30,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableBridgelessArchitectureCache: Boolean? = null
private var enableCppPropsIteratorSetterCache: Boolean? = null
private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null
private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null
private var enableDoubleMeasurementFixAndroidCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
@@ -44,6 +45,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableNativeCSSParsingCache: Boolean? = null
private var enableNetworkEventReportingCache: Boolean? = null
private var enableNewBackgroundAndBorderDrawablesCache: Boolean? = null
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableResourceTimingAPICache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
@@ -156,6 +158,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableDestroyShadowTreeRevisionAsync(): Boolean {
var cached = enableDestroyShadowTreeRevisionAsyncCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableDestroyShadowTreeRevisionAsync()
enableDestroyShadowTreeRevisionAsyncCache = cached
}
return cached
}
override fun enableDoubleMeasurementFixAndroid(): Boolean {
var cached = enableDoubleMeasurementFixAndroidCache
if (cached == null) {
@@ -282,6 +293,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enablePreparedTextLayout(): Boolean {
var cached = enablePreparedTextLayoutCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enablePreparedTextLayout()
enablePreparedTextLayoutCache = cached
}
return cached
}
override fun enablePropsUpdateReconciliationAndroid(): Boolean {
var cached = enablePropsUpdateReconciliationAndroidCache
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<<b95c0d1f576b67fcf9d9df498ea30994>>
* @generated SignedSource<<9d0b02395a08331bca956ea600602a31>>
*/
/**
@@ -48,6 +48,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun enableDestroyShadowTreeRevisionAsync(): Boolean
@DoNotStrip @JvmStatic public external fun enableDoubleMeasurementFixAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun enableEagerRootViewAttachment(): Boolean
@@ -76,6 +78,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableNewBackgroundAndBorderDrawables(): Boolean
@DoNotStrip @JvmStatic public external fun enablePreparedTextLayout(): Boolean
@DoNotStrip @JvmStatic public external fun enablePropsUpdateReconciliationAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun enableResourceTimingAPI(): Boolean
@@ -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<<c7425e6212e4e9d70e64452e550bf638>>
* @generated SignedSource<<cf12cdfdfb343e79247379b5549ae92a>>
*/
/**
@@ -43,6 +43,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = true
override fun enableDestroyShadowTreeRevisionAsync(): Boolean = false
override fun enableDoubleMeasurementFixAndroid(): Boolean = false
override fun enableEagerRootViewAttachment(): Boolean = false
@@ -71,6 +73,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableNewBackgroundAndBorderDrawables(): Boolean = true
override fun enablePreparedTextLayout(): Boolean = false
override fun enablePropsUpdateReconciliationAndroid(): Boolean = false
override fun enableResourceTimingAPI(): 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<<5b145ee103cf6b40e13d33c88cd52777>>
* @generated SignedSource<<4c81ed8a06c192eb4007219d163650e5>>
*/
/**
@@ -34,6 +34,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableBridgelessArchitectureCache: Boolean? = null
private var enableCppPropsIteratorSetterCache: Boolean? = null
private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null
private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null
private var enableDoubleMeasurementFixAndroidCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
@@ -48,6 +49,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableNativeCSSParsingCache: Boolean? = null
private var enableNetworkEventReportingCache: Boolean? = null
private var enableNewBackgroundAndBorderDrawablesCache: Boolean? = null
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableResourceTimingAPICache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
@@ -170,6 +172,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableDestroyShadowTreeRevisionAsync(): Boolean {
var cached = enableDestroyShadowTreeRevisionAsyncCache
if (cached == null) {
cached = currentProvider.enableDestroyShadowTreeRevisionAsync()
accessedFeatureFlags.add("enableDestroyShadowTreeRevisionAsync")
enableDestroyShadowTreeRevisionAsyncCache = cached
}
return cached
}
override fun enableDoubleMeasurementFixAndroid(): Boolean {
var cached = enableDoubleMeasurementFixAndroidCache
if (cached == null) {
@@ -310,6 +322,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enablePreparedTextLayout(): Boolean {
var cached = enablePreparedTextLayoutCache
if (cached == null) {
cached = currentProvider.enablePreparedTextLayout()
accessedFeatureFlags.add("enablePreparedTextLayout")
enablePreparedTextLayoutCache = cached
}
return cached
}
override fun enablePropsUpdateReconciliationAndroid(): Boolean {
var cached = enablePropsUpdateReconciliationAndroidCache
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<<9cdb965a420d53cf36b6531684cb07ae>>
* @generated SignedSource<<2482f57e0652cfaa4806b5333c50ad9f>>
*/
/**
@@ -43,6 +43,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean
@DoNotStrip public fun enableDestroyShadowTreeRevisionAsync(): Boolean
@DoNotStrip public fun enableDoubleMeasurementFixAndroid(): Boolean
@DoNotStrip public fun enableEagerRootViewAttachment(): Boolean
@@ -71,6 +73,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableNewBackgroundAndBorderDrawables(): Boolean
@DoNotStrip public fun enablePreparedTextLayout(): Boolean
@DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean
@DoNotStrip public fun enableResourceTimingAPI(): Boolean
@@ -12,9 +12,9 @@ package com.facebook.react.modules.systeminfo
public object ReactNativeVersion {
@JvmField
public val VERSION: Map<String, Any?> = mapOf(
"major" to 1000,
"minor" to 0,
"major" to 0,
"minor" to 80,
"patch" to 0,
"prerelease" to null
"prerelease" to "rc.0"
)
}
@@ -12,7 +12,6 @@
package com.facebook.react.runtime
import android.content.res.AssetManager
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.CatalystInstance
import com.facebook.react.bridge.JavaScriptContextHolder
import com.facebook.react.bridge.JavaScriptModule
@@ -26,14 +25,16 @@ import com.facebook.react.bridge.RuntimeScheduler
import com.facebook.react.bridge.UIManager
import com.facebook.react.bridge.queue.ReactQueueConfiguration
import com.facebook.react.common.annotations.VisibleForTesting
import com.facebook.react.common.annotations.internal.LegacyArchitecture
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry
import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder
@DoNotStrip
@Deprecated(
message =
"This class is deprecated, please to migrate to new architecture using [com.facebook.react.defaults.DefaultReactHost] instead.")
@LegacyArchitecture
internal class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : CatalystInstance {
override fun handleMemoryPressure(level: Int) {
@@ -68,7 +69,6 @@ internal class BridgelessCatalystInstance(private val reactHost: ReactHostImpl)
throw UnsupportedOperationException("Unimplemented method 'hasRunJSBundle'")
}
@DoNotStrip
override fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) {
throw UnsupportedOperationException("Unimplemented method 'invokeCallback'")
}
@@ -172,4 +172,10 @@ internal class BridgelessCatalystInstance(private val reactHost: ReactHostImpl)
override fun getFabricUIManager(): UIManager {
throw UnsupportedOperationException("Unimplemented method 'getFabricUIManager'")
}
private companion object {
init {
LegacyArchitectureLogger.assertLegacyArchitecture("BridgelessCatalystInstance")
}
}
}
@@ -24,6 +24,7 @@ import com.facebook.react.bridge.UIManager
import com.facebook.react.bridge.WritableNativeArray
import com.facebook.react.common.annotations.FrameworkAPI
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.devsupport.interfaces.DevSupportManager
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
@@ -63,6 +64,10 @@ internal class BridgelessReactContext(context: Context, private val reactHost: R
override fun getFabricUIManager(): UIManager? = reactHost.uiManager
override fun getCatalystInstance(): CatalystInstance {
if (ReactBuildConfig.UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE) {
throw UnsupportedOperationException(
"CatalystInstance is not supported when Bridgeless mode is enabled.")
}
Log.w(
TAG,
"[WARNING] Bridgeless doesn't support CatalystInstance. Accessing an API that's not part of" +
@@ -9,6 +9,7 @@ package com.facebook.react.uimanager
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import com.facebook.react.R
import com.facebook.react.bridge.ReadableArray
@@ -90,7 +91,11 @@ private object ReactAxOrderHelper {
): Array<View?> {
val axOrderViews = arrayOfNulls<View?>(axOrderIds.size)
fun traverseAndDisableAxFromExcludedViews(view: View, parent: View) {
fun traverseAndDisableAxFromExcludedViews(
view: View,
parent: View,
hasCooptingAncestor: Boolean
) {
val nativeId = view.getTag(R.id.view_tag_native_id) as String?
val isIncluded = nativeId != null && axOrderSet.contains(nativeId)
@@ -101,28 +106,40 @@ private object ReactAxOrderHelper {
view, view.isFocusable, view.importantForAccessibility)
}
// There is a strange bug with TalkBack where if a focused view has nothing to announce, and
// there are no accessibility node's below it, then it will run OCR model on its bounds and
// announce any text it sees. Also, if there is a TextView below the View backing the node,
// it will announce that text too. This can happen frequently with our implementation here
// we change importantForAccessibility for TextView's that are not included in
// accessibilityOrder thus kicking off the logic described. To avoid this double announcement
// we just do not set importantForAccessibility on TextView's in the case where someone is
// coopting them. We will not focus the text since they got coopted.
if (isIncluded) {
axOrderViews[axOrderIds.indexOf(nativeId)] = view
} else {
} else if (!(view is TextView && hasCooptingAncestor)) {
// Save original state before disabling
view.setTag(R.id.original_important_for_ax, view.importantForAccessibility)
view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
val wantsToCoopt = isIncluded && view.contentDescription.isNullOrEmpty()
if (view is ViewGroup) {
// Continue to try to disable children if this view is not included and is focusable.
// This view being focusable means it's an element, and not a container which means its
// presence doesn't imply all its children should be focusable. And if its not included we
// still want to attempt to disable the children of the container
// Continue to try to disable children if this view is not included and is
// focusable. This view being focusable means it's an element, and not a container which
// means its presence doesn't imply all its children should be focusable. And if its not
// included we still want to attempt to disable the children of the container
if (!isIncluded || view.isFocusable()) {
for (i in 0 until view.childCount) {
traverseAndDisableAxFromExcludedViews(view.getChildAt(i), parent)
traverseAndDisableAxFromExcludedViews(view.getChildAt(i), parent, wantsToCoopt)
}
}
}
}
traverseAndDisableAxFromExcludedViews(root, root)
// Technically we don't know if there is a coopting ancestor here, as it could be above the root
// but those cases should be fairly rare
traverseAndDisableAxFromExcludedViews(root, root, false)
return axOrderViews
}
@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text
import android.text.Layout
import com.facebook.proguard.annotations.DoNotStrip
/**
* Encapsulates an {android.text.Layout} along with any additional state needed to render or measure
* it.
*/
@DoNotStrip
internal class PreparedLayout(public val layout: Layout, public val maximumNumberOfLines: Int)
@@ -31,6 +31,7 @@ import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.UnstableReactNativeAPI;
import com.facebook.react.common.mapbuffer.MapBuffer;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
import com.facebook.react.uimanager.PixelUtil;
@@ -503,7 +504,8 @@ public class TextLayoutManager {
}
}
private static Layout createLayout(
@UnstableReactNativeAPI
public static Layout createLayout(
@NonNull Context context,
MapBuffer attributedString,
MapBuffer paragraphAttributes,
@@ -732,6 +734,47 @@ public class TextLayoutManager {
return YogaMeasureOutput.make(widthInSP, heightInSP);
}
@UnstableReactNativeAPI
public static float[] measurePreparedLayout(
PreparedLayout preparedLayout,
float width,
YogaMeasureMode widthYogaMeasureMode,
float height,
YogaMeasureMode heightYogaMeasureMode) {
Layout layout = preparedLayout.getLayout();
Spanned text = (Spanned) layout.getText();
int maximumNumberOfLines = preparedLayout.getMaximumNumberOfLines();
int calculatedLineCount = calculateLineCount(layout, maximumNumberOfLines);
float calculatedWidth =
calculateWidth(layout, text, width, widthYogaMeasureMode, calculatedLineCount);
float calculatedHeight =
calculateHeight(layout, text, height, heightYogaMeasureMode, calculatedLineCount);
ArrayList<Float> retList = new ArrayList<>();
retList.add(PixelUtil.toDIPFromPixel(calculatedWidth));
retList.add(PixelUtil.toDIPFromPixel(calculatedHeight));
AttachmentMetrics metrics = new AttachmentMetrics();
int lastAttachmentFoundInSpan;
for (int i = 0; i < text.length(); i = lastAttachmentFoundInSpan) {
lastAttachmentFoundInSpan =
nextAttachmentMetrics(layout, text, calculatedWidth, calculatedLineCount, i, metrics);
if (metrics.wasFound) {
retList.add(PixelUtil.toDIPFromPixel(metrics.top));
retList.add(PixelUtil.toDIPFromPixel(metrics.left));
retList.add(PixelUtil.toDIPFromPixel(metrics.width));
retList.add(PixelUtil.toDIPFromPixel(metrics.height));
}
}
float[] ret = new float[retList.size()];
for (int i = 0; i < retList.size(); i++) {
ret[i] = retList.get(i);
}
return ret;
}
private static int calculateLineCount(Layout layout, int maximumNumberOfLines) {
return maximumNumberOfLines == ReactConstants.UNSET || maximumNumberOfLines == 0
? layout.getLineCount()
@@ -753,9 +796,19 @@ public class TextLayoutManager {
for (int lineIndex = 0; lineIndex < calculatedLineCount; lineIndex++) {
boolean endsWithNewLine =
text.length() > 0 && text.charAt(layout.getLineEnd(lineIndex) - 1) == '\n';
if (!endsWithNewLine && lineIndex + 1 < layout.getLineCount()) {
calculatedWidth = width;
break;
// Line-wrapping or ellipsizing to truncate should result in taking the full available width
// of the container, instead of width after line-breaking/ellipsizing.
if (ReactNativeFeatureFlags.incorporateMaxLinesDuringAndroidLayout()) {
if ((!endsWithNewLine && lineIndex + 1 < layout.getLineCount())
|| layout.getEllipsisCount(lineIndex) > 0) {
calculatedWidth = width;
break;
}
} else {
if (!endsWithNewLine && lineIndex + 1 < layout.getLineCount()) {
calculatedWidth = width;
break;
}
}
float lineWidth =
endsWithNewLine ? layout.getLineMax(lineIndex) : layout.getLineWidth(lineIndex);
@@ -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<<c89831646046b10c7b2a40e14deb3d1a>>
* @generated SignedSource<<49dbfe02e06cc5d6b12683ed91ea1d13>>
*/
/**
@@ -99,6 +99,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableDestroyShadowTreeRevisionAsync() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableDestroyShadowTreeRevisionAsync");
return method(javaProvider_);
}
bool enableDoubleMeasurementFixAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableDoubleMeasurementFixAndroid");
@@ -183,6 +189,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enablePreparedTextLayout() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enablePreparedTextLayout");
return method(javaProvider_);
}
bool enablePropsUpdateReconciliationAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enablePropsUpdateReconciliationAndroid");
@@ -363,6 +375,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableCustomFocusSearchOnClippedElement
return ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::enableDestroyShadowTreeRevisionAsync(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync();
}
bool JReactNativeFeatureFlagsCxxInterop::enableDoubleMeasurementFixAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid();
@@ -433,6 +450,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableNewBackgroundAndBorderDrawables(
return ReactNativeFeatureFlags::enableNewBackgroundAndBorderDrawables();
}
bool JReactNativeFeatureFlagsCxxInterop::enablePreparedTextLayout(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enablePreparedTextLayout();
}
bool JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid();
@@ -599,6 +621,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableCustomFocusSearchOnClippedElementsAndroid",
JReactNativeFeatureFlagsCxxInterop::enableCustomFocusSearchOnClippedElementsAndroid),
makeNativeMethod(
"enableDestroyShadowTreeRevisionAsync",
JReactNativeFeatureFlagsCxxInterop::enableDestroyShadowTreeRevisionAsync),
makeNativeMethod(
"enableDoubleMeasurementFixAndroid",
JReactNativeFeatureFlagsCxxInterop::enableDoubleMeasurementFixAndroid),
@@ -641,6 +666,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableNewBackgroundAndBorderDrawables",
JReactNativeFeatureFlagsCxxInterop::enableNewBackgroundAndBorderDrawables),
makeNativeMethod(
"enablePreparedTextLayout",
JReactNativeFeatureFlagsCxxInterop::enablePreparedTextLayout),
makeNativeMethod(
"enablePropsUpdateReconciliationAndroid",
JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid),
@@ -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<<2a5ad62d2cb4f1111391f1b566e10c1d>>
* @generated SignedSource<<ae23312f2dccee934a8a91c05625662a>>
*/
/**
@@ -60,6 +60,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableCustomFocusSearchOnClippedElementsAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableDestroyShadowTreeRevisionAsync(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableDoubleMeasurementFixAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -102,6 +105,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableNewBackgroundAndBorderDrawables(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enablePreparedTextLayout(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enablePropsUpdateReconciliationAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -46,12 +46,12 @@ Pod::Spec.new do |s|
s.dependency "React-Core"
s.dependency "React-debug"
s.dependency "React-featureflags"
s.dependency "React-utils"
s.dependency "React-runtimescheduler"
s.dependency "React-cxxreact"
add_dependency(s, "React-rendererdebug")
add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"])
add_dependency(s, "React-utils", :additional_framework_paths => ["react/utils/platform/ios"])
depend_on_js_engine(s)
add_rn_third_party_dependencies(s)
@@ -12,17 +12,17 @@
#include <cstdint>
#include <string_view>
#define REACT_NATIVE_VERSION_MAJOR 1000
#define REACT_NATIVE_VERSION_MINOR 0
#define REACT_NATIVE_VERSION_MAJOR 0
#define REACT_NATIVE_VERSION_MINOR 80
#define REACT_NATIVE_VERSION_PATCH 0
namespace facebook::react {
constexpr struct {
int32_t Major = 1000;
int32_t Minor = 0;
int32_t Major = 0;
int32_t Minor = 80;
int32_t Patch = 0;
std::string_view Prerelease = "";
std::string_view Prerelease = "rc.0";
} ReactNativeVersion;
} // namespace facebook::react
@@ -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<<0eb63ead191be88440cbb73fd760dec7>>
* @generated SignedSource<<8410561a80edd67b4528181b1f8557fe>>
*/
/**
@@ -66,6 +66,10 @@ bool ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid()
return getAccessor().enableCustomFocusSearchOnClippedElementsAndroid();
}
bool ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync() {
return getAccessor().enableDestroyShadowTreeRevisionAsync();
}
bool ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid() {
return getAccessor().enableDoubleMeasurementFixAndroid();
}
@@ -122,6 +126,10 @@ bool ReactNativeFeatureFlags::enableNewBackgroundAndBorderDrawables() {
return getAccessor().enableNewBackgroundAndBorderDrawables();
}
bool ReactNativeFeatureFlags::enablePreparedTextLayout() {
return getAccessor().enablePreparedTextLayout();
}
bool ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid() {
return getAccessor().enablePropsUpdateReconciliationAndroid();
}
@@ -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<<80dcd75591a392144c2fd2817cc4408a>>
* @generated SignedSource<<8b135b02d868914f6b3487f09e8955ff>>
*/
/**
@@ -89,6 +89,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableCustomFocusSearchOnClippedElementsAndroid();
/**
* Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.
*/
RN_EXPORT static bool enableDestroyShadowTreeRevisionAsync();
/**
* When enabled a subset of components will avoid double measurement on Android.
*/
@@ -159,6 +164,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableNewBackgroundAndBorderDrawables();
/**
* Enables caching text layout artifacts for later reuse
*/
RN_EXPORT static bool enablePreparedTextLayout();
/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
@@ -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<<0c39183e7858453bfc18515be46eb17f>>
* @generated SignedSource<<18cf18662a540ff1bc0f151cd53bcb0d>>
*/
/**
@@ -209,6 +209,24 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomFocusSearchOnClippedElementsAn
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableDestroyShadowTreeRevisionAsync() {
auto flagValue = enableDestroyShadowTreeRevisionAsync_.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(10, "enableDestroyShadowTreeRevisionAsync");
flagValue = currentProvider_->enableDestroyShadowTreeRevisionAsync();
enableDestroyShadowTreeRevisionAsync_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() {
auto flagValue = enableDoubleMeasurementFixAndroid_.load();
@@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(10, "enableDoubleMeasurementFixAndroid");
markFlagAsAccessed(11, "enableDoubleMeasurementFixAndroid");
flagValue = currentProvider_->enableDoubleMeasurementFixAndroid();
enableDoubleMeasurementFixAndroid_ = flagValue;
@@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(11, "enableEagerRootViewAttachment");
markFlagAsAccessed(12, "enableEagerRootViewAttachment");
flagValue = currentProvider_->enableEagerRootViewAttachment();
enableEagerRootViewAttachment_ = flagValue;
@@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(12, "enableFabricLogs");
markFlagAsAccessed(13, "enableFabricLogs");
flagValue = currentProvider_->enableFabricLogs();
enableFabricLogs_ = flagValue;
@@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRenderer() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(13, "enableFabricRenderer");
markFlagAsAccessed(14, "enableFabricRenderer");
flagValue = currentProvider_->enableFabricRenderer();
enableFabricRenderer_ = flagValue;
@@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFixForParentTagDuringReparenting() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(14, "enableFixForParentTagDuringReparenting");
markFlagAsAccessed(15, "enableFixForParentTagDuringReparenting");
flagValue = currentProvider_->enableFixForParentTagDuringReparenting();
enableFixForParentTagDuringReparenting_ = flagValue;
@@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(15, "enableFontScaleChangesUpdatingLayout");
markFlagAsAccessed(16, "enableFontScaleChangesUpdatingLayout");
flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout();
enableFontScaleChangesUpdatingLayout_ = flagValue;
@@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(16, "enableIOSViewClipToPaddingBox");
markFlagAsAccessed(17, "enableIOSViewClipToPaddingBox");
flagValue = currentProvider_->enableIOSViewClipToPaddingBox();
enableIOSViewClipToPaddingBox_ = flagValue;
@@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableJSRuntimeGCOnMemoryPressureOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(17, "enableJSRuntimeGCOnMemoryPressureOnIOS");
markFlagAsAccessed(18, "enableJSRuntimeGCOnMemoryPressureOnIOS");
flagValue = currentProvider_->enableJSRuntimeGCOnMemoryPressureOnIOS();
enableJSRuntimeGCOnMemoryPressureOnIOS_ = flagValue;
@@ -362,7 +380,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(18, "enableLayoutAnimationsOnAndroid");
markFlagAsAccessed(19, "enableLayoutAnimationsOnAndroid");
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
enableLayoutAnimationsOnAndroid_ = flagValue;
@@ -380,7 +398,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(19, "enableLayoutAnimationsOnIOS");
markFlagAsAccessed(20, "enableLayoutAnimationsOnIOS");
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
enableLayoutAnimationsOnIOS_ = flagValue;
@@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueModulesOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(20, "enableMainQueueModulesOnIOS");
markFlagAsAccessed(21, "enableMainQueueModulesOnIOS");
flagValue = currentProvider_->enableMainQueueModulesOnIOS();
enableMainQueueModulesOnIOS_ = flagValue;
@@ -416,7 +434,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(21, "enableNativeCSSParsing");
markFlagAsAccessed(22, "enableNativeCSSParsing");
flagValue = currentProvider_->enableNativeCSSParsing();
enableNativeCSSParsing_ = flagValue;
@@ -434,7 +452,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(22, "enableNetworkEventReporting");
markFlagAsAccessed(23, "enableNetworkEventReporting");
flagValue = currentProvider_->enableNetworkEventReporting();
enableNetworkEventReporting_ = flagValue;
@@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "enableNewBackgroundAndBorderDrawables");
markFlagAsAccessed(24, "enableNewBackgroundAndBorderDrawables");
flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables();
enableNewBackgroundAndBorderDrawables_ = flagValue;
@@ -461,6 +479,24 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() {
auto flagValue = enablePreparedTextLayout_.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(25, "enablePreparedTextLayout");
flagValue = currentProvider_->enablePreparedTextLayout();
enablePreparedTextLayout_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
auto flagValue = enablePropsUpdateReconciliationAndroid_.load();
@@ -470,7 +506,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(24, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(26, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -488,7 +524,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(25, "enableResourceTimingAPI");
markFlagAsAccessed(27, "enableResourceTimingAPI");
flagValue = currentProvider_->enableResourceTimingAPI();
enableResourceTimingAPI_ = flagValue;
@@ -506,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "enableSynchronousStateUpdates");
markFlagAsAccessed(28, "enableSynchronousStateUpdates");
flagValue = currentProvider_->enableSynchronousStateUpdates();
enableSynchronousStateUpdates_ = flagValue;
@@ -524,7 +560,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(27, "enableViewCulling");
markFlagAsAccessed(29, "enableViewCulling");
flagValue = currentProvider_->enableViewCulling();
enableViewCulling_ = flagValue;
@@ -542,7 +578,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(28, "enableViewRecycling");
markFlagAsAccessed(30, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@@ -560,7 +596,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(29, "enableViewRecyclingForText");
markFlagAsAccessed(31, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -578,7 +614,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(30, "enableViewRecyclingForView");
markFlagAsAccessed(32, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -596,7 +632,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(31, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(33, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -614,7 +650,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(32, "fuseboxEnabledRelease");
markFlagAsAccessed(34, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -632,7 +668,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(33, "fuseboxNetworkInspectionEnabled");
markFlagAsAccessed(35, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -650,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::incorporateMaxLinesDuringAndroidLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(34, "incorporateMaxLinesDuringAndroidLayout");
markFlagAsAccessed(36, "incorporateMaxLinesDuringAndroidLayout");
flagValue = currentProvider_->incorporateMaxLinesDuringAndroidLayout();
incorporateMaxLinesDuringAndroidLayout_ = flagValue;
@@ -668,7 +704,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(35, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(37, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -686,7 +722,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(36, "updateRuntimeShadowNodeReferencesOnCommit");
markFlagAsAccessed(38, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -704,7 +740,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(37, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(39, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -722,7 +758,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(38, "useFabricInterop");
markFlagAsAccessed(40, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -740,7 +776,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(39, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(41, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -758,7 +794,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(40, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(42, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -776,7 +812,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(41, "useRawPropsJsiValue");
markFlagAsAccessed(43, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -794,7 +830,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(42, "useShadowNodeStateOnClone");
markFlagAsAccessed(44, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -812,7 +848,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(43, "useTurboModuleInterop");
markFlagAsAccessed(45, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -830,7 +866,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(44, "useTurboModules");
markFlagAsAccessed(46, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = 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<<336504fac084993c9aff86f274d45342>>
* @generated SignedSource<<2736c730fcd0b82717faa0fea527d007>>
*/
/**
@@ -42,6 +42,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableBridgelessArchitecture();
bool enableCppPropsIteratorSetter();
bool enableCustomFocusSearchOnClippedElementsAndroid();
bool enableDestroyShadowTreeRevisionAsync();
bool enableDoubleMeasurementFixAndroid();
bool enableEagerRootViewAttachment();
bool enableFabricLogs();
@@ -56,6 +57,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableNativeCSSParsing();
bool enableNetworkEventReporting();
bool enableNewBackgroundAndBorderDrawables();
bool enablePreparedTextLayout();
bool enablePropsUpdateReconciliationAndroid();
bool enableResourceTimingAPI();
bool enableSynchronousStateUpdates();
@@ -88,7 +90,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 45> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 47> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> animatedShouldSignalBatch_;
@@ -100,6 +102,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableBridgelessArchitecture_;
std::atomic<std::optional<bool>> enableCppPropsIteratorSetter_;
std::atomic<std::optional<bool>> enableCustomFocusSearchOnClippedElementsAndroid_;
std::atomic<std::optional<bool>> enableDestroyShadowTreeRevisionAsync_;
std::atomic<std::optional<bool>> enableDoubleMeasurementFixAndroid_;
std::atomic<std::optional<bool>> enableEagerRootViewAttachment_;
std::atomic<std::optional<bool>> enableFabricLogs_;
@@ -114,6 +117,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableNativeCSSParsing_;
std::atomic<std::optional<bool>> enableNetworkEventReporting_;
std::atomic<std::optional<bool>> enableNewBackgroundAndBorderDrawables_;
std::atomic<std::optional<bool>> enablePreparedTextLayout_;
std::atomic<std::optional<bool>> enablePropsUpdateReconciliationAndroid_;
std::atomic<std::optional<bool>> enableResourceTimingAPI_;
std::atomic<std::optional<bool>> enableSynchronousStateUpdates_;
@@ -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<<825294594363fb672b6cd64eaa954dd4>>
* @generated SignedSource<<ef056e269a4ed514c18ebaed89885661>>
*/
/**
@@ -67,6 +67,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return true;
}
bool enableDestroyShadowTreeRevisionAsync() override {
return false;
}
bool enableDoubleMeasurementFixAndroid() override {
return false;
}
@@ -123,6 +127,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return true;
}
bool enablePreparedTextLayout() override {
return false;
}
bool enablePropsUpdateReconciliationAndroid() override {
return 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<<d9dc055d9cda1ac17b7039aa70691a6b>>
* @generated SignedSource<<53e92dc49c9d5b93de14cde2fd319e92>>
*/
/**
@@ -135,6 +135,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableCustomFocusSearchOnClippedElementsAndroid();
}
bool enableDestroyShadowTreeRevisionAsync() override {
auto value = values_["enableDestroyShadowTreeRevisionAsync"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableDestroyShadowTreeRevisionAsync();
}
bool enableDoubleMeasurementFixAndroid() override {
auto value = values_["enableDoubleMeasurementFixAndroid"];
if (!value.isNull()) {
@@ -261,6 +270,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableNewBackgroundAndBorderDrawables();
}
bool enablePreparedTextLayout() override {
auto value = values_["enablePreparedTextLayout"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enablePreparedTextLayout();
}
bool enablePropsUpdateReconciliationAndroid() override {
auto value = values_["enablePropsUpdateReconciliationAndroid"];
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<<905f26c85dc731205b1a6f44c4e32468>>
* @generated SignedSource<<7ad3c1ab9913ee5b20d0f6217580a14c>>
*/
/**
@@ -35,6 +35,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableBridgelessArchitecture() = 0;
virtual bool enableCppPropsIteratorSetter() = 0;
virtual bool enableCustomFocusSearchOnClippedElementsAndroid() = 0;
virtual bool enableDestroyShadowTreeRevisionAsync() = 0;
virtual bool enableDoubleMeasurementFixAndroid() = 0;
virtual bool enableEagerRootViewAttachment() = 0;
virtual bool enableFabricLogs() = 0;
@@ -49,6 +50,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableNativeCSSParsing() = 0;
virtual bool enableNetworkEventReporting() = 0;
virtual bool enableNewBackgroundAndBorderDrawables() = 0;
virtual bool enablePreparedTextLayout() = 0;
virtual bool enablePropsUpdateReconciliationAndroid() = 0;
virtual bool enableResourceTimingAPI() = 0;
virtual bool enableSynchronousStateUpdates() = 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<<614814062283858cf70138a2ac3d6304>>
* @generated SignedSource<<cf54fabb5023a846cadd14482ea80999>>
*/
/**
@@ -94,6 +94,11 @@ bool NativeReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndr
return ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid();
}
bool NativeReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync();
}
bool NativeReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid();
@@ -164,6 +169,11 @@ bool NativeReactNativeFeatureFlags::enableNewBackgroundAndBorderDrawables(
return ReactNativeFeatureFlags::enableNewBackgroundAndBorderDrawables();
}
bool NativeReactNativeFeatureFlags::enablePreparedTextLayout(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enablePreparedTextLayout();
}
bool NativeReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid();
@@ -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<<1c0248c3eda43cc89a61b3e3ff8b4345>>
* @generated SignedSource<<4169b34b13c62ab90d7976de908b5f16>>
*/
/**
@@ -57,6 +57,8 @@ class NativeReactNativeFeatureFlags
bool enableCustomFocusSearchOnClippedElementsAndroid(jsi::Runtime& runtime);
bool enableDestroyShadowTreeRevisionAsync(jsi::Runtime& runtime);
bool enableDoubleMeasurementFixAndroid(jsi::Runtime& runtime);
bool enableEagerRootViewAttachment(jsi::Runtime& runtime);
@@ -85,6 +87,8 @@ class NativeReactNativeFeatureFlags
bool enableNewBackgroundAndBorderDrawables(jsi::Runtime& runtime);
bool enablePreparedTextLayout(jsi::Runtime& runtime);
bool enablePropsUpdateReconciliationAndroid(jsi::Runtime& runtime);
bool enableResourceTimingAPI(jsi::Runtime& runtime);
@@ -165,15 +165,23 @@ void ParagraphShadowNode::updateStateIfNeeded(const Content& content) {
Size ParagraphShadowNode::measureContent(
const LayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
if constexpr (TextLayoutManagerExtended::supportsPreparedLayout()) {
for (const auto& preparedLayout : preparedLayouts_) {
if (preparedLayout.layoutConstraints == layoutConstraints) {
return preparedLayout.measureSize;
}
}
}
auto content =
getContentWithMeasuredAttachments(layoutContext, layoutConstraints);
auto attributedString = content.attributedString;
if (attributedString.isEmpty()) {
// Note: `zero-width space` is insufficient in some cases (e.g. when we need
// to measure the "height" of the font).
// TODO T67606511: We will redefine the measurement of empty strings as part
// of T67606511
// Note: `zero-width space` is insufficient in some cases (e.g. when we
// need to measure the "height" of the font).
// TODO T67606511: We will redefine the measurement of empty strings as
// part of T67606511
auto string = BaseTextShadowNode::getEmptyPlaceholder();
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
@@ -186,6 +194,28 @@ Size ParagraphShadowNode::measureContent(
.surfaceId = getSurfaceId(),
};
if constexpr (TextLayoutManagerExtended::supportsPreparedLayout()) {
if (ReactNativeFeatureFlags::enablePreparedTextLayout()) {
TextLayoutManagerExtended tme(*textLayoutManager_);
auto preparedLayout = tme.prepareLayout(
attributedString,
content.paragraphAttributes,
textLayoutContext,
layoutConstraints);
auto mesaurements = tme.measurePreparedLayout(
preparedLayout, textLayoutContext, layoutConstraints);
preparedLayouts_.push_back(PreparedLayoutResult{
layoutConstraints,
mesaurements.size,
// PreparedLayout is not trivially copyable on all platforms
// NOLINTNEXTLINE(performance-move-const-arg)
std::move(preparedLayout)});
return mesaurements.size;
}
}
return textLayoutManager_
->measure(
AttributedStringBox{attributedString},
@@ -206,10 +236,10 @@ Float ParagraphShadowNode::baseline(
auto attributedString = content.attributedString;
if (attributedString.isEmpty()) {
// Note: `zero-width space` is insufficient in some cases (e.g. when we need
// to measure the "height" of the font).
// TODO T67606511: We will redefine the measurement of empty strings as part
// of T67606511
// Note: `zero-width space` is insufficient in some cases (e.g. when we
// need to measure the "height" of the font).
// TODO T67606511: We will redefine the measurement of empty strings as
// part of T67606511
auto string = BaseTextShadowNode::getEmptyPlaceholder();
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
@@ -281,8 +311,8 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
// `paragraphShadowNode` that represents clones of `this` object.
auto paragraphShadowNode = static_cast<ParagraphShadowNode*>(this);
// `paragraphOwningShadowNode` is owning pointer to`paragraphShadowNode`
// (besides the initial case when `paragraphShadowNode == this`), we need this
// only to keep it in memory for a while.
// (besides the initial case when `paragraphShadowNode == this`), we need
// this only to keep it in memory for a while.
auto paragraphOwningShadowNode = ShadowNode::Unshared{};
react_native_assert(
@@ -313,7 +343,7 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
const auto& attachmentMeasurement = measurement.attachments[i];
if (attachmentMeasurement.isClipped) {
layoutableShadowNode.setLayoutMetrics(
LayoutMetrics{.displayType = DisplayType::None});
LayoutMetrics{.frame = {}, .displayType = DisplayType::None});
continue;
}
@@ -333,15 +363,15 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
layoutableShadowNode.layoutTree(
attachmentLayoutContext, attachmentLayoutConstrains);
// Altering the origin of the `ShadowNode` (which is defined by text layout,
// not by internal styles and state).
// Altering the origin of the `ShadowNode` (which is defined by text
// layout, not by internal styles and state).
auto attachmentLayoutMetrics = layoutableShadowNode.getLayoutMetrics();
attachmentLayoutMetrics.frame.origin = attachmentOrigin;
layoutableShadowNode.setLayoutMetrics(attachmentLayoutMetrics);
}
// If we ended up cloning something, we need to update the list of children to
// reflect the changes that we made.
// If we ended up cloning something, we need to update the list of children
// to reflect the changes that we made.
if (paragraphShadowNode != this) {
this->children_ =
static_cast<const ParagraphShadowNode*>(paragraphShadowNode)->children_;
@@ -15,6 +15,7 @@
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
#include <react/renderer/textlayoutmanager/TextLayoutManagerExtended.h>
namespace facebook::react {
@@ -115,6 +116,18 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode<
* Cached content of the subtree started from the node.
*/
mutable std::optional<Content> content_{};
/*
* Intermediate layout results generated during measurement, that may be
* reused by the platform.
*/
struct PreparedLayoutResult {
LayoutConstraints layoutConstraints;
Size measureSize;
TextLayoutManagerExtended::PreparedLayout preparedLayout{};
};
mutable std::vector<PreparedLayoutResult> preparedLayouts_;
};
} // namespace facebook::react
@@ -9,7 +9,9 @@
#include <cxxreact/TraceSection.h>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/utils/LowPriorityExecutor.h>
#include <condition_variable>
#include "updateMountedFlag.h"
@@ -186,6 +188,9 @@ std::optional<MountingTransaction> MountingCoordinator::pullTransaction(
#endif
if (lastRevision_.has_value()) {
if (ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync()) {
LowPriorityExecutor::execute([toDelete = std::move(baseRevision_)]() {});
}
baseRevision_ = std::move(*lastRevision_);
lastRevision_.reset();
@@ -8,6 +8,7 @@
#pragma once
#include <glog/logging.h>
#include <cstddef>
#include <react/renderer/attributedstring/AttributedStringBox.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
@@ -17,7 +18,36 @@
namespace facebook::react {
template <typename TextLayoutManagerT>
concept TextLayoutManagerWithPreparedLayout = requires(
TextLayoutManagerT textLayoutManager,
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
TextLayoutContext layoutContext,
LayoutConstraints layoutConstraints,
typename TextLayoutManagerT::PreparedLayout preparedLayout) {
sizeof(typename TextLayoutManagerT::PreparedLayout);
{
textLayoutManager.prepareLayout(
attributedString, paragraphAttributes, layoutContext, layoutConstraints)
} -> std::same_as<typename TextLayoutManagerT::PreparedLayout>;
{
textLayoutManager.measurePreparedLayout(
preparedLayout, layoutContext, layoutConstraints)
} -> std::same_as<TextMeasurement>;
};
namespace detail {
template <typename T>
struct PreparedLayoutT {
using type = std::nullptr_t;
};
template <TextLayoutManagerWithPreparedLayout T>
struct PreparedLayoutT<T> {
using type = typename T::PreparedLayout;
};
/**
* TextLayoutManagerExtended acts as an adapter for TextLayoutManager methods
* which may not exist for a specific platform. Callers can check at
@@ -36,6 +66,12 @@ class TextLayoutManagerExtended {
};
}
static constexpr bool supportsPreparedLayout() {
return TextLayoutManagerWithPreparedLayout<TextLayoutManagerT>;
}
using PreparedLayout = typename PreparedLayoutT<TextLayoutManagerT>::type;
TextLayoutManagerExtended(const TextLayoutManagerT& textLayoutManager)
: textLayoutManager_(textLayoutManager) {}
@@ -50,6 +86,33 @@ class TextLayoutManagerExtended {
LOG(FATAL) << "Platform TextLayoutManager does not support measureLines";
}
PreparedLayout prepareLayout(
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
if constexpr (supportsPreparedLayout()) {
return textLayoutManager_.prepareLayout(
attributedString,
paragraphAttributes,
layoutContext,
layoutConstraints);
}
LOG(FATAL) << "Platform TextLayoutManager does not support prepareLayout";
}
TextMeasurement measurePreparedLayout(
const PreparedLayout& layout,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
if constexpr (supportsPreparedLayout()) {
return textLayoutManager_.measurePreparedLayout(
layout, layoutContext, layoutConstraints);
}
LOG(FATAL)
<< "Platform TextLayoutManager does not support measurePreparedLayout";
}
private:
const TextLayoutManagerT& textLayoutManager_;
};
@@ -0,0 +1,20 @@
/*
* 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>
namespace facebook::react {
class JPreparedLayout : public jni::JavaClass<JPreparedLayout> {
public:
static auto constexpr kJavaDescriptor =
"Lcom/facebook/react/views/text/PreparedLayout;";
};
} // namespace facebook::react
@@ -5,21 +5,25 @@
* LICENSE file in the root directory of this source tree.
*/
#include "TextLayoutManager.h"
#include <span>
#include <utility>
#include <react/common/mapbuffer/JReadableMapBuffer.h>
#include <react/debug/react_native_assert.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/core/conversions.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
#include <react/renderer/telemetry/TransactionTelemetry.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
#include <react/renderer/textlayoutmanager/TextLayoutManagerExtended.h>
namespace facebook::react {
static_assert(TextLayoutManagerExtended::supportsLineMeasurement());
static_assert(TextLayoutManagerExtended::supportsPreparedLayout());
namespace {
int countAttachments(const AttributedString& attributedString) {
@@ -289,4 +293,95 @@ LinesMeasurements TextLayoutManager::measureLines(
return lineMeasurements;
}
TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const {
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto prepareLayout =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<JPreparedLayout::javaobject(
jint,
JReadableMapBuffer::javaobject,
JReadableMapBuffer::javaobject,
jfloat,
jfloat)>("prepareLayout");
auto attributedStringMB =
JReadableMapBuffer::createWithContents(toMapBuffer(attributedString));
auto paragraphAttributesMB =
JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes));
// T222682416: We don't have any global cache here. We should investigate
// whether that is desirable
return {jni::make_global(prepareLayout(
fabricUIManager,
layoutContext.surfaceId,
attributedStringMB.get(),
paragraphAttributesMB.get(),
layoutConstraints.maximumSize.width,
layoutConstraints.maximumSize.height))};
}
TextMeasurement TextLayoutManager::measurePreparedLayout(
const PreparedLayout& preparedLayout,
const TextLayoutContext& /*layoutContext*/,
const LayoutConstraints& layoutConstraints) const {
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto measurePreparedLayout =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<jni::JArrayFloat(
JPreparedLayout::javaobject, jfloat, jfloat, jfloat, jfloat)>(
"measurePreparedLayout");
auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;
auto measurementsArr = measurePreparedLayout(
fabricUIManager,
preparedLayout.get(),
minimumSize.width,
maximumSize.width,
minimumSize.height,
maximumSize.height);
auto measurements = measurementsArr->getRegion(
0, static_cast<jsize>(measurementsArr->size()));
react_native_assert(measurementsArr->size() >= 2);
react_native_assert((measurementsArr->size() - 2) % 4 == 0);
TextMeasurement textMeasurement;
textMeasurement.size.width = measurements[0];
textMeasurement.size.height = measurements[1];
if (measurementsArr->size() > 2) {
textMeasurement.attachments.reserve((measurementsArr->size() - 2) / 4);
for (size_t i = 2; i < measurementsArr->size(); i += 4) {
auto top = measurements[i];
auto left = measurements[i + 1];
auto width = measurements[i + 2];
auto height = measurements[i + 3];
if (std::isnan(top) || std::isnan(left)) {
textMeasurement.attachments.push_back(
TextMeasurement::Attachment{.frame = Rect{}, .isClipped = true});
} else {
textMeasurement.attachments.push_back(TextMeasurement::Attachment{
.frame =
{.origin = {.x = left, .y = top},
.size = {.width = width, .height = height}},
.isClipped = false});
}
}
}
return textMeasurement;
}
} // namespace facebook::react
@@ -7,12 +7,16 @@
#pragma once
#include <react/jni/SafeReleaseJniRef.h>
#include <react/renderer/attributedstring/AttributedStringBox.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/textlayoutmanager/JPreparedLayout.h>
#include <react/renderer/textlayoutmanager/TextLayoutContext.h>
#include <react/renderer/textlayoutmanager/TextMeasureCache.h>
#include <react/utils/ContextContainer.h>
#include <fbjni/fbjni.h>
#include <memory>
namespace facebook::react {
@@ -25,6 +29,8 @@ class TextLayoutManager;
*/
class TextLayoutManager {
public:
using PreparedLayout = SafeReleaseJniRef<jni::global_ref<JPreparedLayout>>;
TextLayoutManager(const ContextContainer::Shared& contextContainer);
/*
@@ -67,6 +73,24 @@ class TextLayoutManager {
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;
/**
* Create a platform representation of fully laid out text, to later be
* reused.
*/
PreparedLayout prepareLayout(
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const;
/**
* Derive text and attachment measurements from a PreparedLayout.
*/
TextMeasurement measurePreparedLayout(
const PreparedLayout& layout,
const TextLayoutContext& layoutContext,
const LayoutConstraints& layoutConstraints) const;
private:
std::shared_ptr<const ContextContainer> contextContainer_;
TextMeasureCache textMeasureCache_;
@@ -8,10 +8,14 @@ set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB react_utils_SRC CONFIGURE_DEPENDS *.cpp *.mm)
file(GLOB react_utils_SRC CONFIGURE_DEPENDS *.cpp platform/android/react/utils/*.cpp)
add_library(react_utils OBJECT ${react_utils_SRC})
target_include_directories(react_utils PUBLIC ${REACT_COMMON_DIR})
target_include_directories(react_utils
PUBLIC
${REACT_COMMON_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
)
target_link_libraries(react_utils
glog
@@ -16,12 +16,12 @@ else
source[:tag] = "v#{version}"
end
header_search_paths = [
"\"$(PODS_TARGET_SRCROOT)\"",
"\"$(PODS_TARGET_SRCROOT)/ReactCommon\"",
]
Pod::Spec.new do |s|
source_files = "*.{m,mm,cpp,h}", "platform/ios/**/*.{m,mm,cpp,h}"
header_search_paths = [
"\"$(PODS_TARGET_SRCROOT)/../../\"",
]
s.name = "React-utils"
s.version = version
s.summary = "-" # TODO
@@ -30,18 +30,21 @@ Pod::Spec.new do |s|
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = "**/*.{cpp,h,mm}"
s.source_files = source_files
s.header_dir = "react/utils"
s.exclude_files = "tests"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
"DEFINES_MODULE" => "YES" }
if ENV['USE_FRAMEWORKS']
s.module_name = "React_utils"
s.header_mappings_dir = "../.."
header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""]
end
s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO",
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
"DEFINES_MODULE" => "YES" }
s.dependency "React-jsi", version
depend_on_js_engine(s)
@@ -0,0 +1,95 @@
/*
* 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 <pthread.h>
#include <sched.h>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
namespace facebook::react::LowPriorityExecutor {
struct LowPriorityExecutorThread {
LowPriorityExecutorThread() : thread_{std::thread([this] { run(); })} {
pthread_t hThread = thread_.native_handle();
struct sched_param param {};
param.sched_priority = 19; // Higher value means lower priority
pthread_setschedparam(hThread, SCHED_OTHER, &param);
}
// Deleted constructors
LowPriorityExecutorThread(const LowPriorityExecutorThread& other) = delete;
LowPriorityExecutorThread(LowPriorityExecutorThread&& other) = delete;
LowPriorityExecutorThread& operator=(const LowPriorityExecutorThread& other) =
delete;
LowPriorityExecutorThread& operator=(LowPriorityExecutorThread&& other) =
delete;
~LowPriorityExecutorThread() {
// Stop the thread
{
std::lock_guard<std::mutex> lock(mutex_);
running_ = false;
}
// Unblock the thread to check the running_ flag and terminate.
cv_.notify_one();
// Wait for thread completion to avoid use-after-free on background thread.
thread_.join();
}
void post(std::function<void()>&& workItem) {
// Move the object to the queue.
{
std::lock_guard<std::mutex> lock(mutex_);
queue_.emplace(std::move(workItem));
}
// Notify the background thread.
cv_.notify_one();
}
private:
void run() {
pthread_setname_np(pthread_self(), "LowPriorityExecutorThread");
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
// Wait until an object is in the queue or the thread is stopped.
cv_.wait(lock, [this] { return !queue_.empty() || !running_; });
// Empty the queue.
while (!queue_.empty()) {
queue_.front()();
queue_.pop();
}
// Check if the thread is stopping.
if (!running_) {
break;
}
}
}
std::string threadName_;
std::thread thread_;
std::queue<std::function<void()>> queue_;
std::mutex mutex_;
std::condition_variable cv_;
bool running_{true};
};
void execute(std::function<void()>&& workItem) {
static LowPriorityExecutorThread thread{};
thread.post(std::move(workItem));
}
} // namespace facebook::react::LowPriorityExecutor
@@ -0,0 +1,14 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()>&& workItem);
} // namespace facebook::react::LowPriorityExecutor
@@ -0,0 +1,18 @@
/*
* 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 <functional>
namespace facebook::react::LowPriorityExecutor {
inline void execute(std::function<void()>&& workItem) {
workItem();
}
} // namespace facebook::react::LowPriorityExecutor
@@ -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.
*/
#pragma once
#include <functional>
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()>&& workItem);
} // namespace facebook::react::LowPriorityExecutor
@@ -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.
*/
#import "LowPriorityExecutor.h"
#import <Foundation/Foundation.h>
namespace facebook::react::LowPriorityExecutor {
void execute(std::function<void()> &&workItem)
{
std::function<void()> localWorkItem = std::move(workItem);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
localWorkItem();
});
}
} // namespace facebook::react::LowPriorityExecutor
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.80.0-rc.0",
"description": "A framework for building native apps using React",
"license": "MIT",
"repository": {
@@ -154,13 +154,13 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.80.0-main",
"@react-native/codegen": "0.80.0-main",
"@react-native/community-cli-plugin": "0.80.0-main",
"@react-native/gradle-plugin": "0.80.0-main",
"@react-native/js-polyfills": "0.80.0-main",
"@react-native/normalize-colors": "0.80.0-main",
"@react-native/virtualized-lists": "0.80.0-main",
"@react-native/assets-registry": "0.80.0-rc.0",
"@react-native/codegen": "0.80.0-rc.0",
"@react-native/community-cli-plugin": "0.80.0-rc.0",
"@react-native/gradle-plugin": "0.80.0-rc.0",
"@react-native/js-polyfills": "0.80.0-rc.0",
"@react-native/normalize-colors": "0.80.0-rc.0",
"@react-native/virtualized-lists": "0.80.0-rc.0",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -155,6 +155,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
enableDestroyShadowTreeRevisionAsync: {
defaultValue: false,
metadata: {
dateAdded: '2025-04-29',
description:
'Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
enableDoubleMeasurementFixAndroid: {
defaultValue: false,
metadata: {
@@ -297,6 +308,16 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
enablePreparedTextLayout: {
defaultValue: false,
metadata: {
dateAdded: '2025-05-01',
description: 'Enables caching text layout artifacts for later reuse',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
enablePropsUpdateReconciliationAndroid: {
defaultValue: false,
metadata: {
@@ -0,0 +1 @@
hermes-2025-05-06-RNv0.80.0-4eb6132a5bf0450bf4c6c91987675381d7ac8bca
@@ -0,0 +1,61 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import ReactNativeElement from '../../webapis/dom/nodes/ReadOnlyNode';
import ensureInstance from './ensureInstance';
import * as Fantom from '@react-native/fantom';
export function createShadowNodeReferenceCounter(
element: ReactNativeElement,
): () => number {
const getReferenceCount = Fantom.createShadowNodeReferenceCounter(element);
// Create the reference counting function in a helper instead of creating a
// closure here, which would unintentionally retain a reference to `element`.
return createExpirationChecker(getReferenceCount);
}
function createExpirationChecker(
getReferenceCount: () => number,
): () => number {
return () => {
Fantom.runTask(() => {
global.gc();
});
return getReferenceCount();
};
}
export function createShadowNodeReferenceCountingRef(): [
() => number,
React.RefSetter<mixed>,
] {
let getReferenceCount: ?() => number;
function getShadowNodeReferenceCount() {
if (getReferenceCount == null) {
throw new Error('ShadowNode reference counter was not initialized.');
}
return getReferenceCount();
}
function ref(instance: mixed | null) {
if (instance == null) {
return;
}
const element = ensureInstance(instance, ReactNativeElement);
if (getReferenceCount != null) {
throw new Error('ShadowNode reference counter was already initialized.');
}
getReferenceCount = createShadowNodeReferenceCounter(element);
}
return [getShadowNodeReferenceCount, ref];
}
@@ -0,0 +1,210 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import 'react-native/Libraries/Core/InitializeCore';
import type {Node} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes';
import {getNodeFromPublicInstance} from '../../../../../Libraries/ReactPrivate/ReactNativePrivateInterface';
import ReactNativeElement from '../../../webapis/dom/nodes/ReactNativeElement';
import ensureInstance from '../ensureInstance';
import isUnreachable from '../isUnreachable';
import {
createShadowNodeReferenceCounter,
createShadowNodeReferenceCountingRef,
} from '../ShadowNodeReferenceCounter';
import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {View} from 'react-native';
test('shadow node expires when root is destroyed', () => {
const root = Fantom.createRoot();
const [getReferenceCount, ref] = createShadowNodeReferenceCountingRef();
Fantom.runTask(() => {
root.render(<View ref={ref} />);
});
expect(getReferenceCount()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.destroy();
});
expect(getReferenceCount()).toBe(0);
});
test('element is not retained by `createShadowNodeReferenceCounter`', () => {
const root = Fantom.createRoot();
let elementWeakRef: ?WeakRef<ReactNativeElement>;
let getReferenceCount: ?() => number;
function ref(instance: React.ElementRef<typeof View> | null) {
if (instance == null) {
return;
}
const element = ensureInstance(instance, ReactNativeElement);
elementWeakRef = new WeakRef(element);
getReferenceCount = createShadowNodeReferenceCounter(element);
}
Fantom.runTask(() => {
root.render(
<View>
<View ref={ref} />
</View>,
);
});
expect(isUnreachable(nullthrows(elementWeakRef))).toBe(false);
expect(getReferenceCount?.()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.destroy();
});
expect(isUnreachable(nullthrows(elementWeakRef))).toBe(true);
expect(getReferenceCount?.()).toBe(0);
});
test('shadow node expires when JavaScript element is reachable', () => {
const root = Fantom.createRoot();
let element: ?ReactNativeElement;
let getReferenceCount: ?() => number;
function ref(instance: React.ElementRef<typeof View> | null) {
if (instance == null) {
return;
}
element = ensureInstance(instance, ReactNativeElement);
getReferenceCount = createShadowNodeReferenceCounter(element);
}
Fantom.runTask(() => {
root.render(
<View>
<View ref={ref} />
</View>,
);
});
expect(element).not.toBe(undefined);
expect(getNodeFromPublicInstance(nullthrows(element))).not.toBe(null);
expect(getReferenceCount?.()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.destroy();
});
expect(element).not.toBe(undefined);
expect(getNodeFromPublicInstance(nullthrows(element))).toBe(null);
expect(getReferenceCount?.()).toBe(0);
});
test('shadow node is retained when JavaScript node is reachable', () => {
const root = Fantom.createRoot();
let node: ?Node;
let getReferenceCount: ?() => number;
function ref(instance: React.ElementRef<typeof View> | null) {
if (instance == null) {
return;
}
const element = ensureInstance(instance, ReactNativeElement);
node = getNodeFromPublicInstance(element);
getReferenceCount = createShadowNodeReferenceCounter(element);
}
Fantom.runTask(() => {
root.render(
<View>
<View ref={ref} />
</View>,
);
});
expect(node).not.toBe(undefined);
expect(getReferenceCount?.()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.destroy();
});
expect(node).not.toBe(undefined);
expect(getReferenceCount?.()).toBeGreaterThan(0);
});
test('shadow node expires when replaced by null', () => {
const root = Fantom.createRoot();
const [getReferenceCount, ref] = createShadowNodeReferenceCountingRef();
Fantom.runTask(() => {
root.render(
<View>
<View ref={ref} />
</View>,
);
});
expect(getReferenceCount()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.render(<View>{null}</View>);
});
// TODO (T223254666): Delete this and figure out why test fails.
Fantom.runTask(() => {
root.render(<View>{null}</View>);
});
expect(getReferenceCount()).toBe(0);
});
test('shadow node expires when replaced by another view', () => {
const root = Fantom.createRoot();
const [getReferenceCount, ref] = createShadowNodeReferenceCountingRef();
Fantom.runTask(() => {
root.render(
<View>
<View key="a" ref={ref} />
</View>,
);
});
expect(getReferenceCount()).toBeGreaterThan(0);
Fantom.runTask(() => {
root.render(
<View>
<View key="b" />
</View>,
);
});
// TODO (T223254666): Delete this and figure out why test fails.
Fantom.runTask(() => {
root.render(
<View>
<View key="b" />
</View>,
);
});
expect(getReferenceCount()).toBe(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<<fab0bb6a5cd8a2b8366e8184659188c0>>
* @generated SignedSource<<6667fc8e4fdd2db0b54c908155b110cf>>
* @flow strict
*/
@@ -56,6 +56,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableBridgelessArchitecture: Getter<boolean>,
enableCppPropsIteratorSetter: Getter<boolean>,
enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean>,
enableDestroyShadowTreeRevisionAsync: Getter<boolean>,
enableDoubleMeasurementFixAndroid: Getter<boolean>,
enableEagerRootViewAttachment: Getter<boolean>,
enableFabricLogs: Getter<boolean>,
@@ -70,6 +71,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableNativeCSSParsing: Getter<boolean>,
enableNetworkEventReporting: Getter<boolean>,
enableNewBackgroundAndBorderDrawables: Getter<boolean>,
enablePreparedTextLayout: Getter<boolean>,
enablePropsUpdateReconciliationAndroid: Getter<boolean>,
enableResourceTimingAPI: Getter<boolean>,
enableSynchronousStateUpdates: Getter<boolean>,
@@ -197,6 +199,10 @@ export const enableCppPropsIteratorSetter: Getter<boolean> = createNativeFlagGet
* This enables the fabric implementation of focus search so that we can focus clipped elements
*/
export const enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean> = createNativeFlagGetter('enableCustomFocusSearchOnClippedElementsAndroid', true);
/**
* Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.
*/
export const enableDestroyShadowTreeRevisionAsync: Getter<boolean> = createNativeFlagGetter('enableDestroyShadowTreeRevisionAsync', false);
/**
* When enabled a subset of components will avoid double measurement on Android.
*/
@@ -253,6 +259,10 @@ export const enableNetworkEventReporting: Getter<boolean> = createNativeFlagGett
* Use BackgroundDrawable and BorderDrawable instead of CSSBackgroundDrawable
*/
export const enableNewBackgroundAndBorderDrawables: Getter<boolean> = createNativeFlagGetter('enableNewBackgroundAndBorderDrawables', true);
/**
* Enables caching text layout artifacts for later reuse
*/
export const enablePreparedTextLayout: Getter<boolean> = createNativeFlagGetter('enablePreparedTextLayout', false);
/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
@@ -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<<715d2a137ded85479120f5090c05b3b6>>
* @generated SignedSource<<4bf477efaec82ad8e9e4b1bc17705fd2>>
* @flow strict
*/
@@ -34,6 +34,7 @@ export interface Spec extends TurboModule {
+enableBridgelessArchitecture?: () => boolean;
+enableCppPropsIteratorSetter?: () => boolean;
+enableCustomFocusSearchOnClippedElementsAndroid?: () => boolean;
+enableDestroyShadowTreeRevisionAsync?: () => boolean;
+enableDoubleMeasurementFixAndroid?: () => boolean;
+enableEagerRootViewAttachment?: () => boolean;
+enableFabricLogs?: () => boolean;
@@ -48,6 +49,7 @@ export interface Spec extends TurboModule {
+enableNativeCSSParsing?: () => boolean;
+enableNetworkEventReporting?: () => boolean;
+enableNewBackgroundAndBorderDrawables?: () => boolean;
+enablePreparedTextLayout?: () => boolean;
+enablePropsUpdateReconciliationAndroid?: () => boolean;
+enableResourceTimingAPI?: () => boolean;
+enableSynchronousStateUpdates?: () => boolean;
@@ -93,6 +93,9 @@ interface Spec extends TurboModule {
validateEmptyMessageQueue: () => void;
getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string;
reportTestSuiteResultsJSON: (results: string) => void;
createShadowNodeReferenceCounter(
shadowNode: mixed /* ShadowNode */,
): () => number;
saveJSMemoryHeapSnapshot: (filePath: string) => void;
}
@@ -49,9 +49,10 @@ Pod::Spec.new do |spec|
mkdir -p Headers
XCFRAMEWORK_PATH=$(find "$CURRENT_PATH" -type d -name "ReactNativeDependencies.xcframework")
HEADERS_PATH=$(find "$XCFRAMEWORK_PATH" -type d -name "Headers" | head -n 1)
rsync -a "$HEADERS_PATH/" Headers
cp -R "$HEADERS_PATH/" Headers
mkdir -p framework/packages/react-native
rsync -a --remove-source-files "$XCFRAMEWORK_PATH/.." framework/packages/react-native/
cp -R "$XCFRAMEWORK_PATH/.." framework/packages/react-native/
find "$XCFRAMEWORK_PATH/.." -type f -exec rm {} +
find "$CURRENT_PATH" -type d -empty -delete
CMD
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/tester",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"private": true,
"description": "React Native tester app.",
"license": "MIT",
@@ -26,9 +26,9 @@
"e2e-test-ios": "./scripts/maestro-test-ios.sh"
},
"dependencies": {
"@react-native/oss-library-example": "0.80.0-main",
"@react-native/new-app-screen": "0.80.0-main",
"@react-native/popup-menu-android": "0.80.0-main",
"@react-native/oss-library-example": "0.80.0-rc.0",
"@react-native/new-app-screen": "0.80.0-rc.0",
"@react-native/popup-menu-android": "0.80.0-rc.0",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/typescript-config",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Default TypeScript configuration for React Native apps",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/virtualized-lists",
"version": "0.80.0-main",
"version": "0.80.0-rc.0",
"description": "Virtualized lists for React Native.",
"license": "MIT",
"repository": {