diff --git a/.flowconfig b/.flowconfig index 99fe6099efb485..376b90c338f215 100644 --- a/.flowconfig +++ b/.flowconfig @@ -90,4 +90,4 @@ untyped-import untyped-type-import [version] -^0.247.1 +^0.248.1 diff --git a/.github/actions/maestro-ios/action.yml b/.github/actions/maestro-ios/action.yml index 9a07eb8e7fd6c3..ba1c9880804f29 100644 --- a/.github/actions/maestro-ios/action.yml +++ b/.github/actions/maestro-ios/action.yml @@ -44,6 +44,7 @@ runs: run: | cd ${{ inputs.working-directory }} yarn start & + sleep 5 # to give metro time to load - name: Run tests id: run-tests shell: bash @@ -68,8 +69,13 @@ runs: echo "Launch the app" xcrun simctl launch $UDID ${{ inputs.app-id }} + if [[ ${{ inputs.flavor }} == 'Debug' ]]; then + # To give the app time to warm the metro's cache + sleep 5 + fi + echo "Running tests with Maestro" - export MAESTRO_DRIVER_STARTUP_TIMEOUT=1800000 # 30 min. CI is extremely slow + export MAESTRO_DRIVER_STARTUP_TIMEOUT=1200000 # 20 min. CI is extremely slow # Add retries for flakyness MAX_ATTEMPTS=5 @@ -112,6 +118,8 @@ runs: video_record_1.mov video_record_2.mov video_record_3.mov + video_record_4.mov + video_record_5.mov report.xml - name: Store Logs if: failure() && steps.run-tests.outcome == 'failure' diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index eb456dcc3a0589..bb08c217bfc865 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -184,7 +184,7 @@ jobs: test_e2e_ios_rntester: if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }} - runs-on: macos-13 + runs-on: macos-13-large needs: [build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos] env: @@ -222,7 +222,7 @@ jobs: test_e2e_ios_templateapp: if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }} - runs-on: macos-13 + runs-on: macos-13-large needs: build_npm_package env: HERMES_WS_DIR: /tmp/hermes diff --git a/CHANGELOG.md b/CHANGELOG.md index 11c60a82e9b0ef..f3f0df51348086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,57 @@ # Changelog +## v0.76.0-rc.4 + +### Breaking + +#### Android specific + +- Update ReactNativeFlipper deprecation to ERROR ([531657b394](https://github.com/facebook/react-native/commit/531657b394aa0fac70f26d9facdb32d23a1b05d6) by [@cortinico](https://github.com/cortinico)) + +#### iOS specific + +- Add ability to control bundle loading on the new architecture similar to `loadSourceForBridge`. Removed some properties from the `RCTRootViewFactory`. ([7487a2c277](https://github.com/facebook/react-native/commit/7487a2c27713a0129ef2db70efc2fa543a8c185e) by [@alanjhughes](https://github.com/alanjhughes)) + +### Added + +- Expose `MetroConfig` type directly from `react-native/metro-config`. ([cc6d1eb844](https://github.com/facebook/react-native/commit/cc6d1eb8449c25fe3c76aaf4da5cd38b3ac0783d) by [@tjzel](https://github.com/tjzel)) + +#### iOS specific + +- Improve RCTAppDelegate usage for brownfield, add `automaticallyLoadReactNativeWindow` flag ([391680fe84](https://github.com/facebook/react-native/commit/391680fe844aad887e497912378c699aed13464b) by [@okwasniewski](https://github.com/okwasniewski)) + +### Changed + +- Update debugger-frontend from e8c7943...ce5d32a ([7a601f428e](https://github.com/facebook/react-native/commit/7a601f428e171827ecb79cb0829d018835229d92) by [@huntie](https://github.com/huntie)) +- Use Metro terminal reporter for dev-middleware logs ([2f04dfe795](https://github.com/facebook/react-native/commit/2f04dfe7951e5f4c63f94c74d7ad10cd53969da0) by [@huntie](https://github.com/huntie)) +- Simplify key handling in start command ([5a0df6d0bf](https://github.com/facebook/react-native/commit/5a0df6d0bf5e84d3f7d2a6f000e617e00b8bef02) by [@huntie](https://github.com/huntie)) +- Add CLI selection of multiple debug targets ([6a24df7eaa](https://github.com/facebook/react-native/commit/6a24df7eaab2420e91b9a4fc9202c175c0802441) by [@huntie](https://github.com/huntie)) +- Update Metro to 0.81.0-alpha.2 ([2a344a9580](https://github.com/facebook/react-native/commit/2a344a95806c62bbc708b5466760b009eae0c579) by [@huntie](https://github.com/huntie)) + +#### iOS specific + +- Rename `RCTUIGraphicsImageRenderer` to `RCTMakeUIGraphicsImageRenderer` ([6a09fc09af](https://github.com/facebook/react-native/commit/6a09fc09af8eedbf409ab870d5714e44892b2a16) by [@Saadnajmi](https://github.com/Saadnajmi)) +- Passed correct title and titleColor prop to updateTitle function ([5c7a166dca](https://github.com/facebook/react-native/commit/5c7a166dcaf5400fbc4c12d4c8b451228157c036) by [@shubhamguptadream11](https://github.com/shubhamguptadream11)) + +### Fixed + +- Fix ReactFragment on New Architecture ([52322fbd0e](https://github.com/facebook/react-native/commit/52322fbd0e641726b224b2b9b89d99d93dbc6e33) by [@cortinico](https://github.com/cortinico)) +- Fix logbox reporting React errors as Warnings ([cbb313253f](https://github.com/facebook/react-native/commit/cbb313253fdf96c4fca06a24ed4cbd3f3df9db80) by [@rickhanlonii](https://github.com/rickhanlonii)) +- Respond with status code `200` when successfully launching RNDT ([eeb6122f39](https://github.com/facebook/react-native/commit/eeb6122f390d355191182eaee6cf126468e7b4d4) by [@byCedric](https://github.com/byCedric)) +- Fix parsing of modern Flow syntax when `transformer.hermesParser = false` is configured in Metro config ([1387f521fd](https://github.com/facebook/react-native/commit/1387f521fdd8f187eab7a4a6a05d4d75a96b4f88) by [@huntie](https://github.com/huntie)) +- Restore Metro log forwarding, change notice to signal future removal ([6047f9cc09](https://github.com/facebook/react-native/commit/6047f9cc09a7a86b79084a3ff1b4303c4583d2ce) by [@huntie](https://github.com/huntie)) + +#### Android specific + +- Add missing Android implementation for DevMenu Module ([1bdae07d89](https://github.com/facebook/react-native/commit/1bdae07d89fdda486d3f9dbcc4aa5cbb026fc8b6) by [@cortinico](https://github.com/cortinico)) +- Fix measuring text with incorrect hyphenationFrequency ([fc8224036b](https://github.com/facebook/react-native/commit/fc8224036b4b5880c881f471e432ee5b47113b0b) by [@NickGerleman](https://github.com/NickGerleman)) +- Properly set `REACTNATIVE_MERGED_SO` for autolinked libraries ([c005609b01](https://github.com/facebook/react-native/commit/c005609b0155223f3ae778c5cd96bc78c4dfe399) by [@cortinico](https://github.com/cortinico)) + +#### iOS specific + +- Properly retain/release backgroundColor in RCTBorderDrawing ([47748c7935](https://github.com/facebook/react-native/commit/47748c7935540014abed03b4d2ff809b471c4fe3) by [@Saadnajmi](https://github.com/Saadnajmi)) +- Fix applying of tintColor and progressViewOffset props for RefreshControl component with New Architecture enabled ([19d468fed9](https://github.com/facebook/react-native/commit/19d468fed966f2adb973ad3f19a97a5ec0372e3a) by [@TuTejsy](https://github.com/TuTejsy)) + ## v0.75.4 ### Fixed @@ -17,6 +69,39 @@ - Use CONFIG_CMD if set ([a4ec49cbe6](https://github.com/facebook/react-native/commit/a4ec49cbe6d0157417276731ba1608e482f3f10e) by [@krystofwoldrich](https://github.com/krystofwoldrich)) - App crash happening when navigate to a new app screen with a displaying modal ([8ec672204d](https://github.com/facebook/react-native/commit/8ec672204d5dee2b967cac08adf03c082e36ad79) by [@zhouzh1](https://github.com/zhouzh1)) +## v0.76.0-rc.3 + +### Added + +- Add CLI selection of multiple debug targets ([6a24df7eaa](https://github.com/facebook/react-native/commit/6a24df7eaab2420e91b9a4fc9202c175c0802441) by [@huntie](https://github.com/huntie)) + +#### iOS specific + +- Improve RCTAppDelegate usage for brownfield, add `automaticallyLoadReactNativeWindow` flag ([391680fe84](https://github.com/facebook/react-native/commit/391680fe844aad887e497912378c699aed13464b) by [@okwasniewski](https://github.com/okwasniewski)) + +### Changed + +- Use Metro terminal reporter for dev-middleware logs ([2f04dfe795](https://github.com/facebook/react-native/commit/2f04dfe7951e5f4c63f94c74d7ad10cd53969da0) by [@huntie](https://github.com/huntie)) +- Simplify key handling in start command ([5a0df6d0bf](https://github.com/facebook/react-native/commit/5a0df6d0bf5e84d3f7d2a6f000e617e00b8bef02) by [@huntie](https://github.com/huntie)) +- Update Metro to 0.81.0-alpha.2 ([2a344a9580](https://github.com/facebook/react-native/commit/2a344a95806c62bbc708b5466760b009eae0c579) by [@huntie](https://github.com/huntie)) + +### Fixed + +- Fix parsing of modern Flow syntax when `transformer.hermesParser = false` is configured in Metro config ([1387f521fd](https://github.com/facebook/react-native/commit/1387f521fdd8f187eab7a4a6a05d4d75a96b4f88) by [@huntie](https://github.com/huntie)) +- Fix ReactFragment on New Architecture ([52322fbd0e](https://github.com/facebook/react-native/commit/52322fbd0e641726b224b2b9b89d99d93dbc6e33) by [@cortinico](https://github.com/cortinico)) + +#### Android specific + +- Add missing Android implementation for DevMenu Module ([1bdae07d89](https://github.com/facebook/react-native/commit/1bdae07d89fdda486d3f9dbcc4aa5cbb026fc8b6) by [@cortinico](https://github.com/cortinico)) +- Fix measuring text with incorrect hyphenationFrequency ([fc8224036b](https://github.com/facebook/react-native/commit/fc8224036b4b5880c881f471e432ee5b47113b0b) by [@NickGerleman](https://github.com/NickGerleman)) +- Properly set `REACTNATIVE_MERGED_SO` for autolinked libraries ([c005609b01](https://github.com/facebook/react-native/commit/c005609b0155223f3ae778c5cd96bc78c4dfe399) by [@cortinico](https://github.com/cortinico)) + +#### iOS specific + +- Fix applying of tintColor and progressViewOffset props for RefreshControl component with New Architecture enabled ([19d468fed9](https://github.com/facebook/react-native/commit/19d468fed966f2adb973ad3f19a97a5ec0372e3a) by [@TuTejsy](https://github.com/TuTejsy)) +- Convert `NSNull` to `nil` before checking `type` in `readAsDataURL` ([99ab845a5c](https://github.com/facebook/react-native/commit/99ab845a5cf0fe3463ff39b03373b95d4f5c0fac) by [@haileyok](https://github.com/haileyok)) +- Fix `` with floating keyboard on iPadOS ([3c54e1ee45](https://github.com/facebook/react-native/commit/3c54e1ee4522b26698bb3f99262a2a621b26fb64) by [@renchap](https://github.com/renchap)) + ## v0.76.0-rc.2 ### Added diff --git a/flow-typed/npm/yargs_v17.x.x.js b/flow-typed/npm/yargs_v17.x.x.js index ea049a3ba5b355..9f614e41f16f67 100644 --- a/flow-typed/npm/yargs_v17.x.x.js +++ b/flow-typed/npm/yargs_v17.x.x.js @@ -323,6 +323,7 @@ declare module "yargs" { updateStrings(obj: { [key: string]: string, ... }): this; usage(message: string, opts?: { [key: string]: Options, ... }): this; + usage(message: string, desc?: string, builder: CommonModuleObject["builder"], handler: CommonModuleObject["handler"]): this; version(): this; version(version: string | false): this; diff --git a/package.json b/package.json index 70b8160cc3797f..88fc77a30d4221 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "eslint-plugin-redundant-undefined": "^0.4.0", "eslint-plugin-relay": "^1.8.3", "flow-api-translator": "0.24.0", - "flow-bin": "^0.247.1", + "flow-bin": "^0.248.1", "glob": "^7.1.1", "hermes-eslint": "0.24.0", "hermes-transform": "0.24.0", diff --git a/packages/gradle-plugin/gradle/libs.versions.toml b/packages/gradle-plugin/gradle/libs.versions.toml index 715209b8beeb0a..25e4ee62a53962 100644 --- a/packages/gradle-plugin/gradle/libs.versions.toml +++ b/packages/gradle-plugin/gradle/libs.versions.toml @@ -4,7 +4,7 @@ gson = "2.8.9" guava = "31.0.1-jre" javapoet = "1.13.0" junit = "4.13.2" -kotlin = "1.9.24" +kotlin = "2.0.21" assertj = "3.25.1" [libraries] diff --git a/packages/gradle-plugin/react-native-gradle-plugin/build.gradle.kts b/packages/gradle-plugin/react-native-gradle-plugin/build.gradle.kts index 6669e9e3b6d4e3..022f60c29f42f1 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/build.gradle.kts +++ b/packages/gradle-plugin/react-native-gradle-plugin/build.gradle.kts @@ -6,6 +6,8 @@ */ import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -60,10 +62,10 @@ java { targetCompatibility = JavaVersion.VERSION_11 } kotlin { jvmToolchain(17) } tasks.withType().configureEach { - kotlinOptions { - apiVersion = "1.6" + compilerOptions { + apiVersion.set(KotlinVersion.KOTLIN_1_7) // See comment above on JDK 11 support - jvmTarget = "11" + jvmTarget.set(JvmTarget.JVM_11) allWarningsAsErrors = project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false } diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt index 86d40a0ebf03da..0191326aed4d53 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt @@ -43,6 +43,9 @@ internal object NdkConfiguratorUtils { if (cmakeArgs.none { it.startsWith("-DPROJECT_BUILD_DIR") }) { cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.layout.buildDirectory.get().asFile}") } + if (cmakeArgs.none { it.startsWith("-DPROJECT_ROOT_DIR") }) { + cmakeArgs.add("-DPROJECT_ROOT_DIR=${project.rootProject.layout.projectDirectory.asFile}") + } if (cmakeArgs.none { it.startsWith("-DREACT_ANDROID_DIR") }) { cmakeArgs.add( "-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}") diff --git a/packages/gradle-plugin/settings-plugin/build.gradle.kts b/packages/gradle-plugin/settings-plugin/build.gradle.kts index 40e094ee48de0a..8f5ae862d38521 100644 --- a/packages/gradle-plugin/settings-plugin/build.gradle.kts +++ b/packages/gradle-plugin/settings-plugin/build.gradle.kts @@ -6,6 +6,8 @@ */ import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -50,10 +52,10 @@ java { targetCompatibility = JavaVersion.VERSION_11 } kotlin { jvmToolchain(17) } tasks.withType().configureEach { - kotlinOptions { - apiVersion = "1.6" + compilerOptions { + apiVersion.set(KotlinVersion.KOTLIN_1_7) // See comment above on JDK 11 support - jvmTarget = "11" + jvmTarget.set(JvmTarget.JVM_11) allWarningsAsErrors = project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false } diff --git a/packages/gradle-plugin/shared-testutil/build.gradle.kts b/packages/gradle-plugin/shared-testutil/build.gradle.kts index 30a1124ebb68b7..b3c0204b0f5082 100644 --- a/packages/gradle-plugin/shared-testutil/build.gradle.kts +++ b/packages/gradle-plugin/shared-testutil/build.gradle.kts @@ -6,6 +6,8 @@ */ import org.gradle.api.tasks.testing.logging.TestExceptionFormat +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) } @@ -21,9 +23,10 @@ java { targetCompatibility = JavaVersion.VERSION_11 } kotlin { jvmToolchain(17) } tasks.withType().configureEach { - kotlinOptions { - apiVersion = "1.6" - jvmTarget = "11" + compilerOptions { + apiVersion.set(KotlinVersion.KOTLIN_1_7) + // See comment above on JDK 11 support + jvmTarget.set(JvmTarget.JVM_11) allWarningsAsErrors = project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false } diff --git a/packages/gradle-plugin/shared/build.gradle.kts b/packages/gradle-plugin/shared/build.gradle.kts index f7f55f5f2f134b..b705a7c6997deb 100644 --- a/packages/gradle-plugin/shared/build.gradle.kts +++ b/packages/gradle-plugin/shared/build.gradle.kts @@ -6,6 +6,8 @@ */ import org.gradle.api.tasks.testing.logging.TestExceptionFormat +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) } @@ -27,9 +29,10 @@ java { targetCompatibility = JavaVersion.VERSION_11 } kotlin { jvmToolchain(17) } tasks.withType().configureEach { - kotlinOptions { - apiVersion = "1.6" - jvmTarget = "11" + compilerOptions { + apiVersion.set(KotlinVersion.KOTLIN_1_7) + // See comment above on JDK 11 support + jvmTarget.set(JvmTarget.JVM_11) allWarningsAsErrors = project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false } diff --git a/packages/helloworld/android/build.gradle b/packages/helloworld/android/build.gradle index 0dd0fccb82db6c..b51b5dffbbf05f 100644 --- a/packages/helloworld/android/build.gradle +++ b/packages/helloworld/android/build.gradle @@ -12,7 +12,7 @@ buildscript { compileSdkVersion = 35 targetSdkVersion = 34 ndkVersion = "26.1.10909125" - kotlinVersion = "1.9.24" + kotlinVersion = "2.0.21" } repositories { google() diff --git a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsJavaDelegate-test.js.snap b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsJavaDelegate-test.js.snap index ebc5a9b14945e3..9364845137ccf0 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsJavaDelegate-test.js.snap +++ b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GeneratePropsJavaDelegate-test.js.snap @@ -16,10 +16,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ArrayPropsNativeComponentViewManagerDelegate & ArrayPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class ArrayPropsNativeComponentViewManagerDelegate & ArrayPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public ArrayPropsNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -86,10 +87,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class BooleanPropNativeComponentViewManagerDelegate & BooleanPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class BooleanPropNativeComponentViewManagerDelegate & BooleanPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public BooleanPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -127,10 +129,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ColorPropNativeComponentViewManagerDelegate & ColorPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class ColorPropNativeComponentViewManagerDelegate & ColorPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public ColorPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -165,10 +168,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DimensionPropConverter; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class DimensionPropNativeComponentViewManagerDelegate & DimensionPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class DimensionPropNativeComponentViewManagerDelegate & DimensionPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public DimensionPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -202,10 +206,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EdgeInsetsPropNativeComponentViewManagerDelegate & EdgeInsetsPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class EdgeInsetsPropNativeComponentViewManagerDelegate & EdgeInsetsPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public EdgeInsetsPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -233,10 +238,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EnumPropNativeComponentViewManagerDelegate & EnumPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class EnumPropNativeComponentViewManagerDelegate & EnumPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public EnumPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -273,10 +279,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EventNestedObjectPropsNativeComponentViewManagerDelegate & EventNestedObjectPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class EventNestedObjectPropsNativeComponentViewManagerDelegate & EventNestedObjectPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public EventNestedObjectPropsNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -310,10 +317,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EventPropsNativeComponentViewManagerDelegate & EventPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class EventPropsNativeComponentViewManagerDelegate & EventPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public EventPropsNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -347,10 +355,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class FloatPropsNativeComponentViewManagerDelegate & FloatPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class FloatPropsNativeComponentViewManagerDelegate & FloatPropsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public FloatPropsNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -403,10 +412,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ImagePropNativeComponentViewManagerDelegate & ImagePropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class ImagePropNativeComponentViewManagerDelegate & ImagePropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public ImagePropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -440,10 +450,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class IntegerPropNativeComponentViewManagerDelegate & IntegerPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class IntegerPropNativeComponentViewManagerDelegate & IntegerPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public IntegerPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -483,10 +494,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class InterfaceOnlyNativeComponentViewManagerDelegate & InterfaceOnlyNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class InterfaceOnlyNativeComponentViewManagerDelegate & InterfaceOnlyNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public InterfaceOnlyNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -521,10 +533,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DynamicFromObject; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MixedPropNativeComponentViewManagerDelegate & MixedPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class MixedPropNativeComponentViewManagerDelegate & MixedPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public MixedPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -560,10 +573,11 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiNativePropNativeComponentViewManagerDelegate & MultiNativePropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class MultiNativePropNativeComponentViewManagerDelegate & MultiNativePropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public MultiNativePropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -606,10 +620,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class NoPropsNoEventsNativeComponentViewManagerDelegate & NoPropsNoEventsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class NoPropsNoEventsNativeComponentViewManagerDelegate & NoPropsNoEventsNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public NoPropsNoEventsNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -638,10 +653,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ObjectPropsNativeComponentManagerDelegate & ObjectPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ObjectPropsNativeComponentManagerDelegate & ObjectPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ObjectPropsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -682,10 +698,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class PointPropNativeComponentViewManagerDelegate & PointPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class PointPropNativeComponentViewManagerDelegate & PointPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public PointPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } @@ -719,10 +736,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class StringPropNativeComponentViewManagerDelegate & StringPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { +public class StringPropNativeComponentViewManagerDelegate & StringPropNativeComponentViewManagerInterface> extends BaseViewManagerDelegate { public StringPropNativeComponentViewManagerDelegate(U viewManager) { super(viewManager); } diff --git a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js index a758d69ecc3fb8..b49330a46bebe2 100644 --- a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js +++ b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js @@ -55,7 +55,7 @@ package ${packageName}; ${imports} -public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { +public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { public ${className}(U viewManager) { super(viewManager); } @@ -272,7 +272,8 @@ function getDelegateImports(component: ComponentShape) { } imports.add('import androidx.annotation.Nullable;'); imports.add('import com.facebook.react.uimanager.BaseViewManagerDelegate;'); - imports.add('import com.facebook.react.uimanager.BaseViewManagerInterface;'); + imports.add('import com.facebook.react.uimanager.BaseViewManager;'); + imports.add('import com.facebook.react.uimanager.LayoutShadowNode;'); return imports; } diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap index 681a198f252b8c..597fd594783e10 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap @@ -16,10 +16,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ArrayPropsNativeComponentManagerDelegate & ArrayPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ArrayPropsNativeComponentManagerDelegate & ArrayPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ArrayPropsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -87,10 +88,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ArrayPropsNativeComponentManagerDelegate & ArrayPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ArrayPropsNativeComponentManagerDelegate & ArrayPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ArrayPropsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -124,10 +126,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class BooleanPropNativeComponentManagerDelegate & BooleanPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class BooleanPropNativeComponentManagerDelegate & BooleanPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public BooleanPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -162,10 +165,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ColorPropNativeComponentManagerDelegate & ColorPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ColorPropNativeComponentManagerDelegate & ColorPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ColorPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -200,10 +204,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class CommandNativeComponentManagerDelegate & CommandNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class CommandNativeComponentManagerDelegate & CommandNativeComponentManagerInterface> extends BaseViewManagerDelegate { public CommandNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -244,10 +249,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class CommandNativeComponentManagerDelegate & CommandNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class CommandNativeComponentManagerDelegate & CommandNativeComponentManagerInterface> extends BaseViewManagerDelegate { public CommandNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -297,10 +303,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DimensionPropConverter; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class DimensionPropNativeComponentManagerDelegate & DimensionPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class DimensionPropNativeComponentManagerDelegate & DimensionPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public DimensionPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -334,10 +341,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class DoublePropNativeComponentManagerDelegate & DoublePropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class DoublePropNativeComponentManagerDelegate & DoublePropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public DoublePropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -386,10 +394,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EventsNestedObjectNativeComponentManagerDelegate & EventsNestedObjectNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class EventsNestedObjectNativeComponentManagerDelegate & EventsNestedObjectNativeComponentManagerInterface> extends BaseViewManagerDelegate { public EventsNestedObjectNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -423,10 +432,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class EventsNativeComponentManagerDelegate & EventsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class EventsNativeComponentManagerDelegate & EventsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public EventsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -460,10 +470,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class InterfaceOnlyComponentManagerDelegate & InterfaceOnlyComponentManagerInterface> extends BaseViewManagerDelegate { +public class InterfaceOnlyComponentManagerDelegate & InterfaceOnlyComponentManagerInterface> extends BaseViewManagerDelegate { public InterfaceOnlyComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -495,10 +506,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ExcludedIosComponentManagerDelegate & ExcludedIosComponentManagerInterface> extends BaseViewManagerDelegate { +public class ExcludedIosComponentManagerDelegate & ExcludedIosComponentManagerInterface> extends BaseViewManagerDelegate { public ExcludedIosComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -521,10 +533,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiFileIncludedNativeComponentManagerDelegate & MultiFileIncludedNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MultiFileIncludedNativeComponentManagerDelegate & MultiFileIncludedNativeComponentManagerInterface> extends BaseViewManagerDelegate { public MultiFileIncludedNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -558,10 +571,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class FloatPropNativeComponentManagerDelegate & FloatPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class FloatPropNativeComponentManagerDelegate & FloatPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public FloatPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -611,10 +625,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ImagePropNativeComponentManagerDelegate & ImagePropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ImagePropNativeComponentManagerDelegate & ImagePropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ImagePropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -649,10 +664,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class InsetsPropNativeComponentManagerDelegate & InsetsPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class InsetsPropNativeComponentManagerDelegate & InsetsPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public InsetsPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -686,10 +702,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class Int32EnumPropsNativeComponentManagerDelegate & Int32EnumPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class Int32EnumPropsNativeComponentManagerDelegate & Int32EnumPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public Int32EnumPropsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -723,10 +740,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class IntegerPropNativeComponentManagerDelegate & IntegerPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class IntegerPropNativeComponentManagerDelegate & IntegerPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public IntegerPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -766,10 +784,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class InterfaceOnlyComponentManagerDelegate & InterfaceOnlyComponentManagerInterface> extends BaseViewManagerDelegate { +public class InterfaceOnlyComponentManagerDelegate & InterfaceOnlyComponentManagerInterface> extends BaseViewManagerDelegate { public InterfaceOnlyComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -804,10 +823,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DynamicFromObject; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MixedPropNativeComponentManagerDelegate & MixedPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MixedPropNativeComponentManagerDelegate & MixedPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public MixedPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -843,10 +863,11 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ImageColorPropNativeComponentManagerDelegate & ImageColorPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class ImageColorPropNativeComponentManagerDelegate & ImageColorPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public ImageColorPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -889,10 +910,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class NoPropsNoEventsComponentManagerDelegate & NoPropsNoEventsComponentManagerInterface> extends BaseViewManagerDelegate { +public class NoPropsNoEventsComponentManagerDelegate & NoPropsNoEventsComponentManagerInterface> extends BaseViewManagerDelegate { public NoPropsNoEventsComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -921,10 +943,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class ObjectPropsManagerDelegate & ObjectPropsManagerInterface> extends BaseViewManagerDelegate { +public class ObjectPropsManagerDelegate & ObjectPropsManagerInterface> extends BaseViewManagerDelegate { public ObjectPropsManagerDelegate(U viewManager) { super(viewManager); } @@ -959,10 +982,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class PointPropNativeComponentManagerDelegate & PointPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class PointPropNativeComponentManagerDelegate & PointPropNativeComponentManagerInterface> extends BaseViewManagerDelegate { public PointPropNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -996,10 +1020,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class StringEnumPropsNativeComponentManagerDelegate & StringEnumPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class StringEnumPropsNativeComponentManagerDelegate & StringEnumPropsNativeComponentManagerInterface> extends BaseViewManagerDelegate { public StringEnumPropsNativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -1033,10 +1058,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class StringPropComponentManagerDelegate & StringPropComponentManagerInterface> extends BaseViewManagerDelegate { +public class StringPropComponentManagerDelegate & StringPropComponentManagerInterface> extends BaseViewManagerDelegate { public StringPropComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -1073,10 +1099,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiFile1NativeComponentManagerDelegate & MultiFile1NativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MultiFile1NativeComponentManagerDelegate & MultiFile1NativeComponentManagerInterface> extends BaseViewManagerDelegate { public MultiFile1NativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -1105,10 +1132,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiFile2NativeComponentManagerDelegate & MultiFile2NativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MultiFile2NativeComponentManagerDelegate & MultiFile2NativeComponentManagerInterface> extends BaseViewManagerDelegate { public MultiFile2NativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -1142,10 +1170,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiComponent1NativeComponentManagerDelegate & MultiComponent1NativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MultiComponent1NativeComponentManagerDelegate & MultiComponent1NativeComponentManagerInterface> extends BaseViewManagerDelegate { public MultiComponent1NativeComponentManagerDelegate(U viewManager) { super(viewManager); } @@ -1174,10 +1203,11 @@ package com.facebook.react.viewmanagers; import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.LayoutShadowNode; -public class MultiComponent2NativeComponentManagerDelegate & MultiComponent2NativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class MultiComponent2NativeComponentManagerDelegate & MultiComponent2NativeComponentManagerInterface> extends BaseViewManagerDelegate { public MultiComponent2NativeComponentManagerDelegate(U viewManager) { super(viewManager); } diff --git a/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java index 736b778e5948f5..f79c0c1a6ec075 100644 --- a/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java +++ b/packages/react-native-popup-menu-android/android/src/main/java/com/facebook/react/viewmanagers/AndroidPopupMenuManagerDelegate.java @@ -13,9 +13,10 @@ import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.BaseViewManager; +import com.facebook.react.uimanager.LayoutShadowNode; -public class AndroidPopupMenuManagerDelegate & AndroidPopupMenuManagerInterface> extends BaseViewManagerDelegate { +public class AndroidPopupMenuManagerDelegate & AndroidPopupMenuManagerInterface> extends BaseViewManagerDelegate { public AndroidPopupMenuManagerDelegate(U viewManager) { super(viewManager); } diff --git a/packages/react-native-test-library/android/src/main/java/com/facebook/react/osslibraryexample/SampleNativeComponentViewManager.kt b/packages/react-native-test-library/android/src/main/java/com/facebook/react/osslibraryexample/SampleNativeComponentViewManager.kt index e76fedbb338c7b..3ab92ac3bd8cb1 100644 --- a/packages/react-native-test-library/android/src/main/java/com/facebook/react/osslibraryexample/SampleNativeComponentViewManager.kt +++ b/packages/react-native-test-library/android/src/main/java/com/facebook/react/osslibraryexample/SampleNativeComponentViewManager.kt @@ -76,6 +76,7 @@ internal class SampleNativeComponentViewManager : } } + @Deprecated("Deprecated in Java") @SuppressLint("BadMethodUse-android.view.View.setBackgroundColor") @Suppress("DEPRECATION") // We intentionally want to test against the legacy API here. override fun receiveCommand(view: SampleNativeView, commandId: Int, args: ReadableArray?) { diff --git a/packages/react-native-test-library/android/src/main/java/com/facebook/react/viewmanagers/SampleNativeComponentManagerDelegate.java b/packages/react-native-test-library/android/src/main/java/com/facebook/react/viewmanagers/SampleNativeComponentManagerDelegate.java index 4883fd3ed9d644..bb5b9b5273607f 100644 --- a/packages/react-native-test-library/android/src/main/java/com/facebook/react/viewmanagers/SampleNativeComponentManagerDelegate.java +++ b/packages/react-native-test-library/android/src/main/java/com/facebook/react/viewmanagers/SampleNativeComponentManagerDelegate.java @@ -13,9 +13,10 @@ import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.BaseViewManagerDelegate; -import com.facebook.react.uimanager.BaseViewManagerInterface; +import com.facebook.react.uimanager.BaseViewManager; +import com.facebook.react.uimanager.LayoutShadowNode; -public class SampleNativeComponentManagerDelegate & SampleNativeComponentManagerInterface> extends BaseViewManagerDelegate { +public class SampleNativeComponentManagerDelegate & SampleNativeComponentManagerInterface> extends BaseViewManagerDelegate { public SampleNativeComponentManagerDelegate(U viewManager) { super(viewManager); } diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 28e339d0e00c51..2be6b0d8f73838 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -181,8 +181,12 @@ - (void)hostDidStart:(RCTHost *)host - (void)host:(RCTHost *)host didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message + originalMessage:(NSString *_Nullable)originalMessage + name:(NSString *_Nullable)name + componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId isFatal:(BOOL)isFatal + extraData:(NSDictionary *)extraData { } diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h index 27e55f56645ac1..d3ce6212fac15f 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h @@ -59,13 +59,17 @@ typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *modu - (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock newArchEnabled:(BOOL)newArchEnabled turboModuleEnabled:(BOOL)turboModuleEnabled - bridgelessEnabled:(BOOL)bridgelessEnabled NS_DESIGNATED_INITIALIZER; + bridgelessEnabled:(BOOL)bridgelessEnabled NS_DESIGNATED_INITIALIZER __deprecated; - (instancetype)initWithBundleURL:(NSURL *)bundleURL newArchEnabled:(BOOL)newArchEnabled turboModuleEnabled:(BOOL)turboModuleEnabled bridgelessEnabled:(BOOL)bridgelessEnabled __deprecated; +- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock newArchEnabled:(BOOL)newArchEnabled; + +- (instancetype)initWithBundleURL:(NSURL *)bundleURL newArchEnabled:(BOOL)newArchEnabled; + /** * Block that allows to override logic of creating root view instance. * It creates a `UIView` starting from a bridge, a module name and a set of initial properties. diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index 80e9fabd1879c9..c6818da3a36627 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -53,6 +53,22 @@ @implementation RCTRootViewFactoryConfiguration +- (instancetype)initWithBundleURL:(NSURL *)bundleURL newArchEnabled:(BOOL)newArchEnabled +{ + return [self initWithBundleURL:bundleURL + newArchEnabled:newArchEnabled + turboModuleEnabled:newArchEnabled + bridgelessEnabled:newArchEnabled]; +} + +- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock newArchEnabled:(BOOL)newArchEnabled +{ + return [self initWithBundleURLBlock:bundleURLBlock + newArchEnabled:newArchEnabled + turboModuleEnabled:newArchEnabled + bridgelessEnabled:newArchEnabled]; +} + - (instancetype)initWithBundleURL:(NSURL *)bundleURL newArchEnabled:(BOOL)newArchEnabled turboModuleEnabled:(BOOL)turboModuleEnabled diff --git a/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js index 7f724524add5ce..8cdceda9e9a8af 100644 --- a/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -153,10 +153,10 @@ const ActivityIndicator = ( ``` */ -const ActivityIndicatorWithRef: React.AbstractComponent< - Props, - HostComponent, -> = React.forwardRef(ActivityIndicator); +const ActivityIndicatorWithRef: component( + ref: React.RefSetter>, + ...props: Props +) = React.forwardRef(ActivityIndicator); ActivityIndicatorWithRef.displayName = 'ActivityIndicator'; const styles = StyleSheet.create({ diff --git a/packages/react-native/Libraries/Components/Button.js b/packages/react-native/Libraries/Components/Button.js index 6de743d1bb9fe8..1b5c0b6027894d 100644 --- a/packages/react-native/Libraries/Components/Button.js +++ b/packages/react-native/Libraries/Components/Button.js @@ -381,6 +381,9 @@ const Button: component( disabled={disabled} onPress={onPress} touchSoundDisabled={touchSoundDisabled} + // $FlowFixMe[incompatible-exact] + // $FlowFixMe[prop-missing] + // $FlowFixMe[incompatible-type-arg] ref={ref}> diff --git a/packages/react-native/Libraries/Components/Pressable/Pressable.js b/packages/react-native/Libraries/Components/Pressable/Pressable.js index bd0d6a43a3a666..68b2574156b127 100644 --- a/packages/react-native/Libraries/Components/Pressable/Pressable.js +++ b/packages/react-native/Libraries/Components/Pressable/Pressable.js @@ -363,7 +363,7 @@ function usePressState(forcePressed: boolean): [boolean, (boolean) => void] { const MemoedPressable = React.memo(React.forwardRef(Pressable)); MemoedPressable.displayName = 'Pressable'; -export default (MemoedPressable: React.AbstractComponent< - Props, - React.ElementRef, ->); +export default (MemoedPressable: component( + ref: React.RefSetter>, + ...props: Props +)); diff --git a/packages/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js b/packages/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js index dd18cc4d2dce8a..ca5c4fb6a05341 100644 --- a/packages/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js +++ b/packages/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js @@ -78,7 +78,12 @@ export type ProgressBarAndroidProps = $ReadOnly<{| * }, * ``` */ -const ProgressBarAndroid = ( +const ProgressBarAndroidWithForwardedRef: component( + ref: React.RefSetter< + React.ElementRef, + >, + ...props: ProgressBarAndroidProps +) = React.forwardRef(function ProgressBarAndroid( { // $FlowFixMe[incompatible-type] styleAttr = 'Normal', @@ -89,7 +94,7 @@ const ProgressBarAndroid = ( forwardedRef: ?React.RefSetter< React.ElementRef, >, -) => { +) { return ( ); -}; - -const ProgressBarAndroidToExport = React.forwardRef(ProgressBarAndroid); +}); module.exports = /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an * error found when Flow v0.89 was deployed. To see the error, delete this * comment and run Flow. */ - (ProgressBarAndroidToExport: typeof ProgressBarAndroidNativeComponent); + (ProgressBarAndroidWithForwardedRef: typeof ProgressBarAndroidNativeComponent); diff --git a/packages/react-native/Libraries/Components/SafeAreaView/SafeAreaView.js b/packages/react-native/Libraries/Components/SafeAreaView/SafeAreaView.js index a154c827511f34..e3da1d7798c42d 100644 --- a/packages/react-native/Libraries/Components/SafeAreaView/SafeAreaView.js +++ b/packages/react-native/Libraries/Components/SafeAreaView/SafeAreaView.js @@ -23,10 +23,10 @@ import * as React from 'react'; * limitation of the screen, such as rounded corners or camera notches (aka * sensor housing area on iPhone X). */ -const exported: React.AbstractComponent< - ViewProps, - React.ElementRef, -> = Platform.select({ +const exported: component( + ref: React.RefSetter>, + ...props: ViewProps +) = Platform.select({ ios: require('./RCTSafeAreaViewNativeComponent').default, default: View, }); diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js index 8d2bb60fde6135..39740a8ae593ad 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js @@ -1935,7 +1935,10 @@ function createRefForwarder( // NOTE: This wrapper component is necessary because `ScrollView` is a class // component and we need to map `ref` to a differently named prop. This can be // removed when `ScrollView` is a functional component. -const Wrapper = React.forwardRef(function Wrapper( +const Wrapper: component( + ref: React.RefSetter, + ...props: Props +) = React.forwardRef(function Wrapper( props: Props, ref: ?React.RefSetter, ): React.Node { @@ -1949,8 +1952,5 @@ Wrapper.displayName = 'ScrollView'; // $FlowExpectedError[prop-missing] Wrapper.Context = ScrollViewContext; -module.exports = ((Wrapper: $FlowFixMe): React.AbstractComponent< - React.ElementConfig, - PublicScrollViewInstance, -> & +module.exports = ((Wrapper: $FlowFixMe): typeof Wrapper & ScrollViewComponentStatics); diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js index 052f01eb164d0e..8386bec0cce701 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js @@ -38,10 +38,10 @@ type Instance = { ... }; -const ScrollViewStickyHeaderWithForwardedRef: React.AbstractComponent< - Props, - Instance, -> = React.forwardRef(function ScrollViewStickyHeader(props, forwardedRef) { +const ScrollViewStickyHeaderWithForwardedRef: component( + ref: React.RefSetter, + ...props: Props +) = React.forwardRef(function ScrollViewStickyHeader(props, forwardedRef) { const { inverted, scrollViewHeight, diff --git a/packages/react-native/Libraries/Components/Switch/Switch.js b/packages/react-native/Libraries/Components/Switch/Switch.js index eb0f18521e47c8..b1aa2383defc87 100644 --- a/packages/react-native/Libraries/Components/Switch/Switch.js +++ b/packages/react-native/Libraries/Components/Switch/Switch.js @@ -130,12 +130,14 @@ const returnsTrue = () => true; ``` */ -const SwitchWithForwardedRef: React.AbstractComponent< - Props, - React.ElementRef< - typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent, - >, -> = React.forwardRef(function Switch(props, forwardedRef): React.Node { +type SwitchRef = React.ElementRef< + typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent, +>; + +const SwitchWithForwardedRef: component( + ref: React.RefSetter, + ...props: Props +) = React.forwardRef(function Switch(props, forwardedRef): React.Node { const { disabled, ios_backgroundColor, diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 8eeb465cf5f9f0..ad5a1d83e606c3 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -1787,11 +1787,11 @@ const autoCompleteWebToTextContentTypeMap = { username: 'username', }; -const ExportedForwardRef: React.AbstractComponent< - React.ElementConfig, - TextInputInstance, +const ExportedForwardRef: component( + ref: React.RefSetter, + ...props: React.ElementConfig // $FlowFixMe[incompatible-call] -> = React.forwardRef(function TextInput( +) = React.forwardRef(function TextInput( { allowFontScaling = true, rejectResponderTermination = true, diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js b/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js index 44ef0bfd6bf1ba..9a2bdfff23b283 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableBounce.js @@ -215,4 +215,7 @@ class TouchableBounce extends React.Component { module.exports = (React.forwardRef((props, hostRef) => ( -)): React.AbstractComponent<$ReadOnly<$Diff>>); +)): component( + ref: React.RefSetter, + ...props: $ReadOnly<$Diff> +)); diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js index e59b2954e91a7e..e0aad9ca2e5ddb 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js @@ -381,10 +381,10 @@ class TouchableHighlight extends React.Component { } } -const Touchable: React.AbstractComponent< - $ReadOnly<$Diff>, - React.ElementRef, -> = React.forwardRef((props, hostRef) => ( +const Touchable: component( + ref: React.RefSetter>, + ...props: $ReadOnly<$Diff> +) = React.forwardRef((props, hostRef) => ( )); diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js b/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js index f047f91c02012a..92eea6cb3fc7f7 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableOpacity.js @@ -326,10 +326,10 @@ class TouchableOpacity extends React.Component { } } -const Touchable: React.AbstractComponent< - Props, - React.ElementRef, -> = React.forwardRef((props, ref) => ( +const Touchable: component( + ref: React.RefSetter>, + ...props: Props +) = React.forwardRef((props, ref) => ( )); diff --git a/packages/react-native/Libraries/Components/View/View.js b/packages/react-native/Libraries/Components/View/View.js index e51583ca6fe04c..f1522cf7a857f3 100644 --- a/packages/react-native/Libraries/Components/View/View.js +++ b/packages/react-native/Libraries/Components/View/View.js @@ -23,10 +23,10 @@ export type Props = ViewProps; * * @see https://reactnative.dev/docs/view */ -const View: React.AbstractComponent< - ViewProps, - React.ElementRef, -> = React.forwardRef( +const View: component( + ref: React.RefSetter>, + ...props: ViewProps +) = React.forwardRef( ( { accessibilityElementsHidden, diff --git a/packages/react-native/Libraries/Core/ExceptionsManager.js b/packages/react-native/Libraries/Core/ExceptionsManager.js index eac2be19b981f1..e64a3a0fd6e54f 100644 --- a/packages/react-native/Libraries/Core/ExceptionsManager.js +++ b/packages/react-native/Libraries/Core/ExceptionsManager.js @@ -22,10 +22,11 @@ type ExceptionDecorator = ExceptionData => ExceptionData; let userExceptionDecorator: ?ExceptionDecorator; let inUserExceptionDecorator = false; -// This Symbol is used to decorate an ExtendedError with extra data in select usecases. +// This string is used to decorate an ExtendedError with extra data in select usecases. // Note that data passed using this method should be strictly contained, // as data that's not serializable/too large may cause issues with passing the error to the native code. -const decoratedExtraDataKey: symbol = Symbol('decoratedExtraDataKey'); +// TODO(T204185517): We should use a Symbol for this, but jsi through jsc doesn't support it yet. +const decoratedExtraDataKey = 'RN$ErrorExtraDataKey'; /** * Allows the app to add information to the exception report before it is sent diff --git a/packages/react-native/Libraries/Debugging/DebuggingOverlay.js b/packages/react-native/Libraries/Debugging/DebuggingOverlay.js index d4b7ea8d9438cb..3d0c52ae583c10 100644 --- a/packages/react-native/Libraries/Debugging/DebuggingOverlay.js +++ b/packages/react-native/Libraries/Debugging/DebuggingOverlay.js @@ -102,10 +102,9 @@ const styles = StyleSheet.create({ }, }); -const DebuggingOverlayWithForwardedRef: React.AbstractComponent< - {}, - DebuggingOverlayHandle, - React.Node, -> = React.forwardRef(DebuggingOverlay); +const DebuggingOverlayWithForwardedRef: component( + ref: React.RefSetter, + ...props: {} +) = React.forwardRef(DebuggingOverlay); export default DebuggingOverlayWithForwardedRef; diff --git a/packages/react-native/Libraries/Image/ImageProps.js b/packages/react-native/Libraries/Image/ImageProps.js index 658693bb2c67b6..c0c39075872fdf 100644 --- a/packages/react-native/Libraries/Image/ImageProps.js +++ b/packages/react-native/Libraries/Image/ImageProps.js @@ -20,7 +20,7 @@ import type { import type {LayoutEvent, SyntheticEvent} from '../Types/CoreEventTypes'; import typeof Image from './Image'; import type {ImageSource} from './ImageSource'; -import type {Node, Ref} from 'react'; +import type {ElementRef, Node, RefSetter} from 'react'; export type ImageLoadEvent = SyntheticEvent< $ReadOnly<{| @@ -77,7 +77,7 @@ type AndroidImageProps = $ReadOnly<{| resizeMultiplier?: ?number, |}>; -export type ImageProps = {| +export type ImageProps = $ReadOnly<{| ...$Diff>, ...IOSImageProps, ...AndroidImageProps, @@ -266,7 +266,7 @@ export type ImageProps = {| */ srcSet?: ?string, children?: empty, -|}; +|}>; export type ImageBackgroundProps = $ReadOnly<{| ...ImageProps, @@ -291,5 +291,5 @@ export type ImageBackgroundProps = $ReadOnly<{| * * See https://reactnative.dev/docs/imagebackground#imageref */ - imageRef?: Ref, + imageRef?: RefSetter>, |}>; diff --git a/packages/react-native/Libraries/Image/ImageTypes.flow.js b/packages/react-native/Libraries/Image/ImageTypes.flow.js index 7ea0f71a5707ef..eff14cf1d01634 100644 --- a/packages/react-native/Libraries/Image/ImageTypes.flow.js +++ b/packages/react-native/Libraries/Image/ImageTypes.flow.js @@ -56,18 +56,20 @@ type ImageComponentStaticsAndroid = $ReadOnly<{ abortPrefetch(requestId: number): void, }>; -export type AbstractImageAndroid = React.AbstractComponent< - ImagePropsType, - | React.ElementRef - | React.ElementRef, ->; +export type AbstractImageAndroid = component( + ref: React.RefSetter< + | React.ElementRef + | React.ElementRef, + >, + ...props: ImagePropsType +); export type ImageAndroid = AbstractImageAndroid & ImageComponentStaticsAndroid; -export type AbstractImageIOS = React.AbstractComponent< - ImagePropsType, - React.ElementRef, ->; +export type AbstractImageIOS = component( + ref: React.RefSetter>, + ...props: ImagePropsType +); export type ImageIOS = AbstractImageIOS & ImageComponentStaticsIOS; diff --git a/packages/react-native/Libraries/LogBox/LogBox.js b/packages/react-native/Libraries/LogBox/LogBox.js index e9987147da7bc9..82930e7f34d29e 100644 --- a/packages/react-native/Libraries/LogBox/LogBox.js +++ b/packages/react-native/Libraries/LogBox/LogBox.js @@ -52,6 +52,17 @@ if (__DEV__) { isLogBoxInstalled = true; + if (global.RN$registerExceptionListener != null) { + global.RN$registerExceptionListener( + (error: ExtendedExceptionData & {preventDefault: () => mixed}) => { + if (!error.isFatal) { + error.preventDefault(); + addException(error); + } + }, + ); + } + // Trigger lazy initialization of module. require('../NativeModules/specs/NativeLogBox'); @@ -122,13 +133,15 @@ if (__DEV__) { } }, - addException(error: ExtendedExceptionData): void { - if (isLogBoxInstalled) { - LogBoxData.addException(error); - } - }, + addException, }; + function addException(error: ExtendedExceptionData): void { + if (isLogBoxInstalled) { + LogBoxData.addException(error); + } + } + const isRCTLogAdviceWarning = (...args: Array) => { // RCTLogAdvice is a native logging function designed to show users // a message in the console, but not show it to them in Logbox. diff --git a/packages/react-native/Libraries/Text/Text.js b/packages/react-native/Libraries/Text/Text.js index 01fe0d57a20e28..e327b652a287a6 100644 --- a/packages/react-native/Libraries/Text/Text.js +++ b/packages/react-native/Libraries/Text/Text.js @@ -33,231 +33,156 @@ type TextForwardRef = React.ElementRef< * * @see https://reactnative.dev/docs/text */ -const Text: React.AbstractComponent = - React.forwardRef( - ( - { - accessible, - accessibilityLabel, - accessibilityState, - allowFontScaling, - 'aria-busy': ariaBusy, - 'aria-checked': ariaChecked, - 'aria-disabled': ariaDisabled, - 'aria-expanded': ariaExpanded, - 'aria-label': ariaLabel, - 'aria-selected': ariaSelected, - children, - ellipsizeMode, - disabled, - id, - nativeID, - numberOfLines, - onLongPress, - onPress, - onPressIn, - onPressOut, - onResponderGrant, - onResponderMove, - onResponderRelease, - onResponderTerminate, - onResponderTerminationRequest, - onStartShouldSetResponder, - pressRetentionOffset, - selectable, - selectionColor, - suppressHighlighting, - style, - ...restProps - }: TextProps, - forwardedRef, - ) => { - const _accessibilityLabel = ariaLabel ?? accessibilityLabel; - - let _accessibilityState: ?TextProps['accessibilityState'] = - accessibilityState; - if ( - ariaBusy != null || - ariaChecked != null || - ariaDisabled != null || - ariaExpanded != null || - ariaSelected != null - ) { - if (_accessibilityState != null) { - _accessibilityState = { - busy: ariaBusy ?? _accessibilityState.busy, - checked: ariaChecked ?? _accessibilityState.checked, - disabled: ariaDisabled ?? _accessibilityState.disabled, - expanded: ariaExpanded ?? _accessibilityState.expanded, - selected: ariaSelected ?? _accessibilityState.selected, - }; - } else { - _accessibilityState = { - busy: ariaBusy, - checked: ariaChecked, - disabled: ariaDisabled, - expanded: ariaExpanded, - selected: ariaSelected, - }; - } +const Text: component( + ref: React.RefSetter, + ...props: TextProps +) = React.forwardRef( + ( + { + accessible, + accessibilityLabel, + accessibilityState, + allowFontScaling, + 'aria-busy': ariaBusy, + 'aria-checked': ariaChecked, + 'aria-disabled': ariaDisabled, + 'aria-expanded': ariaExpanded, + 'aria-label': ariaLabel, + 'aria-selected': ariaSelected, + children, + ellipsizeMode, + disabled, + id, + nativeID, + numberOfLines, + onLongPress, + onPress, + onPressIn, + onPressOut, + onResponderGrant, + onResponderMove, + onResponderRelease, + onResponderTerminate, + onResponderTerminationRequest, + onStartShouldSetResponder, + pressRetentionOffset, + selectable, + selectionColor, + suppressHighlighting, + style, + ...restProps + }: TextProps, + forwardedRef, + ) => { + const _accessibilityLabel = ariaLabel ?? accessibilityLabel; + + let _accessibilityState: ?TextProps['accessibilityState'] = + accessibilityState; + if ( + ariaBusy != null || + ariaChecked != null || + ariaDisabled != null || + ariaExpanded != null || + ariaSelected != null + ) { + if (_accessibilityState != null) { + _accessibilityState = { + busy: ariaBusy ?? _accessibilityState.busy, + checked: ariaChecked ?? _accessibilityState.checked, + disabled: ariaDisabled ?? _accessibilityState.disabled, + expanded: ariaExpanded ?? _accessibilityState.expanded, + selected: ariaSelected ?? _accessibilityState.selected, + }; + } else { + _accessibilityState = { + busy: ariaBusy, + checked: ariaChecked, + disabled: ariaDisabled, + expanded: ariaExpanded, + selected: ariaSelected, + }; } + } - const _accessibilityStateDisabled = _accessibilityState?.disabled; - const _disabled = disabled ?? _accessibilityStateDisabled; + const _accessibilityStateDisabled = _accessibilityState?.disabled; + const _disabled = disabled ?? _accessibilityStateDisabled; - const isPressable = - (onPress != null || - onLongPress != null || - onStartShouldSetResponder != null) && - _disabled !== true; + const isPressable = + (onPress != null || + onLongPress != null || + onStartShouldSetResponder != null) && + _disabled !== true; - // TODO: Move this processing to the view configuration. - const _selectionColor = - selectionColor == null ? null : processColor(selectionColor); + // TODO: Move this processing to the view configuration. + const _selectionColor = + selectionColor != null ? processColor(selectionColor) : undefined; - let _style = style; - if (__DEV__) { - if (PressabilityDebug.isEnabled() && onPress != null) { - _style = [style, {color: 'magenta'}]; - } + let _style = style; + if (__DEV__) { + if (PressabilityDebug.isEnabled() && onPress != null) { + _style = [style, {color: 'magenta'}]; } + } - let _numberOfLines = numberOfLines; - if (_numberOfLines != null && !(_numberOfLines >= 0)) { - if (__DEV__) { - console.error( - `'numberOfLines' in must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`, - ); - } - _numberOfLines = 0; + let _numberOfLines = numberOfLines; + if (_numberOfLines != null && !(_numberOfLines >= 0)) { + if (__DEV__) { + console.error( + `'numberOfLines' in must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`, + ); } + _numberOfLines = 0; + } - let _selectable = selectable; - - let processedStyle = flattenStyle(_style); - if (processedStyle != null) { - let overrides: ?{...TextStyleInternal} = null; - if (typeof processedStyle.fontWeight === 'number') { - overrides = overrides || ({}: {...TextStyleInternal}); - overrides.fontWeight = - // $FlowFixMe[incompatible-cast] - (processedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']); - } - - if (processedStyle.userSelect != null) { - _selectable = userSelectToSelectableMap[processedStyle.userSelect]; - overrides = overrides || ({}: {...TextStyleInternal}); - overrides.userSelect = undefined; - } - - if (processedStyle.verticalAlign != null) { - overrides = overrides || ({}: {...TextStyleInternal}); - overrides.textAlignVertical = - verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign]; - overrides.verticalAlign = undefined; - } + let _selectable = selectable; - if (overrides != null) { - // $FlowFixMe[incompatible-type] - _style = [_style, overrides]; - } + let processedStyle = flattenStyle(_style); + if (processedStyle != null) { + let overrides: ?{...TextStyleInternal} = null; + if (typeof processedStyle.fontWeight === 'number') { + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.fontWeight = + // $FlowFixMe[incompatible-cast] + (processedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']); } - const _nativeID = id ?? nativeID; - - const hasTextAncestor = useContext(TextAncestor); - if (hasTextAncestor) { - if (isPressable) { - return ( - - ); - } + if (processedStyle.userSelect != null) { + _selectable = userSelectToSelectableMap[processedStyle.userSelect]; + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.userSelect = undefined; + } - return ( - - {children} - - ); + if (processedStyle.verticalAlign != null) { + overrides = overrides || ({}: {...TextStyleInternal}); + overrides.textAlignVertical = + verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign]; + overrides.verticalAlign = undefined; } - // If the disabled prop and accessibilityState.disabled are out of sync but not both in - // falsy states we need to update the accessibilityState object to use the disabled prop. - if ( - _disabled !== _accessibilityStateDisabled && - ((_disabled != null && _disabled !== false) || - (_accessibilityStateDisabled != null && - _accessibilityStateDisabled !== false)) - ) { - _accessibilityState = {..._accessibilityState, disabled: _disabled}; + if (overrides != null) { + // $FlowFixMe[incompatible-type] + _style = [_style, overrides]; } + } - const _accessible = Platform.select({ - ios: accessible !== false, - android: - accessible == null - ? onPress != null || onLongPress != null - : accessible, - default: accessible, - }); + const _nativeID = id ?? nativeID; - let nativeText = null; + const hasTextAncestor = useContext(TextAncestor); + if (hasTextAncestor) { if (isPressable) { - nativeText = ( - = }} /> ); - } else { - nativeText = ( - - {children} - - ); } - if (children == null) { - return nativeText; - } + return ( + + {children} + + ); + } - // If the children do not contain a JSX element it would not be possible to have a - // nested `Text` component so we can skip adding the `TextAncestor` context wrapper - // which has a performance overhead. Since we do this for performance reasons we need - // to keep the check simple to avoid regressing overall perf. For this reason the - // `children.length` constant is set to `3`, this should be a reasonable tradeoff - // to capture the majority of `Text` uses but also not make this check too expensive. - if (Array.isArray(children) && children.length <= 3) { - let hasNonTextChild = false; - for (let child of children) { - if (child != null && typeof child === 'object') { - hasNonTextChild = true; - break; - } - } - if (!hasNonTextChild) { - return nativeText; + // If the disabled prop and accessibilityState.disabled are out of sync but not both in + // falsy states we need to update the accessibilityState object to use the disabled prop. + if ( + _disabled !== _accessibilityStateDisabled && + ((_disabled != null && _disabled !== false) || + (_accessibilityStateDisabled != null && + _accessibilityStateDisabled !== false)) + ) { + _accessibilityState = {..._accessibilityState, disabled: _disabled}; + } + + const _accessible = Platform.select({ + ios: accessible !== false, + android: + accessible == null + ? onPress != null || onLongPress != null + : accessible, + default: accessible, + }); + + let nativeText = null; + if (isPressable) { + nativeText = ( + + ); + } else { + nativeText = ( + + {children} + + ); + } + + if (children == null) { + return nativeText; + } + + // If the children do not contain a JSX element it would not be possible to have a + // nested `Text` component so we can skip adding the `TextAncestor` context wrapper + // which has a performance overhead. Since we do this for performance reasons we need + // to keep the check simple to avoid regressing overall perf. For this reason the + // `children.length` constant is set to `3`, this should be a reasonable tradeoff + // to capture the majority of `Text` uses but also not make this check too expensive. + if (Array.isArray(children) && children.length <= 3) { + let hasNonTextChild = false; + for (let child of children) { + if (child != null && typeof child === 'object') { + hasNonTextChild = true; + break; } - } else if (typeof children !== 'object') { + } + if (!hasNonTextChild) { return nativeText; } + } else if (typeof children !== 'object') { + return nativeText; + } - return ( - {nativeText} - ); - }, - ); + return ( + {nativeText} + ); + }, +); Text.displayName = 'Text'; @@ -476,10 +475,10 @@ type NativePressableTextProps = $ReadOnly<{ * This logic is split out from the main Text component to enable the more * expensive pressability logic to be only initialized when needed. */ -const NativePressableVirtualText: React.AbstractComponent< - NativePressableTextProps, - TextForwardRef, -> = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { +const NativePressableVirtualText: component( + ref: React.RefSetter, + ...props: NativePressableTextProps +) = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { const [isHighlighted, eventHandlersForText] = useTextPressability( textPressabilityProps, ); @@ -501,10 +500,10 @@ const NativePressableVirtualText: React.AbstractComponent< * This logic is split out from the main Text component to enable the more * expensive pressability logic to be only initialized when needed. */ -const NativePressableText: React.AbstractComponent< - NativePressableTextProps, - TextForwardRef, -> = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { +const NativePressableText: component( + ref: React.RefSetter, + ...props: NativePressableTextProps +) = React.forwardRef(({textProps, textPressabilityProps}, forwardedRef) => { const [isHighlighted, eventHandlersForText] = useTextPressability( textPressabilityProps, ); diff --git a/packages/react-native/Libraries/Text/__tests__/Text-test.js b/packages/react-native/Libraries/Text/__tests__/Text-test.js index b2455ff54ad84c..fa6bd876a84c23 100644 --- a/packages/react-native/Libraries/Text/__tests__/Text-test.js +++ b/packages/react-native/Libraries/Text/__tests__/Text-test.js @@ -36,8 +36,6 @@ describe('Text', () => { accessible={true} allowFontScaling={true} ellipsizeMode="tail" - isHighlighted={false} - selectionColor={null} /> `); }); @@ -62,9 +60,7 @@ describe('Text compat with web', () => { accessible={true} allowFontScaling={true} ellipsizeMode="tail" - isHighlighted={false} nativeID="id" - selectionColor={null} tabIndex={0} testID="testID" /> @@ -178,9 +174,7 @@ describe('Text compat with web', () => { aria-valuetext="3" disabled={true} ellipsizeMode="tail" - isHighlighted={false} role="main" - selectionColor={null} /> `); }); @@ -202,9 +196,7 @@ describe('Text compat with web', () => { accessible={true} allowFontScaling={true} ellipsizeMode="tail" - isHighlighted={false} selectable={false} - selectionColor={null} style={ Object { "backgroundColor": "white", diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 2e1295aa8ee70d..895b81f3bd281f 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -1657,10 +1657,10 @@ type Props = $ReadOnly<{| color?: ?ColorValue, size?: ?IndicatorSize, |}>; -declare const ActivityIndicatorWithRef: React.AbstractComponent< - Props, - HostComponent, ->; +declare const ActivityIndicatorWithRef: component( + ref: React.RefSetter>, + ...props: Props +); declare export default typeof ActivityIndicatorWithRef; " `; @@ -1891,10 +1891,10 @@ type Props = $ReadOnly<{| unstable_pressDelay?: ?number, \\"aria-label\\"?: ?string, |}>; -declare export default React.AbstractComponent< - Props, - React.ElementRef, ->; +declare export default component( + ref: React.RefSetter>, + ...props: Props +); " `; @@ -1995,10 +1995,10 @@ declare export default typeof RCTSafeAreaViewNativeComponent; `; exports[`public API should not change unintentionally Libraries/Components/SafeAreaView/SafeAreaView.js 1`] = ` -"declare const exported: React.AbstractComponent< - ViewProps, - React.ElementRef, ->; +"declare const exported: component( + ref: React.RefSetter>, + ...props: ViewProps +); declare export default typeof exported; " `; @@ -2258,11 +2258,11 @@ type RefForwarder = { nativeInstance: TNativeInstance | null, publicInstance: TPublicInstance | null, }; -declare module.exports: React.AbstractComponent< - React.ElementConfig, - PublicScrollViewInstance, -> & - ScrollViewComponentStatics; +declare const Wrapper: component( + ref: React.RefSetter, + ...props: Props +); +declare module.exports: typeof Wrapper & ScrollViewComponentStatics; " `; @@ -2396,10 +2396,10 @@ type Instance = { setNextHeaderY: (number) => void, ... }; -declare const ScrollViewStickyHeaderWithForwardedRef: React.AbstractComponent< - Props, - Instance, ->; +declare const ScrollViewStickyHeaderWithForwardedRef: component( + ref: React.RefSetter, + ...props: Props +); declare export default typeof ScrollViewStickyHeaderWithForwardedRef; " `; @@ -2529,12 +2529,13 @@ export type Props = $ReadOnly<{| onChange?: ?(event: SwitchChangeEvent) => Promise | void, onValueChange?: ?(value: boolean) => Promise | void, |}>; -declare const SwitchWithForwardedRef: React.AbstractComponent< - Props, - React.ElementRef< - typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent, - >, +type SwitchRef = React.ElementRef< + typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent, >; +declare const SwitchWithForwardedRef: component( + ref: React.RefSetter, + ...props: Props +); declare export default typeof SwitchWithForwardedRef; " `; @@ -3686,9 +3687,10 @@ exports[`public API should not change unintentionally Libraries/Components/Touch style?: ?ViewStyleProp, hostRef: React.RefSetter>, |}>; -declare module.exports: React.AbstractComponent< - $ReadOnly<$Diff>, ->; +declare module.exports: component( + ref: React.RefSetter, + ...props: $ReadOnly<$Diff> +); " `; @@ -3715,10 +3717,10 @@ type Props = $ReadOnly<{| testOnly_pressed?: ?boolean, hostRef: React.RefSetter>, |}>; -declare const Touchable: React.AbstractComponent< - $ReadOnly<$Diff>, - React.ElementRef, ->; +declare const Touchable: component( + ref: React.RefSetter>, + ...props: $ReadOnly<$Diff> +); declare module.exports: Touchable; " `; @@ -3803,10 +3805,10 @@ type Props = $ReadOnly<{| style?: ?ViewStyleProp, hostRef?: ?React.RefSetter>, |}>; -declare const Touchable: React.AbstractComponent< - Props, - React.ElementRef, ->; +declare const Touchable: component( + ref: React.RefSetter>, + ...props: Props +); declare module.exports: Touchable; " `; @@ -3923,10 +3925,10 @@ declare module.exports: ReactNativeViewAttributes; exports[`public API should not change unintentionally Libraries/Components/View/View.js 1`] = ` "export type Props = ViewProps; -declare const View: React.AbstractComponent< - ViewProps, - React.ElementRef, ->; +declare const View: component( + ref: React.RefSetter>, + ...props: ViewProps +); declare module.exports: View; " `; @@ -4336,7 +4338,7 @@ exports[`public API should not change unintentionally Libraries/Core/ExceptionsM name: string; } type ExceptionDecorator = (ExceptionData) => ExceptionData; -declare const decoratedExtraDataKey: symbol; +declare const decoratedExtraDataKey: \\"RN$ErrorExtraDataKey\\"; declare function unstable_setExceptionDecorator( exceptionDecorator: ?ExceptionDecorator ): void; @@ -4537,11 +4539,10 @@ exports[`public API should not change unintentionally Libraries/Debugging/Debugg highlightElements(elements: ElementRectangle[]): void, clearElementsHighlight(): void, }; -declare const DebuggingOverlayWithForwardedRef: React.AbstractComponent< - {}, - DebuggingOverlayHandle, - React.Node, ->; +declare const DebuggingOverlayWithForwardedRef: component( + ref: React.RefSetter, + ...props: {} +); declare export default typeof DebuggingOverlayWithForwardedRef; " `; @@ -4829,7 +4830,7 @@ type AndroidImageProps = $ReadOnly<{| resizeMethod?: ?(\\"auto\\" | \\"resize\\" | \\"scale\\" | \\"none\\"), resizeMultiplier?: ?number, |}>; -export type ImageProps = {| +export type ImageProps = $ReadOnly<{| ...$Diff>, ...IOSImageProps, ...AndroidImageProps, @@ -4873,13 +4874,13 @@ export type ImageProps = {| src?: ?string, srcSet?: ?string, children?: empty, -|}; +|}>; export type ImageBackgroundProps = $ReadOnly<{| ...ImageProps, children?: Node, style?: ?ViewStyleProp, imageStyle?: ?ImageStyleProp, - imageRef?: Ref, + imageRef?: RefSetter>, |}>; " `; @@ -4968,16 +4969,18 @@ type ImageComponentStaticsAndroid = $ReadOnly<{ ...ImageComponentStaticsIOS, abortPrefetch(requestId: number): void, }>; -export type AbstractImageAndroid = React.AbstractComponent< - ImagePropsType, - | React.ElementRef - | React.ElementRef, ->; +export type AbstractImageAndroid = component( + ref: React.RefSetter< + | React.ElementRef + | React.ElementRef, + >, + ...props: ImagePropsType +); export type ImageAndroid = AbstractImageAndroid & ImageComponentStaticsAndroid; -export type AbstractImageIOS = React.AbstractComponent< - ImagePropsType, - React.ElementRef, ->; +export type AbstractImageIOS = component( + ref: React.RefSetter>, + ...props: ImagePropsType +); export type ImageIOS = AbstractImageIOS & ImageComponentStaticsIOS; export type Image = ImageIOS | ImageAndroid; export type { ImageProps } from \\"./ImageProps\\"; @@ -8487,7 +8490,10 @@ exports[`public API should not change unintentionally Libraries/Text/Text.js 1`] "type TextForwardRef = React.ElementRef< typeof NativeText | typeof NativeVirtualText, >; -declare const Text: React.AbstractComponent; +declare const Text: component( + ref: React.RefSetter, + ...props: TextProps +); declare module.exports: Text; " `; diff --git a/packages/react-native/Libraries/promiseRejectionTrackingOptions.js b/packages/react-native/Libraries/promiseRejectionTrackingOptions.js index 5a12d2d300b3e4..d8fdbd617843fb 100644 --- a/packages/react-native/Libraries/promiseRejectionTrackingOptions.js +++ b/packages/react-native/Libraries/promiseRejectionTrackingOptions.js @@ -36,7 +36,7 @@ let rejectionTrackingOptions: $NonMaybeType[0]> = { } // 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') { + if (rejection?.stack && typeof rejection.stack === 'string') { stack = rejection.stack; } } diff --git a/packages/react-native/React/Base/RCTConvert.mm b/packages/react-native/React/Base/RCTConvert.mm index 314dc678cb0785..95a4bdc696d6cf 100644 --- a/packages/react-native/React/Base/RCTConvert.mm +++ b/packages/react-native/React/Base/RCTConvert.mm @@ -307,6 +307,7 @@ + (NSLocale *)NSLocale:(id)json @"tail" : @(NSLineBreakByTruncatingTail), @"middle" : @(NSLineBreakByTruncatingMiddle), @"wordWrapping" : @(NSLineBreakByWordWrapping), + @"char" : @(NSLineBreakByCharWrapping), }), NSLineBreakByTruncatingTail, integerValue) diff --git a/packages/react-native/React/Base/RCTModuleMethod.mm b/packages/react-native/React/Base/RCTModuleMethod.mm index f2d32d9aaf708f..5b2b66d5453000 100644 --- a/packages/react-native/React/Base/RCTModuleMethod.mm +++ b/packages/react-native/React/Base/RCTModuleMethod.mm @@ -273,7 +273,7 @@ - (void)processMethodSignature for (NSUInteger i = 2; i < numberOfArguments; i++) { const char *objcType = [methodSignature getArgumentTypeAtIndex:i]; - BOOL isNullableType = NO; + [[maybe_unused]] BOOL isNullableType = NO; RCTMethodArgument *argument = arguments[i - 2]; NSString *typeName = argument.type; SEL selector = selectorForType(typeName); diff --git a/packages/react-native/React/Base/RCTTouchEvent.m b/packages/react-native/React/Base/RCTTouchEvent.m index 4fb743f454bd92..42783d26580530 100644 --- a/packages/react-native/React/Base/RCTTouchEvent.m +++ b/packages/react-native/React/Base/RCTTouchEvent.m @@ -59,7 +59,7 @@ - (BOOL)canCoalesce newEvent); BOOL newEventIsMoreRecent = NO; - BOOL oldEventIsMoreRecent = NO; + __unused BOOL oldEventIsMoreRecent = NO; NSInteger count = _reactTouches.count; for (int i = 0; i < count; i++) { NSDictionary *touch = _reactTouches[i]; diff --git a/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.h b/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.h new file mode 100644 index 00000000000000..a400bbed0040f5 --- /dev/null +++ b/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.h @@ -0,0 +1,12 @@ +/* + * 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 + +@interface RCTDevToolsRuntimeSettingsModule : NSObject + +@end diff --git a/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.mm b/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.mm new file mode 100644 index 00000000000000..f7654940363da2 --- /dev/null +++ b/packages/react-native/React/CoreModules/RCTDevToolsRuntimeSettingsModule.mm @@ -0,0 +1,58 @@ +/* + * 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 +#import + +#import "CoreModulesPlugins.h" + +struct Config { + bool shouldReloadAndProfile = false; + bool recordChangeDescriptions = false; +}; + +// static to persist across Turbo Module reloads +static Config _config; + +@interface RCTDevToolsRuntimeSettingsModule () { +} +@end + +@implementation RCTDevToolsRuntimeSettingsModule +RCT_EXPORT_MODULE(ReactDevToolsRuntimeSettingsModule) + +RCT_EXPORT_METHOD(setReloadAndProfileConfig + : (JS::NativeReactDevToolsRuntimeSettingsModule::PartialReloadAndProfileConfig &)config) +{ + if (config.shouldReloadAndProfile().has_value()) { + _config.shouldReloadAndProfile = config.shouldReloadAndProfile().value(); + } + if (config.recordChangeDescriptions().has_value()) { + _config.recordChangeDescriptions = config.recordChangeDescriptions().value(); + } +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getReloadAndProfileConfig) +{ + return @{ + @"shouldReloadAndProfile" : @(_config.shouldReloadAndProfile), + @"recordChangeDescriptions" : @(_config.recordChangeDescriptions), + }; +} + +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} + +@end + +Class RCTDevToolsRuntimeSettingsCls(void) +{ + return RCTDevToolsRuntimeSettingsModule.class; +} diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm index 93af874f451c58..2d884a73641683 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm @@ -7,6 +7,7 @@ #import "RCTEnhancedScrollView.h" #import +#import @interface RCTEnhancedScrollView () @end @@ -261,7 +262,8 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView - (BOOL)isHorizontal:(UIScrollView *)scrollView { - return scrollView.contentSize.width > self.frame.size.width; + return !facebook::react::floatEquality(scrollView.contentSize.width, self.frame.size.width) && + scrollView.contentSize.width > self.frame.size.width; } @end diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 4ac6fcf763f26a..6e198a0bfa2d41 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -539,6 +539,7 @@ - (BOOL)_shouldDisableScrollInteraction metrics.contentInset = RCTEdgeInsetsFromUIEdgeInsets(_scrollView.contentInset); metrics.containerSize = RCTSizeFromCGSize(_scrollView.bounds.size); metrics.zoomScale = _scrollView.zoomScale; + metrics.timestamp = CACurrentMediaTime(); if (_layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) { metrics.contentOffset.x = metrics.contentSize.width - metrics.containerSize.width - metrics.contentOffset.x; @@ -554,12 +555,17 @@ - (void)_updateStateWithContentOffset } auto contentOffset = RCTPointFromCGPoint(_scrollView.contentOffset); - auto data = _state->getData(); - - if (contentOffset != data.contentOffset) { - data.contentOffset = contentOffset; - _state->updateState(std::move(data)); - } + _state->updateState( + [contentOffset]( + const ScrollViewShadowNode::ConcreteState::Data &oldData) -> ScrollViewShadowNode::ConcreteState::SharedData { + if (oldData.contentOffset == contentOffset) { + // avoid doing a state update if content offset didn't change. + return nullptr; + } + auto newData = oldData; + newData.contentOffset = contentOffset; + return std::make_shared(newData); + }); } - (void)prepareForRecycle diff --git a/packages/react-native/React/Tests/Text/RCTParagraphComponentViewTests.mm b/packages/react-native/React/Tests/Text/RCTParagraphComponentViewTests.mm index 760d1601616caa..d94ca995977e5c 100644 --- a/packages/react-native/React/Tests/Text/RCTParagraphComponentViewTests.mm +++ b/packages/react-native/React/Tests/Text/RCTParagraphComponentViewTests.mm @@ -121,8 +121,8 @@ - (void)setUp auto &props = *sharedProps; props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}}; auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) .children({ @@ -134,10 +134,10 @@ - (void)setUp props.accessible = true; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(0)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(0)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(0)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) .children({ @@ -214,10 +214,10 @@ - (void)setUp props.accessible = true; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(0)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(30)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(50)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(30)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50)); return sharedProps; }) .children({ @@ -258,10 +258,10 @@ - (void)setUp props.accessible = true; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(0)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(90)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(50)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50)); return sharedProps; }) .children({ @@ -418,8 +418,8 @@ - (void)testEntireParagraphLink auto &props = *sharedProps; props.layoutConstraints = LayoutConstraints{{0, 0}, {500, 500}}; auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) .children({ @@ -432,10 +432,10 @@ - (void)testEntireParagraphLink props.accessibilityTraits = AccessibilityTraits::Link; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(0)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(90)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(20)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(0)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(90)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20)); return sharedProps; }) .children({ diff --git a/packages/react-native/React/Views/ScrollView/RCTScrollEvent.m b/packages/react-native/React/Views/ScrollView/RCTScrollEvent.m index 944c64cdb408e1..366060782151bd 100644 --- a/packages/react-native/React/Views/ScrollView/RCTScrollEvent.m +++ b/packages/react-native/React/Views/ScrollView/RCTScrollEvent.m @@ -16,6 +16,7 @@ @implementation RCTScrollEvent { CGFloat _scrollViewZoomScale; NSDictionary *_userData; uint16_t _coalescingKey; + CFTimeInterval _timestamp; } @synthesize viewTag = _viewTag; @@ -43,6 +44,7 @@ - (instancetype)initWithEventName:(NSString *)eventName _scrollViewZoomScale = scrollViewZoomScale; _userData = userData; _coalescingKey = coalescingKey; + _timestamp = CACurrentMediaTime(); } return self; } @@ -67,6 +69,7 @@ - (NSDictionary *)body @"contentSize" : @{@"width" : @(_scrollViewContentSize.width), @"height" : @(_scrollViewContentSize.height)}, @"layoutMeasurement" : @{@"width" : @(_scrollViewFrame.size.width), @"height" : @(_scrollViewFrame.size.height)}, @"zoomScale" : @(_scrollViewZoomScale ?: 1), + @"timestamp" : @(_timestamp * 1000), }; if (_userData) { diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 57530f104874ea..77eb9602e6b390 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2133,7 +2133,6 @@ public final class com/facebook/react/devsupport/DefaultDevLoadingViewImplementa public final class com/facebook/react/devsupport/DefaultDevSupportManagerFactory : com/facebook/react/devsupport/DevSupportManagerFactory { public fun ()V - public final fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZI)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; public fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager; } @@ -2396,12 +2395,16 @@ public class com/facebook/react/devsupport/ReleaseDevSupportManager : com/facebo public class com/facebook/react/devsupport/StackTraceHelper { public static final field COLUMN_KEY Ljava/lang/String; + public static final field COMPONENT_STACK_KEY Ljava/lang/String; + public static final field EXTRA_DATA_KEY Ljava/lang/String; public static final field FILE_KEY Ljava/lang/String; public static final field ID_KEY Ljava/lang/String; public static final field IS_FATAL_KEY Ljava/lang/String; public static final field LINE_NUMBER_KEY Ljava/lang/String; public static final field MESSAGE_KEY Ljava/lang/String; public static final field METHOD_NAME_KEY Ljava/lang/String; + public static final field NAME_KEY Ljava/lang/String; + public static final field ORIGINAL_MESSAGE_KEY Ljava/lang/String; public static final field STACK_KEY Ljava/lang/String; public fun ()V public static fun convertJavaStackTrace (Ljava/lang/Throwable;)[Lcom/facebook/react/devsupport/interfaces/StackFrame; @@ -2893,16 +2896,20 @@ public abstract interface class com/facebook/react/interfaces/TaskInterface { } public abstract interface class com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler$ParsedError { - public abstract fun getExceptionId ()I - public abstract fun getFrames ()Ljava/util/List; + public abstract fun getComponentStack ()Ljava/lang/String; + public abstract fun getExtraData ()Lcom/facebook/react/bridge/ReadableMap; + public abstract fun getId ()I public abstract fun getMessage ()Ljava/lang/String; + public abstract fun getName ()Ljava/lang/String; + public abstract fun getOriginalMessage ()Ljava/lang/String; + public abstract fun getStack ()Ljava/util/List; public abstract fun isFatal ()Z } public abstract interface class com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler$ParsedError$StackFrame { - public abstract fun getColumnNumber ()I - public abstract fun getFileName ()Ljava/lang/String; - public abstract fun getLineNumber ()I + public abstract fun getColumn ()Ljava/lang/Integer; + public abstract fun getFile ()Ljava/lang/String; + public abstract fun getLineNumber ()Ljava/lang/Integer; public abstract fun getMethodName ()Ljava/lang/String; } @@ -3373,6 +3380,12 @@ public final class com/facebook/react/modules/devloading/DevLoadingModule : com/ public final class com/facebook/react/modules/devloading/DevLoadingModule$Companion { } +public final class com/facebook/react/modules/devtoolsruntimesettings/ReactDevToolsRuntimeSettingsModule : com/facebook/fbreact/specs/NativeReactDevToolsRuntimeSettingsModuleSpec { + public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V + public fun getReloadAndProfileConfig ()Lcom/facebook/react/bridge/WritableMap; + public fun setReloadAndProfileConfig (Lcom/facebook/react/bridge/ReadableMap;)V +} + public class com/facebook/react/modules/dialog/AlertFragment : androidx/fragment/app/DialogFragment, android/content/DialogInterface$OnClickListener { public fun ()V public fun (Lcom/facebook/react/modules/dialog/DialogModule$AlertFragmentListener;Landroid/os/Bundle;)V @@ -4118,7 +4131,7 @@ public final class com/facebook/react/uimanager/BackgroundStyleApplicator { public static final fun setOutlineWidth (Landroid/view/View;F)V } -public abstract class com/facebook/react/uimanager/BaseViewManager : com/facebook/react/uimanager/ViewManager, android/view/View$OnLayoutChangeListener, com/facebook/react/uimanager/BaseViewManagerInterface { +public abstract class com/facebook/react/uimanager/BaseViewManager : com/facebook/react/uimanager/ViewManager, android/view/View$OnLayoutChangeListener { public fun ()V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun getExportedCustomBubblingEventTypeConstants ()Ljava/util/Map; @@ -4141,6 +4154,7 @@ public abstract class com/facebook/react/uimanager/BaseViewManager : com/faceboo public fun setBorderRadius (Landroid/view/View;F)V public fun setBorderTopLeftRadius (Landroid/view/View;F)V public fun setBorderTopRightRadius (Landroid/view/View;F)V + public fun setBoxShadow (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V public fun setClick (Landroid/view/View;Z)V public fun setClickCapture (Landroid/view/View;Z)V public fun setElevation (Landroid/view/View;F)V @@ -4197,48 +4211,12 @@ public abstract class com/facebook/react/uimanager/BaseViewManager : com/faceboo } public abstract class com/facebook/react/uimanager/BaseViewManagerDelegate : com/facebook/react/uimanager/ViewManagerDelegate { - protected final field mViewManager Lcom/facebook/react/uimanager/BaseViewManagerInterface; - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + protected final field mViewManager Lcom/facebook/react/uimanager/BaseViewManager; + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } -public abstract interface class com/facebook/react/uimanager/BaseViewManagerInterface { - public abstract fun setAccessibilityActions (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public abstract fun setAccessibilityCollection (Landroid/view/View;Lcom/facebook/react/bridge/ReadableMap;)V - public abstract fun setAccessibilityCollectionItem (Landroid/view/View;Lcom/facebook/react/bridge/ReadableMap;)V - public abstract fun setAccessibilityHint (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setAccessibilityLabel (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setAccessibilityLabelledBy (Landroid/view/View;Lcom/facebook/react/bridge/Dynamic;)V - public abstract fun setAccessibilityLiveRegion (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setAccessibilityRole (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setBackgroundColor (Landroid/view/View;I)V - public abstract fun setBorderBottomLeftRadius (Landroid/view/View;F)V - public abstract fun setBorderBottomRightRadius (Landroid/view/View;F)V - public abstract fun setBorderRadius (Landroid/view/View;F)V - public abstract fun setBorderTopLeftRadius (Landroid/view/View;F)V - public abstract fun setBorderTopRightRadius (Landroid/view/View;F)V - public abstract fun setElevation (Landroid/view/View;F)V - public abstract fun setFilter (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public abstract fun setImportantForAccessibility (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setMixBlendMode (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setNativeId (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setOpacity (Landroid/view/View;F)V - public abstract fun setRenderToHardwareTexture (Landroid/view/View;Z)V - public abstract fun setRole (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setRotation (Landroid/view/View;F)V - public abstract fun setScaleX (Landroid/view/View;F)V - public abstract fun setScaleY (Landroid/view/View;F)V - public abstract fun setShadowColor (Landroid/view/View;I)V - public abstract fun setTestId (Landroid/view/View;Ljava/lang/String;)V - public abstract fun setTransform (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public abstract fun setTransformOrigin (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public abstract fun setTranslateX (Landroid/view/View;F)V - public abstract fun setTranslateY (Landroid/view/View;F)V - public abstract fun setViewState (Landroid/view/View;Lcom/facebook/react/bridge/ReadableMap;)V - public abstract fun setZIndex (Landroid/view/View;F)V -} - public abstract interface class com/facebook/react/uimanager/ComponentNameResolver { public abstract fun getComponentNames ()[Ljava/lang/String; } @@ -6307,7 +6285,7 @@ public final class com/facebook/react/util/RNLog { } public class com/facebook/react/viewmanagers/ActivityIndicatorViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6319,7 +6297,7 @@ public abstract interface class com/facebook/react/viewmanagers/ActivityIndicato } public class com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6336,7 +6314,7 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidDrawerLay } public class com/facebook/react/viewmanagers/AndroidHorizontalScrollContentViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6345,7 +6323,7 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidHorizonta } public class com/facebook/react/viewmanagers/AndroidProgressBarManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6360,7 +6338,7 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidProgressB } public class com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6376,7 +6354,7 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidSwipeRefr } public class com/facebook/react/viewmanagers/AndroidSwitchManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6395,7 +6373,7 @@ public abstract interface class com/facebook/react/viewmanagers/AndroidSwitchMan } public class com/facebook/react/viewmanagers/DebuggingOverlayManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6407,7 +6385,7 @@ public abstract interface class com/facebook/react/viewmanagers/DebuggingOverlay } public class com/facebook/react/viewmanagers/ModalHostViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6424,7 +6402,7 @@ public abstract interface class com/facebook/react/viewmanagers/ModalHostViewMan } public class com/facebook/react/viewmanagers/SafeAreaViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6432,7 +6410,7 @@ public abstract interface class com/facebook/react/viewmanagers/SafeAreaViewMana } public class com/facebook/react/viewmanagers/UnimplementedNativeViewManagerDelegate : com/facebook/react/uimanager/BaseViewManagerDelegate { - public fun (Lcom/facebook/react/uimanager/BaseViewManagerInterface;)V + public fun (Lcom/facebook/react/uimanager/BaseViewManager;)V public fun setProperty (Landroid/view/View;Ljava/lang/String;Ljava/lang/Object;)V } @@ -6689,7 +6667,6 @@ public final class com/facebook/react/views/image/ReactImageManager : com/facebo public final fun setBorderColor (Lcom/facebook/react/views/image/ReactImageView;Ljava/lang/Integer;)V public final fun setBorderRadius (Lcom/facebook/react/views/image/ReactImageView;IF)V public final fun setBorderWidth (Lcom/facebook/react/views/image/ReactImageView;F)V - public final fun setBoxShadow (Lcom/facebook/react/views/image/ReactImageView;Lcom/facebook/react/bridge/ReadableArray;)V public final fun setDefaultSource (Lcom/facebook/react/views/image/ReactImageView;Ljava/lang/String;)V public final fun setFadeDuration (Lcom/facebook/react/views/image/ReactImageView;I)V public final fun setHeaders (Lcom/facebook/react/views/image/ReactImageView;Lcom/facebook/react/bridge/ReadableMap;)V @@ -7115,7 +7092,6 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollViewManager : public fun setBorderStyle (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;Ljava/lang/String;)V public fun setBorderWidth (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;IF)V public fun setBottomFillColor (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;I)V - public fun setBoxShadow (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;Lcom/facebook/react/bridge/ReadableArray;)V public fun setContentOffset (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;Lcom/facebook/react/bridge/ReadableMap;)V public fun setDecelerationRate (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;F)V public fun setDisableIntervalMomentum (Lcom/facebook/react/views/scroll/ReactHorizontalScrollView;Z)V @@ -7356,7 +7332,6 @@ public class com/facebook/react/views/scroll/ReactScrollViewManager : com/facebo public fun setBorderStyle (Lcom/facebook/react/views/scroll/ReactScrollView;Ljava/lang/String;)V public fun setBorderWidth (Lcom/facebook/react/views/scroll/ReactScrollView;IF)V public fun setBottomFillColor (Lcom/facebook/react/views/scroll/ReactScrollView;I)V - public fun setBoxShadow (Lcom/facebook/react/views/scroll/ReactScrollView;Lcom/facebook/react/bridge/ReadableArray;)V public fun setContentOffset (Lcom/facebook/react/views/scroll/ReactScrollView;Lcom/facebook/react/bridge/ReadableMap;)V public fun setDecelerationRate (Lcom/facebook/react/views/scroll/ReactScrollView;F)V public fun setDisableIntervalMomentum (Lcom/facebook/react/views/scroll/ReactScrollView;Z)V @@ -7656,7 +7631,6 @@ public abstract class com/facebook/react/views/text/ReactTextAnchorViewManager : public fun setBorderRadius (Lcom/facebook/react/views/text/ReactTextView;IF)V public fun setBorderStyle (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V public fun setBorderWidth (Lcom/facebook/react/views/text/ReactTextView;IF)V - public fun setBoxShadow (Lcom/facebook/react/views/text/ReactTextView;Lcom/facebook/react/bridge/ReadableArray;)V public fun setDataDetectorType (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V public fun setDisabled (Lcom/facebook/react/views/text/ReactTextView;Z)V public fun setEllipsizeMode (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V @@ -8139,7 +8113,6 @@ public class com/facebook/react/views/textinput/ReactTextInputManager : com/face public fun setBorderRadius (Lcom/facebook/react/views/textinput/ReactEditText;IF)V public fun setBorderStyle (Lcom/facebook/react/views/textinput/ReactEditText;Ljava/lang/String;)V public fun setBorderWidth (Lcom/facebook/react/views/textinput/ReactEditText;IF)V - public fun setBoxShadow (Lcom/facebook/react/views/textinput/ReactEditText;Lcom/facebook/react/bridge/ReadableArray;)V public fun setCaretHidden (Lcom/facebook/react/views/textinput/ReactEditText;Z)V public fun setColor (Lcom/facebook/react/views/textinput/ReactEditText;Ljava/lang/Integer;)V public fun setContextMenuHidden (Lcom/facebook/react/views/textinput/ReactEditText;Z)V @@ -8322,6 +8295,8 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro } public class com/facebook/react/views/view/ReactViewManager : com/facebook/react/views/view/ReactClippingViewManager { + public static final field Companion Lcom/facebook/react/views/view/ReactViewManager$Companion; + public static final field REACT_CLASS Ljava/lang/String; public fun ()V public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View; public fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/view/ReactViewGroup; @@ -8332,7 +8307,7 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react public fun nextFocusLeft (Lcom/facebook/react/views/view/ReactViewGroup;I)V public fun nextFocusRight (Lcom/facebook/react/views/view/ReactViewGroup;I)V public fun nextFocusUp (Lcom/facebook/react/views/view/ReactViewGroup;I)V - protected synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View; + public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View; protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/view/ReactViewGroup;)Lcom/facebook/react/views/view/ReactViewGroup; public synthetic fun receiveCommand (Landroid/view/View;ILcom/facebook/react/bridge/ReadableArray;)V public synthetic fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V @@ -8346,7 +8321,6 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react public fun setBorderRadius (Lcom/facebook/react/views/view/ReactViewGroup;ILcom/facebook/react/bridge/Dynamic;)V public fun setBorderStyle (Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;)V public fun setBorderWidth (Lcom/facebook/react/views/view/ReactViewGroup;IF)V - public fun setBoxShadow (Lcom/facebook/react/views/view/ReactViewGroup;Lcom/facebook/react/bridge/ReadableArray;)V public fun setCollapsable (Lcom/facebook/react/views/view/ReactViewGroup;Z)V public fun setCollapsableChildren (Lcom/facebook/react/views/view/ReactViewGroup;Z)V public fun setFocusable (Lcom/facebook/react/views/view/ReactViewGroup;Z)V @@ -8359,7 +8333,7 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react public fun setOverflow (Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;)V public fun setPointerEvents (Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;)V public fun setTVPreferredFocus (Lcom/facebook/react/views/view/ReactViewGroup;Z)V - protected synthetic fun setTransformProperty (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V + public synthetic fun setTransformProperty (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V protected fun setTransformProperty (Lcom/facebook/react/views/view/ReactViewGroup;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V } @@ -8370,6 +8344,9 @@ public class com/facebook/react/views/view/ReactViewManager$$PropsSetter : com/f public fun setProperty (Lcom/facebook/react/views/view/ReactViewManager;Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;Ljava/lang/Object;)V } +public final class com/facebook/react/views/view/ReactViewManager$Companion { +} + public final class com/facebook/react/views/view/ViewGroupClickEvent : com/facebook/react/uimanager/events/Event { public fun (I)V public fun (II)V diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index 1ce7e621ad50e0..cd4d907728d615 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -106,6 +106,10 @@ val preparePrefab by Pair("../ReactCommon/cxxreact/", "cxxreact/"), // react_featureflags Pair("../ReactCommon/react/featureflags/", "react/featureflags/"), + // react_devtoolsruntimesettings + Pair( + "../ReactCommon/react/devtoolsruntimesettings/", + "react/devtoolsruntimesettings/"), // react_render_animations Pair( "../ReactCommon/react/renderer/animations/", diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java index 48c59f8ee72bc9..8630a29723b000 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.Callback; import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.core.PermissionListener; +import com.facebook.systrace.Systrace; /** * Delegate class for {@link ReactActivity}. You can subclass this to provide custom implementations @@ -102,35 +103,41 @@ public String getMainComponentName() { } public void onCreate(Bundle savedInstanceState) { - String mainComponentName = getMainComponentName(); - final Bundle launchOptions = composeLaunchOptions(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) { - mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); - } - if (ReactNativeFeatureFlags.enableBridgelessArchitecture()) { - mReactDelegate = - new ReactDelegate(getPlainActivity(), getReactHost(), mainComponentName, launchOptions); - } else { - mReactDelegate = - new ReactDelegate( - getPlainActivity(), - getReactNativeHost(), - mainComponentName, - launchOptions, - isFabricEnabled()) { - @Override - protected ReactRootView createRootView() { - ReactRootView rootView = ReactActivityDelegate.this.createRootView(); - if (rootView == null) { - rootView = super.createRootView(); - } - return rootView; - } - }; - } - if (mainComponentName != null) { - loadApp(mainComponentName); - } + Systrace.traceSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "ReactActivityDelegate.onCreate::init", + () -> { + String mainComponentName = getMainComponentName(); + final Bundle launchOptions = composeLaunchOptions(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) { + mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); + } + if (ReactNativeFeatureFlags.enableBridgelessArchitecture()) { + mReactDelegate = + new ReactDelegate( + getPlainActivity(), getReactHost(), mainComponentName, launchOptions); + } else { + mReactDelegate = + new ReactDelegate( + getPlainActivity(), + getReactNativeHost(), + mainComponentName, + launchOptions, + isFabricEnabled()) { + @Override + protected ReactRootView createRootView() { + ReactRootView rootView = ReactActivityDelegate.this.createRootView(); + if (rootView == null) { + rootView = super.createRootView(); + } + return rootView; + } + }; + } + if (mainComponentName != null) { + loadApp(mainComponentName); + } + }); } protected void loadApp(String appKey) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 939f12c91eff4e..bc190d6f6e6c62 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -1391,14 +1391,14 @@ private void detachRootViewFromInstance(ReactRoot reactRoot, ReactContext reactC new RuntimeException( "detachRootViewFromInstance called with ReactRootView with invalid id")); } + + clearReactRoot(reactRoot); } else { reactContext .getCatalystInstance() .getJSModule(AppRegistry.class) .unmountApplicationComponentAtRootTag(reactRoot.getRootViewTag()); } - - clearReactRoot(reactRoot); } @ThreadConfined(UI) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt index b4b1a46b886195..cf7600f1885ef3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt @@ -24,6 +24,7 @@ internal class EventAnimationDriver( private val eventPath: List, @JvmField internal var valueNode: ValueAnimatedNode ) : RCTModernEventEmitter { + @Deprecated("Deprecated in Java") override fun receiveEvent(targetReactTag: Int, eventName: String, event: WritableMap?) = receiveEvent(-1, targetReactTag, eventName, event) @@ -36,6 +37,7 @@ internal class EventAnimationDriver( // We assume this event can't be coalesced. `customCoalesceKey` has no meaning in Fabric. receiveEvent(surfaceId, targetTag, eventName, false, 0, event, EventCategoryDef.UNSPECIFIED) + @Deprecated("Deprecated in Java") override fun receiveTouches( eventName: String, touches: WritableArray, @@ -44,6 +46,7 @@ internal class EventAnimationDriver( throw UnsupportedOperationException("receiveTouches is not support by native animated events") } + @Deprecated("Deprecated in Java") override fun receiveTouches(touchEvent: TouchEvent) { throw UnsupportedOperationException("receiveTouches is not support by native animated events") } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt index ac7d5daabe8bb3..9ca3b860bf48cb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt @@ -23,31 +23,6 @@ import com.facebook.react.packagerconnection.RequestHandler */ public class DefaultDevSupportManagerFactory : DevSupportManagerFactory { - @Deprecated( - "in favor of the customisable create for DevSupportManagerFactory", - ReplaceWith( - "create(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, redBoxHandler, devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, surfaceDelegateFactory, devLoadingViewManager, pausedInDebuggerOverlayManager)")) - public fun create( - applicationContext: Context, - reactInstanceDevHelper: ReactInstanceDevHelper, - packagerPathForJSBundleName: String?, - enableOnCreate: Boolean, - minNumShakes: Int - ): DevSupportManager { - return create( - applicationContext, - reactInstanceDevHelper, - packagerPathForJSBundleName, - enableOnCreate, - null, - null, - minNumShakes, - null, - null, - null, - null) - } - public override fun create( applicationContext: Context, reactInstanceManagerHelper: ReactInstanceDevHelper, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSettingsActivity.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSettingsActivity.kt index f19f7b52d439e0..6eaf92ac4b986d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSettingsActivity.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSettingsActivity.kt @@ -18,6 +18,8 @@ import com.facebook.react.R * be triggered through the developers option menu displayed by [DevSupportManager]. */ public class DevSettingsActivity : PreferenceActivity() { + + @Deprecated("Deprecated in Java") public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) title = application.resources.getString(R.string.catalyst_settings_title) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java index 70b779a8dc8e10..d901f8b2f73c79 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java @@ -34,9 +34,13 @@ public class StackTraceHelper { public static final String METHOD_NAME_KEY = "methodName"; public static final String MESSAGE_KEY = "message"; + public static final String ORIGINAL_MESSAGE_KEY = "originalMessage"; + public static final String NAME_KEY = "name"; + public static final String COMPONENT_STACK_KEY = "componentStack"; public static final String STACK_KEY = "stack"; public static final String ID_KEY = "id"; public static final String IS_FATAL_KEY = "isFatal"; + public static final String EXTRA_DATA_KEY = "extraData"; private static final Pattern STACK_FRAME_PATTERN1 = Pattern.compile("^(?:(.*?)@)?(.*?)\\:([0-9]+)\\:([0-9]+)$"); @@ -260,22 +264,32 @@ public static String formatStackTrace(String title, StackFrame[] stack) { } public static JavaOnlyMap convertParsedError(ParsedError error) { - List frames = error.getFrames(); + List frames = error.getStack(); List readableMapList = new ArrayList<>(); for (ParsedError.StackFrame frame : frames) { JavaOnlyMap map = new JavaOnlyMap(); - map.putDouble(COLUMN_KEY, frame.getColumnNumber()); + map.putDouble(COLUMN_KEY, frame.getColumn()); map.putDouble(LINE_NUMBER_KEY, frame.getLineNumber()); - map.putString(FILE_KEY, (String) frame.getFileName()); + map.putString(FILE_KEY, (String) frame.getFile()); map.putString(METHOD_NAME_KEY, (String) frame.getMethodName()); readableMapList.add(map); } JavaOnlyMap data = new JavaOnlyMap(); data.putString(MESSAGE_KEY, error.getMessage()); + if (error.getOriginalMessage() != null) { + data.putString(ORIGINAL_MESSAGE_KEY, error.getOriginalMessage()); + } + if (error.getName() != null) { + data.putString(NAME_KEY, error.getName()); + } + if (error.getComponentStack() != null) { + data.putString(COMPONENT_STACK_KEY, error.getComponentStack()); + } data.putArray(STACK_KEY, JavaOnlyArray.from(readableMapList)); - data.putInt(ID_KEY, error.getExceptionId()); + data.putInt(ID_KEY, error.getId()); data.putBoolean(IS_FATAL_KEY, error.isFatal()); + data.putMap(EXTRA_DATA_KEY, error.getExtraData()); return data; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.kt index 2c85e1273d13bf..3420e0ed61b0c6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.kt @@ -17,6 +17,7 @@ import com.facebook.react.uimanager.events.TouchEvent import com.facebook.systrace.Systrace public class FabricEventEmitter(private val uiManager: FabricUIManager) : RCTModernEventEmitter { + @Deprecated("Deprecated in Java") public override fun receiveEvent(reactTag: Int, eventName: String, params: WritableMap?): Unit { receiveEvent(ViewUtil.NO_SURFACE_ID, reactTag, eventName, params) } @@ -49,6 +50,7 @@ public class FabricEventEmitter(private val uiManager: FabricUIManager) : RCTMod } /** Touches are dispatched by [.receiveTouches] */ + @Deprecated("Deprecated in Java") public override fun receiveTouches( eventName: String, touches: WritableArray, @@ -57,6 +59,7 @@ public class FabricEventEmitter(private val uiManager: FabricUIManager) : RCTMod throw UnsupportedOperationException("EventEmitter#receiveTouches is not supported by Fabric") } + @Deprecated("Deprecated in Java") public override fun receiveTouches(event: TouchEvent): Unit { // Calls are expected to go via TouchesHelper throw UnsupportedOperationException("EventEmitter#receiveTouches is not supported by Fabric") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java index 887dc73189815c..d85716c6f0bee4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java @@ -411,7 +411,7 @@ public void addViewAt(final int parentTag, final int tag, final int index) { try { getViewGroupManager(parentViewState).addView(parentView, view, index); - } catch (IllegalStateException e) { + } catch (IllegalStateException | IndexOutOfBoundsException e) { // Wrap error with more context for debugging throw new IllegalStateException( "addViewAt: failed to insert view [" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler.kt index c12a1ebeef96f4..60113e7e6bdb35 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler.kt @@ -8,6 +8,8 @@ package com.facebook.react.interfaces.exceptionmanager import com.facebook.proguard.annotations.DoNotStripAny +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.ReadableNativeMap import com.facebook.react.common.annotations.UnstableReactNativeAPI import java.util.ArrayList @@ -18,32 +20,40 @@ public fun interface ReactJsExceptionHandler { public interface ParsedError { @DoNotStripAny public interface StackFrame { - public val fileName: String + public val file: String? public val methodName: String - public val lineNumber: Int - public val columnNumber: Int + public val lineNumber: Int? + public val column: Int? } - public val frames: List public val message: String - public val exceptionId: Int + public val originalMessage: String? + public val name: String? + public val componentStack: String? + public val stack: List + public val id: Int public val isFatal: Boolean + public val extraData: ReadableMap } @DoNotStripAny private data class ParsedStackFrameImpl( - override val fileName: String, + override val file: String?, override val methodName: String, - override val lineNumber: Int, - override val columnNumber: Int, + override val lineNumber: Int?, + override val column: Int?, ) : ParsedError.StackFrame @DoNotStripAny private data class ParsedErrorImpl( - override val frames: ArrayList, override val message: String, - override val exceptionId: Int, + override val originalMessage: String?, + override val name: String?, + override val componentStack: String?, + override val stack: ArrayList, + override val id: Int, override val isFatal: Boolean, + override val extraData: ReadableNativeMap, ) : ParsedError public fun reportJsException(errorMap: ParsedError) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 81c22a2f25a932..9fcacc8514a137 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -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<> + * @generated SignedSource<<68fdc215dff92398e5c5ebd1f7544ac0>> */ /** @@ -364,6 +364,32 @@ public object ReactNativeFeatureFlags { accessor = accessorProvider() } + /** + * This is a combination of `dangerouslyReset` and `override` that reduces + * the likeliness of a race condition between the two calls. + * + * This is **dangerous** because it can introduce consistency issues that will + * be much harder to debug. For example, it could hide the fact that feature + * flags are read before you set the values you want to use everywhere. It + * could also cause a workflow to suddently have different feature flags for + * behaviors that were configured with different values before. + * + * It returns a string that contains the feature flags that were accessed + * before this call (or between the last call to `dangerouslyReset` and this + * call). If you are using this method, you do not want the hard crash that + * you would get from using `dangerouslyReset` and `override` separately, + * but you should still log this somehow. + * + * Please see the documentation of `dangerouslyReset` for additional details. + */ + @JvmStatic + public fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? { + val newAccessor = accessorProvider() + val previouslyAccessedFlags = newAccessor.dangerouslyForceOverride(provider) + accessor = newAccessor + return previouslyAccessedFlags + } + /** * This is just used to replace the default ReactNativeFeatureFlagsCxxAccessor * that uses JNI with a version that doesn't, to simplify testing. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsAccessor.kt index b20cafdc4c215e..79c04da9c13b86 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsAccessor.kt @@ -11,4 +11,6 @@ public interface ReactNativeFeatureFlagsAccessor : ReactNativeFeatureFlagsProvid public fun override(provider: ReactNativeFeatureFlagsProvider) public fun dangerouslyReset() + + public fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 9723b947f21a45..87f0cf9b174434 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -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<<02f9fa1fd92a89a823e8fd35e1a2409b>> + * @generated SignedSource<<2ae504095230637e5a2669c0621a45cf>> */ /** @@ -525,4 +525,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso ReactNativeFeatureFlagsCxxInterop.override(provider as Any) override fun dangerouslyReset(): Unit = ReactNativeFeatureFlagsCxxInterop.dangerouslyReset() + + override fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? = + ReactNativeFeatureFlagsCxxInterop.dangerouslyForceOverride(provider as Any) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 43ccb1023d8163..f78831171ca177 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -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<> + * @generated SignedSource<<663b0e32ebe7f3a04957550be0c1a5e9>> */ /** @@ -131,4 +131,6 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun override(provider: Any) @DoNotStrip @JvmStatic public external fun dangerouslyReset() + + @DoNotStrip @JvmStatic public external fun dangerouslyForceOverride(provider: Any): String? } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index d17edec3f98493..ae5715e091969e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -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<> + * @generated SignedSource<> */ /** @@ -117,7 +117,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useOptimizedEventBatchingOnAndroid(): Boolean = false - override fun useRuntimeShadowNodeReferenceUpdate(): Boolean = true + override fun useRuntimeShadowNodeReferenceUpdate(): Boolean = false override fun useTurboModuleInterop(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 718e1489a00101..30bcc8763c55fc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -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<> + * @generated SignedSource<<555ab22658d3d6e7165aed11bd20e2c3>> */ /** @@ -585,7 +585,21 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces } override fun dangerouslyReset() { - // We don't need to do anything here because `ReactNativeFeatureFlags` will - // just create a new instance of this class. + // We don't need to do anything else here because `ReactNativeFeatureFlags` will just create a + // new instance of this class. + } + + override fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? { + val accessedFeatureFlags = getAccessedFeatureFlags() + currentProvider = provider + return accessedFeatureFlags + } + + internal fun getAccessedFeatureFlags(): String? { + if (accessedFeatureFlags.isEmpty()) { + return null + } + + return accessedFeatureFlags.joinToString(separator = ", ") { it } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/interop/InteropEventEmitter.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/interop/InteropEventEmitter.kt index 5c4e7e42c30160..eca7be1f6832ae 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/interop/InteropEventEmitter.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/interop/InteropEventEmitter.kt @@ -39,6 +39,7 @@ public class InteropEventEmitter(private val reactContext: ReactContext) : RCTEv dispatcher?.dispatchEvent(InteropEvent(eventName, eventData, surfaceId, targetReactTag)) } + @Deprecated("Deprecated in Java") override fun receiveTouches( eventName: String, touches: WritableArray, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devtoolsruntimesettings/ReactDevToolsRuntimeSettingsModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devtoolsruntimesettings/ReactDevToolsRuntimeSettingsModule.kt new file mode 100644 index 00000000000000..63e30b7cd96383 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devtoolsruntimesettings/ReactDevToolsRuntimeSettingsModule.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.modules.devtoolsruntimesettings + +import com.facebook.fbreact.specs.NativeReactDevToolsRuntimeSettingsModuleSpec +import com.facebook.jni.annotations.DoNotStripAny +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.WritableMap +import com.facebook.react.module.annotations.ReactModule + +private class Settings { + var shouldReloadAndProfile: Boolean = false + var recordChangeDescriptions: Boolean = false +} + +@DoNotStripAny +@ReactModule(name = NativeReactDevToolsRuntimeSettingsModuleSpec.NAME) +public class ReactDevToolsRuntimeSettingsModule(reactContext: ReactApplicationContext?) : + NativeReactDevToolsRuntimeSettingsModuleSpec(reactContext) { + + // static to persist across Turbo Module reloads + private companion object { + private val settings = Settings() + } + + override fun setReloadAndProfileConfig(map: ReadableMap) { + if (map.hasKey("shouldReloadAndProfile")) { + settings.shouldReloadAndProfile = map.getBoolean("shouldReloadAndProfile") + } + if (map.hasKey("recordChangeDescriptions")) { + settings.recordChangeDescriptions = map.getBoolean("recordChangeDescriptions") + } + } + + override fun getReloadAndProfileConfig(): WritableMap { + val map = Arguments.createMap() + map.putBoolean("shouldReloadAndProfile", settings.shouldReloadAndProfile) + map.putBoolean("recordChangeDescriptions", settings.recordChangeDescriptions) + return map + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt index 4bf0b77429aaa6..1a988f2afacca0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt @@ -87,10 +87,12 @@ public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : override fun getJSModule(jsInterface: Class): T? = reactHost.currentReactContext?.getJSModule(jsInterface) + @get:Deprecated("Deprecated in Java") override public val javaScriptContextHolder: JavaScriptContextHolder get() = reactHost.getJavaScriptContextHolder()!! @Suppress("INAPPLICABLE_JVM_NAME") + @get:Deprecated("Deprecated in Java") @get:JvmName("getJSCallInvokerHolder") // This is needed to keep backward compatibility override public val jsCallInvokerHolder: CallInvokerHolder get() = reactHost.getJSCallInvokerHolder()!! diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index 68c405ad02ba3a..089c0b2e523c7f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -28,6 +28,7 @@ import com.facebook.react.modules.camera.ImageStoreManager; import com.facebook.react.modules.clipboard.ClipboardModule; import com.facebook.react.modules.devloading.DevLoadingModule; +import com.facebook.react.modules.devtoolsruntimesettings.ReactDevToolsRuntimeSettingsModule; import com.facebook.react.modules.dialog.DialogModule; import com.facebook.react.modules.fresco.FrescoModule; import com.facebook.react.modules.i18nmanager.I18nManagerModule; @@ -88,6 +89,7 @@ NetworkingModule.class, PermissionsModule.class, ReactDevToolsSettingsManagerModule.class, + ReactDevToolsRuntimeSettingsModule.class, ShareModule.class, SoundManagerModule.class, StatusBarModule.class, @@ -156,6 +158,8 @@ public MainReactPackage(MainPackageConfig config) { return new WebSocketModule(context); case ReactDevToolsSettingsManagerModule.NAME: return new ReactDevToolsSettingsManagerModule(context); + case ReactDevToolsRuntimeSettingsModule.NAME: + return new ReactDevToolsRuntimeSettingsModule(context); default: return null; } @@ -302,6 +306,7 @@ private ReactModuleInfoProvider fallbackForMissingClass() { NetworkingModule.class, PermissionsModule.class, ReactDevToolsSettingsManagerModule.class, + ReactDevToolsRuntimeSettingsModule.class, ShareModule.class, StatusBarModule.class, SoundManagerModule.class, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 6b2d334835b44b..c8aae2e92f9344 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -47,7 +47,7 @@ * provides support for base view properties such as backgroundColor, opacity, etc. */ public abstract class BaseViewManager - extends ViewManager implements BaseViewManagerInterface, View.OnLayoutChangeListener { + extends ViewManager implements View.OnLayoutChangeListener { private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2; private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = (float) Math.sqrt(5); @@ -191,7 +191,6 @@ public void onLayoutChange( } } - @Override @ReactProp( name = ViewProps.BACKGROUND_COLOR, defaultInt = Color.TRANSPARENT, @@ -200,7 +199,6 @@ public void setBackgroundColor(@NonNull T view, @ColorInt int backgroundColor) { BackgroundStyleApplicator.setBackgroundColor(view, backgroundColor); } - @Override @ReactProp(name = ViewProps.FILTER, customType = "Filter") public void setFilter(@NonNull T view, @Nullable ReadableArray filter) { if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) { @@ -208,7 +206,6 @@ public void setFilter(@NonNull T view, @Nullable ReadableArray filter) { } } - @Override @ReactProp(name = ViewProps.MIX_BLEND_MODE) public void setMixBlendMode(@NonNull T view, @Nullable String mixBlendMode) { if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) { @@ -221,7 +218,6 @@ public void setMixBlendMode(@NonNull T view, @Nullable String mixBlendMode) { } } - @Override @ReactProp(name = ViewProps.TRANSFORM) public void setTransform(@NonNull T view, @Nullable ReadableArray matrix) { @Nullable ReadableArray currentTransform = (ReadableArray) view.getTag(R.id.transform); @@ -231,7 +227,6 @@ public void setTransform(@NonNull T view, @Nullable ReadableArray matrix) { } } - @Override @ReactProp(name = ViewProps.TRANSFORM_ORIGIN) public void setTransformOrigin(@NonNull T view, @Nullable ReadableArray transformOrigin) { @Nullable @@ -242,19 +237,16 @@ public void setTransformOrigin(@NonNull T view, @Nullable ReadableArray transfor } } - @Override @ReactProp(name = ViewProps.OPACITY, defaultFloat = 1.f) public void setOpacity(@NonNull T view, float opacity) { view.setAlpha(opacity); } - @Override @ReactProp(name = ViewProps.ELEVATION) public void setElevation(@NonNull T view, float elevation) { ViewCompat.setElevation(view, PixelUtil.toPixelFromDIP(elevation)); } - @Override @ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color") public void setShadowColor(@NonNull T view, int shadowColor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -263,7 +255,6 @@ public void setShadowColor(@NonNull T view, int shadowColor) { } } - @Override @ReactProp(name = ViewProps.Z_INDEX) public void setZIndex(@NonNull T view, float zIndex) { int integerZIndex = Math.round(zIndex); @@ -274,13 +265,11 @@ public void setZIndex(@NonNull T view, float zIndex) { } } - @Override @ReactProp(name = ViewProps.RENDER_TO_HARDWARE_TEXTURE) public void setRenderToHardwareTexture(@NonNull T view, boolean useHWTexture) { view.setTag(R.id.use_hardware_layer, useHWTexture); } - @Override @ReactProp(name = ViewProps.TEST_ID) public void setTestId(@NonNull T view, @Nullable String testId) { view.setTag(R.id.react_test_id, testId); @@ -289,14 +278,12 @@ public void setTestId(@NonNull T view, @Nullable String testId) { view.setTag(testId); } - @Override @ReactProp(name = ViewProps.NATIVE_ID) public void setNativeId(@NonNull T view, @Nullable String nativeId) { view.setTag(R.id.view_tag_native_id, nativeId); ReactFindViewUtil.notifyViewRendered(view); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_LABELLED_BY) public void setAccessibilityLabelledBy(@NonNull T view, @Nullable Dynamic nativeId) { if (nativeId.isNull()) { @@ -311,21 +298,18 @@ public void setAccessibilityLabelledBy(@NonNull T view, @Nullable Dynamic native } } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_LABEL) public void setAccessibilityLabel(@NonNull T view, @Nullable String accessibilityLabel) { view.setTag(R.id.accessibility_label, accessibilityLabel); updateViewContentDescription(view); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_HINT) public void setAccessibilityHint(@NonNull T view, @Nullable String accessibilityHint) { view.setTag(R.id.accessibility_hint, accessibilityHint); updateViewContentDescription(view); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_ROLE) public void setAccessibilityRole(@NonNull T view, @Nullable String accessibilityRole) { if (accessibilityRole == null) { @@ -335,21 +319,18 @@ public void setAccessibilityRole(@NonNull T view, @Nullable String accessibility } } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_COLLECTION) public void setAccessibilityCollection( @NonNull T view, @Nullable ReadableMap accessibilityCollection) { view.setTag(R.id.accessibility_collection, accessibilityCollection); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_COLLECTION_ITEM) public void setAccessibilityCollectionItem( @NonNull T view, @Nullable ReadableMap accessibilityCollectionItem) { view.setTag(R.id.accessibility_collection_item, accessibilityCollectionItem); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_STATE) public void setViewState(@NonNull T view, @Nullable ReadableMap accessibilityState) { if (accessibilityState == null) { @@ -437,7 +418,6 @@ private void updateViewContentDescription(@NonNull T view) { } } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_ACTIONS) public void setAccessibilityActions(T view, ReadableArray accessibilityActions) { if (accessibilityActions == null) { @@ -460,7 +440,6 @@ public void setAccessibilityValue(T view, ReadableMap accessibilityValue) { } } - @Override @ReactProp(name = ViewProps.IMPORTANT_FOR_ACCESSIBILITY) public void setImportantForAccessibility( @NonNull T view, @Nullable String importantForAccessibility) { @@ -476,7 +455,6 @@ public void setImportantForAccessibility( } } - @Override @ReactProp(name = ViewProps.ROLE) public void setRole(@NonNull T view, @Nullable String role) { if (role == null) { @@ -486,42 +464,36 @@ public void setRole(@NonNull T view, @Nullable String role) { } } - @Override @Deprecated @ReactProp(name = ViewProps.ROTATION) public void setRotation(@NonNull T view, float rotation) { view.setRotation(rotation); } - @Override @Deprecated @ReactProp(name = ViewProps.SCALE_X, defaultFloat = 1f) public void setScaleX(@NonNull T view, float scaleX) { view.setScaleX(scaleX); } - @Override @Deprecated @ReactProp(name = ViewProps.SCALE_Y, defaultFloat = 1f) public void setScaleY(@NonNull T view, float scaleY) { view.setScaleY(scaleY); } - @Override @Deprecated @ReactProp(name = ViewProps.TRANSLATE_X, defaultFloat = 0f) public void setTranslateX(@NonNull T view, float translateX) { view.setTranslationX(PixelUtil.toPixelFromDIP(translateX)); } - @Override @Deprecated @ReactProp(name = ViewProps.TRANSLATE_Y, defaultFloat = 0f) public void setTranslateY(@NonNull T view, float translateY) { view.setTranslationY(PixelUtil.toPixelFromDIP(translateY)); } - @Override @ReactProp(name = ViewProps.ACCESSIBILITY_LIVE_REGION) public void setAccessibilityLiveRegion(@NonNull T view, @Nullable String liveRegion) { if (liveRegion == null || liveRegion.equals("none")) { @@ -762,27 +734,24 @@ protected void onAfterUpdateTransaction(@NonNull T view) { return eventTypeConstants; } - @Override + // TODO: These are all pretty silly, since they do nothing, support less props and shapes than + // View, and the only external usage is RNSVG which just calls the superclass ViewManager version. public void setBorderRadius(T view, float borderRadius) { logUnsupportedPropertyWarning(ViewProps.BORDER_RADIUS); } - @Override public void setBorderBottomLeftRadius(T view, float borderRadius) { logUnsupportedPropertyWarning(ViewProps.BORDER_BOTTOM_LEFT_RADIUS); } - @Override public void setBorderBottomRightRadius(T view, float borderRadius) { logUnsupportedPropertyWarning(ViewProps.BORDER_BOTTOM_RIGHT_RADIUS); } - @Override public void setBorderTopLeftRadius(T view, float borderRadius) { logUnsupportedPropertyWarning(ViewProps.BORDER_TOP_LEFT_RADIUS); } - @Override public void setBorderTopRightRadius(T view, float borderRadius) { logUnsupportedPropertyWarning(ViewProps.BORDER_TOP_RIGHT_RADIUS); } @@ -810,6 +779,11 @@ public void setOutlineWidth(T view, float width) { BackgroundStyleApplicator.setOutlineWidth(view, width); } + @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") + public void setBoxShadow(T view, @Nullable ReadableArray shadows) { + BackgroundStyleApplicator.setBoxShadow(view, shadows); + } + private void logUnsupportedPropertyWarning(String propName) { FLog.w(ReactConstants.TAG, "%s doesn't support property '%s'", getName(), propName); } @@ -969,4 +943,6 @@ public void setTouchEnd(@NonNull T view, boolean value) { public void setTouchCancel(@NonNull T view, boolean value) { // no-op, handled by JSResponder } + + // Please add new props to BaseViewManagerDelegate as well! } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.kt index 5acc194ccf3c07..0903d4ae21e1f0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.kt @@ -19,9 +19,11 @@ import com.facebook.yoga.YogaConstants * This is a base implementation of [ViewManagerDelegate] which supports setting properties that * every view should support, such as rotation, background color, etc. */ -public abstract class BaseViewManagerDelegate>( +public abstract class BaseViewManagerDelegate< + T : View, U : BaseViewManager>( @Suppress("NoHungarianNotation") @JvmField protected val mViewManager: U ) : ViewManagerDelegate { + @Suppress("DEPRECATION") override public fun setProperty(view: T, propName: String, value: Any?) { when (propName) { ViewProps.ACCESSIBILITY_ACTIONS -> @@ -40,6 +42,9 @@ public abstract class BaseViewManagerDelegate mViewManager.setAccessibilityCollectionItem(view, value as ReadableMap?) + ViewProps.ACCESSIBILITY_VALUE -> + mViewManager.setAccessibilityValue(view, value as ReadableMap?) + ViewProps.BACKGROUND_COLOR -> mViewManager.setBackgroundColor( view, if (value == null) 0 else ColorPropConverter.getColor(value, view.context)) @@ -64,8 +69,14 @@ public abstract class BaseViewManagerDelegate mViewManager.setBoxShadow(view, value as ReadableArray?) + ViewProps.ELEVATION -> mViewManager.setElevation(view, (value as Double?)?.toFloat() ?: 0.0f) + ViewProps.FILTER -> mViewManager.setFilter(view, value as ReadableArray?) + + ViewProps.MIX_BLEND_MODE -> mViewManager.setMixBlendMode(view, value as String?) + ViewProps.SHADOW_COLOR -> mViewManager.setShadowColor( view, if (value == null) 0 else ColorPropConverter.getColor(value, view.context)) @@ -82,6 +93,18 @@ public abstract class BaseViewManagerDelegate mViewManager.setOpacity(view, (value as Double?)?.toFloat() ?: 1.0f) + ViewProps.OUTLINE_COLOR -> mViewManager.setOutlineColor(view, value as Int?) + + ViewProps.OUTLINE_OFFSET -> + mViewManager.setOutlineOffset( + view, (value as Double?)?.toFloat() ?: YogaConstants.UNDEFINED) + + ViewProps.OUTLINE_STYLE -> mViewManager.setOutlineStyle(view, value as String?) + + ViewProps.OUTLINE_WIDTH -> + mViewManager.setOutlineWidth( + view, (value as Double?)?.toFloat() ?: YogaConstants.UNDEFINED) + ViewProps.RENDER_TO_HARDWARE_TEXTURE -> mViewManager.setRenderToHardwareTexture(view, value as Boolean? ?: false) @@ -101,6 +124,22 @@ public abstract class BaseViewManagerDelegate mViewManager.setZIndex(view, (value as Double?)?.toFloat() ?: 0.0f) + + // Experimental pointer events + "onPointerEnter" -> mViewManager.setPointerEnter(view, value as Boolean? ?: false) + "onPointerEnterCapture" -> + mViewManager.setPointerEnterCapture(view, value as Boolean? ?: false) + "onPointerOver" -> mViewManager.setPointerOver(view, value as Boolean? ?: false) + "onPointerOverCapture" -> mViewManager.setPointerOverCapture(view, value as Boolean? ?: false) + "onPointerOut" -> mViewManager.setPointerOut(view, value as Boolean? ?: false) + "onPointerOutCapture" -> mViewManager.setPointerOutCapture(view, value as Boolean? ?: false) + "onPointerLeave" -> mViewManager.setPointerLeave(view, value as Boolean? ?: false) + "onPointerLeaveCapture" -> + mViewManager.setPointerLeaveCapture(view, value as Boolean? ?: false) + "onPointerMove" -> mViewManager.setPointerMove(view, value as Boolean? ?: false) + "onPointerMoveCapture" -> mViewManager.setPointerMoveCapture(view, value as Boolean? ?: false) + "onClick" -> mViewManager.setClick(view, value as Boolean? ?: false) + "onClickCapture" -> mViewManager.setClickCapture(view, value as Boolean? ?: false) } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.kt deleted file mode 100644 index 8e3619e900ccb6..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerInterface.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.uimanager - -import android.view.View -import com.facebook.react.bridge.Dynamic -import com.facebook.react.bridge.ReadableArray -import com.facebook.react.bridge.ReadableMap - -/** - * This is an interface that should be implemented by view managers supporting the base view - * properties such as backgroundColor, opacity, etc. - */ -public interface BaseViewManagerInterface { - public fun setAccessibilityActions(view: T, accessibilityActions: ReadableArray?) - - public fun setAccessibilityHint(view: T, accessibilityHint: String?) - - public fun setAccessibilityLabel(view: T, accessibilityLabel: String?) - - public fun setAccessibilityLiveRegion(view: T, liveRegion: String?) - - public fun setAccessibilityRole(view: T, accessibilityRole: String?) - - public fun setAccessibilityCollection(view: T, accessibilityCollection: ReadableMap?) - - public fun setAccessibilityCollectionItem(view: T, accessibilityCollectionItem: ReadableMap?) - - public fun setViewState(view: T, accessibilityState: ReadableMap?) - - public fun setBackgroundColor(view: T, backgroundColor: Int) - - public fun setBorderRadius(view: T, borderRadius: Float) - - public fun setBorderBottomLeftRadius(view: T, borderRadius: Float) - - public fun setBorderBottomRightRadius(view: T, borderRadius: Float) - - public fun setBorderTopLeftRadius(view: T, borderRadius: Float) - - public fun setBorderTopRightRadius(view: T, borderRadius: Float) - - public fun setElevation(view: T, elevation: Float) - - public fun setFilter(view: T, filter: ReadableArray) - - public fun setMixBlendMode(view: T, setMixBlendMode: String) - - public fun setShadowColor(view: T, shadowColor: Int) - - public fun setImportantForAccessibility(view: T, importantForAccessibility: String?) - - public fun setRole(view: T, role: String?) - - public fun setNativeId(view: T, nativeId: String?) - - public fun setAccessibilityLabelledBy(view: T, nativeId: Dynamic?) - - public fun setOpacity(view: T, opacity: Float) - - public fun setRenderToHardwareTexture(view: T, useHWTexture: Boolean) - - public fun setRotation(view: T, rotation: Float) - - public fun setScaleX(view: T, scaleX: Float) - - public fun setScaleY(view: T, scaleY: Float) - - public fun setTestId(view: T, testId: String?) - - public fun setTransform(view: T, matrix: ReadableArray?) - - public fun setTransformOrigin(view: T, transformOrigin: ReadableArray?) - - public fun setTranslateX(view: T, translateX: Float) - - public fun setTranslateY(view: T, translateY: Float) - - public fun setZIndex(view: T, zIndex: Float) -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index 34ccb6f05bd9b8..b5b1151317ed6e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -674,6 +674,9 @@ public synchronized void removeRootView(int rootViewTag) { View rootView = mTagsToViews.get(rootViewTag); dropView(rootView); mRootTags.delete(rootViewTag); + if (rootView != null) { + rootView.setId(View.NO_ID); + } } /** diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt index f29f148efdb2c0..a241468a7149d3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt @@ -85,6 +85,7 @@ internal class InsetBoxShadowDrawable( invalidateSelf() } + @Deprecated("Deprecated in Java") override fun getOpacity(): Int { val alpha = Color.alpha(shadowColor) return if (alpha == 0) PixelFormat.TRANSPARENT diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutlineDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutlineDrawable.kt index cb7e2f10494b00..fcc6079f7e3e3b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutlineDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutlineDrawable.kt @@ -105,6 +105,7 @@ internal class OutlineDrawable( invalidateSelf() } + @Deprecated("Deprecated in Java") override fun getOpacity(): Int = ((outlinePaint.alpha / 255f) / (Color.alpha(outlineColor) / 255f) * 255f).roundToInt() diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt index 9df839902af88e..3d4a433d908dda 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt @@ -73,6 +73,7 @@ internal class OutsetBoxShadowDrawable( invalidateSelf() } + @Deprecated("Deprecated in Java") override fun getOpacity(): Int { val alpha = Color.alpha(shadowColor) return if (alpha == 0) PixelFormat.TRANSPARENT diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.kt index e25e980eb40931..2c0229f1ccf816 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.kt @@ -34,6 +34,7 @@ public class BlackHoleEventDispatcher private constructor() : EventDispatcher { listener: BatchEventDispatchedListener ): Unit = Unit + @Deprecated("Deprecated in Java") @Suppress("DEPRECATION") public override fun registerEventEmitter( uiManagerType: Int, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java index b4ed223d41ed0d..ab106403070db8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/PointerEvent.java @@ -278,12 +278,12 @@ private List createPointersEventData() { int activePointerIndex = mMotionEvent.getActionIndex(); List pointersEventData = null; switch (mEventName) { - // Cases where all pointer info is relevant + // Cases where all pointer info is relevant case PointerEventHelper.POINTER_MOVE: case PointerEventHelper.POINTER_CANCEL: pointersEventData = createW3CPointerEvents(); break; - // Cases where only the "active" pointer info is relevant + // Cases where only the "active" pointer info is relevant case PointerEventHelper.POINTER_ENTER: case PointerEventHelper.POINTER_DOWN: case PointerEventHelper.POINTER_UP: diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.kt index a62acf4be2b42c..8feb8b3600f26d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchEvent.kt @@ -110,6 +110,7 @@ public class TouchEvent private constructor() : Event() { override fun getCoalescingKey(): Short = coalescingKey + @Deprecated("Deprecated in Java") override fun dispatch(rctEventEmitter: RCTEventEmitter) { if (verifyMotionEvent()) { TouchesHelper.sendTouchesLegacy(rctEventEmitter, this) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageDownloadListener.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageDownloadListener.kt index caf3f3eebe2bfc..a6cf0551fe8cbb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageDownloadListener.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageDownloadListener.kt @@ -44,7 +44,7 @@ internal open class ReactImageDownloadListener : override fun setColorFilter(colorFilter: ColorFilter?) = Unit - override fun getOpacity(): Int = PixelFormat.OPAQUE + @Deprecated("Deprecated in Java") override fun getOpacity(): Int = PixelFormat.OPAQUE } companion object { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt index d30da39b08d0d3..77e1d986655597 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt @@ -231,11 +231,6 @@ public constructor( } } - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public fun setBoxShadow(view: ReactImageView, shadows: ReadableArray?): Unit { - BackgroundStyleApplicator.setBoxShadow(view, shadows) - } - public override fun getExportedCustomDirectEventTypeConstants(): MutableMap = (super.getExportedCustomDirectEventTypeConstants() ?: mutableMapOf()).apply { put( diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt index 5ed3c15d781e29..6a2a6c922ed0aa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt @@ -338,7 +338,16 @@ public class ReactImageView( public override fun onDraw(canvas: Canvas) { BackgroundStyleApplicator.clipToPaddingBox(this, canvas) - super.onDraw(canvas) + try { + super.onDraw(canvas) + } catch (e: RuntimeException) { + // Only provide updates if downloadListener is set (shouldNotify is true) + if (downloadListener != null) { + val eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id) + eventDispatcher?.dispatchEvent(createErrorEvent(UIManagerHelper.getSurfaceId(this), id, e)) + } + } } public fun maybeUpdateView() { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 759c7c88168bc9..96b45c1316c70f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -360,9 +360,4 @@ public void setScrollEventThrottle(ReactHorizontalScrollView view, int scrollEve public void setHorizontal(ReactHorizontalScrollView view, boolean horizontal) { // Do Nothing: Align with static ViewConfigs } - - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public void setBoxShadow(ReactHorizontalScrollView view, @Nullable ReadableArray shadows) { - BackgroundStyleApplicator.setBoxShadow(view, shadows); - } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 6823251974f5df..055679d6cd7ac6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -342,11 +342,6 @@ public void setMaintainVisibleContentPosition(ReactScrollView view, ReadableMap } } - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public void setBoxShadow(ReactScrollView view, @Nullable ReadableArray shadows) { - BackgroundStyleApplicator.setBoxShadow(view, shadows); - } - @Override public @Nullable Object updateState( ReactScrollView view, ReactStylesDiffMap props, StateWrapper stateWrapper) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.kt index 1f9fafd977dd30..73b8f444f6bf6e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ScrollEvent.kt @@ -7,6 +7,7 @@ package com.facebook.react.views.scroll +import android.os.SystemClock import androidx.core.util.Pools.SynchronizedPool import com.facebook.infer.annotation.Assertions import com.facebook.react.bridge.Arguments @@ -27,6 +28,7 @@ public class ScrollEvent private constructor() : Event() { private var scrollViewWidth = 0 private var scrollViewHeight = 0 private var scrollEventType: ScrollEventType? = null + private var timestamp: Long = 0 private var experimental_isSynchronous = false override fun onDispose() { @@ -63,6 +65,7 @@ public class ScrollEvent private constructor() : Event() { this.contentHeight = contentHeight this.scrollViewWidth = scrollViewWidth this.scrollViewHeight = scrollViewHeight + this.timestamp = SystemClock.uptimeMillis() this.experimental_isSynchronous = experimental_isSynchronous } @@ -100,6 +103,7 @@ public class ScrollEvent private constructor() : Event() { event.putMap("layoutMeasurement", layoutMeasurement) event.putMap("velocity", velocity) event.putInt("target", viewTag) + event.putDouble("timestamp", timestamp.toDouble()) event.putBoolean("responderIgnoreScroll", true) return event } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java index 730af692e62e66..ca180d73ff6d0b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java @@ -15,7 +15,6 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.BackgroundStyleApplicator; import com.facebook.react.uimanager.BaseViewManager; @@ -231,9 +230,4 @@ public void setDataDetectorType(ReactTextView view, @Nullable String type) { public void setNotifyOnInlineViewLayout(ReactTextView view, boolean notifyOnInlineViewLayout) { view.setNotifyOnInlineViewLayout(notifyOnInlineViewLayout); } - - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public void setBoxShadow(ReactTextView view, @Nullable ReadableArray shadows) { - BackgroundStyleApplicator.setBoxShadow(view, shadows); - } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index ebd62e6dfa005a..87da1f00fc2d17 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -1053,12 +1053,18 @@ public void onStartTemporaryDetach() { public void onAttachedToWindow() { super.onAttachedToWindow(); + int selectionStart = getSelectionStart(); + int selectionEnd = getSelectionEnd(); + // Used to ensure that text is selectable inside of removeClippedSubviews // See https://github.com/facebook/react-native/issues/6805 for original // fix that was ported to here. super.setTextIsSelectable(true); + // Restore the selection since `setTextIsSelectable` changed it. + setSelection(selectionStart, selectionEnd); + if (mContainsImages) { Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index d3f462be077b1c..3d53a038396f38 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -1029,11 +1029,6 @@ public void setOverflow(ReactEditText view, @Nullable String overflow) { view.setOverflow(overflow); } - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public void setBoxShadow(ReactEditText view, @Nullable ReadableArray shadows) { - BackgroundStyleApplicator.setBoxShadow(view, shadows); - } - @Override protected void onAfterUpdateTransaction(ReactEditText view) { super.onAfterUpdateTransaction(view); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index c65314e926e59e..8277b71a9f010d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -90,7 +90,7 @@ public class ReactViewGroup extends ViewGroup */ private static final class ChildrenLayoutChangeListener implements View.OnLayoutChangeListener { - private final ReactViewGroup mParent; + @Nullable private ReactViewGroup mParent; private ChildrenLayoutChangeListener(ReactViewGroup parent) { mParent = parent; @@ -107,12 +107,18 @@ public void onLayoutChange( int oldTop, int oldRight, int oldBottom) { - if (mParent.getRemoveClippedSubviews()) { + if (mParent != null && mParent.getRemoveClippedSubviews()) { mParent.updateSubviewClipStatus(v); } } + + public void shutdown() { + mParent = null; + } } + private int mRecycleCount = 0; + // Following properties are here to support the option {@code removeClippedSubviews}. This is a // temporary optimization/hack that is mainly applicable to the large list of images. The way // it's implemented is that we store an additional array of children in view node. We selectively @@ -145,7 +151,7 @@ public ReactViewGroup(Context context) { /** * Set all default values here as opposed to in the constructor or field defaults. It is important * that these properties are set during the constructor, but also on-demand whenever an existing - * ReactTextView is recycled. + * ReactViewGroup is recycled. */ private void initView() { setClipChildren(false); @@ -168,8 +174,10 @@ private void initView() { } /* package */ void recycleView() { + mRecycleCount++; // Remove dangling listeners if (mAllChildren != null && mChildrenLayoutChangeListener != null) { + mChildrenLayoutChangeListener.shutdown(); for (int i = 0; i < mAllChildrenCount; i++) { mAllChildren[i].removeOnLayoutChangeListener(mChildrenLayoutChangeListener); } @@ -403,7 +411,22 @@ private void updateClippingToRect(Rect clippingRect) { Assertions.assertNotNull(mAllChildren); int clippedSoFar = 0; for (int i = 0; i < mAllChildrenCount; i++) { - updateSubviewClipStatus(clippingRect, i, clippedSoFar); + try { + updateSubviewClipStatus(clippingRect, i, clippedSoFar); + } catch (IndexOutOfBoundsException e) { + throw new IllegalStateException( + "Invalid clipping state. i=" + + i + + " clippedSoFar=" + + clippedSoFar + + " count=" + + getChildCount() + + " allChildrenCount=" + + mAllChildrenCount + + " recycleCount=" + + mRecycleCount, + e); + } if (mAllChildren[i].getParent() == null) { clippedSoFar++; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java deleted file mode 100644 index f811115c5d338d..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.view; - -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.DynamicFromObject; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.BackgroundStyleApplicator; -import com.facebook.react.uimanager.LengthPercentage; -import com.facebook.react.uimanager.LengthPercentageType; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.PointerEvents; -import com.facebook.react.uimanager.Spacing; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.annotations.ReactPropGroup; -import com.facebook.react.uimanager.common.UIManagerType; -import com.facebook.react.uimanager.common.ViewUtil; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.uimanager.style.BackgroundImageLayer; -import com.facebook.react.uimanager.style.BorderRadiusProp; -import com.facebook.react.uimanager.style.BorderStyle; -import com.facebook.react.uimanager.style.LogicalEdge; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** View manager for AndroidViews (plain React Views). */ -@ReactModule(name = ReactViewManager.REACT_CLASS) -@Nullsafe(Nullsafe.Mode.LOCAL) -public class ReactViewManager extends ReactClippingViewManager { - - @VisibleForTesting public static final String REACT_CLASS = ViewProps.VIEW_CLASS_NAME; - - private static final int[] SPACING_TYPES = { - Spacing.ALL, - Spacing.LEFT, - Spacing.RIGHT, - Spacing.TOP, - Spacing.BOTTOM, - Spacing.START, - Spacing.END, - Spacing.BLOCK, - Spacing.BLOCK_END, - Spacing.BLOCK_START - }; - private static final int CMD_HOTSPOT_UPDATE = 1; - private static final int CMD_SET_PRESSED = 2; - private static final String HOTSPOT_UPDATE_KEY = "hotspotUpdate"; - - public ReactViewManager() { - super(); - - setupViewRecycling(); - } - - @Override - protected @Nullable ReactViewGroup prepareToRecycleView( - ThemedReactContext reactContext, ReactViewGroup view) { - // BaseViewManager - ReactViewGroup preparedView = super.prepareToRecycleView(reactContext, view); - if (preparedView != null) { - preparedView.recycleView(); - } - return view; - } - - @ReactProp(name = "accessible") - public void setAccessible(ReactViewGroup view, boolean accessible) { - view.setFocusable(accessible); - } - - @ReactProp(name = "hasTVPreferredFocus") - public void setTVPreferredFocus(ReactViewGroup view, boolean hasTVPreferredFocus) { - if (hasTVPreferredFocus) { - view.setFocusable(true); - view.setFocusableInTouchMode(true); - view.requestFocus(); - } - } - - @ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage") - public void setBackgroundImage(ReactViewGroup view, @Nullable ReadableArray backgroundImage) { - if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) { - if (backgroundImage != null && backgroundImage.size() > 0) { - List backgroundImageLayers = new ArrayList<>(backgroundImage.size()); - for (int i = 0; i < backgroundImage.size(); i++) { - ReadableMap backgroundImageMap = backgroundImage.getMap(i); - BackgroundImageLayer layer = - new BackgroundImageLayer(backgroundImageMap, view.getContext()); - backgroundImageLayers.add(layer); - } - BackgroundStyleApplicator.setBackgroundImage(view, backgroundImageLayers); - } else { - BackgroundStyleApplicator.setBackgroundImage(view, null); - } - } - } - - @ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID) - public void nextFocusDown(ReactViewGroup view, int viewId) { - view.setNextFocusDownId(viewId); - } - - @ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID) - public void nextFocusForward(ReactViewGroup view, int viewId) { - view.setNextFocusForwardId(viewId); - } - - @ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID) - public void nextFocusLeft(ReactViewGroup view, int viewId) { - view.setNextFocusLeftId(viewId); - } - - @ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID) - public void nextFocusRight(ReactViewGroup view, int viewId) { - view.setNextFocusRightId(viewId); - } - - @ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID) - public void nextFocusUp(ReactViewGroup view, int viewId) { - view.setNextFocusUpId(viewId); - } - - @ReactPropGroup( - names = { - ViewProps.BORDER_RADIUS, - ViewProps.BORDER_TOP_LEFT_RADIUS, - ViewProps.BORDER_TOP_RIGHT_RADIUS, - ViewProps.BORDER_BOTTOM_RIGHT_RADIUS, - ViewProps.BORDER_BOTTOM_LEFT_RADIUS, - ViewProps.BORDER_TOP_START_RADIUS, - ViewProps.BORDER_TOP_END_RADIUS, - ViewProps.BORDER_BOTTOM_START_RADIUS, - ViewProps.BORDER_BOTTOM_END_RADIUS, - ViewProps.BORDER_END_END_RADIUS, - ViewProps.BORDER_END_START_RADIUS, - ViewProps.BORDER_START_END_RADIUS, - ViewProps.BORDER_START_START_RADIUS, - }) - public void setBorderRadius(ReactViewGroup view, int index, Dynamic rawBorderRadius) { - @Nullable LengthPercentage borderRadius = LengthPercentage.setFromDynamic(rawBorderRadius); - - // We do not support percentage border radii on Paper in order to be consistent with iOS (to - // avoid developer surprise if it works on one platform but not another). - if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC - && borderRadius != null - && borderRadius.getType() == LengthPercentageType.PERCENT) { - borderRadius = null; - } - - BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], borderRadius); - } - - /** - * @deprecated Use {@link #setBorderRadius(ReactViewGroup, int, Dynamic)} instead. - */ - @Deprecated(since = "0.75.0", forRemoval = true) - public void setBorderRadius(ReactViewGroup view, int index, float borderRadius) { - setBorderRadius(view, index, new DynamicFromObject(borderRadius)); - } - - @ReactProp(name = "borderStyle") - public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) { - @Nullable - BorderStyle parsedBorderStyle = - borderStyle == null ? null : BorderStyle.fromString(borderStyle); - BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle); - } - - @ReactProp(name = "hitSlop") - public void setHitSlop(final ReactViewGroup view, Dynamic hitSlop) { - switch (hitSlop.getType()) { - case Map: - ReadableMap hitSlopMap = hitSlop.asMap(); - view.setHitSlopRect( - new Rect( - hitSlopMap.hasKey("left") - ? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("left")) - : 0, - hitSlopMap.hasKey("top") - ? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("top")) - : 0, - hitSlopMap.hasKey("right") - ? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("right")) - : 0, - hitSlopMap.hasKey("bottom") - ? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("bottom")) - : 0)); - break; - case Number: - int hitSlopValue = (int) PixelUtil.toPixelFromDIP(hitSlop.asDouble()); - view.setHitSlopRect(new Rect(hitSlopValue, hitSlopValue, hitSlopValue, hitSlopValue)); - break; - default: - FLog.w(ReactConstants.TAG, "Invalid type for 'hitSlop' value " + hitSlop.getType()); - /* falls through */ - case Null: - view.setHitSlopRect(null); - break; - } - } - - @ReactProp(name = ViewProps.POINTER_EVENTS) - public void setPointerEvents(ReactViewGroup view, @Nullable String pointerEventsStr) { - view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr)); - } - - @ReactProp(name = "nativeBackgroundAndroid") - public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) { - Drawable background; - if (bg != null) { - background = ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg); - } else { - background = null; - } - BackgroundStyleApplicator.setFeedbackUnderlay(view, background); - } - - @ReactProp(name = "nativeForegroundAndroid") - public void setNativeForeground(ReactViewGroup view, @Nullable ReadableMap fg) { - view.setForeground( - fg == null - ? null - : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), fg)); - } - - @ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING) - public void setNeedsOffscreenAlphaCompositing( - ReactViewGroup view, boolean needsOffscreenAlphaCompositing) { - view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing); - } - - @ReactPropGroup( - names = { - ViewProps.BORDER_WIDTH, - ViewProps.BORDER_LEFT_WIDTH, - ViewProps.BORDER_RIGHT_WIDTH, - ViewProps.BORDER_TOP_WIDTH, - ViewProps.BORDER_BOTTOM_WIDTH, - ViewProps.BORDER_START_WIDTH, - ViewProps.BORDER_END_WIDTH, - }, - defaultFloat = Float.NaN) - public void setBorderWidth(ReactViewGroup view, int index, float width) { - BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width); - } - - @ReactPropGroup( - names = { - ViewProps.BORDER_COLOR, - ViewProps.BORDER_LEFT_COLOR, - ViewProps.BORDER_RIGHT_COLOR, - ViewProps.BORDER_TOP_COLOR, - ViewProps.BORDER_BOTTOM_COLOR, - ViewProps.BORDER_START_COLOR, - ViewProps.BORDER_END_COLOR, - ViewProps.BORDER_BLOCK_COLOR, - ViewProps.BORDER_BLOCK_END_COLOR, - ViewProps.BORDER_BLOCK_START_COLOR - }, - customType = "Color") - public void setBorderColor(ReactViewGroup view, int index, @Nullable Integer color) { - BackgroundStyleApplicator.setBorderColor( - view, LogicalEdge.fromSpacingType(SPACING_TYPES[index]), color); - } - - @ReactProp(name = ViewProps.COLLAPSABLE) - public void setCollapsable(ReactViewGroup view, boolean collapsable) { - // no-op: it's here only so that "collapsable" property is exported to JS. The value is actually - // handled in NativeViewHierarchyOptimizer - } - - @ReactProp(name = ViewProps.COLLAPSABLE_CHILDREN) - public void setCollapsableChildren(ReactViewGroup view, boolean collapsableChildren) { - // no-op: it's here only so that "collapsableChildren" property is exported to JS. - } - - @ReactProp(name = "focusable") - public void setFocusable(final ReactViewGroup view, boolean focusable) { - if (focusable) { - view.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - final EventDispatcher mEventDispatcher = - UIManagerHelper.getEventDispatcherForReactTag( - (ReactContext) view.getContext(), view.getId()); - if (mEventDispatcher == null) { - return; - } - mEventDispatcher.dispatchEvent( - new ViewGroupClickEvent( - UIManagerHelper.getSurfaceId(view.getContext()), view.getId())); - } - }); - - // Clickable elements are focusable. On API 26, this is taken care by setClickable. - // Explicitly calling setFocusable here for backward compatibility. - view.setFocusable(true /*isFocusable*/); - } else { - view.setOnClickListener(null); - view.setClickable(false); - // Don't set view.setFocusable(false) because we might still want it to be focusable for - // accessibility reasons - } - } - - @ReactProp(name = ViewProps.OVERFLOW) - public void setOverflow(ReactViewGroup view, String overflow) { - view.setOverflow(overflow); - } - - @ReactProp(name = "backfaceVisibility") - public void setBackfaceVisibility(ReactViewGroup view, String backfaceVisibility) { - view.setBackfaceVisibility(backfaceVisibility); - } - - @Override - public void setOpacity(ReactViewGroup view, float opacity) { - view.setOpacityIfPossible(opacity); - } - - @Override - protected void setTransformProperty( - ReactViewGroup view, - @Nullable ReadableArray transforms, - @Nullable ReadableArray transformOrigin) { - super.setTransformProperty(view, transforms, transformOrigin); - view.setBackfaceVisibilityDependantOpacity(); - } - - @ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow") - public void setBoxShadow(ReactViewGroup view, @Nullable ReadableArray shadows) { - BackgroundStyleApplicator.setBoxShadow(view, shadows); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - public ReactViewGroup createViewInstance(ThemedReactContext context) { - return new ReactViewGroup(context); - } - - @Override - public Map getCommandsMap() { - return MapBuilder.of(HOTSPOT_UPDATE_KEY, CMD_HOTSPOT_UPDATE, "setPressed", CMD_SET_PRESSED); - } - - @Override - public void receiveCommand(ReactViewGroup root, int commandId, @Nullable ReadableArray args) { - switch (commandId) { - case CMD_HOTSPOT_UPDATE: - { - handleHotspotUpdate(root, args); - break; - } - case CMD_SET_PRESSED: - { - handleSetPressed(root, args); - break; - } - } - } - - @Override - public void receiveCommand(ReactViewGroup root, String commandId, @Nullable ReadableArray args) { - switch (commandId) { - case HOTSPOT_UPDATE_KEY: - { - handleHotspotUpdate(root, args); - break; - } - case "setPressed": - { - handleSetPressed(root, args); - break; - } - } - } - - private void handleSetPressed(ReactViewGroup root, @Nullable ReadableArray args) { - if (args == null || args.size() != 1) { - throw new JSApplicationIllegalArgumentException( - "Illegal number of arguments for 'setPressed' command"); - } - root.setPressed(args.getBoolean(0)); - } - - private void handleHotspotUpdate(ReactViewGroup root, @Nullable ReadableArray args) { - if (args == null || args.size() != 2) { - throw new JSApplicationIllegalArgumentException( - "Illegal number of arguments for 'updateHotspot' command"); - } - - float x = PixelUtil.toPixelFromDIP(args.getDouble(0)); - float y = PixelUtil.toPixelFromDIP(args.getDouble(1)); - root.drawableHotspotChanged(x, y); - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt new file mode 100644 index 00000000000000..9c87b54b06c579 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt @@ -0,0 +1,372 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.view + +import android.graphics.Rect +import android.view.View +import com.facebook.common.logging.FLog +import com.facebook.react.bridge.Dynamic +import com.facebook.react.bridge.DynamicFromObject +import com.facebook.react.bridge.JSApplicationIllegalArgumentException +import com.facebook.react.bridge.ReactContext +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.module.annotations.ReactModule +import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.LengthPercentage +import com.facebook.react.uimanager.LengthPercentageType +import com.facebook.react.uimanager.PixelUtil.dpToPx +import com.facebook.react.uimanager.PointerEvents +import com.facebook.react.uimanager.Spacing +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.ViewProps +import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.uimanager.annotations.ReactPropGroup +import com.facebook.react.uimanager.common.UIManagerType +import com.facebook.react.uimanager.common.ViewUtil +import com.facebook.react.uimanager.style.BackgroundImageLayer +import com.facebook.react.uimanager.style.BorderRadiusProp +import com.facebook.react.uimanager.style.BorderStyle +import com.facebook.react.uimanager.style.LogicalEdge + +/** View manager for AndroidViews (plain React Views). */ +@ReactModule(name = ReactViewManager.REACT_CLASS) +public open class ReactViewManager : ReactClippingViewManager() { + + public companion object { + public const val REACT_CLASS: String = ViewProps.VIEW_CLASS_NAME + + private val SPACING_TYPES = + intArrayOf( + Spacing.ALL, + Spacing.LEFT, + Spacing.RIGHT, + Spacing.TOP, + Spacing.BOTTOM, + Spacing.START, + Spacing.END, + Spacing.BLOCK, + Spacing.BLOCK_END, + Spacing.BLOCK_START, + ) + private const val CMD_HOTSPOT_UPDATE = 1 + private const val CMD_SET_PRESSED = 2 + private const val HOTSPOT_UPDATE_KEY = "hotspotUpdate" + } + + init { + setupViewRecycling() + } + + override fun prepareToRecycleView( + reactContext: ThemedReactContext, + view: ReactViewGroup + ): ReactViewGroup { + // BaseViewManager + val preparedView = super.prepareToRecycleView(reactContext, view) + preparedView?.recycleView() + return view + } + + @ReactProp(name = "accessible") + public open fun setAccessible(view: ReactViewGroup, accessible: Boolean) { + view.isFocusable = accessible + } + + @ReactProp(name = "hasTVPreferredFocus") + public open fun setTVPreferredFocus(view: ReactViewGroup, hasTVPreferredFocus: Boolean) { + if (hasTVPreferredFocus) { + view.isFocusable = true + view.isFocusableInTouchMode = true + view.requestFocus() + } + } + + @ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage") + public open fun setBackgroundImage(view: ReactViewGroup, backgroundImage: ReadableArray?) { + if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) { + if (backgroundImage != null && backgroundImage.size() > 0) { + val backgroundImageLayers = ArrayList(backgroundImage.size()) + for (i in 0 until backgroundImage.size()) { + val backgroundImageMap = backgroundImage.getMap(i) + val layer = BackgroundImageLayer(backgroundImageMap, view.context) + backgroundImageLayers.add(layer) + } + BackgroundStyleApplicator.setBackgroundImage(view, backgroundImageLayers) + } else { + BackgroundStyleApplicator.setBackgroundImage(view, null) + } + } + } + + @ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID) + public open fun nextFocusDown(view: ReactViewGroup, viewId: Int) { + view.nextFocusDownId = viewId + } + + @ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID) + public open fun nextFocusForward(view: ReactViewGroup, viewId: Int) { + view.nextFocusForwardId = viewId + } + + @ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID) + public open fun nextFocusLeft(view: ReactViewGroup, viewId: Int) { + view.nextFocusLeftId = viewId + } + + @ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID) + public open fun nextFocusRight(view: ReactViewGroup, viewId: Int) { + view.nextFocusRightId = viewId + } + + @ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID) + public open fun nextFocusUp(view: ReactViewGroup, viewId: Int) { + view.nextFocusUpId = viewId + } + + @ReactPropGroup( + names = + [ + ViewProps.BORDER_RADIUS, + ViewProps.BORDER_TOP_LEFT_RADIUS, + ViewProps.BORDER_TOP_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_LEFT_RADIUS, + ViewProps.BORDER_TOP_START_RADIUS, + ViewProps.BORDER_TOP_END_RADIUS, + ViewProps.BORDER_BOTTOM_START_RADIUS, + ViewProps.BORDER_BOTTOM_END_RADIUS, + ViewProps.BORDER_END_END_RADIUS, + ViewProps.BORDER_END_START_RADIUS, + ViewProps.BORDER_START_END_RADIUS, + ViewProps.BORDER_START_START_RADIUS, + ]) + public open fun setBorderRadius(view: ReactViewGroup, index: Int, rawBorderRadius: Dynamic) { + var borderRadius = LengthPercentage.setFromDynamic(rawBorderRadius) + + // We do not support percentage border radii on Paper in order to be consistent with iOS (to + // avoid developer surprise if it works on one platform but not another). + if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC && + borderRadius != null && + borderRadius.type == LengthPercentageType.PERCENT) { + borderRadius = null + } + + BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], borderRadius) + } + + @Deprecated( + "Don't use setBorderRadius(view, int, Float) as it was deprecated in React Native 0.75.0.", + ReplaceWith("setBorderRadius(view, index, DynamicFromObject(borderRadius)")) + public open fun setBorderRadius(view: ReactViewGroup, index: Int, borderRadius: Float) { + setBorderRadius(view, index, DynamicFromObject(borderRadius)) + } + + @ReactProp(name = "borderStyle") + public open fun setBorderStyle(view: ReactViewGroup, borderStyle: String?) { + val parsedBorderStyle = if (borderStyle == null) null else BorderStyle.fromString(borderStyle) + BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle) + } + + @ReactProp(name = "hitSlop") + public open fun setHitSlop(view: ReactViewGroup, hitSlop: Dynamic) { + when (hitSlop.type) { + ReadableType.Map -> { + val hitSlopMap = hitSlop.asMap() + view.setHitSlopRect( + Rect( + hitSlopMap.px("left"), + hitSlopMap.px("top"), + hitSlopMap.px("right"), + hitSlopMap.px("bottom"), + )) + } + + ReadableType.Number -> { + val hitSlopValue = hitSlop.asDouble().dpToPx().toInt() + view.setHitSlopRect(Rect(hitSlopValue, hitSlopValue, hitSlopValue, hitSlopValue)) + } + + ReadableType.Null -> view.setHitSlopRect(null) + else -> { + FLog.w(ReactConstants.TAG, "Invalid type for 'hitSlop' value ${hitSlop.type}") + view.setHitSlopRect(null) + } + } + } + + private fun ReadableMap.px(key: String) = if (hasKey(key)) getDouble(key).dpToPx().toInt() else 0 + + @ReactProp(name = ViewProps.POINTER_EVENTS) + public open fun setPointerEvents(view: ReactViewGroup, pointerEventsStr: String?) { + view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr) + } + + @ReactProp(name = "nativeBackgroundAndroid") + public open fun setNativeBackground(view: ReactViewGroup, background: ReadableMap?) { + val bg = + background?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) } + BackgroundStyleApplicator.setFeedbackUnderlay(view, bg) + } + + @ReactProp(name = "nativeForegroundAndroid") + public open fun setNativeForeground(view: ReactViewGroup, foreground: ReadableMap?) { + view.foreground = + foreground?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) } + } + + @ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING) + public open fun setNeedsOffscreenAlphaCompositing( + view: ReactViewGroup, + needsOffscreenAlphaCompositing: Boolean + ) { + view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing) + } + + @ReactPropGroup( + names = + [ + ViewProps.BORDER_WIDTH, + ViewProps.BORDER_LEFT_WIDTH, + ViewProps.BORDER_RIGHT_WIDTH, + ViewProps.BORDER_TOP_WIDTH, + ViewProps.BORDER_BOTTOM_WIDTH, + ViewProps.BORDER_START_WIDTH, + ViewProps.BORDER_END_WIDTH, + ], + defaultFloat = Float.NaN) + public open fun setBorderWidth(view: ReactViewGroup, index: Int, width: Float) { + BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width) + } + + @ReactPropGroup( + names = + [ + ViewProps.BORDER_COLOR, + ViewProps.BORDER_LEFT_COLOR, + ViewProps.BORDER_RIGHT_COLOR, + ViewProps.BORDER_TOP_COLOR, + ViewProps.BORDER_BOTTOM_COLOR, + ViewProps.BORDER_START_COLOR, + ViewProps.BORDER_END_COLOR, + ViewProps.BORDER_BLOCK_COLOR, + ViewProps.BORDER_BLOCK_END_COLOR, + ViewProps.BORDER_BLOCK_START_COLOR, + ], + customType = "Color") + public open fun setBorderColor(view: ReactViewGroup, index: Int, color: Int?) { + BackgroundStyleApplicator.setBorderColor( + view, LogicalEdge.fromSpacingType(SPACING_TYPES[index]), color) + } + + @ReactProp(name = ViewProps.COLLAPSABLE) + @Suppress("UNUSED_PARAMETER") + public open fun setCollapsable(view: ReactViewGroup, collapsable: Boolean) { + // no-op: it's here only so that "collapsable" property is exported to JS. The value is actually + // handled in NativeViewHierarchyOptimizer + } + + @ReactProp(name = ViewProps.COLLAPSABLE_CHILDREN) + @Suppress("UNUSED_PARAMETER") + public open fun setCollapsableChildren(view: ReactViewGroup, collapsableChildren: Boolean) { + // no-op: it's here only so that "collapsableChildren" property is exported to JS. + } + + @ReactProp(name = "focusable") + public open fun setFocusable(view: ReactViewGroup, focusable: Boolean) { + if (focusable) { + view.setOnClickListener { + val eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag((view.context as ReactContext), view.id) + eventDispatcher?.dispatchEvent( + ViewGroupClickEvent(UIManagerHelper.getSurfaceId(view.context), view.id)) + } + + // Clickable elements are focusable. On API 26, this is taken care by setClickable. + // Explicitly calling setFocusable here for backward compatibility. + view.isFocusable = true + } else { + view.setOnClickListener(null) + view.isClickable = false + // Don't set view.setFocusable(false) because we might still want it to be focusable for + // accessibility reasons + } + } + + @ReactProp(name = ViewProps.OVERFLOW) + public open fun setOverflow(view: ReactViewGroup, overflow: String?) { + view.overflow = overflow + } + + @ReactProp(name = "backfaceVisibility") + public open fun setBackfaceVisibility(view: ReactViewGroup, backfaceVisibility: String) { + view.setBackfaceVisibility(backfaceVisibility) + } + + override fun setOpacity(view: ReactViewGroup, opacity: Float) { + view.setOpacityIfPossible(opacity) + } + + override fun setTransformProperty( + view: ReactViewGroup, + transforms: ReadableArray?, + transformOrigin: ReadableArray? + ) { + super.setTransformProperty(view, transforms, transformOrigin) + view.setBackfaceVisibilityDependantOpacity() + } + + override fun getName(): String = REACT_CLASS + + public override fun createViewInstance(context: ThemedReactContext): ReactViewGroup = + ReactViewGroup(context) + + override fun getCommandsMap(): MutableMap = + mutableMapOf(HOTSPOT_UPDATE_KEY to CMD_HOTSPOT_UPDATE, "setPressed" to CMD_SET_PRESSED) + + @Deprecated( + "Use receiveCommand(View, String, ReadableArray)", + ReplaceWith("receiveCommand(root, commandIdString, args)")) + override fun receiveCommand(root: ReactViewGroup, commandId: Int, args: ReadableArray?) { + when (commandId) { + CMD_HOTSPOT_UPDATE -> handleHotspotUpdate(root, args) + CMD_SET_PRESSED -> handleSetPressed(root, args) + else -> {} + } + } + + override fun receiveCommand(root: ReactViewGroup, commandId: String, args: ReadableArray?) { + when (commandId) { + HOTSPOT_UPDATE_KEY -> handleHotspotUpdate(root, args) + "setPressed" -> handleSetPressed(root, args) + else -> {} + } + } + + private fun handleSetPressed(root: ReactViewGroup, args: ReadableArray?) { + if (args == null || args.size() != 1) { + throw JSApplicationIllegalArgumentException( + "Illegal number of arguments for 'setPressed' command") + } + root.isPressed = args.getBoolean(0) + } + + private fun handleHotspotUpdate(root: ReactViewGroup, args: ReadableArray?) { + if (args == null || args.size() != 2) { + throw JSApplicationIllegalArgumentException( + "Illegal number of arguments for 'updateHotspot' command") + } + + val x = args.getDouble(0).dpToPx() + val y = args.getDouble(1).dpToPx() + root.drawableHotspotChanged(x, y) + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/systrace/Systrace.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/systrace/Systrace.kt index 14b8a8b60551ca..49f63e48909fa2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/systrace/Systrace.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/systrace/Systrace.kt @@ -8,6 +8,7 @@ package com.facebook.systrace import androidx.tracing.Trace +import java.lang.Runnable import kotlin.text.StringBuilder /** @@ -31,6 +32,16 @@ public object Systrace { @JvmStatic public fun traceInstant(tag: Long, title: String?, scope: EventScope?): Unit = Unit + @JvmStatic + public fun traceSection(tag: Long, sectionName: String, block: Runnable) { + beginSection(tag, sectionName) + try { + block.run() + } finally { + endSection(tag) + } + } + @JvmStatic public fun beginSection(tag: Long, sectionName: String) { Trace.beginSection(sectionName) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index cf822c55dcad43..f31c018f338386 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -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<<0908596d167df88df2a2ab2d4b8c06ec>> + * @generated SignedSource<<53e8e5519090ca41c78f3862447b44e1>> */ /** @@ -605,11 +605,25 @@ void JReactNativeFeatureFlagsCxxInterop::dangerouslyReset( ReactNativeFeatureFlags::dangerouslyReset(); } +jni::local_ref JReactNativeFeatureFlagsCxxInterop::dangerouslyForceOverride( + facebook::jni::alias_ref /*unused*/, + jni::alias_ref provider) { + auto accessedFlags = ReactNativeFeatureFlags::dangerouslyForceOverride( + std::make_unique(provider)); + if (accessedFlags.has_value()) { + return jni::make_jstring(accessedFlags.value()); + } + return nullptr; +} + void JReactNativeFeatureFlagsCxxInterop::registerNatives() { javaClassLocal()->registerNatives({ makeNativeMethod( "override", JReactNativeFeatureFlagsCxxInterop::override), makeNativeMethod("dangerouslyReset", JReactNativeFeatureFlagsCxxInterop::dangerouslyReset), + makeNativeMethod( + "dangerouslyForceOverride", + JReactNativeFeatureFlagsCxxInterop::dangerouslyForceOverride), makeNativeMethod( "commonTestFlag", JReactNativeFeatureFlagsCxxInterop::commonTestFlag), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index f01ebba058fef2..2a195b181ba742 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -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<> + * @generated SignedSource<> */ /** @@ -187,6 +187,10 @@ class JReactNativeFeatureFlagsCxxInterop static void dangerouslyReset( facebook::jni::alias_ref); + static jni::local_ref dangerouslyForceOverride( + facebook::jni::alias_ref, + jni::alias_ref provider); + static void registerNatives(); }; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.cpp index 157c3eb454a876..e0ac376122bd9d 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.cpp @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include namespace facebook::react { @@ -28,7 +31,10 @@ class ParsedStackFrameImpl static facebook::jni::local_ref create( const JsErrorHandler::ParsedError::StackFrame& frame) { return newInstance( - frame.fileName, frame.methodName, frame.lineNumber, frame.columnNumber); + frame.file ? jni::make_jstring(*frame.file) : nullptr, + frame.methodName, + frame.lineNumber ? jni::JInteger::valueOf(*frame.lineNumber) : nullptr, + frame.column ? jni::JInteger::valueOf(*frame.column) : nullptr); } }; @@ -39,27 +45,42 @@ class ParsedErrorImpl "Lcom/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler$ParsedErrorImpl;"; static facebook::jni::local_ref create( + jsi::Runtime& runtime, const JsErrorHandler::ParsedError& error) { - auto stackFrames = - facebook::jni::JArrayList::create(); - for (const auto& frame : error.frames) { - stackFrames->add(ParsedStackFrameImpl::create(frame)); + auto stack = facebook::jni::JArrayList::create(); + for (const auto& frame : error.stack) { + stack->add(ParsedStackFrameImpl::create(frame)); } + auto extraDataDynamic = + jsi::dynamicFromValue(runtime, jsi::Value(runtime, error.extraData)); + + auto extraData = + ReadableNativeMap::createWithContents(std::move(extraDataDynamic)); + return newInstance( - stackFrames, error.message, error.exceptionId, error.isFatal); + error.message, + error.originalMessage ? jni::make_jstring(*error.originalMessage) + : nullptr, + error.name ? jni::make_jstring(*error.name) : nullptr, + error.componentStack ? jni::make_jstring(*error.componentStack) + : nullptr, + stack, + error.id, + error.isFatal, + extraData); } }; - } // namespace void JReactExceptionManager::reportJsException( + jsi::Runtime& runtime, const JsErrorHandler::ParsedError& error) { static const auto method = javaClassStatic()->getMethod)>( "reportJsException"); if (self() != nullptr) { - method(self(), ParsedErrorImpl::create(error)); + method(self(), ParsedErrorImpl::create(runtime, error)); } } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.h index 3261545582f434..078977a0e1e2ce 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactExceptionManager.h @@ -19,7 +19,9 @@ class JReactExceptionManager static auto constexpr kJavaDescriptor = "Lcom/facebook/react/interfaces/exceptionmanager/ReactJsExceptionHandler;"; - void reportJsException(const JsErrorHandler::ParsedError& error); + void reportJsException( + jsi::Runtime& runtime, + const JsErrorHandler::ParsedError& error); }; } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp index 3bb569a9d0ecb5..e6cd6614e3e3e1 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp @@ -52,10 +52,11 @@ JReactInstance::JReactInstance( jReactExceptionManager_ = jni::make_global(jReactExceptionManager); auto onJsError = [weakJReactExceptionManager = jni::make_weak(jReactExceptionManager)]( + jsi::Runtime& runtime, const JsErrorHandler::ParsedError& error) mutable noexcept { if (auto jReactExceptionManager = weakJReactExceptionManager.lockLocal()) { - jReactExceptionManager->reportJsException(error); + jReactExceptionManager->reportJsException(runtime, error); } }; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.cpp index d31d97db2b5ae2..227f65a2eb90a3 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.cpp @@ -12,11 +12,20 @@ namespace facebook::react { BindingsInstallerHolder::BindingsInstallerHolder( - std::function bindingsInstaller) + BindingsInstallFunc bindingsInstaller) : bindingsInstaller_(std::move(bindingsInstaller)) {} -void BindingsInstallerHolder::installBindings(jsi::Runtime& runtime) { - bindingsInstaller_(runtime); +BindingsInstallerHolder::BindingsInstallerHolder( + std::function oldBindingsInstaller) + : bindingsInstaller_( + [f = std::move(oldBindingsInstaller)]( + jsi::Runtime& runtime, + const std::shared_ptr&) { f(runtime); }) {} + +void BindingsInstallerHolder::installBindings( + jsi::Runtime& runtime, + const std::shared_ptr& callInvoker) { + bindingsInstaller_(runtime, callInvoker); } } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h index 976799adbf22ab..c95f1d3961b6d4 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -17,14 +18,24 @@ class BindingsInstallerHolder public: static auto constexpr kJavaDescriptor = "Lcom/facebook/react/turbomodule/core/interfaces/BindingsInstallerHolder;"; + using BindingsInstallFunc = std::function& callInvoker)>; - void installBindings(jsi::Runtime& runtime); + void installBindings( + jsi::Runtime& runtime, + const std::shared_ptr& callInvoker); private: - friend HybridBase; + BindingsInstallerHolder(BindingsInstallFunc bindingsInstaller); + [[deprecated( + "Use 'BindingsInstaller([](Runtime, CallInvoker) { ... })' instead")]] BindingsInstallerHolder( - std::function bindingsInstaller); - std::function bindingsInstaller_; + std::function oldBindingsInstaller); + + private: + friend HybridBase; + BindingsInstallFunc bindingsInstaller_; }; } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp index 20a47db010e274..6a036308e4d5c6 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp @@ -199,7 +199,7 @@ std::shared_ptr TurboModuleManager::getTurboModule( "getBindingsInstaller"); auto installer = getBindingsInstaller(moduleInstance); if (installer) { - installer->cthis()->installBindings(runtime); + installer->cthis()->installBindings(runtime, jsCallInvoker_); } } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/interop/FakeRCTEventEmitter.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/interop/FakeRCTEventEmitter.kt index 46f96d0b6bfc91..f325893d0222b4 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/interop/FakeRCTEventEmitter.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/interop/FakeRCTEventEmitter.kt @@ -17,8 +17,10 @@ import com.facebook.react.uimanager.events.RCTEventEmitter @UnstableReactNativeAPI class FakeRCTEventEmitter : RCTEventEmitter { + @Deprecated("Deprecated in Java") override fun receiveEvent(targetReactTag: Int, eventName: String, event: WritableMap?) = Unit + @Deprecated("Deprecated in Java") override fun receiveTouches( eventName: String, touches: WritableArray, diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt index 5e19da6b688908..2f642b1f1b76b2 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt @@ -7,6 +7,7 @@ package com.facebook.react.devsupport +import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.ReadableMap import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler.* @@ -98,27 +99,31 @@ class StackTraceHelperTest { private fun getParsedErrorTestData(): ParsedError { val frame1 = object : ParsedError.StackFrame { - override val fileName = "file1" + override val file = "file1" override val methodName = "method1" override val lineNumber = 1 - override val columnNumber = 10 + override val column = 10 } val frame2 = object : ParsedError.StackFrame { - override val fileName = "file2" + override val file = "file2" override val methodName = "method2" override val lineNumber = 2 - override val columnNumber = 20 + override val column = 20 } val frames = listOf(frame1, frame2) return object : ParsedError { - override val frames = frames override val message = "error message" - override val exceptionId = 123 + override val originalMessage = null + override val name = null + override val componentStack = null + override val stack = frames + override val id = 123 override val isFatal = true + override val extraData = JavaOnlyMap() } } } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeEventDispatcher.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeEventDispatcher.kt index d386ac5858e633..e3c9fafab72ee0 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeEventDispatcher.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/fakes/FakeEventDispatcher.kt @@ -38,6 +38,7 @@ class FakeEventDispatcher : EventDispatcher { override fun removeBatchEventDispatchedListener(listener: BatchEventDispatchedListener) = Unit + @Deprecated("Deprecated in Java") override fun registerEventEmitter(uiManagerType: Int, eventEmitter: RCTEventEmitter) = Unit override fun registerEventEmitter(uiManagerType: Int, eventEmitter: RCTModernEventEmitter) = Unit diff --git a/packages/react-native/ReactCommon/devtoolsruntimesettings/CMakeLists.txt b/packages/react-native/ReactCommon/devtoolsruntimesettings/CMakeLists.txt new file mode 100644 index 00000000000000..244fabb9cca851 --- /dev/null +++ b/packages/react-native/ReactCommon/devtoolsruntimesettings/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fexceptions + -frtti + -std=c++20 + -Wall + -Wpedantic) + +add_library(react_devtoolsruntimesettingscxx INTERFACE) + +target_include_directories(react_devtoolsruntimesettingscxx INTERFACE .) + +target_link_libraries(react_devtoolsruntimesettingscxx + jsi +) diff --git a/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.cpp b/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.cpp new file mode 100644 index 00000000000000..02f4da23d92ff1 --- /dev/null +++ b/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.cpp @@ -0,0 +1,27 @@ +/* + * 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 "DevToolsRuntimeSettings.h" + +namespace facebook::react { + +void DevToolsRuntimeSettings::setReloadAndProfileConfig( + NativePartialReloadAndProfileConfig config) { + if (config.shouldReloadAndProfile.has_value()) { + _config.shouldReloadAndProfile = config.shouldReloadAndProfile.value(); + } + if (config.shouldReloadAndProfile.has_value()) { + _config.recordChangeDescriptions = config.recordChangeDescriptions.value(); + } +}; + +NativeReloadAndProfileConfig +DevToolsRuntimeSettings::getReloadAndProfileConfig() const { + return _config; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.h b/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.h new file mode 100644 index 00000000000000..aa6ae8dd750879 --- /dev/null +++ b/packages/react-native/ReactCommon/devtoolsruntimesettings/DevToolsRuntimeSettings.h @@ -0,0 +1,56 @@ +/* + * 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 + +namespace facebook::react { + +using NativePartialReloadAndProfileConfig = + NativeReactDevToolsRuntimeSettingsModulePartialReloadAndProfileConfig< + std::optional, + std::optional>; + +template <> +struct Bridging + : NativeReactDevToolsRuntimeSettingsModulePartialReloadAndProfileConfigBridging< + NativePartialReloadAndProfileConfig> {}; + +using NativeReloadAndProfileConfig = + NativeReactDevToolsRuntimeSettingsModuleReloadAndProfileConfig; + +template <> +struct Bridging + : NativeReactDevToolsRuntimeSettingsModuleReloadAndProfileConfigBridging< + NativeReloadAndProfileConfig> {}; + +class DevToolsRuntimeSettings { + public: + // static to persist across Turbo Module reloads + static DevToolsRuntimeSettings& getInstance() { + static DevToolsRuntimeSettings instance; + return instance; + } + + private: + NativeReloadAndProfileConfig _config; + + DevToolsRuntimeSettings() : _config() {} + + public: + ~DevToolsRuntimeSettings() = default; + DevToolsRuntimeSettings(const DevToolsRuntimeSettings&) = delete; + DevToolsRuntimeSettings(DevToolsRuntimeSettings&&) = delete; + void operator=(const DevToolsRuntimeSettings&) = delete; + void operator=(DevToolsRuntimeSettings&&) = delete; + + void setReloadAndProfileConfig(NativePartialReloadAndProfileConfig config); + NativeReloadAndProfileConfig getReloadAndProfileConfig() const; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp index e63260acb21b18..2216126af41028 100644 --- a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp +++ b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp @@ -668,9 +668,9 @@ jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String& str) { } jsi::PropNameID JSCRuntime::createPropNameIDFromSymbol(const jsi::Symbol& sym) { - // TODO: Support for symbols through the native API in JSC is very limited. - // While we could construct a PropNameID here, we would not be able to get a - // symbol property through the C++ API. + // TODO(T204185517): Support for symbols through the native API in JSC is very + // limited. While we could construct a PropNameID here, we would not be able + // to get a symbol property through the C++ API. throw std::logic_error("Not implemented"); } diff --git a/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt b/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt index 7015dc360a7b02..71ee2630328601 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt +++ b/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_VERBOSE_MAKEFILE on) add_compile_options(-std=c++20) -file(GLOB_RECURSE js_error_handler_SRC CONFIGURE_DEPENDS *.cpp) +file(GLOB js_error_handler_SRC CONFIGURE_DEPENDS *.cpp) add_library( jserrorhandler OBJECT diff --git a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp index 08dac54ccd10db..8bcbe849d1fe9b 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp +++ b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp @@ -8,95 +8,144 @@ #include "JsErrorHandler.h" #include #include -#include -#include +#include #include -#include +#include "StackTraceParser.h" + +using namespace facebook; + +namespace { +std::string quote(const std::string& view) { + return "\"" + view + "\""; +} + +int nextExceptionId() { + static int exceptionId = 0; + return exceptionId++; +} + +bool isLooselyNull(const jsi::Value& value) { + return value.isNull() || value.isUndefined(); +} + +bool isEmptyString(jsi::Runtime& runtime, const jsi::Value& value) { + return jsi::Value::strictEquals( + runtime, value, jsi::String::createFromUtf8(runtime, "")); +} + +std::string stringifyToCpp(jsi::Runtime& runtime, const jsi::Value& value) { + return value.toString(runtime).utf8(runtime); +} + +bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { + auto Boolean = runtime.global().getPropertyAsFunction(runtime, "Boolean"); + return Boolean.call(runtime, value).getBool(); +} + +void objectAssign( + jsi::Runtime& runtime, + jsi::Object& target, + const jsi::Object& value) { + auto Object = runtime.global().getPropertyAsObject(runtime, "Object"); + auto assign = Object.getPropertyAsFunction(runtime, "assign"); + assign.callWithThis(runtime, Object, target, value); +} +} // namespace namespace facebook::react { -// TODO(T198763073): Migrate away from std::regex in this function -static JsErrorHandler::ParsedError -parseErrorStack(const jsi::JSError& error, bool isFatal, bool isHermes) { - /** - * This parses the different stack traces and puts them into one format - * This borrows heavily from TraceKit (https://github.com/occ/TraceKit) - * This is the same regex from stacktrace-parser.js. - */ - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - const std::regex REGEX_CHROME( - R"(^\s*at (?:(?:(?:Anonymous function)?|((?:\[object object\])?\S+(?: \[as \S+\])?)) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$)"); - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - const std::regex REGEX_GECKO( - R"(^(?:\s*([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$)"); - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - const std::regex REGEX_NODE( - R"(^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$)"); - - // Capture groups for Hermes (from parseHermesStack.js): - // 1. function name - // 2. is this a native stack frame? - // 3. is this a bytecode address or a source location? - // 4. source URL (filename) - // 5. line number (1 based) - // 6. column number (1 based) or virtual offset (0 based) - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - const std::regex REGEX_HERMES( - R"(^ {4}at (.+?)(?: \((native)\)?| \((address at )?(.*?):(\d+):(\d+)\))$)"); - - std::string line; - std::stringstream strStream(error.getStack()); - - std::vector frames; - - while (std::getline(strStream, line, '\n')) { - auto searchResults = std::smatch{}; - - if (isHermes) { - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - if (std::regex_search(line, searchResults, REGEX_HERMES)) { - std::string str2 = std::string(searchResults[2]); - if (str2.compare("native")) { - frames.push_back({ - .fileName = std::string(searchResults[4]), - .methodName = std::string(searchResults[1]), - .lineNumber = std::stoi(searchResults[5]), - .columnNumber = std::stoi(searchResults[6]), - }); - } - } - } else { - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - if (std::regex_search(line, searchResults, REGEX_GECKO)) { - frames.push_back({ - .fileName = std::string(searchResults[3]), - .methodName = std::string(searchResults[1]), - .lineNumber = std::stoi(searchResults[4]), - .columnNumber = std::stoi(searchResults[5]), - }); - } else if ( - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - std::regex_search(line, searchResults, REGEX_CHROME) || - // @lint-ignore CLANGTIDY facebook-hte-StdRegexIsAwful - std::regex_search(line, searchResults, REGEX_NODE)) { - frames.push_back({ - .fileName = std::string(searchResults[2]), - .methodName = std::string(searchResults[1]), - .lineNumber = std::stoi(searchResults[3]), - .columnNumber = std::stoi(searchResults[4]), - }); - } else { - continue; - } +template <> +struct Bridging { + static jsi::Value toJs( + jsi::Runtime& runtime, + const JsErrorHandler::ParsedError::StackFrame& frame) { + auto stackFrame = jsi::Object(runtime); + auto file = bridging::toJs(runtime, frame.file, nullptr); + auto lineNumber = bridging::toJs(runtime, frame.lineNumber, nullptr); + auto column = bridging::toJs(runtime, frame.column, nullptr); + + stackFrame.setProperty(runtime, "file", file); + stackFrame.setProperty(runtime, "methodName", frame.methodName); + stackFrame.setProperty(runtime, "lineNumber", lineNumber); + stackFrame.setProperty(runtime, "column", column); + return stackFrame; + } +}; + +template <> +struct Bridging { + static jsi::Value toJs( + jsi::Runtime& runtime, + const JsErrorHandler::ParsedError& error) { + auto data = jsi::Object(runtime); + data.setProperty(runtime, "message", error.message); + data.setProperty( + runtime, + "originalMessage", + bridging::toJs(runtime, error.originalMessage, nullptr)); + data.setProperty( + runtime, "name", bridging::toJs(runtime, error.name, nullptr)); + data.setProperty( + runtime, + "componentStack", + bridging::toJs(runtime, error.componentStack, nullptr)); + + auto stack = jsi::Array(runtime, error.stack.size()); + for (size_t i = 0; i < error.stack.size(); i++) { + auto& frame = error.stack[i]; + stack.setValueAtIndex(runtime, i, bridging::toJs(runtime, frame)); } + + data.setProperty(runtime, "stack", stack); + data.setProperty(runtime, "id", error.id); + data.setProperty(runtime, "isFatal", error.isFatal); + data.setProperty(runtime, "extraData", error.extraData); + return data; } +}; - return { - .frames = std::move(frames), - .message = "EarlyJsError: " + error.getMessage(), - .exceptionId = 0, - .isFatal = isFatal, - }; +std::ostream& operator<<( + std::ostream& os, + const JsErrorHandler::ParsedError::StackFrame& frame) { + auto file = frame.file ? quote(*frame.file) : "nil"; + auto methodName = quote(frame.methodName); + auto lineNumber = + frame.lineNumber ? std::to_string(*frame.lineNumber) : "nil"; + auto column = frame.column ? std::to_string(*frame.column) : "nil"; + + os << "StackFrame { .file = " << file << ", .methodName = " << methodName + << ", .lineNumber = " << lineNumber << ", .column = " << column << " }"; + return os; +} +std::ostream& operator<<( + std::ostream& os, + const JsErrorHandler::ParsedError& error) { + auto message = quote(error.message); + auto originalMessage = + error.originalMessage ? quote(*error.originalMessage) : "nil"; + auto name = error.name ? quote(*error.name) : "nil"; + auto componentStack = + error.componentStack ? quote(*error.componentStack) : "nil"; + auto id = std::to_string(error.id); + auto isFatal = std::to_string(static_cast(error.isFatal)); + auto extraData = "jsi::Object{ } "; + + os << "ParsedError {\n" + << " .message = " << message << "\n" + << " .originalMessage = " << originalMessage << "\n" + << " .name = " << name << "\n" + << " .componentStack = " << componentStack << "\n" + << " .stack = [\n"; + + for (const auto& frame : error.stack) { + os << " " << frame << ", \n"; + } + os << " ]\n" + << " .id = " << id << "\n" + << " .isFatal " << isFatal << "\n" + << " .extraData = " << extraData << "\n" + << "}"; + return os; } JsErrorHandler::JsErrorHandler(JsErrorHandler::OnJsError onJsError) @@ -107,16 +156,19 @@ JsErrorHandler::JsErrorHandler(JsErrorHandler::OnJsError onJsError) JsErrorHandler::~JsErrorHandler() {} -void JsErrorHandler::handleFatalError( +void JsErrorHandler::handleError( jsi::Runtime& runtime, - jsi::JSError& error) { + jsi::JSError& error, + bool isFatal) { // TODO: Current error parsing works and is stable. Can investigate using - // REGEX_HERMES to get additional Hermes data, though it requires JS setup. - _hasHandledFatalError = true; - + // REGEX_HERMES to get additional Hermes data, though it requires JS setup if (_isRuntimeReady) { + if (isFatal) { + _hasHandledFatalError = true; + } + try { - handleJSError(runtime, error, true); + handleJSError(runtime, error, isFatal); return; } catch (jsi::JSError& e) { LOG(ERROR) @@ -126,9 +178,128 @@ void JsErrorHandler::handleFatalError( << "Original js error: " << error.getMessage() << std::endl; } } - // This is a hacky way to get Hermes stack trace. - ParsedError parsedError = parseErrorStack(error, true, false); - _onJsError(parsedError); + + emitError(runtime, error, isFatal); +} + +void JsErrorHandler::emitError( + jsi::Runtime& runtime, + jsi::JSError& error, + bool isFatal) { + auto message = error.getMessage(); + auto errorObj = error.value().getObject(runtime); + auto componentStackValue = errorObj.getProperty(runtime, "componentStack"); + if (!isLooselyNull(componentStackValue)) { + message += "\n" + stringifyToCpp(runtime, componentStackValue); + } + + auto nameValue = errorObj.getProperty(runtime, "name"); + auto name = (isLooselyNull(nameValue) || isEmptyString(runtime, nameValue)) + ? std::nullopt + : std::optional(stringifyToCpp(runtime, nameValue)); + + if (name && !message.starts_with(*name + ": ")) { + message = *name + ": " + message; + } + + auto jsEngineValue = errorObj.getProperty(runtime, "jsEngine"); + + if (!isLooselyNull(jsEngineValue)) { + message += ", js engine: " + stringifyToCpp(runtime, jsEngineValue); + } + + auto extraDataKey = jsi::PropNameID::forUtf8(runtime, "RN$ErrorExtraDataKey"); + auto extraDataValue = errorObj.getProperty(runtime, extraDataKey); + + auto extraData = jsi::Object(runtime); + if (extraDataValue.isObject()) { + objectAssign(runtime, extraData, extraDataValue.asObject(runtime)); + } + + extraData.setProperty(runtime, "jsEngine", jsEngineValue); + extraData.setProperty(runtime, "rawStack", error.getStack()); + + auto cause = errorObj.getProperty(runtime, "cause"); + if (cause.isObject()) { + auto causeObj = cause.asObject(runtime); + // TODO: Consider just forwarding all properties. For now, just forward the + // stack properties to maintain symmetry with js pipeline + auto stackSymbols = causeObj.getProperty(runtime, "stackSymbols"); + extraData.setProperty(runtime, "stackSymbols", stackSymbols); + + auto stackReturnAddresses = + causeObj.getProperty(runtime, "stackReturnAddresses"); + extraData.setProperty( + runtime, "stackReturnAddresses", stackReturnAddresses); + + auto stackElements = causeObj.getProperty(runtime, "stackElements"); + extraData.setProperty(runtime, "stackElements", stackElements); + } + + auto originalMessage = message == error.getMessage() + ? std::nullopt + : std::optional(error.getMessage()); + + auto componentStack = !componentStackValue.isString() + ? std::nullopt + : std::optional(componentStackValue.asString(runtime).utf8(runtime)); + + auto isHermes = runtime.global().hasProperty(runtime, "HermesInternal"); + auto stackFrames = StackTraceParser::parse(isHermes, error.getStack()); + + auto id = nextExceptionId(); + + ParsedError parsedError = { + .message = "EarlyJsError: " + message, + .originalMessage = originalMessage, + .name = name, + .componentStack = componentStack, + .stack = stackFrames, + .id = id, + .isFatal = isFatal, + .extraData = std::move(extraData), + }; + + auto data = bridging::toJs(runtime, parsedError).asObject(runtime); + + auto isComponentError = + isTruthy(runtime, errorObj.getProperty(runtime, "isComponentError")); + data.setProperty(runtime, "isComponentError", isComponentError); + + std::shared_ptr shouldPreventDefault = std::make_shared(false); + auto preventDefault = jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "preventDefault"), + 0, + [shouldPreventDefault]( + jsi::Runtime& /*rt*/, + const jsi::Value& /*thisVal*/, + const jsi::Value* /*args*/, + size_t /*count*/) { + *shouldPreventDefault = true; + return jsi::Value::undefined(); + }); + + data.setProperty(runtime, "preventDefault", preventDefault); + + for (auto& errorListener : _errorListeners) { + errorListener(runtime, jsi::Value(runtime, data)); + } + + if (*shouldPreventDefault) { + return; + } + + if (isFatal) { + _hasHandledFatalError = true; + } + + _onJsError(runtime, parsedError); +} + +void JsErrorHandler::registerErrorListener( + const std::function& errorListener) { + _errorListeners.push_back(errorListener); } bool JsErrorHandler::hasHandledFatalError() { diff --git a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.h b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.h index a9b94f2d5c09b7..b8d784ac63eb97 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.h +++ b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.h @@ -8,6 +8,8 @@ #pragma once #include +#include +#include namespace facebook::react { @@ -15,25 +17,36 @@ class JsErrorHandler { public: struct ParsedError { struct StackFrame { - std::string fileName; + std::optional file; std::string methodName; - int lineNumber; - int columnNumber; + std::optional lineNumber; + std::optional column; + friend std::ostream& operator<<( + std::ostream& os, + const StackFrame& frame); }; - std::vector frames; std::string message; - int exceptionId; + std::optional originalMessage; + std::optional name; + std::optional componentStack; + std::vector stack; + int id; bool isFatal; + jsi::Object extraData; + friend std::ostream& operator<<(std::ostream& os, const ParsedError& error); }; - using OnJsError = std::function; + using OnJsError = + std::function; explicit JsErrorHandler(OnJsError onJsError); ~JsErrorHandler(); - void handleFatalError(jsi::Runtime& runtime, jsi::JSError& error); + void handleError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal); bool hasHandledFatalError(); + void registerErrorListener( + const std::function& listener); void setRuntimeReady(); bool isRuntimeReady(); void notifyOfFatalError(); @@ -49,6 +62,9 @@ class JsErrorHandler { OnJsError _onJsError; bool _hasHandledFatalError; bool _isRuntimeReady{}; + std::vector> _errorListeners; + + void emitError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal); }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec index 4c8d71179cfa88..da6c75c6838677 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec +++ b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec @@ -22,7 +22,7 @@ folly_version = folly_config[:version] folly_dep_name = folly_config[:dep_name] boost_config = get_boost_config() -boost_compiler_flags = boost_config[:compiler_flags] +boost_compiler_flags = boost_config[:compiler_flags] react_native_path = ".." Pod::Spec.new do |s| @@ -35,7 +35,7 @@ Pod::Spec.new do |s| s.platforms = min_supported_versions s.source = source s.header_dir = "jserrorhandler" - s.source_files = "JsErrorHandler.{cpp,h}" + s.source_files = "JsErrorHandler.{cpp,h}", "StackTraceParser.{cpp,h}" s.pod_target_xcconfig = { "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard() @@ -51,8 +51,9 @@ Pod::Spec.new do |s| s.dependency "React-jsi" s.dependency "React-cxxreact" s.dependency "glog" + s.dependency "ReactCommon/turbomodule/bridging" add_dependency(s, "React-debug") - + if ENV['USE_HERMES'] == nil || ENV['USE_HERMES'] == "1" s.dependency 'hermes-engine' end diff --git a/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.cpp b/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.cpp new file mode 100644 index 00000000000000..a389609ed8f87d --- /dev/null +++ b/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.cpp @@ -0,0 +1,317 @@ +/* + * 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 "StackTraceParser.h" +#include +#include +#include +#include +#include +#include + +using namespace facebook::react; + +const std::string UNKNOWN_FUNCTION = ""; + +// TODO(T198763073): Migrate away from std::regex in this file +// @lint-ignore-every CLANGTIDY facebook-hte-StdRegexIsAwful + +/** + * Stack trace parsing for other jsvms: + * Port of https://github.com/errwischt/stacktrace-parser + */ +namespace { + +std::optional toInt(std::string_view input) { + int out; + const std::from_chars_result result = + std::from_chars(input.data(), input.data() + input.size(), out); + if (result.ec == std::errc::invalid_argument || + result.ec == std::errc::result_out_of_range) { + return std::nullopt; + } + return out; +} + +JsErrorHandler::ParsedError::StackFrame parseStackFrame( + std::string_view file, + std::string_view methodName, + std::string_view lineStr, + std::string_view columnStr) { + JsErrorHandler::ParsedError::StackFrame frame; + frame.file = file.empty() ? std::nullopt : std::optional(file); + frame.methodName = !methodName.empty() ? methodName : UNKNOWN_FUNCTION; + frame.lineNumber = !lineStr.empty() ? toInt(lineStr) : std::nullopt; + auto columnOpt = !columnStr.empty() ? toInt(columnStr) : std::nullopt; + frame.column = columnOpt ? std::optional(*columnOpt - 1) : std::nullopt; + return frame; +} + +std::optional parseChrome( + const std::string& line) { + static const std::regex chromeRe( + R"(^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/|[a-z]:\\|\\\\).*?)(?::(\d+))?(?::(\d+))?\)?\s*$)", + std::regex::icase); + static const std::regex chromeEvalRe(R"(\((\S*)(?::(\d+))(?::(\d+))\))"); + std::smatch match; + + if (!std::regex_match(line, match, chromeRe)) { + return std::nullopt; + } + std::string methodName = match[1].str(); + std::string file = match[2].str(); + std::string lineStr = match[3].str(); + std::string columnStr = match[4].str(); + + bool isNative = std::regex_search(file, std::regex("^native")); + bool isEval = std::regex_search(file, std::regex("^eval")); + std::string evalFile; + std::string evalLine; + std::string evalColumn; + if (isEval && std::regex_search(file, match, chromeEvalRe)) { + evalFile = match[1].str(); + evalLine = match[2].str(); + evalColumn = match[3].str(); + file = evalFile; + lineStr = evalLine; + columnStr = evalColumn; + } + std::string actualFile = !isNative ? file : ""; + return parseStackFrame(actualFile, methodName, lineStr, columnStr); +} + +std::optional parseWinjs( + const std::string& line) { + static const std::regex winjsRe( + R"(^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$)", + std::regex::icase); + std::smatch match; + if (!std::regex_match(line, match, winjsRe)) { + return std::nullopt; + } + std::string methodName = match[1].str(); + std::string file = match[2].str(); + std::string lineStr = match[3].str(); + std::string columnStr = match[4].str(); + return parseStackFrame(file, methodName, lineStr, columnStr); +} + +std::optional parseGecko( + const std::string& line) { + static const std::regex geckoRe( + R"(^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$)", + std::regex::icase); + static const std::regex geckoEvalRe( + R"((\S+) line (\d+)(?: > eval line \d+)* > eval)", std::regex::icase); + std::smatch match; + if (!std::regex_match(line, match, geckoRe)) { + return std::nullopt; + } + std::string methodName = match[1].str(); + std::string tmpStr = match[2].str(); + std::string file = match[3].str(); + std::string lineStr = match[4].str(); + std::string columnStr = match[5].str(); + bool isEval = std::regex_search(file, std::regex(" > eval")); + std::string evalFile; + std::string evalLine; + if (isEval && std::regex_search(file, match, geckoEvalRe)) { + evalFile = match[1].str(); + evalLine = match[2].str(); + file = evalFile; + lineStr = evalLine; + columnStr = ""; // No column number in eval + } + return parseStackFrame(file, methodName, lineStr, columnStr); +} + +std::optional parseJSC( + const std::string& line) { + static const std::regex javaScriptCoreRe( + R"(^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$)", + std::regex::icase); + std::smatch match; + if (!std::regex_match(line, match, javaScriptCoreRe)) { + return std::nullopt; + } + std::string methodName = match[1].str(); + std::string tmpStr = + match[2].str(); // This captures any string within parentheses if present + std::string file = match[3].str(); + std::string lineStr = match[4].str(); + std::string columnStr = match[5].str(); + return parseStackFrame(file, methodName, lineStr, columnStr); +} + +std::optional parseNode( + const std::string& line) { + static const std::regex nodeRe( + R"(^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$)", + std::regex::icase); + std::smatch match; + if (!std::regex_match(line, match, nodeRe)) { + return std::nullopt; + } + std::string methodName = match[1].str(); + std::string file = match[2].str(); + std::string lineStr = match[3].str(); + std::string columnStr = match[4].str(); + return parseStackFrame(file, methodName, lineStr, columnStr); +} + +std::vector parseOthers( + const std::string& stackString) { + std::vector stack; + std::istringstream iss(stackString); + std::string line; + + while (std::getline(iss, line)) { + std::optional frame = + parseChrome(line); + + if (!frame) { + frame = parseWinjs(line); + } + if (!frame) { + frame = parseGecko(line); + } + if (!frame) { + frame = parseNode(line); + } + if (!frame) { + frame = parseJSC(line); + } + + if (frame) { + stack.push_back(*frame); + } + } + + return stack; +} + +} // namespace + +/** + * Hermes stack trace parsing logic + */ +namespace { +struct HermesStackLocation { + std::string type; + std::string sourceUrl; + int line1Based{}; + int column1Based{}; + int virtualOffset0Based{}; +}; + +struct HermesStackEntry { + std::string type; + std::string functionName; + HermesStackLocation location; + int count{}; +}; + +bool isInternalBytecodeSourceUrl(const std::string& sourceUrl) { + return sourceUrl == "InternalBytecode.js"; +} + +std::vector convertHermesStack( + const std::vector& stack) { + std::vector frames; + for (const auto& entry : stack) { + if (entry.type != "FRAME") { + continue; + } + if (entry.location.type == "NATIVE" || + entry.location.type == "INTERNAL_BYTECODE") { + continue; + } + JsErrorHandler::ParsedError::StackFrame frame; + frame.methodName = entry.functionName; + frame.file = entry.location.sourceUrl; + frame.lineNumber = entry.location.line1Based; + if (entry.location.type == "SOURCE") { + frame.column = entry.location.column1Based - 1; + } else { + frame.column = entry.location.virtualOffset0Based; + } + frames.push_back(frame); + } + return frames; +} + +HermesStackEntry parseLine(const std::string& line) { + static const std::regex RE_FRAME( + R"(^ {4}at (.+?)(?: \((native)\)?| \((address at )?(.*?):(\d+):(\d+)\))$)"); + static const std::regex RE_SKIPPED(R"(^ {4}... skipping (\d+) frames$)"); + HermesStackEntry entry; + std::smatch match; + if (std::regex_match(line, match, RE_FRAME)) { + entry.type = "FRAME"; + entry.functionName = match[1].str(); + std::string type = match[2].str(); + std::string addressAt = match[3].str(); + std::string sourceUrl = match[4].str(); + if (type == "native") { + entry.location.type = "NATIVE"; + } else { + int line1Based = std::stoi(match[5].str()); + int columnOrOffset = std::stoi(match[6].str()); + if (addressAt == "address at ") { + if (isInternalBytecodeSourceUrl(sourceUrl)) { + entry.location = { + "INTERNAL_BYTECODE", sourceUrl, line1Based, 0, columnOrOffset}; + } else { + entry.location = { + "BYTECODE", sourceUrl, line1Based, 0, columnOrOffset}; + } + } else { + entry.location = {"SOURCE", sourceUrl, line1Based, columnOrOffset, 0}; + } + } + return entry; + } + if (std::regex_match(line, match, RE_SKIPPED)) { + entry.type = "SKIPPED"; + entry.count = std::stoi(match[1].str()); + } + return entry; +} + +std::vector parseHermes( + const std::string& stack) { + static const std::regex RE_COMPONENT_NO_STACK(R"(^ {4}at .*?$)"); + std::istringstream stream(stack); + std::string line; + std::vector entries; + std::smatch match; + while (std::getline(stream, line)) { + if (line.empty()) { + continue; + } + HermesStackEntry entry = parseLine(line); + if (!entry.type.empty()) { + entries.push_back(entry); + continue; + } + + if (std::regex_match(line, match, RE_COMPONENT_NO_STACK)) { + continue; + } + entries.clear(); + } + return convertHermesStack(entries); +} +} // namespace + +std::vector StackTraceParser::parse( + const bool isHermes, + const std::string& stackString) { + std::vector stackFrames = + isHermes ? parseHermes(stackString) : parseOthers(stackString); + return stackFrames; +} diff --git a/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.h b/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.h new file mode 100644 index 00000000000000..0e401e52756df2 --- /dev/null +++ b/packages/react-native/ReactCommon/jserrorhandler/StackTraceParser.h @@ -0,0 +1,23 @@ +/* + * 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 +#include +#include "JsErrorHandler.h" + +namespace facebook::react { + +class StackTraceParser { + public: + static std::vector parse( + bool isHermes, + const std::string& stackString); +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jserrorhandler/tests/StackTraceParserTest.cpp b/packages/react-native/ReactCommon/jserrorhandler/tests/StackTraceParserTest.cpp new file mode 100644 index 00000000000000..83737775a89056 --- /dev/null +++ b/packages/react-native/ReactCommon/jserrorhandler/tests/StackTraceParserTest.cpp @@ -0,0 +1,1196 @@ +/* + * 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 + +#include + +using namespace facebook::react; + +#include +#include + +std::unordered_map CapturedExceptions = { + {"NODE_12", + "Error: Just an Exception\n" + " at promiseMe (/home/xyz/hack/asyncnode.js:11:9)\n" + " at async main (/home/xyz/hack/asyncnode.js:15:13)"}, + {"NODE_ANONYM", + "Error\n" + " at Spect.get (C:\\projects\\spect\\src\\index.js:161:26)\n" + " at Object.get (C:\\projects\\spect\\src\\index.js:43:36)\n" + " at \n" + " at (anonymous function).then (C:\\projects\\spect\\src\\index.js:165:33)\n" + " at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:52:5)\n" + " at C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34535\n" + " at C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34176\n" + " at process. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34506)\n" + " at Function. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:296856)\n" + " at Function. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:296555)"}, + {"NODE_SPACE", + "Error\n" + " at Spect.get (C:\\project files\\spect\\src\\index.js:161:26)\n" + " at Object.get (C:\\project files\\spect\\src\\index.js:43:36)\n" + " at \n" + " at (anonymous function).then (C:\\project files\\spect\\src\\index.js:165:33)\n" + " at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:52:5)\n" + " at C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34535\n" + " at C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34176\n" + " at process. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34506)\n" + " at Function. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:296856)\n" + " at Function. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:296555)"}, + {"OPERA_25", + "TypeError: Cannot read property 'undef' of null\n" + " at http://path/to/file.js:47:22\n" + " at foo (http://path/to/file.js:52:15)\n" + " at bar (http://path/to/file.js:108:168)"}, + {"CHROME_15", + "TypeError: Object # has no method 'undef'\n" + " at bar (http://path/to/file.js:13:17)\n" + " at bar (http://path/to/file.js:16:5)\n" + " at foo (http://path/to/file.js:20:5)\n" + " at http://path/to/file.js:24:4"}, + {"CHROME_36", + "Error: Default error\n" + " at dumpExceptionError (http://localhost:8080/file.js:41:27)\n" + " at HTMLButtonElement.onclick (http://localhost:8080/file.js:107:146)\n" + " at I.e.fn.(anonymous function) [as index] (http://localhost:8080/file.js:10:3651)"}, + {"CHROME_76", + "Error: BEEP BEEP\n" + " at bar (:8:9)\n" + " at async foo (:2:3)"}, + {"CHROME_XX_WEBPACK", + "TypeError: Cannot read property 'error' of undefined\n" + " at TESTTESTTEST.eval(webpack:///./src/components/test/test.jsx?:295:108)\n" + " at TESTTESTTEST.render(webpack:///./src/components/test/test.jsx?:272:32)\n" + " at TESTTESTTEST.tryRender(webpack:///./~/react-transform-catch-errors/lib/index.js?:34:31)\n" + " at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)\n" + " at Module../pages/index.js (C:\\root\\server\\development\\pages\\index.js:182:7)"}, + {"FIREFOX_3", + "()@http://127.0.0.1:8000/js/stacktrace.js:44\n" + "(null)@http://127.0.0.1:8000/js/stacktrace.js:31\n" + "printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18\n" + "bar(1)@http://127.0.0.1:8000/js/file.js:13\n" + "bar(2)@http://127.0.0.1:8000/js/file.js:16\n" + "foo()@http://127.0.0.1:8000/js/file.js:20\n" + "@http://127.0.0.1:8000/js/file.js:24\n"}, + {"FIREFOX_7", + "()@file:///G:/js/stacktrace.js:44\n" + "(null)@file:///G:/js/stacktrace.js:31\n" + "printStackTrace()@file:///G:/js/stacktrace.js:18\n" + "bar(1)@file:///G:/js/file.js:13\n" + "bar(2)@file:///G:/js/file.js:16\n" + "foo()@file:///G:/js/file.js:20\n" + "@file:///G:/js/file.js:24\n"}, + {"FIREFOX_14", + "@http://path/to/file.js:48\n" + "dumpException3@http://path/to/file.js:52\n" + "onclick@http://path/to/file.js:1\n"}, + {"FIREFOX_31", + "foo@http://path/to/file.js:41:13\n" + "bar@http://path/to/file.js:1:1\n" + ".plugin/e.fn[c]/<@http://path/to/file.js:1:1\n"}, + {"FIREFOX_43_EVAL", + "baz@http://localhost:8080/file.js line 26 > eval line 2 > eval:1:30\n" + "foo@http://localhost:8080/file.js line 26 > eval:2:96\n" + "@http://localhost:8080/file.js line 26 > eval:4:18\n" + "speak@http://localhost:8080/file.js:26:17\n" + "@http://localhost:8080/file.js:33:9"}, + {"FIREFOX_44_NS_EXCEPTION", + "[2]:1:30)\n" + "at foo (eval at speak (http://localhost:8080/file.js:21:17), :2:96)\n" + "at eval (eval at speak (http://localhost:8080/file.js:21:17), :4:18)\n" + "at Object.speak (http://localhost:8080/file.js:21:17)\n" + "at http://localhost:8080/file.js:31:13\n"}, + {"PHANTOMJS_1_19", + "Error: foo\n" + " at file:///path/to/file.js:878\n" + " at foo (http://path/to/file.js:4283)\n" + " at http://path/to/file.js:4287"}, + {"ANDROID_REACT_NATIVE", + "Error: test\n" + "at render(/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js:78:24)\n" + "at _renderValidatedComponentWithoutOwnerOrContext(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:1050:29)\n" + "at _renderValidatedComponent(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:1075:15)\n" + "at renderedElement(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:484:29)\n" + "at _currentElement(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:346:40)\n" + "at child(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactReconciler.js:68:25)\n" + "at children(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactMultiChild.js:264:10)\n" + "at this(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js:74:41)\n"}, + {"ANDROID_REACT_NATIVE_PROD", + "value@index.android.bundle:12:1917\n" + "onPress@index.android.bundle:12:2336\n" + "touchableHandlePress@index.android.bundle:258:1497\n" + "[native code]\n" + "_performSideEffectsForTransition@index.android.bundle:252:8508\n" + "[native code]\n" + "_receiveSignal@index.android.bundle:252:7291\n" + "[native code]\n" + "touchableHandleResponderRelease@index.android.bundle:252:4735\n" + "[native code]\n" + "u@index.android.bundle:79:142\n" + "invokeGuardedCallback@index.android.bundle:79:459\n" + "invokeGuardedCallbackAndCatchFirstError@index.android.bundle:79:580\n" + "c@index.android.bundle:95:365\n" + "a@index.android.bundle:95:567\n" + "v@index.android.bundle:146:501\n" + "g@index.android.bundle:146:604\n" + "forEach@[native code]\n" + "i@index.android.bundle:149:80\n" + "processEventQueue@index.android.bundle:146:1432\n" + "s@index.android.bundle:157:88\n" + "handleTopLevel@index.android.bundle:157:174\n" + "index.android.bundle:156:572\n" + "a@index.android.bundle:93:276\n" + "c@index.android.bundle:93:60\n" + "perform@index.android.bundle:177:596\n" + "batchedUpdates@index.android.bundle:188:464\n" + "i@index.android.bundle:176:358\n" + "i@index.android.bundle:93:90\n" + "u@index.android.bundle:93:150\n" + "_receiveRootNodeIDEvent@index.android.bundle:156:544\n" + "receiveTouches@index.android.bundle:156:918\n" + "value@index.android.bundle:29:3016\n" + "index.android.bundle:29:955\n" + "value@index.android.bundle:29:2417\n" + "value@index.android.bundle:29:927\n" + "[native code]"}, + {"IOS_REACT_NATIVE_1", + "_exampleFunction@/home/test/project/App.js:125:13\n" + "_depRunCallbacks@/home/test/project/node_modules/dep/index.js:77:45\n" + "tryCallTwo@/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js:45:5\n" + "doResolve@/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js:200:13"}, + {"IOS_REACT_NATIVE_2", + "s@33.js:1:531\n" + "b@1959.js:1:1469\n" + "onSocketClose@2932.js:1:727\n" + "value@81.js:1:1505\n" + "102.js:1:2956\n" + "value@89.js:1:1247\n" + "value@42.js:1:3311\n" + "42.js:1:822\n" + "value@42.js:1:2565\n" + "value@42.js:1:794\n" + "value@[native code]"}, + + {"ANONYMOUS_SOURCES", + "x\n" + "at new (http://www.example.com/test.js:2:1\n" + "at :1:2\n"}, + {"NODE_JS_TEST_1", + "ReferenceError: test is not defined\n" + "at repl:1:2\n" + "at REPLServer.self.eval (repl.js:110:21)\n" + "at Interface. (repl.js:239:12)\n" + "at Interface.EventEmitter.emit (events.js:95:17)\n" + "at emitKey (readline.js:1095:12)\n"}, + {"NODE_JS_TEST_2", + "ReferenceError: breakDown is not defined\n" + "at null._onTimeout (repl:1:25)\n" + "at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)\n"}, + {"IO_JS", + "ReferenceError: test is not defined\n" + "at repl:1:1\n" + "at REPLServer.defaultEval (repl.js:154:27)\n" + "at bound (domain.js:254:14)\n" + "at REPLServer.runBound [as eval] (domain.js:267:12)\n" + "at REPLServer. (repl.js:308:12)\n" + "at emitOne (events.js:77:13)\n" + "at REPLServer.emit (events.js:169:7)\n" + "at REPLServer.Interface._onLine (readline.js:210:10)\n" + "at REPLServer.Interface._line (readline.js:549:8)\n" + "at REPLServer.Interface._ttyWrite (readline.js:826:14)\n"}}; + +TEST(StackTraceParser, nodeWithSpaceInPath) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["NODE_SPACE"]); + EXPECT_EQ(actualStackFrames.size(), 9); + + std::vector expectedStackFrames = { + {R"(C:\project files\spect\src\index.js)", "Spect.get", 161, 25}, + {R"(C:\project files\spect\src\index.js)", "Object.get", 43, 35}, + {R"(C:\project files\spect\src\index.js)", + "(anonymous function).then", + 165, + 32}, + {"internal/process/task_queues.js", + "process.runNextTicks [as _tickCallback]", + 52, + 4}, + {R"(C:\project files\spect\node_modules\esm\esm.js)", + "", + 1, + 34534}, + {R"(C:\project files\spect\node_modules\esm\esm.js)", + "", + 1, + 34175}, + {R"(C:\project files\spect\node_modules\esm\esm.js)", + "process.", + 1, + 34505}, + {R"(C:\project files\spect\node_modules\esm\esm.js)", + "Function.", + 1, + 296855}, + {R"(C:\project files\spect\node_modules\esm\esm.js)", + "Function.", + 1, + 296554}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, javaScriptCore) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IOS_REACT_NATIVE_1"]); + EXPECT_EQ(actualStackFrames.size(), 4); + + std::vector expectedStackFrames = { + {"/home/test/project/App.js", "_exampleFunction", 125, 12}, + {"/home/test/project/node_modules/dep/index.js", + "_depRunCallbacks", + 77, + 44}, + {"/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js", + "tryCallTwo", + 45, + 4}, + {"/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js", + "doResolve", + 200, + 12}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, errorInReactNative) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IOS_REACT_NATIVE_2"]); + EXPECT_EQ(actualStackFrames.size(), 11); + + std::vector expectedStackFrames = { + {"33.js", "s", 1, 530}, + {"1959.js", "b", 1, 1468}, + {"2932.js", "onSocketClose", 1, 726}, + {"81.js", "value", 1, 1504}, + {"102.js", "", 1, 2955}, + {"89.js", "value", 1, 1246}, + {"42.js", "value", 1, 3310}, + {"42.js", "", 1, 821}, + {"42.js", "value", 1, 2564}, + {"42.js", "value", 1, 793}, + {"[native code]", "value", std::nullopt, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, simpleJavaScriptCoreErrors) { + auto actualStackFrames = + StackTraceParser::parse(false, "global code@stack_traces/test:83:55"); + EXPECT_EQ(actualStackFrames.size(), 1); + + std::vector expectedStackFrames = { + {"stack_traces/test", "global code", 83, 54}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, safari6Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["SAFARI_6"]); + EXPECT_EQ(actualStackFrames.size(), 4); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "", 48, std::nullopt}, + {"http://path/to/file.js", "dumpException3", 52, std::nullopt}, + {"http://path/to/file.js", "onclick", 82, std::nullopt}, + {"[native code]", "", std::nullopt, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, safari7Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["SAFARI_7"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "", 48, 21}, + {"http://path/to/file.js", "foo", 52, 14}, + {"http://path/to/file.js", "bar", 108, 106}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, safari8Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["SAFARI_8"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "", 47, 21}, + {"http://path/to/file.js", "foo", 52, 14}, + {"http://path/to/file.js", "bar", 108, 22}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, safari8EvalError) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["SAFARI_8_EVAL"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"[native code]", "eval", std::nullopt, std::nullopt}, + {"http://path/to/file.js", "foo", 58, 20}, + {"http://path/to/file.js", "bar", 109, 90}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, firefox3Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["FIREFOX_3"]); + EXPECT_EQ(actualStackFrames.size(), 7); + + std::vector expectedStackFrames = { + {"http://127.0.0.1:8000/js/stacktrace.js", "", 44, std::nullopt}, + {"http://127.0.0.1:8000/js/stacktrace.js", "", 31, std::nullopt}, + {"http://127.0.0.1:8000/js/stacktrace.js", + "printStackTrace", + 18, + std::nullopt}, + {"http://127.0.0.1:8000/js/file.js", "bar", 13, std::nullopt}, + {"http://127.0.0.1:8000/js/file.js", "bar", 16, std::nullopt}, + {"http://127.0.0.1:8000/js/file.js", "foo", 20, std::nullopt}, + {"http://127.0.0.1:8000/js/file.js", "", 24, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, firefox7Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["FIREFOX_7"]); + EXPECT_EQ(actualStackFrames.size(), 7); + + std::vector expectedStackFrames = { + {"file:///G:/js/stacktrace.js", "", 44, std::nullopt}, + {"file:///G:/js/stacktrace.js", "", 31, std::nullopt}, + {"file:///G:/js/stacktrace.js", "printStackTrace", 18, std::nullopt}, + {"file:///G:/js/file.js", "bar", 13, std::nullopt}, + {"file:///G:/js/file.js", "bar", 16, std::nullopt}, + {"file:///G:/js/file.js", "foo", 20, std::nullopt}, + {"file:///G:/js/file.js", "", 24, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, firefox14Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["FIREFOX_14"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "", 48, std::nullopt}, + {"http://path/to/file.js", "dumpException3", 52, std::nullopt}, + {"http://path/to/file.js", "onclick", 1, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, firefox31Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["FIREFOX_31"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "foo", 41, 12}, + {"http://path/to/file.js", "bar", 1, 0}, + {"http://path/to/file.js", ".plugin/e.fn[c]/<", 1, 0}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, firefox44) { + auto actualStackFrames = StackTraceParser::parse( + false, CapturedExceptions["FIREFOX_44_NS_EXCEPTION"]); + EXPECT_EQ(actualStackFrames.size(), 4); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "[2]", 23, 0}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chromeErrorWithNoLocation) { + auto actualStackFrames = + StackTraceParser::parse(false, "error\n at Array.forEach (native)"); + EXPECT_EQ(actualStackFrames.size(), 1); + + std::vector expectedStackFrames = { + {std::nullopt, "Array.forEach", std::nullopt, std::nullopt}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chrome15Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_15"]); + EXPECT_EQ(actualStackFrames.size(), 4); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "bar", 13, 16}, + {"http://path/to/file.js", "bar", 16, 4}, + {"http://path/to/file.js", "foo", 20, 4}, + {"http://path/to/file.js", "", 24, 3}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chrome36Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_36"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://localhost:8080/file.js", "dumpExceptionError", 41, 26}, + {"http://localhost:8080/file.js", "HTMLButtonElement.onclick", 107, 145}, + {"http://localhost:8080/file.js", + "I.e.fn.(anonymous function) [as index]", + 10, + 3650}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chrome76Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_76"]); + EXPECT_EQ(actualStackFrames.size(), 2); + + std::vector expectedStackFrames = { + {"", "bar", 8, 8}, {"", "async foo", 2, 2}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chromeErrorWithWebpackURLS) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_XX_WEBPACK"]); + EXPECT_EQ(actualStackFrames.size(), 5); + + std::vector expectedStackFrames = { + {"webpack:///./src/components/test/test.jsx?", + "TESTTESTTEST.eval", + 295, + 107}, + {"webpack:///./src/components/test/test.jsx?", + "TESTTESTTEST.render", + 272, + 31}, + {"webpack:///./~/react-transform-catch-errors/lib/index.js?", + "TESTTESTTEST.tryRender", + 34, + 30}, + {"webpack:///./~/react-proxy/modules/createPrototypeProxy.js?", + "TESTTESTTEST.proxiedMethod", + 44, + 29}, + {R"(C:\root\server\development\pages\index.js)", + "Module../pages/index.js", + 182, + 6}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, nestedEvalsFromChrome) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_48_EVAL"]); + EXPECT_EQ(actualStackFrames.size(), 5); + + std::vector expectedStackFrames = { + {"http://localhost:8080/file.js", "baz", 21, 16}, + {"http://localhost:8080/file.js", "foo", 21, 16}, + {"http://localhost:8080/file.js", "eval", 21, 16}, + {"http://localhost:8080/file.js", "Object.speak", 21, 16}, + {"http://localhost:8080/file.js", "", 31, 12}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, chromeErrorWithBlobURLs) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["CHROME_48_BLOB"]); + EXPECT_EQ(actualStackFrames.size(), 7); + + std::vector expectedStackFrames = { + {std::nullopt, "Error", std::nullopt, std::nullopt}, + {"blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379", + "s", + 31, + 29145}, + {"blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379", + "Object.d [as add]", + 31, + 30038}, + {"blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a", + "", + 15, + 10977}, + {"blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379", + "", + 1, + 6910}, + {"blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379", + "n.fire", + 7, + 3018}, + {"blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379", + "n.handle", + 7, + 2862}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, ie10Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IE_10"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "Anonymous function", 48, 12}, + {"http://path/to/file.js", "foo", 46, 8}, + {"http://path/to/file.js", "bar", 82, 0}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, ie11Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IE_11"]); + EXPECT_EQ(actualStackFrames.size(), 3); + + std::vector expectedStackFrames = { + {"http://path/to/file.js", "Anonymous function", 47, 20}, + {"http://path/to/file.js", "foo", 45, 12}, + {"http://path/to/file.js", "bar", 108, 0}}; + + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, ie11EvalError) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IE_11_EVAL"]); + EXPECT_EQ(actualStackFrames.size(), 3); + std::vector expectedStackFrames = { + {"eval code", "eval code", 1, 0}, + {"http://path/to/file.js", "foo", 58, 16}, + {"http://path/to/file.js", "bar", 109, 0}}; + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, Opera25Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["OPERA_25"]); + EXPECT_EQ(actualStackFrames.size(), 3); + std::vector expectedStackFrames = { + {"http://path/to/file.js", "", 47, 21}, + {"http://path/to/file.js", "foo", 52, 14}, + {"http://path/to/file.js", "bar", 108, 167}}; + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, PhantomJS119Error) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["PHANTOMJS_1_19"]); + EXPECT_EQ(actualStackFrames.size(), 3); + std::vector expectedStackFrames = { + {"file:///path/to/file.js", "", 878, std::nullopt}, + {"http://path/to/file.js", "foo", 4283, std::nullopt}, + {"http://path/to/file.js", "", 4287, std::nullopt}}; + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, FirefoxResourceUrlError) { + auto actualStackFrames = StackTraceParser::parse( + false, CapturedExceptions["FIREFOX_50_RESOURCE_URL"]); + EXPECT_EQ(actualStackFrames.size(), 3); + std::vector expectedStackFrames = { + {"resource://path/data/content/bundle.js", "render", 5529, 15}}; + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, FirefoxEvalUrlError) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["FIREFOX_43_EVAL"]); + EXPECT_EQ(actualStackFrames.size(), 5); + std::vector expectedStackFrames = { + {"http://localhost:8080/file.js", "baz", 26, std::nullopt}, + {"http://localhost:8080/file.js", "foo", 26, std::nullopt}, + {"http://localhost:8080/file.js", "", 26, std::nullopt}, + {"http://localhost:8080/file.js", "speak", 26, 16}, + {"http://localhost:8080/file.js", "", 33, 8}}; + for (auto i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, ReactNativeAndroidError) { + auto actualStackFrames = StackTraceParser::parse( + false, CapturedExceptions["ANDROID_REACT_NATIVE"]); + EXPECT_EQ(actualStackFrames.size(), 8); + std::vector expectedStackFrames = { + {"/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js", + "render", + 78, + 23}, + {"/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js", + "this", + 74, + 40}}; + + EXPECT_EQ(actualStackFrames[0].column, expectedStackFrames[0].column); + EXPECT_EQ(actualStackFrames[0].file, expectedStackFrames[0].file); + EXPECT_EQ(actualStackFrames[0].lineNumber, expectedStackFrames[0].lineNumber); + EXPECT_EQ(actualStackFrames[0].methodName, expectedStackFrames[0].methodName); + + EXPECT_EQ(actualStackFrames[7].column, expectedStackFrames[1].column); + EXPECT_EQ(actualStackFrames[7].file, expectedStackFrames[1].file); + EXPECT_EQ(actualStackFrames[7].lineNumber, expectedStackFrames[1].lineNumber); + EXPECT_EQ(actualStackFrames[7].methodName, expectedStackFrames[1].methodName); +} + +TEST(StackTraceParser, ReactNativeAndroidProdError) { + auto actualStackFrames = StackTraceParser::parse( + false, CapturedExceptions["ANDROID_REACT_NATIVE_PROD"]); + EXPECT_EQ(actualStackFrames.size(), 37); + std::vector expectedStackFrames = { + {"index.android.bundle", "value", 12, 1916}, + {"index.android.bundle", "value", 29, 926}, + {"[native code]", "", std::nullopt, std::nullopt}}; + EXPECT_EQ(actualStackFrames[0].column, expectedStackFrames[0].column); + EXPECT_EQ(actualStackFrames[0].file, expectedStackFrames[0].file); + EXPECT_EQ(actualStackFrames[0].lineNumber, expectedStackFrames[0].lineNumber); + EXPECT_EQ(actualStackFrames[0].methodName, expectedStackFrames[0].methodName); + + EXPECT_EQ(actualStackFrames[35].column, expectedStackFrames[1].column); + EXPECT_EQ(actualStackFrames[35].file, expectedStackFrames[1].file); + EXPECT_EQ( + actualStackFrames[35].lineNumber, expectedStackFrames[1].lineNumber); + EXPECT_EQ( + actualStackFrames[35].methodName, expectedStackFrames[1].methodName); + + EXPECT_EQ(actualStackFrames[36].column, expectedStackFrames[2].column); + EXPECT_EQ(actualStackFrames[36].file, expectedStackFrames[2].file); + EXPECT_EQ( + actualStackFrames[36].lineNumber, expectedStackFrames[2].lineNumber); + EXPECT_EQ( + actualStackFrames[36].methodName, expectedStackFrames[2].methodName); +} + +TEST(StackTraceParser, NodeJsAsyncErrorsVersion12) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["NODE_12"]); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"/home/xyz/hack/asyncnode.js", "promiseMe", 11, 8}, + {"/home/xyz/hack/asyncnode.js", "async main", 15, 12}}; + + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, NodeJsErrorsWithAnonymousCalls) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["NODE_ANONYM"]); + EXPECT_EQ(actualStackFrames.size(), 9); + std::vector expectedStackFrames = { + {R"(C:\projects\spect\src\index.js)", "Spect.get", 161, 25}, + {R"(C:\projects\spect\src\index.js)", + "(anonymous function).then", + 165, + 32}, + {R"(C:\projects\spect\node_modules\esm\esm.js)", "", 1, 34534}, + {R"(C:\projects\spect\node_modules\esm\esm.js)", + "process.", + 1, + 34505}}; + // Check specific stack frames as per the JavaScript test + EXPECT_EQ(actualStackFrames[0].column, expectedStackFrames[0].column); + EXPECT_EQ(actualStackFrames[0].file, expectedStackFrames[0].file); + EXPECT_EQ(actualStackFrames[0].lineNumber, expectedStackFrames[0].lineNumber); + EXPECT_EQ(actualStackFrames[0].methodName, expectedStackFrames[0].methodName); + + EXPECT_EQ(actualStackFrames[2].column, expectedStackFrames[1].column); + EXPECT_EQ(actualStackFrames[2].file, expectedStackFrames[1].file); + EXPECT_EQ(actualStackFrames[2].lineNumber, expectedStackFrames[1].lineNumber); + EXPECT_EQ(actualStackFrames[2].methodName, expectedStackFrames[1].methodName); + + EXPECT_EQ(actualStackFrames[4].column, expectedStackFrames[2].column); + EXPECT_EQ(actualStackFrames[4].file, expectedStackFrames[2].file); + EXPECT_EQ(actualStackFrames[4].lineNumber, expectedStackFrames[2].lineNumber); + EXPECT_EQ(actualStackFrames[4].methodName, expectedStackFrames[2].methodName); + + EXPECT_EQ(actualStackFrames[6].column, expectedStackFrames[3].column); + EXPECT_EQ(actualStackFrames[6].file, expectedStackFrames[3].file); + EXPECT_EQ(actualStackFrames[6].lineNumber, expectedStackFrames[3].lineNumber); + EXPECT_EQ(actualStackFrames[6].methodName, expectedStackFrames[3].methodName); +} + +TEST(StackTraceParser, AnonymousSources) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["ANONYMOUS_SOURCES"]); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"http://www.example.com/test.js", "new ", 2, 0}, + {"", "", 1, 1}}; + + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, NodeJsTest1) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["NODE_JS_TEST_1"]); + EXPECT_EQ(actualStackFrames.size(), 5); + std::vector expectedStackFrames = { + {"repl", "", 1, 1}, + {"repl.js", "REPLServer.self.eval", 110, 20}, + {"repl.js", "Interface.", 239, 11}, + {"events.js", "Interface.EventEmitter.emit", 95, 16}, + {"readline.js", "emitKey", 1095, 11}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, NodeJsTest2) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["NODE_JS_TEST_2"]); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"repl", "null._onTimeout", 1, 24}, + {"timers.js", "Timer.listOnTimeout [as ontimeout]", 110, 14}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, IoJs) { + auto actualStackFrames = + StackTraceParser::parse(false, CapturedExceptions["IO_JS"]); + EXPECT_EQ(actualStackFrames.size(), 10); + std::vector expectedStackFrames = { + {"repl", "", 1, 0}, + {"repl.js", "REPLServer.defaultEval", 154, 26}, + {"domain.js", "bound", 254, 13}, + {"domain.js", "REPLServer.runBound [as eval]", 267, 11}, + {"repl.js", "REPLServer.", 308, 11}, + {"events.js", "emitOne", 77, 12}, + {"events.js", "REPLServer.emit", 169, 6}, + {"readline.js", "REPLServer.Interface._onLine", 210, 9}, + {"readline.js", "REPLServer.Interface._line", 549, 7}, + {"readline.js", "REPLServer.Interface._ttyWrite", 826, 13}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +/** + * Hermes tests + */ +TEST(StackTraceParser, hermesBytecodeLocation) { + auto actualStackFrames = StackTraceParser::parse( + true, + "TypeError: undefined is not a function\n" + " at global (address at unknown:1:9)\n" + " at foo$bar (address at /js/foo.hbc:10:1234)"); + + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"unknown", "global", 1, 9}, {"/js/foo.hbc", "foo$bar", 10, 1234}}; + + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, internalBytecodeLocation) { + auto actualStackFrames = StackTraceParser::parse( + true, + "TypeError: undefined is not a function\n" + " at internal (address at InternalBytecode.js:1:9)\n" + " at notInternal (address at /js/InternalBytecode.js:10:1234)"); + EXPECT_EQ(actualStackFrames.size(), 1); + std::vector expectedStackFrames = { + {"/js/InternalBytecode.js", "notInternal", 10, 1234}}; + + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, sourceLocation) { + auto actualStackFrames = StackTraceParser::parse( + true, + "TypeError: undefined is not a function\n" + " at global (unknown:1:9)\n" + " at foo$bar (/js/foo.js:10:1234)"); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"unknown", "global", 1, 8}, {"/js/foo.js", "foo$bar", 10, 1233}}; + + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, tolerateEmptyFilename) { + auto actualStackFrames = StackTraceParser::parse( + true, + "TypeError: undefined is not a function\n" + " at global (unknown:1:9)\n" + " at foo$bar (:10:1234)"); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"unknown", "global", 1, 8}, {"", "foo$bar", 10, 1233}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, skippedFrames) { + auto actualStackFrames = StackTraceParser::parse( + true, + "TypeError: undefined is not a function\n" + " at global (unknown:1:9)\n" + " ... skipping 50 frames\n" + " at foo$bar (/js/foo.js:10:1234)"); + EXPECT_EQ(actualStackFrames.size(), 2); + std::vector expectedStackFrames = { + {"unknown", "global", 1, 8}, {"/js/foo.js", "foo$bar", 10, 1233}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} + +TEST(StackTraceParser, handleNonStandardLines) { + auto actualStackFrames = StackTraceParser::parse( + true, + "The next line is not a stack frame\n" + " at bogus (filename:1:2)\n" + " but the real stack trace follows below.\n" + " at foo$bar (/js/foo.js:10:1234)"); + EXPECT_EQ(actualStackFrames.size(), 1); + std::vector expectedStackFrames = { + {"/js/foo.js", "foo$bar", 10, 1233}}; + for (size_t i = 0; i < expectedStackFrames.size(); i++) { + EXPECT_EQ(actualStackFrames[i].column, expectedStackFrames[i].column); + EXPECT_EQ(actualStackFrames[i].file, expectedStackFrames[i].file); + EXPECT_EQ( + actualStackFrames[i].lineNumber, expectedStackFrames[i].lineNumber); + EXPECT_EQ( + actualStackFrames[i].methodName, expectedStackFrames[i].methodName); + } +} diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp index eebe18dafaa2b1..7389b61ee1d84d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp @@ -722,6 +722,136 @@ TYPED_TEST(JsiIntegrationHermesTest, FunctionDescriptionIncludesName) { })"); } +TYPED_TEST(JsiIntegrationHermesTest, ReleaseRemoteObject) { + this->connect(); + + InSequence s; + + // Create a remote object. + auto objectInfo = this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/id", 1), + AtJsonPtr("/result/result/type", "object"), + AtJsonPtr("/result/result/objectId", Not(IsEmpty()))))); + this->toPage_->sendMessage(R"({ + "id": 1, + "method": "Runtime.evaluate", + "params": {"expression": "[]"} + })"); + + ASSERT_TRUE(objectInfo->has_value()); + auto objectId = objectInfo->value()["result"]["result"]["objectId"]; + + // Ensure we can get the properties of the object. + this->expectMessageFromPage(JsonParsed( + AllOf(AtJsonPtr("/id", 2), AtJsonPtr("/result/result", SizeIs(Gt(0)))))); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 2, + "method": "Runtime.getProperties", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); + + // Release the object. + this->expectMessageFromPage(JsonEq(R"({ + "id": 3, + "result": {} + })")); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 3, + "method": "Runtime.releaseObject", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); + + // Getting properties for a released object results in an error. + this->expectMessageFromPage( + JsonParsed(AllOf(AtJsonPtr("/id", 4), AtJsonPtr("/error/code", -32000)))); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 4, + "method": "Runtime.getProperties", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); + + // Releasing an already released object is an error. + this->expectMessageFromPage( + JsonParsed(AllOf(AtJsonPtr("/id", 5), AtJsonPtr("/error/code", -32000)))); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 5, + "method": "Runtime.releaseObject", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); +} + +TYPED_TEST(JsiIntegrationHermesTest, ReleaseRemoteObjectGroup) { + this->connect(); + + InSequence s; + + // Create a remote object. + auto objectInfo = this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/id", 1), + AtJsonPtr("/result/result/type", "object"), + AtJsonPtr("/result/result/objectId", Not(IsEmpty()))))); + this->toPage_->sendMessage(R"({ + "id": 1, + "method": "Runtime.evaluate", + "params": {"expression": "[]", "objectGroup": "foo"} + })"); + + ASSERT_TRUE(objectInfo->has_value()); + auto objectId = objectInfo->value()["result"]["result"]["objectId"]; + + // Ensure we can get the properties of the object. + this->expectMessageFromPage(JsonParsed( + AllOf(AtJsonPtr("/id", 2), AtJsonPtr("/result/result", SizeIs(Gt(0)))))); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 2, + "method": "Runtime.getProperties", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); + + // Release the object group containing our object. + this->expectMessageFromPage(JsonEq(R"({ + "id": 3, + "result": {} + })")); + this->toPage_->sendMessage(R"({ + "id": 3, + "method": "Runtime.releaseObjectGroup", + "params": {"objectGroup": "foo"} + })"); + + // Getting properties for a released object results in an error. + this->expectMessageFromPage( + JsonParsed(AllOf(AtJsonPtr("/id", 4), AtJsonPtr("/error/code", -32000)))); + this->toPage_->sendMessage(sformat( + R"({{ + "id": 4, + "method": "Runtime.getProperties", + "params": {{"objectId": {}, "ownProperties": true}} + }})", + folly::toJson(objectId))); + + // Releasing an already released object group is a no-op. + this->expectMessageFromPage(JsonEq(R"({ + "id": 5, + "result": {} + })")); + this->toPage_->sendMessage(R"({ + "id": 5, + "method": "Runtime.releaseObjectGroup", + "params": {"objectGroup": "foo"} + })"); +} + #pragma endregion // AllHermesVariants } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp index 3c3f81edd1f470..42549ee8c8a1e2 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp @@ -34,19 +34,10 @@ void ReactInstanceIntegrationTest::SetUp() { auto timerManager = std::make_shared(std::move(mockRegistry)); - auto onJsError = [](const JsErrorHandler::ParsedError& errorMap) noexcept { + auto onJsError = [](jsi::Runtime& /*runtime*/, + const JsErrorHandler::ParsedError& error) noexcept { LOG(INFO) << "[jsErrorHandlingFunc called]"; - LOG(INFO) << "message: " << errorMap.message; - LOG(INFO) << "exceptionId: " << std::to_string(errorMap.exceptionId); - LOG(INFO) << "isFatal: " - << std::to_string(static_cast(errorMap.isFatal)); - auto frames = errorMap.frames; - for (const auto& mapBuffer : frames) { - LOG(INFO) << "[Frame]" << std::endl << "\tfile: " << mapBuffer.fileName; - LOG(INFO) << "\tmethodName: " << mapBuffer.methodName; - LOG(INFO) << "\tlineNumber: " << std::to_string(mapBuffer.lineNumber); - LOG(INFO) << "\tcolumn: " << std::to_string(mapBuffer.columnNumber); - } + LOG(INFO) << error << std::endl; }; auto jsRuntimeFactory = std::make_unique(); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 1e09a9e471b342..a7166723f81831 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -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<<2eeb1b7571481a8404226d0bbc8d2e5d>> + * @generated SignedSource<> */ /** @@ -21,6 +21,11 @@ namespace facebook::react { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wglobal-constructors" +std::unique_ptr accessor_; +#pragma GCC diagnostic pop + bool ReactNativeFeatureFlags::commonTestFlag() { return getAccessor().commonTestFlag(); } @@ -227,16 +232,26 @@ void ReactNativeFeatureFlags::override( } void ReactNativeFeatureFlags::dangerouslyReset() { - getAccessor(true); + accessor_ = std::make_unique(); +} + +std::optional ReactNativeFeatureFlags::dangerouslyForceOverride( + std::unique_ptr provider) { + auto accessor = std::make_unique(); + accessor->override(std::move(provider)); + + std::swap(accessor_, accessor); + + // Now accessor is the old accessor + return accessor == nullptr ? std::nullopt + : accessor->getAccessedFeatureFlagNames(); } -ReactNativeFeatureFlagsAccessor& ReactNativeFeatureFlags::getAccessor( - bool reset) { - static std::unique_ptr accessor; - if (accessor == nullptr || reset) { - accessor = std::make_unique(); +ReactNativeFeatureFlagsAccessor& ReactNativeFeatureFlags::getAccessor() { + if (accessor_ == nullptr) { + accessor_ = std::make_unique(); } - return *accessor; + return *accessor_; } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index d65c2917dc30e3..eb591218a8035d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -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<<6c4322adf33ac5b1bc921b306b2f5f8f>> + * @generated SignedSource<> */ /** @@ -22,6 +22,8 @@ #include #include #include +#include +#include #ifndef RN_EXPORT #define RN_EXPORT __attribute__((visibility("default"))) @@ -322,9 +324,24 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static void dangerouslyReset(); + /** + * This is a combination of `dangerouslyReset` and `override` that reduces + * the likeliness of a race condition between the two calls. + * + * This is **dangerous** because it can introduce consistency issues that will + * be much harder to debug. For example, it could hide the fact that feature + * flags are read before you set the values you want to use everywhere. It + * could also cause a workflow to suddently have different feature flags for + * behaviors that were configured with different values before. + * + * Please see the documentation of `dangerouslyReset` for additional details. + */ + RN_EXPORT static std::optional dangerouslyForceOverride( + std::unique_ptr provider); + private: ReactNativeFeatureFlags() = delete; - static ReactNativeFeatureFlagsAccessor& getAccessor(bool reset = false); + static ReactNativeFeatureFlagsAccessor& getAccessor(); }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index b156380fe6fad5..19456178b848ec 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -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<<351c20a1608a72fe03ef4e8082496964>> + * @generated SignedSource<> */ /** @@ -941,13 +941,8 @@ void ReactNativeFeatureFlagsAccessor::override( currentProvider_ = std::move(provider); } -void ReactNativeFeatureFlagsAccessor::markFlagAsAccessed( - int position, - const char* flagName) { - accessedFeatureFlags_[position] = flagName; -} - -void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { +std::optional +ReactNativeFeatureFlagsAccessor::getAccessedFeatureFlagNames() const { std::ostringstream featureFlagListBuilder; for (const auto& featureFlagName : accessedFeatureFlags_) { if (featureFlagName != nullptr) { @@ -961,10 +956,24 @@ void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { accessedFeatureFlagNames.substr(0, accessedFeatureFlagNames.size() - 2); } - if (!accessedFeatureFlagNames.empty()) { + return accessedFeatureFlagNames.empty() + ? std::nullopt + : std::optional{accessedFeatureFlagNames}; +} + +void ReactNativeFeatureFlagsAccessor::markFlagAsAccessed( + int position, + const char* flagName) { + accessedFeatureFlags_[position] = flagName; +} + +void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { + auto accessedFeatureFlagNames = getAccessedFeatureFlagNames(); + + if (accessedFeatureFlagNames.has_value()) { throw std::runtime_error( "Feature flags were accessed before being overridden: " + - accessedFeatureFlagNames); + accessedFeatureFlagNames.value()); } } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 2aa3df0238a430..92099b6b79845e 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -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<<7b875c6b1b7120f664ab511a4b91c8d8>> + * @generated SignedSource<> */ /** @@ -24,6 +24,7 @@ #include #include #include +#include namespace facebook::react { @@ -83,6 +84,7 @@ class ReactNativeFeatureFlagsAccessor { bool useTurboModules(); void override(std::unique_ptr provider); + std::optional getAccessedFeatureFlagNames() const; private: void markFlagAsAccessed(int position, const char* flagName); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index b3516b3f38a0a9..0882336dad3cc8 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -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<<0d812fecaa84185ac194eb680318a4e3>> + * @generated SignedSource<> */ /** @@ -216,7 +216,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool useRuntimeShadowNodeReferenceUpdate() override { - return true; + return false; } bool useTurboModuleInterop() override { diff --git a/packages/react-native/ReactCommon/react/featureflags/tests/ReactNativeFeatureFlagsTest.cpp b/packages/react-native/ReactCommon/react/featureflags/tests/ReactNativeFeatureFlagsTest.cpp index 2047767dbdef39..dad5df2afa7719 100644 --- a/packages/react-native/ReactCommon/react/featureflags/tests/ReactNativeFeatureFlagsTest.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/tests/ReactNativeFeatureFlagsTest.cpp @@ -121,4 +121,27 @@ TEST_F(ReactNativeFeatureFlagsTest, allowsOverridingAgainAfterReset) { EXPECT_EQ(ReactNativeFeatureFlags::commonTestFlag(), true); } +TEST_F( + ReactNativeFeatureFlagsTest, + allowsDangerouslyForcingOverridesWhenValuesHaveNotBeenAccessed) { + auto accessedFlags = ReactNativeFeatureFlags::dangerouslyForceOverride( + std::make_unique()); + + EXPECT_EQ(ReactNativeFeatureFlags::commonTestFlag(), true); + EXPECT_EQ(accessedFlags.has_value(), false); +} + +TEST_F( + ReactNativeFeatureFlagsTest, + allowsDangerouslyForcingOverridesWhenValuesHaveBeenAccessed) { + EXPECT_EQ(ReactNativeFeatureFlags::commonTestFlag(), false); + + auto accessedFlags = ReactNativeFeatureFlags::dangerouslyForceOverride( + std::make_unique()); + + EXPECT_EQ(ReactNativeFeatureFlags::commonTestFlag(), true); + EXPECT_EQ(accessedFlags.has_value(), true); + EXPECT_EQ(accessedFlags.value(), "commonTestFlag"); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index fe4d66abb3673a..b7e32bdead22a2 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -404,7 +404,10 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy } _turboModuleCache.insert({moduleName, turboModule}); - if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) { + if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) { + [(id)module installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker]; + } else if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) { + // Old API without CallInvoker (deprecated) [(id)module installJSIBindingsWithRuntime:*runtime]; } return turboModule; diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleWithJSIBindings.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleWithJSIBindings.h index 44c8cfceb8b938..60e29bd164a674 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleWithJSIBindings.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleWithJSIBindings.h @@ -8,13 +8,21 @@ #import #ifdef __cplusplus +#include #include #endif @protocol RCTTurboModuleWithJSIBindings #ifdef __cplusplus -- (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime; + +@optional +- (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime + callInvoker:(const std::shared_ptr &)callinvoker; + +- (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime + __attribute__((deprecated("Use 'installJSIBindingsWithRuntime:callInvoker:' instead"))); + #endif @end diff --git a/packages/react-native/ReactCommon/react/nativemodule/defaults/CMakeLists.txt b/packages/react-native/ReactCommon/react/nativemodule/defaults/CMakeLists.txt index 952ffe520cc589..7984050cbdec1d 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/defaults/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/nativemodule/defaults/CMakeLists.txt @@ -21,6 +21,7 @@ target_include_directories(react_nativemodule_defaults PUBLIC ${REACT_COMMON_DIR target_link_libraries(react_nativemodule_defaults react_nativemodule_dom + react_nativemodule_devtoolsruntimesettings react_nativemodule_featureflags react_nativemodule_microtasks react_nativemodule_idlecallbacks diff --git a/packages/react-native/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp b/packages/react-native/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp index 1e0201666d2c59..2d857ec8686009 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp @@ -11,6 +11,10 @@ #include #include +#ifdef HERMES_ENABLE_DEBUGGER +#include +#endif + namespace facebook::react { /* static */ std::shared_ptr DefaultTurboModules::getTurboModule( @@ -32,6 +36,12 @@ namespace facebook::react { return std::make_shared(jsInvoker); } +#ifdef HERMES_ENABLE_DEBUGGER + if (name == DevToolsRuntimeSettingsModule::kModuleName) { + return std::make_shared(jsInvoker); + } +#endif + return nullptr; } diff --git a/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/CMakeLists.txt b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/CMakeLists.txt new file mode 100644 index 00000000000000..318e0f5d6b1075 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fexceptions + -frtti + -std=c++20 + -Wall + -Wpedantic + -DLOG_TAG=\"ReactNative\") + +file(GLOB react_nativemodule_devtoolsruntimesettings_SRC CONFIGURE_DEPENDS *.cpp) +add_library(react_nativemodule_devtoolsruntimesettings OBJECT ${react_nativemodule_devtoolsruntimesettings_SRC}) + +target_include_directories(react_nativemodule_devtoolsruntimesettings PUBLIC ${REACT_COMMON_DIR}) + +target_link_libraries(react_nativemodule_devtoolsruntimesettings + react_devtoolsruntimesettingscxx +) diff --git a/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.cpp new file mode 100644 index 00000000000000..4b0e5ca0bd2f6a --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.cpp @@ -0,0 +1,35 @@ +/* + * 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 "DevToolsRuntimeSettingsModule.h" +#include "Plugins.h" + +std::shared_ptr +ReactDevToolsRuntimeSettingsModuleProvider( + std::shared_ptr jsInvoker) { + return std::make_shared( + std::move(jsInvoker)); +} + +namespace facebook::react { + +DevToolsRuntimeSettingsModule::DevToolsRuntimeSettingsModule( + std::shared_ptr jsInvoker) + : NativeReactDevToolsRuntimeSettingsModuleCxxSpec(std::move(jsInvoker)) {} + +void DevToolsRuntimeSettingsModule::setReloadAndProfileConfig( + jsi::Runtime& /*rt*/, + NativePartialReloadAndProfileConfig config) { + DevToolsRuntimeSettings::getInstance().setReloadAndProfileConfig(config); +}; + +NativeReloadAndProfileConfig +DevToolsRuntimeSettingsModule::getReloadAndProfileConfig(jsi::Runtime& /*rt*/) { + return DevToolsRuntimeSettings::getInstance().getReloadAndProfileConfig(); +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.h b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.h new file mode 100644 index 00000000000000..6f4d54aeb1c70c --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/DevToolsRuntimeSettingsModule.h @@ -0,0 +1,27 @@ +/* + * 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 "devtoolsruntimesettingscxx/DevToolsRuntimeSettings.h" + +namespace facebook::react { + +class DevToolsRuntimeSettingsModule + : public NativeReactDevToolsRuntimeSettingsModuleCxxSpec< + DevToolsRuntimeSettingsModule> { + public: + DevToolsRuntimeSettingsModule(std::shared_ptr jsInvoker); + + void setReloadAndProfileConfig( + jsi::Runtime& rt, + NativePartialReloadAndProfileConfig config); + + NativeReloadAndProfileConfig getReloadAndProfileConfig(jsi::Runtime& rt); +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/README.md b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/README.md new file mode 100644 index 00000000000000..cbf4f5d7430569 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/devtoolsruntimesettings/README.md @@ -0,0 +1,6 @@ +# Options for DevTools Settings + +| Module | Survives native restarts | Survives JavaScript VM restarts | +| --- | --- | --- | +| DevToolsRuntimeSettings | No | Yes +| DevToolsSettings | Yes | Yes diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleJSIBindings.cpp b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleJSIBindings.cpp index 44fbfa2be1b5f9..5495b5b88d9550 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleJSIBindings.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleJSIBindings.cpp @@ -22,10 +22,11 @@ void SampleTurboModuleJSIBindings::registerNatives() { jni::local_ref SampleTurboModuleJSIBindings::getBindingsInstaller( jni::alias_ref /*jobj*/) { - return BindingsInstallerHolder::newObjectCxxArgs([](jsi::Runtime& runtime) { - runtime.global().setProperty( - runtime, "__SampleTurboModuleJSIBindings", "Hello JSI!"); - }); + return BindingsInstallerHolder::newObjectCxxArgs( + [](jsi::Runtime& runtime, const std::shared_ptr&) { + runtime.global().setProperty( + runtime, "__SampleTurboModuleJSIBindings", "Hello JSI!"); + }); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm index c9132b0f032de2..4813d906a5e8d1 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm @@ -73,6 +73,7 @@ - (NSDictionary *)constantsToExport #pragma mark - RCTTurboModuleWithJSIBindings - (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime + callInvoker:(const std::shared_ptr &)callinvoker { runtime.global().setProperty(runtime, "__SampleTurboModuleJSIBindings", "Hello JSI!"); } diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp index 9f6897f2613308..620b678cd804a2 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp @@ -53,7 +53,7 @@ uint32_t PerformanceEntryReporter::getDroppedEntriesCount( PerformanceEntryType entryType) const noexcept { std::shared_lock lock(buffersMutex_); - return getBuffer(entryType).droppedEntriesCount; + return (uint32_t)getBuffer(entryType).droppedEntriesCount; } std::vector PerformanceEntryReporter::getEntries() const { diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index 956bb8d0071c37..71526197456bad 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -125,6 +125,7 @@ class PerformanceEntryReporter { case PerformanceEntryType::_NEXT: throw std::logic_error("Cannot get buffer for _NEXT entry type"); } + throw std::logic_error("Unhandled PerformanceEntryType"); } inline PerformanceEntryBuffer& getBufferRef(PerformanceEntryType entryType) { @@ -140,6 +141,7 @@ class PerformanceEntryReporter { case PerformanceEntryType::_NEXT: throw std::logic_error("Cannot get buffer for _NEXT entry type"); } + throw std::logic_error("Unhandled PerformanceEntryType"); } }; diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp index 95313a2e91f98d..08dadd11f95b74 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp @@ -43,6 +43,7 @@ jsi::Value ScrollEvent::asJSIValue(jsi::Runtime& runtime) const { } payload.setProperty(runtime, "zoomScale", zoomScale); + payload.setProperty(runtime, "timestamp", timestamp * 1000); return payload; } @@ -61,11 +62,12 @@ folly::dynamic ScrollEvent::asDynamic() const { auto containerSizeObj = folly::dynamic::object("width", containerSize.width)( "height", containerSize.height); - auto metrics = folly::dynamic::object( - "contentOffset", std::move(contentOffsetObj))( - "contentInset", std::move(contentInsetObj))( - "contentSize", std::move(contentSizeObj))( - "layoutMeasurement", std::move(containerSizeObj))("zoomScale", zoomScale); + auto metrics = + folly::dynamic::object("contentOffset", std::move(contentOffsetObj))( + "contentInset", std::move(contentInsetObj))( + "contentSize", std::move(contentSizeObj))( + "layoutMeasurement", std::move(containerSizeObj))( + "zoomScale", zoomScale)("timestamp", timestamp * 1000); return metrics; }; @@ -91,7 +93,7 @@ std::vector getDebugProps( {"layoutMeasurement", getDebugDescription(scrollEvent.layoutMeasurement, options)}, {"zoomScale", getDebugDescription(scrollEvent.zoomScale, options)}, - }; + {"timestamp", getDebugDescription(scrollEvent.timestamp, options)}}; } #endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h index 5a8ff342f3e53d..8eac9e879b5448 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h @@ -21,6 +21,11 @@ struct ScrollEvent : public EventPayload { Size containerSize; Float zoomScale{}; + /* + * The time in seconds when the touch occurred or when it was last mutated. + */ + Float timestamp{}; + ScrollEvent() = default; folly::dynamic asDynamic() const; diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp index df2adf005c64db..674c0e2c921621 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp @@ -117,7 +117,7 @@ ScrollViewProps::ScrollViewProps( rawProps, "endDraggingSensitivityMultiplier", sourceProps.endDraggingSensitivityMultiplier, - 1)), + (Float)1)), enableSyncOnScroll( CoreFeatures::enablePropIteratorSetter ? sourceProps.enableSyncOnScroll diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h index 6ee6c1e73c192c..dd31d0ba53dca5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputComponentDescriptor.h @@ -105,23 +105,25 @@ class AndroidTextInputComponentDescriptor final !textInputProps.hasPaddingLeft && !textInputProps.hasPaddingHorizontal) { changedPadding = true; - style.setPadding(yoga::Edge::Start, yoga::value::points(theme.start)); + style.setPadding( + yoga::Edge::Start, yoga::StyleLength::points(theme.start)); } if (!textInputProps.hasPadding && !textInputProps.hasPaddingEnd && !textInputProps.hasPaddingRight && !textInputProps.hasPaddingHorizontal) { changedPadding = true; - style.setPadding(yoga::Edge::End, yoga::value::points(theme.end)); + style.setPadding(yoga::Edge::End, yoga::StyleLength::points(theme.end)); } if (!textInputProps.hasPadding && !textInputProps.hasPaddingTop && !textInputProps.hasPaddingVertical) { changedPadding = true; - style.setPadding(yoga::Edge::Top, yoga::value::points(theme.top)); + style.setPadding(yoga::Edge::Top, yoga::StyleLength::points(theme.top)); } if (!textInputProps.hasPadding && !textInputProps.hasPaddingBottom && !textInputProps.hasPaddingVertical) { changedPadding = true; - style.setPadding(yoga::Edge::Bottom, yoga::value::points(theme.bottom)); + style.setPadding( + yoga::Edge::Bottom, yoga::StyleLength::points(theme.bottom)); } // If the TextInput initially does not have paddingLeft or paddingStart, a @@ -132,12 +134,12 @@ class AndroidTextInputComponentDescriptor final if ((textInputProps.hasPadding || textInputProps.hasPaddingLeft || textInputProps.hasPaddingHorizontal) && !textInputProps.hasPaddingStart) { - style.setPadding(yoga::Edge::Start, yoga::value::undefined()); + style.setPadding(yoga::Edge::Start, yoga::StyleLength::undefined()); } if ((textInputProps.hasPadding || textInputProps.hasPaddingRight || textInputProps.hasPaddingHorizontal) && !textInputProps.hasPaddingEnd) { - style.setPadding(yoga::Edge::End, yoga::value::undefined()); + style.setPadding(yoga::Edge::End, yoga::StyleLength::undefined()); } // Note that this is expensive: on every adopt, we need to set the Yoga diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index ecd79739d0faf8..c57292dfbc54a5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -532,8 +532,10 @@ void YogaLayoutableShadowNode::setSize(Size size) const { ensureUnsealed(); auto style = yogaNode_.style(); - style.setDimension(yoga::Dimension::Width, yoga::value::points(size.width)); - style.setDimension(yoga::Dimension::Height, yoga::value::points(size.height)); + style.setDimension( + yoga::Dimension::Width, yoga::StyleLength::points(size.width)); + style.setDimension( + yoga::Dimension::Height, yoga::StyleLength::points(size.height)); yogaNode_.setStyle(style); yogaNode_.setDirty(true); } @@ -543,19 +545,21 @@ void YogaLayoutableShadowNode::setPadding(RectangleEdges padding) const { auto style = yogaNode_.style(); - auto leftPadding = yoga::value::points(padding.left); - auto topPadding = yoga::value::points(padding.top); - auto rightPadding = yoga::value::points(padding.right); - auto bottomPadding = yoga::value::points(padding.bottom); + auto leftPadding = yoga::StyleLength::points(padding.left); + auto topPadding = yoga::StyleLength::points(padding.top); + auto rightPadding = yoga::StyleLength::points(padding.right); + auto bottomPadding = yoga::StyleLength::points(padding.bottom); if (leftPadding != style.padding(yoga::Edge::Left) || topPadding != style.padding(yoga::Edge::Top) || rightPadding != style.padding(yoga::Edge::Right) || bottomPadding != style.padding(yoga::Edge::Bottom)) { - style.setPadding(yoga::Edge::Top, yoga::value::points(padding.top)); - style.setPadding(yoga::Edge::Left, yoga::value::points(padding.left)); - style.setPadding(yoga::Edge::Right, yoga::value::points(padding.right)); - style.setPadding(yoga::Edge::Bottom, yoga::value::points(padding.bottom)); + style.setPadding(yoga::Edge::Top, yoga::StyleLength::points(padding.top)); + style.setPadding(yoga::Edge::Left, yoga::StyleLength::points(padding.left)); + style.setPadding( + yoga::Edge::Right, yoga::StyleLength::points(padding.right)); + style.setPadding( + yoga::Edge::Bottom, yoga::StyleLength::points(padding.bottom)); yogaNode_.setStyle(style); yogaNode_.setDirty(true); } @@ -622,16 +626,16 @@ void YogaLayoutableShadowNode::layoutTree( auto ownerHeight = yogaFloatFromFloat(maximumSize.height); yogaStyle.setMaxDimension( - yoga::Dimension::Width, yoga::value::points(maximumSize.width)); + yoga::Dimension::Width, yoga::StyleLength::points(maximumSize.width)); yogaStyle.setMaxDimension( - yoga::Dimension::Height, yoga::value::points(maximumSize.height)); + yoga::Dimension::Height, yoga::StyleLength::points(maximumSize.height)); yogaStyle.setMinDimension( - yoga::Dimension::Width, yoga::value::points(minimumSize.width)); + yoga::Dimension::Width, yoga::StyleLength::points(minimumSize.width)); yogaStyle.setMinDimension( - yoga::Dimension::Height, yoga::value::points(minimumSize.height)); + yoga::Dimension::Height, yoga::StyleLength::points(minimumSize.height)); auto direction = yogaDirectionFromLayoutDirection(layoutConstraints.layoutDirection); @@ -894,44 +898,44 @@ void YogaLayoutableShadowNode::swapLeftAndRightInYogaStyleProps() { if (yogaStyle.position(yoga::Edge::Left).isDefined()) { yogaStyle.setPosition( yoga::Edge::Start, yogaStyle.position(yoga::Edge::Left)); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::undefined()); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::undefined()); } if (yogaStyle.position(yoga::Edge::Right).isDefined()) { yogaStyle.setPosition( yoga::Edge::End, yogaStyle.position(yoga::Edge::Right)); - yogaStyle.setPosition(yoga::Edge::Right, yoga::value::undefined()); + yogaStyle.setPosition(yoga::Edge::Right, yoga::StyleLength::undefined()); } if (yogaStyle.padding(yoga::Edge::Left).isDefined()) { yogaStyle.setPadding( yoga::Edge::Start, yogaStyle.padding(yoga::Edge::Left)); - yogaStyle.setPadding(yoga::Edge::Left, yoga::value::undefined()); + yogaStyle.setPadding(yoga::Edge::Left, yoga::StyleLength::undefined()); } if (yogaStyle.padding(yoga::Edge::Right).isDefined()) { yogaStyle.setPadding(yoga::Edge::End, yogaStyle.padding(yoga::Edge::Right)); - yogaStyle.setPadding(yoga::Edge::Right, yoga::value::undefined()); + yogaStyle.setPadding(yoga::Edge::Right, yoga::StyleLength::undefined()); } if (yogaStyle.margin(yoga::Edge::Left).isDefined()) { yogaStyle.setMargin(yoga::Edge::Start, yogaStyle.margin(yoga::Edge::Left)); - yogaStyle.setMargin(yoga::Edge::Left, yoga::value::undefined()); + yogaStyle.setMargin(yoga::Edge::Left, yoga::StyleLength::undefined()); } if (yogaStyle.margin(yoga::Edge::Right).isDefined()) { yogaStyle.setMargin(yoga::Edge::End, yogaStyle.margin(yoga::Edge::Right)); - yogaStyle.setMargin(yoga::Edge::Right, yoga::value::undefined()); + yogaStyle.setMargin(yoga::Edge::Right, yoga::StyleLength::undefined()); } if (yogaStyle.border(yoga::Edge::Left).isDefined()) { yogaStyle.setBorder(yoga::Edge::Start, yogaStyle.border(yoga::Edge::Left)); - yogaStyle.setBorder(yoga::Edge::Left, yoga::value::undefined()); + yogaStyle.setBorder(yoga::Edge::Left, yoga::StyleLength::undefined()); } if (yogaStyle.border(yoga::Edge::Right).isDefined()) { yogaStyle.setBorder(yoga::Edge::End, yogaStyle.border(yoga::Edge::Right)); - yogaStyle.setBorder(yoga::Edge::Right, yoga::value::undefined()); + yogaStyle.setBorder(yoga::Edge::Right, yoga::StyleLength::undefined()); } yogaNode_.setStyle(yogaStyle); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp index 3a3f7ee8cba3a6..4f36bf6bb813e1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp @@ -790,98 +790,98 @@ void YogaStylableProps::convertRawPropAliases( rawProps, "insetBlockEnd", sourceProps.insetBlockEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); insetBlockStart = convertRawProp( context, rawProps, "insetBlockStart", sourceProps.insetBlockStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); insetInlineEnd = convertRawProp( context, rawProps, "insetInlineEnd", sourceProps.insetInlineEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); insetInlineStart = convertRawProp( context, rawProps, "insetInlineStart", sourceProps.insetInlineStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginInline = convertRawProp( context, rawProps, "marginInline", sourceProps.marginInline, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginInlineStart = convertRawProp( context, rawProps, "marginInlineStart", sourceProps.marginInlineStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginInlineEnd = convertRawProp( context, rawProps, "marginInlineEnd", sourceProps.marginInlineEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginBlock = convertRawProp( context, rawProps, "marginBlock", sourceProps.marginBlock, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginBlockStart = convertRawProp( context, rawProps, "marginBlockStart", sourceProps.marginBlockStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); marginBlockEnd = convertRawProp( context, rawProps, "marginBlockEnd", sourceProps.marginBlockEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingInline = convertRawProp( context, rawProps, "paddingInline", sourceProps.paddingInline, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingInlineStart = convertRawProp( context, rawProps, "paddingInlineStart", sourceProps.paddingInlineStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingInlineEnd = convertRawProp( context, rawProps, "paddingInlineEnd", sourceProps.paddingInlineEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingBlock = convertRawProp( context, rawProps, "paddingBlock", sourceProps.paddingBlock, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingBlockStart = convertRawProp( context, rawProps, "paddingBlockStart", sourceProps.paddingBlockStart, - yoga::value::undefined()); + yoga::StyleLength::undefined()); paddingBlockEnd = convertRawProp( context, rawProps, "paddingBlockEnd", sourceProps.paddingBlockEnd, - yoga::value::undefined()); + yoga::StyleLength::undefined()); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index b1b71c0ebbf777..35f6047c318c22 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -437,31 +437,31 @@ inline void fromRawValue( const RawValue& value, yoga::Style::Length& result) { if (value.hasType()) { - result = yoga::value::points((float)value); + result = yoga::StyleLength::points((float)value); return; } else if (value.hasType()) { const auto stringValue = (std::string)value; if (stringValue == "auto") { - result = yoga::value::ofAuto(); + result = yoga::StyleLength::ofAuto(); return; } else { if (stringValue.back() == '%') { auto tryValue = folly::tryTo( std::string_view(stringValue).substr(0, stringValue.length() - 1)); if (tryValue.hasValue()) { - result = yoga::value::percent(tryValue.value()); + result = yoga::StyleLength::percent(tryValue.value()); return; } } else { auto tryValue = folly::tryTo(stringValue); if (tryValue.hasValue()) { - result = yoga::value::points(tryValue.value()); + result = yoga::StyleLength::points(tryValue.value()); return; } } } } - result = yoga::value::undefined(); + result = yoga::StyleLength::undefined(); } inline void fromRawValue( diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/tests/LayoutTest.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/tests/LayoutTest.cpp index 9933cce9f47ed7..5277a2b153401d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/tests/LayoutTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/tests/LayoutTest.cpp @@ -77,8 +77,8 @@ class LayoutTest : public ::testing::Test { auto &props = *sharedProps; props.layoutConstraints = LayoutConstraints{{0,0}, {500, 500}}; auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(200)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(200)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) .children({ @@ -90,8 +90,8 @@ class LayoutTest : public ::testing::Test { auto &props = *sharedProps; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(50)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(50)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(50)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(50)); return sharedProps; }) .children({ @@ -103,10 +103,10 @@ class LayoutTest : public ::testing::Test { auto &props = *sharedProps; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(10)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(10)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(30)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(90)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(10)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(10)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(30)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(90)); if (testCase == TRANSFORM_SCALE) { props.transform = props.transform * Transform::Scale(2, 2, 1); @@ -136,10 +136,10 @@ class LayoutTest : public ::testing::Test { } yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(10)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(10)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(110)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(20)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(10)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(10)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(110)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20)); return sharedProps; }) .children({ @@ -151,10 +151,10 @@ class LayoutTest : public ::testing::Test { auto &props = *sharedProps; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(70)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(-50)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(30)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(60)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(70)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(-50)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(30)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(60)); return sharedProps; }) }), @@ -166,10 +166,10 @@ class LayoutTest : public ::testing::Test { auto &props = *sharedProps; auto &yogaStyle = props.yogaStyle; yogaStyle.setPositionType(yoga::PositionType::Absolute); - yogaStyle.setPosition(yoga::Edge::Left, yoga::value::points(-60)); - yogaStyle.setPosition(yoga::Edge::Top, yoga::value::points(50)); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(70)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(20)); + yogaStyle.setPosition(yoga::Edge::Left, yoga::StyleLength::points(-60)); + yogaStyle.setPosition(yoga::Edge::Top, yoga::StyleLength::points(50)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(70)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(20)); return sharedProps; }) }) diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp index 39b1927c9bf224..17262807c0fe59 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.cpp @@ -18,18 +18,6 @@ void EventBeat::request() const { isRequested_ = true; } -void EventBeat::beat(jsi::Runtime& runtime) const { - if (!this->isRequested_) { - return; - } - - isRequested_ = false; - - if (beatCallback_) { - beatCallback_(runtime); - } -} - void EventBeat::induce() const { // Default implementation does nothing. } diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h index 7f2b143dd0b075..2a13a4af44e167 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventBeat.h @@ -7,13 +7,15 @@ #pragma once -#include #include #include #include -namespace facebook::react { +namespace facebook::jsi { +class Runtime; +} +namespace facebook::react { /* * Event Beat serves two interleaving purposes: synchronization of event queues * and ensuring that event dispatching happens on proper threads. @@ -57,17 +59,6 @@ class EventBeat { */ virtual void request() const; - /* - * Induces the next beat to happen as soon as possible. If the method - * is called on the proper thread, the beat must happen synchronously. - * Subclasses might override this method to implement specific - * out-of-turn beat scheduling. - * Some types of Event Beats do not support inducing, hence the default - * implementation does nothing. - * Receiver might ignore the call if a beat was not requested. - */ - virtual void induce() const; - /* * Sets the beat callback function. * The callback is must be called on the proper thread. @@ -76,10 +67,10 @@ class EventBeat { protected: /* - * Should be used by subclasses to send a beat. + * Induces the next beat to happen as soon as possible. * Receiver might ignore the call if a beat was not requested. */ - void beat(jsi::Runtime& runtime) const; + virtual void induce() const; BeatCallback beatCallback_; SharedOwnerBox ownerBox_; diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp index 9efe362d27bdee..daf4edc10777e6 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/EventDispatcher.cpp @@ -37,8 +37,8 @@ void EventDispatcher::dispatchEvent(RawEvent&& rawEvent) const { auto eventLogger = eventLogger_.lock(); if (eventLogger != nullptr) { - rawEvent.loggingTag = - eventLogger->onEventStart(rawEvent.type, rawEvent.eventTarget); + rawEvent.loggingTag = eventLogger->onEventStart( + rawEvent.type, rawEvent.eventTarget, rawEvent.eventStartTimeStamp); } eventQueue_.enqueueEvent(std::move(rawEvent)); } diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h b/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h index ceb3a7b02bf703..86fdc36297a756 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include namespace facebook::react { @@ -30,7 +31,9 @@ class EventLogger { */ virtual EventTag onEventStart( std::string_view name, - SharedEventTarget target) = 0; + SharedEventTarget target, + DOMHighResTimeStamp eventStartTimeStamp = + DOM_HIGH_RES_TIME_STAMP_UNSET) = 0; /* * Called when event starts getting dispatched (processed by the handlers, if diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h b/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h index 03a2f0857aa1a0..c79aca59aa9920 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h +++ b/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace facebook::react { @@ -69,6 +70,11 @@ struct RawEvent { SharedEventTarget eventTarget; Category category; EventTag loggingTag{0}; + + // The client may specify a platform-specific timestamp for the event start + // time, for example when MotionEvent was triggered on the Android native + // side. + DOMHighResTimeStamp eventStartTimeStamp{DOM_HIGH_RES_TIME_STAMP_UNSET}; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp index d9cd860098cbc6..a189a538453704 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.cpp @@ -31,7 +31,9 @@ ShadowNodeFamily::ShadowNodeFamily( eventEmitter_(std::move(eventEmitter)), componentDescriptor_(componentDescriptor), componentHandle_(componentDescriptor.getComponentHandle()), - componentName_(componentDescriptor.getComponentName()) {} + componentName_(componentDescriptor.getComponentName()), + isDeletionOfUnmountedViewsEnabled_( + ReactNativeFeatureFlags::enableDeletionOfUnmountedViews()) {} void ShadowNodeFamily::setParent(const ShadowNodeFamily::Shared& parent) const { react_native_assert(parent_.lock() == nullptr || parent_.lock() == parent); @@ -77,8 +79,8 @@ Tag ShadowNodeFamily::getTag() const { } ShadowNodeFamily::~ShadowNodeFamily() { - if (ReactNativeFeatureFlags::enableDeletionOfUnmountedViews() && - !hasBeenMounted_ && onUnmountedFamilyDestroyedCallback_ != nullptr) { + if (isDeletionOfUnmountedViewsEnabled_ && !hasBeenMounted_ && + onUnmountedFamilyDestroyedCallback_ != nullptr) { onUnmountedFamilyDestroyedCallback_(*this); } } diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h index c06213774192dd..44453ccd490020 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h @@ -196,6 +196,12 @@ class ShadowNodeFamily final { * Determines if the ShadowNodeFamily was ever mounted on the screen. */ mutable bool hasBeenMounted_{false}; + + /* + * Determines if Views that were never mounted on the screen should be deleted + * when the shadow node family is destroyed. + */ + const bool isDeletionOfUnmountedViewsEnabled_; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp index 9a59a6d16be30c..e841ab161e9a14 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp @@ -21,8 +21,10 @@ namespace facebook::react { class MockEventLogger : public EventLogger { - EventTag onEventStart(std::string_view /*name*/, SharedEventTarget /*target*/) - override { + EventTag onEventStart( + std::string_view /*name*/, + SharedEventTarget /*target*/, + DOMHighResTimeStamp /*eventStartTimeStamp*/) override { return EMPTY_EVENT_TAG; } void onEventProcessingStart(EventTag /*tag*/) override {} diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp index 82fc8321652a13..dd7d74a5245816 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StackingContextTest.cpp @@ -252,8 +252,8 @@ TEST_F(StackingContextTest, mostPropsDoNotForceViewsToMaterialize) { mutateViewShadowNodeProps_(nodeAA_, [](ViewProps& props) { auto& yogaStyle = props.yogaStyle; - yogaStyle.setPadding(yoga::Edge::All, yoga::value::points(42)); - yogaStyle.setMargin(yoga::Edge::All, yoga::value::points(42)); + yogaStyle.setPadding(yoga::Edge::All, yoga::StyleLength::points(42)); + yogaStyle.setMargin(yoga::Edge::All, yoga::StyleLength::points(42)); yogaStyle.setPositionType(yoga::PositionType::Absolute); props.shadowRadius = 42; props.shadowOffset = Size{42, 42}; @@ -264,7 +264,7 @@ TEST_F(StackingContextTest, mostPropsDoNotForceViewsToMaterialize) { auto& yogaStyle = props.yogaStyle; props.zIndex = 42; yogaStyle.setPositionType(yoga::PositionType::Static); - yogaStyle.setMargin(yoga::Edge::All, yoga::value::points(42)); + yogaStyle.setMargin(yoga::Edge::All, yoga::StyleLength::points(42)); props.shadowColor = clearColor(); props.shadowOpacity = 0.42; }); diff --git a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp index 48b2c8c00beb2a..7c0bf6e813c7d7 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp @@ -103,7 +103,8 @@ EventPerformanceLogger::EventPerformanceLogger( EventTag EventPerformanceLogger::onEventStart( std::string_view name, - SharedEventTarget target) { + SharedEventTarget target, + DOMHighResTimeStamp eventStartTimeStamp) { auto performanceEntryReporter = performanceEntryReporter_.lock(); if (performanceEntryReporter == nullptr) { return EMPTY_EVENT_TAG; @@ -119,7 +120,11 @@ EventTag EventPerformanceLogger::onEventStart( auto eventTag = createEventTag(); - auto timeStamp = performanceEntryReporter->getCurrentTimeStamp(); + // The event start timestamp may be provided by the caller in order to + // specify the platform specific event start time. + auto timeStamp = eventStartTimeStamp == DOM_HIGH_RES_TIME_STAMP_UNSET + ? performanceEntryReporter->getCurrentTimeStamp() + : eventStartTimeStamp; { std::lock_guard lock(eventsInFlightMutex_); eventsInFlight_.emplace( diff --git a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h index a1121f2ee52c7b..b878459f270a50 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h @@ -27,8 +27,11 @@ class EventPerformanceLogger : public EventLogger, #pragma mark - EventLogger - EventTag onEventStart(std::string_view name, SharedEventTarget target) - override; + EventTag onEventStart( + std::string_view name, + SharedEventTarget target, + DOMHighResTimeStamp eventStartTimeStamp = + DOM_HIGH_RES_TIME_STAMP_UNSET) override; void onEventProcessingStart(EventTag tag) override; void onEventProcessingEnd(EventTag tag) override; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/tests/PointerEventsProcessorTest.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/tests/PointerEventsProcessorTest.cpp index 889507450da684..d51547f310f073 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/tests/PointerEventsProcessorTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/tests/PointerEventsProcessorTest.cpp @@ -87,8 +87,8 @@ class PointerEventsProcessorTest : public ::testing::Test { listenToAllPointerEvents(props); props.layoutConstraints = LayoutConstraints{{0,0}, {500, 500}}; auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(400)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(400)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(400)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(400)); yogaStyle.setDisplay(yoga::Display::Flex); yogaStyle.setFlexDirection(yoga::FlexDirection::Row); yogaStyle.setAlignItems(yoga::Align::Center); @@ -109,8 +109,8 @@ class PointerEventsProcessorTest : public ::testing::Test { yogaStyle.setFlexDirection(yoga::FlexDirection::Column); yogaStyle.setAlignItems(yoga::Align::FlexEnd); yogaStyle.setJustifyContent(yoga::Justify::Center); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(150)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(300)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(150)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(300)); return sharedProps; }) .children({ @@ -123,8 +123,8 @@ class PointerEventsProcessorTest : public ::testing::Test { auto &props = *sharedProps; listenToAllPointerEvents(props); auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(100)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(100)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) }), @@ -141,8 +141,8 @@ class PointerEventsProcessorTest : public ::testing::Test { yogaStyle.setFlexDirection(yoga::FlexDirection::Column); yogaStyle.setAlignItems(yoga::Align::FlexStart); yogaStyle.setJustifyContent(yoga::Justify::Center); - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(150)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(300)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(150)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(300)); return sharedProps; }) .children({ @@ -155,8 +155,8 @@ class PointerEventsProcessorTest : public ::testing::Test { auto &props = *sharedProps; listenToAllPointerEvents(props); auto &yogaStyle = props.yogaStyle; - yogaStyle.setDimension(yoga::Dimension::Width, yoga::value::points(100)); - yogaStyle.setDimension(yoga::Dimension::Height, yoga::value::points(200)); + yogaStyle.setDimension(yoga::Dimension::Width, yoga::StyleLength::points(100)); + yogaStyle.setDimension(yoga::Dimension::Height, yoga::StyleLength::points(200)); return sharedProps; }) }) diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 78eccf35f9073f..97c420ccf5b060 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -84,7 +84,7 @@ ReactInstance::ReactInstance( } } } catch (jsi::JSError& originalError) { - jsErrorHandler->handleFatalError(jsiRuntime, originalError); + jsErrorHandler->handleError(jsiRuntime, originalError, true); } }); } @@ -129,7 +129,7 @@ ReactInstance::ReactInstance( RuntimeSchedulerClock::now, [jsErrorHandler = jsErrorHandler_]( jsi::Runtime& runtime, jsi::JSError& error) { - jsErrorHandler->handleFatalError(runtime, error); + jsErrorHandler->handleError(runtime, error, true); }); runtimeScheduler_->setPerformanceEntryReporter( // FIXME: Move creation of PerformanceEntryReporter to here and guarantee @@ -365,6 +365,17 @@ bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { return Boolean.call(runtime, value).getBool(); } +jsi::Value wrapInErrorIfNecessary( + jsi::Runtime& runtime, + const jsi::Value& value) { + auto Error = runtime.global().getPropertyAsFunction(runtime, "Error"); + auto isError = + value.isObject() && value.asObject(runtime).instanceOf(runtime, Error); + auto error = isError ? value.getObject(runtime) + : Error.callAsConstructor(runtime, value); + return jsi::Value(runtime, error); +} + } // namespace void ReactInstance::initializeRuntime( @@ -411,14 +422,52 @@ void ReactInstance::initializeRuntime( return jsi::Value(false); } - if (isFatal) { - auto jsError = - jsi::JSError(runtime, jsi::Value(runtime, args[0])); - jsErrorHandler->handleFatalError(runtime, jsError); - return jsi::Value(true); + auto jsError = jsi::JSError( + runtime, wrapInErrorIfNecessary(runtime, args[0])); + jsErrorHandler->handleError(runtime, jsError, isFatal); + + return jsi::Value(true); + })); + + defineReadOnlyGlobal( + runtime, + "RN$registerExceptionListener", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "registerExceptionListener"), + 1, + [errorListeners = std::vector>(), + jsErrorHandler = jsErrorHandler_]( + jsi::Runtime& runtime, + const jsi::Value& /*unused*/, + const jsi::Value* args, + size_t count) mutable { + if (count < 1) { + throw jsi::JSError( + runtime, + "registerExceptionListener: requires 1 argument: fn"); + } + + if (!args[0].isObject() || + !args[0].getObject(runtime).isFunction(runtime)) { + throw jsi::JSError( + runtime, + "registerExceptionListener: The first argument must be a function"); } - return jsi::Value(false); + auto errorListener = std::make_shared( + args[0].getObject(runtime).getFunction(runtime)); + errorListeners.emplace_back(errorListener); + + jsErrorHandler->registerErrorListener( + [weakErrorListener = std::weak_ptr( + errorListener)](jsi::Runtime& runtime, jsi::Value data) { + if (auto strongErrorListener = weakErrorListener.lock()) { + strongErrorListener->call(runtime, data); + } + }); + + return jsi::Value::undefined(); })); defineReadOnlyGlobal( diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index 5c76e1956c0b05..0380e3185d698c 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm index cadf6d61cdca8e..2b007f02fdd807 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTHostTests.mm @@ -135,11 +135,29 @@ - (void)testDidReceiveErrorStack stackFrame0[@"file"] = @"file2.js"; [stack addObject:stackFrame1]; - [instanceDelegate instance:[OCMArg any] didReceiveJSErrorStack:stack message:@"message" exceptionId:5 isFatal:YES]; + id extraData = [NSDictionary dictionary]; + + [instanceDelegate instance:[OCMArg any] + didReceiveJSErrorStack:stack + message:@"message" + originalMessage:nil + name:nil + componentStack:nil + exceptionId:5 + isFatal:YES + extraData:extraData]; OCMVerify( OCMTimes(1), - [_mockHostDelegate host:_subject didReceiveJSErrorStack:stack message:@"message" exceptionId:5 isFatal:YES]); + [_mockHostDelegate host:_subject + didReceiveJSErrorStack:stack + message:@"message" + originalMessage:nil + name:nil + componentStack:nil + exceptionId:5 + isFatal:YES + extraData:extraData]); } - (void)testDidInitializeRuntime diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index fa16f997266346..25e213a11c27b5 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -30,8 +30,12 @@ typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void); - (void)host:(RCTHost *)host didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message + originalMessage:(NSString *_Nullable)originalMessage + name:(NSString *_Nullable)name + componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId - isFatal:(BOOL)isFatal; + isFatal:(BOOL)isFatal + extraData:(NSDictionary *)extraData; - (void)hostDidStart:(RCTHost *)host; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 5fb599a8595ca0..75fe99c2353cbd 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -318,10 +318,22 @@ - (void)dealloc - (void)instance:(RCTInstance *)instance didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message + originalMessage:(NSString *_Nullable)originalMessage + name:(NSString *_Nullable)name + componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId isFatal:(BOOL)isFatal + extraData:(NSDictionary *)extraData { - [_hostDelegate host:self didReceiveJSErrorStack:stack message:message exceptionId:exceptionId isFatal:isFatal]; + [_hostDelegate host:self + didReceiveJSErrorStack:stack + message:message + originalMessage:originalMessage + name:name + componentStack:componentStack + exceptionId:exceptionId + isFatal:isFatal + extraData:extraData]; } - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index 8d26c642de3e57..b77c8994e35a2f 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -40,8 +40,12 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); - (void)instance:(RCTInstance *)instance didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message + originalMessage:(NSString *_Nullable)originalMessage + name:(NSString *_Nullable)name + componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId - isFatal:(BOOL)isFatal; + isFatal:(BOOL)isFatal + extraData:(NSDictionary *)extraData; - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 89aeb2e19ca764..09ee426ea0af80 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -31,6 +31,7 @@ #import #import #import +#import #import #import #import @@ -220,7 +221,9 @@ - (void)_start objCTimerRegistryRawPtr->setTimerManager(timerManager); __weak __typeof(self) weakSelf = self; - auto onJsError = [=](const JsErrorHandler::ParsedError &error) { [weakSelf _handleJSError:error]; }; + auto onJsError = [=](jsi::Runtime &runtime, const JsErrorHandler::ParsedError &error) { + [weakSelf _handleJSError:error withRuntime:runtime]; + }; // Create the React Instance _reactInstance = std::make_unique( @@ -462,23 +465,34 @@ - (void)_loadScriptFromSource:(RCTSource *)source [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil]; } -- (void)_handleJSError:(const JsErrorHandler::ParsedError &)error +- (void)_handleJSError:(const JsErrorHandler::ParsedError &)error withRuntime:(jsi::Runtime &)runtime { - NSString *message = [NSString stringWithCString:error.message.c_str() encoding:[NSString defaultCStringEncoding]]; + NSString *message = @(error.message.c_str()); NSMutableArray *> *stack = [NSMutableArray new]; - for (const JsErrorHandler::ParsedError::StackFrame &frame : error.frames) { + for (const JsErrorHandler::ParsedError::StackFrame &frame : error.stack) { [stack addObject:@{ - @"file" : [NSString stringWithCString:frame.fileName.c_str() encoding:[NSString defaultCStringEncoding]], - @"methodName" : [NSString stringWithCString:frame.methodName.c_str() encoding:[NSString defaultCStringEncoding]], - @"lineNumber" : [NSNumber numberWithInt:frame.lineNumber], - @"column" : [NSNumber numberWithInt:frame.columnNumber], + @"file" : frame.file ? @((*frame.file).c_str()) : [NSNull null], + @"methodName" : @(frame.methodName.c_str()), + @"lineNumber" : frame.lineNumber ? @(*frame.lineNumber) : [NSNull null], + @"column" : frame.column ? @(*frame.column) : [NSNull null], }]; } + + NSString *originalMessage = error.originalMessage ? @(error.originalMessage->c_str()) : nil; + NSString *name = error.name ? @(error.name->c_str()) : nil; + NSString *componentStack = error.componentStack ? @(error.componentStack->c_str()) : nil; + id extraData = + TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsi::Value(runtime, error.extraData), nullptr); + [_delegate instance:self didReceiveJSErrorStack:stack message:message - exceptionId:error.exceptionId - isFatal:error.isFatal]; + originalMessage:originalMessage + name:name + componentStack:componentStack + exceptionId:error.id + isFatal:error.isFatal + extraData:extraData]; } @end diff --git a/packages/react-native/ReactCommon/react/runtime/tests/cxx/ReactInstanceTest.cpp b/packages/react-native/ReactCommon/react/runtime/tests/cxx/ReactInstanceTest.cpp index cea0100cb2a606..cfee71e3c3eeb2 100644 --- a/packages/react-native/ReactCommon/react/runtime/tests/cxx/ReactInstanceTest.cpp +++ b/packages/react-native/ReactCommon/react/runtime/tests/cxx/ReactInstanceTest.cpp @@ -124,7 +124,8 @@ class ReactInstanceTest : public ::testing::Test { auto mockRegistry = std::make_unique(); mockRegistry_ = mockRegistry.get(); timerManager_ = std::make_shared(std::move(mockRegistry)); - auto onJsError = [](const JsErrorHandler::ParsedError& errorMap) noexcept { + auto onJsError = [](jsi::Runtime& /*runtime*/, + const JsErrorHandler::ParsedError& /*error*/) noexcept { // Do nothing }; diff --git a/packages/react-native/ReactCommon/react/timing/primitives.h b/packages/react-native/ReactCommon/react/timing/primitives.h index 135be263c9b05e..c6b3c304635cdc 100644 --- a/packages/react-native/ReactCommon/react/timing/primitives.h +++ b/packages/react-native/ReactCommon/react/timing/primitives.h @@ -17,6 +17,8 @@ namespace facebook::react { // not necessary in React Native. using DOMHighResTimeStamp = double; +constexpr DOMHighResTimeStamp DOM_HIGH_RES_TIME_STAMP_UNSET = -1.0; + inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp( std::chrono::steady_clock::duration duration) { return static_cast>(duration) diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGNodeStyle.cpp b/packages/react-native/ReactCommon/yoga/yoga/YGNodeStyle.cpp index d2a10b0591a1d1..8664b53ec81518 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGNodeStyle.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/YGNodeStyle.cpp @@ -177,18 +177,19 @@ float YGNodeStyleGetFlexShrink(const YGNodeConstRef nodeRef) { void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) { updateStyle<&Style::flexBasis, &Style::setFlexBasis>( - node, value::points(flexBasis)); + node, StyleLength::points(flexBasis)); } void YGNodeStyleSetFlexBasisPercent( const YGNodeRef node, const float flexBasisPercent) { updateStyle<&Style::flexBasis, &Style::setFlexBasis>( - node, value::percent(flexBasisPercent)); + node, StyleLength::percent(flexBasisPercent)); } void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) { - updateStyle<&Style::flexBasis, &Style::setFlexBasis>(node, value::ofAuto()); + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleLength::ofAuto()); } YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { @@ -197,17 +198,17 @@ YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { void YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float points) { updateStyle<&Style::position, &Style::setPosition>( - node, scopedEnum(edge), value::points(points)); + node, scopedEnum(edge), StyleLength::points(points)); } void YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float percent) { updateStyle<&Style::position, &Style::setPosition>( - node, scopedEnum(edge), value::percent(percent)); + node, scopedEnum(edge), StyleLength::percent(percent)); } void YGNodeStyleSetPositionAuto(YGNodeRef node, YGEdge edge) { updateStyle<&Style::position, &Style::setPosition>( - node, scopedEnum(edge), value::ofAuto()); + node, scopedEnum(edge), StyleLength::ofAuto()); } YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) { @@ -216,17 +217,17 @@ YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) { void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float points) { updateStyle<&Style::margin, &Style::setMargin>( - node, scopedEnum(edge), value::points(points)); + node, scopedEnum(edge), StyleLength::points(points)); } void YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float percent) { updateStyle<&Style::margin, &Style::setMargin>( - node, scopedEnum(edge), value::percent(percent)); + node, scopedEnum(edge), StyleLength::percent(percent)); } void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge) { updateStyle<&Style::margin, &Style::setMargin>( - node, scopedEnum(edge), value::ofAuto()); + node, scopedEnum(edge), StyleLength::ofAuto()); } YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) { @@ -235,12 +236,12 @@ YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) { void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float points) { updateStyle<&Style::padding, &Style::setPadding>( - node, scopedEnum(edge), value::points(points)); + node, scopedEnum(edge), StyleLength::points(points)); } void YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float percent) { updateStyle<&Style::padding, &Style::setPadding>( - node, scopedEnum(edge), value::percent(percent)); + node, scopedEnum(edge), StyleLength::percent(percent)); } YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) { @@ -252,7 +253,7 @@ void YGNodeStyleSetBorder( const YGEdge edge, const float border) { updateStyle<&Style::border, &Style::setBorder>( - node, scopedEnum(edge), value::points(border)); + node, scopedEnum(edge), StyleLength::points(border)); } float YGNodeStyleGetBorder(const YGNodeConstRef node, const YGEdge edge) { @@ -269,12 +270,12 @@ void YGNodeStyleSetGap( const YGGutter gutter, const float gapLength) { updateStyle<&Style::gap, &Style::setGap>( - node, scopedEnum(gutter), value::points(gapLength)); + node, scopedEnum(gutter), StyleLength::points(gapLength)); } void YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float percent) { updateStyle<&Style::gap, &Style::setGap>( - node, scopedEnum(gutter), value::percent(percent)); + node, scopedEnum(gutter), StyleLength::percent(percent)); } float YGNodeStyleGetGap(const YGNodeConstRef node, const YGGutter gutter) { @@ -307,17 +308,17 @@ YGBoxSizing YGNodeStyleGetBoxSizing(const YGNodeConstRef node) { void YGNodeStyleSetWidth(YGNodeRef node, float points) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Width, value::points(points)); + node, Dimension::Width, StyleLength::points(points)); } void YGNodeStyleSetWidthPercent(YGNodeRef node, float percent) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Width, value::percent(percent)); + node, Dimension::Width, StyleLength::percent(percent)); } void YGNodeStyleSetWidthAuto(YGNodeRef node) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Width, value::ofAuto()); + node, Dimension::Width, StyleLength::ofAuto()); } YGValue YGNodeStyleGetWidth(YGNodeConstRef node) { @@ -326,17 +327,17 @@ YGValue YGNodeStyleGetWidth(YGNodeConstRef node) { void YGNodeStyleSetHeight(YGNodeRef node, float points) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Height, value::points(points)); + node, Dimension::Height, StyleLength::points(points)); } void YGNodeStyleSetHeightPercent(YGNodeRef node, float percent) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Height, value::percent(percent)); + node, Dimension::Height, StyleLength::percent(percent)); } void YGNodeStyleSetHeightAuto(YGNodeRef node) { updateStyle<&Style::dimension, &Style::setDimension>( - node, Dimension::Height, value::ofAuto()); + node, Dimension::Height, StyleLength::ofAuto()); } YGValue YGNodeStyleGetHeight(YGNodeConstRef node) { @@ -345,12 +346,12 @@ YGValue YGNodeStyleGetHeight(YGNodeConstRef node) { void YGNodeStyleSetMinWidth(const YGNodeRef node, const float minWidth) { updateStyle<&Style::minDimension, &Style::setMinDimension>( - node, Dimension::Width, value::points(minWidth)); + node, Dimension::Width, StyleLength::points(minWidth)); } void YGNodeStyleSetMinWidthPercent(const YGNodeRef node, const float minWidth) { updateStyle<&Style::minDimension, &Style::setMinDimension>( - node, Dimension::Width, value::percent(minWidth)); + node, Dimension::Width, StyleLength::percent(minWidth)); } YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) { @@ -359,14 +360,14 @@ YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) { void YGNodeStyleSetMinHeight(const YGNodeRef node, const float minHeight) { updateStyle<&Style::minDimension, &Style::setMinDimension>( - node, Dimension::Height, value::points(minHeight)); + node, Dimension::Height, StyleLength::points(minHeight)); } void YGNodeStyleSetMinHeightPercent( const YGNodeRef node, const float minHeight) { updateStyle<&Style::minDimension, &Style::setMinDimension>( - node, Dimension::Height, value::percent(minHeight)); + node, Dimension::Height, StyleLength::percent(minHeight)); } YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) { @@ -375,12 +376,12 @@ YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) { void YGNodeStyleSetMaxWidth(const YGNodeRef node, const float maxWidth) { updateStyle<&Style::maxDimension, &Style::setMaxDimension>( - node, Dimension::Width, value::points(maxWidth)); + node, Dimension::Width, StyleLength::points(maxWidth)); } void YGNodeStyleSetMaxWidthPercent(const YGNodeRef node, const float maxWidth) { updateStyle<&Style::maxDimension, &Style::setMaxDimension>( - node, Dimension::Width, value::percent(maxWidth)); + node, Dimension::Width, StyleLength::percent(maxWidth)); } YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) { @@ -389,14 +390,14 @@ YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) { void YGNodeStyleSetMaxHeight(const YGNodeRef node, const float maxHeight) { updateStyle<&Style::maxDimension, &Style::setMaxDimension>( - node, Dimension::Height, value::points(maxHeight)); + node, Dimension::Height, StyleLength::points(maxHeight)); } void YGNodeStyleSetMaxHeightPercent( const YGNodeRef node, const float maxHeight) { updateStyle<&Style::maxDimension, &Style::setMaxDimension>( - node, Dimension::Height, value::percent(maxHeight)); + node, Dimension::Height, StyleLength::percent(maxHeight)); } YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) { diff --git a/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp b/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp index 3189f3bbeff598..7c6bea55e3cfe7 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/node/Node.cpp @@ -288,9 +288,10 @@ Style::Length Node::processFlexBasis() const { return flexBasis; } if (style_.flex().isDefined() && style_.flex().unwrap() > 0.0f) { - return config_->useWebDefaults() ? value::ofAuto() : value::points(0); + return config_->useWebDefaults() ? StyleLength::ofAuto() + : StyleLength::points(0); } - return value::ofAuto(); + return StyleLength::ofAuto(); } FloatOptional Node::resolveFlexBasis( diff --git a/packages/react-native/ReactCommon/yoga/yoga/node/Node.h b/packages/react-native/ReactCommon/yoga/yoga/node/Node.h index 5f38ac1350f6e1..34ef6d955ed8da 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/node/Node.h +++ b/packages/react-native/ReactCommon/yoga/yoga/node/Node.h @@ -302,7 +302,7 @@ class YG_EXPORT Node : public ::YGNode { std::vector children_; const Config* config_; std::array processedDimensions_{ - {value::undefined(), value::undefined()}}; + {StyleLength::undefined(), StyleLength::undefined()}}; }; inline Node* resolveRef(const YGNodeRef ref) { diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/StyleLength.h b/packages/react-native/ReactCommon/yoga/yoga/style/StyleLength.h index 242698abf4e49b..7cc42d9eb5c8d0 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/StyleLength.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/StyleLength.h @@ -104,36 +104,4 @@ inline bool inexactEquals(const StyleLength& a, const StyleLength& b) { return a.unit() == b.unit() && inexactEquals(a.value(), b.value()); } -namespace value { - -/** - * Canonical unit (one YGUnitPoint) - */ -constexpr StyleLength points(float value) { - return StyleLength::points(value); -} - -/** - * Percent of reference - */ -constexpr StyleLength percent(float value) { - return StyleLength::percent(value); -} - -/** - * "auto" keyword - */ -constexpr StyleLength ofAuto() { - return StyleLength::ofAuto(); -} - -/** - * Undefined - */ -constexpr StyleLength undefined() { - return StyleLength::undefined(); -} - -} // namespace value - } // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h index 3b177254447da4..597eae4c4374a9 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/StyleValuePool.h @@ -49,9 +49,9 @@ class StyleValuePool { StyleLength getLength(StyleValueHandle handle) const { if (handle.isUndefined()) { - return value::undefined(); + return StyleLength::undefined(); } else if (handle.isAuto()) { - return value::ofAuto(); + return StyleLength::ofAuto(); } else { assert( handle.type() == StyleValueHandle::Type::Point || @@ -61,8 +61,8 @@ class StyleValuePool { : unpackInlineInteger(handle.value()); return handle.type() == StyleValueHandle::Type::Point - ? value::points(value) - : value::percent(value); + ? StyleLength::points(value) + : StyleLength::percent(value); } } diff --git a/packages/react-native/gradle/libs.versions.toml b/packages/react-native/gradle/libs.versions.toml index c43ee422c33357..20745f01185210 100644 --- a/packages/react-native/gradle/libs.versions.toml +++ b/packages/react-native/gradle/libs.versions.toml @@ -23,7 +23,7 @@ javax-annotation-api = "1.3.2" javax-inject = "1" jsr305 = "3.0.2" junit = "4.13.2" -kotlin = "1.9.24" +kotlin = "2.0.21" mockito = "3.12.4" nexus-publish = "1.3.0" okhttp = "4.9.2" diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index fa175cac13144a..510e084f600ec6 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -3,6 +3,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +require 'shellwords' + require_relative "./helpers.rb" # Utilities class for React Native Cocoapods @@ -237,8 +239,8 @@ def self.create_xcode_env_if_missing(file_manager: File) # When installing pods with a yarn alias, yarn creates a fake yarn and node executables # in a temporary folder. # Using `node --print "process.argv[0]";` we are able to retrieve the actual path from which node is running. - # see https://github.com/facebook/react-native/issues/43285 for more info - node_binary = `node --print "process.argv[0]";` + # see https://github.com/facebook/react-native/issues/43285 for more info. We've tweaked this slightly. + node_binary = Shellwords.escape(`node --print "process.argv[0]"`.strip) system("echo 'export NODE_BINARY=#{node_binary}' > #{file_path}.local") end end diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index bdd7c24574ef90..08a474add644b1 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -440,7 +440,7 @@ const definitions: FeatureFlagDefinitions = { }, }, useRuntimeShadowNodeReferenceUpdate: { - defaultValue: true, + defaultValue: false, metadata: { dateAdded: '2024-06-03', description: @@ -614,4 +614,5 @@ const definitions: FeatureFlagDefinitions = { }, }; -export default definitions; +// Keep it as a CommonJS module so we can easily import it from Node.js +module.exports = definitions; diff --git a/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.cpp-template.js b/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.cpp-template.js index 223a0e272153e5..8256b7c35b4eca 100644 --- a/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.cpp-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.cpp-template.js @@ -88,11 +88,25 @@ void JReactNativeFeatureFlagsCxxInterop::dangerouslyReset( ReactNativeFeatureFlags::dangerouslyReset(); } +jni::local_ref JReactNativeFeatureFlagsCxxInterop::dangerouslyForceOverride( + facebook::jni::alias_ref /*unused*/, + jni::alias_ref provider) { + auto accessedFlags = ReactNativeFeatureFlags::dangerouslyForceOverride( + std::make_unique(provider)); + if (accessedFlags.has_value()) { + return jni::make_jstring(accessedFlags.value()); + } + return nullptr; +} + void JReactNativeFeatureFlagsCxxInterop::registerNatives() { javaClassLocal()->registerNatives({ makeNativeMethod( "override", JReactNativeFeatureFlagsCxxInterop::override), makeNativeMethod("dangerouslyReset", JReactNativeFeatureFlagsCxxInterop::dangerouslyReset), + makeNativeMethod( + "dangerouslyForceOverride", + JReactNativeFeatureFlagsCxxInterop::dangerouslyForceOverride), ${Object.entries(definitions.common) .map( ([flagName, flagConfig]) => diff --git a/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.h-template.js b/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.h-template.js index 72995b1f714ab1..763d1a805cf76a 100644 --- a/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.h-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/JReactNativeFeatureFlagsCxxInterop.h-template.js @@ -55,6 +55,10 @@ ${Object.entries(definitions.common) static void dangerouslyReset( facebook::jni::alias_ref); + static jni::local_ref dangerouslyForceOverride( + facebook::jni::alias_ref, + jni::alias_ref provider); + static void registerNatives(); }; diff --git a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlags.kt-template.js b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlags.kt-template.js index 9d7ba68a0e196a..68b21fa53b0cea 100644 --- a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlags.kt-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlags.kt-template.js @@ -93,6 +93,32 @@ ${Object.entries(definitions.common) accessor = accessorProvider() } + /** + * This is a combination of \`dangerouslyReset\` and \`override\` that reduces + * the likeliness of a race condition between the two calls. + * + * This is **dangerous** because it can introduce consistency issues that will + * be much harder to debug. For example, it could hide the fact that feature + * flags are read before you set the values you want to use everywhere. It + * could also cause a workflow to suddently have different feature flags for + * behaviors that were configured with different values before. + * + * It returns a string that contains the feature flags that were accessed + * before this call (or between the last call to \`dangerouslyReset\` and this + * call). If you are using this method, you do not want the hard crash that + * you would get from using \`dangerouslyReset\` and \`override\` separately, + * but you should still log this somehow. + * + * Please see the documentation of \`dangerouslyReset\` for additional details. + */ + @JvmStatic + public fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? { + val newAccessor = accessorProvider() + val previouslyAccessedFlags = newAccessor.dangerouslyForceOverride(provider) + accessor = newAccessor + return previouslyAccessedFlags + } + /** * This is just used to replace the default ReactNativeFeatureFlagsCxxAccessor * that uses JNI with a version that doesn't, to simplify testing. diff --git a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxAccessor.kt-template.js b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxAccessor.kt-template.js index 9b2b6894e759ab..71b7ce3ce251a9 100644 --- a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxAccessor.kt-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxAccessor.kt-template.js @@ -57,6 +57,9 @@ ${Object.entries(definitions.common) ReactNativeFeatureFlagsCxxInterop.override(provider as Any) override fun dangerouslyReset(): Unit = ReactNativeFeatureFlagsCxxInterop.dangerouslyReset() + + override fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? = + ReactNativeFeatureFlagsCxxInterop.dangerouslyForceOverride(provider as Any) } `); } diff --git a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxInterop.kt-template.js b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxInterop.kt-template.js index d2e43130ee0756..fa0fc27fdddb50 100644 --- a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxInterop.kt-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsCxxInterop.kt-template.js @@ -51,6 +51,8 @@ ${Object.entries(definitions.common) @DoNotStrip @JvmStatic public external fun override(provider: Any) @DoNotStrip @JvmStatic public external fun dangerouslyReset() + + @DoNotStrip @JvmStatic public external fun dangerouslyForceOverride(provider: Any): String? } `); } diff --git a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsLocalAccessor.kt-template.js b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsLocalAccessor.kt-template.js index c1606e41e0b856..b731aee4e84fcf 100644 --- a/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsLocalAccessor.kt-template.js +++ b/packages/react-native/scripts/featureflags/templates/android/ReactNativeFeatureFlagsLocalAccessor.kt-template.js @@ -68,8 +68,22 @@ ${Object.entries(definitions.common) } override fun dangerouslyReset() { - // We don't need to do anything here because \`ReactNativeFeatureFlags\` will - // just create a new instance of this class. + // We don't need to do anything else here because \`ReactNativeFeatureFlags\` will just create a + // new instance of this class. + } + + override fun dangerouslyForceOverride(provider: ReactNativeFeatureFlagsProvider): String? { + val accessedFeatureFlags = getAccessedFeatureFlags() + currentProvider = provider + return accessedFeatureFlags + } + + internal fun getAccessedFeatureFlags(): String? { + if (accessedFeatureFlags.isEmpty()) { + return null + } + + return accessedFeatureFlags.joinToString(separator = ", ") { it } } } `); diff --git a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.cpp-template.js b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.cpp-template.js index bef6fbb8c20e83..32d4e178858241 100644 --- a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.cpp-template.js +++ b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.cpp-template.js @@ -29,6 +29,11 @@ ${DO_NOT_MODIFY_COMMENT} namespace facebook::react { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wglobal-constructors" +std::unique_ptr accessor_; +#pragma GCC diagnostic pop + ${Object.entries(definitions.common) .map( ([flagName, flagConfig]) => @@ -46,16 +51,26 @@ void ReactNativeFeatureFlags::override( } void ReactNativeFeatureFlags::dangerouslyReset() { - getAccessor(true); + accessor_ = std::make_unique(); +} + +std::optional ReactNativeFeatureFlags::dangerouslyForceOverride( + std::unique_ptr provider) { + auto accessor = std::make_unique(); + accessor->override(std::move(provider)); + + std::swap(accessor_, accessor); + + // Now accessor is the old accessor + return accessor == nullptr ? std::nullopt + : accessor->getAccessedFeatureFlagNames(); } -ReactNativeFeatureFlagsAccessor& ReactNativeFeatureFlags::getAccessor( - bool reset) { - static std::unique_ptr accessor; - if (accessor == nullptr || reset) { - accessor = std::make_unique(); +ReactNativeFeatureFlagsAccessor& ReactNativeFeatureFlags::getAccessor() { + if (accessor_ == nullptr) { + accessor_ = std::make_unique(); } - return *accessor; + return *accessor_; } } // namespace facebook::react diff --git a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.h-template.js b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.h-template.js index 3cfeb19e9b62bc..d6a59223341c17 100644 --- a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.h-template.js +++ b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlags.h-template.js @@ -30,6 +30,8 @@ ${DO_NOT_MODIFY_COMMENT} #include #include #include +#include +#include #ifndef RN_EXPORT #define RN_EXPORT __attribute__((visibility("default"))) @@ -90,9 +92,24 @@ ${Object.entries(definitions.common) */ RN_EXPORT static void dangerouslyReset(); + /** + * This is a combination of \`dangerouslyReset\` and \`override\` that reduces + * the likeliness of a race condition between the two calls. + * + * This is **dangerous** because it can introduce consistency issues that will + * be much harder to debug. For example, it could hide the fact that feature + * flags are read before you set the values you want to use everywhere. It + * could also cause a workflow to suddently have different feature flags for + * behaviors that were configured with different values before. + * + * Please see the documentation of \`dangerouslyReset\` for additional details. + */ + RN_EXPORT static std::optional dangerouslyForceOverride( + std::unique_ptr provider); + private: ReactNativeFeatureFlags() = delete; - static ReactNativeFeatureFlagsAccessor& getAccessor(bool reset = false); + static ReactNativeFeatureFlagsAccessor& getAccessor(); }; } // namespace facebook::react diff --git a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.cpp-template.js b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.cpp-template.js index 26b9df54910eeb..1d1dc5f249f3fe 100644 --- a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.cpp-template.js +++ b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.cpp-template.js @@ -74,13 +74,8 @@ void ReactNativeFeatureFlagsAccessor::override( currentProvider_ = std::move(provider); } -void ReactNativeFeatureFlagsAccessor::markFlagAsAccessed( - int position, - const char* flagName) { - accessedFeatureFlags_[position] = flagName; -} - -void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { +std::optional +ReactNativeFeatureFlagsAccessor::getAccessedFeatureFlagNames() const { std::ostringstream featureFlagListBuilder; for (const auto& featureFlagName : accessedFeatureFlags_) { if (featureFlagName != nullptr) { @@ -94,10 +89,24 @@ void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { accessedFeatureFlagNames.substr(0, accessedFeatureFlagNames.size() - 2); } - if (!accessedFeatureFlagNames.empty()) { + return accessedFeatureFlagNames.empty() + ? std::nullopt + : std::optional{accessedFeatureFlagNames}; +} + +void ReactNativeFeatureFlagsAccessor::markFlagAsAccessed( + int position, + const char* flagName) { + accessedFeatureFlags_[position] = flagName; +} + +void ReactNativeFeatureFlagsAccessor::ensureFlagsNotAccessed() { + auto accessedFeatureFlagNames = getAccessedFeatureFlagNames(); + + if (accessedFeatureFlagNames.has_value()) { throw std::runtime_error( "Feature flags were accessed before being overridden: " + - accessedFeatureFlagNames); + accessedFeatureFlagNames.value()); } } diff --git a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.h-template.js b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.h-template.js index 07ad24bc0fa92d..86c6cdd7caff45 100644 --- a/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.h-template.js +++ b/packages/react-native/scripts/featureflags/templates/common-cxx/ReactNativeFeatureFlagsAccessor.h-template.js @@ -32,6 +32,7 @@ ${DO_NOT_MODIFY_COMMENT} #include #include #include +#include namespace facebook::react { @@ -47,6 +48,7 @@ ${Object.entries(definitions.common) .join('\n')} void override(std::unique_ptr provider); + std::optional getAccessedFeatureFlagNames() const; private: void markFlagAsAccessed(int position, const char* flagName); diff --git a/packages/react-native/src/private/components/VScrollViewNativeComponents.js b/packages/react-native/src/private/components/VScrollViewNativeComponents.js index 89d17271f5ba06..41c691f23a47d0 100644 --- a/packages/react-native/src/private/components/VScrollViewNativeComponents.js +++ b/packages/react-native/src/private/components/VScrollViewNativeComponents.js @@ -23,11 +23,10 @@ import * as React from 'react'; import {forwardRef} from 'react'; // TODO: After upgrading to React 19, remove `forwardRef` from this component. -export const VScrollViewNativeComponent: React.AbstractComponent< - ScrollViewNativeProps, - TScrollViewNativeImperativeHandle, - // $FlowExpectedError[incompatible-type] - Flow cannot model imperative handles, yet. -> = forwardRef(function VScrollViewNativeComponent( +export const VScrollViewNativeComponent: component( + ref: React.RefSetter, + ...props: ScrollViewNativeProps +) = forwardRef(function VScrollViewNativeComponent( props: ScrollViewNativeProps, ref: ?React.RefSetter, ): React.Node { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index e1e4bcbe0749e4..1c754469b47232 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -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<> + * @generated SignedSource<<9907eb55fc7e2075ba946cdf2e3f6b4b>> * @flow strict */ @@ -378,7 +378,7 @@ export const useOptimizedEventBatchingOnAndroid: Getter = createNativeF /** * When enabled, cloning shadow nodes within react native will update the reference held by the current JS fiber tree. */ -export const useRuntimeShadowNodeReferenceUpdate: Getter = createNativeFlagGetter('useRuntimeShadowNodeReferenceUpdate', true); +export const useRuntimeShadowNodeReferenceUpdate: Getter = createNativeFlagGetter('useRuntimeShadowNodeReferenceUpdate', false); /** * In Bridgeless mode, should legacy NativeModules use the TurboModule system? */ diff --git a/packages/react-native/src/private/fusebox/specs/NativeReactDevToolsRuntimeSettingsModule.js b/packages/react-native/src/private/fusebox/specs/NativeReactDevToolsRuntimeSettingsModule.js new file mode 100644 index 00000000000000..7f6f48a5d26be1 --- /dev/null +++ b/packages/react-native/src/private/fusebox/specs/NativeReactDevToolsRuntimeSettingsModule.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; + +import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; + +export type ReloadAndProfileConfig = { + shouldReloadAndProfile: boolean, + recordChangeDescriptions: boolean, +}; + +// Linter doesn't speak Flow's `Partial` type +export type PartialReloadAndProfileConfig = { + shouldReloadAndProfile?: boolean, + recordChangeDescriptions?: boolean, +}; + +export interface Spec extends TurboModule { + +setReloadAndProfileConfig: (config: PartialReloadAndProfileConfig) => void; + +getReloadAndProfileConfig: () => ReloadAndProfileConfig; +} + +export default (TurboModuleRegistry.get( + 'ReactDevToolsRuntimeSettingsModule', +): ?Spec); diff --git a/packages/react-native/src/private/specs/modules/NativeAppearance.js b/packages/react-native/src/private/specs/modules/NativeAppearance.js index c12236e7a53c8e..d7bc36160eaf81 100644 --- a/packages/react-native/src/private/specs/modules/NativeAppearance.js +++ b/packages/react-native/src/private/specs/modules/NativeAppearance.js @@ -12,21 +12,15 @@ import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; -export type ColorSchemeName = 'light' | 'dark'; +export type ColorSchemeName = 'light' | 'dark' | 'unspecified'; export type AppearancePreferences = { - // TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union - // types. - /* 'light' | 'dark' */ - colorScheme?: ?string, + colorScheme?: ?ColorSchemeName, }; export interface Spec extends TurboModule { - // TODO: (hramos) T52919652 Use ?ColorSchemeName once codegen supports union - // types. - /* 'light' | 'dark' */ - +getColorScheme: () => ?string; - +setColorScheme: (colorScheme: string) => void; + +getColorScheme: () => ?ColorSchemeName; + +setColorScheme: (colorScheme: ColorSchemeName) => void; // RCTEventEmitter +addListener: (eventName: string) => void; diff --git a/packages/rn-tester/.maestro/image.yml b/packages/rn-tester/.maestro/image.yml new file mode 100644 index 00000000000000..f963352f4b5154 --- /dev/null +++ b/packages/rn-tester/.maestro/image.yml @@ -0,0 +1,12 @@ +appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp +--- +- launchApp +- assertVisible: "Components" +- scrollUntilVisible: + element: + id: "Image" + direction: DOWN + speed: 40 +- tapOn: + id: "Image" +- assertVisible: "Plain Network Image with `source` prop." diff --git a/packages/rn-tester/.maestro/pressable.yml b/packages/rn-tester/.maestro/pressable.yml index 4c5bcd48556a28..30942df17b80bc 100644 --- a/packages/rn-tester/.maestro/pressable.yml +++ b/packages/rn-tester/.maestro/pressable.yml @@ -21,3 +21,28 @@ appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebo id: "one_press_me_button" - assertVisible: text: "2x onPress" +- scrollUntilVisible: + element: + id: "pressable_feedback_events" + direction: DOWN + speed: 10 + visibilityPercentage: 100 +- tapOn: + id: "pressable_feedback_events_button" +- scrollUntilVisible: + element: + text: "pressIn" + direction: DOWN + speed: 10 +- assertVisible: + text: "press" +- assertVisible: + text: "pressOut" +- longPressOn: + id: "pressable_feedback_events_button" +- assertVisible: + text: "pressIn" +- assertVisible: + text: "longPress" +- assertVisible: + text: "pressOut" diff --git a/packages/rn-tester/NativeComponentExample/js/MyNativeView.js b/packages/rn-tester/NativeComponentExample/js/MyNativeView.js index f86134320f3fdb..0ab8d56c29850a 100644 --- a/packages/rn-tester/NativeComponentExample/js/MyNativeView.js +++ b/packages/rn-tester/NativeComponentExample/js/MyNativeView.js @@ -85,6 +85,14 @@ function getTextFor(measureStruct: MeasureStruct): string { measureStruct.height, )}`; } +const opacityDecrementCounter = 0.2; + +function computeNextOpacity(opacity: number): number { + if (parseFloat(opacity.toFixed(1)) > 0.0) { + return opacity - opacityDecrementCounter; + } + return 1.0; +} // This is an example component that migrates to use the new architecture. export default function MyNativeView(props: {}): React.Node { @@ -201,7 +209,7 @@ export default function MyNativeView(props: {}): React.Node {