Compare commits

..
Author SHA1 Message Date
Rob Hogan ba5b9e4c29 Add flow types for memfs
Summary:
Add a Flow lib def for `memfs`, for use in internal and subsequently Metro tests, now that `metro-memory-fs` is deprecated.

Changelog: [Internal]

Reviewed By: vzaidman

Differential Revision: D78268713
2025-07-15 02:24:35 -07:00
230 changed files with 3133 additions and 2643 deletions
+9
View File
@@ -9,3 +9,12 @@ end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
[*.gradle]
indent_size = 4
[*.kts]
indent_size = 4
[BUCK]
indent_size = 4
@@ -14,23 +14,23 @@ runs:
uses: actions/download-artifact@v4
with:
name: hermes-workspace
path: 'C:\tmp\hermes'
path: 'D:\tmp\hermes'
- name: Set up workspace
shell: powershell
run: |
mkdir -p C:\tmp\hermes\osx-bin
mkdir -p D:\tmp\hermes\osx-bin
mkdir -p .\packages\react-native\sdks\hermes
cp -r -Force C:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force D:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force .\packages\react-native\sdks\hermes-engine\utils\* .\packages\react-native\sdks\hermes\.
- name: Windows cache
uses: actions/cache@v4
with:
key: v3-hermes-${{ github.job }}-windows-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
path: |
C:\tmp\hermes\win64-bin\
C:\tmp\hermes\hermes\icu\
C:\tmp\hermes\hermes\deps\
C:\tmp\hermes\hermes\build_release\
D:\tmp\hermes\win64-bin\
D:\tmp\hermes\hermes\icu\
D:\tmp\hermes\hermes\deps\
D:\tmp\hermes\hermes\build_release\
- name: setup-msbuild
uses: microsoft/setup-msbuild@v1.3.2
- name: Set up workspace
@@ -83,4 +83,4 @@ runs:
uses: actions/upload-artifact@v4.3.4
with:
name: hermes-win64-bin
path: C:\tmp\hermes\win64-bin\
path: D:\tmp\hermes\win64-bin\
+3 -3
View File
@@ -128,9 +128,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
+3 -3
View File
@@ -125,9 +125,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
+4 -4
View File
@@ -382,9 +382,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
@@ -583,7 +583,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ["24", "22", "20"]
node-version: ["24", "22"]
steps:
- name: Checkout
uses: actions/checkout@v4
-30
View File
@@ -1,35 +1,5 @@
# Changelog
## v0.81.0-rc.1
### Added
#### iOS specific
- Add release/debug switch script for React-Core-prebuilt ([42d1a7934c](https://github.com/facebook/react-native/commit/42d1a7934cad4b2c92653e3fa7781c2af8f44df4) by [@chrfalch](https://github.com/chrfalch))
- Added support for using USE_FRAMEWORKS with prebuilt React Native Core ([40e45f5366](https://github.com/facebook/react-native/commit/40e45f53661ce80c3a6fbbf07f52dc900afcad52) by [@chrfalch](https://github.com/chrfalch))
- Add the `ENTERPRISE_REPOSITORY` env variable to cocopaods infra ([23f3bf9239](https://github.com/facebook/react-native/commit/23f3bf9239a849590f1c72b25732d0090780128c) by [@cipolleschi](https://github.com/cipolleschi))
### Changed
- Bump Metro to 0.83.0 ([6b9f5d622f](https://github.com/facebook/react-native/commit/6b9f5d622ffbe79da8f4e7b7d8094504a480425e) by [@robhogan](https://github.com/robhogan))
#### Android specific
- Gradle to 8.14.3 ([6892dde363](https://github.com/facebook/react-native/commit/6892dde36373bbef2d0afe535ae818b1a7164f08) by [@cortinico](https://github.com/cortinico))
- Expose `react_renderer_bridging` headers via prefab ([d1730ff960](https://github.com/facebook/react-native/commit/d1730ff960fcb9a01ee94b9e46e5a9fbb7d73f4a) by [@tomekzaw](https://github.com/tomekzaw))
- Introduce more deprecation warnings for Legacy Arch classes ([625f69f284](https://github.com/facebook/react-native/commit/625f69f284ddfd9c6beecaa4052a871d092053ef) by [@cortinico](https://github.com/cortinico))
### Fixed
- Fixed nodes with `display: contents` set being cloned with the wrong owner ([d4b36b0300](https://github.com/facebook/react-native/commit/d4b36b03003eb2de9eaf5b57bb639bae8cc12f20) by [@j-piasecki](https://github.com/j-piasecki))
#### iOS specific
- Fixed premature return in header file generation from podspec globs ([f2b064c2d4](https://github.com/facebook/react-native/commit/f2b064c2d40c39017ac2a31bf3caf8acef23038c) by [@chrfalch](https://github.com/chrfalch))
## v0.81.0-rc.0
### Breaking
-28
View File
@@ -13,7 +13,6 @@ plugins {
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.binary.compatibility.validator) apply true
alias(libs.plugins.android.test) apply false
alias(libs.plugins.ktfmt) apply true
}
val reactAndroidProperties = java.util.Properties()
@@ -131,30 +130,3 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
}
}
}
ktfmt {
blockIndent.set(2)
continuationIndent.set(4)
maxWidth.set(100)
removeUnusedImports.set(false)
manageTrailingCommas.set(false)
}
// Configure ktfmt tasks to include gradle-plugin
listOf("ktfmtCheck", "ktfmtFormat").forEach { taskName ->
tasks.named(taskName) { dependsOn(gradle.includedBuild("gradle-plugin").task(":$taskName")) }
}
allprojects {
// Apply exclusions for specific files that should not be formatted
val excludePatterns =
listOf(
"**/build/**",
"**/hermes-engine/**",
"**/internal/featureflags/**",
"**/systeminfo/ReactNativeVersion.kt")
listOf(
com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask::class,
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class)
.forEach { tasks.withType(it) { exclude(excludePatterns) } }
}
-2
View File
@@ -19,8 +19,6 @@
"featureflags": "yarn --cwd packages/react-native featureflags",
"js-api-diff": "node ./scripts/js-api/diff-api-snapshot",
"lint-ci": "./.github/workflow-scripts/analyze_code.sh && yarn shellcheck",
"lint-kotlin-check": "./gradlew ktfmtCheck",
"lint-kotlin": "./gradlew ktfmtFormat",
"lint-markdown": "markdownlint-cli2 2>&1",
"lint": "eslint --max-warnings 0 .",
"prettier": "prettier --write \"./**/*.{js,md,yml,ts,tsx}\"",
+1 -1
View File
@@ -17,7 +17,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"files": [
"path-support.js",
+1 -1
View File
@@ -19,7 +19,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"files": [
"index.js"
+1 -1
View File
@@ -43,6 +43,6 @@
}
},
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"files": [
"dist"
+1 -1
View File
@@ -20,6 +20,6 @@
"BUILD_INFO"
],
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
}
}
+1 -1
View File
@@ -26,7 +26,7 @@
},
"license": "MIT",
"engines": {
"node": ">= 20.19.4",
"node": ">= 22.14.0",
"electron": ">=36.3.0"
},
"dependencies": {
@@ -10,25 +10,9 @@
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
const {app} = require('electron') as any;
const util = require('util');
// Handle global command line arguments which don't require a window
// or the single instance lock to be held.
const {
values: {version = false},
} = util.parseArgs({
options: {version: {type: 'boolean'}},
args: process.argv.slice(app.isPackaged ? 1 : 2),
strict: false,
});
if (version) {
console.log(`${app.getName()} v${app.getVersion()}`);
// Not app.quit() - we want to exit immediately without initialising the graphical subsystem.
app.exit(0);
}
const gotTheLock = app.requestSingleInstanceLock({
argv: process.argv.slice(app.isPackaged ? 1 : 2),
argv: process.argv.slice(2),
});
if (!gotTheLock) {
+1 -1
View File
@@ -35,7 +35,7 @@
"ws": "^6.2.3"
},
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"devDependencies": {
"selfsigned": "^2.4.1",
@@ -16,7 +16,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"main": "index.js",
"dependencies": {
@@ -22,6 +22,6 @@
"hermes-eslint": "0.29.1"
},
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
}
}
+1 -1
View File
@@ -36,6 +36,6 @@
"hermes-eslint": "0.29.1"
},
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
}
}
+1 -22
View File
@@ -5,10 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
plugins {
alias(libs.plugins.kotlin.jvm).apply(false)
alias(libs.plugins.ktfmt).apply(true)
}
plugins { alias(libs.plugins.kotlin.jvm).apply(false) }
tasks.register("build") {
dependsOn(
@@ -27,21 +24,3 @@ tasks.register("clean") {
":shared:clean",
)
}
tasks.named("ktfmtCheck") {
dependsOn(
":react-native-gradle-plugin:ktfmtCheck",
":settings-plugin:ktfmtCheck",
":shared-testutil:ktfmtCheck",
":shared:ktfmtCheck",
)
}
tasks.named("ktfmtFormat") {
dependsOn(
":react-native-gradle-plugin:ktfmtFormat",
":settings-plugin:ktfmtFormat",
":shared-testutil:ktfmtFormat",
":shared:ktfmtFormat",
)
}
@@ -6,7 +6,6 @@ javapoet = "1.13.0"
junit = "4.13.2"
kotlin = "2.1.20"
assertj = "3.25.1"
ktfmt = "0.22.0"
[libraries]
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
@@ -19,4 +18,3 @@ assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmt" }
+1 -1
View File
@@ -16,7 +16,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"scripts": {
"build": "./gradlew build",
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktfmt)
id("java-gradle-plugin")
}
@@ -18,7 +18,6 @@ import com.facebook.react.tasks.GenerateEntryPointTask
import com.facebook.react.tasks.GeneratePackageListTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildTypesForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevServerLocation
import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
@@ -85,7 +84,6 @@ class ReactPlugin : Plugin<Project> {
configureAutolinking(project, extension)
configureCodegen(project, extension, rootExtension, isLibrary = false)
configureResources(project, extension)
configureBuildTypesForApp(project)
}
// Library Only Configuration
@@ -19,7 +19,6 @@ import java.net.Inet4Address
import java.net.NetworkInterface
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import kotlin.plus
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.plugins.AppliedPlugin
@@ -28,26 +27,6 @@ import org.w3c.dom.Element
@Suppress("UnstableApiUsage")
internal object AgpConfiguratorUtils {
fun configureBuildTypesForApp(project: Project) {
val action =
Action<AppliedPlugin> {
project.extensions
.getByType(ApplicationAndroidComponentsExtension::class.java)
.finalizeDsl { ext ->
ext.buildTypes {
val debug =
getByName("debug").apply {
manifestPlaceholders["usesCleartextTraffic"] = "true"
}
getByName("release").apply {
manifestPlaceholders["usesCleartextTraffic"] = "false"
}
}
}
}
project.pluginManager.withPlugin("com.android.application", action)
}
fun configureBuildConfigFieldsForApp(project: Project, extension: ReactExtension) {
val action =
Action<AppliedPlugin> {
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktfmt)
id("java-gradle-plugin")
}
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktfmt)
}
plugins { alias(libs.plugins.kotlin.jvm) }
repositories { mavenCentral() }
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktfmt)
}
plugins { alias(libs.plugins.kotlin.jvm) }
repositories { mavenCentral() }
+1 -1
View File
@@ -16,7 +16,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"exports": {
".": "./src/index.js",
+1 -1
View File
@@ -30,6 +30,6 @@
}
},
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
}
}
@@ -16,7 +16,6 @@ describe('console.timeStamp()', () => {
});
it("doesn't throw when label is not specified", () => {
// $FlowExpectedError[incompatible-call] not passing label intentionally
expect(() => console.timeStamp()).not.toThrow();
});
@@ -26,6 +25,7 @@ describe('console.timeStamp()', () => {
it("doesn't throw when additional arguments are specified", () => {
expect(() =>
// $FlowExpectedError[extra-arg]
console.timeStamp('label', 100, 500, 'Track', 'Group', 'error'),
).not.toThrow();
});
@@ -34,7 +34,7 @@ describe('console.timeStamp()', () => {
// $FlowExpectedError[incompatible-call]
expect(() => console.timeStamp({})).not.toThrow();
expect(() =>
// $FlowExpectedError[incompatible-call]
// $FlowExpectedError[extra-arg]
console.timeStamp('label', true, null, {}, [], () => {}),
).not.toThrow();
});
+6 -10
View File
@@ -569,7 +569,10 @@ function consoleAssertPolyfill(expression, label) {
}
}
function stub() {}
// https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp_static.
// Non-standard API for recording markers on a timeline of the Performance instrumentation.
// The actual logging is not provided by definition.
function consoleTimeStampPolyfill() {}
if (global.nativeLoggingHook) {
const originalConsole = global.console;
@@ -582,11 +585,7 @@ if (global.nativeLoggingHook) {
}
global.console = {
time: stub,
timeEnd: stub,
timeStamp: stub,
count: stub,
countReset: stub,
timeStamp: consoleTimeStampPolyfill,
...(originalConsole ?? {}),
error: getNativeLogFunction(LOG_LEVELS.error),
info: getNativeLogFunction(LOG_LEVELS.info),
@@ -677,6 +676,7 @@ if (global.nativeLoggingHook) {
});
}
} else if (!global.console) {
function stub() {}
const log = global.print || stub;
global.console = {
@@ -692,8 +692,6 @@ if (global.nativeLoggingHook) {
}
},
clear: stub,
count: stub,
countReset: stub,
dir: stub,
dirxml: stub,
group: stub,
@@ -702,8 +700,6 @@ if (global.nativeLoggingHook) {
profile: stub,
profileEnd: stub,
table: stub,
time: stub,
timeEnd: stub,
timeStamp: stub,
};
+1 -1
View File
@@ -18,7 +18,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"files": [
"console.js",
@@ -13,7 +13,7 @@
],
"license": "MIT",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"main": "src/index.js",
"files": [
+14 -22
View File
@@ -38,6 +38,19 @@ function isFirstParty(fileName) {
// use `this.foo = bar` instead of `this.defineProperty('foo', ...)`
const loose = true;
const defaultPlugins = [
[require('babel-plugin-syntax-hermes-parser'), {parseLangTypes: 'flow'}],
[require('babel-plugin-transform-flow-enums')],
[require('@babel/plugin-transform-block-scoping')],
[require('@babel/plugin-transform-class-properties'), {loose}],
[require('@babel/plugin-transform-private-methods'), {loose}],
[require('@babel/plugin-transform-private-property-in-object'), {loose}],
[require('@babel/plugin-syntax-dynamic-import')],
[require('@babel/plugin-syntax-export-default-from')],
...passthroughSyntaxPlugins,
[require('@babel/plugin-transform-unicode-regex')],
];
// For Static Hermes testing (experimental), the hermes-canary transformProfile
// is used to enable regenerator (and some related lowering passes) because SH
// requires more Babel lowering than Hermes temporarily.
@@ -221,28 +234,7 @@ const getPreset = (src, options) => {
plugins: [require('@babel/plugin-transform-flow-strip-types')],
},
{
plugins: [
[
require('babel-plugin-syntax-hermes-parser'),
{
parseLangTypes: 'flow',
reactRuntimeTarget: '19',
...options.hermesParserOptions,
},
],
[require('babel-plugin-transform-flow-enums')],
[require('@babel/plugin-transform-block-scoping')],
[require('@babel/plugin-transform-class-properties'), {loose}],
[require('@babel/plugin-transform-private-methods'), {loose}],
[
require('@babel/plugin-transform-private-property-in-object'),
{loose},
],
[require('@babel/plugin-syntax-dynamic-import')],
[require('@babel/plugin-syntax-export-default-from')],
...passthroughSyntaxPlugins,
[require('@babel/plugin-transform-unicode-regex')],
],
plugins: defaultPlugins,
},
{
test: isTypeScriptSource,
@@ -14,7 +14,7 @@
],
"license": "MIT",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"main": "src/index.js",
"files": [
+1 -1
View File
@@ -18,7 +18,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"scripts": {
"build": "yarn clean && node scripts/build.js --verbose",
@@ -19,7 +19,7 @@
],
"bugs": "https://github.com/facebook/react-native/issues",
"engines": {
"node": ">= 20.19.4"
"node": ">= 22.14.0"
},
"exports": {
".": "./src/index.js",
@@ -31,8 +31,12 @@ function mockQueueMicrotask() {
}
describe('Animated', () => {
let ReactNativeFeatureFlags;
beforeEach(() => {
jest.resetModules();
ReactNativeFeatureFlags = require('../../../src/private/featureflags/ReactNativeFeatureFlags');
});
mockQueueMicrotask();
@@ -109,7 +113,50 @@ describe('Animated', () => {
expect(callback.mock.calls.length).toBe(1);
});
it('does not detach on updates', async () => {
ReactNativeFeatureFlags.override({
scheduleAnimatedCleanupInMicrotask: () => false,
});
const opacity = new Animated.Value(0);
jest.spyOn(opacity, '__detach');
const root = await create(<Animated.View style={{opacity}} />);
expect(opacity.__detach).not.toBeCalled();
await update(root, <Animated.View style={{opacity}} />);
expect(opacity.__detach).not.toBeCalled();
await unmount(root);
expect(opacity.__detach).toBeCalled();
});
it('stops animation when detached', async () => {
ReactNativeFeatureFlags.override({
scheduleAnimatedCleanupInMicrotask: () => false,
});
const opacity = new Animated.Value(0);
const callback = jest.fn();
const root = await create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: false,
}).start(callback);
await unmount(root);
expect(callback).toBeCalledWith({finished: false});
});
it('detaches only on unmount (in a microtask)', async () => {
ReactNativeFeatureFlags.override({
scheduleAnimatedCleanupInMicrotask: () => true,
});
const opacity = new Animated.Value(0);
jest.spyOn(opacity, '__detach');
@@ -128,6 +175,10 @@ describe('Animated', () => {
});
it('restores default values only on update (in a microtask)', async () => {
ReactNativeFeatureFlags.override({
scheduleAnimatedCleanupInMicrotask: () => true,
});
const __restoreDefaultValues = jest.spyOn(
AnimatedProps.prototype,
'__restoreDefaultValues',
@@ -162,6 +213,10 @@ describe('Animated', () => {
});
it('stops animation when detached (in a microtask)', async () => {
ReactNativeFeatureFlags.override({
scheduleAnimatedCleanupInMicrotask: () => true,
});
const opacity = new Animated.Value(0);
const callback = jest.fn();
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @fantom_flags enableFixForParentTagDuringReparenting:true
* @fantom_flags enableSynchronousStateUpdates:true
* @fantom_flags enableViewCulling:true
* @flow strict-local
* @format
@@ -2555,56 +2556,3 @@ describe('culling inside ScrollView with overflow visible', () => {
);
});
});
describe('horizontal ScrollView in RTL script', () => {
it('renders item 1', () => {
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
Fantom.runTask(() => {
root.render(
<ScrollView
style={{direction: 'rtl', height: 100, width: 100}}
horizontal={true}>
<View nativeID={'item1'} style={{height: 90, width: 90, margin: 5}} />
<View nativeID={'item2'} style={{height: 90, width: 90, margin: 5}} />
</ScrollView>,
);
});
expect(root.takeMountingManagerLogs()).toEqual([
'Update {type: "RootView", nativeID: (root)}',
'Create {type: "ScrollView", nativeID: (N/A)}',
'Create {type: "AndroidHorizontalScrollContentView", nativeID: (N/A)}',
'Create {type: "View", nativeID: "item1"}',
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "item1"}',
'Insert {type: "AndroidHorizontalScrollContentView", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
]);
});
it('takes contentOffset into account', () => {
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
Fantom.runTask(() => {
root.render(
<ScrollView
style={{direction: 'rtl', height: 100, width: 100}}
horizontal={true}
contentOffset={{x: 100, y: 0}}>
<View nativeID={'item1'} style={{height: 90, width: 90, margin: 5}} />
<View nativeID={'item2'} style={{height: 90, width: 90, margin: 5}} />
</ScrollView>,
);
});
expect(root.takeMountingManagerLogs()).toEqual([
'Update {type: "RootView", nativeID: (root)}',
'Create {type: "ScrollView", nativeID: (N/A)}',
'Create {type: "AndroidHorizontalScrollContentView", nativeID: (N/A)}',
'Create {type: "View", nativeID: "item2"}',
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "item2"}',
'Insert {type: "AndroidHorizontalScrollContentView", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
]);
});
});
@@ -10,14 +10,9 @@
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import type {HostInstance} from 'react-native';
import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
import * as Fantom from '@react-native/fantom';
import * as React from 'react';
import {createRef} from 'react';
import {View} from 'react-native';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
describe('<View>', () => {
describe('width and height style', () => {
@@ -186,40 +181,6 @@ describe('<View>', () => {
<rn-view transform='[{"translateX": 10.000000}]' />,
);
});
[
[undefined, {x: -5, y: 0, width: 20, height: 10}],
['50% 50%', {x: -5, y: 0, width: 20, height: 10}],
['top left', {x: 0, y: 0, width: 20, height: 10}],
['right bottom', {x: -10, y: 0, width: 20, height: 10}],
].forEach(([transformOrigin, expectedBounds]) => {
it(`applies transformOrigin correctly for ${String(transformOrigin)}`, () => {
const root = Fantom.createRoot();
const viewRef = createRef<HostInstance>();
Fantom.runTask(() => {
root.render(
<View
ref={viewRef}
style={{
width: 10,
height: 10,
transform: [{scaleX: 2}],
transformOrigin,
}}
/>,
);
});
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
const viewBounds = viewElement.getBoundingClientRect();
expect(viewBounds.x).toBe(expectedBounds.x);
expect(viewBounds.y).toBe(expectedBounds.y);
expect(viewBounds.width).toBe(expectedBounds.width);
expect(viewBounds.height).toBe(expectedBounds.height);
});
});
});
describe('props', () => {
@@ -49,7 +49,6 @@ function _defineCheckVersionTests() {
// $FlowFixMe[cannot-write]
console.error = jest.fn();
// $FlowFixMe[cannot-write]
// $FlowFixMe[unsafe-addition]
global.console = {error: jest.fn(error => (consoleOutput += error))};
spyOnConsoleError = jest.spyOn(global.console, 'error');
});
+1 -2
View File
@@ -8,13 +8,12 @@
* @format
*/
import Performance from '../../src/private/webapis/performance/Performance';
import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance';
// In case if the native implementation of the Performance API is available, use it,
// otherwise fall back to the legacy/default one, which only defines 'Performance.now()'
if (NativePerformance) {
const Performance =
require('../../src/private/webapis/performance/Performance').default;
// $FlowExpectedError[cannot-write]
global.performance = new Performance();
} else {
@@ -4,32 +4,180 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
*/
import type {EventSubscription} from '../vendor/emitter/EventEmitter';
import type {Task} from './TaskQueue';
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
import EventEmitter from '../vendor/emitter/EventEmitter';
const BatchedBridge = require('../BatchedBridge/BatchedBridge').default;
const TaskQueue = require('./TaskQueue').default;
const invariant = require('invariant');
export type SimpleTask = {
name: string,
run: () => void,
};
export type PromiseTask = {
name: string,
gen: () => Promise<void>,
};
export type Task = SimpleTask | PromiseTask | (() => void);
export type {Task, SimpleTask, PromiseTask} from './TaskQueue';
export type Handle = number;
// NOTE: The original implementation of `InteractionManager` never rejected
// the returned promise. This preserves that behavior in the stub.
function reject(error: Error): void {
setTimeout(() => {
throw error;
}, 0);
const _emitter = new EventEmitter<{
interactionComplete: [],
interactionStart: [],
}>();
const DEBUG_DELAY: 0 = 0;
const DEBUG: false = false;
const InteractionManagerImpl = {
Events: {
interactionStart: 'interactionStart',
interactionComplete: 'interactionComplete',
},
/**
* Schedule a function to run after all interactions have completed. Returns a cancellable
* "promise".
*/
runAfterInteractions(task: ?Task): {
then: <U>(
onFulfill?: ?(void) => ?(Promise<U> | U),
onReject?: ?(error: mixed) => ?(Promise<U> | U),
) => Promise<U>,
cancel: () => void,
...
} {
const tasks: Array<Task> = [];
const promise = new Promise((resolve: () => void) => {
_scheduleUpdate();
if (task) {
tasks.push(task);
}
tasks.push({
run: resolve,
name: 'resolve ' + ((task && task.name) || '?'),
});
_taskQueue.enqueueTasks(tasks);
});
return {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
then: promise.then.bind(promise),
cancel: function () {
_taskQueue.cancelTasks(tasks);
},
};
},
/**
* Notify manager that an interaction has started.
*/
createInteractionHandle(): Handle {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('InteractionManager: create interaction handle');
_scheduleUpdate();
const handle = ++_inc;
_addInteractionSet.add(handle);
return handle;
},
/**
* Notify manager that an interaction has completed.
*/
clearInteractionHandle(handle: Handle) {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('InteractionManager: clear interaction handle');
invariant(!!handle, 'InteractionManager: Must provide a handle to clear.');
_scheduleUpdate();
_addInteractionSet.delete(handle);
_deleteInteractionSet.add(handle);
},
// $FlowFixMe[unclear-type] unclear type of _emitter
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
addListener: _emitter.addListener.bind(_emitter) as (
eventType: string,
// $FlowFixMe[unclear-type] unclear type of arguments
listener: (...args: any) => mixed,
context: mixed,
) => EventSubscription,
/**
* A positive number will use setTimeout to schedule any tasks after the
* eventLoopRunningTime hits the deadline value, otherwise all tasks will be
* executed in one setImmediate batch (default).
*/
setDeadline(deadline: number) {
_deadline = deadline;
},
};
const _interactionSet = new Set<number | Handle>();
const _addInteractionSet = new Set<number | Handle>();
const _deleteInteractionSet = new Set<Handle>();
const _taskQueue = new TaskQueue({onMoreTasks: _scheduleUpdate});
let _nextUpdateHandle: $FlowFixMe | TimeoutID = 0;
let _inc = 0;
let _deadline = -1;
/**
* Schedule an asynchronous update to the interaction state.
*/
function _scheduleUpdate() {
if (!_nextUpdateHandle) {
if (_deadline > 0) {
_nextUpdateHandle = setTimeout(_processUpdate, 0 + DEBUG_DELAY);
} else {
_nextUpdateHandle = setImmediate(_processUpdate);
}
}
}
/**
* Notify listeners, process queue, etc
*/
function _processUpdate() {
_nextUpdateHandle = 0;
const interactionCount = _interactionSet.size;
_addInteractionSet.forEach(handle => _interactionSet.add(handle));
_deleteInteractionSet.forEach(handle => _interactionSet.delete(handle));
const nextInteractionCount = _interactionSet.size;
if (interactionCount !== 0 && nextInteractionCount === 0) {
// transition from 1+ --> 0 interactions
/* $FlowFixMe[prop-missing] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
/* $FlowFixMe[invalid-computed-prop] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
_emitter.emit(InteractionManager.Events.interactionComplete);
} else if (interactionCount === 0 && nextInteractionCount !== 0) {
// transition from 0 --> 1+ interactions
/* $FlowFixMe[prop-missing] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
/* $FlowFixMe[invalid-computed-prop] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
_emitter.emit(InteractionManager.Events.interactionStart);
}
// process the queue regardless of a transition
if (nextInteractionCount === 0) {
while (_taskQueue.hasTasksToProcess()) {
_taskQueue.processNext();
if (
_deadline > 0 &&
BatchedBridge.getEventLoopRunningTime() >= _deadline
) {
// Hit deadline before processing all tasks, so process more later.
_scheduleUpdate();
break;
}
}
}
_addInteractionSet.clear();
_deleteInteractionSet.clear();
}
/**
@@ -83,106 +231,11 @@ function reject(error: Error): void {
*
* @deprecated
*/
const InteractionManagerStub = {
Events: {
interactionStart: 'interactionStart',
interactionComplete: 'interactionComplete',
},
const InteractionManager = (
ReactNativeFeatureFlags.disableInteractionManager()
? // $FlowFixMe[incompatible-variance]
require('./InteractionManagerStub').default
: InteractionManagerImpl
) as typeof InteractionManagerImpl;
/**
* Schedule a function to run after all interactions have completed. Returns a cancellable
* "promise".
*
* @deprecated
*/
runAfterInteractions(task: ?Task): {
then: <U>(
onFulfill?: ?(void) => ?(Promise<U> | U),
onReject?: ?(error: mixed) => ?(Promise<U> | U),
) => Promise<U>,
cancel: () => void,
...
} {
let immediateID: ?$FlowIssue;
const promise = new Promise(resolve => {
immediateID = setImmediate(() => {
if (typeof task === 'object' && task !== null) {
if (typeof task.gen === 'function') {
task.gen().then(resolve, reject);
} else if (typeof task.run === 'function') {
try {
task.run();
resolve();
} catch (error) {
reject(error);
}
} else {
reject(new TypeError(`Task "${task.name}" missing gen or run.`));
}
} else if (typeof task === 'function') {
try {
task();
resolve();
} catch (error) {
reject(error);
}
} else {
reject(new TypeError('Invalid task of type: ' + typeof task));
}
});
});
return {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
then: promise.then.bind(promise),
cancel() {
clearImmediate(immediateID);
},
};
},
/**
* Notify manager that an interaction has started.
*
* @deprecated
*/
createInteractionHandle(): Handle {
return -1;
},
/**
* Notify manager that an interaction has completed.
*
* @deprecated
*/
clearInteractionHandle(handle: Handle) {
invariant(!!handle, 'InteractionManager: Must provide a handle to clear.');
},
/**
* @deprecated
*/
addListener(
eventType: string,
// $FlowIgnore[unclear-type]
listener: (...args: any) => mixed,
context: mixed,
): EventSubscription {
return {
remove() {},
};
},
/**
* A positive number will use setTimeout to schedule any tasks after the
* eventLoopRunningTime hits the deadline value, otherwise all tasks will be
* executed in one setImmediate batch (default).
*
* @deprecated
*/
setDeadline(deadline: number) {
// Do nothing.
},
};
export default InteractionManagerStub;
export default InteractionManager;
@@ -0,0 +1,184 @@
/**
* 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
* @format
*/
import type {EventSubscription} from '../vendor/emitter/EventEmitter';
const invariant = require('invariant');
export type Handle = number;
type Task =
| {
name: string,
run: () => void,
}
| {
name: string,
gen: () => Promise<void>,
}
| (() => void);
// NOTE: The original implementation of `InteractionManager` never rejected
// the returned promise. This preserves that behavior in the stub.
function reject(error: Error): void {
setTimeout(() => {
throw error;
}, 0);
}
/**
* InteractionManager allows long-running work to be scheduled after any
* interactions/animations have completed. In particular, this allows JavaScript
* animations to run smoothly.
*
* Applications can schedule tasks to run after interactions with the following:
*
* ```
* InteractionManager.runAfterInteractions(() => {
* // ...long-running synchronous task...
* });
* ```
*
* Compare this to other scheduling alternatives:
*
* - requestAnimationFrame(): for code that animates a view over time.
* - setImmediate/setTimeout(): run code later, note this may delay animations.
* - runAfterInteractions(): run code later, without delaying active animations.
*
* The touch handling system considers one or more active touches to be an
* 'interaction' and will delay `runAfterInteractions()` callbacks until all
* touches have ended or been cancelled.
*
* InteractionManager also allows applications to register animations by
* creating an interaction 'handle' on animation start, and clearing it upon
* completion:
*
* ```
* var handle = InteractionManager.createInteractionHandle();
* // run animation... (`runAfterInteractions` tasks are queued)
* // later, on animation completion:
* InteractionManager.clearInteractionHandle(handle);
* // queued tasks run if all handles were cleared
* ```
*
* `runAfterInteractions` takes either a plain callback function, or a
* `PromiseTask` object with a `gen` method that returns a `Promise`. If a
* `PromiseTask` is supplied, then it is fully resolved (including asynchronous
* dependencies that also schedule more tasks via `runAfterInteractions`) before
* starting on the next task that might have been queued up synchronously
* earlier.
*
* By default, queued tasks are executed together in a loop in one
* `setImmediate` batch. If `setDeadline` is called with a positive number, then
* tasks will only be executed until the deadline (in terms of js event loop run
* time) approaches, at which point execution will yield via setTimeout,
* allowing events such as touches to start interactions and block queued tasks
* from executing, making apps more responsive.
*
* @deprecated
*/
const InteractionManagerStub = {
Events: {
interactionStart: 'interactionStart',
interactionComplete: 'interactionComplete',
},
/**
* Schedule a function to run after all interactions have completed. Returns a cancellable
* "promise".
*
* @deprecated
*/
runAfterInteractions(task: ?Task): {
then: <U>(
onFulfill?: ?(void) => ?(Promise<U> | U),
onReject?: ?(error: mixed) => ?(Promise<U> | U),
) => Promise<U>,
cancel: () => void,
...
} {
let immediateID: ?$FlowIssue;
const promise = new Promise(resolve => {
immediateID = setImmediate(() => {
if (typeof task === 'object' && task !== null) {
if (typeof task.gen === 'function') {
task.gen().then(resolve, reject);
} else if (typeof task.run === 'function') {
try {
task.run();
resolve();
} catch (error) {
reject(error);
}
} else {
reject(new TypeError(`Task "${task.name}" missing gen or run.`));
}
} else if (typeof task === 'function') {
try {
task();
resolve();
} catch (error) {
reject(error);
}
} else {
reject(new TypeError('Invalid task of type: ' + typeof task));
}
});
});
return {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
then: promise.then.bind(promise),
cancel() {
clearImmediate(immediateID);
},
};
},
/**
* Notify manager that an interaction has started.
*
* @deprecated
*/
createInteractionHandle(): Handle {
return -1;
},
/**
* Notify manager that an interaction has completed.
*
* @deprecated
*/
clearInteractionHandle(handle: Handle) {
invariant(!!handle, 'InteractionManager: Must provide a handle to clear.');
},
/**
* @deprecated
*/
addListener(): EventSubscription {
return {
remove() {},
};
},
/**
* A positive number will use setTimeout to schedule any tasks after the
* eventLoopRunningTime hits the deadline value, otherwise all tasks will be
* executed in one setImmediate batch (default).
*
* @deprecated
*/
setDeadline(deadline: number) {
// Do nothing.
},
};
export default InteractionManagerStub;
+197
View File
@@ -0,0 +1,197 @@
/**
* 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
* @format
*/
'use strict';
const invariant = require('invariant');
export type SimpleTask = {
name: string,
run: () => void,
};
export type PromiseTask = {
name: string,
gen: () => Promise<void>,
};
export type Task = SimpleTask | PromiseTask | (() => void);
const DEBUG: false = false;
/**
* TaskQueue - A system for queueing and executing a mix of simple callbacks and
* trees of dependent tasks based on Promises. No tasks are executed unless
* `processNext` is called.
*
* `enqueue` takes a Task object with either a simple `run` callback, or a
* `gen` function that returns a `Promise` and puts it in the queue. If a gen
* function is supplied, then the promise it returns will block execution of
* tasks already in the queue until it resolves. This can be used to make sure
* the first task is fully resolved (including asynchronous dependencies that
* also schedule more tasks via `enqueue`) before starting on the next task.
* The `onMoreTasks` constructor argument is used to inform the owner that an
* async task has resolved and that the queue should be processed again.
*
* Note: Tasks are only actually executed with explicit calls to `processNext`.
*/
class TaskQueue {
/**
* TaskQueue instances are self contained and independent, so multiple tasks
* of varying semantics and priority can operate together.
*
* `onMoreTasks` is invoked when `PromiseTask`s resolve if there are more
* tasks to process.
*/
constructor({onMoreTasks}: {onMoreTasks: () => void, ...}) {
this._onMoreTasks = onMoreTasks;
this._queueStack = [{tasks: [], popable: false}];
}
/**
* Add a task to the queue. It is recommended to name your tasks for easier
* async debugging. Tasks will not be executed until `processNext` is called
* explicitly.
*/
enqueue(task: Task): void {
this._getCurrentQueue().push(task);
}
enqueueTasks(tasks: Array<Task>): void {
tasks.forEach(task => this.enqueue(task));
}
cancelTasks(tasksToCancel: Array<Task>): void {
// search through all tasks and remove them.
this._queueStack = this._queueStack
.map(queue => ({
...queue,
tasks: queue.tasks.filter(task => tasksToCancel.indexOf(task) === -1),
}))
.filter((queue, idx) => queue.tasks.length > 0 || idx === 0);
}
/**
* Check to see if `processNext` should be called.
*
* @returns {boolean} Returns true if there are tasks that are ready to be
* processed with `processNext`, or returns false if there are no more tasks
* to be processed right now, although there may be tasks in the queue that
* are blocked by earlier `PromiseTask`s that haven't resolved yet.
* `onMoreTasks` will be called after each `PromiseTask` resolves if there are
* tasks ready to run at that point.
*/
hasTasksToProcess(): boolean {
return this._getCurrentQueue().length > 0;
}
/**
* Executes the next task in the queue.
*/
processNext(): void {
const queue = this._getCurrentQueue();
if (queue.length) {
const task = queue.shift();
try {
if (typeof task === 'object' && task.gen) {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('TaskQueue: genPromise for task ' + task.name);
this._genPromise(task);
} else if (typeof task === 'object' && task.run) {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('TaskQueue: run task ' + task.name);
task.run();
} else {
invariant(
typeof task === 'function',
'Expected Function, SimpleTask, or PromiseTask, but got:\n' +
JSON.stringify(task, null, 2),
);
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('TaskQueue: run anonymous task');
task();
}
} catch (e) {
e.message =
// $FlowFixMe[incompatible-type]
// $FlowFixMe[incompatible-use]
'TaskQueue: Error with task ' + (task.name || '') + ': ' + e.message;
throw e;
}
}
}
_queueStack: Array<{
tasks: Array<Task>,
popable: boolean,
...
}>;
_onMoreTasks: () => void;
_getCurrentQueue(): Array<Task> {
const stackIdx = this._queueStack.length - 1;
const queue = this._queueStack[stackIdx];
if (
queue.popable &&
queue.tasks.length === 0 &&
this._queueStack.length > 1
) {
this._queueStack.pop();
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG &&
console.log('TaskQueue: popped queue: ', {
stackIdx,
queueStackSize: this._queueStack.length,
});
return this._getCurrentQueue();
} else {
return queue.tasks;
}
}
_genPromise(task: PromiseTask) {
// Each async task pushes it's own queue onto the queue stack. This
// effectively defers execution of previously queued tasks until the promise
// resolves, at which point we allow the new queue to be popped, which
// happens once it is fully processed.
this._queueStack.push({tasks: [], popable: false});
const stackIdx = this._queueStack.length - 1;
const stackItem = this._queueStack[stackIdx];
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('TaskQueue: push new queue: ', {stackIdx});
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG && console.log('TaskQueue: exec gen task ' + task.name);
task
.gen()
.then(() => {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
DEBUG &&
console.log('TaskQueue: onThen for gen task ' + task.name, {
stackIdx,
queueStackSize: this._queueStack.length,
});
stackItem.popable = true;
this.hasTasksToProcess() && this._onMoreTasks();
})
.catch(ex => {
setTimeout(() => {
ex.message = `TaskQueue: Error resolving Promise in task ${task.name}: ${ex.message}`;
throw ex;
}, 0);
});
}
}
export default TaskQueue;
@@ -0,0 +1,342 @@
/**
* 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.
*
* @noflow
* @format
*/
'use strict';
import type {ReactNativeFeatureFlagsJsOnlyOverrides} from '../../../src/private/featureflags/ReactNativeFeatureFlags';
function importModules(overrides: ReactNativeFeatureFlagsJsOnlyOverrides) {
const ReactNativeFeatureFlags = require('../../../src/private/featureflags/ReactNativeFeatureFlags');
// Make sure to setup overrides before importing any modules.
ReactNativeFeatureFlags.override(overrides);
const BatchedBridge = require('../../BatchedBridge/BatchedBridge').default;
const InteractionManager = require('../InteractionManager').default;
jest.mock('../../vendor/core/ErrorUtils');
jest.mock('../../BatchedBridge/BatchedBridge');
return {
BatchedBridge,
InteractionManager,
};
}
const isWindows = process.platform === 'win32';
const itif = (condition: boolean) => (condition ? it : it.skip);
function expectToBeCalledOnce(
fn: JestMockFn<$ReadOnlyArray<mixed>, mixed>,
): void {
expect(fn.mock.calls.length).toBe(1);
}
describe('InteractionManager', () => {
let InteractionManager;
let interactionStart;
let interactionComplete;
beforeEach(() => {
jest.resetModules();
({InteractionManager} = importModules({
disableInteractionManager: () => false,
}));
interactionStart = jest.fn();
interactionComplete = jest.fn();
InteractionManager.addListener(
InteractionManager.Events.interactionStart,
interactionStart,
);
InteractionManager.addListener(
InteractionManager.Events.interactionComplete,
interactionComplete,
);
});
it('throws when clearing an undefined handle', () => {
// $FlowExpectedError[incompatible-call]
expect(() => InteractionManager.clearInteractionHandle()).toThrow();
});
it('notifies asynchronously when interaction starts', () => {
InteractionManager.createInteractionHandle();
expect(interactionStart).not.toBeCalled();
jest.runAllTimers();
expect(interactionStart).toBeCalled();
expect(interactionComplete).not.toBeCalled();
});
it('notifies asynchronously when interaction stops', () => {
const handle = InteractionManager.createInteractionHandle();
jest.runAllTimers();
interactionStart.mockClear();
InteractionManager.clearInteractionHandle(handle);
expect(interactionComplete).not.toBeCalled();
jest.runAllTimers();
expect(interactionStart).not.toBeCalled();
expect(interactionComplete).toBeCalled();
});
it('does not notify when started & stopped in same event loop', () => {
const handle = InteractionManager.createInteractionHandle();
InteractionManager.clearInteractionHandle(handle);
jest.runAllTimers();
expect(interactionStart).not.toBeCalled();
expect(interactionComplete).not.toBeCalled();
});
it('does not notify when going from two -> one active interactions', () => {
InteractionManager.createInteractionHandle();
const handle = InteractionManager.createInteractionHandle();
jest.runAllTimers();
interactionStart.mockClear();
interactionComplete.mockClear();
InteractionManager.clearInteractionHandle(handle);
jest.runAllTimers();
expect(interactionStart).not.toBeCalled();
expect(interactionComplete).not.toBeCalled();
});
it('runs tasks asynchronously when there are interactions', () => {
const task = jest.fn();
InteractionManager.runAfterInteractions(task);
expect(task).not.toBeCalled();
jest.runAllTimers();
expect(task).toBeCalled();
});
it('runs tasks when interactions complete', () => {
const task = jest.fn();
const handle = InteractionManager.createInteractionHandle();
InteractionManager.runAfterInteractions(task);
jest.runAllTimers();
InteractionManager.clearInteractionHandle(handle);
expect(task).not.toBeCalled();
jest.runAllTimers();
expect(task).toBeCalled();
});
it('does not run tasks twice', () => {
const task1 = jest.fn();
const task2 = jest.fn();
InteractionManager.runAfterInteractions(task1);
jest.runAllTimers();
InteractionManager.runAfterInteractions(task2);
jest.runAllTimers();
expectToBeCalledOnce(task1);
});
it('runs tasks added while processing previous tasks', () => {
const task1 = jest.fn(() => {
InteractionManager.runAfterInteractions(task2);
});
const task2 = jest.fn();
InteractionManager.runAfterInteractions(task1);
expect(task2).not.toBeCalled();
jest.runAllTimers();
expect(task1).toBeCalled();
expect(task2).toBeCalled();
});
it('allows tasks to be cancelled', () => {
const task1 = jest.fn();
const task2 = jest.fn();
const promise1 = InteractionManager.runAfterInteractions(task1);
InteractionManager.runAfterInteractions(task2);
expect(task1).not.toBeCalled();
expect(task2).not.toBeCalled();
promise1.cancel();
jest.runAllTimers();
expect(task1).not.toBeCalled();
expect(task2).toBeCalled();
});
});
describe('promise tasks', () => {
let BatchedBridge;
let InteractionManager;
let sequenceId;
function createSequenceTask(expectedSequenceId: number) {
return jest.fn(() => {
expect(++sequenceId).toBe(expectedSequenceId);
});
}
beforeEach(() => {
jest.resetModules();
({BatchedBridge, InteractionManager} = importModules({
disableInteractionManager: () => false,
}));
sequenceId = 0;
});
it('should run a basic promise task', () => {
const task1 = jest.fn(() => {
expect(++sequenceId).toBe(1);
return new Promise(resolve => resolve());
});
InteractionManager.runAfterInteractions({gen: task1, name: 'gen1'});
jest.runAllTimers();
expectToBeCalledOnce(task1);
});
it('should handle nested promises', () => {
const task1 = jest.fn(() => {
expect(++sequenceId).toBe(1);
return new Promise(resolve => {
InteractionManager.runAfterInteractions({
gen: task2,
name: 'gen2',
}).then(resolve);
});
});
const task2 = jest.fn(() => {
expect(++sequenceId).toBe(2);
return new Promise(resolve => resolve());
});
InteractionManager.runAfterInteractions({gen: task1, name: 'gen1'});
jest.runAllTimers();
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
});
it('should pause promise tasks during interactions then resume', () => {
const task1 = createSequenceTask(1);
const task2 = jest.fn(() => {
expect(++sequenceId).toBe(2);
return new Promise(resolve => {
setTimeout(() => {
InteractionManager.runAfterInteractions(task3).then(resolve);
}, 1);
});
});
const task3 = createSequenceTask(3);
InteractionManager.runAfterInteractions(task1);
InteractionManager.runAfterInteractions({gen: task2, name: 'gen2'});
jest.runOnlyPendingTimers();
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
const handle = InteractionManager.createInteractionHandle();
jest.runAllTimers();
jest.runAllTimers(); // Just to be sure...
expect(task3).not.toBeCalled();
InteractionManager.clearInteractionHandle(handle);
jest.runAllTimers();
expectToBeCalledOnce(task3);
});
it('should execute tasks in loop within deadline', () => {
InteractionManager.setDeadline(100);
BatchedBridge.getEventLoopRunningTime.mockReturnValue(10);
const task1 = createSequenceTask(1);
const task2 = createSequenceTask(2);
InteractionManager.runAfterInteractions(task1);
InteractionManager.runAfterInteractions(task2);
jest.runOnlyPendingTimers();
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
});
it('should execute tasks one at a time if deadline exceeded', () => {
InteractionManager.setDeadline(100);
BatchedBridge.getEventLoopRunningTime.mockReturnValue(200);
const task1 = createSequenceTask(1);
const task2 = createSequenceTask(2);
InteractionManager.runAfterInteractions(task1);
InteractionManager.runAfterInteractions(task2);
jest.runOnlyPendingTimers();
expectToBeCalledOnce(task1);
expect(task2).not.toBeCalled();
jest.runOnlyPendingTimers(); // resolve1
jest.runOnlyPendingTimers(); // task2
expectToBeCalledOnce(task2);
});
const bigAsyncTest = resolveTest => {
jest.useRealTimers();
const task1 = createSequenceTask(1);
const task2 = jest.fn(() => {
expect(++sequenceId).toBe(2);
return new Promise(resolve => {
InteractionManager.runAfterInteractions(task3);
setTimeout(() => {
InteractionManager.runAfterInteractions({
gen: task4,
name: 'gen4',
}).then(resolve);
}, 1);
});
});
const task3 = createSequenceTask(3);
const task4 = jest.fn(() => {
expect(++sequenceId).toBe(4);
return new Promise(resolve => {
InteractionManager.runAfterInteractions(task5).then(resolve);
});
});
const task5 = createSequenceTask(5);
const task6 = createSequenceTask(6);
InteractionManager.runAfterInteractions(task1);
InteractionManager.runAfterInteractions({gen: task2, name: 'gen2'});
InteractionManager.runAfterInteractions(task6);
setTimeout(() => {
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
expectToBeCalledOnce(task3);
expectToBeCalledOnce(task4);
expectToBeCalledOnce(task5);
expectToBeCalledOnce(task6);
resolveTest();
}, 100);
};
itif(!isWindows)(
'resolves async tasks recursively before other queued tasks',
() => {
return new Promise(bigAsyncTest);
},
);
itif(!isWindows)('should also work with a deadline', () => {
InteractionManager.setDeadline(100);
BatchedBridge.getEventLoopRunningTime.mockReturnValue(200);
return new Promise(bigAsyncTest);
});
});
@@ -0,0 +1,169 @@
/**
* 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.
*
* @noflow
* @format
*/
'use strict';
const Promise = require('promise');
function expectToBeCalledOnce(fn) {
expect(fn.mock.calls.length).toBe(1);
}
function clearTaskQueue(taskQueue) {
do {
jest.runAllTimers();
taskQueue.processNext();
jest.runAllTimers();
} while (taskQueue.hasTasksToProcess());
}
describe('TaskQueue', () => {
let taskQueue;
let onMoreTasks;
let sequenceId;
function createSequenceTask(expectedSequenceId) {
return jest.fn(() => {
expect(++sequenceId).toBe(expectedSequenceId);
});
}
beforeEach(() => {
jest.resetModules();
onMoreTasks = jest.fn();
const TaskQueue = require('../TaskQueue').default;
taskQueue = new TaskQueue({onMoreTasks});
sequenceId = 0;
});
it('should run a basic task', () => {
const task1 = createSequenceTask(1);
taskQueue.enqueue({run: task1, name: 'run1'});
expect(taskQueue.hasTasksToProcess()).toBe(true);
taskQueue.processNext();
expectToBeCalledOnce(task1);
});
it('should handle blocking promise task', () => {
const task1 = jest.fn(() => {
return new Promise(resolve => {
setTimeout(() => {
expect(++sequenceId).toBe(1);
resolve();
}, 1);
});
});
const task2 = createSequenceTask(2);
taskQueue.enqueue({gen: task1, name: 'gen1'});
taskQueue.enqueue({run: task2, name: 'run2'});
taskQueue.processNext();
expectToBeCalledOnce(task1);
expect(task2).not.toBeCalled();
expect(onMoreTasks).not.toBeCalled();
expect(taskQueue.hasTasksToProcess()).toBe(false);
clearTaskQueue(taskQueue);
expectToBeCalledOnce(onMoreTasks);
expectToBeCalledOnce(task2);
});
it('should handle nested simple tasks', () => {
const task1 = jest.fn(() => {
expect(++sequenceId).toBe(1);
taskQueue.enqueue({run: task3, name: 'run3'});
});
const task2 = createSequenceTask(2);
const task3 = createSequenceTask(3);
taskQueue.enqueue({run: task1, name: 'run1'});
taskQueue.enqueue({run: task2, name: 'run2'}); // not blocked by task 1
clearTaskQueue(taskQueue);
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
expectToBeCalledOnce(task3);
});
it('should handle nested promises', () => {
const task1 = jest.fn(() => {
return new Promise(resolve => {
setTimeout(() => {
expect(++sequenceId).toBe(1);
taskQueue.enqueue({gen: task2, name: 'gen2'});
taskQueue.enqueue({run: resolve, name: 'resolve1'});
}, 1);
});
});
const task2 = jest.fn(() => {
return new Promise(resolve => {
setTimeout(() => {
expect(++sequenceId).toBe(2);
taskQueue.enqueue({run: task3, name: 'run3'});
taskQueue.enqueue({run: resolve, name: 'resolve2'});
}, 1);
});
});
const task3 = createSequenceTask(3);
const task4 = createSequenceTask(4);
taskQueue.enqueue({gen: task1, name: 'gen1'});
taskQueue.enqueue({run: task4, name: 'run4'}); // blocked by task 1 promise
clearTaskQueue(taskQueue);
expectToBeCalledOnce(task1);
expectToBeCalledOnce(task2);
expectToBeCalledOnce(task3);
expectToBeCalledOnce(task4);
});
it('should be able to cancel tasks', () => {
const task1 = jest.fn();
const task2 = createSequenceTask(1);
const task3 = jest.fn();
const task4 = createSequenceTask(2);
taskQueue.enqueue(task1);
taskQueue.enqueue(task2);
taskQueue.enqueue(task3);
taskQueue.enqueue(task4);
taskQueue.cancelTasks([task1, task3]);
clearTaskQueue(taskQueue);
expect(task1).not.toBeCalled();
expect(task3).not.toBeCalled();
expectToBeCalledOnce(task2);
expectToBeCalledOnce(task4);
expect(taskQueue.hasTasksToProcess()).toBe(false);
});
it('should not crash when last task is cancelled', () => {
const task1 = jest.fn();
taskQueue.enqueue(task1);
taskQueue.cancelTasks([task1]);
clearTaskQueue(taskQueue);
expect(task1).not.toBeCalled();
expect(taskQueue.hasTasksToProcess()).toBe(false);
});
it('should not crash when task is cancelled between being started and resolved', () => {
const task1 = jest.fn(() => {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1);
});
});
taskQueue.enqueue({gen: task1, name: 'gen1'});
taskQueue.processNext();
taskQueue.cancelTasks([task1]);
jest.runAllTimers();
});
});
@@ -400,6 +400,7 @@ class XMLHttpRequest extends EventTarget {
if (XMLHttpRequest._profiling) {
console.timeStamp(
'Incremental Data: ' + this._getMeasureURL(),
// $FlowFixMe[extra-arg] Add correct typing for `console.timeStamp`
start,
undefined,
PERFORMANCE_TRACK_NAME,
@@ -454,6 +455,7 @@ class XMLHttpRequest extends EventTarget {
const start = this._startTime;
console.timeStamp(
this._getMeasureURL(),
// $FlowFixMe[extra-arg] Add correct typing for `console.timeStamp`
start,
undefined,
PERFORMANCE_TRACK_NAME,
@@ -10,21 +10,21 @@
import typeof {enable} from 'promise/setimmediate/rejection-tracking';
import ExceptionsManager from './Core/ExceptionsManager';
import LogBox from './LogBox/LogBox';
let rejectionTrackingOptions: $NonMaybeType<Parameters<enable>[0]> = {
allRejections: true,
onUnhandled: (id, rejection) => {
onUnhandled: (id, rejection = {}) => {
let message: string;
let stack: ?string;
if (rejection === undefined) {
message = '';
} else if (
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
Object.prototype.toString.call(rejection) === '[object Error]'
) {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
const stringValue = Object.prototype.toString.call(rejection);
if (stringValue === '[object Error]') {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
message = Error.prototype.toString.call(rejection);
const error: Error = (rejection: $FlowFixMe);
stack = error.stack;
} else {
try {
message = require('pretty-format').format(rejection);
@@ -34,23 +34,37 @@ let rejectionTrackingOptions: $NonMaybeType<Parameters<enable>[0]> = {
? rejection
: JSON.stringify((rejection: $FlowFixMe));
}
// It could although this object is not a standard error, it still has stack information to unwind
// $FlowFixMe ignore types just check if stack is there
if (rejection?.stack && typeof rejection.stack === 'string') {
stack = rejection.stack;
}
}
ExceptionsManager.handleException(
new Error(
`Uncaught (in promise, id: ${id})${message ? `: "${message}"` : ''}`,
{
cause: rejection,
const warning = `Possible unhandled promise rejection (id: ${id}):\n${
message ?? ''
}`;
if (__DEV__) {
LogBox.addLog({
level: 'warn',
message: {
content: warning,
substitutions: [],
},
),
false /* isFatal */,
);
componentStack: [],
componentStackType: null,
stack,
category: 'possible_unhandled_promise_rejection',
});
} else {
console.warn(warning);
}
},
onHandled: id => {
const warning =
`Promise rejection handled (id: ${id})\n` +
'This means you can ignore any previous messages of the form ' +
`"Uncaught (in promise, id: ${id})"`;
`"Possible unhandled promise rejection (id: ${id}):"`;
console.warn(warning);
},
};
@@ -24,13 +24,9 @@ using namespace facebook::react;
@end
@implementation RCTPullToRefreshViewComponentView {
BOOL _isBeforeInitialLayout;
UIRefreshControl *_refreshControl;
RCTScrollViewComponentView *__weak _scrollViewComponentView;
// This variable keeps track of whether the view is recycled or not. Once the view is recycled, the component
// creates a new instance of UIRefreshControl, resetting the native props to the default values.
// However, when recycling, we are keeping around the old _props. The flag is used to force the application
// of the current props to the newly created UIRefreshControl the first time that updateProps is called.
BOOL _recycled;
}
- (instancetype)initWithFrame:(CGRect)frame
@@ -40,7 +36,8 @@ using namespace facebook::react;
// attaching and detaching of a pull-to-refresh view to a scroll view.
// The pull-to-refresh view is not a subview of this view.
self.hidden = YES;
_recycled = NO;
_isBeforeInitialLayout = YES;
[self _initializeUIRefreshControl];
}
@@ -66,53 +63,54 @@ using namespace facebook::react;
{
[super prepareForRecycle];
_scrollViewComponentView = nil;
_props = nil;
_isBeforeInitialLayout = YES;
[self _initializeUIRefreshControl];
_recycled = YES;
}
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
{
// Prop updates are ignored by _refreshControl until after the initial layout, so just store them in _props until then
if (_isBeforeInitialLayout) {
_props = std::static_pointer_cast<const PullToRefreshViewProps>(props);
return;
}
const auto &oldConcreteProps = static_cast<const PullToRefreshViewProps &>(*_props);
const auto &newConcreteProps = static_cast<const PullToRefreshViewProps &>(*props);
if (_recycled || newConcreteProps.tintColor != oldConcreteProps.tintColor) {
if (newConcreteProps.tintColor != oldConcreteProps.tintColor) {
_refreshControl.tintColor = RCTUIColorFromSharedColor(newConcreteProps.tintColor);
}
if (_recycled || newConcreteProps.progressViewOffset != oldConcreteProps.progressViewOffset) {
if (newConcreteProps.progressViewOffset != oldConcreteProps.progressViewOffset) {
[self _updateProgressViewOffset:newConcreteProps.progressViewOffset];
}
BOOL needsUpdateTitle = NO;
if (_recycled || newConcreteProps.title != oldConcreteProps.title) {
if (newConcreteProps.title != oldConcreteProps.title) {
needsUpdateTitle = YES;
}
if (_recycled || newConcreteProps.titleColor != oldConcreteProps.titleColor) {
if (newConcreteProps.titleColor != oldConcreteProps.titleColor) {
needsUpdateTitle = YES;
}
[super updateProps:props oldProps:oldProps];
if (_recycled || needsUpdateTitle) {
if (needsUpdateTitle) {
[self _updateTitle];
}
// All prop updates must happen above the call to begin refreshing, or else _refreshControl will ignore the updates
if (_recycled || newConcreteProps.refreshing != oldConcreteProps.refreshing) {
if (newConcreteProps.refreshing != oldConcreteProps.refreshing) {
if (newConcreteProps.refreshing) {
[self beginRefreshingProgrammatically];
} else {
[_refreshControl endRefreshing];
}
}
if (_recycled || newConcreteProps.zIndex != oldConcreteProps.zIndex) {
_refreshControl.layer.zPosition = newConcreteProps.zIndex.value_or(0);
}
_recycled = NO;
}
#pragma mark -
@@ -157,12 +155,10 @@ using namespace facebook::react;
// Attempts to begin refreshing before the initial layout are ignored by _refreshControl. So if the control is
// refreshing when mounted, we need to call beginRefreshing in layoutSubviews or it won't work.
if (self.window) {
const auto &concreteProps = static_cast<const PullToRefreshViewProps &>(*_props);
if (_isBeforeInitialLayout) {
_isBeforeInitialLayout = NO;
if (concreteProps.refreshing) {
[self beginRefreshingProgrammatically];
}
[self updateProps:_props oldProps:PullToRefreshViewShadowNode::defaultSharedProps()];
}
}
@@ -218,12 +214,11 @@ using namespace facebook::react;
// When refreshing programmatically (i.e. without pulling down), we must explicitly adjust the ScrollView content
// offset, or else the _refreshControl won't be visible
if (!_refreshControl.isRefreshing) {
UIScrollView *scrollView = _scrollViewComponentView.scrollView;
CGPoint offset = {scrollView.contentOffset.x, scrollView.contentOffset.y - _refreshControl.frame.size.height};
[scrollView setContentOffset:offset];
[_refreshControl beginRefreshing];
}
UIScrollView *scrollView = _scrollViewComponentView.scrollView;
CGPoint offset = {scrollView.contentOffset.x, scrollView.contentOffset.y - _refreshControl.frame.size.height};
[scrollView setContentOffset:offset];
[_refreshControl beginRefreshing];
}
#pragma mark - Native commands
@@ -112,11 +112,6 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
__weak UIView *_firstVisibleView;
CGFloat _endDraggingSensitivityMultiplier;
// A flag indicating that accessibility API is used.
// It is not restored to the default value in prepareForRecycle.
// Once an accessibility API is used, view culling will be disabled for the entire session.
BOOL _isAccessibilityAPIUsed;
}
+ (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view
@@ -139,7 +134,6 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
_isUserTriggeredScrolling = NO;
_shouldUpdateContentInsetAdjustmentBehavior = YES;
_automaticallyAdjustKeyboardInsets = NO;
_isAccessibilityAPIUsed = NO;
[self addSubview:_scrollView];
_containerView = [[UIView alloc] initWithFrame:CGRectZero];
@@ -599,38 +593,6 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
return metrics;
}
/**
* When use of accessibility APIs is detected, view culling is disabled to make accessibility work correctly.
*/
- (void)_disableViewCullingIfNecessary
{
if (!_isAccessibilityAPIUsed) {
_isAccessibilityAPIUsed = YES;
[self _updateStateWithContentOffset];
}
}
- (NSInteger)accessibilityElementCount
{
// From empirical testing, method `accessibilityElementCount` is called lazily only
// when accessibility is used.
// Why we don't use UIAccessibilitySwitchControlStatusDidChangeNotification and
// UIAccessibilityVoiceOverStatusDidChangeNotification? The notifications are not called when using Accessibility
// Inspector. We anticipate developers will want to debug accessbility with Accessibility Inspector and don't want to
// break that developer workflow with view culling. Therefore, we are using API use detection to disable view culling
// instead of the notifications.
[self _disableViewCullingIfNecessary];
return [super accessibilityElementCount];
}
- (NSArray<id<UIFocusItem>> *)focusItemsInRect:(CGRect)rect
{
// From empirical testing, method `focusItemsInRect:` is called lazily only
// when keyboard navigation is used.
[self _disableViewCullingIfNecessary];
return [super focusItemsInRect:rect];
}
- (void)_updateStateWithContentOffset
{
if (!_state) {
@@ -638,23 +600,17 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
}
auto contentOffset = RCTPointFromCGPoint(_scrollView.contentOffset);
BOOL isAccessibilityAPIUsed = _isAccessibilityAPIUsed;
_state->updateState(
[contentOffset, isAccessibilityAPIUsed](
[contentOffset](
const ScrollViewShadowNode::ConcreteState::Data &oldData) -> ScrollViewShadowNode::ConcreteState::SharedData {
if (oldData.contentOffset == contentOffset && oldData.disableViewCulling == isAccessibilityAPIUsed) {
// avoid doing a state update if content offset and use of accessibility didn't change.
if (oldData.contentOffset == contentOffset) {
// avoid doing a state update if content offset didn't change.
return nullptr;
}
auto newData = oldData;
newData.contentOffset = contentOffset;
newData.disableViewCulling =
UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning() || isAccessibilityAPIUsed;
return std::make_shared<const ScrollViewShadowNode::ConcreteState::Data>(newData);
},
ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges()
? EventQueue::UpdateMode::unstable_Immediate
: EventQueue::UpdateMode::Asynchronous);
});
}
- (void)prepareForRecycle
@@ -9,6 +9,7 @@
#import "RCTParagraphComponentAccessibilityProvider.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <React/RCTViewAccessibilityElement.h>
#import <react/renderer/components/text/ParagraphComponentDescriptor.h>
#import <react/renderer/components/text/ParagraphProps.h>
#import <react/renderer/components/text/ParagraphState.h>
@@ -226,6 +227,10 @@ using namespace facebook::react;
for (NSObject *element in elements) {
if ([element isKindOfClass:[UIView class]] && [cooptingCandidates containsObject:((UIView *)element)]) {
return YES;
} else if (
[element isKindOfClass:[RCTViewAccessibilityElement class]] &&
[cooptingCandidates containsObject:((RCTViewAccessibilityElement *)element).view]) {
return YES;
}
}
}
@@ -0,0 +1,28 @@
/*
* 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 "RCTViewComponentView.h"
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/*
* A UIAcccessibilityElement representing a RCTViewComponentView from an
* accessibility standpoint. This enables RCTViewComponentView's to reference
* themselves in `accessibilityElements` without actually being an accessibility
* element. If it were, then iOS would not call into `accessibilityElements`.
*/
@interface RCTViewAccessibilityElement : UIAccessibilityElement
@property (readonly) RCTViewComponentView *view;
- (instancetype)initWithView:(RCTViewComponentView *)view;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,83 @@
/*
* 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 "RCTViewAccessibilityElement.h"
@implementation RCTViewAccessibilityElement
- (instancetype)initWithView:(RCTViewComponentView *)view
{
if (self = [super initWithAccessibilityContainer:view]) {
_view = view;
}
return self;
}
- (CGRect)accessibilityFrame
{
return UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view);
}
#pragma mark - Forwarding to _view
- (NSString *)accessibilityLabel
{
return _view.accessibilityLabel;
}
- (NSString *)accessibilityValue
{
return _view.accessibilityValue;
}
- (UIAccessibilityTraits)accessibilityTraits
{
return _view.accessibilityTraits;
}
- (NSString *)accessibilityHint
{
return _view.accessibilityHint;
}
- (BOOL)accessibilityIgnoresInvertColors
{
return _view.accessibilityIgnoresInvertColors;
}
- (BOOL)shouldGroupAccessibilityChildren
{
return _view.shouldGroupAccessibilityChildren;
}
- (NSArray<UIAccessibilityCustomAction *> *)accessibilityCustomActions
{
return _view.accessibilityCustomActions;
}
- (NSString *)accessibilityLanguage
{
return _view.accessibilityLanguage;
}
- (BOOL)accessibilityViewIsModal
{
return _view.accessibilityViewIsModal;
}
- (BOOL)accessibilityElementsHidden
{
return _view.accessibilityElementsHidden;
}
- (BOOL)accessibilityRespondsToUserInteraction
{
return _view.accessibilityRespondsToUserInteraction;
}
@end
@@ -6,6 +6,7 @@
*/
#import "RCTViewComponentView.h"
#import "RCTViewAccessibilityElement.h"
#import <CoreGraphics/CoreGraphics.h>
#import <QuartzCore/QuartzCore.h>
@@ -50,6 +51,8 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
UIView *_containerView;
BOOL _useCustomContainerView;
NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
NSMutableArray<NSObject *> *_accessibilityElements;
RCTViewAccessibilityElement *_axElementDescribingSelf;
}
#ifdef RCT_DYNAMIC_FRAMEWORKS
@@ -402,11 +405,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
[_accessibilityOrderNativeIDs addObject:RCTNSStringFromString(childId)];
}
// If we are prop updating and have children we can go ahead and assign this prop.
// Otherwise, we might not have children attached yet and need to wait before then.
if (self.currentContainerView.subviews.count > 0) {
[self updateAccessibilityElements];
}
_accessibilityElements = [NSMutableArray new];
}
// `accessibilityTraits`
@@ -618,6 +617,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
_isJSResponder = NO;
_removeClippedSubviews = NO;
_reactSubviews = [NSMutableArray new];
_accessibilityElements = [NSMutableArray new];
}
- (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet<NSString *> *_Nullable)props
@@ -1149,37 +1149,43 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
return self;
}
- (void)didMoveToSuperview
- (NSArray<NSObject *> *)accessibilityElements
{
// At this point we are guaranteed to have subviews, if we are going to have them
if (ReactNativeFeatureFlags::enableAccessibilityOrder()) {
[self updateAccessibilityElements];
if ([_accessibilityOrderNativeIDs count] <= 0) {
return super.accessibilityElements;
}
}
- (void)updateAccessibilityElements
{
if ([_accessibilityOrderNativeIDs count] == 0) {
self.accessibilityElements = nil;
return;
// TODO: Currently this ignores changes to descendant nativeID's. While that should rarely, if ever happen, it's an
// edge case we should address. Currently this fixes some app deaths so landing this without addressing that edge case
// for now.
if ([_accessibilityElements count] > 0) {
return _accessibilityElements;
}
NSMutableDictionary<NSString *, UIView *> *nativeIdToView = [NSMutableDictionary new];
[RCTViewComponentView collectAccessibilityElements:self
intoDictionary:nativeIdToView
nativeIds:_accessibilityOrderNativeIDs];
NSMutableArray *accessibilityElements = [NSMutableArray new];
for (const auto &childId : _props->accessibilityOrder) {
for (auto childId : _props->accessibilityOrder) {
NSString *nsStringChildId = RCTNSStringFromString(childId);
UIView *viewWithMatchingNativeId = [nativeIdToView objectForKey:nsStringChildId];
if (viewWithMatchingNativeId != nil) {
[accessibilityElements addObject:viewWithMatchingNativeId];
// Special case to allow for self-referencing with accessibilityOrder
if ([nsStringChildId isEqualToString:self.nativeId]) {
if (!_axElementDescribingSelf) {
_axElementDescribingSelf = [[RCTViewAccessibilityElement alloc] initWithView:self];
}
_axElementDescribingSelf.isAccessibilityElement = [super isAccessibilityElement];
[_accessibilityElements addObject:_axElementDescribingSelf];
} else {
UIView *viewWithMatchingNativeId = [nativeIdToView objectForKey:nsStringChildId];
if (viewWithMatchingNativeId) {
[_accessibilityElements addObject:viewWithMatchingNativeId];
}
}
}
self.accessibilityElements = accessibilityElements;
return _accessibilityElements;
}
+ (void)collectAccessibilityElements:(UIView *)view
@@ -1246,6 +1252,13 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
return self.contentView.isAccessibilityElement;
}
// If we reference ourselves in accessibilityOrder then we will make a
// UIAccessibilityElement object to represent ourselves since returning YES
// here would mean iOS would not call into accessibilityElements
if ([_accessibilityOrderNativeIDs containsObject:self.nativeId]) {
return NO;
}
return [super isAccessibilityElement];
}
@@ -31,8 +31,8 @@ public abstract class com/facebook/react/HeadlessJsTaskService : android/app/Ser
public fun <init> ()V
public static final fun acquireWakeLockNow (Landroid/content/Context;)V
protected final fun getReactContext ()Lcom/facebook/react/bridge/ReactContext;
protected fun getReactHost ()Lcom/facebook/react/ReactHost;
protected fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost;
protected final fun getReactHost ()Lcom/facebook/react/ReactHost;
protected final fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost;
protected fun getTaskConfig (Landroid/content/Intent;)Lcom/facebook/react/jstasks/HeadlessJsTaskConfig;
public fun onBind (Landroid/content/Intent;)Landroid/os/IBinder;
public fun onDestroy ()V
@@ -19,7 +19,6 @@ plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.download)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ktfmt)
}
version = project.findProperty("VERSION_NAME")?.toString()!!
@@ -621,7 +620,6 @@ dependencies {
api(libs.androidx.autofill)
api(libs.androidx.swiperefreshlayout)
api(libs.androidx.tracing)
api(libs.androidx.window)
api(libs.fbjni)
api(libs.fresco)
@@ -113,7 +113,7 @@ public abstract class HeadlessJsTaskService : Service(), HeadlessJsTaskEventList
* somewhere.
*/
@Suppress("DEPRECATION")
protected open val reactNativeHost: ReactNativeHost
protected val reactNativeHost: ReactNativeHost
get() = (application as ReactApplication).reactNativeHost
/**
@@ -121,7 +121,7 @@ public abstract class HeadlessJsTaskService : Service(), HeadlessJsTaskEventList
* [ReactApplication] and calls [ReactApplication.reactHost]. This method assumes it is called in
* new architecture and returns null if not.
*/
protected open val reactHost: ReactHost?
protected val reactHost: ReactHost?
get() = (application as ReactApplication).reactHost
protected val reactContext: ReactContext?
@@ -135,10 +135,6 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
private void init() {
setRootViewTag(ReactRootViewTagGenerator.getNextRootViewTag());
setClipChildren(false);
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
}
}
@Override
@@ -869,7 +865,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
private int mDeviceRotation = 0;
/* package */ CustomGlobalLayoutListener() {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext());
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
mVisibleViewArea = new Rect();
mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
}
@@ -992,7 +988,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
return;
}
mDeviceRotation = rotation;
DisplayMetricsHolder.initDisplayMetrics(getContext());
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
emitOrientationChanged(rotation);
}
@@ -8,7 +8,6 @@
package com.facebook.react.bridge
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import java.util.ArrayList
import java.util.Arrays
import kotlin.jvm.JvmStatic
@@ -66,16 +65,9 @@ public open class ReadableNativeArray protected constructor() : NativeArray(), R
if (other !is ReadableNativeArray) {
return false
}
return if (ReactNativeFeatureFlags.useNativeEqualsInNativeReadableArrayAndroid()) {
nativeEquals(other)
} else {
localArray.contentDeepEquals(other.localArray)
}
return localArray.contentDeepEquals(other.localArray)
}
private external fun nativeEquals(other: ReadableNativeArray): Boolean
override fun toArrayList(): ArrayList<Any?> {
val arrayList = ArrayList<Any?>()
repeat(size()) { i ->
@@ -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<<a90efac589511beb130c499e51150de8>>
* @generated SignedSource<<9b580dfb56a10ebe9e80bc7ce0c2ca36>>
*/
/**
@@ -162,12 +162,6 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableIOSViewClipToPaddingBox(): Boolean = accessor.enableIOSViewClipToPaddingBox()
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
@JvmStatic
public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean = accessor.enableImmediateUpdateModeForContentOffsetChanges()
/**
* This is to fix the issue with interop view manager where component descriptor lookup is causing ViewManager to preload.
*/
@@ -234,6 +228,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableResourceTimingAPI(): Boolean = accessor.enableResourceTimingAPI()
/**
* Dispatches state updates synchronously in Fabric (e.g.: updates the scroll position in the shadow tree synchronously from the main thread).
*/
@JvmStatic
public fun enableSynchronousStateUpdates(): Boolean = accessor.enableSynchronousStateUpdates()
/**
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
*/
@@ -306,24 +306,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun preparedTextCacheSize(): Double = accessor.preparedTextCacheSize()
/**
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
*/
@JvmStatic
public fun preventShadowTreeCommitExhaustionWithLocking(): Boolean = accessor.preventShadowTreeCommitExhaustionWithLocking()
/**
* Releases the cached image data when it is consumed by the observers.
*/
@JvmStatic
public fun releaseImageDataWhenConsumed(): Boolean = accessor.releaseImageDataWhenConsumed()
/**
* Skip activity identity assertion in ReactHostImpl::onHostPause()
*/
@JvmStatic
public fun skipActivityIdentityAssertionOnHostPause(): Boolean = accessor.skipActivityIdentityAssertionOnHostPause()
/**
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
*/
@@ -348,18 +336,6 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun useFabricInterop(): Boolean = accessor.useFabricInterop()
/**
* Use a native implementation of equals in NativeReadableArray.
*/
@JvmStatic
public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = accessor.useNativeEqualsInNativeReadableArrayAndroid()
/**
* Use a native implementation of TransformHelper
*/
@JvmStatic
public fun useNativeTransformHelperAndroid(): Boolean = accessor.useNativeTransformHelperAndroid()
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -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<<b32f66fb09971e786dd1380bbf417720>>
* @generated SignedSource<<99871d495699fdea7f67b3b29af2d28f>>
*/
/**
@@ -42,7 +42,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
@@ -54,6 +53,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableResourceTimingAPICache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
private var enableViewCullingCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
@@ -66,15 +66,11 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionWithLockingCache: Boolean? = null
private var releaseImageDataWhenConsumedCache: Boolean? = null
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -281,15 +277,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean {
var cached = enableImmediateUpdateModeForContentOffsetChangesCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableImmediateUpdateModeForContentOffsetChanges()
enableImmediateUpdateModeForContentOffsetChangesCache = cached
}
return cached
}
override fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean {
var cached = enableInteropViewManagerClassLookUpOptimizationIOSCache
if (cached == null) {
@@ -389,6 +376,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableSynchronousStateUpdates(): Boolean {
var cached = enableSynchronousStateUpdatesCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableSynchronousStateUpdates()
enableSynchronousStateUpdatesCache = cached
}
return cached
}
override fun enableViewCulling(): Boolean {
var cached = enableViewCullingCache
if (cached == null) {
@@ -497,15 +493,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun preventShadowTreeCommitExhaustionWithLocking(): Boolean {
var cached = preventShadowTreeCommitExhaustionWithLockingCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.preventShadowTreeCommitExhaustionWithLocking()
preventShadowTreeCommitExhaustionWithLockingCache = cached
}
return cached
}
override fun releaseImageDataWhenConsumed(): Boolean {
var cached = releaseImageDataWhenConsumedCache
if (cached == null) {
@@ -515,15 +502,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun skipActivityIdentityAssertionOnHostPause(): Boolean {
var cached = skipActivityIdentityAssertionOnHostPauseCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.skipActivityIdentityAssertionOnHostPause()
skipActivityIdentityAssertionOnHostPauseCache = cached
}
return cached
}
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
if (cached == null) {
@@ -560,24 +538,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean {
var cached = useNativeEqualsInNativeReadableArrayAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useNativeEqualsInNativeReadableArrayAndroid()
useNativeEqualsInNativeReadableArrayAndroidCache = cached
}
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useNativeTransformHelperAndroid()
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
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<<d1da48f826bc6a1793d1630cb89cb5c1>>
* @generated SignedSource<<cfa7407ac0b1fe6cfb92dbc22449bae2>>
*/
/**
@@ -72,8 +72,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableIOSViewClipToPaddingBox(): Boolean
@DoNotStrip @JvmStatic public external fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean
@DoNotStrip @JvmStatic public external fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@DoNotStrip @JvmStatic public external fun enableLayoutAnimationsOnAndroid(): Boolean
@@ -96,6 +94,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableResourceTimingAPI(): Boolean
@DoNotStrip @JvmStatic public external fun enableSynchronousStateUpdates(): Boolean
@DoNotStrip @JvmStatic public external fun enableViewCulling(): Boolean
@DoNotStrip @JvmStatic public external fun enableViewRecycling(): Boolean
@@ -120,12 +120,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun preparedTextCacheSize(): Double
@DoNotStrip @JvmStatic public external fun preventShadowTreeCommitExhaustionWithLocking(): Boolean
@DoNotStrip @JvmStatic public external fun releaseImageDataWhenConsumed(): Boolean
@DoNotStrip @JvmStatic public external fun skipActivityIdentityAssertionOnHostPause(): Boolean
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean
@@ -134,10 +130,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip @JvmStatic public external fun useOptimizedEventBatchingOnAndroid(): 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<<c5ed8a904dcda7ade87527233b879b92>>
* @generated SignedSource<<21fdb20c1fca274a6096de088bdd85ff>>
*/
/**
@@ -67,8 +67,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableIOSViewClipToPaddingBox(): Boolean = false
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean = false
override fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean = false
override fun enableLayoutAnimationsOnAndroid(): Boolean = false
@@ -91,6 +89,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableResourceTimingAPI(): Boolean = false
override fun enableSynchronousStateUpdates(): Boolean = false
override fun enableViewCulling(): Boolean = false
override fun enableViewRecycling(): Boolean = false
@@ -101,7 +101,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableVirtualViewDebugFeatures(): Boolean = false
override fun enableVirtualViewRenderState(): Boolean = true
override fun enableVirtualViewRenderState(): Boolean = false
override fun enableVirtualViewWindowFocusDetection(): Boolean = false
@@ -115,12 +115,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun preparedTextCacheSize(): Double = 200.0
override fun preventShadowTreeCommitExhaustionWithLocking(): Boolean = false
override fun releaseImageDataWhenConsumed(): Boolean = false
override fun skipActivityIdentityAssertionOnHostPause(): Boolean = false
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = false
@@ -129,10 +125,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun useFabricInterop(): Boolean = true
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = false
override fun useNativeTransformHelperAndroid(): Boolean = false
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false
override fun useOptimizedEventBatchingOnAndroid(): 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<<23605f090bfbebe911caa9d3d834d3e8>>
* @generated SignedSource<<09d954a90ac197146c441b71a7352c24>>
*/
/**
@@ -46,7 +46,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
@@ -58,6 +57,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
private var enableResourceTimingAPICache: Boolean? = null
private var enableSynchronousStateUpdatesCache: Boolean? = null
private var enableViewCullingCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
@@ -70,15 +70,11 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
private var preparedTextCacheSizeCache: Double? = null
private var preventShadowTreeCommitExhaustionWithLockingCache: Boolean? = null
private var releaseImageDataWhenConsumedCache: Boolean? = null
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -307,16 +303,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean {
var cached = enableImmediateUpdateModeForContentOffsetChangesCache
if (cached == null) {
cached = currentProvider.enableImmediateUpdateModeForContentOffsetChanges()
accessedFeatureFlags.add("enableImmediateUpdateModeForContentOffsetChanges")
enableImmediateUpdateModeForContentOffsetChangesCache = cached
}
return cached
}
override fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean {
var cached = enableInteropViewManagerClassLookUpOptimizationIOSCache
if (cached == null) {
@@ -427,6 +413,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableSynchronousStateUpdates(): Boolean {
var cached = enableSynchronousStateUpdatesCache
if (cached == null) {
cached = currentProvider.enableSynchronousStateUpdates()
accessedFeatureFlags.add("enableSynchronousStateUpdates")
enableSynchronousStateUpdatesCache = cached
}
return cached
}
override fun enableViewCulling(): Boolean {
var cached = enableViewCullingCache
if (cached == null) {
@@ -547,16 +543,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun preventShadowTreeCommitExhaustionWithLocking(): Boolean {
var cached = preventShadowTreeCommitExhaustionWithLockingCache
if (cached == null) {
cached = currentProvider.preventShadowTreeCommitExhaustionWithLocking()
accessedFeatureFlags.add("preventShadowTreeCommitExhaustionWithLocking")
preventShadowTreeCommitExhaustionWithLockingCache = cached
}
return cached
}
override fun releaseImageDataWhenConsumed(): Boolean {
var cached = releaseImageDataWhenConsumedCache
if (cached == null) {
@@ -567,16 +553,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun skipActivityIdentityAssertionOnHostPause(): Boolean {
var cached = skipActivityIdentityAssertionOnHostPauseCache
if (cached == null) {
cached = currentProvider.skipActivityIdentityAssertionOnHostPause()
accessedFeatureFlags.add("skipActivityIdentityAssertionOnHostPause")
skipActivityIdentityAssertionOnHostPauseCache = cached
}
return cached
}
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
if (cached == null) {
@@ -617,26 +593,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean {
var cached = useNativeEqualsInNativeReadableArrayAndroidCache
if (cached == null) {
cached = currentProvider.useNativeEqualsInNativeReadableArrayAndroid()
accessedFeatureFlags.add("useNativeEqualsInNativeReadableArrayAndroid")
useNativeEqualsInNativeReadableArrayAndroidCache = cached
}
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = currentProvider.useNativeTransformHelperAndroid()
accessedFeatureFlags.add("useNativeTransformHelperAndroid")
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
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<<780793412b76f101be1569d7a866c435>>
* @generated SignedSource<<50a3f126540e1b2afd4a4ce85a4d1ac3>>
*/
/**
@@ -67,8 +67,6 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableIOSViewClipToPaddingBox(): Boolean
@DoNotStrip public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean
@DoNotStrip public fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@DoNotStrip public fun enableLayoutAnimationsOnAndroid(): Boolean
@@ -91,6 +89,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableResourceTimingAPI(): Boolean
@DoNotStrip public fun enableSynchronousStateUpdates(): Boolean
@DoNotStrip public fun enableViewCulling(): Boolean
@DoNotStrip public fun enableViewRecycling(): Boolean
@@ -115,12 +115,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun preparedTextCacheSize(): Double
@DoNotStrip public fun preventShadowTreeCommitExhaustionWithLocking(): Boolean
@DoNotStrip public fun releaseImageDataWhenConsumed(): Boolean
@DoNotStrip public fun skipActivityIdentityAssertionOnHostPause(): Boolean
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
@DoNotStrip public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean
@@ -129,10 +125,6 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun useFabricInterop(): Boolean
@DoNotStrip public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip public fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip public fun useOptimizedEventBatchingOnAndroid(): Boolean
@@ -23,6 +23,10 @@ import com.facebook.react.common.build.ReactBuildConfig
@SuppressLint("UseReactNativeNewArchitectureFeatureFlagDetector")
public object ReactNativeNewArchitectureFeatureFlags {
@JvmStatic
public fun isNewArchitectureStrictModeEnabled(): Boolean =
ReactBuildConfig.UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE
@JvmStatic
public fun enableBridgelessArchitecture(): Boolean {
if (ReactBuildConfig.UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE) {
@@ -253,18 +253,11 @@ public class ReactHostImpl(
val currentActivity = this.currentActivity
if (currentActivity != null) {
val isSameActivity = activity === currentActivity
if (!isSameActivity) {
val currentActivityClass = currentActivity.javaClass.simpleName
val activityClass = if (activity == null) "null" else activity.javaClass.simpleName
val isNotSameActivityMessage =
"Pausing an activity that is not the current activity, this is incorrect! Current activity: $currentActivityClass Paused activity: $activityClass"
if (ReactNativeFeatureFlags.skipActivityIdentityAssertionOnHostPause()) {
log(method, isNotSameActivityMessage)
} else {
Assertions.assertCondition(isSameActivity, isNotSameActivityMessage)
}
}
val currentActivityClass = currentActivity.javaClass.simpleName
val activityClass = if (activity == null) "null" else activity.javaClass.simpleName
Assertions.assertCondition(
activity === currentActivity,
"Pausing an activity that is not the current activity, this is incorrect! Current activity: $currentActivityClass Paused activity: $activityClass")
}
maybeEnableDevSupport(false)
@@ -13,10 +13,8 @@ import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.window.layout.WindowMetricsCalculator
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
/**
* Holds an instance of the current DisplayMetrics so we don't have to thread it through all the
@@ -64,19 +62,9 @@ public object DisplayMetricsHolder {
@JvmStatic
public fun initDisplayMetrics(context: Context) {
val displayMetrics = context.resources.displayMetrics
val windowDisplayMetrics = DisplayMetrics()
windowDisplayMetrics = displayMetrics
val screenDisplayMetrics = DisplayMetrics()
windowDisplayMetrics.setTo(displayMetrics)
screenDisplayMetrics.setTo(displayMetrics)
if (isEdgeToEdgeFeatureFlagOn) {
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context).let {
windowDisplayMetrics.widthPixels = it.bounds.width()
windowDisplayMetrics.heightPixels = it.bounds.height()
}
}
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
// Get the real display metrics if we are using API level 17 or higher.
// The real metrics include system decor elements (e.g. soft menu bar).
@@ -84,8 +72,6 @@ public object DisplayMetricsHolder {
// See:
// http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(screenDisplayMetrics)
DisplayMetricsHolder.windowDisplayMetrics = windowDisplayMetrics
DisplayMetricsHolder.screenDisplayMetrics = screenDisplayMetrics
}
@@ -19,7 +19,7 @@ import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel
import com.facebook.react.uimanager.events.Event
/** Event used to notify JS component about changes of its position or dimensions. */
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.WARNING)
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
public class OnLayoutEvent private constructor() : Event<OnLayoutEvent>() {
@VisibleForTesting internal var x: Int = 0
@VisibleForTesting internal var y: Int = 0
@@ -60,7 +60,7 @@ public class OnLayoutEvent private constructor() : Event<OnLayoutEvent>() {
public companion object {
init {
LegacyArchitectureLogger.assertLegacyArchitecture(
"OnLayoutEvent", LegacyArchitectureLogLevel.WARNING)
"OnLayoutEvent", LegacyArchitectureLogLevel.ERROR)
}
private val EVENTS_POOL: SynchronizedPool<OnLayoutEvent> = SynchronizedPool<OnLayoutEvent>(20)
@@ -8,12 +8,10 @@
package com.facebook.react.uimanager
import com.facebook.common.logging.FLog
import com.facebook.react.bridge.NativeArray
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.ReactConstants
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
public object TransformHelper {
@@ -71,14 +69,6 @@ public object TransformHelper {
transformOrigin: ReadableArray?,
allowPercentageResolution: Boolean
) {
if (allowPercentageResolution &&
ReactNativeFeatureFlags.useNativeTransformHelperAndroid() &&
transforms is NativeArray &&
transformOrigin is NativeArray?) {
nativeProcessTransform(transforms, result, viewWidth, viewHeight, transformOrigin)
return
}
val helperMatrix = helperMatrix.get()!!
MatrixMathHelper.resetIdentityMatrix(result)
val offsets =
@@ -230,13 +220,4 @@ public object TransformHelper {
return doubleArrayOf(newTranslateX, newTranslateY, newTranslateZ)
}
@JvmStatic
private external fun nativeProcessTransform(
transforms: NativeArray,
result: DoubleArray,
viewWidth: Float,
viewHeight: Float,
transformOrigin: NativeArray?
)
}
@@ -377,9 +377,6 @@ public class ReactScrollView extends ScrollView
}
ReactScrollViewHelper.emitLayoutEvent(this);
if (mVirtualViewContainerState != null) {
mVirtualViewContainerState.updateState();
}
}
@Override
@@ -489,9 +486,6 @@ public class ReactScrollView extends ScrollView
this,
mOnScrollDispatchHelper.getXFlingVelocity(),
mOnScrollDispatchHelper.getYFlingVelocity());
if (mVirtualViewContainerState != null) {
mVirtualViewContainerState.updateState();
}
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT);
@@ -11,7 +11,6 @@ import android.graphics.Rect
import android.view.ViewGroup
import com.facebook.common.logging.FLog
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.views.virtual.VirtualViewMode
import java.util.*
@@ -46,19 +45,22 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean {
return true
}
internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
internal class VirtualViewContainerState(private val scrollView: ViewGroup) :
ReactScrollViewHelper.ScrollListener {
private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
private val prerenderRatio: Int = 1
private val virtualViews: MutableSet<VirtualView> = mutableSetOf()
private val emptyRect: Rect = Rect()
private val visibleRect: Rect = Rect()
private val prerenderRect: Rect = Rect()
public fun onChange(virtualView: VirtualView) {
if (virtualViews.add(virtualView)) {
debugLog("add", { "virtualViewID=${virtualView.virtualViewID}" })
} else {
debugLog("update", { "virtualViewID=${virtualView.virtualViewID}" })
init {
ReactScrollViewHelper.addScrollListener(this)
}
public fun add(virtualView: VirtualView) {
assert(virtualViews.add(virtualView)) {
"Attempting to add duplicate VirtualView: ${virtualView.virtualViewID}"
}
updateModes(virtualView)
}
@@ -67,13 +69,35 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
assert(virtualViews.remove(virtualView)) {
"Attempting to remove non-existent VirtualView: ${virtualView.virtualViewID}"
}
debugLog("remove", { "virtualViewID=${virtualView.virtualViewID}" })
}
// Called on ScrollView onLayout or onScroll
public fun updateState() {
debugLog("VirtualViewContainer.updateState")
updateModes()
// ReactScrollViewHelper.ScrollListener.onLayout
// Emitted from ScrollView's onLayout
override fun onLayout(scrollView: ViewGroup?) {
// ReactScrollViewHelper is global
if (this.scrollView == scrollView) {
debugLog("ReactScrollViewHelper.onLayout")
updateModes()
}
}
// ReactScrollViewHelper.ScrollListener.onScroll
// Emitted from ScrollView's onLayout
override fun onScroll(
scrollView: ViewGroup?,
scrollEventType: ScrollEventType?,
xVelocity: Float,
yVelocity: Float
) {
// ReactScrollViewHelper is global
if (this.scrollView == scrollView) {
debugLog("ReactScrollViewHelper.onScroll")
updateModes()
}
}
public fun update(virtualView: VirtualView) {
updateModes(virtualView)
}
private fun updateModes(virtualView: VirtualView? = null) {
@@ -86,26 +110,17 @@ internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
val virtualViewsIt = if (virtualView != null) listOf(virtualView) else virtualViews
virtualViewsIt.forEach { vv ->
val rect = vv.containerRelativeRect
var mode = VirtualViewMode.Hidden
var thresholdRect = emptyRect
when {
rect.isEmpty -> {}
rectsOverlap(rect, visibleRect) -> {
mode = VirtualViewMode.Visible
thresholdRect = visibleRect
vv.onModeChange(VirtualViewMode.Visible, visibleRect)
}
rectsOverlap(rect, prerenderRect) -> {
mode = VirtualViewMode.Prerender
thresholdRect = prerenderRect
vv.onModeChange(VirtualViewMode.Prerender, prerenderRect)
}
else -> {
vv.onModeChange(VirtualViewMode.Hidden, emptyRect)
}
else -> {}
}
debugLog(
"updateModes",
{ "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" })
vv.onModeChange(mode, thresholdRect)
}
}
}
@@ -101,7 +101,7 @@ public class ReactVirtualView(context: Context) :
ReactScrollViewHelper.removeScrollListener(this)
ReactScrollViewHelper.removeLayoutChangeListener(this)
if (detectWindowFocus) {
viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)
viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
cleanupLayoutListeners()
}
@@ -34,7 +34,6 @@ public class ReactVirtualViewExperimental(context: Context) :
override val containerRelativeRect: Rect = Rect()
private var offsetX: Int = 0
private var offsetY: Int = 0
private var hadLayout: Boolean = false
internal val nativeId: String?
get() = getTag(R.id.view_tag_native_id) as? String
@@ -46,20 +45,14 @@ public class ReactVirtualViewExperimental(context: Context) :
@VisibleForTesting
internal fun doAttachedToWindow() {
scrollView = getScrollView()
// onAttachedToWindow is usually called before layout but there are cases where it's called
// after. If called after, we need to report the updated layout to the VirtualViewContainer
if (hadLayout) {
updateParentOffset()
reportChangeToContainer()
}
// Assuming that layout has been called before this
scrollView = getScrollView()?.also { scrollView?.virtualViewContainerState?.add(this) }
}
/** From [View#onLayout] */
// This is when the view itself has layout changes
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
hadLayout = true
if (changed) {
containerRelativeRect.set(
left + offsetX,
@@ -67,7 +60,7 @@ public class ReactVirtualViewExperimental(context: Context) :
right + offsetX,
bottom + offsetY,
)
reportChangeToContainer()
updateContainer()
}
}
@@ -84,8 +77,24 @@ public class ReactVirtualViewExperimental(context: Context) :
oldBottom: Int
) {
if (oldLeft != left || oldTop != top) {
updateParentOffset()
reportChangeToContainer()
val virtualViewScrollView = scrollView ?: return
offsetX = 0
offsetY = 0
var parent: ViewParent? = parent
while (parent != null && parent != virtualViewScrollView) {
if (parent is View) {
offsetX += parent.left
offsetY += parent.top
}
parent = parent.parent
}
containerRelativeRect.set(
left + offsetX,
top + offsetY,
right + offsetX,
bottom + offsetY,
)
updateContainer()
}
}
@@ -100,8 +109,6 @@ public class ReactVirtualViewExperimental(context: Context) :
scrollView = null
mode = null
modeChangeEmitter = null
hadLayout = false
containerRelativeRect.setEmpty()
}
override val virtualViewID: String
@@ -149,28 +156,8 @@ public class ReactVirtualViewExperimental(context: Context) :
}
}
private fun updateParentOffset() {
val virtualViewScrollView = scrollView ?: return
offsetX = 0
offsetY = 0
var parent: ViewParent? = parent
while (parent != null && parent != virtualViewScrollView) {
if (parent is View) {
offsetX += parent.left
offsetY += parent.top
}
parent = parent.parent
}
containerRelativeRect.set(
left + offsetX,
top + offsetY,
right + offsetX,
bottom + offsetY,
)
}
private fun reportChangeToContainer() {
scrollView?.virtualViewContainerState?.onChange(this)
private fun updateContainer() {
scrollView?.virtualViewContainerState?.update(this)
}
private fun getScrollView(): VirtualViewContainer? = traverseParentStack(true)
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f310a5dc27fd655eab4445cb25d4c85c>>
* @generated SignedSource<<d106cd4237f65206b31428ec09344536>>
*/
/**
@@ -171,12 +171,6 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableImmediateUpdateModeForContentOffsetChanges");
return method(javaProvider_);
}
bool enableInteropViewManagerClassLookUpOptimizationIOS() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableInteropViewManagerClassLookUpOptimizationIOS");
@@ -243,6 +237,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableSynchronousStateUpdates() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableSynchronousStateUpdates");
return method(javaProvider_);
}
bool enableViewCulling() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableViewCulling");
@@ -315,24 +315,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool preventShadowTreeCommitExhaustionWithLocking() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("preventShadowTreeCommitExhaustionWithLocking");
return method(javaProvider_);
}
bool releaseImageDataWhenConsumed() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("releaseImageDataWhenConsumed");
return method(javaProvider_);
}
bool skipActivityIdentityAssertionOnHostPause() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("skipActivityIdentityAssertionOnHostPause");
return method(javaProvider_);
}
bool traceTurboModulePromiseRejectionsOnAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("traceTurboModulePromiseRejectionsOnAndroid");
@@ -357,18 +345,6 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeEqualsInNativeReadableArrayAndroid");
return method(javaProvider_);
}
bool useNativeTransformHelperAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeTransformHelperAndroid");
return method(javaProvider_);
}
bool useNativeViewConfigsInBridgelessMode() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeViewConfigsInBridgelessMode");
@@ -525,11 +501,6 @@ bool JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox(
return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
}
bool JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges();
}
bool JReactNativeFeatureFlagsCxxInterop::enableInteropViewManagerClassLookUpOptimizationIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS();
@@ -585,6 +556,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableResourceTimingAPI(
return ReactNativeFeatureFlags::enableResourceTimingAPI();
}
bool JReactNativeFeatureFlagsCxxInterop::enableSynchronousStateUpdates(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableSynchronousStateUpdates();
}
bool JReactNativeFeatureFlagsCxxInterop::enableViewCulling(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableViewCulling();
@@ -645,21 +621,11 @@ double JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize(
return ReactNativeFeatureFlags::preparedTextCacheSize();
}
bool JReactNativeFeatureFlagsCxxInterop::preventShadowTreeCommitExhaustionWithLocking(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::preventShadowTreeCommitExhaustionWithLocking();
}
bool JReactNativeFeatureFlagsCxxInterop::releaseImageDataWhenConsumed(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::releaseImageDataWhenConsumed();
}
bool JReactNativeFeatureFlagsCxxInterop::skipActivityIdentityAssertionOnHostPause(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::skipActivityIdentityAssertionOnHostPause();
}
bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
@@ -680,16 +646,6 @@ bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop(
return ReactNativeFeatureFlags::useFabricInterop();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -822,9 +778,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableIOSViewClipToPaddingBox",
JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox),
makeNativeMethod(
"enableImmediateUpdateModeForContentOffsetChanges",
JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges),
makeNativeMethod(
"enableInteropViewManagerClassLookUpOptimizationIOS",
JReactNativeFeatureFlagsCxxInterop::enableInteropViewManagerClassLookUpOptimizationIOS),
@@ -858,6 +811,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableResourceTimingAPI",
JReactNativeFeatureFlagsCxxInterop::enableResourceTimingAPI),
makeNativeMethod(
"enableSynchronousStateUpdates",
JReactNativeFeatureFlagsCxxInterop::enableSynchronousStateUpdates),
makeNativeMethod(
"enableViewCulling",
JReactNativeFeatureFlagsCxxInterop::enableViewCulling),
@@ -894,15 +850,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"preparedTextCacheSize",
JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize),
makeNativeMethod(
"preventShadowTreeCommitExhaustionWithLocking",
JReactNativeFeatureFlagsCxxInterop::preventShadowTreeCommitExhaustionWithLocking),
makeNativeMethod(
"releaseImageDataWhenConsumed",
JReactNativeFeatureFlagsCxxInterop::releaseImageDataWhenConsumed),
makeNativeMethod(
"skipActivityIdentityAssertionOnHostPause",
JReactNativeFeatureFlagsCxxInterop::skipActivityIdentityAssertionOnHostPause),
makeNativeMethod(
"traceTurboModulePromiseRejectionsOnAndroid",
JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid),
@@ -915,12 +865,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"useFabricInterop",
JReactNativeFeatureFlagsCxxInterop::useFabricInterop),
makeNativeMethod(
"useNativeEqualsInNativeReadableArrayAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAndroid),
makeNativeMethod(
"useNativeTransformHelperAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid),
makeNativeMethod(
"useNativeViewConfigsInBridgelessMode",
JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode),
@@ -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<<8c1da07c0b7d2053f7fdaac4326c3ac1>>
* @generated SignedSource<<42e5e857fdfb9ad780ccc3f942638891>>
*/
/**
@@ -96,9 +96,6 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableIOSViewClipToPaddingBox(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableImmediateUpdateModeForContentOffsetChanges(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableInteropViewManagerClassLookUpOptimizationIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -132,6 +129,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableResourceTimingAPI(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableSynchronousStateUpdates(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableViewCulling(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -168,15 +168,9 @@ class JReactNativeFeatureFlagsCxxInterop
static double preparedTextCacheSize(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool preventShadowTreeCommitExhaustionWithLocking(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool releaseImageDataWhenConsumed(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool skipActivityIdentityAssertionOnHostPause(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool traceTurboModulePromiseRejectionsOnAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -189,12 +183,6 @@ class JReactNativeFeatureFlagsCxxInterop
static bool useFabricInterop(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeEqualsInNativeReadableArrayAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -31,14 +31,13 @@ add_library(
OnLoad-common.cpp
ReadableNativeArray.cpp
ReadableNativeMap.cpp
TransformHelper.cpp
WritableNativeArray.cpp
WritableNativeMap.cpp
)
target_merge_so(reactnativejni_common)
target_include_directories(reactnativejni_common PUBLIC ../../)
target_link_libraries(reactnativejni_common fbjni folly_runtime react_cxxreact rrc_view)
target_link_libraries(reactnativejni_common fbjni folly_runtime react_cxxreact)
target_compile_reactnative_options(reactnativejni_common PRIVATE)
target_compile_options(reactnativejni_common PRIVATE -Wno-unused-lambda-capture)
@@ -21,10 +21,6 @@ class NativeArray : public jni::HybridClass<NativeArray> {
jni::local_ref<jstring> toString();
const folly::dynamic& getArray() const {
return array_;
}
RN_EXPORT folly::dynamic consume();
// Whether this array has been added to another array or map and no longer
@@ -11,7 +11,6 @@
#include "JReactMarker.h"
#include "NativeArray.h"
#include "NativeMap.h"
#include "TransformHelper.h"
#include "WritableNativeArray.h"
#include "WritableNativeMap.h"
@@ -28,7 +27,6 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
ReadableNativeMap::registerNatives();
WritableNativeArray::registerNatives();
WritableNativeMap::registerNatives();
TransformHelper::registerNatives();
});
}
@@ -40,16 +40,10 @@ local_ref<JArrayClass<jobject>> ReadableNativeArray::importTypeArray() {
return jarray;
}
bool ReadableNativeArray::equals(
jni::alias_ref<ReadableNativeArray::javaobject> other) {
return array_ == other->cthis()->array_;
}
void ReadableNativeArray::registerNatives() {
registerHybrid({
makeNativeMethod("importArray", ReadableNativeArray::importArray),
makeNativeMethod("importTypeArray", ReadableNativeArray::importTypeArray),
makeNativeMethod("nativeEquals", ReadableNativeArray::equals),
});
}
@@ -35,7 +35,6 @@ class ReadableNativeArray
static void mapException(std::exception_ptr ex);
static void registerNatives();
bool equals(jni::alias_ref<ReadableNativeArray::javaobject> other);
jni::local_ref<jni::JArrayClass<jobject>> importArray();
jni::local_ref<jni::JArrayClass<jobject>> importTypeArray();
};
@@ -1,60 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "TransformHelper.h"
#include <react/renderer/components/view/BaseViewProps.h>
#include <react/renderer/components/view/conversions.h>
#include "NativeArray.h"
using namespace facebook::jni;
namespace facebook::react {
namespace {
void processTransform(
jni::alias_ref<jclass> /*unused*/,
NativeArray* jTransforms,
jni::alias_ref<jni::JArrayDouble> jResult,
float viewWidth,
float viewHeight,
NativeArray* jTransformOrigin) {
// Assuming parsing transforms doesn't require a real PropsParserContext
static ContextContainer contextContainer;
static PropsParserContext context(0, contextContainer);
RawValue transformValue(jTransforms->getArray());
Transform transform;
fromRawValue(context, transformValue, transform);
TransformOrigin transformOrigin;
if (jTransformOrigin != nullptr) {
RawValue transformOriginValue(jTransformOrigin->getArray());
fromRawValue(context, transformOriginValue, transformOrigin);
}
auto result = BaseViewProps::resolveTransform(
Size(viewWidth, viewHeight), transform, transformOrigin);
// Convert from matrix of floats to double matrix
constexpr size_t MatrixSize = std::tuple_size_v<decltype(result.matrix)>;
std::array<double, MatrixSize> doubleTransform{};
std::copy(
result.matrix.begin(), result.matrix.end(), doubleTransform.begin());
jResult->setRegion(0, MatrixSize, doubleTransform.data());
}
} // namespace
void TransformHelper::registerNatives() {
javaClassLocal()->registerNatives({
makeNativeMethod("nativeProcessTransform", processTransform),
});
}
} // namespace facebook::react
@@ -1,22 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fbjni/fbjni.h>
namespace facebook::react {
class TransformHelper : public jni::JavaClass<TransformHelper> {
public:
static auto constexpr* kJavaDescriptor =
"Lcom/facebook/react/uimanager/TransformHelper;";
static void registerNatives();
};
} // namespace facebook::react
@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string>
namespace facebook::react::jsinspector_modern {
/**
* https://developer.chrome.com/docs/devtools/performance/extension?utm_source=devtools#devtools_object
*/
struct DevToolsTrackEntryPayload {
std::string track;
};
} // namespace facebook::react::jsinspector_modern
@@ -113,8 +113,7 @@ void PerformanceTracer::collectEvents(
void PerformanceTracer::reportMark(
const std::string_view& name,
HighResTimeStamp start,
folly::dynamic&& detail) {
HighResTimeStamp start) {
if (!tracingAtomic_) {
return;
}
@@ -124,13 +123,6 @@ void PerformanceTracer::reportMark(
return;
}
folly::dynamic eventArgs = folly::dynamic::object();
if (detail != nullptr) {
eventArgs = folly::dynamic::object(
"data",
folly::dynamic::object("detail", folly::toJson(std::move(detail))));
}
buffer_.emplace_back(TraceEvent{
.name = std::string(name),
.cat = "blink.user_timing",
@@ -138,7 +130,6 @@ void PerformanceTracer::reportMark(
.ts = start,
.pid = processId_,
.tid = oscompat::getCurrentThreadId(),
.args = eventArgs,
});
}
@@ -146,15 +137,18 @@ void PerformanceTracer::reportMeasure(
const std::string_view& name,
HighResTimeStamp start,
HighResDuration duration,
folly::dynamic&& detail) {
const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
if (!tracingAtomic_) {
return;
}
folly::dynamic beginEventArgs = folly::dynamic::object();
if (detail != nullptr) {
if (trackMetadata.has_value()) {
folly::dynamic devtoolsObject = folly::dynamic::object(
"devtools",
folly::dynamic::object("track", trackMetadata.value().track));
beginEventArgs =
folly::dynamic::object("detail", folly::toJson(std::move(detail)));
folly::dynamic::object("detail", folly::toJson(devtoolsObject));
}
auto currentThreadId = oscompat::getCurrentThreadId();
@@ -7,6 +7,7 @@
#pragma once
#include "CdpTracing.h"
#include "ConsoleTimeStamp.h"
#include "TraceEvent.h"
#include "TraceEventProfile.h"
@@ -59,17 +60,13 @@ class PerformanceTracer {
const std::function<void(const folly::dynamic& eventsChunk)>&
resultCallback,
uint16_t chunkSize);
/**
* Record a `Performance.mark()` event - a labelled timestamp. If not
* currently tracing, this is a no-op.
*
* See https://w3c.github.io/user-timing/#mark-method.
*/
void reportMark(
const std::string_view& name,
HighResTimeStamp start,
folly::dynamic&& detail = nullptr);
void reportMark(const std::string_view& name, HighResTimeStamp start);
/**
* Record a `Performance.measure()` event - a labelled duration. If not
@@ -81,7 +78,7 @@ class PerformanceTracer {
const std::string_view& name,
HighResTimeStamp start,
HighResDuration duration,
folly::dynamic&& detail = nullptr);
const std::optional<DevToolsTrackEntryPayload>& trackMetadata);
/**
* Record a "TimeStamp" Trace Event - a labelled entry on Performance
@@ -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<<355265aa8f13e3f307d6e30db6b80d41>>
* @generated SignedSource<<0ebe67635de9568917a9ed02d92a88e2>>
*/
/**
@@ -114,10 +114,6 @@ bool ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox() {
return getAccessor().enableIOSViewClipToPaddingBox();
}
bool ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges() {
return getAccessor().enableImmediateUpdateModeForContentOffsetChanges();
}
bool ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS() {
return getAccessor().enableInteropViewManagerClassLookUpOptimizationIOS();
}
@@ -162,6 +158,10 @@ bool ReactNativeFeatureFlags::enableResourceTimingAPI() {
return getAccessor().enableResourceTimingAPI();
}
bool ReactNativeFeatureFlags::enableSynchronousStateUpdates() {
return getAccessor().enableSynchronousStateUpdates();
}
bool ReactNativeFeatureFlags::enableViewCulling() {
return getAccessor().enableViewCulling();
}
@@ -210,18 +210,10 @@ double ReactNativeFeatureFlags::preparedTextCacheSize() {
return getAccessor().preparedTextCacheSize();
}
bool ReactNativeFeatureFlags::preventShadowTreeCommitExhaustionWithLocking() {
return getAccessor().preventShadowTreeCommitExhaustionWithLocking();
}
bool ReactNativeFeatureFlags::releaseImageDataWhenConsumed() {
return getAccessor().releaseImageDataWhenConsumed();
}
bool ReactNativeFeatureFlags::skipActivityIdentityAssertionOnHostPause() {
return getAccessor().skipActivityIdentityAssertionOnHostPause();
}
bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() {
return getAccessor().traceTurboModulePromiseRejectionsOnAndroid();
}
@@ -238,14 +230,6 @@ bool ReactNativeFeatureFlags::useFabricInterop() {
return getAccessor().useFabricInterop();
}
bool ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid() {
return getAccessor().useNativeEqualsInNativeReadableArrayAndroid();
}
bool ReactNativeFeatureFlags::useNativeTransformHelperAndroid() {
return getAccessor().useNativeTransformHelperAndroid();
}
bool ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode() {
return getAccessor().useNativeViewConfigsInBridgelessMode();
}
@@ -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<<c584290ce61ee78bdf843c19dd40c40d>>
* @generated SignedSource<<ce7268c5f71d95c5214909fbcfa9355e>>
*/
/**
@@ -149,11 +149,6 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableIOSViewClipToPaddingBox();
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
RN_EXPORT static bool enableImmediateUpdateModeForContentOffsetChanges();
/**
* This is to fix the issue with interop view manager where component descriptor lookup is causing ViewManager to preload.
*/
@@ -209,6 +204,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableResourceTimingAPI();
/**
* Dispatches state updates synchronously in Fabric (e.g.: updates the scroll position in the shadow tree synchronously from the main thread).
*/
RN_EXPORT static bool enableSynchronousStateUpdates();
/**
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
*/
@@ -269,21 +269,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static double preparedTextCacheSize();
/**
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
*/
RN_EXPORT static bool preventShadowTreeCommitExhaustionWithLocking();
/**
* Releases the cached image data when it is consumed by the observers.
*/
RN_EXPORT static bool releaseImageDataWhenConsumed();
/**
* Skip activity identity assertion in ReactHostImpl::onHostPause()
*/
RN_EXPORT static bool skipActivityIdentityAssertionOnHostPause();
/**
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
*/
@@ -304,16 +294,6 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool useFabricInterop();
/**
* Use a native implementation of equals in NativeReadableArray.
*/
RN_EXPORT static bool useNativeEqualsInNativeReadableArrayAndroid();
/**
* Use a native implementation of TransformHelper
*/
RN_EXPORT static bool useNativeTransformHelperAndroid();
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -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<<dc5c6b3273ef312426c4ac366cc13772>>
* @generated SignedSource<<674887274e2f33e31ca43b0aabe77135>>
*/
/**
@@ -425,24 +425,6 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetChanges() {
auto flagValue = enableImmediateUpdateModeForContentOffsetChanges_.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(22, "enableImmediateUpdateModeForContentOffsetChanges");
flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges();
enableImmediateUpdateModeForContentOffsetChanges_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimizationIOS() {
auto flagValue = enableInteropViewManagerClassLookUpOptimizationIOS_.load();
@@ -452,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "enableInteropViewManagerClassLookUpOptimizationIOS");
markFlagAsAccessed(22, "enableInteropViewManagerClassLookUpOptimizationIOS");
flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS();
enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue;
@@ -470,7 +452,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(24, "enableLayoutAnimationsOnAndroid");
markFlagAsAccessed(23, "enableLayoutAnimationsOnAndroid");
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
enableLayoutAnimationsOnAndroid_ = flagValue;
@@ -488,7 +470,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(25, "enableLayoutAnimationsOnIOS");
markFlagAsAccessed(24, "enableLayoutAnimationsOnIOS");
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
enableLayoutAnimationsOnIOS_ = flagValue;
@@ -506,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "enableMainQueueCoordinatorOnIOS");
markFlagAsAccessed(25, "enableMainQueueCoordinatorOnIOS");
flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS();
enableMainQueueCoordinatorOnIOS_ = flagValue;
@@ -524,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(27, "enableModuleArgumentNSNullConversionIOS");
markFlagAsAccessed(26, "enableModuleArgumentNSNullConversionIOS");
flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS();
enableModuleArgumentNSNullConversionIOS_ = flagValue;
@@ -542,7 +524,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(28, "enableNativeCSSParsing");
markFlagAsAccessed(27, "enableNativeCSSParsing");
flagValue = currentProvider_->enableNativeCSSParsing();
enableNativeCSSParsing_ = flagValue;
@@ -560,7 +542,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(29, "enableNetworkEventReporting");
markFlagAsAccessed(28, "enableNetworkEventReporting");
flagValue = currentProvider_->enableNetworkEventReporting();
enableNetworkEventReporting_ = flagValue;
@@ -578,7 +560,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(30, "enableNewBackgroundAndBorderDrawables");
markFlagAsAccessed(29, "enableNewBackgroundAndBorderDrawables");
flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables();
enableNewBackgroundAndBorderDrawables_ = flagValue;
@@ -596,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "enablePreparedTextLayout");
markFlagAsAccessed(30, "enablePreparedTextLayout");
flagValue = currentProvider_->enablePreparedTextLayout();
enablePreparedTextLayout_ = flagValue;
@@ -614,7 +596,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(32, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(31, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -632,7 +614,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(33, "enableResourceTimingAPI");
markFlagAsAccessed(32, "enableResourceTimingAPI");
flagValue = currentProvider_->enableResourceTimingAPI();
enableResourceTimingAPI_ = flagValue;
@@ -641,6 +623,24 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() {
auto flagValue = enableSynchronousStateUpdates_.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(33, "enableSynchronousStateUpdates");
flagValue = currentProvider_->enableSynchronousStateUpdates();
enableSynchronousStateUpdates_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
auto flagValue = enableViewCulling_.load();
@@ -857,24 +857,6 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustionWithLocking() {
auto flagValue = preventShadowTreeCommitExhaustionWithLocking_.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(46, "preventShadowTreeCommitExhaustionWithLocking");
flagValue = currentProvider_->preventShadowTreeCommitExhaustionWithLocking();
preventShadowTreeCommitExhaustionWithLocking_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
auto flagValue = releaseImageDataWhenConsumed_.load();
@@ -884,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(47, "releaseImageDataWhenConsumed");
markFlagAsAccessed(46, "releaseImageDataWhenConsumed");
flagValue = currentProvider_->releaseImageDataWhenConsumed();
releaseImageDataWhenConsumed_ = flagValue;
@@ -893,24 +875,6 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() {
auto flagValue = skipActivityIdentityAssertionOnHostPause_.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(48, "skipActivityIdentityAssertionOnHostPause");
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
skipActivityIdentityAssertionOnHostPause_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid() {
auto flagValue = traceTurboModulePromiseRejectionsOnAndroid_.load();
@@ -920,7 +884,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(49, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(47, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -938,7 +902,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(50, "updateRuntimeShadowNodeReferencesOnCommit");
markFlagAsAccessed(48, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -956,7 +920,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(51, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(49, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -974,7 +938,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(52, "useFabricInterop");
markFlagAsAccessed(50, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -983,42 +947,6 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroid() {
auto flagValue = useNativeEqualsInNativeReadableArrayAndroid_.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(53, "useNativeEqualsInNativeReadableArrayAndroid");
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
auto flagValue = useNativeTransformHelperAndroid_.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(54, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
auto flagValue = useNativeViewConfigsInBridgelessMode_.load();
@@ -1028,7 +956,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(55, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(51, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1046,7 +974,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(56, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(52, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1064,7 +992,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(57, "useRawPropsJsiValue");
markFlagAsAccessed(53, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1082,7 +1010,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(58, "useShadowNodeStateOnClone");
markFlagAsAccessed(54, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1100,7 +1028,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(59, "useTurboModuleInterop");
markFlagAsAccessed(55, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1118,7 +1046,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(60, "useTurboModules");
markFlagAsAccessed(56, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1136,7 +1064,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(61, "virtualViewPrerenderRatio");
markFlagAsAccessed(57, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<edfd880ca5034a683053dc95e177a7c2>>
* @generated SignedSource<<7355987993949fd07c60efac54994d4b>>
*/
/**
@@ -54,7 +54,6 @@ class ReactNativeFeatureFlagsAccessor {
bool enableFontScaleChangesUpdatingLayout();
bool enableIOSTextBaselineOffsetPerLine();
bool enableIOSViewClipToPaddingBox();
bool enableImmediateUpdateModeForContentOffsetChanges();
bool enableInteropViewManagerClassLookUpOptimizationIOS();
bool enableLayoutAnimationsOnAndroid();
bool enableLayoutAnimationsOnIOS();
@@ -66,6 +65,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enablePreparedTextLayout();
bool enablePropsUpdateReconciliationAndroid();
bool enableResourceTimingAPI();
bool enableSynchronousStateUpdates();
bool enableViewCulling();
bool enableViewRecycling();
bool enableViewRecyclingForText();
@@ -78,15 +78,11 @@ class ReactNativeFeatureFlagsAccessor {
bool fuseboxNetworkInspectionEnabled();
bool hideOffscreenVirtualViewsOnIOS();
double preparedTextCacheSize();
bool preventShadowTreeCommitExhaustionWithLocking();
bool releaseImageDataWhenConsumed();
bool skipActivityIdentityAssertionOnHostPause();
bool traceTurboModulePromiseRejectionsOnAndroid();
bool updateRuntimeShadowNodeReferencesOnCommit();
bool useAlwaysAvailableJSErrorHandling();
bool useFabricInterop();
bool useNativeEqualsInNativeReadableArrayAndroid();
bool useNativeTransformHelperAndroid();
bool useNativeViewConfigsInBridgelessMode();
bool useOptimizedEventBatchingOnAndroid();
bool useRawPropsJsiValue();
@@ -105,7 +101,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 62> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 58> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> cxxNativeAnimatedEnabled_;
@@ -129,7 +125,6 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableFontScaleChangesUpdatingLayout_;
std::atomic<std::optional<bool>> enableIOSTextBaselineOffsetPerLine_;
std::atomic<std::optional<bool>> enableIOSViewClipToPaddingBox_;
std::atomic<std::optional<bool>> enableImmediateUpdateModeForContentOffsetChanges_;
std::atomic<std::optional<bool>> enableInteropViewManagerClassLookUpOptimizationIOS_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnAndroid_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnIOS_;
@@ -141,6 +136,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enablePreparedTextLayout_;
std::atomic<std::optional<bool>> enablePropsUpdateReconciliationAndroid_;
std::atomic<std::optional<bool>> enableResourceTimingAPI_;
std::atomic<std::optional<bool>> enableSynchronousStateUpdates_;
std::atomic<std::optional<bool>> enableViewCulling_;
std::atomic<std::optional<bool>> enableViewRecycling_;
std::atomic<std::optional<bool>> enableViewRecyclingForText_;
@@ -153,15 +149,11 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> fuseboxNetworkInspectionEnabled_;
std::atomic<std::optional<bool>> hideOffscreenVirtualViewsOnIOS_;
std::atomic<std::optional<double>> preparedTextCacheSize_;
std::atomic<std::optional<bool>> preventShadowTreeCommitExhaustionWithLocking_;
std::atomic<std::optional<bool>> releaseImageDataWhenConsumed_;
std::atomic<std::optional<bool>> skipActivityIdentityAssertionOnHostPause_;
std::atomic<std::optional<bool>> traceTurboModulePromiseRejectionsOnAndroid_;
std::atomic<std::optional<bool>> updateRuntimeShadowNodeReferencesOnCommit_;
std::atomic<std::optional<bool>> useAlwaysAvailableJSErrorHandling_;
std::atomic<std::optional<bool>> useFabricInterop_;
std::atomic<std::optional<bool>> useNativeEqualsInNativeReadableArrayAndroid_;
std::atomic<std::optional<bool>> useNativeTransformHelperAndroid_;
std::atomic<std::optional<bool>> useNativeViewConfigsInBridgelessMode_;
std::atomic<std::optional<bool>> useOptimizedEventBatchingOnAndroid_;
std::atomic<std::optional<bool>> useRawPropsJsiValue_;
@@ -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<<614fe0894f6235d879a0b29e7dab7759>>
* @generated SignedSource<<ce04a87cb868a9885b18951c3c0822ae>>
*/
/**
@@ -115,10 +115,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
return false;
}
bool enableInteropViewManagerClassLookUpOptimizationIOS() override {
return false;
}
@@ -163,6 +159,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableSynchronousStateUpdates() override {
return false;
}
bool enableViewCulling() override {
return false;
}
@@ -184,7 +184,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
}
bool enableVirtualViewRenderState() override {
return true;
return false;
}
bool enableVirtualViewWindowFocusDetection() override {
@@ -211,18 +211,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return 200.0;
}
bool preventShadowTreeCommitExhaustionWithLocking() override {
return false;
}
bool releaseImageDataWhenConsumed() override {
return false;
}
bool skipActivityIdentityAssertionOnHostPause() override {
return false;
}
bool traceTurboModulePromiseRejectionsOnAndroid() override {
return false;
}
@@ -239,14 +231,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return true;
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
return false;
}
bool useNativeTransformHelperAndroid() override {
return false;
}
bool useNativeViewConfigsInBridgelessMode() 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<<6502c090532e1d7d2ac67cf6e711d9f4>>
* @generated SignedSource<<f7c839906f0f5c784d05cd4dae80171c>>
*/
/**
@@ -243,15 +243,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableIOSViewClipToPaddingBox();
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
auto value = values_["enableImmediateUpdateModeForContentOffsetChanges"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableImmediateUpdateModeForContentOffsetChanges();
}
bool enableInteropViewManagerClassLookUpOptimizationIOS() override {
auto value = values_["enableInteropViewManagerClassLookUpOptimizationIOS"];
if (!value.isNull()) {
@@ -351,6 +342,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableResourceTimingAPI();
}
bool enableSynchronousStateUpdates() override {
auto value = values_["enableSynchronousStateUpdates"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableSynchronousStateUpdates();
}
bool enableViewCulling() override {
auto value = values_["enableViewCulling"];
if (!value.isNull()) {
@@ -459,15 +459,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::preparedTextCacheSize();
}
bool preventShadowTreeCommitExhaustionWithLocking() override {
auto value = values_["preventShadowTreeCommitExhaustionWithLocking"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::preventShadowTreeCommitExhaustionWithLocking();
}
bool releaseImageDataWhenConsumed() override {
auto value = values_["releaseImageDataWhenConsumed"];
if (!value.isNull()) {
@@ -477,15 +468,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::releaseImageDataWhenConsumed();
}
bool skipActivityIdentityAssertionOnHostPause() override {
auto value = values_["skipActivityIdentityAssertionOnHostPause"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::skipActivityIdentityAssertionOnHostPause();
}
bool traceTurboModulePromiseRejectionsOnAndroid() override {
auto value = values_["traceTurboModulePromiseRejectionsOnAndroid"];
if (!value.isNull()) {
@@ -522,24 +504,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::useFabricInterop();
}
bool useNativeEqualsInNativeReadableArrayAndroid() override {
auto value = values_["useNativeEqualsInNativeReadableArrayAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useNativeEqualsInNativeReadableArrayAndroid();
}
bool useNativeTransformHelperAndroid() override {
auto value = values_["useNativeTransformHelperAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useNativeTransformHelperAndroid();
}
bool useNativeViewConfigsInBridgelessMode() override {
auto value = values_["useNativeViewConfigsInBridgelessMode"];
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<<a26af781141939d35cff83bacf522bb0>>
* @generated SignedSource<<2aa195b3a81e14b785768b9d74f77132>>
*/
/**
@@ -47,7 +47,6 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableFontScaleChangesUpdatingLayout() = 0;
virtual bool enableIOSTextBaselineOffsetPerLine() = 0;
virtual bool enableIOSViewClipToPaddingBox() = 0;
virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0;
virtual bool enableInteropViewManagerClassLookUpOptimizationIOS() = 0;
virtual bool enableLayoutAnimationsOnAndroid() = 0;
virtual bool enableLayoutAnimationsOnIOS() = 0;
@@ -59,6 +58,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enablePreparedTextLayout() = 0;
virtual bool enablePropsUpdateReconciliationAndroid() = 0;
virtual bool enableResourceTimingAPI() = 0;
virtual bool enableSynchronousStateUpdates() = 0;
virtual bool enableViewCulling() = 0;
virtual bool enableViewRecycling() = 0;
virtual bool enableViewRecyclingForText() = 0;
@@ -71,15 +71,11 @@ class ReactNativeFeatureFlagsProvider {
virtual bool fuseboxNetworkInspectionEnabled() = 0;
virtual bool hideOffscreenVirtualViewsOnIOS() = 0;
virtual double preparedTextCacheSize() = 0;
virtual bool preventShadowTreeCommitExhaustionWithLocking() = 0;
virtual bool releaseImageDataWhenConsumed() = 0;
virtual bool skipActivityIdentityAssertionOnHostPause() = 0;
virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0;
virtual bool updateRuntimeShadowNodeReferencesOnCommit() = 0;
virtual bool useAlwaysAvailableJSErrorHandling() = 0;
virtual bool useFabricInterop() = 0;
virtual bool useNativeEqualsInNativeReadableArrayAndroid() = 0;
virtual bool useNativeTransformHelperAndroid() = 0;
virtual bool useNativeViewConfigsInBridgelessMode() = 0;
virtual bool useOptimizedEventBatchingOnAndroid() = 0;
virtual bool useRawPropsJsiValue() = 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<<cadb1f2c1c10a03593dc7c9a9a94747b>>
* @generated SignedSource<<38ab47539ec62bb4be34a46bb567e28a>>
*/
/**
@@ -154,11 +154,6 @@ bool NativeReactNativeFeatureFlags::enableIOSViewClipToPaddingBox(
return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
}
bool NativeReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges();
}
bool NativeReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableInteropViewManagerClassLookUpOptimizationIOS();
@@ -214,6 +209,11 @@ bool NativeReactNativeFeatureFlags::enableResourceTimingAPI(
return ReactNativeFeatureFlags::enableResourceTimingAPI();
}
bool NativeReactNativeFeatureFlags::enableSynchronousStateUpdates(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableSynchronousStateUpdates();
}
bool NativeReactNativeFeatureFlags::enableViewCulling(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableViewCulling();
@@ -274,21 +274,11 @@ double NativeReactNativeFeatureFlags::preparedTextCacheSize(
return ReactNativeFeatureFlags::preparedTextCacheSize();
}
bool NativeReactNativeFeatureFlags::preventShadowTreeCommitExhaustionWithLocking(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::preventShadowTreeCommitExhaustionWithLocking();
}
bool NativeReactNativeFeatureFlags::releaseImageDataWhenConsumed(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::releaseImageDataWhenConsumed();
}
bool NativeReactNativeFeatureFlags::skipActivityIdentityAssertionOnHostPause(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::skipActivityIdentityAssertionOnHostPause();
}
bool NativeReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
@@ -309,16 +299,6 @@ bool NativeReactNativeFeatureFlags::useFabricInterop(
return ReactNativeFeatureFlags::useFabricInterop();
}
bool NativeReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeTransformHelperAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -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<<de04d72490f2c09abdcea51e6f02796c>>
* @generated SignedSource<<854d17fee309ed9228698bd773c5d44e>>
*/
/**
@@ -80,8 +80,6 @@ class NativeReactNativeFeatureFlags
bool enableIOSViewClipToPaddingBox(jsi::Runtime& runtime);
bool enableImmediateUpdateModeForContentOffsetChanges(jsi::Runtime& runtime);
bool enableInteropViewManagerClassLookUpOptimizationIOS(jsi::Runtime& runtime);
bool enableLayoutAnimationsOnAndroid(jsi::Runtime& runtime);
@@ -104,6 +102,8 @@ class NativeReactNativeFeatureFlags
bool enableResourceTimingAPI(jsi::Runtime& runtime);
bool enableSynchronousStateUpdates(jsi::Runtime& runtime);
bool enableViewCulling(jsi::Runtime& runtime);
bool enableViewRecycling(jsi::Runtime& runtime);
@@ -128,12 +128,8 @@ class NativeReactNativeFeatureFlags
double preparedTextCacheSize(jsi::Runtime& runtime);
bool preventShadowTreeCommitExhaustionWithLocking(jsi::Runtime& runtime);
bool releaseImageDataWhenConsumed(jsi::Runtime& runtime);
bool skipActivityIdentityAssertionOnHostPause(jsi::Runtime& runtime);
bool traceTurboModulePromiseRejectionsOnAndroid(jsi::Runtime& runtime);
bool updateRuntimeShadowNodeReferencesOnCommit(jsi::Runtime& runtime);
@@ -142,10 +138,6 @@ class NativeReactNativeFeatureFlags
bool useFabricInterop(jsi::Runtime& runtime);
bool useNativeEqualsInNativeReadableArrayAndroid(jsi::Runtime& runtime);
bool useNativeTransformHelperAndroid(jsi::Runtime& runtime);
bool useNativeViewConfigsInBridgelessMode(jsi::Runtime& runtime);
bool useOptimizedEventBatchingOnAndroid(jsi::Runtime& runtime);
@@ -49,6 +49,21 @@ NativeIntersectionObserver::NativeIntersectionObserver(
std::shared_ptr<CallInvoker> jsInvoker)
: NativeIntersectionObserverCxxSpec(std::move(jsInvoker)) {}
void NativeIntersectionObserver::observe(
jsi::Runtime& runtime,
NativeIntersectionObserverObserveOptions options) {
observeV2(runtime, std::move(options));
}
void NativeIntersectionObserver::unobserve(
jsi::Runtime& runtime,
IntersectionObserverObserverId intersectionObserverId,
std::shared_ptr<const ShadowNode> targetShadowNode) {
auto token =
tokenFromShadowNodeFamily(runtime, targetShadowNode->getFamilyShared());
unobserveV2(runtime, intersectionObserverId, std::move(token));
}
jsi::Object NativeIntersectionObserver::observeV2(
jsi::Runtime& runtime,
NativeIntersectionObserverObserveOptions options) {
@@ -64,6 +64,19 @@ class NativeIntersectionObserver
public:
NativeIntersectionObserver(std::shared_ptr<CallInvoker> jsInvoker);
// TODO(T223605846): Remove legacy observe method
[[deprecated("Please use observeV2")]]
void observe(
jsi::Runtime& runtime,
NativeIntersectionObserverObserveOptions options);
// TODO(T223605846): Remove legacy unobserve method
[[deprecated("Please use unobserveV2")]]
void unobserve(
jsi::Runtime& runtime,
IntersectionObserverObserverId intersectionObserverId,
std::shared_ptr<const ShadowNode> targetShadowNode);
jsi::Object observeV2(
jsi::Runtime& runtime,
NativeIntersectionObserverObserveOptions options);
@@ -18,4 +18,4 @@ target_link_libraries(react_nativemodule_webperformance
react_cxxreact
)
target_compile_reactnative_options(react_nativemodule_webperformance PRIVATE)
target_compile_options(react_nativemodule_webperformance PRIVATE -Wpedantic -Wno-deprecated-declarations)
target_compile_options(react_nativemodule_webperformance PRIVATE -Wpedantic)

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