From 7128482e49dc93c5db873bb8480a0aff86eb8149 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Tue, 28 Apr 2026 22:50:41 -0700 Subject: [PATCH 01/34] Bump Metro to ^0.84.3 (#56638) Summary: Bumps Metro dependency from `^0.84.2` to `^0.84.3` across all packages. Metro 0.84.3 adds `changeId` to HMR `update-done` messages, which is required by the `unstable_fastRefreshComplete` CDP event and the Fast Refresh performance marker to deduplicate events across multiple updates for the same file change. ## Changelog: [Internal] Pull Request resolved: https://github.com/facebook/react-native/pull/56638 Test Plan: - CI cc huntie Reviewed By: cortinico Differential Revision: D102810175 Pulled By: motiz88 fbshipit-source-id: b05bc522fd7815f2858ce645b03bdea86a8027ea --- package.json | 4 +- packages/community-cli-plugin/package.json | 8 +- packages/metro-config/package.json | 4 +- packages/react-native/package.json | 4 +- yarn.lock | 212 ++++++++++----------- 5 files changed, 107 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 2a843a43db89..d41ef125a0ec 100644 --- a/package.json +++ b/package.json @@ -100,8 +100,8 @@ "markdownlint-cli2": "^0.17.2", "markdownlint-rule-relative-links": "^3.0.0", "memfs": "^4.38.2", - "metro-babel-register": "^0.84.2", - "metro-transform-plugins": "^0.84.2", + "metro-babel-register": "^0.84.3", + "metro-transform-plugins": "^0.84.3", "micromatch": "^4.0.4", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", diff --git a/packages/community-cli-plugin/package.json b/packages/community-cli-plugin/package.json index 882dab653560..2bdc2dad316c 100644 --- a/packages/community-cli-plugin/package.json +++ b/packages/community-cli-plugin/package.json @@ -34,13 +34,13 @@ "@react-native/dev-middleware": "0.86.0-main", "debug": "^4.4.0", "invariant": "^2.2.4", - "metro": "^0.84.2", - "metro-config": "^0.84.2", - "metro-core": "^0.84.2", + "metro": "^0.84.3", + "metro-config": "^0.84.3", + "metro-core": "^0.84.3", "semver": "^7.1.3" }, "devDependencies": { - "metro-resolver": "^0.84.2" + "metro-resolver": "^0.84.3" }, "peerDependencies": { "@react-native-community/cli": "*", diff --git a/packages/metro-config/package.json b/packages/metro-config/package.json index 3812023efc37..9a29952fb263 100644 --- a/packages/metro-config/package.json +++ b/packages/metro-config/package.json @@ -37,7 +37,7 @@ "dependencies": { "@react-native/js-polyfills": "0.86.0-main", "@react-native/metro-babel-transformer": "0.86.0-main", - "metro-config": "^0.84.2", - "metro-runtime": "^0.84.2" + "metro-config": "^0.84.3", + "metro-runtime": "^0.84.3" } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index b2a69e7cce8a..79ab61fba700 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -179,8 +179,8 @@ "hermes-compiler": "0.0.0", "invariant": "^2.2.4", "memoize-one": "^5.0.0", - "metro-runtime": "^0.84.2", - "metro-source-map": "^0.84.2", + "metro-runtime": "^0.84.3", + "metro-source-map": "^0.84.3", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", diff --git a/yarn.lock b/yarn.lock index 876aaa1bc739..f850d2b1aca9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3024,13 +3024,6 @@ babel-plugin-replace-ts-export-assignment@^0.0.2: resolved "https://registry.yarnpkg.com/babel-plugin-replace-ts-export-assignment/-/babel-plugin-replace-ts-export-assignment-0.0.2.tgz#927a30ba303fcf271108980a8d4f80a693e1d53f" integrity sha512-BiTEG2Ro+O1spuheL5nB289y37FFmz0ISE6GjpNCG2JuA/WNcuEHSYw01+vN8quGf208sID3FnZFDwVyqX18YQ== -babel-plugin-syntax-hermes-parser@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz#07602f8163cc7e63c4ec17d47467f2a3c5db70e9" - integrity sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA== - dependencies: - hermes-parser "0.33.3" - babel-plugin-syntax-hermes-parser@0.35.0: version "0.35.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.35.0.tgz#9daa5340b6a5b329985f4c5cda2c6b1a355daed5" @@ -5133,7 +5126,7 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: hermes-compiler@0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/hermes-compiler/-/hermes-compiler-0.0.0.tgz#8d9f6a0b2740ce34d71258fec684e7b6bfd97efa" + resolved "https://registry.facebook.net/hermes-compiler/-/hermes-compiler-0.0.0.tgz#8d9f6a0b2740ce34d71258fec684e7b6bfd97efa" integrity sha512-boVFutx6ME/Km2mB6vvsQcdnazEYYI/jV1pomx1wcFUG/EVqTkr5CU0CW9bKipOA/8Hyu3NYwW3THg2Q1kNCfA== hermes-eslint@0.35.0: @@ -5150,11 +5143,6 @@ hermes-estree@0.25.1: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== -hermes-estree@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.33.3.tgz#6d6b593d4b471119772c82bdb0212dfadabb6f17" - integrity sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg== - hermes-estree@0.35.0: version "0.35.0" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.35.0.tgz#767cce0b14a68b4bc06cd5db7efe889f6188c565" @@ -5167,13 +5155,6 @@ hermes-parser@0.25.1, hermes-parser@^0.25.1: dependencies: hermes-estree "0.25.1" -hermes-parser@0.33.3: - version "0.33.3" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.33.3.tgz#da50ababb7a5ab636d339e7b2f6e3848e217e09d" - integrity sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA== - dependencies: - hermes-estree "0.33.3" - hermes-parser@0.35.0: version "0.35.0" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.35.0.tgz#7625ec2f34ab897c2a17a7bea9788d136d5fd8c9" @@ -6703,10 +6684,10 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -metro-babel-register@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.84.2.tgz#180fb50e158040b49303ee4d1bf40e46293bcfbc" - integrity sha512-GDToA2s3egLO8rZv9Lj11lQBimkUT0rgl20d8qd8Z98khaRUGVtNjFVs88JQZzHjU8xpudFGbXuHiyztEMS9eg== +metro-babel-register@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.84.3.tgz#219f8b44f6bee97e4b3c87b266fe50b24bf6c545" + integrity sha512-YxAZL2LgwD98wxkaaU4LzJS/C4uMO9s2ruBIcIfrcs1C4zyQc78wQ07RW1B09GCO1ajBgvpe5Gghp1Vr6zLizg== dependencies: "@babel/core" "^7.25.2" "@babel/plugin-proposal-export-namespace-from" "^7.18.9" @@ -6715,65 +6696,66 @@ metro-babel-register@^0.84.2: "@babel/preset-typescript" "^7.24.7" "@babel/register" "^7.24.6" babel-plugin-replace-ts-export-assignment "^0.0.2" - babel-plugin-syntax-hermes-parser "0.33.3" + babel-plugin-syntax-hermes-parser "0.35.0" babel-plugin-transform-flow-enums "^0.0.2" escape-string-regexp "^1.0.5" flow-enums-runtime "^0.0.6" -metro-babel-transformer@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.84.2.tgz#deeacd8169cb95fa79b2d1e52e7c84a2f5a44aa7" - integrity sha512-UZqjh1VMRDm0WasifM0aN+JreCn3CW0BaPoZgDXb0xOMFSF9dKZJsKhcrpzkjL1+qwmHFYjlhGiQ+tvXdSx+OQ== +metro-babel-transformer@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.84.3.tgz#ee6ab39cb0c8d0c015799b11911bf7f0325b7e1e" + integrity sha512-svAA+yMLpeMiGcz/jKJs4oHpIGEx4nBqNEJ5AGj4CYIg1efvK+A0TjR6tgIuc6tKO5e8JmN/1lglpN2+f3/z/w== dependencies: "@babel/core" "^7.25.2" flow-enums-runtime "^0.0.6" - hermes-parser "0.33.3" + hermes-parser "0.35.0" + metro-cache-key "0.84.3" nullthrows "^1.1.1" -metro-cache-key@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.84.2.tgz#6744a65548b54494c95bd2c07f29e4f87dac0315" - integrity sha512-+yJxLYu5nhKp7jZD6wtx4dMoSqLzK6MeYVkjMaUgjuh2Lu8DwGrxRnbmIVnn5Z9AQOs/K4eOWmuD7N2p64UCMw== +metro-cache-key@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.84.3.tgz#e472f8295d1d44c8f7bea377c554f9b19c5c09b0" + integrity sha512-TnSL1Fdvrw+2glTdBSRmA5TL8l/i16ECjsrUdf3E5HncA+sNx8KcwDG8r+3ct1UhfYcusJypzZqTN55FZZcwGg== dependencies: flow-enums-runtime "^0.0.6" -metro-cache@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.84.2.tgz#43cca15afd618467bb2dee0c9e31f52a882224f0" - integrity sha512-jPX2fwOc/MmP2KRScSg2jFtVN9BTd+QN6j/3qZ+HIbEAsePLONozbKR2kCIBGvVeBTe7js48WXziI4+AdfwfFQ== +metro-cache@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.84.3.tgz#42296dd69033c8c03d0e9da4ff71226486e1aa7f" + integrity sha512-0QElxwLaHqLZf+Xqio8QrjVbuXP/8sJfQBGSPiITlKDVXrVLefuzYVSH9Sj+QL6lrPj2gYZd/iwQh1yZuVKnLA== dependencies: exponential-backoff "^3.1.1" flow-enums-runtime "^0.0.6" https-proxy-agent "^7.0.5" - metro-core "0.84.2" + metro-core "0.84.3" -metro-config@0.84.2, metro-config@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.84.2.tgz#486bc7f64d3b6a4a325c4bedf654c8f2e4181d6f" - integrity sha512-ze7IgJwLJoXoTxeXW86xqqKoxXjE0gZg5w8kW2mawaWLSfuvI0KgVaaERXgoVuWl+DQU2q22tIeAEdsCyUZvBQ== +metro-config@0.84.3, metro-config@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.84.3.tgz#aee12c219e76ba5a9ad02a650bf9acc3b6f86c12" + integrity sha512-JmCzZWOETR+O22q8oPBWyQppx3roU9EbkbGzD8Gf1jukQ4b5T1fTzqqHruu6K4sTiNq5zVQySmKF6bp4kVARew== dependencies: connect "^3.6.5" flow-enums-runtime "^0.0.6" jest-validate "^29.7.0" - metro "0.84.2" - metro-cache "0.84.2" - metro-core "0.84.2" - metro-runtime "0.84.2" + metro "0.84.3" + metro-cache "0.84.3" + metro-core "0.84.3" + metro-runtime "0.84.3" yaml "^2.6.1" -metro-core@0.84.2, metro-core@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.84.2.tgz#8c9ea2a3aa4d4db7639b1bbf4b17fc0b46e35a04" - integrity sha512-s9Ko372nzfbu5Y2uhWDlB/g3E6mba3Es95QzF/8IwNM4ynZgqM9rfnU0PR54onGvDGDfj44jbooSxaA1D09rDA== +metro-core@0.84.3, metro-core@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.84.3.tgz#afa7da4046a60c5e1b249da65df34c48e6dd4192" + integrity sha512-cc0pvAa80ai1nDmqqz0P59a+0ZqCZ/YHU/3jEekZL6spFnYDfX8iDLdn9FR6kX+67rmzKxHNrbrSRFLX2AYocw== dependencies: flow-enums-runtime "^0.0.6" lodash.throttle "^4.1.1" - metro-resolver "0.84.2" + metro-resolver "0.84.3" -metro-file-map@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.84.2.tgz#3d8c60771b1cb757a2ff8c3ba82fc5a6d04dd47d" - integrity sha512-ZgX1lXO9YJCgTY6OSuwvRcHdhXjAFd1DdYC4g2B+d7yAtLUW1/OqwTLpW6ixl1zqZDDQSDSYZXDsN7DL2IumBw== +metro-file-map@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.84.3.tgz#dc287129cdcbe451fe7cc541431480ba3cb83950" + integrity sha512-1cL4m4Jv1yRUt9RJExZQLfccscdlMNOcRG6LHLtmJhf3BG9j3MujPVc7CIpKYdFl+KUl+sdjge6oO3+meKCHQA== dependencies: debug "^4.4.0" fb-watchman "^2.0.0" @@ -6785,60 +6767,60 @@ metro-file-map@0.84.2: nullthrows "^1.1.1" walker "^1.0.7" -metro-minify-terser@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.84.2.tgz#f1a0a46f83ddaeb1e73dd08f362c0daaad01c87b" - integrity sha512-1TNGPN4oUose+XSHsdDUvcvPHQxKP5lZNbiS6UteTXX+6zFNu+IzxqSokyrDoj9BSjVbdClrB3okuI+Fpls3LA== +metro-minify-terser@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.84.3.tgz#f6aeb28c268afadbe40d69a622ed550f970ac02d" + integrity sha512-3ofrG2OQyJbO9RNhCfOcl8QU7EE2WrSsnN5dFkuZaJO5+4Imujr9bUXmspeNlXRsOVk0F/rVRbEFH98lFSCkBQ== dependencies: flow-enums-runtime "^0.0.6" terser "^5.15.0" -metro-resolver@0.84.2, metro-resolver@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.84.2.tgz#9ddefcdfc244f503b77573aacdcdbf947e94b7b3" - integrity sha512-2i6OQJIv18+olvLnmcM20uhi1T729+25izZozqOugSaV0YGzMV/EXkYFqxkXC9iNsantGcI/w9PgaI89wLK6JQ== +metro-resolver@0.84.3, metro-resolver@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.84.3.tgz#a9c6155ccc345fde2affe8c1086b5c7132c2e4ee" + integrity sha512-pjEzGDtoM8DTHAIPK/9u9ZxszEiuRohYUVImWvgbnB91V4gqYJpQcoEYUugf2NIm1lrX5HNu0OvNqWmPBnGYjA== dependencies: flow-enums-runtime "^0.0.6" -metro-runtime@0.84.2, metro-runtime@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.84.2.tgz#0f6846d72a040780c39575983a51d674d4e5f335" - integrity sha512-NzzORY2+mmN3tLhsZ7N4GDOBERusalyM1o1k36euulUIEe8UkDhwzcsRexvxKaSkrGLiRQ9PYDLp9uxPkQ+A0Q== +metro-runtime@0.84.3, metro-runtime@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.84.3.tgz#0ecfba8567cac4d6d3deeeeb83ae807f2d822285" + integrity sha512-o7HLRfMyVk9N2dUZ9VjQfB6xxUItL9Pi9WcqxURE7MEKOH6wbGt9/E92YdYLluTOtkzYAEVfdC6h6lcxqA+hMQ== dependencies: "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-source-map@0.84.2, metro-source-map@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.84.2.tgz#19ccc64c1d586ec9dbc8031327e7b65a14b08197" - integrity sha512-m6rRVBefzaAyn6dBk5GOabVchCQ3VIS1/MhCj61dJB5cqLOOx34BV3DRFwnDBkuPw2RR/LUoul0U1sixlS9VQg== +metro-source-map@0.84.3, metro-source-map@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.84.3.tgz#b84f1a1da427a3ac63095e623e9fdb60fcd5ce44" + integrity sha512-jS48CeSzw78M8y6VE0f9uy3lVmfbOS677j2VCxnlmlYmnahcXuC6IhoN9K6LynNvos9517yUadcfgioju38xYQ== dependencies: "@babel/traverse" "^7.29.0" "@babel/types" "^7.29.0" flow-enums-runtime "^0.0.6" invariant "^2.2.4" - metro-symbolicate "0.84.2" + metro-symbolicate "0.84.3" nullthrows "^1.1.1" - ob1 "0.84.2" + ob1 "0.84.3" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.84.2.tgz#da54c2edb7db535392ddf77c565b52b609f66101" - integrity sha512-o0RY49012YcGE1E4GsZtgzFCBPeoxlASzIsD5CNOTmAoKDIroHfTFFiYCGPLCGwRwQjMaCChhoH0TZCjAyyCKA== +metro-symbolicate@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.84.3.tgz#d03e99887024849031e46728e4a9808e4e670a01" + integrity sha512-J9Tpo8NCycYrozRvBIUyOwGAu4xkawOsAppmTscFiaegK0WvuDGwIM53GbzVSnytCHjVAF0io5GQxpkrKTuc7g== dependencies: flow-enums-runtime "^0.0.6" invariant "^2.2.4" - metro-source-map "0.84.2" + metro-source-map "0.84.3" nullthrows "^1.1.1" source-map "^0.5.6" vlq "^1.0.0" -metro-transform-plugins@0.84.2, metro-transform-plugins@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.84.2.tgz#b0eb7eff77d16b33b76349976dd19d33c3ca9c38" - integrity sha512-/821YLQv4PgD1NOruzPkr0r3HDALXqwCEECewyEQZ5hmSb8jzf1VdEpf3F8fx8zI4/5dHY/rARDVVuHCEb/Xrg== +metro-transform-plugins@0.84.3, metro-transform-plugins@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.84.3.tgz#c0b4e08e38acc4146ae5cde5d0227eca407e21c3" + integrity sha512-8S3baq2XhBaafHEH5Q8sJW6tmzsEJk80qKc3RU/nZV1MsnYq94RdjTUR6AyKjQd6Rfsk1BtBxhtiNnk7mgslCg== dependencies: "@babel/core" "^7.25.2" "@babel/generator" "^7.29.1" @@ -6847,29 +6829,29 @@ metro-transform-plugins@0.84.2, metro-transform-plugins@^0.84.2: flow-enums-runtime "^0.0.6" nullthrows "^1.1.1" -metro-transform-worker@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.84.2.tgz#7843ce4e50bacb8f3d9a13a4c55b072625ed42bd" - integrity sha512-aR09svo3WC7OTYk5YB0VY0iSXOGrPdfmQWIxG8ADD2cKf/B95VR+y4GgVUbqB31buNvgtU+iCx9186i/YaNGlw== +metro-transform-worker@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.84.3.tgz#d2afb2c3ee320ab8d8d74aa2cd43e51f1a7f7720" + integrity sha512-Wjba7PyYktNRsHbPmkx2J2UX32rAzcDXjCu49zPHeF/viJlYJhwRaNePQcHaCRqQ+kmgQT4ThprsnJfDj71ZMA== dependencies: "@babel/core" "^7.25.2" "@babel/generator" "^7.29.1" "@babel/parser" "^7.29.0" "@babel/types" "^7.29.0" flow-enums-runtime "^0.0.6" - metro "0.84.2" - metro-babel-transformer "0.84.2" - metro-cache "0.84.2" - metro-cache-key "0.84.2" - metro-minify-terser "0.84.2" - metro-source-map "0.84.2" - metro-transform-plugins "0.84.2" + metro "0.84.3" + metro-babel-transformer "0.84.3" + metro-cache "0.84.3" + metro-cache-key "0.84.3" + metro-minify-terser "0.84.3" + metro-source-map "0.84.3" + metro-transform-plugins "0.84.3" nullthrows "^1.1.1" -metro@0.84.2, metro@^0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.84.2.tgz#edc12b614a482013039174780130eb20e701faac" - integrity sha512-Qw7sl+e34cf/0LYEvDfVPiWvXmkvpuVgFqjzhPCc9Mw30NsvRFYZEH6I9zEHlpjugIveV+Jzdqt3YSPMU+Hx/w== +metro@0.84.3, metro@^0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.84.3.tgz#1a49f82ecd36dd8876a657110c4a99cd99556fbe" + integrity sha512-1h3lbVrE6hGf1e/764HfhPGg/bGrWMJDDh7G2rc4gFYZboVuI40BlG/y+UhtbhQDNlO/csMvrcnK0YrTlHUVew== dependencies: "@babel/code-frame" "^7.29.0" "@babel/core" "^7.25.2" @@ -6886,24 +6868,24 @@ metro@0.84.2, metro@^0.84.2: error-stack-parser "^2.0.6" flow-enums-runtime "^0.0.6" graceful-fs "^4.2.4" - hermes-parser "0.33.3" + hermes-parser "0.35.0" image-size "^1.0.2" invariant "^2.2.4" jest-worker "^29.7.0" jsc-safe-url "^0.2.2" lodash.throttle "^4.1.1" - metro-babel-transformer "0.84.2" - metro-cache "0.84.2" - metro-cache-key "0.84.2" - metro-config "0.84.2" - metro-core "0.84.2" - metro-file-map "0.84.2" - metro-resolver "0.84.2" - metro-runtime "0.84.2" - metro-source-map "0.84.2" - metro-symbolicate "0.84.2" - metro-transform-plugins "0.84.2" - metro-transform-worker "0.84.2" + metro-babel-transformer "0.84.3" + metro-cache "0.84.3" + metro-cache-key "0.84.3" + metro-config "0.84.3" + metro-core "0.84.3" + metro-file-map "0.84.3" + metro-resolver "0.84.3" + metro-runtime "0.84.3" + metro-source-map "0.84.3" + metro-symbolicate "0.84.3" + metro-transform-plugins "0.84.3" + metro-transform-worker "0.84.3" mime-types "^3.0.1" nullthrows "^1.1.1" serialize-error "^2.1.0" @@ -7374,10 +7356,10 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -ob1@0.84.2: - version "0.84.2" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.84.2.tgz#e5ca33ff4cdda57218197c662f377baf79529d64" - integrity sha512-JID0ti8tDRQZJdQ3l+UeVAsKP+dW5Ucmktes/J9FwqP5KarafoTMqWvw4LRKrMtA7yWT3r/+E2w5wapd89GToA== +ob1@0.84.3: + version "0.84.3" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.84.3.tgz#a3f77263ae46811f98746902329e2227ee802ddd" + integrity sha512-J7554Ef8bzmKaDY365Afq6PF+qtdnY/d5PKUQFrsKlZHV/N3OGZewVrvDrQDyX5V5NJjTpcAKtlrFZcDr+HvpQ== dependencies: flow-enums-runtime "^0.0.6" From ef598d9b5a38c547f1c96a027b2bb2c4fe5d87cb Mon Sep 17 00:00:00 2001 From: Mathieu Acthernoene Date: Wed, 29 Apr 2026 09:30:53 -0700 Subject: [PATCH 02/34] Add tests for DeviceInfoModule.getWindowDisplayMetrics edge-to-edge branches (#56632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This PR adds two focused unit tests for `DeviceInfoModule.getWindowDisplayMetrics()` that attach a mocked `Activity`, swap in a fake `WindowMetricsCalculator` via `WindowMetricsCalculator.overrideDecorator()` and verify both branches of `isEdgeToEdgeFeatureFlagOn`: - edge-to-edge on -> `WindowMetrics.bounds` are used directly. - edge-to-edge off -> bounds minus `systemBars | displayCutout` insets. To make this testable without reflection or `SuppressLint("RestrictedApi")` outside the production code, three small visibility tweaks: - `DeviceInfoModule.getWindowDisplayMetrics()` and `getDisplayMetricsWritableMap()`: `private` → `internal` + `VisibleForTesting` - `WindowUtilKt.isEdgeToEdgeFeatureFlagOn` setter: `private set` → `VisibleForTesting internal set` ## Changelog: [INTERNAL] [ADDED] - Add unit tests for `DeviceInfoModule.getWindowDisplayMetrics` covering both edge-to-edge branches Pull Request resolved: https://github.com/facebook/react-native/pull/56632 Test Plan: ``` ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \ --tests com.facebook.react.modules.deviceinfo.DeviceInfoModuleTest ``` Reviewed By: javache Differential Revision: D102801090 Pulled By: alanleedev fbshipit-source-id: 5e103404ef822d782c87117d77916221c512eaa2 --- .../modules/deviceinfo/DeviceInfoModule.kt | 7 +- .../facebook/react/views/view/WindowUtil.kt | 3 +- .../deviceinfo/DeviceInfoModuleTest.kt | 91 +++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.kt index 6919dd78fded..970d6230082c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.kt @@ -8,6 +8,7 @@ package com.facebook.react.modules.deviceinfo import android.util.DisplayMetrics +import androidx.annotation.VisibleForTesting import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.window.layout.WindowMetricsCalculator @@ -36,7 +37,8 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) : reactContext.addLifecycleEventListener(this) } - private fun getWindowDisplayMetrics(): DisplayMetrics { + @VisibleForTesting + internal fun getWindowDisplayMetrics(): DisplayMetrics { val windowDisplayMetrics = DisplayMetrics() windowDisplayMetrics.setTo(reactApplicationContext.resources.displayMetrics) @@ -64,7 +66,8 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) : return windowDisplayMetrics } - fun getDisplayMetricsWritableMap(): WritableMap = + @VisibleForTesting + internal fun getDisplayMetricsWritableMap(): WritableMap = WritableNativeMap().apply { putMap( "windowPhysicalPixels", diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt index 1fadc7410290..400860cc17ab 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt @@ -14,6 +14,7 @@ import android.view.View import android.view.Window import android.view.WindowInsetsController import android.view.WindowManager +import androidx.annotation.VisibleForTesting import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -35,7 +36,7 @@ internal val DarkNavigationBarColor = Color.argb(0x80, 0x1b, 0x1b, 0x1b) * as enabled elsewhere in the application. */ public var isEdgeToEdgeFeatureFlagOn: Boolean = false - private set + @VisibleForTesting internal set public fun setEdgeToEdgeFeatureFlagOn() { isEdgeToEdgeFeatureFlagOn = true diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.kt index 79d7f7056ece..5182b3ae10f5 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.kt @@ -9,7 +9,17 @@ package com.facebook.react.modules.deviceinfo +import android.app.Activity +import android.graphics.Rect import android.util.DisplayMetrics +import android.view.View +import android.view.Window +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.window.layout.WindowMetrics +import androidx.window.layout.WindowMetricsCalculator +import androidx.window.layout.WindowMetricsCalculatorDecorator import com.facebook.react.bridge.BridgeReactContext import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.ReactContext @@ -17,6 +27,7 @@ import com.facebook.react.bridge.ReactTestHelper import com.facebook.react.bridge.WritableMap import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests import com.facebook.react.uimanager.DisplayMetricsHolder +import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn import com.facebook.testutils.shadows.ShadowNativeLoader import com.facebook.testutils.shadows.ShadowNativeMap import com.facebook.testutils.shadows.ShadowReadableNativeMap @@ -33,6 +44,7 @@ import org.mockito.ArgumentMatchers import org.mockito.MockedStatic import org.mockito.Mockito.mockStatic import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -80,6 +92,7 @@ class DeviceInfoModuleTest : TestCase() { @After fun teardown() { displayMetricsHolder.close() + isEdgeToEdgeFeatureFlagOn = false } @Test @@ -150,10 +163,88 @@ class DeviceInfoModuleTest : TestCase() { assertThat(windowMap.hasKey("densityDpi")).isTrue() } + @Test + fun getWindowDisplayMetrics_usesBoundsWhenEdgeToEdgeOn() { + isEdgeToEdgeFeatureFlagOn = true + + val activity = mock() + doReturn(activity).whenever(reactContext).currentActivity + + val bounds = Rect(0, 0, 1080, 2400) + val calculator = mockCalculator(activity, bounds) + + withWindowMetricsCalculator(calculator) { + val metrics = deviceInfoModule.getWindowDisplayMetrics() + assertThat(metrics.widthPixels).isEqualTo(bounds.width()) + assertThat(metrics.heightPixels).isEqualTo(bounds.height()) + } + } + + @Test + fun getWindowDisplayMetrics_subtractsSystemBarsWhenEdgeToEdgeOff() { + isEdgeToEdgeFeatureFlagOn = false + + val window = mock() + val decorView = mock() + whenever(window.decorView).thenReturn(decorView) + val activity = mock() + whenever(activity.window).thenReturn(window) + doReturn(activity).whenever(reactContext).currentActivity + + val bounds = Rect(0, 0, 1080, 2400) + val calculator = mockCalculator(activity, bounds) + val rootInsets = mockRootInsets(Insets.of(20, 80, 30, 100)) + + withWindowMetricsCalculator(calculator) { + mockStatic(ViewCompat::class.java).use { viewCompatStatic -> + viewCompatStatic + .`when` { ViewCompat.getRootWindowInsets(decorView) } + .thenReturn(rootInsets) + + val metrics = deviceInfoModule.getWindowDisplayMetrics() + assertThat(metrics.widthPixels).isEqualTo(bounds.width() - (20 + 30)) + assertThat(metrics.heightPixels).isEqualTo(bounds.height() - (80 + 100)) + } + } + } + private fun givenDisplayMetricsHolderContains(fakeDisplayMetrics: WritableMap?) { doReturn(fakeDisplayMetrics).whenever(deviceInfoModule).getDisplayMetricsWritableMap() } + private fun mockCalculator(activity: Activity, bounds: Rect): WindowMetricsCalculator { + val windowMetrics = mock() + whenever(windowMetrics.bounds).thenReturn(bounds) + val calculator = mock() + whenever(calculator.computeCurrentWindowMetrics(activity)).thenReturn(windowMetrics) + return calculator + } + + private fun mockRootInsets(insets: Insets): WindowInsetsCompat { + val rootInsets = mock() + val type = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + whenever(rootInsets.getInsets(type)).thenReturn(insets) + return rootInsets + } + + @Suppress("RestrictedApi") + private fun withWindowMetricsCalculator( + target: WindowMetricsCalculator, + block: () -> Unit, + ) { + WindowMetricsCalculator.overrideDecorator( + object : WindowMetricsCalculatorDecorator { + override fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator = + target + } + ) + try { + block() + } finally { + WindowMetricsCalculator.reset() + } + } + companion object { private fun verifyUpdateDimensionsEventsEmitted( context: ReactContext?, From 795d9022707d543540217c72bf05d4e5893ecfe2 Mon Sep 17 00:00:00 2001 From: Ingrid Wang Date: Wed, 29 Apr 2026 09:59:25 -0700 Subject: [PATCH 03/34] Fix unreachable-code-break (#56648) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56648 Changelog: [iOS][Fixed] - Fixing unreachable-code-break Reviewed By: fkgozali Differential Revision: D102855020 fbshipit-source-id: 9f57faa06ee4101da875ec06b7d1e6e37f2de98e --- .../React/CxxLogUtils/RCTDefaultCxxLogFunction.mm | 1 - packages/react-native/ReactCommon/logger/react_native_log.cpp | 1 - .../renderer/imagemanager/RCTImagePrimitivesConversions.h | 4 ---- 3 files changed, 6 deletions(-) diff --git a/packages/react-native/React/CxxLogUtils/RCTDefaultCxxLogFunction.mm b/packages/react-native/React/CxxLogUtils/RCTDefaultCxxLogFunction.mm index 2eec0dda176d..138dc553c681 100644 --- a/packages/react-native/React/CxxLogUtils/RCTDefaultCxxLogFunction.mm +++ b/packages/react-native/React/CxxLogUtils/RCTDefaultCxxLogFunction.mm @@ -28,7 +28,6 @@ void RCTDefaultCxxLogFunction(ReactNativeLogLevel level, const char *message) break; case ReactNativeLogLevelFatal: LOG(FATAL) << message; - break; } } diff --git a/packages/react-native/ReactCommon/logger/react_native_log.cpp b/packages/react-native/ReactCommon/logger/react_native_log.cpp index a15ff7cd587c..344e61770e54 100644 --- a/packages/react-native/ReactCommon/logger/react_native_log.cpp +++ b/packages/react-native/ReactCommon/logger/react_native_log.cpp @@ -47,6 +47,5 @@ void _react_native_log_default(ReactNativeLogLevel level, const char* message) { break; case ReactNativeLogLevelFatal: LOG(FATAL) << message; - break; } } diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h index 905cfe8f2692..a13e6fb6d408 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImagePrimitivesConversions.h @@ -55,16 +55,12 @@ inline static NSURLRequestCachePolicy NSURLRequestCachePolicyFromImageSource( switch (imageSource.cache) { case facebook::react::ImageSource::CacheStategy::Reload: return NSURLRequestReloadIgnoringLocalCacheData; - break; case facebook::react::ImageSource::CacheStategy::ForceCache: return NSURLRequestReturnCacheDataElseLoad; - break; case facebook::react::ImageSource::CacheStategy::OnlyIfCached: return NSURLRequestReturnCacheDataDontLoad; - break; default: return NSURLRequestUseProtocolCachePolicy; - break; } } From 32d3eafdc438eab517e84845ea9efe5165023c56 Mon Sep 17 00:00:00 2001 From: Shubham Kumar Savita Date: Wed, 29 Apr 2026 18:11:39 -0700 Subject: [PATCH 04/34] Add exponential backoff to InspectorPackagerConnection reconnect and exception handling to connect (#56650) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56650 ## Summary Fix occasional SIGSEGV in `operator new` during `InspectorPackagerConnection::connectWebSocket()`. ### Root Cause The `InspectorPackagerConnection` reconnection loop retries every 2 seconds with no backoff or limit. When no Metro dev server is reachable, this results in hundreds of failed WebSocket connection attempts over the app lifetime. Each attempt allocates and deallocates C++ hybrid objects, JNI references, and OkHttp WebSocket instances. This accumulated churn leads to heap fragmentation and eventual heap metadata corruption, manifesting as SIGSEGV in `operator new` when the allocator follows corrupted internal pointers. Additionally, the `connect()` method had no exception handling around the `connectWebSocket()` call, meaning any JNI/Java exception would propagate uncaught. ### Changes 1. **Exponential backoff in `reconnect()`**: Start at 2s, double each retry, cap at 120s. This reduces reconnection attempts from ~500 in 17 minutes to ~15, drastically reducing resource churn. 2. **Try-catch in `connect()`**: Catch exceptions from `connectWebSocket()` (e.g., fbjni/JNI exceptions) and gracefully trigger a reconnect instead of crashing. 3. **Reset backoff on success**: When `didOpen()` fires (successful connection), reset the delay back to 2s and clear `suppressConnectionErrors_`. ## Changelog: [General][Fixed] - Add exponential backoff and exception handling to InspectorPackagerConnection reconnect loop to prevent heap fragmentation crashes Reviewed By: cipolleschi Differential Revision: D100956687 fbshipit-source-id: 235dc0f65f1a4083acf52be4348f4932bb739d33 --- .../InspectorPackagerConnection.cpp | 21 +++++++++++++++++-- .../InspectorPackagerConnectionImpl.h | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp index e2ea5b908bf5..4edf6e0c6e88 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include using namespace std::literals; @@ -22,6 +23,7 @@ namespace facebook::react::jsinspector_modern { static constexpr const std::chrono::duration RECONNECT_DELAY = std::chrono::milliseconds{2000}; +static constexpr const std::chrono::milliseconds MAX_RECONNECT_DELAY{120000}; static constexpr const char* INVALID = ""; // InspectorPackagerConnection::Impl method definitions @@ -301,6 +303,8 @@ void InspectorPackagerConnection::Impl::didReceiveMessage( void InspectorPackagerConnection::Impl::didOpen() { connected_ = true; + reconnectDelayMs_ = RECONNECT_DELAY; + suppressConnectionErrors_ = false; } void InspectorPackagerConnection::Impl::didClose() { @@ -333,7 +337,15 @@ void InspectorPackagerConnection::Impl::connect() { << "Illegal state: Can't connect after having previously been closed."; return; } - webSocket_ = delegate_->connectWebSocket(url_, weak_from_this()); + try { + webSocket_ = delegate_->connectWebSocket(url_, weak_from_this()); + } catch (const std::exception& e) { + LOG(ERROR) << "Failed to create WebSocket connection: " << e.what(); + reconnect(); + } catch (...) { + LOG(ERROR) << "Failed to create WebSocket connection: unknown error"; + reconnect(); + } } void InspectorPackagerConnection::Impl::reconnect() { @@ -357,6 +369,11 @@ void InspectorPackagerConnection::Impl::reconnect() { reconnectPending_ = true; + auto currentDelay = reconnectDelayMs_; + + // Exponential backoff: double the delay for next time, up to the max. + reconnectDelayMs_ = std::min(reconnectDelayMs_ * 2, MAX_RECONNECT_DELAY); + delegate_->scheduleCallback( [weakSelf = weak_from_this()] { auto strongSelf = weakSelf.lock(); @@ -370,7 +387,7 @@ void InspectorPackagerConnection::Impl::reconnect() { strongSelf->connect(); } }, - RECONNECT_DELAY); + currentDelay); } void InspectorPackagerConnection::Impl::closeQuietly() { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h index 560e7c2129a6..d669bfd5ac6a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h @@ -132,6 +132,9 @@ class InspectorPackagerConnection::Impl : public IWebSocketDelegate, // Whether a reconnection is currently pending. bool reconnectPending_{false}; + // Current reconnect delay in milliseconds, used for exponential backoff. + std::chrono::milliseconds reconnectDelayMs_{2000}; + SessionId nextSessionId_{1}; }; From 118198599937601c119357359f996f0f84e27cdd Mon Sep 17 00:00:00 2001 From: Marco Wang Date: Wed, 29 Apr 2026 20:11:37 -0700 Subject: [PATCH 05/34] Update prettier-plugin-hermes-parser in fbsource to 0.36.0 Summary: X-link: https://github.com/facebook/metro/pull/1704 Bump prettier-plugin-hermes-parser to 0.36.0. Changelog: [internal] Reviewed By: gkz Differential Revision: D103107243 fbshipit-source-id: d28c27d35784a1b53f86dea4473b7783d08ef09f --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d41ef125a0ec..af149829f0c3 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", "prettier": "3.6.2", - "prettier-plugin-hermes-parser": "0.35.0", + "prettier-plugin-hermes-parser": "0.36.0", "react": "19.2.3", "react-test-renderer": "19.2.3", "rimraf": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index f850d2b1aca9..a8aaf7439df5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7797,10 +7797,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier-plugin-hermes-parser@0.35.0: - version "0.35.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-hermes-parser/-/prettier-plugin-hermes-parser-0.35.0.tgz#f037f76b50669aa9fd9dcbe78ceec7b891239646" - integrity sha512-+qAxEwdNI6sWw/g/+OxbUp5Tt5WaiuufQpDyE95M13S+P+IO8UDmErSBUwN+QvxakcT0bGTd8sfSLJNZrfV4eg== +prettier-plugin-hermes-parser@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/prettier-plugin-hermes-parser/-/prettier-plugin-hermes-parser-0.36.0.tgz#8839207d3a4290b7afe1bd54fbc5154f963b8cf2" + integrity sha512-CtlJ5l0DC48SN3OWSht5jAEIFU92xwHSe87/Bl005/9TlHyN2aAmPa6T/A2JLiIgRT/lGsCtc6srwEMM3/5k4w== prettier@3.6.2: version "3.6.2" From 29d8405850395e899bed6f380868c1e192fdff00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 30 Apr 2026 10:15:37 -0700 Subject: [PATCH 06/34] Improve Fantom documentation in __docs__/README.md (#56660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56660 Expand `private/react-native-fantom/__docs__/README.md` so it's the single source of truth for Fantom usage. Added sections: - Updated the Usage example to import `setUpDefaultReactNativeEnvironment` and explicitly warn against `InitializeCore` (which installs LogBox and breaks Fantom's error handling). All real `-itest.js` files use this import. - New `### Conventions` section: `__tests__` placement, `-benchmark-itest.js` suffix, using `Fantom.runTask()` for rendering, the `ensureInstance` helper, component-specific instance types, ref-based element access, testing imperative APIs / method timing / edge cases, `takeMountingManagerLogs` for command verification, and "don't oversimplify assertions" / "avoid trivial tests" guidance. - New `### Assertions` section: prefer `.toEqual()` + inline JSX over `.toMatchSnapshot()`, full examples for `getRenderedOutput().toJSX()` with `includeLayoutMetrics` and the `props` filter, plus an element-level assertion example covering `tagName`, `getBoundingClientRect`, and child structure. - New `### Limitations` section: cannot nest `runTask()`, subset of Jest API, tests must live under `packages/react-native`. - New `#### C++ sampling profiler` subsection under Profiling, documenting `FANTOM_PROFILE_CPP` (parallel to the existing JS profiler section). Pure API references (`setLogBoxCheckEnabled`, `dispatchNativeEvent`, `scheduleTask`, `enqueueNativeEvent`, `runOnUIThread`, `runWorkLoop`, `scrollTo`, `takeJSMemoryHeapSnapshot`, `createRoot` options, etc.) are not duplicated — they continue to live in the inline source docs in `src/index.js`, which the README points to. Changelog: [Internal] Reviewed By: mdvacca, cipolleschi Differential Revision: D103217408 fbshipit-source-id: ff01523682961914d795ef400954bcb3ded19dc1 --- .../react-native-fantom/__docs__/README.md | 116 +++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/private/react-native-fantom/__docs__/README.md b/private/react-native-fantom/__docs__/README.md index d2d7e3e68057..c14053413283 100644 --- a/private/react-native-fantom/__docs__/README.md +++ b/private/react-native-fantom/__docs__/README.md @@ -70,9 +70,13 @@ only a subset of Jest's Global API is currently available. For example, APIs. If you are blocked by the lack of a specific API, please reach out to us. Most of the interesting APIs are available via the `@react-native/fantom` -package: +package. Test files should also import `setUpDefaultReactNativeEnvironment` at +the top to set up the React Native environment. **Do not import +`InitializeCore`** — it installs LogBox, which interferes with Fantom's error +handling. ```javascript +import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; import * as Fantom from '@react-native/fantom'; describe('My feature', () => { @@ -108,6 +112,97 @@ Similar to Jest, you can also run Fantom in watch mode using `--watch`: yarn fantom --watch ``` +### Conventions + +- Place test files in `__tests__` directories alongside the code being tested. +- Benchmark tests use the `-benchmark-itest.js` suffix. +- Use `Fantom.runTask()` to render and run synchronous operations; it ensures + the React work is flushed before assertions. +- Access elements through refs and use the + [`ensureInstance`](../../../packages/react-native/src/private/__tests__/utilities/ensureInstance.js) + helper for type-safe access to the underlying instance: + + ```javascript + import ensureInstance from 'react-native/src/private/__tests__/utilities/ensureInstance'; + + const element = ensureInstance(elementRef.current, ReactNativeElement); + ``` + +- Prefer component-specific instance types (`TextInputInstance`, + `ScrollViewInstance`, etc.) over the generic `HostInstance` when available. +- For components with imperative APIs (`focus`, `blur`, `clear`, etc.), test: + - Each method's behaviour and edge cases (e.g. `blur` when not focused). + - Method timing — that it works when called from refs, `useLayoutEffect`, and + `useEffect`. +- Verify that native commands are dispatched with + `root.takeMountingManagerLogs()`. +- Don't write tests with only trivial assertions like + `expect(element).toBeDefined()` when more complex behaviour is under test. + When a test fails, understand what should render rather than weakening the + assertion to make it pass. + +### Assertions + +Prefer evaluating rendered output inline with `.toEqual()` and inline JSX over +`.toMatchSnapshot()` or weak numeric assertions like +`element.childNodes.length`. + +```javascript +// Get JSX representation +expect(root.getRenderedOutput().toJSX()).toEqual( + , +); + +// Include layout metrics +expect(root.getRenderedOutput({includeLayoutMetrics: true}).toJSX()).toEqual( + , +); + +// Filter to specific props for minimal assertions +expect(root.getRenderedOutput({props: ['backgroundColor']}).toJSX()).toEqual( + , +); +``` + +For element-level assertions, get a typed reference and inspect tag names, +layout metrics, or children: + +```javascript +const elementRef = createRef(); + +Fantom.runTask(() => { + root.render( + + the quick brown fox + , + ); +}); + +const element = ensureInstance(elementRef.current, ReactNativeElement); +expect(element.tagName).toBe('RN:View'); + +const bounds = element.getBoundingClientRect(); +expect(bounds.width).toBe(100); +expect(bounds.height).toBe(50); + +expect(root.getRenderedOutput().toJSX()).toEqual( + + the quick brown fox + , +); +``` + +### Limitations + +- `Fantom.runTask()` calls cannot be nested — doing so will throw. +- Only a subset of Jest's Global API is available (e.g. `test.each` is not + implemented). Reach out if you're blocked by a specific missing API. +- Tests must live within `packages/react-native`; Fantom is not currently + intended for application-specific code. + ### Test configuration You can configure certain aspects of the test execution using pragmas in the @@ -303,6 +398,25 @@ memory heap and print a message indicating where it was saved. E.g.: You can have multiple calls to `Fantom.takeJSMemoryHeapSnapshot()` in your test, and each one will create a different file. +#### C++ sampling profiler + +You can automatically record C++ sampling profiler traces (with DWARF call +graphs) by wrapping the Fantom tester binary with Linux `perf record`. Run your +fantom test with the flag `FANTOM_PROFILE_CPP`: + +```shell +FANTOM_PROFILE_CPP=1 yarn fantom +``` + +Output is saved to `.out/cpp-traces/perf-.data`. Analyze it with: + +```shell +perf report -i .out/cpp-traces/perf-.data +``` + +This is not available on CI (the runner will throw an error if attempted) and +requires `perf` to be installed on the host. + ### Running the full suite locally When running the entire suite locally, set `FANTOM_FORCE_CI_MODE=1`: From a3906b629c4831ee4982f7927dfc6943431382b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 30 Apr 2026 10:20:25 -0700 Subject: [PATCH 07/34] Expand globals test coverage in setUpDefaultReactNativeEnvironment-Globals-itest.js (#56661) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56661 Expand the Fantom integration test for `setUpDefaultReactNativeEnvironment` globals so it covers the full set of globals that React Native's default environment is expected to install, not just the global-object aliases and `__DEV__`/`NODE_ENV`. Reorganized the existing tests into nested `describe` blocks (`global object`, `environment`, `JavaScript language features`, `Web APIs`) and added coverage for: - DOM: `Node`, `Document`, `CharacterData`, `Text`, `Element`, `HTMLElement`, `HTMLCollection`, `NodeList`. - Geometry: `DOMRect`, `DOMRectReadOnly`, `DOMRectList`. - Events: `Event`, `EventTarget`, `CustomEvent`. - Performance: `performance`, `Performance`, `PerformanceEntry`, `PerformanceMark`, `PerformanceMeasure`, `PerformanceObserver`, `PerformanceObserverEntryList`, `PerformanceEventTiming`, `PerformanceLongTaskTiming`, `PerformanceResourceTiming`, `EventCounts`, `TaskAttributionTiming`. - Timers and microtasks: `setTimeout`/`clearTimeout`, `setInterval`/`clearInterval`, `setImmediate`/`clearImmediate`, `requestAnimationFrame`/`cancelAnimationFrame`, `requestIdleCallback`/`cancelIdleCallback`, `queueMicrotask`. - Networking: `XMLHttpRequest`, `FormData`, `fetch`, `Headers`, `Request`, `Response`, `WebSocket`, `AbortController`, `AbortSignal`, `URL`, `URLSearchParams`. - File: `Blob`, `File`, and a presence-only check for `FileReader` (accessing the lazy getter triggers a native module that isn't available in the Fantom environment, so we assert the property is registered on `globalThis` without reading it). - JavaScript language features: `Promise`. - Other: `alert`, `navigator`. `PerformanceObserverEntryList` and `EventCounts` aren't recognized as globals by Flow in this file, so they're declared via `declare var ... : unknown` at the top of the file and the assertion checks `typeof`. Changelog: [Internal] ___ overriding_review_checks_triggers_an_audit_and_retroactive_review Oncall Short Name: react_native_iroc landed-with-radar-review Differential Revision: D103224423 fbshipit-source-id: 561687eb5c4b489d3beea0d83fc544d5ac9363d4 --- ...ultReactNativeEnvironment-Globals-itest.js | 265 +++++++++++++++++- 1 file changed, 257 insertions(+), 8 deletions(-) diff --git a/packages/react-native/src/private/setup/__tests__/setUpDefaultReactNativeEnvironment-Globals-itest.js b/packages/react-native/src/private/setup/__tests__/setUpDefaultReactNativeEnvironment-Globals-itest.js index d56c4f9692d5..c319ddbdc061 100644 --- a/packages/react-native/src/private/setup/__tests__/setUpDefaultReactNativeEnvironment-Globals-itest.js +++ b/packages/react-native/src/private/setup/__tests__/setUpDefaultReactNativeEnvironment-Globals-itest.js @@ -11,18 +11,267 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; +declare var PerformanceObserverEntryList: unknown; +declare var EventCounts: unknown; + describe('setUpDefaultReactNativeEnvironment (globals)', () => { - it('should be exposed as globalThis, global, window and self', () => { - expect(globalThis).toBe(global); - expect(globalThis).toBe(window); - expect(globalThis).toBe(self); + describe('global object', () => { + it('should be exposed as globalThis, global, window and self', () => { + expect(globalThis).toBe(global); + expect(globalThis).toBe(window); + expect(globalThis).toBe(self); + }); + }); + + describe('environment', () => { + it('should provide process.env.NODE_ENV', () => { + expect(process.env.NODE_ENV).toBe('development'); + }); + + it('should provide the __DEV__ constant', () => { + expect(__DEV__).toBe(true); + }); }); - it('should provide process.env.NODE_ENV', () => { - expect(process.env.NODE_ENV).toBe('development'); + describe('JavaScript language features', () => { + it('should provide Promise', () => { + expect(typeof Promise).toBe('function'); + }); }); - it('should provide the __DEV__ constant', () => { - expect(__DEV__).toBe(true); + describe('Web APIs', () => { + describe('DOM', () => { + it('should provide Node', () => { + expect(typeof Node).toBe('function'); + }); + + it('should provide Document', () => { + expect(typeof Document).toBe('function'); + }); + + it('should provide CharacterData', () => { + expect(typeof CharacterData).toBe('function'); + }); + + it('should provide Text', () => { + expect(typeof Text).toBe('function'); + }); + + it('should provide Element', () => { + expect(typeof Element).toBe('function'); + }); + + it('should provide HTMLElement', () => { + expect(typeof HTMLElement).toBe('function'); + }); + + it('should provide HTMLCollection', () => { + expect(typeof HTMLCollection).toBe('function'); + }); + + it('should provide NodeList', () => { + expect(typeof NodeList).toBe('function'); + }); + }); + + describe('Geometry', () => { + it('should provide DOMRect', () => { + expect(typeof DOMRect).toBe('function'); + }); + + it('should provide DOMRectReadOnly', () => { + expect(typeof DOMRectReadOnly).toBe('function'); + }); + + it('should provide DOMRectList', () => { + expect(typeof DOMRectList).toBe('function'); + }); + }); + + describe('Events', () => { + it('should provide Event', () => { + expect(typeof Event).toBe('function'); + }); + + it('should provide EventTarget', () => { + expect(typeof EventTarget).toBe('function'); + }); + + it('should provide CustomEvent', () => { + expect(typeof CustomEvent).toBe('function'); + }); + }); + + describe('Performance', () => { + it('should provide performance', () => { + expect(typeof performance).toBe('object'); + expect(performance).not.toBeNull(); + }); + + it('should provide Performance', () => { + expect(typeof Performance).toBe('function'); + }); + + it('should provide PerformanceEntry', () => { + expect(typeof PerformanceEntry).toBe('function'); + }); + + it('should provide PerformanceMark', () => { + expect(typeof PerformanceMark).toBe('function'); + }); + + it('should provide PerformanceMeasure', () => { + expect(typeof PerformanceMeasure).toBe('function'); + }); + + it('should provide PerformanceObserver', () => { + expect(typeof PerformanceObserver).toBe('function'); + }); + + it('should provide PerformanceObserverEntryList', () => { + expect(typeof PerformanceObserverEntryList).toBe('function'); + }); + + it('should provide PerformanceEventTiming', () => { + expect(typeof PerformanceEventTiming).toBe('function'); + }); + + it('should provide PerformanceLongTaskTiming', () => { + expect(typeof PerformanceLongTaskTiming).toBe('function'); + }); + + it('should provide PerformanceResourceTiming', () => { + expect(typeof PerformanceResourceTiming).toBe('function'); + }); + + it('should provide EventCounts', () => { + expect(typeof EventCounts).toBe('function'); + }); + + it('should provide TaskAttributionTiming', () => { + expect(typeof TaskAttributionTiming).toBe('function'); + }); + }); + + describe('Timers and microtasks', () => { + it('should provide setTimeout', () => { + expect(typeof setTimeout).toBe('function'); + }); + + it('should provide clearTimeout', () => { + expect(typeof clearTimeout).toBe('function'); + }); + + it('should provide setInterval', () => { + expect(typeof setInterval).toBe('function'); + }); + + it('should provide clearInterval', () => { + expect(typeof clearInterval).toBe('function'); + }); + + it('should provide setImmediate', () => { + expect(typeof setImmediate).toBe('function'); + }); + + it('should provide clearImmediate', () => { + expect(typeof clearImmediate).toBe('function'); + }); + + it('should provide requestAnimationFrame', () => { + expect(typeof requestAnimationFrame).toBe('function'); + }); + + it('should provide cancelAnimationFrame', () => { + expect(typeof cancelAnimationFrame).toBe('function'); + }); + + it('should provide requestIdleCallback', () => { + expect(typeof requestIdleCallback).toBe('function'); + }); + + it('should provide cancelIdleCallback', () => { + expect(typeof cancelIdleCallback).toBe('function'); + }); + + it('should provide queueMicrotask', () => { + expect(typeof queueMicrotask).toBe('function'); + }); + }); + + describe('Networking', () => { + it('should provide XMLHttpRequest', () => { + expect(typeof XMLHttpRequest).toBe('function'); + }); + + it('should provide FormData', () => { + expect(typeof FormData).toBe('function'); + }); + + it('should provide fetch', () => { + expect(typeof fetch).toBe('function'); + }); + + it('should provide Headers', () => { + expect(typeof Headers).toBe('function'); + }); + + it('should provide Request', () => { + expect(typeof Request).toBe('function'); + }); + + it('should provide Response', () => { + expect(typeof Response).toBe('function'); + }); + + it('should provide WebSocket', () => { + expect(typeof WebSocket).toBe('function'); + }); + + it('should provide AbortController', () => { + expect(typeof AbortController).toBe('function'); + }); + + it('should provide AbortSignal', () => { + expect(typeof AbortSignal).toBe('function'); + }); + + it('should provide URL', () => { + expect(typeof URL).toBe('function'); + }); + + it('should provide URLSearchParams', () => { + expect(typeof URLSearchParams).toBe('function'); + }); + }); + + describe('File', () => { + it('should provide Blob', () => { + expect(typeof Blob).toBe('function'); + }); + + it('should provide File', () => { + expect(typeof File).toBe('function'); + }); + + it('should provide FileReader', () => { + // Note: accessing `FileReader` directly triggers a lazy require that + // depends on a native module not available in the Fantom test + // environment. Verify the global property is registered without + // triggering the lazy getter. + expect('FileReader' in globalThis).toBe(true); + }); + }); + + describe('Other', () => { + it('should provide alert', () => { + expect(typeof alert).toBe('function'); + }); + + it('should provide navigator', () => { + expect(typeof navigator).toBe('object'); + expect(navigator).not.toBeNull(); + }); + }); }); }); From 6ec459735dcb536f365ccd5ec4ec8b099d2d7e07 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Thu, 30 Apr 2026 10:48:33 -0700 Subject: [PATCH 08/34] Fix LayoutAnimation crash from mutation sort ordering (#56629) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56629 `LayoutAnimationDriver::animationMutationsForFrame` was sorting animation-frame Update mutations together with structural mutations (Remove/Insert/Delete) from completed animations. Animation-frame Updates reference views by their pre-structural-change parentTag. When `std::stable_sort` reordered these Updates after Remove/Insert pairs that reparented a view, `StubViewTree::mutate` hit a parentTag mismatch assertion (`react_native_assert(oldStubView->parentTag == mutation.parentTag)`) and crashed with SIGABRT. The fix records the boundary between animation-frame Updates and final structural mutations, then only sorts the structural portion. This preserves the invariant that animation-frame Updates execute before any structural changes. Per zeyap's review: simplified to just the LayoutAnimationDriver.cpp change. The earlier MutationComparator (`Update < Insert`) tweak and the `TEST_F` + feature-flag test refactor were dropped — the driver-side fix alone unbreaks the failing test. Changelog: [Internal] Reviewed By: zeyap Differential Revision: D102696875 fbshipit-source-id: abaac2ae6dae44f7df1f4088bb8ec19b055c2b1a --- .../renderer/animations/LayoutAnimationDriver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp index 5b634bfba162..5317ecc5890e 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp @@ -74,6 +74,10 @@ void LayoutAnimationDriver::animationMutationsForFrame( } } + // Record boundary: animation-frame Updates above this point carry + // parentTag from before any structural changes and must execute first. + auto animationUpdatesEnd = mutationsList.size(); + // Clear out finished animations for (auto it = inflightAnimations_.begin(); it != inflightAnimations_.end();) { @@ -99,10 +103,11 @@ void LayoutAnimationDriver::animationMutationsForFrame( } } - // Final step: make sure that all operations execute in the proper order. - // REMOVE operations with highest indices must operate first. + // Sort only the final mutations from completed animations. + // Animation-frame Updates must stay before structural mutations because + // they reference views by their pre-structural-change parentTag. std::stable_sort( - mutationsList.begin(), + mutationsList.begin() + static_cast(animationUpdatesEnd), mutationsList.end(), &shouldFirstComeBeforeSecondMutation); } From eba0bfb2c8a3e9b16ffdda1d3a704b71b40dc26a Mon Sep 17 00:00:00 2001 From: jingjing2222 Date: Thu, 30 Apr 2026 11:51:01 -0700 Subject: [PATCH 09/34] Enable `overrideBySynchronousMountPropsAtMountingAndroid` by default on Android (#56652) Summary: Enable `overrideBySynchronousMountPropsAtMountingAndroid` by default on Android. This flag was added to prevent regular Fabric mount updates from overriding newer props that were already applied synchronously by Native Animated direct manipulation. Without this path enabled, Android can still hit a race where: 1. Native Animated applies `transform` props synchronously on the UI thread. 2. A regular Fabric mount update for the same view is already in flight with older props. 3. That regular mount update reaches `SurfaceMountingManager.updateProps()` later and overwrites the newer synchronous animated value. 4. The view visibly jumps/flickers during the animation. This is not just theoretical. I spent about a week debugging this from a real Android flickering issue, and the final repro is intentionally very small. The visual impact is clear in the README videos, and I am attaching the same videos here as well. Minimal repro: https://github.com/jingjing2222/fix-react-native-android-flickering The repro uses React Native 0.85.2 with Android Fabric. The JS only runs `Animated.timing(..., {useNativeDriver: true})` for `translateX` and `translateY`. ## As-is https://github.com/user-attachments/assets/64882c09-1580-4209-a4aa-aa62bcf58287 With the current default, flickering is visible during the animation. ## To-be https://github.com/user-attachments/assets/fa929562-36b3-4930-98b5-754098e571e3 With `overrideBySynchronousMountPropsAtMountingAndroid` enabled, the flickering is gone. The existing mounting-layer flag fixes this by storing the latest synchronous mount props and reapplying them when regular mount props are processed. This makes the final mounted props preserve the latest Native Animated direct manipulation value. Because this can happen with a simple native-driver transform animation, enabling this default should prevent other Android users from spending time debugging the same race condition. ### Why Android only? iOS Fabric already tracks props managed by NativeAnimated on the component view. When NativeAnimated synchronously updates props such as `transform` or `opacity`, iOS records those prop keys and prevents later normal Fabric updates from re-applying stale values for those props. Android Fabric has a similar mechanism through `tagToSynchronousMountProps`, but it was gated behind `overrideBySynchronousMountPropsAtMountingAndroid`, which defaults to false. Therefore, NativeAnimated could synchronously apply the final transform to the view, and a later normal Fabric props update could re-apply stale transform props, causing a transient flicker. This change makes Android preserve synchronously mounted NativeAnimated props across later Fabric prop updates, matching the ownership behavior already present on iOS. ## Changelog: [ANDROID] [FIXED] - Prevent stale Fabric mount updates from overriding newer Native Animated synchronous prop updates. Pull Request resolved: https://github.com/facebook/react-native/pull/56652 Test Plan: - Ran `yarn featureflags --update` - Ran `git diff --check` - Verified generated feature flag defaults were updated for Android, C++, and JS. - Verified the minimal repro with the README videos attached above: - as-is/current default: flickering is visible during `translateX`/`translateY` native-driver animation - to-be/flag enabled: flickering is not visible Reviewed By: javache, sammy-SC Differential Revision: D103070058 Pulled By: zeyap fbshipit-source-id: c5f140ee28ec090677b0cc1b1ad9db35c79aba8d --- .../internal/featureflags/ReactNativeFeatureFlagsDefaults.kt | 4 ++-- .../react/featureflags/ReactNativeFeatureFlagsDefaults.h | 4 ++-- .../scripts/featureflags/ReactNativeFeatureFlags.config.js | 2 +- .../src/private/featureflags/ReactNativeFeatureFlags.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) 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 a9e1a1e0cb74..7136c0a4758a 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<> */ /** @@ -153,7 +153,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun hideOffscreenVirtualViewsOnIOS(): Boolean = false - override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = false + override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = true override fun perfIssuesEnabled(): Boolean = false diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index bfbe407374af..10df0490068d 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<<4a2fd61cbcdb28042f09ccb03c970674>> + * @generated SignedSource<> */ /** @@ -288,7 +288,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool overrideBySynchronousMountPropsAtMountingAndroid() override { - return false; + return true; } bool perfIssuesEnabled() override { diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 8f5856661e98..6008e3209f1b 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -743,7 +743,7 @@ const definitions: FeatureFlagDefinitions = { ossReleaseStage: 'none', }, overrideBySynchronousMountPropsAtMountingAndroid: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2025-09-04', description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index ad14fb3138a6..8f5f75e62a14 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<<1dd51a152bb30c0e2073a14566c8368d>> * @flow strict * @noformat */ @@ -468,7 +468,7 @@ export const hideOffscreenVirtualViewsOnIOS: Getter = createNativeFlagG /** * Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated. */ -export const overrideBySynchronousMountPropsAtMountingAndroid: Getter = createNativeFlagGetter('overrideBySynchronousMountPropsAtMountingAndroid', false); +export const overrideBySynchronousMountPropsAtMountingAndroid: Getter = createNativeFlagGetter('overrideBySynchronousMountPropsAtMountingAndroid', true); /** * Enable reporting Performance Issues (`detail.devtools.performanceIssue`). Displayed in the V2 Performance Monitor and the "Performance Issues" sub-panel in DevTools. */ From 90c2c595dacb0dccddf075f5c52224a6a298d995 Mon Sep 17 00:00:00 2001 From: Kyle Howarth Date: Thu, 30 Apr 2026 12:01:01 -0700 Subject: [PATCH 10/34] Fix text wrapping in absolutely positioned elements on Android (#56651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56651 ## Context On Android TV, text inside absolutely-positioned Views wraps unexpectedly when the computed width from Yoga is fractional. This manifests in Instagram TV as "Keep watching" breaking to two lines in the exit dialog when the button text is focused (rendered via an absolutely-positioned overlay for color animation). The root cause is a rounding mismatch in React Native's Android `TextLayoutManager`: `desiredWidth` (the text's needed width) uses `ceil()` (rounds up), but `layoutWidth` in `EXACTLY` mode uses `floor()` (rounds down). When Yoga passes a fractional width (e.g. 258.5px), the container gets `floor(258.5) = 258px` but the text needs `ceil(258.3) = 259px`, causing a 1px shortfall that triggers wrapping. ## Fix In `TextLayoutManager.createLayout()`, change `floor(width).toInt()` to `ceil(width).toInt()` for `YogaMeasureMode.EXACTLY` in **both** layout paths so the behavior is consistent regardless of which `Layout` class is chosen: - BoringLayout (single-line text that fits) - StaticLayout (multi-line or complex text) `YogaMeasureMode.AT_MOST` is intentionally left as `floor(width).toInt()`. `AT_MOST` is a constraint contract from Yoga ("do not exceed this width"), so flooring remains the correct conservative behavior — ceiling there could violate the constraint by up to 1px. The BoringLayout entry guard (`boring.width <= floor(width)`) is also left unchanged. If a boring text fails the guard, it falls through to the StaticLayout path, which now also ceils for `EXACTLY`, so no truncation results — the only effect is a slightly less optimal layout class choice in a narrow edge case. ## Why ceiling `EXACTLY` is safe `EXACTLY` means Yoga has guaranteed this width — the container has been allocated at least the full fractional width upstream. Ceiling the local layout width by 1px cannot exceed what Yoga has reserved, while flooring it produced a 1px shortfall that mismatched `desiredWidth`'s ceiling and triggered wrapping. The compensating mechanism in `calculateWidth()` — which returns the raw fractional width to Yoga for `EXACTLY` mode rather than the floored `layout.width` — is preserved, so Yoga's upstream allocation reasoning is unchanged. This was the subpixel-safety property introduced in D74685353; only the local pixel rounding inside `createLayout` changes from "round down 1px and risk wrapping" to "round up 1px and match `desiredWidth`". ## Testing Added a new test case to cover this behavior. If this needs to be adjusted in the future, a failure will highlight this bug and ensure it's covered or mitigated with additional test cases. ## Notes This was confirmed as a problem with an absolutely positioned style with `left:0, right:0` applied. Width sizing was confirmed to be the issue when `left:-1, right:-1` resolved the issue. Further investigation found this fix in text sizing. Only `EXACTLY` is needed to fix the observed Instagram TV bug. `AT_MOST` is left untouched because the constraint semantics differ. ## Changelog: [Android] [Fixed] - Fix 1px text wrapping in absolutely positioned elements caused by fractional Yoga widths Reviewed By: Abbondanzo Differential Revision: D102920508 fbshipit-source-id: 05fad96333a282ad274a5686af3a0d30e6a712f2 --- .../react/views/text/TextLayoutManager.kt | 9 +- ...erAbsoluteLayoutWithFractionalPixelTest.kt | 131 ++++++++++++++++++ 2 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt index f2c88294f656..eacec697670d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt @@ -618,9 +618,12 @@ internal object TextLayoutManager { boring != null && (widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || boring.width <= floor(width)) ) { + // Guard uses floor() but layout width below uses ceil() for EXACTLY mode intentionally: + // text that barely fails the floor-based guard falls through to StaticLayout, which also + // ceils for EXACTLY — no wrapping results, just a slightly less optimal layout class in a + // rare subpixel edge case. val layoutWidth = - if (widthYogaMeasureMode == YogaMeasureMode.EXACTLY) floor(width).toInt() - else boring.width + if (widthYogaMeasureMode == YogaMeasureMode.EXACTLY) ceil(width).toInt() else boring.width return BoringLayout.make( text, paint, @@ -637,7 +640,7 @@ internal object TextLayoutManager { val layoutWidth = when (widthYogaMeasureMode) { - YogaMeasureMode.EXACTLY -> floor(width).toInt() + YogaMeasureMode.EXACTLY -> ceil(width).toInt() YogaMeasureMode.AT_MOST -> min(desiredWidth, floor(width).toInt()) else -> desiredWidth } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest.kt new file mode 100644 index 000000000000..33e00f0af10c --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.text + +import android.annotation.SuppressLint +import android.text.BoringLayout +import android.text.Layout +import android.text.SpannableString +import android.text.TextPaint +import android.text.TextUtils +import com.facebook.yoga.YogaMeasureMode +import kotlin.math.ceil +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +/** + * Regression test for the "Keep watching" 1px text-wrap bug. + * + * Scenario: A dialog renders a primary button label inside an absolutely-positioned + * focused/unfocused overlay (left:0/right:0). Yoga hands the inner Text a fractional EXACTLY width + * (e.g. 258.5px on a 517px parent). Pre-fix, TextLayoutManager.createLayout floored the EXACTLY + * width to 258 while the text the layout actually renders needed 259px, leaving the layout 1px + * narrower than its own content — on a real device that forces the text to wrap to a second line + * (taller label height). The user-visible symptom is "Keep watching" rendering on two lines instead + * of one inside the dialog. + * + * Direct assertion: the layout.width returned by createLayout (the horizontal box StaticLayout was + * told it has) must be >= layout.getLineWidth(0) (the horizontal advance of the rendered line). + * When that invariant is violated, the rendered text doesn't fit in the layout's allocated space + * and the next layout pass wraps it. Robolectric stubs real font metrics (1px/char), but the + * relationship between allocated width and rendered line width is faithful enough to expose the + * floor/ceil bug. + */ +@RunWith(RobolectricTestRunner::class) +class TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest { + + @Test + fun `EXACTLY mode with fractional width allocates a layout wide enough to fit its own text`() { + val text = SpannableString("Keep watching") + val paint = TextPaint(TextPaint.ANTI_ALIAS_FLAG).apply { textSize = 16f } + + val desiredWidth = Layout.getDesiredWidth(text, paint) + val ceilDesired = ceil(desiredWidth).toInt() + + // Construct the exact repro: a fractional EXACTLY width whose ceil equals + // ceil(desiredWidth) but whose floor is one pixel short. Yoga has reserved the full + // fractional width upstream, so flooring shaves 1px and triggers the wrap bug. + val fractionalWidth = ceilDesired - 0.5f + + val layout = invokeCreateLayout(text, fractionalWidth, paint) + val renderedLineWidth = layout.getLineWidth(0) + + assertThat(layout.width.toFloat()) + .withFailMessage( + "Layout's allocated width (%d) is smaller than the rendered text it contains " + + "(line 0 width=%.2f) for '%s' at EXACTLY width=%.2f " + + "(layoutClass=%s, desiredWidth=%.2f, ceilDesired=%d). " + + "On a real Android device this 1px shortfall forces the label to wrap to a " + + "second line, doubling its height — the visible \"Keep watching\" bug in a " + + "dialog. Root cause: TextLayoutManager.createLayout used floor(width) for " + + "EXACTLY mode, producing a layout narrower than its own text.", + layout.width, + renderedLineWidth, + text.toString(), + fractionalWidth, + layout::class.java.simpleName, + desiredWidth, + ceilDesired, + ) + .isGreaterThanOrEqualTo(renderedLineWidth) + } + + /** + * Invokes the private TextLayoutManager.createLayout via reflection. We can't call it directly + * because it's `private` (friend_paths only opens up `internal`). Default values mirror what + * measureText() passes in the production path for a plain single-paragraph label. + * + * BREAK_STRATEGY_HIGH_QUALITY and HYPHENATION_FREQUENCY_NONE are API 23+ constants. The test runs + * only on Robolectric (JVM), never on a device, so the inlined integer values are safe. + */ + @SuppressLint("InlinedApi") + private fun invokeCreateLayout( + text: SpannableString, + width: Float, + paint: TextPaint, + ): Layout { + val boring: BoringLayout.Metrics? = BoringLayout.isBoring(text, paint) + val method = + TextLayoutManager::class + .java + .getDeclaredMethod( + "createLayout", + android.text.Spannable::class.java, + BoringLayout.Metrics::class.java, + java.lang.Float.TYPE, + YogaMeasureMode::class.java, + java.lang.Boolean.TYPE, + java.lang.Integer.TYPE, + java.lang.Integer.TYPE, + Layout.Alignment::class.java, + java.lang.Integer.TYPE, + TextUtils.TruncateAt::class.java, + java.lang.Integer.TYPE, + TextPaint::class.java, + ) + .apply { isAccessible = true } + + return method.invoke( + TextLayoutManager, + text, + boring, + width, + YogaMeasureMode.EXACTLY, + /* includeFontPadding = */ false, + /* textBreakStrategy = */ Layout.BREAK_STRATEGY_HIGH_QUALITY, + /* hyphenationFrequency = */ Layout.HYPHENATION_FREQUENCY_NONE, + Layout.Alignment.ALIGN_NORMAL, + /* justificationMode = */ 0, + /* ellipsizeMode = */ null, + /* maxNumberOfLines = */ 2, + paint, + ) as Layout + } +} From 0b3df3f3821222013a579167451fcb8e35240c81 Mon Sep 17 00:00:00 2001 From: Marco Wang Date: Thu, 30 Apr 2026 15:26:55 -0700 Subject: [PATCH 11/34] Update hermes-parser and related packages in fbsource to 0.36.0 (#56662) Summary: X-link: https://github.com/facebook/hermes/pull/2003 X-link: https://github.com/facebook/relay/pull/5265 Pull Request resolved: https://github.com/facebook/react-native/pull/56662 X-link: https://github.com/facebook/flow/pull/9405 Bump hermes-parser and related packages to [0.36.0](https://github.com/facebook/hermes/blob/static_h/tools/hermes-parser/js/CHANGELOG.md). Changelog: [internal] Reviewed By: SamChou19815 Differential Revision: D103172778 fbshipit-source-id: 81bb5f18da6f46d07955956fb0b721f74cceca47 --- package.json | 8 +-- .../eslint-plugin-react-native/package.json | 4 +- packages/eslint-plugin-specs/package.json | 4 +- .../react-native-babel-preset/package.json | 2 +- .../package.json | 2 +- packages/react-native-codegen/package.json | 6 +- packages/react-native/package.json | 2 +- yarn.lock | 61 ++++++++++++------- 8 files changed, 54 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index af149829f0c3..4dfcf99d0c06 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "ansi-regex": "^5.0.0", "ansi-styles": "^4.2.1", "babel-plugin-minify-dead-code-elimination": "^0.5.2", - "babel-plugin-syntax-hermes-parser": "0.35.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", "babel-plugin-transform-define": "^2.1.4", "babel-plugin-transform-flow-enums": "^0.0.2", "clang-format": "^1.8.0", @@ -85,10 +85,10 @@ "eslint-plugin-redundant-undefined": "^0.4.0", "eslint-plugin-relay": "^1.8.3", "fb-dotslash": "0.5.8", - "flow-api-translator": "0.35.0", + "flow-api-translator": "0.36.0", "flow-bin": "^0.311.0", - "hermes-eslint": "0.35.0", - "hermes-transform": "0.35.0", + "hermes-eslint": "0.36.0", + "hermes-transform": "0.36.0", "ini": "^5.0.0", "inquirer": "^7.1.0", "jest": "^29.7.0", diff --git a/packages/eslint-plugin-react-native/package.json b/packages/eslint-plugin-react-native/package.json index b16c8d4bbe3e..c4d16ea88141 100644 --- a/packages/eslint-plugin-react-native/package.json +++ b/packages/eslint-plugin-react-native/package.json @@ -18,8 +18,8 @@ "bugs": "https://github.com/facebook/react-native/issues", "main": "index.js", "devDependencies": { - "babel-plugin-syntax-hermes-parser": "0.35.0", - "hermes-eslint": "0.35.0" + "babel-plugin-syntax-hermes-parser": "0.36.0", + "hermes-eslint": "0.36.0" }, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" diff --git a/packages/eslint-plugin-specs/package.json b/packages/eslint-plugin-specs/package.json index 4b7a36779a83..32d23fd8944b 100644 --- a/packages/eslint-plugin-specs/package.json +++ b/packages/eslint-plugin-specs/package.json @@ -29,11 +29,11 @@ "@react-native/codegen": "0.86.0-main", "make-dir": "^2.1.0", "pirates": "^4.0.1", - "babel-plugin-syntax-hermes-parser": "0.35.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", "source-map-support": "0.5.0" }, "devDependencies": { - "hermes-eslint": "0.35.0" + "hermes-eslint": "0.36.0" }, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" diff --git a/packages/react-native-babel-preset/package.json b/packages/react-native-babel-preset/package.json index ed011f4c265d..567efabac38e 100644 --- a/packages/react-native-babel-preset/package.json +++ b/packages/react-native-babel-preset/package.json @@ -55,7 +55,7 @@ "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@react-native/babel-plugin-codegen": "0.86.0-main", - "babel-plugin-syntax-hermes-parser": "0.35.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, diff --git a/packages/react-native-babel-transformer/package.json b/packages/react-native-babel-transformer/package.json index 6a334b70de66..7e59d70d9404 100644 --- a/packages/react-native-babel-transformer/package.json +++ b/packages/react-native-babel-transformer/package.json @@ -28,7 +28,7 @@ "dependencies": { "@babel/core": "^7.25.2", "@react-native/babel-preset": "0.86.0-main", - "hermes-parser": "0.35.0", + "hermes-parser": "0.36.0", "nullthrows": "^1.1.1" }, "peerDependencies": { diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index df58baf0ea0e..ef337321112a 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -31,7 +31,7 @@ "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.29.0", - "hermes-parser": "0.35.0", + "hermes-parser": "0.36.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "tinyglobby": "^0.2.15", @@ -45,8 +45,8 @@ "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/preset-env": "^7.25.3", - "babel-plugin-syntax-hermes-parser": "0.35.0", - "hermes-estree": "0.35.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", + "hermes-estree": "0.36.0", "micromatch": "^4.0.4", "prettier": "3.6.2", "rimraf": "^3.0.2" diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 79ab61fba700..e2e40fb75c77 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -172,7 +172,7 @@ "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", - "babel-plugin-syntax-hermes-parser": "0.35.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", diff --git a/yarn.lock b/yarn.lock index a8aaf7439df5..e95e2d7f7606 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3031,6 +3031,13 @@ babel-plugin-syntax-hermes-parser@0.35.0: dependencies: hermes-parser "0.35.0" +babel-plugin-syntax-hermes-parser@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.36.0.tgz#51e429f32f23b197c477a20525798a7a97709c80" + integrity sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q== + dependencies: + hermes-parser "0.36.0" + babel-plugin-transform-define@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-2.1.4.tgz#8f7088211176a55a72788d584ceea9f691a021c1" @@ -4727,19 +4734,19 @@ flora-colossus@^2.0.0: debug "^4.3.4" fs-extra "^10.1.0" -flow-api-translator@0.35.0: - version "0.35.0" - resolved "https://registry.yarnpkg.com/flow-api-translator/-/flow-api-translator-0.35.0.tgz#96f5aba20d4514bb2de0b681e875ae6d50781c48" - integrity sha512-YYLZLC/6PJMsBmTmBkXLCmMgVPBjJTHi5z8XB1HAIZShxYcIwK5lv3idwsE2im3VGRjXWBwI3J3lfrvSOt553A== +flow-api-translator@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/flow-api-translator/-/flow-api-translator-0.36.0.tgz#18737e0d74c339d6dda33ba9d179f99595f6f9f2" + integrity sha512-mzyczOh7gYKa1Uo2NqjVEzVDwLWZxIqrinF68TQ/48susbxX8HG3TshVb03eO4Ngp83JBVpNUJyarYeGYK8t4Q== dependencies: "@babel/code-frame" "^7.16.0" "@typescript-eslint/parser" "8.38.0" "@typescript-eslint/visitor-keys" "8.38.0" flow-enums-runtime "^0.0.6" - hermes-eslint "0.35.0" - hermes-estree "0.35.0" - hermes-parser "0.35.0" - hermes-transform "0.35.0" + hermes-eslint "0.36.0" + hermes-estree "0.36.0" + hermes-parser "0.36.0" + hermes-transform "0.36.0" typescript "5.3.2" flow-bin@^0.311.0: @@ -5129,14 +5136,14 @@ hermes-compiler@0.0.0: resolved "https://registry.facebook.net/hermes-compiler/-/hermes-compiler-0.0.0.tgz#8d9f6a0b2740ce34d71258fec684e7b6bfd97efa" integrity sha512-boVFutx6ME/Km2mB6vvsQcdnazEYYI/jV1pomx1wcFUG/EVqTkr5CU0CW9bKipOA/8Hyu3NYwW3THg2Q1kNCfA== -hermes-eslint@0.35.0: - version "0.35.0" - resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.35.0.tgz#b9b0d414d6a00a409319d07fe66d7b8617746357" - integrity sha512-JzoEjvCCBxjicUSwzqJK0JBQU6+31qKIh2xvBQXLKmFtX9YUj0wqNVEHrIU0sAds1deUL5kA62REDSmcuSVNxA== +hermes-eslint@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/hermes-eslint/-/hermes-eslint-0.36.0.tgz#d8148b7425e7c5ce02e6aa4e092fc1edb0419fa7" + integrity sha512-oEmTrP/Onp8kA5N23/yF4kGgSKB+9KH0CsrY+zGsgq6nK4CqY0y7Atv51X+F4deBHxWngLOvYBLt32HtOps78Q== dependencies: esrecurse "^4.3.0" - hermes-estree "0.35.0" - hermes-parser "0.35.0" + hermes-estree "0.36.0" + hermes-parser "0.36.0" hermes-estree@0.25.1: version "0.25.1" @@ -5148,6 +5155,11 @@ hermes-estree@0.35.0: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.35.0.tgz#767cce0b14a68b4bc06cd5db7efe889f6188c565" integrity sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg== +hermes-estree@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/hermes-estree/-/hermes-estree-0.36.0.tgz#5b8c162e986159baf86bc4d01612cff9845ab0f7" + integrity sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w== + hermes-parser@0.25.1, hermes-parser@^0.25.1: version "0.25.1" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" @@ -5162,17 +5174,24 @@ hermes-parser@0.35.0: dependencies: hermes-estree "0.35.0" -hermes-transform@0.35.0: - version "0.35.0" - resolved "https://registry.yarnpkg.com/hermes-transform/-/hermes-transform-0.35.0.tgz#81c83482c93f28b8f36abf8f3a33a426c2fab12d" - integrity sha512-JTsO7k868agX0IDphpNk51AX4HI+jYQdmVw4DIXzqYtNDTr8e2D7gItzdCh8gzEcdGfTtkIpgIFYJlUBEPJOig== +hermes-parser@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/hermes-parser/-/hermes-parser-0.36.0.tgz#756d6b07301789c5ff0ac3ac7ff6bffa8be7098c" + integrity sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w== + dependencies: + hermes-estree "0.36.0" + +hermes-transform@0.36.0: + version "0.36.0" + resolved "https://registry.facebook.net/hermes-transform/-/hermes-transform-0.36.0.tgz#88ed181f4ac16985290928f03124e1107505698d" + integrity sha512-GB4vu6D/JMNX4jpgyr2suZCNdR8toaMdxF2598UiBzQhP89lAFs8c/pMWYEbfX8Y44Bvzzx0ztfJnrWzKls8HA== dependencies: "@babel/code-frame" "^7.16.0" esquery "^1.4.0" flow-enums-runtime "^0.0.6" - hermes-eslint "0.35.0" - hermes-estree "0.35.0" - hermes-parser "0.35.0" + hermes-eslint "0.36.0" + hermes-estree "0.36.0" + hermes-parser "0.36.0" string-width "4.2.3" hosted-git-info@^2.1.4: From 5ae948ebdfe8e48e843b22e1cb44ff6590afff8d Mon Sep 17 00:00:00 2001 From: Marco Wang Date: Thu, 30 Apr 2026 18:58:33 -0700 Subject: [PATCH 12/34] Deploy 0.312.0 to xplat (#56664) Summary: X-link: https://github.com/facebook/relay/pull/5266 Pull Request resolved: https://github.com/facebook/react-native/pull/56664 [changelog](https://github.com/facebook/flow/blob/main/Changelog.md) Changelog: [Internal] Reviewed By: gkz Differential Revision: D103276599 fbshipit-source-id: 1fa9ee978bb17267f36d782df635dc15ffebf6f7 --- .flowconfig | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.flowconfig b/.flowconfig index 0158c44fd67a..c9eefdf29529 100644 --- a/.flowconfig +++ b/.flowconfig @@ -95,4 +95,4 @@ untyped-import untyped-type-import [version] -^0.311.0 +^0.312.0 diff --git a/package.json b/package.json index 4dfcf99d0c06..22804684b8d1 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "eslint-plugin-relay": "^1.8.3", "fb-dotslash": "0.5.8", "flow-api-translator": "0.36.0", - "flow-bin": "^0.311.0", + "flow-bin": "^0.312.0", "hermes-eslint": "0.36.0", "hermes-transform": "0.36.0", "ini": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index e95e2d7f7606..15f049e1396c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4749,10 +4749,10 @@ flow-api-translator@0.36.0: hermes-transform "0.36.0" typescript "5.3.2" -flow-bin@^0.311.0: - version "0.311.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.311.0.tgz#a1cbef22d1cb9e05b58ffdbad1c73ed03f4122e2" - integrity sha512-4lXxjhPdmkeizju3F0HDCMYGkoL7hiq0W9bAW4pQpQTi56op+QZrVyMENjbCGZc+KlFBLwWkur+EkyfPTsa6xw== +flow-bin@^0.312.0: + version "0.312.0" + resolved "https://registry.facebook.net/flow-bin/-/flow-bin-0.312.0.tgz#4962c29a740beff72eb4b89b0f9625213b3a82d8" + integrity sha512-SVjNNbWt8d69cj6dLXy5P8pq3p6dpje8cL3PvPEqY2KoloiYdpxjOOEZTFvliZV7gxKzGB83iGLA1ct6TH/7hw== flow-enums-runtime@^0.0.6: version "0.0.6" From 3446958490e4a117f9f68c14c12a27394ad1bab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 1 May 2026 04:19:01 -0700 Subject: [PATCH 13/34] Honor skipBubbling in the new EventTarget-based event dispatch (#56666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56666 The feature flag `enableNativeEventTargetEventDispatching` switches the React Native renderer from the legacy plugin-based event dispatch to a new W3C `EventTarget`-based dispatch in `dispatchNativeEvent.js`. The legacy path honors the `phasedRegistrationNames.skipBubbling` flag declared in view configs by short-circuiting the bubble traversal. The new path was unconditionally setting `bubbles: true` for any event with a `customBubblingEventTypes` entry, so `topPointerEnter` / `topPointerLeave` (the only events that declare `skipBubbling: true` today) ended up bubbling to all ancestors, breaking the W3C Pointer Events contract that `pointerenter` / `pointerleave` do not bubble. Map `skipBubbling: true` to `bubbles: false` on the synthesized `LegacySyntheticEvent`. The existing `EventTarget.dispatch()` bubble loop already short-circuits when `event.bubbles` is `false` and `target !== eventTarget`, so the target's own bubble + capture handlers still fire — only ancestor bubble handlers are suppressed. Capture-phase listeners are unaffected. Changelog: [internal] Reviewed By: sammy-SC Differential Revision: D103200424 fbshipit-source-id: 8f246b09017bd1a1026e79f5aeb78123f5bba873 --- .../__tests__/EventTargetDispatching-itest.js | 126 ++++++++++++++++++ .../renderer/events/dispatchNativeEvent.js | 12 +- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js b/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js index bad91d553a56..4c230499c4a6 100644 --- a/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js +++ b/packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js @@ -1476,5 +1476,131 @@ const {isOSS} = Fantom.getConstants(); expect(capturedDispatchConfig.registrationName).toBe('onLayout'); }); }); + + // --- skipBubbling --- + + describe('skipBubbling (pointerenter / pointerleave)', () => { + it('does not bubble onPointerEnter to ancestor views', () => { + const root = Fantom.createRoot(); + + const childRef = React.createRef>(); + + const parentSpy = jest.fn((_e: PointerEvent) => {}); + const childSpy = jest.fn((_e: PointerEvent) => {}); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + Fantom.dispatchNativeEvent( + childRef, + 'onPointerEnter', + {x: 0, y: 0}, + { + category: Fantom.NativeEventCategory.ContinuousStart, + }, + ); + + expect(childSpy).toHaveBeenCalledTimes(1); + expect(parentSpy).toHaveBeenCalledTimes(0); + }); + + it('does not bubble onPointerLeave to ancestor views', () => { + const root = Fantom.createRoot(); + + const childRef = React.createRef>(); + + const parentSpy = jest.fn((_e: PointerEvent) => {}); + const childSpy = jest.fn((_e: PointerEvent) => {}); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + Fantom.dispatchNativeEvent( + childRef, + 'onPointerLeave', + {x: 0, y: 0}, + { + category: Fantom.NativeEventCategory.ContinuousEnd, + }, + ); + + expect(childSpy).toHaveBeenCalledTimes(1); + expect(parentSpy).toHaveBeenCalledTimes(0); + }); + + it('still fires onPointerEnterCapture on ancestors during the capture phase', () => { + const root = Fantom.createRoot(); + + const childRef = React.createRef>(); + + const callOrder: Array = []; + const parentCaptureSpy = jest.fn((_e: PointerEvent) => { + callOrder.push('parentCapture'); + }); + const childSpy = jest.fn((_e: PointerEvent) => { + callOrder.push('child'); + }); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + Fantom.dispatchNativeEvent( + childRef, + 'onPointerEnter', + {x: 0, y: 0}, + { + category: Fantom.NativeEventCategory.ContinuousStart, + }, + ); + + expect(parentCaptureSpy).toHaveBeenCalledTimes(1); + expect(childSpy).toHaveBeenCalledTimes(1); + expect(callOrder).toEqual(['parentCapture', 'child']); + }); + + it('still bubbles non-skipBubbling events (onPointerDown) to ancestor views', () => { + const root = Fantom.createRoot(); + + const childRef = React.createRef>(); + + const parentSpy = jest.fn((_e: PointerEvent) => {}); + const childSpy = jest.fn((_e: PointerEvent) => {}); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + Fantom.dispatchNativeEvent( + childRef, + 'onPointerDown', + {x: 0, y: 0}, + { + category: Fantom.NativeEventCategory.Discrete, + }, + ); + + expect(childSpy).toHaveBeenCalledTimes(1); + expect(parentSpy).toHaveBeenCalledTimes(1); + }); + }); }, ); diff --git a/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js b/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js index 41bef5591d3e..7c2f16c96852 100644 --- a/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js +++ b/packages/react-native/src/private/renderer/events/dispatchNativeEvent.js @@ -43,10 +43,18 @@ export default function dispatchNativeEvent( // Normal EventTarget dispatch const bubbleConfig = customBubblingEventTypes[type]; const directConfig = customDirectEventTypes[type]; - const bubbles = bubbleConfig != null; // Skip events that are not registered in the view config - if (bubbles || directConfig != null) { + if (bubbleConfig != null || directConfig != null) { + // Honor `skipBubbling` declared in the view config: when set, the bubble + // phase only fires on the target itself (matching the legacy renderer's + // behavior). The synthesized event reports `bubbles: false`, which causes + // the EventTarget bubble loop to short-circuit after dispatching to the + // target. Capture-phase listeners are unaffected. + const bubbles = + bubbleConfig != null && + bubbleConfig.phasedRegistrationNames.skipBubbling !== true; + const eventType = topLevelTypeToEventType(type); const options: {bubbles: boolean, cancelable: boolean} = { bubbles, From 06b23b885c99ab47a08e46ceebfe5b4f4f965679 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 1 May 2026 05:56:55 -0700 Subject: [PATCH 14/34] Use MutableIntObjectMap for SurfaceMountingManager view registry (#56646) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56646 Replace `ConcurrentHashMap` with `MutableIntObjectMap` + `ReentrantReadWriteLock` in `SurfaceMountingManager`, behind the `useOptimizedViewRegistryOnAndroid` feature flag. `ConcurrentHashMap` boxes every `Int` key to `java.lang.Integer` (16 bytes) and allocates a `Node` object per entry (~32 bytes). `MutableIntObjectMap` from `androidx.collection` uses a Swiss Table layout with primitive `IntArray` keys — ~9 bytes/entry vs ~56-64 bytes, a ~6x reduction in map overhead. For a complex surface with 2000+ views, this saves ~90KB per surface in map overhead alone, plus reduced GC pressure from eliminating `Integer` boxing. The `ReentrantReadWriteLock` replaces CHM's built-in concurrency. This matches the access pattern: reads from any thread (`getEventEmitter`, `enqueuePendingEvent`), writes almost exclusively from the UI thread. Readers never block each other. `stopSurface` snapshots entries under the write lock and processes `onViewStateDeleted` outside the lock to minimize reader blocking. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D102797904 fbshipit-source-id: c951fd31c9582f2196c3e09b0e41b8b29e889d85 --- .../fabric/mounting/SurfaceMountingManager.kt | 131 ++++++++++++++---- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 36 +++-- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 21 files changed, 249 insertions(+), 55 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt index b535c10ce5aa..9e53dc8c365b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt @@ -15,6 +15,7 @@ import android.view.ViewGroup import android.view.ViewParent import androidx.annotation.AnyThread import androidx.annotation.UiThread +import androidx.collection.MutableIntObjectMap import androidx.collection.SparseArrayCompat import androidx.core.graphics.drawable.toDrawable import com.facebook.common.logging.FLog @@ -56,7 +57,10 @@ import java.util.ArrayDeque import java.util.LinkedList import java.util.Queue import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.Volatile +import kotlin.concurrent.read +import kotlin.concurrent.write /** Returns true if the collection contains [key]. */ private operator fun SparseArrayCompat.contains(key: Int): Boolean = containsKey(key) @@ -84,7 +88,20 @@ internal constructor( public var context: ThemedReactContext? = reactContext private set - private val tagToViewState: ConcurrentHashMap = ConcurrentHashMap() // any thread + private val tagToViewState: ConcurrentHashMap? + private val optimizedTagToViewState: MutableIntObjectMap? + private val registryLock = ReentrantReadWriteLock() + + init { + if (ReactNativeFeatureFlags.useOptimizedViewRegistryOnAndroid()) { + tagToViewState = null + optimizedTagToViewState = MutableIntObjectMap() + } else { + tagToViewState = ConcurrentHashMap() + optimizedTagToViewState = null + } + } + private val onViewAttachMountItems: Queue = ArrayDeque() // These are all non-null, until StopSurface is called @@ -126,7 +143,7 @@ internal constructor( return } - tagToViewState[surfaceId] = ViewState(surfaceId, rootView, rootViewManager, true) + registryPut(surfaceId, ViewState(surfaceId, rootView, rootViewManager, true)) val runnable: Runnable = object : GuardedRunnable(checkNotNull(context)) { @@ -190,7 +207,7 @@ internal constructor( if (tagSetForStoppedSurface?.containsKey(tag) == true) { return true } - return tagToViewState.containsKey(tag) + return registryContains(tag) } @UiThread @@ -236,7 +253,7 @@ internal constructor( // Reset all StateWrapper objects // Since this can happen on any thread, is it possible to race between StateWrapper destruction // and some accesses from View classes in the UI thread? - for (viewState in tagToViewState.values) { + registryForEachValue { viewState -> viewState.stateWrapper?.destroyState() viewState.stateWrapper = null @@ -248,15 +265,30 @@ internal constructor( if (ReactNativeFeatureFlags.enableViewRecycling()) { viewManagerRegistry?.onSurfaceStopped(surfaceId) } - val tagSetForStoppedSurface = - SparseArrayCompat().also { this.tagSetForStoppedSurface = it } - for ((key, value) in tagToViewState) { - // Using this as a placeholder value in the map. We're using SparseArrayCompat - // since it can efficiently represent the list of pending tags - tagSetForStoppedSurface[key] = this - - // We must call `onDropViewInstance` on all remaining Views - onViewStateDeleted(value) + + if (optimizedTagToViewState != null) { + val viewStatesToDelete: ArrayList + registryLock.write { + val tagSetForStoppedSurface = + SparseArrayCompat().also { this.tagSetForStoppedSurface = it } + viewStatesToDelete = ArrayList(optimizedTagToViewState.size) + optimizedTagToViewState.forEach { key, value -> + tagSetForStoppedSurface[key] = this@SurfaceMountingManager + viewStatesToDelete.add(value) + } + optimizedTagToViewState.clear() + } + for (viewState in viewStatesToDelete) { + onViewStateDeleted(viewState) + } + } else { + val tagSetForStoppedSurface = + SparseArrayCompat().also { this.tagSetForStoppedSurface = it } + for ((key, value) in tagToViewState!!) { + tagSetForStoppedSurface[key] = this + onViewStateDeleted(value) + } + tagToViewState!!.clear() } // Evict all views from cache and memory @@ -264,7 +296,6 @@ internal constructor( rootViewManager = null mountItemExecutor = null context = null - tagToViewState.clear() onViewAttachMountItems.clear() tagToSynchronousMountProps.clear() FLog.e(TAG, "Surface [$surfaceId] was stopped on SurfaceMountingManager.") @@ -572,7 +603,7 @@ internal constructor( this.stateWrapper = stateWrapper this.eventEmitter = eventEmitterWrapper } - tagToViewState[reactTag] = viewState + registryPut(reactTag, viewState) if (isLayoutable) { @Suppress("UNCHECKED_CAST") @@ -921,13 +952,19 @@ internal constructor( return } - var viewState = tagToViewState[reactTag] - if (viewState == null) { - // TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes - // only. - viewState = ViewState(reactTag) - tagToViewState[reactTag] = viewState - } + // TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes + // only. + val viewState: ViewState = + if (optimizedTagToViewState != null) { + registryLock.write { optimizedTagToViewState.getOrPut(reactTag) { ViewState(reactTag) } } + } else { + var vs = tagToViewState!![reactTag] + if (vs == null) { + vs = ViewState(reactTag) + tagToViewState!![reactTag] = vs + } + vs + } val previousEventEmitterWrapper = viewState.eventEmitter viewState.eventEmitter = eventEmitter @@ -1039,7 +1076,7 @@ internal constructor( // To delete we simply remove the tag from the registry. // We want to rely on the correct set of MountInstructions being sent to the platform, // or StopSurface being called, so we do not handle deleting descendants of the View. - tagToViewState.remove(reactTag) + registryRemove(reactTag) onViewStateDeleted(viewState) } @@ -1084,12 +1121,52 @@ internal constructor( } private fun getViewState(reactTag: Int): ViewState = - tagToViewState[reactTag] + registryGet(reactTag) ?: throw RetryableMountingLayerException( "Unable to find viewState for tag $reactTag. Surface stopped: $isStopped" ) - private fun getNullableViewState(reactTag: Int): ViewState? = tagToViewState[reactTag] + private fun getNullableViewState(reactTag: Int): ViewState? = registryGet(reactTag) + + private fun registryGet(tag: Int): ViewState? { + return if (optimizedTagToViewState != null) { + registryLock.read { optimizedTagToViewState[tag] } + } else { + tagToViewState!![tag] + } + } + + private fun registryPut(tag: Int, state: ViewState) { + if (optimizedTagToViewState != null) { + registryLock.write { optimizedTagToViewState[tag] = state } + } else { + tagToViewState!![tag] = state + } + } + + private fun registryRemove(tag: Int) { + if (optimizedTagToViewState != null) { + registryLock.write { optimizedTagToViewState.remove(tag) } + } else { + tagToViewState!!.remove(tag) + } + } + + private fun registryContains(tag: Int): Boolean { + return if (optimizedTagToViewState != null) { + registryLock.read { optimizedTagToViewState.containsKey(tag) } + } else { + tagToViewState!!.containsKey(tag) + } + } + + private inline fun registryForEachValue(action: (ViewState) -> Unit) { + if (optimizedTagToViewState != null) { + registryLock.read { optimizedTagToViewState.forEachValue(action) } + } else { + tagToViewState!!.values.forEach(action) + } + } /** Applies a bitmap as the background of the view with the given tag, if it exists. */ @UiThread @@ -1100,7 +1177,7 @@ internal constructor( public fun printSurfaceState(): Unit { FLog.e(TAG, "Views created for surface $surfaceId:") - for (viewState in tagToViewState.values) { + registryForEachValue { viewState -> val viewManagerName = viewState.viewManager?.name val view = viewState.view val parent = if (view != null) view.parent as View? else null @@ -1142,7 +1219,7 @@ internal constructor( ): Unit { // When the surface stopped we will reset the view state map. We are not going to enqueue // pending events as they are not expected to be dispatched anyways. - val viewState = tagToViewState[reactTag] + val viewState = registryGet(reactTag) if (viewState == null) { // Cannot queue event without view state. Do nothing here. 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 66c05902b4ee..b4f98727365a 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<<6ad566ffaa8330c696fa2088ff696a2b>> */ /** @@ -534,6 +534,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun useNestedScrollViewAndroid(): Boolean = accessor.useNestedScrollViewAndroid() + /** + * Use MutableIntObjectMap with ReadWriteLock instead of ConcurrentHashMap for the view registry in SurfaceMountingManager to reduce memory overhead and GC pressure. + */ + @JvmStatic + public fun useOptimizedViewRegistryOnAndroid(): Boolean = accessor.useOptimizedViewRegistryOnAndroid() + /** * Use shared animation backend in C++ Animated */ 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 f5c895d05ab6..38ee2e2c268d 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<<4f9f5c1c46217ed6802abd5f786aac19>> + * @generated SignedSource<> */ /** @@ -104,6 +104,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var useLISAlgorithmInDifferentiatorCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNestedScrollViewAndroidCache: Boolean? = null + private var useOptimizedViewRegistryOnAndroidCache: Boolean? = null private var useSharedAnimatedBackendCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null @@ -869,6 +870,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun useOptimizedViewRegistryOnAndroid(): Boolean { + var cached = useOptimizedViewRegistryOnAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useOptimizedViewRegistryOnAndroid() + useOptimizedViewRegistryOnAndroidCache = cached + } + return cached + } + override fun useSharedAnimatedBackend(): Boolean { var cached = useSharedAnimatedBackendCache if (cached == null) { 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 028421437717..cd96914eb900 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<<8916e9f4a938a69ff175c806db9835d4>> + * @generated SignedSource<<817790c7ceb9112376b4ab4ee338ff43>> */ /** @@ -196,6 +196,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun useNestedScrollViewAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun useOptimizedViewRegistryOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun useSharedAnimatedBackend(): Boolean @DoNotStrip @JvmStatic public external fun useTraitHiddenOnAndroid(): Boolean 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 7136c0a4758a..14f7a230e3b6 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<<1d764e41252e2709e574da5e0ac64bd7>> */ /** @@ -191,6 +191,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useNestedScrollViewAndroid(): Boolean = false + override fun useOptimizedViewRegistryOnAndroid(): Boolean = false + override fun useSharedAnimatedBackend(): Boolean = false override fun useTraitHiddenOnAndroid(): 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 698723ec7ef8..65e40ecba524 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<<299507458fe84339ddf816dd58671fd3>> */ /** @@ -108,6 +108,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var useLISAlgorithmInDifferentiatorCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNestedScrollViewAndroidCache: Boolean? = null + private var useOptimizedViewRegistryOnAndroidCache: Boolean? = null private var useSharedAnimatedBackendCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null @@ -957,6 +958,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun useOptimizedViewRegistryOnAndroid(): Boolean { + var cached = useOptimizedViewRegistryOnAndroidCache + if (cached == null) { + cached = currentProvider.useOptimizedViewRegistryOnAndroid() + accessedFeatureFlags.add("useOptimizedViewRegistryOnAndroid") + useOptimizedViewRegistryOnAndroidCache = cached + } + return cached + } + override fun useSharedAnimatedBackend(): Boolean { var cached = useSharedAnimatedBackendCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 3bcc2f82ed22..add0e3f972f6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.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<<536a5156deea17740dd24782bf79feb4>> + * @generated SignedSource<<8f887fb839df553b23886a61537e6991>> */ /** @@ -191,6 +191,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun useNestedScrollViewAndroid(): Boolean + @DoNotStrip public fun useOptimizedViewRegistryOnAndroid(): Boolean + @DoNotStrip public fun useSharedAnimatedBackend(): Boolean @DoNotStrip public fun useTraitHiddenOnAndroid(): Boolean 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 67a3144ccd0f..227cbe59e077 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<> + * @generated SignedSource<<5cd1b7223dd852a97280f6dd9bd3e559>> */ /** @@ -543,6 +543,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool useOptimizedViewRegistryOnAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useOptimizedViewRegistryOnAndroid"); + return method(javaProvider_); + } + bool useSharedAnimatedBackend() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useSharedAnimatedBackend"); @@ -1015,6 +1021,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useNestedScrollViewAndroid( return ReactNativeFeatureFlags::useNestedScrollViewAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::useOptimizedViewRegistryOnAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useOptimizedViewRegistryOnAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::useSharedAnimatedBackend( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useSharedAnimatedBackend(); @@ -1338,6 +1349,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "useNestedScrollViewAndroid", JReactNativeFeatureFlagsCxxInterop::useNestedScrollViewAndroid), + makeNativeMethod( + "useOptimizedViewRegistryOnAndroid", + JReactNativeFeatureFlagsCxxInterop::useOptimizedViewRegistryOnAndroid), makeNativeMethod( "useSharedAnimatedBackend", JReactNativeFeatureFlagsCxxInterop::useSharedAnimatedBackend), 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 aff905ed7d73..6f8d7e2c9cf5 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<<4be32bad403baeca1a28f19ad181c42c>> + * @generated SignedSource<> */ /** @@ -282,6 +282,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool useNestedScrollViewAndroid( facebook::jni::alias_ref); + static bool useOptimizedViewRegistryOnAndroid( + facebook::jni::alias_ref); + static bool useSharedAnimatedBackend( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index c2a07d570235..073f90cdde1f 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<> + * @generated SignedSource<<13f834a8abe75fd71b75aa7d94a40303>> */ /** @@ -362,6 +362,10 @@ bool ReactNativeFeatureFlags::useNestedScrollViewAndroid() { return getAccessor().useNestedScrollViewAndroid(); } +bool ReactNativeFeatureFlags::useOptimizedViewRegistryOnAndroid() { + return getAccessor().useOptimizedViewRegistryOnAndroid(); +} + bool ReactNativeFeatureFlags::useSharedAnimatedBackend() { return getAccessor().useSharedAnimatedBackend(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 423e77658798..55d643ee4060 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<<8e9b09843bf4a0312b254559a975f612>> + * @generated SignedSource<> */ /** @@ -459,6 +459,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool useNestedScrollViewAndroid(); + /** + * Use MutableIntObjectMap with ReadWriteLock instead of ConcurrentHashMap for the view registry in SurfaceMountingManager to reduce memory overhead and GC pressure. + */ + RN_EXPORT static bool useOptimizedViewRegistryOnAndroid(); + /** * Use shared animation backend in C++ Animated */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 948a6241e535..2b2d2a22d7fa 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<> + * @generated SignedSource<<631de9950d2702721e0f926967e77a3e>> */ /** @@ -1541,6 +1541,24 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::useOptimizedViewRegistryOnAndroid() { + auto flagValue = useOptimizedViewRegistryOnAndroid_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(84, "useOptimizedViewRegistryOnAndroid"); + + flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); + useOptimizedViewRegistryOnAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { auto flagValue = useSharedAnimatedBackend_.load(); @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useSharedAnimatedBackend"); + markFlagAsAccessed(85, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(86, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1586,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTurboModuleInterop"); + markFlagAsAccessed(87, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1604,7 +1622,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "useTurboModules"); + markFlagAsAccessed(88, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1622,7 +1640,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(89, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1640,7 +1658,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewCullingOutsetRatio"); + markFlagAsAccessed(90, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1658,7 +1676,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "viewTransitionEnabled"); + markFlagAsAccessed(91, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1676,7 +1694,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(91, "virtualViewPrerenderRatio"); + markFlagAsAccessed(92, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index cf2ca1f872e8..8e25db0821b2 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<<9334675799ea378311b8c675ed419b1d>> + * @generated SignedSource<> */ /** @@ -116,6 +116,7 @@ class ReactNativeFeatureFlagsAccessor { bool useLISAlgorithmInDifferentiator(); bool useNativeViewConfigsInBridgelessMode(); bool useNestedScrollViewAndroid(); + bool useOptimizedViewRegistryOnAndroid(); bool useSharedAnimatedBackend(); bool useTraitHiddenOnAndroid(); bool useTurboModuleInterop(); @@ -135,7 +136,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 92> accessedFeatureFlags_; + std::array, 93> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -221,6 +222,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> useLISAlgorithmInDifferentiator_; std::atomic> useNativeViewConfigsInBridgelessMode_; std::atomic> useNestedScrollViewAndroid_; + std::atomic> useOptimizedViewRegistryOnAndroid_; std::atomic> useSharedAnimatedBackend_; std::atomic> useTraitHiddenOnAndroid_; std::atomic> useTurboModuleInterop_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 10df0490068d..8788decc3dfd 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<> + * @generated SignedSource<<278c24a1e5dc5fc24f9f9349fe6d09b4>> */ /** @@ -363,6 +363,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool useOptimizedViewRegistryOnAndroid() override { + return false; + } + bool useSharedAnimatedBackend() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 1f9a7c1307f6..a48b2c54ddda 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.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<<7853bedb8a11b20a633eb6579257b4bc>> + * @generated SignedSource<<30df75f444340e662066500c20e3614f>> */ /** @@ -801,6 +801,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::useNestedScrollViewAndroid(); } + bool useOptimizedViewRegistryOnAndroid() override { + auto value = values_["useOptimizedViewRegistryOnAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::useOptimizedViewRegistryOnAndroid(); + } + bool useSharedAnimatedBackend() override { auto value = values_["useSharedAnimatedBackend"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 2acdef336041..d463e98800c1 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.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<<2262c1ed8ff7983caf65dc2975d52642>> */ /** @@ -109,6 +109,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool useLISAlgorithmInDifferentiator() = 0; virtual bool useNativeViewConfigsInBridgelessMode() = 0; virtual bool useNestedScrollViewAndroid() = 0; + virtual bool useOptimizedViewRegistryOnAndroid() = 0; virtual bool useSharedAnimatedBackend() = 0; virtual bool useTraitHiddenOnAndroid() = 0; virtual bool useTurboModuleInterop() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 816c333b1415..912c18bad649 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<> + * @generated SignedSource<<0d2290e74b0e522d15403896a40955cc>> */ /** @@ -464,6 +464,11 @@ bool NativeReactNativeFeatureFlags::useNestedScrollViewAndroid( return ReactNativeFeatureFlags::useNestedScrollViewAndroid(); } +bool NativeReactNativeFeatureFlags::useOptimizedViewRegistryOnAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useOptimizedViewRegistryOnAndroid(); +} + bool NativeReactNativeFeatureFlags::useSharedAnimatedBackend( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useSharedAnimatedBackend(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 9097a2a90c49..6efae6f2f3ec 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<<3f44b628a681f0d005827f51f4ad885e>> + * @generated SignedSource<<0afa249e55277f5029eec0a740ad00b2>> */ /** @@ -204,6 +204,8 @@ class NativeReactNativeFeatureFlags bool useNestedScrollViewAndroid(jsi::Runtime& runtime); + bool useOptimizedViewRegistryOnAndroid(jsi::Runtime& runtime); + bool useSharedAnimatedBackend(jsi::Runtime& runtime); bool useTraitHiddenOnAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 6008e3209f1b..15414fa5b818 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -945,6 +945,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + useOptimizedViewRegistryOnAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2026-04-28', + description: + 'Use MutableIntObjectMap with ReadWriteLock instead of ConcurrentHashMap for the view registry in SurfaceMountingManager to reduce memory overhead and GC pressure.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, useSharedAnimatedBackend: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 8f5f75e62a14..5aaa3fab8af3 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<<1dd51a152bb30c0e2073a14566c8368d>> + * @generated SignedSource<<01348c36a6888d3ca4863a37ca70d150>> * @flow strict * @noformat */ @@ -131,6 +131,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ useLISAlgorithmInDifferentiator: Getter, useNativeViewConfigsInBridgelessMode: Getter, useNestedScrollViewAndroid: Getter, + useOptimizedViewRegistryOnAndroid: Getter, useSharedAnimatedBackend: Getter, useTraitHiddenOnAndroid: Getter, useTurboModuleInterop: Getter, @@ -541,6 +542,10 @@ export const useNativeViewConfigsInBridgelessMode: Getter = createNativ * When enabled, ReactScrollView will extend NestedScrollView instead of ScrollView on Android for improved nested scrolling support. */ export const useNestedScrollViewAndroid: Getter = createNativeFlagGetter('useNestedScrollViewAndroid', false); +/** + * Use MutableIntObjectMap with ReadWriteLock instead of ConcurrentHashMap for the view registry in SurfaceMountingManager to reduce memory overhead and GC pressure. + */ +export const useOptimizedViewRegistryOnAndroid: Getter = createNativeFlagGetter('useOptimizedViewRegistryOnAndroid', false); /** * Use shared animation backend in C++ Animated */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index c9116e01e890..f53713c5553b 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.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<<5ab72b9af228bc7e591bc0addaf6150e>> + * @generated SignedSource<<68f7919d416dce9a9b6bc0959bfc259b>> * @flow strict * @noformat */ @@ -109,6 +109,7 @@ export interface Spec extends TurboModule { +useLISAlgorithmInDifferentiator?: () => boolean; +useNativeViewConfigsInBridgelessMode?: () => boolean; +useNestedScrollViewAndroid?: () => boolean; + +useOptimizedViewRegistryOnAndroid?: () => boolean; +useSharedAnimatedBackend?: () => boolean; +useTraitHiddenOnAndroid?: () => boolean; +useTurboModuleInterop?: () => boolean; From 86dbb3b9859d71f5be1ea810bf616bb75833168a Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 1 May 2026 06:13:34 -0700 Subject: [PATCH 15/34] Fix TSAN data races in InspectorFlags and InspectorPackagerConnection (#56534) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56534 Fix two ThreadSanitizer data races caught by the messenger_demo integration test under dev-tsan mode. **InspectorFlags**: `loadFlagsAndAssertUnchanged()` reads and writes `cachedValues_` without synchronization. The main thread calls it via `getIsProfilingBuild()` while the JS thread calls it via `getNetworkInspectionEnabled()`, racing on the shared `optional`. Add a `std::mutex` to protect `cachedValues_` access. The lock is taken after computing `newValues` from feature flags, so flag reads stay outside the critical section. `dangerouslyResetFlags()` also acquires the lock to avoid racing with concurrent flag reads. **Inspector**: `Inspector::connectDebugger()` is called from the main thread but creates and uses `packagerConnection_` which is also accessed on the inspector thread. Dispatch the body of `connectDebugger()` to the task dispatch thread via `invokeElsePost()`, matching the pattern other Inspector methods already use. This ensures `InspectorPackagerConnection::connect()` is called on the correct thread as its API requires. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D101809616 fbshipit-source-id: 92eac22ca9ab364daafc403a0fdbb39394bd0f63 --- .../jsinspector-modern/InspectorFlags.cpp | 8 +++-- .../jsinspector-modern/InspectorFlags.h | 7 +++-- .../react/devsupport/inspector/Inspector.cpp | 30 +++++++++++-------- .../api-snapshots/ReactAndroidDebugCxx.api | 2 ++ .../api-snapshots/ReactAndroidReleaseCxx.api | 2 ++ .../api-snapshots/ReactAppleDebugCxx.api | 2 ++ .../api-snapshots/ReactAppleReleaseCxx.api | 2 ++ .../api-snapshots/ReactCommonDebugCxx.api | 2 ++ .../api-snapshots/ReactCommonReleaseCxx.api | 2 ++ 9 files changed, 41 insertions(+), 16 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp index c2c31ee13d28..6c64e2f1393c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp @@ -50,7 +50,10 @@ bool InspectorFlags::getPerfIssuesEnabled() const { } void InspectorFlags::dangerouslyResetFlags() { - *this = InspectorFlags{}; + std::lock_guard lock(mutex_); + cachedValues_.reset(); + inconsistentFlagsStateLogged_ = false; + fuseboxDisabledForTest_ = false; } void InspectorFlags::dangerouslyDisableFuseboxForTest() { @@ -84,6 +87,8 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged() .perfIssuesEnabled = ReactNativeFeatureFlags::perfIssuesEnabled(), }; + // Protect against concurrent calls + std::lock_guard lock(mutex_); if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) { if (cachedValues_ != newValues) { LOG(ERROR) @@ -93,7 +98,6 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged() inconsistentFlagsStateLogged_ = true; } } - cachedValues_ = newValues; return cachedValues_.value(); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h index f1f1353e5e95..60b452687e1a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h @@ -7,6 +7,7 @@ #pragma once +#include #include namespace facebook::react::jsinspector_modern { @@ -19,6 +20,9 @@ class InspectorFlags { public: static InspectorFlags &getInstance(); + InspectorFlags(const InspectorFlags &) = delete; + InspectorFlags &operator=(const InspectorFlags &) = delete; + /** * Flag determining if the inspector backend should strictly assert that only * a single host is registered. @@ -81,10 +85,9 @@ class InspectorFlags { }; InspectorFlags() = default; - InspectorFlags(const InspectorFlags &) = delete; - InspectorFlags &operator=(const InspectorFlags &) = default; ~InspectorFlags() = default; + mutable std::mutex mutex_; mutable std::optional cachedValues_; mutable bool inconsistentFlagsStateLogged_{false}; bool fuseboxDisabledForTest_{false}; diff --git a/packages/react-native/ReactCxxPlatform/react/devsupport/inspector/Inspector.cpp b/packages/react-native/ReactCxxPlatform/react/devsupport/inspector/Inspector.cpp index 5d909c97c667..0ba040caee6c 100644 --- a/packages/react-native/ReactCxxPlatform/react/devsupport/inspector/Inspector.cpp +++ b/packages/react-native/ReactCxxPlatform/react/devsupport/inspector/Inspector.cpp @@ -148,18 +148,24 @@ Inspector::~Inspector() noexcept { } void Inspector::connectDebugger(const std::string& inspectorUrl) noexcept { - if (!packagerConnection_) { - packagerConnection_ = - std::make_unique( - inspectorUrl, - deviceName_, - appName_, - std::make_unique( - weak_from_this(), webSocketClientFactory_)); - } - if (!packagerConnection_->isConnected()) { - packagerConnection_->connect(); - } + invokeElsePost([weakThis = weak_from_this(), inspectorUrl]() { + auto self = weakThis.lock(); + if (!self) { + return; + } + if (!self->packagerConnection_) { + self->packagerConnection_ = + std::make_unique( + inspectorUrl, + self->deviceName_, + self->appName_, + std::make_unique( + weakThis, self->webSocketClientFactory_)); + } + if (!self->packagerConnection_->isConnected()) { + self->packagerConnection_->connect(); + } + }); } void Inspector::ensureHostTarget( diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 73df6b24cb86..8895f9a22552 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -10528,6 +10528,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -10535,6 +10536,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 3c0caca6c77f..e82d7dfbbeea 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -10384,6 +10384,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -10391,6 +10392,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index d01942d8b290..62d88ff75464 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -12809,6 +12809,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -12816,6 +12817,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 10912429ee41..b8feb8d7d212 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -12675,6 +12675,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -12682,6 +12683,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 941f370fc436..5f8fc0c410be 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -7636,6 +7636,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -7643,6 +7644,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 9f5e20f0c0f1..727e60d3be74 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -7627,6 +7627,7 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate { } class facebook::react::jsinspector_modern::InspectorFlags { + public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public bool getAssertSingleHostState() const; public bool getFrameRecordingEnabled() const; public bool getFuseboxEnabled() const; @@ -7634,6 +7635,7 @@ class facebook::react::jsinspector_modern::InspectorFlags { public bool getNetworkInspectionEnabled() const; public bool getPerfIssuesEnabled() const; public bool getScreenshotCaptureEnabled() const; + public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete; public static facebook::react::jsinspector_modern::InspectorFlags& getInstance(); public void dangerouslyDisableFuseboxForTest(); public void dangerouslyResetFlags(); From 52d87f92be59b7ba1e259a6922436ca53e67deb9 Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Fri, 1 May 2026 19:49:50 -0700 Subject: [PATCH 16/34] Consolidate image prefetch flags into enableImagePrefetchingAndroid (#56610) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56610 Removes deprecated feature flags `enableImagePrefetchingJNIBatchingAndroid` and `enableImagePrefetchingOnUiThreadAndroid` by consolidating their behavior into `enableImagePrefetchingAndroid`. The changes make image prefetch batching and UI thread dispatch the default behavior when `enableImagePrefetchingAndroid` is enabled. This simplifies the feature flag system and removes intermediate experimentation flags that are no longer needed. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D102428102 fbshipit-source-id: af7387e5a3144d7c425fc600a04b33e830d6e2e7 --- .../react/fabric/FabricUIManager.java | 11 +- .../featureflags/ReactNativeFeatureFlags.kt | 14 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 22 +-- .../ReactNativeFeatureFlagsCxxInterop.kt | 6 +- .../ReactNativeFeatureFlagsDefaults.kt | 6 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 24 +-- .../ReactNativeFeatureFlagsProvider.kt | 6 +- .../react/fabric/FabricUIManagerBinding.cpp | 2 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 30 +--- .../JReactNativeFeatureFlagsCxxInterop.h | 8 +- .../featureflags/ReactNativeFeatureFlags.cpp | 10 +- .../featureflags/ReactNativeFeatureFlags.h | 12 +- .../ReactNativeFeatureFlagsAccessor.cpp | 162 +++++++----------- .../ReactNativeFeatureFlagsAccessor.h | 8 +- .../ReactNativeFeatureFlagsDefaults.h | 10 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 20 +-- .../ReactNativeFeatureFlagsProvider.h | 4 +- .../NativeReactNativeFeatureFlags.cpp | 12 +- .../NativeReactNativeFeatureFlags.h | 6 +- .../renderer/imagemanager/ImageFetcher.cpp | 5 - .../renderer/imagemanager/ImageManager.cpp | 4 +- .../ReactNativeFeatureFlags.config.js | 22 --- .../featureflags/ReactNativeFeatureFlags.js | 12 +- .../specs/NativeReactNativeFeatureFlags.js | 4 +- 24 files changed, 87 insertions(+), 333 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index f6658992ec41..9c29ee6213de 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -1011,15 +1011,8 @@ private void scheduleReactRevisionMerge(int surfaceId) { @UnstableReactNativeAPI public void experimental_prefetchResources( int surfaceId, String componentName, ReadableMapBuffer params) { - if (ReactNativeFeatureFlags.enableImagePrefetchingOnUiThreadAndroid()) { - mMountItemDispatcher.addMountItem( - new PrefetchResourcesMountItem(surfaceId, componentName, params)); - } else { - SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId); - if (surfaceMountingManager != null) { - surfaceMountingManager.experimental_prefetchResources(surfaceId, componentName, params); - } - } + mMountItemDispatcher.addMountItem( + new PrefetchResourcesMountItem(surfaceId, componentName, params)); } void setBinding(FabricUIManagerBinding binding) { 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 b4f98727365a..5214e31adf8c 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<<6ad566ffaa8330c696fa2088ff696a2b>> + * @generated SignedSource<> */ /** @@ -204,18 +204,6 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableImagePrefetchingAndroid(): Boolean = accessor.enableImagePrefetchingAndroid() - /** - * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call - */ - @JvmStatic - public fun enableImagePrefetchingJNIBatchingAndroid(): Boolean = accessor.enableImagePrefetchingJNIBatchingAndroid() - - /** - * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread - */ - @JvmStatic - public fun enableImagePrefetchingOnUiThreadAndroid(): Boolean = accessor.enableImagePrefetchingOnUiThreadAndroid() - /** * Dispatches state updates for content offset changes synchronously on the main thread. */ 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 38ee2e2c268d..2509813ce330 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<> + * @generated SignedSource<> */ /** @@ -49,8 +49,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null - private var enableImagePrefetchingJNIBatchingAndroidCache: Boolean? = null - private var enableImagePrefetchingOnUiThreadAndroidCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null @@ -375,24 +373,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } - override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean { - var cached = enableImagePrefetchingJNIBatchingAndroidCache - if (cached == null) { - cached = ReactNativeFeatureFlagsCxxInterop.enableImagePrefetchingJNIBatchingAndroid() - enableImagePrefetchingJNIBatchingAndroidCache = cached - } - return cached - } - - override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean { - var cached = enableImagePrefetchingOnUiThreadAndroidCache - if (cached == null) { - cached = ReactNativeFeatureFlagsCxxInterop.enableImagePrefetchingOnUiThreadAndroid() - enableImagePrefetchingOnUiThreadAndroidCache = cached - } - return cached - } - override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean { var cached = enableImmediateUpdateModeForContentOffsetChangesCache if (cached == null) { 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 cd96914eb900..02e2fd2f8589 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<<817790c7ceb9112376b4ab4ee338ff43>> + * @generated SignedSource<> */ /** @@ -86,10 +86,6 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableImagePrefetchingAndroid(): Boolean - @DoNotStrip @JvmStatic public external fun enableImagePrefetchingJNIBatchingAndroid(): Boolean - - @DoNotStrip @JvmStatic public external fun enableImagePrefetchingOnUiThreadAndroid(): Boolean - @DoNotStrip @JvmStatic public external fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean @DoNotStrip @JvmStatic public external fun enableImperativeFocus(): Boolean 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 14f7a230e3b6..998366d958af 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<<1d764e41252e2709e574da5e0ac64bd7>> + * @generated SignedSource<<54a4f6d01a052e5ab19b15652d5ab1a5>> */ /** @@ -81,10 +81,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableImagePrefetchingAndroid(): Boolean = false - override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean = false - - override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean = false - override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean = false override fun enableImperativeFocus(): 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 65e40ecba524..398b5df92b2c 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<<299507458fe84339ddf816dd58671fd3>> + * @generated SignedSource<<4d09d820304f454bc3850da89087caec>> */ /** @@ -53,8 +53,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null - private var enableImagePrefetchingJNIBatchingAndroidCache: Boolean? = null - private var enableImagePrefetchingOnUiThreadAndroidCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null @@ -408,26 +406,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } - override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean { - var cached = enableImagePrefetchingJNIBatchingAndroidCache - if (cached == null) { - cached = currentProvider.enableImagePrefetchingJNIBatchingAndroid() - accessedFeatureFlags.add("enableImagePrefetchingJNIBatchingAndroid") - enableImagePrefetchingJNIBatchingAndroidCache = cached - } - return cached - } - - override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean { - var cached = enableImagePrefetchingOnUiThreadAndroidCache - if (cached == null) { - cached = currentProvider.enableImagePrefetchingOnUiThreadAndroid() - accessedFeatureFlags.add("enableImagePrefetchingOnUiThreadAndroid") - enableImagePrefetchingOnUiThreadAndroidCache = cached - } - return cached - } - override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean { var cached = enableImmediateUpdateModeForContentOffsetChangesCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index add0e3f972f6..3113bd3ab653 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.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<<8f887fb839df553b23886a61537e6991>> + * @generated SignedSource<<592acf445c4b4e56651cd6afb57e46fa>> */ /** @@ -81,10 +81,6 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableImagePrefetchingAndroid(): Boolean - @DoNotStrip public fun enableImagePrefetchingJNIBatchingAndroid(): Boolean - - @DoNotStrip public fun enableImagePrefetchingOnUiThreadAndroid(): Boolean - @DoNotStrip public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean @DoNotStrip public fun enableImperativeFocus(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp index 839efa3dce53..dfaac96caefc 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp @@ -659,7 +659,7 @@ void FabricUIManagerBinding::schedulerShouldRenderTransactions( return; } - if (ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) { auto weakImageFetcher = scheduler_->getContextContainer()->find>( ImageFetcherKey); 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 227cbe59e077..7c27db4af32a 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<<5cd1b7223dd852a97280f6dd9bd3e559>> + * @generated SignedSource<<55e4d6d6dc930b4ccf3e20cb77f7a042>> */ /** @@ -213,18 +213,6 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } - bool enableImagePrefetchingJNIBatchingAndroid() override { - static const auto method = - getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImagePrefetchingJNIBatchingAndroid"); - return method(javaProvider_); - } - - bool enableImagePrefetchingOnUiThreadAndroid() override { - static const auto method = - getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImagePrefetchingOnUiThreadAndroid"); - return method(javaProvider_); - } - bool enableImmediateUpdateModeForContentOffsetChanges() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImmediateUpdateModeForContentOffsetChanges"); @@ -746,16 +734,6 @@ bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } -bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingJNIBatchingAndroid( - facebook::jni::alias_ref /*unused*/) { - return ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid(); -} - -bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingOnUiThreadAndroid( - facebook::jni::alias_ref /*unused*/) { - return ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid(); -} - bool JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(); @@ -1184,12 +1162,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableImagePrefetchingAndroid", JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid), - makeNativeMethod( - "enableImagePrefetchingJNIBatchingAndroid", - JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingJNIBatchingAndroid), - makeNativeMethod( - "enableImagePrefetchingOnUiThreadAndroid", - JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingOnUiThreadAndroid), makeNativeMethod( "enableImmediateUpdateModeForContentOffsetChanges", JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges), 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 6f8d7e2c9cf5..e6d4455961ef 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<> */ /** @@ -117,12 +117,6 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableImagePrefetchingAndroid( facebook::jni::alias_ref); - static bool enableImagePrefetchingJNIBatchingAndroid( - facebook::jni::alias_ref); - - static bool enableImagePrefetchingOnUiThreadAndroid( - facebook::jni::alias_ref); - static bool enableImmediateUpdateModeForContentOffsetChanges( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 073f90cdde1f..e0a788e58f1c 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<<13f834a8abe75fd71b75aa7d94a40303>> + * @generated SignedSource<<16024bed6914b4b74d4a50de142e872a>> */ /** @@ -142,14 +142,6 @@ bool ReactNativeFeatureFlags::enableImagePrefetchingAndroid() { return getAccessor().enableImagePrefetchingAndroid(); } -bool ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid() { - return getAccessor().enableImagePrefetchingJNIBatchingAndroid(); -} - -bool ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid() { - return getAccessor().enableImagePrefetchingOnUiThreadAndroid(); -} - bool ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges() { return getAccessor().enableImmediateUpdateModeForContentOffsetChanges(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 55d643ee4060..08516f6d1d1c 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<> + * @generated SignedSource<<33ffe2572bbaeba64d2167dbac4bb0ab>> */ /** @@ -184,16 +184,6 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableImagePrefetchingAndroid(); - /** - * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call - */ - RN_EXPORT static bool enableImagePrefetchingJNIBatchingAndroid(); - - /** - * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread - */ - RN_EXPORT static bool enableImagePrefetchingOnUiThreadAndroid(); - /** * Dispatches state updates for content offset changes synchronously on the main thread. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 2b2d2a22d7fa..ba5f777abe14 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<<631de9950d2702721e0f926967e77a3e>> + * @generated SignedSource<<51dc335c5463b6c3f34c5330d69b6a00>> */ /** @@ -551,42 +551,6 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingJNIBatchingAndroid() { - auto flagValue = enableImagePrefetchingJNIBatchingAndroid_.load(); - - if (!flagValue.has_value()) { - // This block is not exclusive but it is not necessary. - // If multiple threads try to initialize the feature flag, we would only - // be accessing the provider multiple times but the end state of this - // instance and the returned flag value would be the same. - - markFlagAsAccessed(29, "enableImagePrefetchingJNIBatchingAndroid"); - - flagValue = currentProvider_->enableImagePrefetchingJNIBatchingAndroid(); - enableImagePrefetchingJNIBatchingAndroid_ = flagValue; - } - - return flagValue.value(); -} - -bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingOnUiThreadAndroid() { - auto flagValue = enableImagePrefetchingOnUiThreadAndroid_.load(); - - if (!flagValue.has_value()) { - // This block is not exclusive but it is not necessary. - // If multiple threads try to initialize the feature flag, we would only - // be accessing the provider multiple times but the end state of this - // instance and the returned flag value would be the same. - - markFlagAsAccessed(30, "enableImagePrefetchingOnUiThreadAndroid"); - - flagValue = currentProvider_->enableImagePrefetchingOnUiThreadAndroid(); - enableImagePrefetchingOnUiThreadAndroid_ = flagValue; - } - - return flagValue.value(); -} - bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetChanges() { auto flagValue = enableImmediateUpdateModeForContentOffsetChanges_.load(); @@ -596,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(29, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -614,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeFocus() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableImperativeFocus"); + markFlagAsAccessed(30, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -632,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(31, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -650,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableIntersectionObserverByDefault"); + markFlagAsAccessed(32, "enableIntersectionObserverByDefault"); flagValue = currentProvider_->enableIntersectionObserverByDefault(); enableIntersectionObserverByDefault_ = flagValue; @@ -668,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "enableKeyEvents"); + markFlagAsAccessed(33, "enableKeyEvents"); flagValue = currentProvider_->enableKeyEvents(); enableKeyEvents_ = flagValue; @@ -686,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(34, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -704,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(35, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -722,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(36, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -740,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(37, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -758,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMutationObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enableMutationObserverByDefault"); + markFlagAsAccessed(38, "enableMutationObserverByDefault"); flagValue = currentProvider_->enableMutationObserverByDefault(); enableMutationObserverByDefault_ = flagValue; @@ -776,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enableNativeCSSParsing"); + markFlagAsAccessed(39, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -794,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeViewPropTransformations() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enableNativeViewPropTransformations"); + markFlagAsAccessed(40, "enableNativeViewPropTransformations"); flagValue = currentProvider_->enableNativeViewPropTransformations(); enableNativeViewPropTransformations_ = flagValue; @@ -812,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enableNetworkEventReporting"); + markFlagAsAccessed(41, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -830,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enablePreparedTextLayout"); + markFlagAsAccessed(42, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -848,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(43, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -866,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(44, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -884,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewCulling"); + markFlagAsAccessed(45, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -902,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecycling"); + markFlagAsAccessed(46, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -920,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForImage"); + markFlagAsAccessed(47, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -938,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(48, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -956,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableViewRecyclingForText"); + markFlagAsAccessed(49, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -974,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableViewRecyclingForView"); + markFlagAsAccessed(50, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -992,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(51, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -1010,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(52, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -1028,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(53, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -1046,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fixFindShadowNodeByTagRaceCondition() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fixFindShadowNodeByTagRaceCondition"); + markFlagAsAccessed(54, "fixFindShadowNodeByTagRaceCondition"); flagValue = currentProvider_->fixFindShadowNodeByTagRaceCondition(); fixFindShadowNodeByTagRaceCondition_ = flagValue; @@ -1064,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(55, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1082,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(56, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1100,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(57, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1118,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxEnabledRelease"); + markFlagAsAccessed(58, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1136,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(59, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1154,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(60, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1172,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(61, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1190,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(62, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1208,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1226,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "perfIssuesEnabled"); + markFlagAsAccessed(64, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1244,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "perfMonitorV2Enabled"); + markFlagAsAccessed(65, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1262,7 +1226,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "preparedTextCacheSize"); + markFlagAsAccessed(66, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1280,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1298,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "redBoxV2Android"); + markFlagAsAccessed(68, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1316,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "redBoxV2IOS"); + markFlagAsAccessed(69, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1334,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(70, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1352,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(71, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1370,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(72, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1388,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(73, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1406,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(74, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1424,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1442,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1460,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(77, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1478,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useFabricInterop"); + markFlagAsAccessed(78, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1496,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useLISAlgorithmInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useLISAlgorithmInDifferentiator"); + markFlagAsAccessed(79, "useLISAlgorithmInDifferentiator"); flagValue = currentProvider_->useLISAlgorithmInDifferentiator(); useLISAlgorithmInDifferentiator_ = flagValue; @@ -1514,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(80, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1532,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useNestedScrollViewAndroid"); + markFlagAsAccessed(81, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1550,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedViewRegistryOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useOptimizedViewRegistryOnAndroid"); + markFlagAsAccessed(82, "useOptimizedViewRegistryOnAndroid"); flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); useOptimizedViewRegistryOnAndroid_ = flagValue; @@ -1568,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useSharedAnimatedBackend"); + markFlagAsAccessed(83, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1586,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(84, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1604,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "useTurboModuleInterop"); + markFlagAsAccessed(85, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1622,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "useTurboModules"); + markFlagAsAccessed(86, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1640,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(87, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1658,7 +1622,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "viewCullingOutsetRatio"); + markFlagAsAccessed(88, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1676,7 +1640,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(91, "viewTransitionEnabled"); + markFlagAsAccessed(89, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1694,7 +1658,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(92, "virtualViewPrerenderRatio"); + markFlagAsAccessed(90, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 8e25db0821b2..bc9a1b7b1e27 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<> + * @generated SignedSource<<62bf5997f9ee2799bf7fd2cd6d5a6cd1>> */ /** @@ -61,8 +61,6 @@ class ReactNativeFeatureFlagsAccessor { bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); bool enableImagePrefetchingAndroid(); - bool enableImagePrefetchingJNIBatchingAndroid(); - bool enableImagePrefetchingOnUiThreadAndroid(); bool enableImmediateUpdateModeForContentOffsetChanges(); bool enableImperativeFocus(); bool enableInteropViewManagerClassLookUpOptimizationIOS(); @@ -136,7 +134,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 93> accessedFeatureFlags_; + std::array, 91> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -167,8 +165,6 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; std::atomic> enableImagePrefetchingAndroid_; - std::atomic> enableImagePrefetchingJNIBatchingAndroid_; - std::atomic> enableImagePrefetchingOnUiThreadAndroid_; std::atomic> enableImmediateUpdateModeForContentOffsetChanges_; std::atomic> enableImperativeFocus_; std::atomic> enableInteropViewManagerClassLookUpOptimizationIOS_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 8788decc3dfd..ac1e9c126022 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<<278c24a1e5dc5fc24f9f9349fe6d09b4>> + * @generated SignedSource<<0042e6f884ccf466dda224021ff7a86a>> */ /** @@ -143,14 +143,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } - bool enableImagePrefetchingJNIBatchingAndroid() override { - return false; - } - - bool enableImagePrefetchingOnUiThreadAndroid() override { - return false; - } - bool enableImmediateUpdateModeForContentOffsetChanges() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index a48b2c54ddda..4bbb20893888 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.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<<30df75f444340e662066500c20e3614f>> + * @generated SignedSource<> */ /** @@ -306,24 +306,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingAndroid(); } - bool enableImagePrefetchingJNIBatchingAndroid() override { - auto value = values_["enableImagePrefetchingJNIBatchingAndroid"]; - if (!value.isNull()) { - return value.getBool(); - } - - return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingJNIBatchingAndroid(); - } - - bool enableImagePrefetchingOnUiThreadAndroid() override { - auto value = values_["enableImagePrefetchingOnUiThreadAndroid"]; - if (!value.isNull()) { - return value.getBool(); - } - - return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingOnUiThreadAndroid(); - } - bool enableImmediateUpdateModeForContentOffsetChanges() override { auto value = values_["enableImmediateUpdateModeForContentOffsetChanges"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index d463e98800c1..7820c8f313e4 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.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<<2262c1ed8ff7983caf65dc2975d52642>> + * @generated SignedSource<> */ /** @@ -54,8 +54,6 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; virtual bool enableImagePrefetchingAndroid() = 0; - virtual bool enableImagePrefetchingJNIBatchingAndroid() = 0; - virtual bool enableImagePrefetchingOnUiThreadAndroid() = 0; virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0; virtual bool enableImperativeFocus() = 0; virtual bool enableInteropViewManagerClassLookUpOptimizationIOS() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 912c18bad649..b6d699cd6e94 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<<0d2290e74b0e522d15403896a40955cc>> + * @generated SignedSource<> */ /** @@ -189,16 +189,6 @@ bool NativeReactNativeFeatureFlags::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } -bool NativeReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid( - jsi::Runtime& /*runtime*/) { - return ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid(); -} - -bool NativeReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid( - jsi::Runtime& /*runtime*/) { - return ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid(); -} - bool NativeReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 6efae6f2f3ec..b3eb68b5f3a4 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<<0afa249e55277f5029eec0a740ad00b2>> + * @generated SignedSource<> */ /** @@ -94,10 +94,6 @@ class NativeReactNativeFeatureFlags bool enableImagePrefetchingAndroid(jsi::Runtime& runtime); - bool enableImagePrefetchingJNIBatchingAndroid(jsi::Runtime& runtime); - - bool enableImagePrefetchingOnUiThreadAndroid(jsi::Runtime& runtime); - bool enableImmediateUpdateModeForContentOffsetChanges(jsi::Runtime& runtime); bool enableImperativeFocus(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp index e8b55d003a05..9cc2f96b9f24 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp @@ -8,7 +8,6 @@ #include "ImageFetcher.h" #include -#include #include namespace facebook::react { @@ -35,10 +34,6 @@ ImageRequest ImageFetcher::requestImage( auto telemetry = std::make_shared(surfaceId); - if (!ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { - flushImageRequests(); - } - return ImageRequest{imageSource, telemetry}; } diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageManager.cpp b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageManager.cpp index 9f3f848cc6fb..25612c4cc33f 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageManager.cpp @@ -25,7 +25,7 @@ ImageManager::ImageManager( : contextContainer_(contextContainer), self_(new std::shared_ptr( std::make_shared(contextContainer))) { - if (ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) { std::weak_ptr weakImageFetcher = *static_cast*>(self_); contextContainer->insert(ImageFetcherKey, weakImageFetcher); @@ -33,7 +33,7 @@ ImageManager::ImageManager( } ImageManager::~ImageManager() { - if (ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) { contextContainer_->erase(ImageFetcherKey); } delete static_cast*>(self_); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 15414fa5b818..c09b656231a5 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -357,28 +357,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - enableImagePrefetchingJNIBatchingAndroid: { - defaultValue: false, - metadata: { - dateAdded: '2025-11-10', - description: - 'When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, - enableImagePrefetchingOnUiThreadAndroid: { - defaultValue: false, - metadata: { - dateAdded: '2025-09-02', - description: - 'When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, enableImmediateUpdateModeForContentOffsetChanges: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 5aaa3fab8af3..fe9178d5b88f 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<<01348c36a6888d3ca4863a37ca70d150>> + * @generated SignedSource<<404bdc9f1cd9ea3d65d1b31dccd1591e>> * @flow strict * @noformat */ @@ -76,8 +76,6 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, enableImagePrefetchingAndroid: Getter, - enableImagePrefetchingJNIBatchingAndroid: Getter, - enableImagePrefetchingOnUiThreadAndroid: Getter, enableImmediateUpdateModeForContentOffsetChanges: Getter, enableImperativeFocus: Getter, enableInteropViewManagerClassLookUpOptimizationIOS: Getter, @@ -322,14 +320,6 @@ export const enableIOSViewClipToPaddingBox: Getter = createNativeFlagGe * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout */ export const enableImagePrefetchingAndroid: Getter = createNativeFlagGetter('enableImagePrefetchingAndroid', false); -/** - * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call - */ -export const enableImagePrefetchingJNIBatchingAndroid: Getter = createNativeFlagGetter('enableImagePrefetchingJNIBatchingAndroid', false); -/** - * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread - */ -export const enableImagePrefetchingOnUiThreadAndroid: Getter = createNativeFlagGetter('enableImagePrefetchingOnUiThreadAndroid', false); /** * Dispatches state updates for content offset changes synchronously on the main thread. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index f53713c5553b..a49f45388213 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.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<<68f7919d416dce9a9b6bc0959bfc259b>> + * @generated SignedSource<<6e8201091111a8451911f4e72cdc0932>> * @flow strict * @noformat */ @@ -54,8 +54,6 @@ export interface Spec extends TurboModule { +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; +enableImagePrefetchingAndroid?: () => boolean; - +enableImagePrefetchingJNIBatchingAndroid?: () => boolean; - +enableImagePrefetchingOnUiThreadAndroid?: () => boolean; +enableImmediateUpdateModeForContentOffsetChanges?: () => boolean; +enableImperativeFocus?: () => boolean; +enableInteropViewManagerClassLookUpOptimizationIOS?: () => boolean; From a25e0fdddf0ab77c798dd00f8c88e842ee72c955 Mon Sep 17 00:00:00 2001 From: Zeya Peng Date: Mon, 4 May 2026 13:49:08 -0700 Subject: [PATCH 17/34] Add test for synchronous mount props override behavior (#56663) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56663 ## Changelog: [Android] [Added] - Add test for synchronous mount props override behavior ___ overriding_review_checks_triggers_an_audit_and_retroactive_review Oncall Short Name: react_native Reviewed By: christophpurrer Differential Revision: D103237771 fbshipit-source-id: 617b2dc5f0d9527910eb2b1fc4ec41f431b2e2f7 --- ...ountingManagerSynchronousMountPropsTest.kt | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/SurfaceMountingManagerSynchronousMountPropsTest.kt diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/SurfaceMountingManagerSynchronousMountPropsTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/SurfaceMountingManagerSynchronousMountPropsTest.kt new file mode 100644 index 000000000000..2c0787d17c84 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/SurfaceMountingManagerSynchronousMountPropsTest.kt @@ -0,0 +1,178 @@ +/* + * 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. + */ + +@file:Suppress("DEPRECATION") + +package com.facebook.react.fabric + +import com.facebook.react.ReactRootView +import com.facebook.react.bridge.JavaOnlyMap +import com.facebook.react.bridge.ReactTestHelper +import com.facebook.react.fabric.mounting.MountingManager +import com.facebook.react.fabric.mounting.MountingManager.MountItemExecutor +import com.facebook.react.fabric.mounting.SurfaceMountingManager +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewManager +import com.facebook.react.uimanager.ViewManagerRegistry +import com.facebook.react.views.view.ReactViewManager +import com.facebook.testutils.shadows.ShadowNativeLoader +import com.facebook.testutils.shadows.ShadowNativeMap +import com.facebook.testutils.shadows.ShadowReadableNativeArray +import com.facebook.testutils.shadows.ShadowReadableNativeMap +import com.facebook.testutils.shadows.ShadowSoLoader +import com.facebook.testutils.shadows.ShadowWritableNativeArray +import com.facebook.testutils.shadows.ShadowWritableNativeMap +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Tests for the synchronous mount props override behavior in [SurfaceMountingManager], controlled + * by the `overrideBySynchronousMountPropsAtMountingAndroid` feature flag (default: true). + * + * This fixes a race condition where Native Animated applies props (e.g. opacity, transform) + * synchronously on native view, but a regular Fabric mount update with stale props arrives later + * and overwrites the fresh values, causing the view to visibly jump/flicker. + */ +@RunWith(RobolectricTestRunner::class) +@Config( + shadows = + [ + ShadowSoLoader::class, + ShadowNativeLoader::class, + ShadowNativeMap::class, + ShadowWritableNativeMap::class, + ShadowReadableNativeMap::class, + ShadowWritableNativeArray::class, + ShadowReadableNativeArray::class, + ] +) +class SurfaceMountingManagerSynchronousMountPropsTest { + private lateinit var mountingManager: MountingManager + private lateinit var themedReactContext: ThemedReactContext + private val surfaceId = 1 + + @Before + fun setUp() { + ReactNativeFeatureFlagsForTests.setUp() + val reactContext = ReactTestHelper.createCatalystContextForTest() + themedReactContext = ThemedReactContext(reactContext, reactContext, null, -1) + mountingManager = + MountingManager( + ViewManagerRegistry(listOf>(ReactViewManager())), + MountItemExecutor {}, + ) + } + + private fun startSurface(): SurfaceMountingManager { + val rootView = ReactRootView(themedReactContext) + mountingManager.startSurface(surfaceId, themedReactContext, rootView) + return mountingManager.getSurfaceManagerEnforced(surfaceId, "test") + } + + private fun createAndAttachView(smm: SurfaceMountingManager, tag: Int) { + smm.preallocateView("RCTView", tag, JavaOnlyMap.of(), null, true) + smm.addViewAt(surfaceId, tag, 0) + } + + /** Stored synchronous opacity should override a stale Fabric mount update. */ + @Test + fun storeSynchronousProps_overridesStaleOpacityInUpdateProps() { + val smm = startSurface() + val tag = 42 + createAndAttachView(smm, tag) + + // Native Animated sets opacity=0.3 synchronously + smm.storeSynchronousMountPropsOverride(tag, JavaOnlyMap.of("opacity", 0.3)) + + // Stale Fabric mount update arrives with opacity=1.0 + smm.updateProps(tag, JavaOnlyMap.of("opacity", 1.0)) + + // The synchronous value (0.3) should win + assertThat(smm.getView(tag).alpha).isEqualTo(0.3f) + } + + /** Multiple storeSynchronousMountPropsOverride calls should merge — later values win. */ + @Test + fun storeSynchronousProps_mergesMultipleCalls() { + val smm = startSurface() + val tag = 42 + createAndAttachView(smm, tag) + + smm.storeSynchronousMountPropsOverride(tag, JavaOnlyMap.of("opacity", 0.3)) + smm.storeSynchronousMountPropsOverride(tag, JavaOnlyMap.of("opacity", 0.7)) + + smm.updateProps(tag, JavaOnlyMap.of("opacity", 1.0)) + + assertThat(smm.getView(tag).alpha).isEqualTo(0.7f) + } + + /** + * Full race condition scenario: synchronous animated props survive a stale Fabric mount update. + */ + @Test + fun raceCondition_synchronousPropsWinOverStaleMount() { + val smm = startSurface() + val tag = 42 + createAndAttachView(smm, tag) + + // Native Animated applies fresh props synchronously + val freshAnimatedProps = JavaOnlyMap.of("opacity", 0.2) + smm.storeSynchronousMountPropsOverride(tag, freshAnimatedProps) + smm.updatePropsSynchronously(tag, freshAnimatedProps) + assertThat(smm.getView(tag).alpha).isEqualTo(0.2f) + + // Stale Fabric mount update arrives + smm.updateProps(tag, JavaOnlyMap.of("opacity", 1.0)) + + // Synchronous value preserved + assertThat(smm.getView(tag).alpha).isEqualTo(0.2f) + } + + /** + * When a view is deleted, stored synchronous props should be cleaned up. A recreated view with + * the same tag should not be affected by the old stored props. + */ + @Test + fun deleteView_cleansUpStoredSynchronousProps() { + val smm = startSurface() + val tag = 42 + createAndAttachView(smm, tag) + + smm.storeSynchronousMountPropsOverride(tag, JavaOnlyMap.of("opacity", 0.3)) + smm.deleteView(tag) + + // Recreate with same tag + smm.createView("RCTView", tag, JavaOnlyMap.of(), null, null, true) + smm.addViewAt(surfaceId, tag, 0) + + smm.updateProps(tag, JavaOnlyMap.of("opacity", 0.9)) + assertThat(smm.getView(tag).alpha).isEqualTo(0.9f) + } + + /** Synchronous props stored for one tag should not affect a different tag. */ + @Test + fun synchronousPropsAreIsolatedPerTag() { + val smm = startSurface() + createAndAttachView(smm, 42) + createAndAttachView(smm, 43) + + smm.storeSynchronousMountPropsOverride(42, JavaOnlyMap.of("opacity", 0.3)) + + smm.updateProps(42, JavaOnlyMap.of("opacity", 1.0)) + smm.updateProps(43, JavaOnlyMap.of("opacity", 1.0)) + + // Tag 42: synchronous override applies (0.3) + assertThat(smm.getView(42).alpha).isEqualTo(0.3f) + // Tag 43: no override, incoming props apply (1.0) + assertThat(smm.getView(43).alpha).isEqualTo(1.0f) + } +} From 52ff3925963f2e571afbb20f920b9dcfd3eb1791 Mon Sep 17 00:00:00 2001 From: generatedunixname1389709262956747 Date: Mon, 4 May 2026 16:27:53 -0700 Subject: [PATCH 18/34] folly::dynamic::push_back (dynamic.h) (#56676) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56676 Reviewed By: tmikov Differential Revision: D103664557 fbshipit-source-id: bcc53150cf357b025918b4dab9f19603284d602b --- packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp b/packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp index 52738cad98da..842e2dce1d1d 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp @@ -169,6 +169,7 @@ folly::dynamic dynamicFromValue( // the stack. Array array = top.obj.getArray(runtime); size_t arraySize = array.size(runtime); + top.dyn->reserve(arraySize); for (size_t i = 0; i < arraySize; ++i) { top.dyn->push_back(nullptr); } From d0672fb3a95062803ac7611182ac3a5a55dadbe9 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Mon, 4 May 2026 21:16:03 -0700 Subject: [PATCH 19/34] Add enableSchedulerDelegateInvalidation feature flag (#56678) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56678 Introduces a new common React Native feature flag, `enableSchedulerDelegateInvalidation`, that gates a defensive guard around `Scheduler::uiManagerDidDispatchCommand` and `uiManagerDidFinishTransaction`. Those call sites enqueue a lambda via `runtimeScheduler_->scheduleRenderingUpdate` that captures the raw `SchedulerDelegate` pointer by value. With `RuntimeScheduler_Modern` (bridgeless) the lambda runs asynchronously, so if the `SchedulerDelegate` is destroyed between enqueue and execution the lambda dereferences dangling memory and the process crashes (`EXC_BAD_ACCESS` / `KERN_INVALID_ADDRESS`). The follow-up commit adds the actual guard wired to this flag. Defaults to `false` so behavior is unchanged on landing. Apps opt in by overriding the flag in their own `ReactNativeFeatureFlagsDefaults` subclass. Includes the regenerated feature-flag boilerplate (Kotlin/Java/Cxx accessors, defaults, providers, JS spec, native module) emitted by `yarn featureflags --update`. Changelog: [Internal] Reviewed By: shwanton Differential Revision: D103757271 fbshipit-source-id: 85991de4a21e3946a25571b6488bee9c0a4e1caf --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- ...agsOverrides_RNOSS_Experimental_Android.kt | 4 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 114 ++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- ...tiveFeatureFlagsOverridesOSSExperimental.h | 6 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 22 files changed, 192 insertions(+), 69 deletions(-) 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 5214e31adf8c..c0a1a9a5cd73 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<<1eca66b21554b00725f2a9be894a0db9>> */ /** @@ -294,6 +294,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enablePropsUpdateReconciliationAndroid(): Boolean = accessor.enablePropsUpdateReconciliationAndroid() + /** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ + @JvmStatic + public fun enableSchedulerDelegateInvalidation(): Boolean = accessor.enableSchedulerDelegateInvalidation() + /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ 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 2509813ce330..a138abe7be3c 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<> + * @generated SignedSource<<76d977ea53cb2a37fc2ea8549e31cebd>> */ /** @@ -64,6 +64,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableNetworkEventReportingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null @@ -508,6 +509,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableSchedulerDelegateInvalidation(): Boolean { + var cached = enableSchedulerDelegateInvalidationCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableSchedulerDelegateInvalidation() + enableSchedulerDelegateInvalidationCache = cached + } + return cached + } + override fun enableSwiftUIBasedFilters(): Boolean { var cached = enableSwiftUIBasedFiltersCache if (cached == null) { 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 02e2fd2f8589..24abbbaa92ee 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<> */ /** @@ -116,6 +116,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableSchedulerDelegateInvalidation(): Boolean + @DoNotStrip @JvmStatic public external fun enableSwiftUIBasedFilters(): Boolean @DoNotStrip @JvmStatic public external fun enableViewCulling(): Boolean 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 998366d958af..4075e0ac58d1 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<<54a4f6d01a052e5ab19b15652d5ab1a5>> + * @generated SignedSource<<9e5b3192d1bec953c116d959ad63283d>> */ /** @@ -111,6 +111,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enablePropsUpdateReconciliationAndroid(): Boolean = false + override fun enableSchedulerDelegateInvalidation(): Boolean = false + override fun enableSwiftUIBasedFilters(): Boolean = false override fun enableViewCulling(): 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 398b5df92b2c..1284abcf7a4c 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<<4d09d820304f454bc3850da89087caec>> + * @generated SignedSource<<378d6a8de497d26ebbbf55885be27a21>> */ /** @@ -68,6 +68,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableNetworkEventReportingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null @@ -556,6 +557,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableSchedulerDelegateInvalidation(): Boolean { + var cached = enableSchedulerDelegateInvalidationCache + if (cached == null) { + cached = currentProvider.enableSchedulerDelegateInvalidation() + accessedFeatureFlags.add("enableSchedulerDelegateInvalidation") + enableSchedulerDelegateInvalidationCache = cached + } + return cached + } + override fun enableSwiftUIBasedFilters(): Boolean { var cached = enableSwiftUIBasedFiltersCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt index c806054c45fc..2e5fa22f4539 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.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<<187ac62197545fbce9d537527b4aed3b>> + * @generated SignedSource<<6a5fd95dffcab319acb765aa25a33d8d>> */ /** @@ -27,6 +27,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android : override fun enableAccessibilityOrder(): Boolean = true + override fun enableSchedulerDelegateInvalidation(): Boolean = true + override fun enableSwiftUIBasedFilters(): Boolean = true override fun preventShadowTreeCommitExhaustion(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 3113bd3ab653..13a3b26839b4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.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<<592acf445c4b4e56651cd6afb57e46fa>> + * @generated SignedSource<<42e555a40da280b24f84e3ee5b45051d>> */ /** @@ -111,6 +111,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip public fun enableSchedulerDelegateInvalidation(): Boolean + @DoNotStrip public fun enableSwiftUIBasedFilters(): Boolean @DoNotStrip public fun enableViewCulling(): Boolean 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 7c27db4af32a..f586f2e3fcb5 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<<55e4d6d6dc930b4ccf3e20cb77f7a042>> + * @generated SignedSource<> */ /** @@ -303,6 +303,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableSchedulerDelegateInvalidation() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableSchedulerDelegateInvalidation"); + return method(javaProvider_); + } + bool enableSwiftUIBasedFilters() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableSwiftUIBasedFilters"); @@ -809,6 +815,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableSchedulerDelegateInvalidation( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableSwiftUIBasedFilters(); @@ -1207,6 +1218,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enablePropsUpdateReconciliationAndroid", JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid), + makeNativeMethod( + "enableSchedulerDelegateInvalidation", + JReactNativeFeatureFlagsCxxInterop::enableSchedulerDelegateInvalidation), makeNativeMethod( "enableSwiftUIBasedFilters", JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters), 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 e6d4455961ef..2b3741c71e80 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<> */ /** @@ -162,6 +162,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enablePropsUpdateReconciliationAndroid( facebook::jni::alias_ref); + static bool enableSchedulerDelegateInvalidation( + facebook::jni::alias_ref); + static bool enableSwiftUIBasedFilters( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index e0a788e58f1c..a8bddab5889c 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<<16024bed6914b4b74d4a50de142e872a>> + * @generated SignedSource<> */ /** @@ -202,6 +202,10 @@ bool ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid() { return getAccessor().enablePropsUpdateReconciliationAndroid(); } +bool ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation() { + return getAccessor().enableSchedulerDelegateInvalidation(); +} + bool ReactNativeFeatureFlags::enableSwiftUIBasedFilters() { return getAccessor().enableSwiftUIBasedFilters(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 08516f6d1d1c..b0c8f5bb492b 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<<33ffe2572bbaeba64d2167dbac4bb0ab>> + * @generated SignedSource<<8b4288e3f5a8b26951150a3c75ad4356>> */ /** @@ -259,6 +259,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enablePropsUpdateReconciliationAndroid(); + /** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ + RN_EXPORT static bool enableSchedulerDelegateInvalidation(); + /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index ba5f777abe14..0f1a8ac663c3 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<<51dc335c5463b6c3f34c5330d69b6a00>> + * @generated SignedSource<> */ /** @@ -821,6 +821,24 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableSchedulerDelegateInvalidation() { + auto flagValue = enableSchedulerDelegateInvalidation_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(44, "enableSchedulerDelegateInvalidation"); + + flagValue = currentProvider_->enableSchedulerDelegateInvalidation(); + enableSchedulerDelegateInvalidation_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { auto flagValue = enableSwiftUIBasedFilters_.load(); @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(45, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableViewCulling"); + markFlagAsAccessed(46, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewRecycling"); + markFlagAsAccessed(47, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecyclingForImage"); + markFlagAsAccessed(48, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(49, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForText"); + markFlagAsAccessed(50, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewRecyclingForView"); + markFlagAsAccessed(51, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(52, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(53, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(54, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fixFindShadowNodeByTagRaceCondition() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "fixFindShadowNodeByTagRaceCondition"); + markFlagAsAccessed(55, "fixFindShadowNodeByTagRaceCondition"); flagValue = currentProvider_->fixFindShadowNodeByTagRaceCondition(); fixFindShadowNodeByTagRaceCondition_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(56, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(57, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(58, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxEnabledRelease"); + markFlagAsAccessed(59, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(60, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(61, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(62, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(63, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(64, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "perfIssuesEnabled"); + markFlagAsAccessed(65, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "perfMonitorV2Enabled"); + markFlagAsAccessed(66, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1226,7 +1244,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "preparedTextCacheSize"); + markFlagAsAccessed(67, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(68, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "redBoxV2Android"); + markFlagAsAccessed(69, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "redBoxV2IOS"); + markFlagAsAccessed(70, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(71, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(72, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(73, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(74, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(75, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(78, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useFabricInterop"); + markFlagAsAccessed(79, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useLISAlgorithmInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useLISAlgorithmInDifferentiator"); + markFlagAsAccessed(80, "useLISAlgorithmInDifferentiator"); flagValue = currentProvider_->useLISAlgorithmInDifferentiator(); useLISAlgorithmInDifferentiator_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(81, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useNestedScrollViewAndroid"); + markFlagAsAccessed(82, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedViewRegistryOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useOptimizedViewRegistryOnAndroid"); + markFlagAsAccessed(83, "useOptimizedViewRegistryOnAndroid"); flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); useOptimizedViewRegistryOnAndroid_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useSharedAnimatedBackend"); + markFlagAsAccessed(84, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTurboModuleInterop"); + markFlagAsAccessed(86, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1586,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTurboModules"); + markFlagAsAccessed(87, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1604,7 +1622,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(88, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1622,7 +1640,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "viewCullingOutsetRatio"); + markFlagAsAccessed(89, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1640,7 +1658,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewTransitionEnabled"); + markFlagAsAccessed(90, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1658,7 +1676,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "virtualViewPrerenderRatio"); + markFlagAsAccessed(91, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index bc9a1b7b1e27..67a5012fe00e 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<<62bf5997f9ee2799bf7fd2cd6d5a6cd1>> + * @generated SignedSource<<17621e5f711352812633c125b7858537>> */ /** @@ -76,6 +76,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableNetworkEventReporting(); bool enablePreparedTextLayout(); bool enablePropsUpdateReconciliationAndroid(); + bool enableSchedulerDelegateInvalidation(); bool enableSwiftUIBasedFilters(); bool enableViewCulling(); bool enableViewRecycling(); @@ -134,7 +135,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 91> accessedFeatureFlags_; + std::array, 92> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -180,6 +181,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableNetworkEventReporting_; std::atomic> enablePreparedTextLayout_; std::atomic> enablePropsUpdateReconciliationAndroid_; + std::atomic> enableSchedulerDelegateInvalidation_; std::atomic> enableSwiftUIBasedFilters_; std::atomic> enableViewCulling_; std::atomic> enableViewRecycling_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ac1e9c126022..ecc79d832fe4 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<<0042e6f884ccf466dda224021ff7a86a>> + * @generated SignedSource<<3d1718afe5b2ed63a918ffe09a4a36b1>> */ /** @@ -203,6 +203,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableSchedulerDelegateInvalidation() override { + return false; + } + bool enableSwiftUIBasedFilters() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 4bbb20893888..2bc41176d59c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.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<<23e79788b0820f61ba95288412367247>> */ /** @@ -441,6 +441,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enablePropsUpdateReconciliationAndroid(); } + bool enableSchedulerDelegateInvalidation() override { + auto value = values_["enableSchedulerDelegateInvalidation"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableSchedulerDelegateInvalidation(); + } + bool enableSwiftUIBasedFilters() override { auto value = values_["enableSwiftUIBasedFilters"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h index e84785c6fd2c..8c52948d3c02 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.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<<887a3ac5f8a93cc5ae5c8d01ec1d5e46>> */ /** @@ -35,6 +35,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur return true; } + bool enableSchedulerDelegateInvalidation() override { + return true; + } + bool enableSwiftUIBasedFilters() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 7820c8f313e4..400e4bea814d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.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<<87316e34516ebbbb34ae384e5a61376b>> */ /** @@ -69,6 +69,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableNetworkEventReporting() = 0; virtual bool enablePreparedTextLayout() = 0; virtual bool enablePropsUpdateReconciliationAndroid() = 0; + virtual bool enableSchedulerDelegateInvalidation() = 0; virtual bool enableSwiftUIBasedFilters() = 0; virtual bool enableViewCulling() = 0; virtual bool enableViewRecycling() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index b6d699cd6e94..8c49f21652bd 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<> + * @generated SignedSource<<80e1d4551c51800c138f16228cfae9f6>> */ /** @@ -264,6 +264,11 @@ bool NativeReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool NativeReactNativeFeatureFlags::enableSchedulerDelegateInvalidation( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); +} + bool NativeReactNativeFeatureFlags::enableSwiftUIBasedFilters( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableSwiftUIBasedFilters(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index b3eb68b5f3a4..a3703ec36073 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.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<<1df283aec4b56f36271a187341e7103d>> */ /** @@ -124,6 +124,8 @@ class NativeReactNativeFeatureFlags bool enablePropsUpdateReconciliationAndroid(jsi::Runtime& runtime); + bool enableSchedulerDelegateInvalidation(jsi::Runtime& runtime); + bool enableSwiftUIBasedFilters(jsi::Runtime& runtime); bool enableViewCulling(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index c09b656231a5..e930a4f00898 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -514,6 +514,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableSchedulerDelegateInvalidation: { + defaultValue: false, + metadata: { + dateAdded: '2026-05-04', + description: + 'Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free).', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'experimental', + }, enableSwiftUIBasedFilters: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index fe9178d5b88f..9be1938a6448 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<<404bdc9f1cd9ea3d65d1b31dccd1591e>> + * @generated SignedSource<<349263e08085c42598f13da74ffbf584>> * @flow strict * @noformat */ @@ -91,6 +91,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableNetworkEventReporting: Getter, enablePreparedTextLayout: Getter, enablePropsUpdateReconciliationAndroid: Getter, + enableSchedulerDelegateInvalidation: Getter, enableSwiftUIBasedFilters: Getter, enableViewCulling: Getter, enableViewRecycling: Getter, @@ -380,6 +381,10 @@ export const enablePreparedTextLayout: Getter = createNativeFlagGetter( * When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node. */ export const enablePropsUpdateReconciliationAndroid: Getter = createNativeFlagGetter('enablePropsUpdateReconciliationAndroid', false); +/** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ +export const enableSchedulerDelegateInvalidation: Getter = createNativeFlagGetter('enableSchedulerDelegateInvalidation', false); /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a49f45388213..77b4f560fdf2 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.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<<6e8201091111a8451911f4e72cdc0932>> + * @generated SignedSource<<1fe579457854f95d09dd24e4578dbc65>> * @flow strict * @noformat */ @@ -69,6 +69,7 @@ export interface Spec extends TurboModule { +enableNetworkEventReporting?: () => boolean; +enablePreparedTextLayout?: () => boolean; +enablePropsUpdateReconciliationAndroid?: () => boolean; + +enableSchedulerDelegateInvalidation?: () => boolean; +enableSwiftUIBasedFilters?: () => boolean; +enableViewCulling?: () => boolean; +enableViewRecycling?: () => boolean; From aadbe965792bd900ca70412d6704b76e339d1aca Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Mon, 4 May 2026 21:16:03 -0700 Subject: [PATCH 20/34] Guard Scheduler deferred lambdas against delegate teardown (#56680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56680 Scheduler::uiManagerDidFinishTransaction and Scheduler::uiManagerDidDispatchCommand queue lambdas via runtimeScheduler_->scheduleRenderingUpdate that capture the raw delegate_ pointer by value. With RuntimeScheduler_Modern (the bridgeless implementation), the lambda runs asynchronously, so if the SchedulerDelegate is destroyed between enqueue and execution (surface teardown, Scheduler destruction, or setDelegate flip), the lambda dereferences dangling memory → EXC_BAD_ACCESS / KERN_INVALID_ADDRESS. Add a per-delegate-identity invalidation token (`shared_ptr>`) owned by Scheduler and captured by-value into the deferred lambdas. The destructor and setDelegate flip the flag to true; lambdas check it (acquire ordering) before dereferencing the captured raw delegate. The shared_ptr keeps the atomic alive for any outstanding lambdas. Public API is unchanged (private member only), so C++ API snapshots are unaffected. Changelog: [General][Fixed] - Prevent Scheduler use-after-free crash when surfaces tear down with pending rendering updates Reviewed By: mdvacca, Abbondanzo Differential Revision: D103727974 fbshipit-source-id: 8abf5073b7fd35e88d65a91c42f311b238d8f156 --- .../react/renderer/scheduler/Scheduler.cpp | 46 ++++++++++++++++++- .../react/renderer/scheduler/Scheduler.h | 6 +++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 28750e02d16f..7ea5fc3c782f 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -29,7 +29,8 @@ Scheduler::Scheduler( const SchedulerToolbox& schedulerToolbox, UIManagerAnimationDelegate* animationDelegate, SchedulerDelegate* delegate) - : runtimeExecutor_(schedulerToolbox.runtimeExecutor), + : delegateInvalidated_(std::make_shared>(false)), + runtimeExecutor_(schedulerToolbox.runtimeExecutor), contextContainer_(schedulerToolbox.contextContainer) { // Creating a container for future `EventDispatcher` instance. eventDispatcher_ = std::make_shared>(); @@ -171,6 +172,15 @@ Scheduler::~Scheduler() { LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this << ")."; + // Invalidate any lambdas already queued via scheduleRenderingUpdate that + // captured a raw delegate_ pointer; without this they'd dereference a + // dangling SchedulerDelegate after Scheduler teardown. (No replacement + // token is allocated here — Scheduler is going away.) + // Gated to allow controlled rollout / rollback. + if (ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation()) { + *delegateInvalidated_ = true; + } + auto weakRuntimeScheduler = contextContainer_->find>( RuntimeSchedulerKey); @@ -260,6 +270,21 @@ Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN( #pragma mark - Delegate void Scheduler::setDelegate(SchedulerDelegate* delegate) { + // Gated to allow controlled rollout / rollback. + if (ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation() && + delegate_ != delegate) { + // Mark the *current* token invalid: any rendering-update lambda already + // queued holds a shared_ptr to this atomic and will observe `true` on + // its next read, so it no-ops instead of calling into the previous + // delegate (which the caller is about to drop). + *delegateInvalidated_ = true; + // Then install a *fresh* token (a new atomic) so lambdas captured + // against the new delegate use their own non-invalidated flag. + // Reusing the previous atomic and flipping it back to `false` would + // re-arm the in-flight lambdas — exactly the use-after-free we're + // trying to prevent — because they share the same shared_ptr. + delegateInvalidated_ = std::make_shared>(false); + } delegate_ = delegate; } @@ -288,10 +313,21 @@ void Scheduler::uiManagerDidFinishTransaction( if (!mountSynchronously) { auto surfaceId = mountingCoordinator->getSurfaceId(); + // Capture the gating flag at queue time: the lambda's decision to + // honor the invalidation guard is fixed when we enqueue, not when it + // later runs. Avoids per-invocation feature-flag reads and keeps the + // contract for an in-flight lambda stable across flag flips. + auto guardEnabled = + ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); runtimeScheduler_->scheduleRenderingUpdate( surfaceId, [delegate = delegate_, + invalidated = delegateInvalidated_, + guardEnabled, mountingCoordinator = std::move(mountingCoordinator)]() { + if (guardEnabled && *invalidated) { + return; + } delegate->schedulerShouldRenderTransactions(mountingCoordinator); }); } else { @@ -314,12 +350,20 @@ void Scheduler::uiManagerDidDispatchCommand( "Scheduler::uiManagerDispatchCommand", "commandName", commandName); if (delegate_ != nullptr) { auto shadowView = ShadowView(*shadowNode); + // See comment in uiManagerDidFinishTransaction above for gating shape. + auto guardEnabled = + ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); runtimeScheduler_->scheduleRenderingUpdate( shadowNode->getSurfaceId(), [delegate = delegate_, + invalidated = delegateInvalidated_, + guardEnabled, shadowView = std::move(shadowView), commandName, args]() { + if (guardEnabled && *invalidated) { + return; + } delegate->schedulerDidDispatchCommand(shadowView, commandName, args); }); } diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index cc665dfde4b5..bd6de832e04d 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -122,6 +123,11 @@ class Scheduler final : public UIManagerDelegate { friend class SurfaceHandler; SchedulerDelegate *delegate_; + // Invalidation token captured by-value into lambdas deferred via + // runtimeScheduler_->scheduleRenderingUpdate. Set to true on delegate + // change or Scheduler destruction so a lambda that outlives its captured + // raw delegate pointer can no-op instead of dereferencing dangling memory. + std::shared_ptr> delegateInvalidated_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; RuntimeExecutor runtimeExecutor_; std::shared_ptr uiManager_; From d1cdce24cca2224f8d37873d24a49523a8271a0a Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Tue, 5 May 2026 07:06:10 -0700 Subject: [PATCH 21/34] Manually fix references to registry.facebook.net -> registry.yarnpkg.com (#56683) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56683 RN's GitHub Actions CI isn't set up to authenticate against `registry.facebook.net`, causing 401s on Yarn install As a quick fix ahead of the 0.86 cut, this manually fixes them back to `yarnpkg.com` Changelog: [Internal] Reviewed By: cortinico Differential Revision: D103845323 fbshipit-source-id: d31f5bbaedfeaa35ef26837ab8faf29dbeff465e --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 15f049e1396c..a7335eb53570 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3033,7 +3033,7 @@ babel-plugin-syntax-hermes-parser@0.35.0: babel-plugin-syntax-hermes-parser@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.36.0.tgz#51e429f32f23b197c477a20525798a7a97709c80" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.36.0.tgz#51e429f32f23b197c477a20525798a7a97709c80" integrity sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q== dependencies: hermes-parser "0.36.0" @@ -4736,7 +4736,7 @@ flora-colossus@^2.0.0: flow-api-translator@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/flow-api-translator/-/flow-api-translator-0.36.0.tgz#18737e0d74c339d6dda33ba9d179f99595f6f9f2" + resolved "https://registry.yarnpkg.com/flow-api-translator/-/flow-api-translator-0.36.0.tgz#18737e0d74c339d6dda33ba9d179f99595f6f9f2" integrity sha512-mzyczOh7gYKa1Uo2NqjVEzVDwLWZxIqrinF68TQ/48susbxX8HG3TshVb03eO4Ngp83JBVpNUJyarYeGYK8t4Q== dependencies: "@babel/code-frame" "^7.16.0" @@ -4751,7 +4751,7 @@ flow-api-translator@0.36.0: flow-bin@^0.312.0: version "0.312.0" - resolved "https://registry.facebook.net/flow-bin/-/flow-bin-0.312.0.tgz#4962c29a740beff72eb4b89b0f9625213b3a82d8" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.312.0.tgz#4962c29a740beff72eb4b89b0f9625213b3a82d8" integrity sha512-SVjNNbWt8d69cj6dLXy5P8pq3p6dpje8cL3PvPEqY2KoloiYdpxjOOEZTFvliZV7gxKzGB83iGLA1ct6TH/7hw== flow-enums-runtime@^0.0.6: @@ -5133,12 +5133,12 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: hermes-compiler@0.0.0: version "0.0.0" - resolved "https://registry.facebook.net/hermes-compiler/-/hermes-compiler-0.0.0.tgz#8d9f6a0b2740ce34d71258fec684e7b6bfd97efa" + resolved "https://registry.yarnpkg.com/hermes-compiler/-/hermes-compiler-0.0.0.tgz#8d9f6a0b2740ce34d71258fec684e7b6bfd97efa" integrity sha512-boVFutx6ME/Km2mB6vvsQcdnazEYYI/jV1pomx1wcFUG/EVqTkr5CU0CW9bKipOA/8Hyu3NYwW3THg2Q1kNCfA== hermes-eslint@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/hermes-eslint/-/hermes-eslint-0.36.0.tgz#d8148b7425e7c5ce02e6aa4e092fc1edb0419fa7" + resolved "https://registry.yarnpkg.com/hermes-eslint/-/hermes-eslint-0.36.0.tgz#d8148b7425e7c5ce02e6aa4e092fc1edb0419fa7" integrity sha512-oEmTrP/Onp8kA5N23/yF4kGgSKB+9KH0CsrY+zGsgq6nK4CqY0y7Atv51X+F4deBHxWngLOvYBLt32HtOps78Q== dependencies: esrecurse "^4.3.0" @@ -5157,7 +5157,7 @@ hermes-estree@0.35.0: hermes-estree@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/hermes-estree/-/hermes-estree-0.36.0.tgz#5b8c162e986159baf86bc4d01612cff9845ab0f7" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.36.0.tgz#5b8c162e986159baf86bc4d01612cff9845ab0f7" integrity sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w== hermes-parser@0.25.1, hermes-parser@^0.25.1: @@ -5176,14 +5176,14 @@ hermes-parser@0.35.0: hermes-parser@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/hermes-parser/-/hermes-parser-0.36.0.tgz#756d6b07301789c5ff0ac3ac7ff6bffa8be7098c" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.36.0.tgz#756d6b07301789c5ff0ac3ac7ff6bffa8be7098c" integrity sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w== dependencies: hermes-estree "0.36.0" hermes-transform@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/hermes-transform/-/hermes-transform-0.36.0.tgz#88ed181f4ac16985290928f03124e1107505698d" + resolved "https://registry.yarnpkg.com/hermes-transform/-/hermes-transform-0.36.0.tgz#88ed181f4ac16985290928f03124e1107505698d" integrity sha512-GB4vu6D/JMNX4jpgyr2suZCNdR8toaMdxF2598UiBzQhP89lAFs8c/pMWYEbfX8Y44Bvzzx0ztfJnrWzKls8HA== dependencies: "@babel/code-frame" "^7.16.0" @@ -7818,7 +7818,7 @@ prelude-ls@^1.2.1: prettier-plugin-hermes-parser@0.36.0: version "0.36.0" - resolved "https://registry.facebook.net/prettier-plugin-hermes-parser/-/prettier-plugin-hermes-parser-0.36.0.tgz#8839207d3a4290b7afe1bd54fbc5154f963b8cf2" + resolved "https://registry.yarnpkg.com/prettier-plugin-hermes-parser/-/prettier-plugin-hermes-parser-0.36.0.tgz#8839207d3a4290b7afe1bd54fbc5154f963b8cf2" integrity sha512-CtlJ5l0DC48SN3OWSht5jAEIFU92xwHSe87/Bl005/9TlHyN2aAmPa6T/A2JLiIgRT/lGsCtc6srwEMM3/5k4w== prettier@3.6.2: From cdb114c87d74fc12a39c788ed4db6548df04de5c Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 06:20:13 -0700 Subject: [PATCH 22/34] feat(macos): port React Native 0.86 platform changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../React/CoreModules/RCTRedBox+Internal.h | 7 ++++--- .../react-native/React/CoreModules/RCTRedBox.mm | 6 +++++- .../CoreModules/RCTRedBox2AnsiParser+Internal.h | 3 +++ .../React/CoreModules/RCTRedBox2AnsiParser.mm | 2 +- .../CoreModules/RCTRedBox2Controller+Internal.h | 2 +- .../React/CoreModules/RCTRedBox2Controller.mm | 2 +- .../CoreModules/RCTRedBoxController+Internal.h | 13 ++++++++++++- .../React/CoreModules/RCTRedBoxController.mm | 4 ++-- 8 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTRedBox+Internal.h b/packages/react-native/React/CoreModules/RCTRedBox+Internal.h index d59e8a946f30..8d9025d86f6d 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBox+Internal.h @@ -6,7 +6,7 @@ */ #import -#import +#import // [macOS] #if RCT_DEV_MENU @@ -14,8 +14,9 @@ @protocol RCTRedBoxControllerActionDelegate -- (void)redBoxController:(UIViewController *)redBoxController openStackFrameInEditor:(RCTJSStackFrame *)stackFrame; -- (void)reloadFromRedBoxController:(UIViewController *)redBoxController; +- (void)redBoxController:(RCTPlatformViewController *)redBoxController // [macOS] + openStackFrameInEditor:(RCTJSStackFrame *)stackFrame; +- (void)reloadFromRedBoxController:(RCTPlatformViewController *)redBoxController; // [macOS] - (void)loadExtraDataViewController; @end diff --git a/packages/react-native/React/CoreModules/RCTRedBox.mm b/packages/react-native/React/CoreModules/RCTRedBox.mm index 6e0a2b617c20..9f425218f108 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox.mm @@ -21,7 +21,9 @@ #import "CoreModulesPlugins.h" #import "RCTRedBox+Internal.h" +#if !TARGET_OS_OSX // [macOS] #import "RCTRedBox2Controller+Internal.h" +#endif // [macOS] #import "RCTRedBoxController+Internal.h" #if RCT_DEV_MENU @@ -195,9 +197,11 @@ - (void)showErrorMessage:(NSString *)message errorInfo = [self _customizeError:errorInfo]; if (self->_controller == nullptr) { - if (facebook::react::ReactNativeFeatureFlags::redBoxV2IOS()) { + if (!TARGET_OS_OSX && facebook::react::ReactNativeFeatureFlags::redBoxV2IOS()) { // [macOS] +#if !TARGET_OS_OSX // [macOS] self->_controller = [[RCTRedBox2Controller alloc] initWithCustomButtonTitles:self->_customButtonTitles customButtonHandlers:self->_customButtonHandlers]; +#endif // [macOS] } else { self->_controller = [[RCTRedBoxController alloc] initWithCustomButtonTitles:self->_customButtonTitles customButtonHandlers:self->_customButtonHandlers]; diff --git a/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser+Internal.h b/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser+Internal.h index 4fa60a55adcb..302724ec71da 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser+Internal.h @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +#import #import // [macOS] /** @@ -26,3 +27,5 @@ #endif // macOS] @end + +#endif // [macOS] diff --git a/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser.mm b/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser.mm index ba3c39798ce1..811c3e02ccfe 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox2AnsiParser.mm @@ -10,7 +10,7 @@ #import #import -#if RCT_DEV_MENU +#if RCT_DEV_MENU && !TARGET_OS_OSX // [macOS] using facebook::react::unstable_redbox::AnsiColor; using facebook::react::unstable_redbox::parseAnsi; diff --git a/packages/react-native/React/CoreModules/RCTRedBox2Controller+Internal.h b/packages/react-native/React/CoreModules/RCTRedBox2Controller+Internal.h index 905e9398487f..c27b69bd64a8 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox2Controller+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBox2Controller+Internal.h @@ -10,7 +10,7 @@ #import "RCTRedBox+Internal.h" -#if RCT_DEV_MENU +#if RCT_DEV_MENU && !TARGET_OS_OSX // [macOS] typedef void (^RCTRedBox2ButtonPressHandler)(void); diff --git a/packages/react-native/React/CoreModules/RCTRedBox2Controller.mm b/packages/react-native/React/CoreModules/RCTRedBox2Controller.mm index f17591f1e984..c2c047ac4075 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox2Controller.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox2Controller.mm @@ -22,7 +22,7 @@ // @lint-ignore-every CLANGTIDY clang-diagnostic-switch-default // NOTE: clang-diagnostic-switch-default conflicts with clang-diagnostic-switch-enum -#if RCT_DEV_MENU +#if RCT_DEV_MENU && !TARGET_OS_OSX // [macOS] #pragma mark - RCTRedBox2Controller diff --git a/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h b/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h index 191f82f461f8..bd917b713cf8 100644 --- a/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h @@ -6,13 +6,24 @@ */ #import +#import // [macOS] #import "RCTRedBox+Internal.h" #import "RCTRedBox.h" #if RCT_DEV_MENU -@interface RCTRedBoxController : UIViewController +@interface RCTRedBoxController + : RCTPlatformViewController < // [macOS + RCTRedBoxControlling, +#if !TARGET_OS_OSX // [macOS + UITableViewDelegate, + UITableViewDataSource +#else // [macOS + NSTableViewDelegate, + NSTableViewDataSource +#endif // macOS] + > @property (nonatomic, weak) id actionDelegate; diff --git a/packages/react-native/React/CoreModules/RCTRedBoxController.mm b/packages/react-native/React/CoreModules/RCTRedBoxController.mm index 656fb4a6f7c9..d6c0bff24d84 100644 --- a/packages/react-native/React/CoreModules/RCTRedBoxController.mm +++ b/packages/react-native/React/CoreModules/RCTRedBoxController.mm @@ -411,14 +411,14 @@ - (void)showExtraDataViewController - (void)copyStack { NSMutableString *fullStackTrace; - + if (_lastErrorMessage != nil) { fullStackTrace = [_lastErrorMessage mutableCopy]; [fullStackTrace appendString:@"\n\n"]; } else { fullStackTrace = [NSMutableString string]; } - + for (RCTJSStackFrame *stackFrame in _lastStackTrace) { [fullStackTrace appendString:[NSString stringWithFormat:@"%@\n", stackFrame.methodName]]; if (stackFrame.file) { From 8ee8d15089f538439c2f945bd863f32ed540e3e1 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 06:24:23 -0700 Subject: [PATCH 23/34] fix(hermes): align 0.86 compiler artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/react-native/package.json | 2 +- packages/react-native/sdks/.hermesv1version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 2434b27b14a5..8440e9b2b0a6 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -176,7 +176,7 @@ "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", - "hermes-compiler": "250829098.0.6", + "hermes-compiler": "250829098.0.12", "invariant": "^2.2.4", "memoize-one": "^5.0.0", "metro-runtime": "^0.84.3", diff --git a/packages/react-native/sdks/.hermesv1version b/packages/react-native/sdks/.hermesv1version index 0472f7dd0a4f..32158fb79f03 100644 --- a/packages/react-native/sdks/.hermesv1version +++ b/packages/react-native/sdks/.hermesv1version @@ -1 +1 @@ -hermes-v250829098.0.2 \ No newline at end of file +hermes-v250829098.0.12 From f6fab8430f5157475d57a8ed419ef7f36fdbd989 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 06:24:23 -0700 Subject: [PATCH 24/34] chore: regenerate 0.86 derived artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../LogBoxInspectorStackFrames-test.js.snap | 64 +++ packages/react-native/ReactNativeApi.d.ts | 317 +++++++----- packages/rn-tester/Podfile.lock | 153 +++++- yarn.lock | 458 +++++++++++++----- 4 files changed, 729 insertions(+), 263 deletions(-) diff --git a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorStackFrames-test.js.snap b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorStackFrames-test.js.snap index 92245dc6ee28..e7732ceda41c 100644 --- a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorStackFrames-test.js.snap +++ b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorStackFrames-test.js.snap @@ -78,6 +78,48 @@ exports[`LogBoxInspectorStackFrames should render stack frames with 1 frame coll } > > + * @generated SignedSource<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -21,9 +21,12 @@ /* eslint-disable redundant-undefined/redundant-undefined */ +import type { HostInstance as HostInstance_2 } from "react-native" import { ListRenderItem } from "@react-native-macos/virtualized-lists" import { ListRenderItemInfo } from "@react-native-macos/virtualized-lists" import { ListViewToken } from "@react-native-macos/virtualized-lists" +import type { PublicRootInstance as PublicRootInstance_2 } from "react-native" +import type { PublicTextInstance as PublicTextInstance_2 } from "react-native" import * as React from "react" import { ScrollToLocationParamsType } from "@react-native-macos/virtualized-lists" import { SectionBase } from "@react-native-macos/virtualized-lists" @@ -90,6 +93,7 @@ declare const $$flattenStyle: typeof flattenStyle_default declare const $$NativeDeviceInfo: typeof NativeDeviceInfo_default declare const $$NativeDialogManagerAndroid: null | Spec | undefined declare const $$ProgressBarAndroidNativeComponent: HostComponent +declare const $$ReactFabric: typeof ReactFabric_default declare const $$ScrollViewContext: typeof ScrollViewContext_default declare const $$ScrollViewNativeComponent: typeof ScrollViewNativeComponent_default declare const $$SwitchNativeComponent: ComponentType @@ -242,12 +246,7 @@ declare const eventImpl: ( argMapping: ReadonlyArray, config: EventConfig, ) => any -declare const findNodeHandle: ( - componentOrHandle: - | (number | React.ComponentRef) - | null - | undefined, -) => null | number | undefined +declare const findNodeHandle: typeof $$ReactFabric.findNodeHandle declare const flatten: typeof $$flattenStyle declare const forkEvent: typeof $$AnimatedImplementation.forkEvent declare const hairlineWidth: number @@ -395,6 +394,8 @@ declare const RCTNetworking_default: { withCredentials: boolean, ): void } +declare let ReactFabric_default: ReactFabricType +declare const ReadOnlyNodeBase: typeof Object declare const registerCallableModule: typeof registerCallableModule_default declare const registerCallableModule_default: RegisterCallableModule declare const requireNativeComponent: typeof requireNativeComponent_default @@ -583,6 +584,7 @@ declare type ____BlendMode_Internal = | "multiply" | "normal" | "overlay" + | "plus-lighter" | "saturation" | "screen" | "soft-light" @@ -1009,6 +1011,7 @@ declare type $$NativeDeviceInfo = typeof $$NativeDeviceInfo declare type $$NativeDialogManagerAndroid = typeof $$NativeDialogManagerAndroid declare type $$ProgressBarAndroidNativeComponent = typeof $$ProgressBarAndroidNativeComponent +declare type $$ReactFabric = typeof $$ReactFabric declare type $$ScrollViewContext = typeof $$ScrollViewContext declare type $$ScrollViewNativeComponent = typeof $$ScrollViewNativeComponent declare type $$SwitchNativeComponent = typeof $$SwitchNativeComponent @@ -1646,7 +1649,6 @@ declare type AppearancePreferences = { colorScheme?: ColorSchemeName } declare type AppParameters = { - fabric?: boolean initialProps: { readonly [$$Key$$: string]: unknown } @@ -2519,6 +2521,10 @@ declare type HandledKeyEvent = { readonly metaKey?: boolean readonly shiftKey?: boolean } +declare interface HardwareBackPressEvent { + readonly timeStamp: number + readonly type: string +} declare type Headers = { [name: string]: string } @@ -3431,6 +3437,9 @@ declare interface NativeUIEvent { } declare type NativeVirtualText = typeof NativeVirtualText declare type Networking = typeof Networking +declare type Node = symbol & { + __Node__: string +} declare class NodeList_default implements Iterable, ArrayLike_2 { [index: number]: T readonly length: number @@ -3955,6 +3964,36 @@ declare type RCTNetworkingEventDefinitions = { ] readonly didSendNetworkData: [[number, number, number]] } +declare type ReactFabricType = { + dispatchCommand( + handle: HostInstance_2, + command: string, + args: Array, + ): void + findHostInstance_DEPRECATED( + componentOrHandle: (number | React.ComponentRef) | undefined, + ): HostInstance_2 | undefined + findNodeHandle( + componentOrHandle: (number | React.ComponentRef) | undefined, + ): number | undefined + getNodeFromInternalInstanceHandle( + internalInstanceHandle: InternalInstanceHandle, + ): Node | undefined + getPublicInstanceFromInternalInstanceHandle( + internalInstanceHandle: InternalInstanceHandle, + ): HostInstance_2 | null | PublicTextInstance_2 + getPublicInstanceFromRootTag(rootTag: number): null | PublicRootInstance_2 + isChildPublicInstance(parent: HostInstance_2, child: HostInstance_2): boolean + render( + element: React.JSX.Element, + containerTag: number, + callback: (() => void) | undefined, + concurrentRoot: boolean | undefined, + options: RenderRootOptions | undefined, + ): React.ComponentRef | undefined + sendAccessibilityEvent(handle: HostInstance_2, eventType: string): void + unmountComponentAtNode(containerTag: number): void +} declare class ReactNativeDocument_default extends ReadOnlyNode_default { get childElementCount(): number get children(): HTMLCollection_default @@ -4046,7 +4085,7 @@ declare class ReadOnlyElement_default extends ReadOnlyNode_default { get tagName(): string get textContent(): string } -declare class ReadOnlyNode_default { +declare class ReadOnlyNode_default extends ReadOnlyNodeBase { static ATTRIBUTE_NODE: number static CDATA_SECTION_NODE: number static COMMENT_NODE: number @@ -4154,6 +4193,28 @@ declare type Registry = { runnables: Runnables sections: ReadonlyArray } +declare type RenderRootOptions = { + onCaughtError?: ( + error: unknown, + errorInfo: { + readonly componentStack?: string + readonly errorBoundary?: React.Component + }, + ) => void + onDefaultTransitionIndicator?: () => (() => void) | void + onRecoverableError?: ( + error: unknown, + errorInfo: { + readonly componentStack?: string + }, + ) => void + onUncaughtError?: ( + error: unknown, + errorInfo: { + readonly componentStack?: string + }, + ) => void +} declare type RequestBody = | ArrayBuffer | ArrayBufferView @@ -4987,7 +5048,7 @@ declare type TaskProvider = () => HeadlessTask declare type TBackHandler = { addEventListener( eventName: BackPressEventName, - handler: () => boolean | undefined, + handler: (event: HardwareBackPressEvent) => boolean | undefined, ): { remove: () => void } @@ -5097,6 +5158,7 @@ declare type TextInputBaseProps = { readonly allowFontScaling?: boolean readonly autoCapitalize?: AutoCapitalize readonly autoComplete?: + | "2fa-app-otp" | "additional-name" | "address-line1" | "address-line2" @@ -5117,12 +5179,18 @@ declare type TextInputBaseProps = { | "cc-type" | "country" | "current-password" + | "email-otp" | "email" | "family-name" + | "flight-confirmation-code" + | "flight-number" | "gender" + | "gift-card-number" + | "gift-card-pin" | "given-name" | "honorific-prefix" | "honorific-suffix" + | "loyalty-account-number" | "name-family" | "name-given" | "name-middle-initial" @@ -5139,21 +5207,26 @@ declare type TextInputBaseProps = { | "password-new" | "password" | "postal-address-country" + | "postal-address-dependent-locality" | "postal-address-extended-postal-code" | "postal-address-extended" | "postal-address-locality" | "postal-address-region" + | "postal-address-unit" | "postal-address" | "postal-code" + | "promo-code" | "sms-otp" | "street-address" | "tel-country-code" | "tel-device" | "tel-national" | "tel" + | "upi-vpa" | "url" | "username-new" | "username" + | "wifi-password" readonly autoCorrect?: boolean readonly autoFocus?: boolean readonly blurOnSubmit?: boolean @@ -5818,38 +5891,38 @@ declare type WrapperComponentProvider = ( appParameters: Object, ) => React.ComponentType export { - AccessibilityActionEvent, // f6181a2c - AccessibilityInfo, // 86321f01 + AccessibilityActionEvent, // 5c5928b9 + AccessibilityInfo, // a43e6000 AccessibilityProps, // d961eb5c AccessibilityRole, // 3cf8c6c5 AccessibilityState, // b0c2b3f7 AccessibilityValue, // cf8bcb74 ActionSheetIOS, // b558559e ActionSheetIOSOptions, // 1756eb5a - ActivityIndicator, // 75b74e39 - ActivityIndicatorProps, // f9d1bc02 + ActivityIndicator, // 9d17a22c + ActivityIndicatorProps, // 3ecc55a4 Alert, // 24958ab5 AlertButton, // bf1a3b60 AlertButtonStyle, // ec9fb242 AlertOptions, // 39b16cfa AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // e0100948 - AppConfig, // ebddad4b - AppRegistry, // eb54df94 + Animated, // 5138836c + AppConfig, // ce4209a7 + AppRegistry, // 1d923149 AppState, // 12012be5 AppStateEvent, // 80f034c3 AppStateStatus, // 447e5ef2 Appearance, // 00cbaa0a AutoCapitalize, // c0e857a0 - BackHandler, // 0b13c289 + BackHandler, // f139fc69 BackPressEventName, // 4620fb76 - BlurEvent, // 870b9bb5 + BlurEvent, // e6151a1f BoxShadowValue, // b679703f - Button, // 94c690cc - ButtonProps, // 9ec3afed + Button, // 5a47e32a + ButtonProps, // 636bb0f3 Clipboard, // 41addb89 - CodegenTypes, // adbc477c + CodegenTypes, // 0b8108a8 ColorSchemeName, // 31a4350e ColorValue, // 98989a8f ColorWithSystemEffectMacOS, // 5644e8fe @@ -5866,9 +5939,9 @@ export { DimensionsPayload, // 653bc26c DisplayMetrics, // 1dc35cef DisplayMetricsAndroid, // 872e62eb - DrawerLayoutAndroid, // a4675368 - DrawerLayoutAndroidProps, // 498fe7cf - DrawerSlideEvent, // cc43db83 + DrawerLayoutAndroid, // dbcc4062 + DrawerLayoutAndroidProps, // 8249f9ee + DrawerSlideEvent, // 0256d35a DropShadowValue, // e9df2606 DynamicColorIOS, // d96c228c DynamicColorIOSTuple, // 023ce58e @@ -5884,47 +5957,47 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // 5cbbf99e - FlatListProps, // ec6dc7bf - FocusEvent, // 529b43eb + FlatList, // 51536750 + FlatListProps, // 1bb18ba2 + FocusEvent, // 62fc1eb8 FontVariant, // 7c7558bb - GestureResponderEvent, // 52d0886d - GestureResponderHandlers, // 1c246fde + GestureResponderEvent, // 07521628 + GestureResponderHandlers, // a992926f Handle, // 2d65285d - HostComponent, // 5e13ff5a - HostInstance, // 489cbe7f + HostComponent, // 16fccab5 + HostInstance, // 9b5a9ec2 I18nManager, // f9870e00 IEventEmitter, // fbef6131 IOSKeyboardEvent, // e67bfe3a IgnorePattern, // ec6f6ece - Image, // 3d7dfbf5 - ImageBackground, // 6bd7906a - ImageBackgroundProps, // a9f5ecde - ImageErrorEvent, // b7b2ae63 - ImageLoadEvent, // 5baae813 - ImageProgressEventIOS, // adb35052 - ImageProps, // b65035af + Image, // 1708f73b + ImageBackground, // 072459bb + ImageBackgroundProps, // 4fa52520 + ImageErrorEvent, // d3ee606e + ImageLoadEvent, // 6b547ea5 + ImageProgressEventIOS, // 4c866a82 + ImageProps, // 936cc4c8 ImagePropsAndroid, // 9fd9bcbb - ImagePropsBase, // e1828114 - ImagePropsIOS, // 318adce2 + ImagePropsBase, // ee82d9dc + ImagePropsIOS, // c4ae0c06 ImageRequireSource, // 681d683b ImageResolvedAssetSource, // f3060931 ImageSize, // 1c47cf88 ImageSource, // 48c7f316 ImageSourcePropType, // bfb5e5c6 - ImageStyle, // 6d9dfbb6 + ImageStyle, // e8ba815a ImageURISource, // 016eb083 - InputAccessoryView, // c2cdeb8a - InputAccessoryViewProps, // fc574890 + InputAccessoryView, // 279d21b1 + InputAccessoryViewProps, // be6fbc12 InputModeOptions, // 4e8581b9 Insets, // e7fe432a InteractionManager, // c324d6e3 - KeyDownEvent, // b092d122 - KeyEvent, // 0bdb72cd - KeyUpEvent, // 97a6175b + KeyDownEvent, // e194b5e5 + KeyEvent, // 91782d67 + KeyUpEvent, // 71a6a0a1 Keyboard, // 49414c97 - KeyboardAvoidingView, // a696aa05 - KeyboardAvoidingViewProps, // f23d84fd + KeyboardAvoidingView, // 048f537e + KeyboardAvoidingViewProps, // ec33c842 KeyboardEvent, // c3f895d4 KeyboardEventEasing, // af4091c8 KeyboardEventName, // 59299ad6 @@ -5937,7 +6010,7 @@ export { LayoutAnimationProperty, // 52995f01 LayoutAnimationType, // 2da0a29b LayoutAnimationTypes, // 081b3bde - LayoutChangeEvent, // c674f902 + LayoutChangeEvent, // c388ba2b LayoutConformanceProps, // 055f03b8 LayoutRectangle, // 6601b294 Linking, // 9a6a174d @@ -5949,34 +6022,34 @@ export { MeasureInWindowOnSuccessCallback, // a285f598 MeasureLayoutOnSuccessCallback, // 3592502a MeasureOnSuccessCallback, // 82824e59 - Modal, // 54c1b6fa - ModalBaseProps, // 0c81c9b1 - ModalProps, // 54fbfeca + Modal, // 13daa63e + ModalBaseProps, // cbd3c10d + ModalProps, // b5f48e63 ModalPropsAndroid, // 515fb173 - ModalPropsIOS, // 4fbcedf6 - ModeChangeEvent, // 16790307 - MouseEvent, // 53ede3db + ModalPropsIOS, // 144bbc95 + ModeChangeEvent, // a5e9864f + MouseEvent, // fdce82bc NativeAppEventEmitter, // 08d4c47d NativeColorValue, // d2094c29 - NativeComponentRegistry, // 7fd99ba6 + NativeComponentRegistry, // 21481a60 NativeDialogManagerAndroid, // 5be8497e NativeEventEmitter, // 27f97c1a NativeEventSubscription, // de3942e7 - NativeMethods, // 03dc51c5 - NativeMethodsMixin, // 4b061b7e + NativeMethods, // a2311987 + NativeMethodsMixin, // a819ef55 NativeModules, // 4597cd36 - NativeMouseEvent, // ff25cf35 - NativePointerEvent, // 89c1f3ad + NativeMouseEvent, // 558d45b0 + NativePointerEvent, // f1763d80 NativeScrollEvent, // 431aad2d - NativeSyntheticEvent, // d2a1fe6a + NativeSyntheticEvent, // 35855c4c NativeTouchEvent, // e9fce623 NativeUIEvent, // 44ac26ac Networking, // bbc5be42 OpaqueColorValue, // 25f3fa5b - PanResponder, // 0b859cba - PanResponderCallbacks, // 218dfc6b + PanResponder, // 0b7b1c4d + PanResponderCallbacks, // 7f2a476b PanResponderGestureState, // 54baf558 - PanResponderInstance, // 324d8e4e + PanResponderInstance, // ede3662a Permission, // 06473f4f PermissionStatus, // 4b7de97b PermissionsAndroid, // db2a401e @@ -5986,50 +6059,50 @@ export { PlatformOSType, // 0a17561e PlatformSelectSpec, // 09ed7758 PointValue, // 69db075f - PointerEvent, // ff3129ff - PressabilityConfig, // 33427f4b - PressabilityEventHandlers, // 060ff815 - Pressable, // 27c6d57e + PointerEvent, // fe3989a1 + PressabilityConfig, // 2574ab10 + PressabilityEventHandlers, // 776582b9 + Pressable, // 5558bef0 PressableAndroidRippleConfig, // 42bc9727 - PressableProps, // 9cc9fb3f + PressableProps, // 79dfa095 PressableStateCallbackType, // 9af36561 ProcessedColorValue, // 33f74304 - ProgressBarAndroid, // aea5a105 - ProgressBarAndroidProps, // a06bb11f + ProgressBarAndroid, // d252c33e + ProgressBarAndroidProps, // aaa8ce59 PromiseTask, // 5102c862 PublicRootInstance, // 8040afd7 - PublicTextInstance, // e775d6b1 + PublicTextInstance, // cd0d8f8d PushNotificationEventName, // 84e7e150 PushNotificationIOS, // b4d1fe78 PushNotificationPermissions, // c2e7ae4f Rationale, // 5df1b1c1 ReactNativeVersion, // abd76827 - RefreshControl, // 337f0825 - RefreshControlProps, // 8872284a + RefreshControl, // 2d0f555c + RefreshControlProps, // 9fcacc31 RefreshControlPropsAndroid, // 99f64c97 RefreshControlPropsIOS, // 72a36381 - Registry, // e1ed403e - ResponderSyntheticEvent, // e0d1564d + Registry, // 6c39216d + ResponderSyntheticEvent, // fb10247c ReturnKeyTypeOptions, // afd47ba3 Role, // af7b889d RootTag, // 3cd10504 RootTagContext, // cc287c11 - RootViewStyleProvider, // 267bc77f - Runnable, // 2cb32c54 - Runnables, // d3749ae1 - SafeAreaView, // 786912a4 + RootViewStyleProvider, // f850b4ba + Runnable, // 594dd93a + Runnables, // 4367c557 + SafeAreaView, // 61c1975b ScaledSize, // 07e417c7 - ScrollEvent, // 7d125b6d - ScrollResponderType, // d61f80b6 + ScrollEvent, // 05e71bb1 + ScrollResponderType, // df1eec01 ScrollToLocationParamsType, - ScrollView, // cfaee2fa - ScrollViewImperativeMethods, // bb909014 - ScrollViewProps, // 191ba7c8 + ScrollView, // 8249b0db + ScrollViewImperativeMethods, // 49630f53 + ScrollViewProps, // 897ab911 ScrollViewPropsAndroid, // 44210553 - ScrollViewPropsIOS, // fb759b3a + ScrollViewPropsIOS, // 1b6bf74d ScrollViewScrollToOptions, // 3313411e SectionBase, - SectionList, // d0b27964 + SectionList, // 7a83800c SectionListData, SectionListProps, // d2fcd6f4 SectionListRenderItem, // c118edfb @@ -6048,52 +6121,52 @@ export { StatusBarProps, // 06c98add StatusBarStyle, // 49e3b6de StyleProp, // fa0e9b4a - StyleSheet, // c05b93f7 + StyleSheet, // 70a81e91 SubmitBehavior, // c4ddf490 - Switch, // 3ea74bfa - SwitchChangeEvent, // 2e5bd2de - SwitchProps, // cbbc5145 + Switch, // 041397f1 + SwitchChangeEvent, // 63e9c50b + SwitchProps, // 2b3afc29 Systrace, // b5aa21fc TVViewPropsIOS, // 330ce7b5 TargetedEvent, // 16e98910 TaskProvider, // 266dedf2 - Text, // c072bee5 + Text, // e59e30e2 TextContentType, // 239b3ecc - TextInput, // 307b5d92 + TextInput, // ded60ba4 TextInputAndroidProps, // 3f09ce49 - TextInputChangeEvent, // 6821f629 - TextInputContentSizeChangeEvent, // 5fba3f54 - TextInputEndEditingEvent, // 8c22fac3 - TextInputFocusEvent, // c36e977c + TextInputChangeEvent, // 3ab11bb4 + TextInputContentSizeChangeEvent, // f71f8571 + TextInputEndEditingEvent, // e5f70633 + TextInputFocusEvent, // 020507e6 TextInputIOSProps, // e8905f3e - TextInputKeyPressEvent, // 967178c2 - TextInputProps, // 689ac20e - TextInputSelectionChangeEvent, // a1a7622f - TextInputSubmitEditingEvent, // 48d903af - TextLayoutEvent, // 45b0a8d7 - TextProps, // 0c068ca2 - TextStyle, // 88ac4ff3 + TextInputKeyPressEvent, // 3924ad9b + TextInputProps, // 7a609b6e + TextInputSelectionChangeEvent, // d4d10630 + TextInputSubmitEditingEvent, // 22885c31 + TextLayoutEvent, // 73ab173e + TextProps, // 678ad7a5 + TextStyle, // 10c3af77 ToastAndroid, // 88a8969a - Touchable, // 5fc72701 - TouchableHighlight, // 44011634 - TouchableHighlightProps, // 0dfa37ec - TouchableNativeFeedback, // 50a60ee1 - TouchableNativeFeedbackProps, // 9587d6ed - TouchableOpacity, // 8a962a32 - TouchableOpacityProps, // c4929043 - TouchableWithoutFeedback, // 68ac8437 - TouchableWithoutFeedbackProps, // ca6e7192 + Touchable, // 08e13b64 + TouchableHighlight, // 9c5765b3 + TouchableHighlightProps, // 4738cdb4 + TouchableNativeFeedback, // 86c82bad + TouchableNativeFeedbackProps, // d1768228 + TouchableOpacity, // 1334ca99 + TouchableOpacityProps, // 0a867474 + TouchableWithoutFeedback, // 60e7e7a6 + TouchableWithoutFeedbackProps, // 2200ace0 TransformsStyle, // 65e70f18 TurboModule, // dfe29706 TurboModuleRegistry, // 4ace6db2 UIManager, // a1a7cc01 UTFSequence, // ad625158 Vibration, // 31e4bbf8 - View, // af22d165 - ViewProps, // 7e0720c0 - ViewPropsAndroid, // 6d846811 + View, // 355ac10b + ViewProps, // 5297a1b8 + ViewPropsAndroid, // 9a2823e3 ViewPropsIOS, // 58ee19bf - ViewStyle, // c0170ec0 + ViewStyle, // d7b4d2fe VirtualViewMode, // 6be59722 VirtualizedList, // d4e15eaa VirtualizedListProps, @@ -6101,15 +6174,15 @@ export { VirtualizedSectionListProps, WrapperComponentProvider, // 9cf3844c codegenNativeCommands, // 628a7c0a - codegenNativeComponent, // a733b8b6 - findNodeHandle, // 65981202 + codegenNativeComponent, // 2baac257 + findNodeHandle, // 93f80214 processColor, // 6e877698 registerCallableModule, // 839c8cfe - requireNativeComponent, // 72c09c3d + requireNativeComponent, // 7f7f105a useAnimatedColor, // e3511f81 useAnimatedValue, // b18adb63 useAnimatedValueXY, // c7ee2332 useColorScheme, // c216d6f7 - usePressability, // f0e04ae5 + usePressability, // 1d848b44 useWindowDimensions, // bb4b683f } diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 79ace43b192b..1bb0787274e9 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -5,10 +5,10 @@ PODS: - FBLazyVector (1000.0.0) - fmt (12.1.0) - glog (0.3.5) - - hermes-engine (250829098.0.6): - - hermes-engine/Pre-built (= 250829098.0.6) - - hermes-engine/Pre-built (250829098.0.6) - - MyNativeView (0.85.0-main): + - hermes-engine (250829098.0.12): + - hermes-engine/Pre-built (= 250829098.0.12) + - hermes-engine/Pre-built (250829098.0.12) + - MyNativeView (0.86.0-main): - boost - DoubleConversion - fast_float @@ -36,7 +36,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - NativeCxxModuleExample (0.85.0-main): + - NativeCxxModuleExample (0.86.0-main): - boost - DoubleConversion - fast_float @@ -510,6 +510,7 @@ PODS: - RCTTypeSafety (= 1000.0.0) - React-Core/CoreModulesHeaders (= 1000.0.0) - React-debug + - React-featureflags - React-jsi (= 1000.0.0) - React-jsinspector - React-jsinspectorcdp @@ -543,7 +544,9 @@ PODS: - React-timing (= 1000.0.0) - React-utils - SocketRocket - - React-debug (1000.0.0) + - React-debug (1000.0.0): + - React-debug/redbox (= 1000.0.0) + - React-debug/redbox (1000.0.0) - React-defaultsnativemodule (1000.0.0): - boost - DoubleConversion @@ -554,6 +557,7 @@ PODS: - RCT-Folly - RCT-Folly/Fabric - React-domnativemodule + - React-Fabric/animated - React-featureflags - React-featureflagsnativemodule - React-idlecallbacksnativemodule @@ -561,7 +565,9 @@ PODS: - React-jsi - React-jsiexecutor - React-microtasksnativemodule + - React-mutationobservernativemodule - React-RCTFBReactNativeSpec + - React-viewtransitionnativemodule - React-webperformancenativemodule - SocketRocket - Yoga @@ -617,6 +623,7 @@ PODS: - React-Fabric/scheduler (= 1000.0.0) - React-Fabric/telemetry (= 1000.0.0) - React-Fabric/uimanager (= 1000.0.0) + - React-Fabric/viewtransition (= 1000.0.0) - React-featureflags - React-graphics - React-jsi @@ -1078,6 +1085,7 @@ PODS: - React-graphics - React-jsi - React-jsiexecutor + - React-jsinspectortracing - React-logger - React-rendererdebug - React-runtimeexecutor @@ -1101,6 +1109,7 @@ PODS: - React-debug - React-Fabric/observers/events (= 1000.0.0) - React-Fabric/observers/intersection (= 1000.0.0) + - React-Fabric/observers/mutation (= 1000.0.0) - React-featureflags - React-graphics - React-jsi @@ -1162,6 +1171,31 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket + - React-Fabric/observers/mutation (1000.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket - React-Fabric/scheduler (1000.0.0): - boost - DoubleConversion @@ -1178,6 +1212,7 @@ PODS: - React-debug - React-Fabric/animationbackend - React-Fabric/observers/events + - React-Fabric/viewtransition - React-featureflags - React-graphics - React-jsi @@ -1269,6 +1304,31 @@ PODS: - React-utils - ReactCommon/turbomodule/core - SocketRocket + - React-Fabric/viewtransition (1000.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket - React-FabricComponents (1000.0.0): - boost - DoubleConversion @@ -1692,6 +1752,7 @@ PODS: - React-featureflags - React-jsi - React-jsiexecutor + - React-rendererdebug - React-utils - SocketRocket - React-hermes (1000.0.0): @@ -1863,6 +1924,7 @@ PODS: - React-jsinspectornetwork - React-oscompat - React-timing + - React-utils - SocketRocket - React-jsitooling (1000.0.0): - boost @@ -1917,6 +1979,27 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - SocketRocket + - React-mutationobservernativemodule (1000.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact + - React-Fabric + - React-Fabric/bridging + - React-Fabric/observers/mutation + - React-featureflags + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - React-NativeModulesApple (1000.0.0): - boost - DoubleConversion @@ -2391,6 +2474,24 @@ PODS: - React-debug - React-jsi (= 1000.0.0) - SocketRocket + - React-viewtransitionnativemodule (1000.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-Fabric + - React-Fabric/bridging + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - React-webperformancenativemodule (1000.0.0): - boost - DoubleConversion @@ -2513,7 +2614,7 @@ PODS: - React-perflogger (= 1000.0.0) - React-utils (= 1000.0.0) - SocketRocket - - ScreenshotManager (0.85.0-main): + - ScreenshotManager (0.86.0-main): - boost - DoubleConversion - fast_float @@ -2592,6 +2693,7 @@ DEPENDENCIES: - React-logger (from `../react-native/ReactCommon/logger`) - React-Mapbuffer (from `../react-native/ReactCommon`) - React-microtasksnativemodule (from `../react-native/ReactCommon/react/nativemodule/microtasks`) + - React-mutationobservernativemodule (from `../react-native/ReactCommon/react/nativemodule/mutationobserver`) - React-NativeModulesApple (from `../react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-networking (from `../react-native/ReactCommon/react/networking`) - React-oscompat (from `../react-native/ReactCommon/oscompat`) @@ -2624,6 +2726,7 @@ DEPENDENCIES: - React-runtimescheduler (from `../react-native/ReactCommon/react/renderer/runtimescheduler`) - React-timing (from `../react-native/ReactCommon/react/timing`) - React-utils (from `../react-native/ReactCommon/react/utils`) + - React-viewtransitionnativemodule (from `../react-native/ReactCommon/react/nativemodule/viewtransition`) - React-webperformancenativemodule (from `../react-native/ReactCommon/react/nativemodule/webperformance`) - ReactAppDependencyProvider (from `build/generated/ios/ReactAppDependencyProvider`) - ReactCodegen (from `build/generated/ios/ReactCodegen`) @@ -2653,7 +2756,7 @@ EXTERNAL SOURCES: :podspec: "../react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-v250829098.0.2 + :tag: hermes-v250829098.0.12 MyNativeView: :path: NativeComponentExample NativeCxxModuleExample: @@ -2730,6 +2833,8 @@ EXTERNAL SOURCES: :path: "../react-native/ReactCommon" React-microtasksnativemodule: :path: "../react-native/ReactCommon/react/nativemodule/microtasks" + React-mutationobservernativemodule: + :path: "../react-native/ReactCommon/react/nativemodule/mutationobserver" React-NativeModulesApple: :path: "../react-native/ReactCommon/react/nativemodule/core/platform/ios" React-networking: @@ -2794,6 +2899,8 @@ EXTERNAL SOURCES: :path: "../react-native/ReactCommon/react/timing" React-utils: :path: "../react-native/ReactCommon/react/utils" + React-viewtransitionnativemodule: + :path: "../react-native/ReactCommon/react/nativemodule/viewtransition" React-webperformancenativemodule: :path: "../react-native/ReactCommon/react/nativemodule/webperformance" ReactAppDependencyProvider: @@ -2816,11 +2923,11 @@ SPEC CHECKSUMS: FBLazyVector: 3b0404adfe03965e932e7220d6a9536813ad3204 fmt: 4cf0c5ec5864511c96d8d4bd7b03c3c69040af06 glog: 8fb6f603b9feab8ebedb130c6e0683bbeafa6fb0 - hermes-engine: f5b26fd0636b1662b9111a7e447ed5613e964c09 - MyNativeView: 02c66351be565a8d3d256f4c1f84c72990991746 - NativeCxxModuleExample: 347c3df59c49d2a9c29494f8d387df50c8116867 + hermes-engine: 842b093f838032a4690f06c33da761d509c968fd + MyNativeView: 0190c023cc3664a9122cecb523189fa8e3f7b75c + NativeCxxModuleExample: 1a3b8d5824dc1c5ce2c32475a2d1bedccd22d2f7 OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74 - RCT-Folly: 49542469679b3c59485604badd9af263834c0d81 + RCT-Folly: e10aa376c9d355431790daf20769ccf9d5ac0bb3 RCTDeprecation: 3808e36294137f9ee5668f4df2e73dc079cd1dcf RCTRequired: b93291e183a8a362e06338542c5284f96c5027f1 RCTSwiftUI: dfe01d1ca52f845f42a0a97f9239e88ea644a436 @@ -2829,17 +2936,17 @@ SPEC CHECKSUMS: React: 0d4122b3ec9033c9d7198b90cce79d60c214a98f React-callinvoker: 99b0d301d5abcae2361eeb0925511e1c8a9451db React-Core: 8fad6ae58728bc85da7360c00fbff44ea4ab0fac - React-CoreModules: c81e37f8227c349f22663d5fdbb7d5667d8a23e5 + React-CoreModules: 94e8da4123b86014353789882b31da86f8d73d94 React-cxxreact: 20b559e03c4c5388911b875856088df38b28d04b - React-debug: 744d140d0af0e4d61030b5c9aae07f9c869cd89f - React-defaultsnativemodule: 125005ae37c44b2db5af4a6a661d0d86c20a8ee2 + React-debug: 80b1b69ee446eba705618c97f6ca38c53303f7a2 + React-defaultsnativemodule: 85e802d41b9a1e7f3533fd7c9debc0ece83bbc92 React-domnativemodule: 9868061942d183333b6e2dae39975f3e97642644 - React-Fabric: 11ac7399303152c923444a8fddcf958538d92386 + React-Fabric: ae13370e1867bd312a11948fa0e888c999cd1266 React-FabricComponents: 13abab452d256d4740b93631eee7e56e5ed21290 React-FabricImage: 068487d08af703a7a161eeb7bb38a7057a421fb8 React-featureflags: 276640cbd7120a351c79bceda3ec413a08c66fd7 React-featureflagsnativemodule: aa0ef3bc2fc39f0e805dc3cdd244ab9ae52cb35e - React-graphics: 455b69d55dfa314c51bbe2e3c8209ad71ead417b + React-graphics: 059a9fb5f9a2183bb8d48977c169433681696497 React-hermes: 61eddc2392aff444b1efd3ca3712b26f777c0bdd React-idlecallbacksnativemodule: 497269d1731393b4e7ab2344e189db836025a198 React-ImageManager: 4a80c90798259925ed4fbcd97d1d3a97e9837c1e @@ -2850,12 +2957,13 @@ SPEC CHECKSUMS: React-jsinspector: cdee9660246ab9964b760f20e1226e11fb1b3901 React-jsinspectorcdp: 8ae29329dc21485404b97d89f2f8d77f44cbf7c8 React-jsinspectornetwork: ad6294a34708c65ff5837664c6b60a24465b59aa - React-jsinspectortracing: e5049921e5ea5cd2694fc3509c2692fee57dc379 + React-jsinspectortracing: 02fbd02c80f567fb476c24b8ca945d7e3cc6e04a React-jsitooling: 1bec8ccbf745dbf860629983974dacf99bfcdc24 React-jsitracing: 40dcb2405a22e5745841622034edfa31478188e2 React-logger: 04a822644a9cfded081b47a873e77bbdb6d2bdc1 React-Mapbuffer: c30aa68686be57373b4fd7a9b4f9c85e0a63c7cc React-microtasksnativemodule: b9feb3a80d0e050fc4ddd19adde3e80a17ef9926 + React-mutationobservernativemodule: 56917d894bb7471b9a592d3a7f527aa7ca16a420 React-NativeModulesApple: 265d498f78423f2daa587a44b8e98b3b925e8fb8 React-networking: dc447a407f1499777c4ebc18f0e29ecb94a94b36 React-oscompat: 68d1a1b37e8774beb2d69f37fb5c1e960cb578c8 @@ -2886,16 +2994,17 @@ SPEC CHECKSUMS: React-runtimeexecutor: a0fcd51fbf8a5e6aaba5133ad335f4c02a682ab1 React-RuntimeHermes: 69a9c0dc2a63b31864dcfd6a7b8cf2e689b991e3 React-runtimescheduler: 2e55f1e82a2c55706abb0110141208a4cab59d21 - React-timing: 31fc2373f588237b2b8634fb6f5829d08b908e3f + React-timing: ae50b20d646addd83ba3e760406a4e955521e0fc React-utils: b90942667c022c99a4f927fc7c667c2c3ca8e1a2 + React-viewtransitionnativemodule: ea3c346f3da579fe6e949a6cf641729d0ec2cc08 React-webperformancenativemodule: 71c916497353228e20d0b35b90784f2f76ac2851 ReactAppDependencyProvider: 597ab0ce7df89a422b39250afb8c70fdb059ebab - ReactCodegen: feb1df47e9028f6138ac07a5833a4054b860f0bf + ReactCodegen: 5bf7f46b08421994b1771286e4219c52ed46221d ReactCommon: 7a4b6dfa868f683af3308931520ef5337d4c33dd ReactCommon-Samples: 15d1d4b0f8dea7f29d49c94bf09f47521648504f - ScreenshotManager: a44841b5094dec6f597e208d531c32dc680177e6 + ScreenshotManager: 4cc0ee03e706570714863decfb45c1637711b465 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: fa95e086be59aacbcf8b0422ca2c1235f197615f + Yoga: 675745ecde3e234f1a55aa1fb4b213e1f5c9de3c PODFILE CHECKSUM: 76dda25425afe238cbe0379633645d667998a038 diff --git a/yarn.lock b/yarn.lock index 59c7f1cc8393..c280d0220acf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -111,7 +111,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.28.0, @babel/generator@npm:^7.29.0, @babel/generator@npm:^7.29.1, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.28.0, @babel/generator@npm:^7.29.0, @babel/generator@npm:^7.29.1, @babel/generator@npm:^7.7.2": version: 7.29.1 resolution: "@babel/generator@npm:7.29.1" dependencies: @@ -2668,7 +2668,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.8, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.29.0": +"@babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.8, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.29.0": version: 7.29.0 resolution: "@babel/traverse@npm:7.29.0" dependencies: @@ -4531,7 +4531,7 @@ __metadata: ansi-regex: "npm:^5.0.0" ansi-styles: "npm:^4.2.1" babel-plugin-minify-dead-code-elimination: "npm:^0.5.2" - babel-plugin-syntax-hermes-parser: "npm:0.33.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" babel-plugin-transform-define: "npm:^2.1.4" babel-plugin-transform-flow-enums: "npm:^0.0.2" clang-format: "npm:^1.8.0" @@ -4551,12 +4551,12 @@ __metadata: eslint-plugin-redundant-undefined: "npm:^0.4.0" eslint-plugin-relay: "npm:^1.8.3" fb-dotslash: "npm:0.5.8" - flow-api-translator: "npm:0.33.3" - flow-bin: "npm:^0.303.0" + flow-api-translator: "npm:0.36.0" + flow-bin: "npm:^0.312.0" glob: "npm:^7.1.1" - hermes-eslint: "npm:0.33.3" + hermes-eslint: "npm:0.36.0" hermes-estree: "npm:0.33.3" - hermes-transform: "npm:0.33.3" + hermes-transform: "npm:0.36.0" ini: "npm:^5.0.0" inquirer: "npm:^7.1.0" invariant: "npm:^2.2.4" @@ -4571,16 +4571,16 @@ __metadata: markdownlint-rule-relative-links: "npm:^3.0.0" memfs: "npm:^4.38.2" metro: "npm:^0.84.0" - metro-babel-register: "npm:^0.83.3" + metro-babel-register: "npm:^0.84.3" metro-config: "npm:^0.84.0" metro-memory-fs: "npm:^0.82.5" - metro-transform-plugins: "npm:^0.83.3" + metro-transform-plugins: "npm:^0.84.3" micromatch: "npm:^4.0.4" node-fetch: "npm:^2.2.0" nullthrows: "npm:^1.1.1" nx: "npm:21.4.1" prettier: "npm:3.6.2" - prettier-plugin-hermes-parser: "npm:0.34.1" + prettier-plugin-hermes-parser: "npm:0.36.0" react: "npm:19.2.3" react-test-renderer: "npm:19.2.3" rimraf: "npm:^3.0.2" @@ -4607,7 +4607,7 @@ __metadata: peerDependencies: "@types/react": ^19.2.0 react: "*" - react-native: "*" + react-native: 1000.0.0 peerDependenciesMeta: "@types/react": optional: true @@ -4676,7 +4676,7 @@ __metadata: "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" "@babel/template": "npm:^7.25.0" "@react-native/babel-plugin-codegen": "workspace:*" - babel-plugin-syntax-hermes-parser: "npm:0.33.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" babel-plugin-transform-flow-enums: "npm:^0.0.2" react-refresh: "npm:^0.14.0" peerDependencies: @@ -4719,9 +4719,10 @@ __metadata: "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" "@babel/preset-env": "npm:^7.25.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" glob: "npm:^7.1.1" - hermes-estree: "npm:0.33.3" - hermes-parser: "npm:0.33.3" + hermes-estree: "npm:0.36.0" + hermes-parser: "npm:0.36.0" invariant: "npm:^2.2.4" micromatch: "npm:^4.0.4" nullthrows: "npm:^1.1.1" @@ -4742,10 +4743,10 @@ __metadata: chalk: "npm:^4.0.0" debug: "npm:^4.4.0" invariant: "npm:^2.2.4" - metro: "npm:^0.84.0" - metro-config: "npm:^0.84.0" - metro-core: "npm:^0.84.0" - metro-resolver: "npm:^0.84.0" + metro: "npm:^0.84.3" + metro-config: "npm:^0.84.3" + metro-core: "npm:^0.84.3" + metro-resolver: "npm:^0.84.3" semver: "npm:^7.1.3" peerDependencies: "@react-native-community/cli": "*" @@ -4853,8 +4854,8 @@ __metadata: "@babel/core": "npm:^7.25.2" "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" "@react-native/codegen": "workspace:*" - babel-plugin-syntax-hermes-parser: "npm:0.33.3" - hermes-eslint: "npm:0.33.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" + hermes-eslint: "npm:0.36.0" make-dir: "npm:^2.1.0" pirates: "npm:^4.0.1" source-map-support: "npm:0.5.0" @@ -4865,8 +4866,8 @@ __metadata: version: 0.0.0-use.local resolution: "@react-native/eslint-plugin@workspace:packages/eslint-plugin-react-native" dependencies: - babel-plugin-syntax-hermes-parser: "npm:0.33.3" - hermes-eslint: "npm:0.33.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" + hermes-eslint: "npm:0.36.0" languageName: unknown linkType: soft @@ -4916,7 +4917,7 @@ __metadata: dependencies: "@babel/core": "npm:^7.25.2" "@react-native/babel-preset": "workspace:*" - hermes-parser: "npm:0.33.3" + hermes-parser: "npm:0.36.0" nullthrows: "npm:^1.1.1" peerDependencies: "@babel/core": "*" @@ -4929,8 +4930,8 @@ __metadata: dependencies: "@react-native/js-polyfills": "workspace:*" "@react-native/metro-babel-transformer": "workspace:*" - metro-config: "npm:^0.84.0" - metro-runtime: "npm:^0.84.0" + metro-config: "npm:^0.84.3" + metro-runtime: "npm:^0.84.3" languageName: unknown linkType: soft @@ -4946,7 +4947,7 @@ __metadata: peerDependencies: "@types/react": ^19.1.0 react: "*" - react-native: "*" + react-native: 1000.0.0 peerDependenciesMeta: "@types/react": optional: true @@ -4981,7 +4982,7 @@ __metadata: peerDependencies: "@types/react": ^19.1.0 react: "*" - react-native: "*" + react-native: 1000.0.0 peerDependenciesMeta: "@types/react": optional: true @@ -6727,21 +6728,21 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-hermes-parser@npm:0.32.0": - version: 0.32.0 - resolution: "babel-plugin-syntax-hermes-parser@npm:0.32.0" +"babel-plugin-syntax-hermes-parser@npm:0.35.0": + version: 0.35.0 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.35.0" dependencies: - hermes-parser: "npm:0.32.0" - checksum: 10c0/2e5aad897d4abd643d33329814ed7adb301047890a8a4325ef140da86e377a1127f1ce6af4064526e5cb603c16d3d3e15784998df4095f1385e7f4e8ca53f03e + hermes-parser: "npm:0.35.0" + checksum: 10c0/374a45ec46a624453e1b21938856a0750d8b8f2996e952244cdf9fe863898b5913c7310f54100cc01f39f29b1f769818f93412458ff5f7af5b5ed26c57ce8b83 languageName: node linkType: hard -"babel-plugin-syntax-hermes-parser@npm:0.33.3": - version: 0.33.3 - resolution: "babel-plugin-syntax-hermes-parser@npm:0.33.3" +"babel-plugin-syntax-hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.36.0" dependencies: - hermes-parser: "npm:0.33.3" - checksum: 10c0/61d9f0014b249247e6d5809b638cec4770769a077d3509b8ad575f62c814b28bdd78157dfddf94b040696497c3b78e69cc14793b0b5c15f893c11dc225cc0e3e + hermes-parser: "npm:0.36.0" + checksum: 10c0/3c20d2b2595e890bf7bf14b48a1dc6eb2ad2625e04f942254f3676440e20489eb8266fba8f8077194d294dfb67552b6b969bb40b93c873b88fdc319528dc2606 languageName: node linkType: hard @@ -9310,31 +9311,31 @@ __metadata: languageName: node linkType: hard -"flow-api-translator@npm:0.33.3": - version: 0.33.3 - resolution: "flow-api-translator@npm:0.33.3" +"flow-api-translator@npm:0.36.0": + version: 0.36.0 + resolution: "flow-api-translator@npm:0.36.0" dependencies: "@babel/code-frame": "npm:^7.16.0" "@typescript-eslint/parser": "npm:8.38.0" "@typescript-eslint/visitor-keys": "npm:8.38.0" flow-enums-runtime: "npm:^0.0.6" - hermes-eslint: "npm:0.33.3" - hermes-estree: "npm:0.33.3" - hermes-parser: "npm:0.33.3" - hermes-transform: "npm:0.33.3" + hermes-eslint: "npm:0.36.0" + hermes-estree: "npm:0.36.0" + hermes-parser: "npm:0.36.0" + hermes-transform: "npm:0.36.0" typescript: "npm:5.3.2" peerDependencies: prettier: ^3.0.0 || ^2.7.1 - checksum: 10c0/5e21aef0f6b7209b387c9054abd57eb0a6ea6db915400e98413826bd9f0e4c8319c67e4cd5942f07fed308bbecb085187ee1f044d0f766c209cf83d81e2825c3 + checksum: 10c0/18cd368a37a7afba0f949491d9f30d4db7d9e8b7d778e201f14bfc603cee503386f116ee0cb26b811539fa75830a76c21b1e825509760bc95b280cab00fe49f6 languageName: node linkType: hard -"flow-bin@npm:^0.303.0": - version: 0.303.0 - resolution: "flow-bin@npm:0.303.0" +"flow-bin@npm:^0.312.0": + version: 0.312.1 + resolution: "flow-bin@npm:0.312.1" bin: flow: cli.js - checksum: 10c0/0f3319f4ba62a8dc1b59a5f684d68249e8365cea32dd73c1430bdb82f47579852bc0ff91a153fa76fc9c1f93a9f166232ba14b2f45622b2457f5ea4bb4eb0380 + checksum: 10c0/4ecb72d410065a65414ab4c25766826a57829247bf77ca23c75f701f2577301a8268145fb894022a127e6e64af42270069df900e45ee4b05f9db749b8ee9f574 languageName: node linkType: hard @@ -9931,21 +9932,21 @@ __metadata: languageName: node linkType: hard -"hermes-compiler@npm:250829098.0.6": - version: 250829098.0.6 - resolution: "hermes-compiler@npm:250829098.0.6" - checksum: 10c0/ca565183f28fb6ee079ba1232ab005be8a2af49ac0db45e912b23a90cc44e8202a1f808a059d6c1601bcc9b976caac356639dc59d7eb5897cb2ecb1ef3661a23 +"hermes-compiler@npm:250829098.0.12": + version: 250829098.0.12 + resolution: "hermes-compiler@npm:250829098.0.12" + checksum: 10c0/068efecbc91286393ac52b10b7f6ca3c51884fa4fa500e65aaaa6ba74cc137786ec0ffa8bf7a2f39f27278822246d8108ad63053a88aca5de69fe599ec0028b9 languageName: node linkType: hard -"hermes-eslint@npm:0.33.3": - version: 0.33.3 - resolution: "hermes-eslint@npm:0.33.3" +"hermes-eslint@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-eslint@npm:0.36.0" dependencies: esrecurse: "npm:^4.3.0" - hermes-estree: "npm:0.33.3" - hermes-parser: "npm:0.33.3" - checksum: 10c0/5894dbe52f5de698934d7cba0fda512d685651763df0e436cbe7e354ca3dff19bf57d1738b7a86d29f7482cfaac9791ed77d96f81150a74518ff7370da257bd2 + hermes-estree: "npm:0.36.0" + hermes-parser: "npm:0.36.0" + checksum: 10c0/d1e7c5e4dcb414fe970af560be8c972e16d0d36e8bc2c6b65042c00364b2955ab6991428c80785508a4f956300dbd398e00953a7979e5b20f3ddce459a171923 languageName: node linkType: hard @@ -9963,13 +9964,6 @@ __metadata: languageName: node linkType: hard -"hermes-estree@npm:0.32.0": - version: 0.32.0 - resolution: "hermes-estree@npm:0.32.0" - checksum: 10c0/3b67d1fe44336240ef7f9c40ecbf363279ba263d51efe120570c3862cc109e652fc09aebddfe6b73d0f0246610bee130e4064c359f1f4cbf002bdb1d99717ef2 - languageName: node - linkType: hard - "hermes-estree@npm:0.33.3": version: 0.33.3 resolution: "hermes-estree@npm:0.33.3" @@ -9984,6 +9978,13 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-estree@npm:0.36.0" + checksum: 10c0/fe27f57b0f8c3921e9dc48c517e25f4399609ca386353f90e04d3f859bacbee93184af80a6b930db5e981e8953a5c4083147556b21b19519917e131e7533b27c + languageName: node + linkType: hard + "hermes-parser@npm:0.25.1, hermes-parser@npm:^0.25.1": version: 0.25.1 resolution: "hermes-parser@npm:0.25.1" @@ -9993,15 +9994,6 @@ __metadata: languageName: node linkType: hard -"hermes-parser@npm:0.32.0": - version: 0.32.0 - resolution: "hermes-parser@npm:0.32.0" - dependencies: - hermes-estree: "npm:0.32.0" - checksum: 10c0/5902d2c5d347c0629fba07a47eaad5569590ac69bc8bfb2e454e08d2dfbe1ebd989d88518dca2cba64061689b5eac5960ae6bd15a4a66600bbf377498a3234b7 - languageName: node - linkType: hard - "hermes-parser@npm:0.33.3": version: 0.33.3 resolution: "hermes-parser@npm:0.33.3" @@ -10020,16 +10012,25 @@ __metadata: languageName: node linkType: hard -"hermes-transform@npm:0.33.3": - version: 0.33.3 - resolution: "hermes-transform@npm:0.33.3" +"hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-parser@npm:0.36.0" + dependencies: + hermes-estree: "npm:0.36.0" + checksum: 10c0/76e726366ac2ea91e9464853f439d582ddee9c07ccb84cac35297ce1a5d197bf96b84ab030423a22940b55a0fa5bb191bc0ebeebd95f8e34116900decdccdb86 + languageName: node + linkType: hard + +"hermes-transform@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-transform@npm:0.36.0" dependencies: "@babel/code-frame": "npm:^7.16.0" esquery: "npm:^1.4.0" flow-enums-runtime: "npm:^0.0.6" - hermes-eslint: "npm:0.33.3" - hermes-estree: "npm:0.33.3" - hermes-parser: "npm:0.33.3" + hermes-eslint: "npm:0.36.0" + hermes-estree: "npm:0.36.0" + hermes-parser: "npm:0.36.0" string-width: "npm:4.2.3" peerDependencies: prettier: ^3.0.0 @@ -10037,7 +10038,7 @@ __metadata: peerDependenciesMeta: prettier-plugin-hermes-parser: optional: true - checksum: 10c0/c744ee058a203c1dfc563736b36ab2a6f7cd2d3b2336d0e25dc2548d11aa2603b02edc101bd2a4d3a37f6e36ace4936a3e79b05a69e45428fa0c6feed4c8cd97 + checksum: 10c0/5f93185237aa4a0be3e4bd6ad5d38c51b7e4d4becd3c5c496bd93f236d924c2a5a4f17e285d2c034c5601f5a7e6583761ea5de408e1a91ed756a86a38b6ff7e7 languageName: node linkType: hard @@ -11973,10 +11974,10 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.11, lodash@npm:^4.17.19, lodash@npm:^4.17.21, lodash@npm:~4.17.15": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c +"lodash@npm:4.18.1": + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 languageName: node linkType: hard @@ -12272,9 +12273,9 @@ __metadata: languageName: node linkType: hard -"metro-babel-register@npm:^0.83.3": - version: 0.83.3 - resolution: "metro-babel-register@npm:0.83.3" +"metro-babel-register@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-babel-register@npm:0.84.4" dependencies: "@babel/core": "npm:^7.25.2" "@babel/plugin-proposal-export-namespace-from": "npm:^7.18.9" @@ -12283,11 +12284,11 @@ __metadata: "@babel/preset-typescript": "npm:^7.24.7" "@babel/register": "npm:^7.24.6" babel-plugin-replace-ts-export-assignment: "npm:^0.0.2" - babel-plugin-syntax-hermes-parser: "npm:0.32.0" + babel-plugin-syntax-hermes-parser: "npm:0.35.0" babel-plugin-transform-flow-enums: "npm:^0.0.2" escape-string-regexp: "npm:^1.0.5" flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/657ae8f37050e92b7b0c91fee3adf73e68f43d13925b34fd6c02dd67d4e73f55e4d6f6460e1db3023e8bffea0cd37148b2234f40a21c9d86a59a3a1f83f3b0aa + checksum: 10c0/3ee05f015730e95ece1c60e926efc3e13499c11b8c42f9d4fe929526e3eb92d7c1052c3712c61eed298a83128298c18c9ee5d93b8d55ab2124ab2e6cb8c9281f languageName: node linkType: hard @@ -12316,6 +12317,19 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.84.4": + version: 0.84.4 + resolution: "metro-babel-transformer@npm:0.84.4" + dependencies: + "@babel/core": "npm:^7.25.2" + flow-enums-runtime: "npm:^0.0.6" + hermes-parser: "npm:0.35.0" + metro-cache-key: "npm:0.84.4" + nullthrows: "npm:^1.1.1" + checksum: 10c0/d1ac996666334bc1cfe9d399cbf4cd747b675f6f8f758c2317eebcc52bd76046ed864ddb7b270efeb8cf337940a61fb03912e5c859b7cbc54687c2f5c41a9d2a + languageName: node + linkType: hard + "metro-cache-key@npm:0.83.7": version: 0.83.7 resolution: "metro-cache-key@npm:0.83.7" @@ -12334,6 +12348,15 @@ __metadata: languageName: node linkType: hard +"metro-cache-key@npm:0.84.4": + version: 0.84.4 + resolution: "metro-cache-key@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/a82ab6367f11886d960cc8fa1f3aa54f6529fe30c16059c141c3e789084c50838fdd7e1a5528534cd9c11a74c63aa5c6a7461dbfa50e8c449b6141eaf2fd05e0 + languageName: node + linkType: hard + "metro-cache@npm:0.83.7": version: 0.83.7 resolution: "metro-cache@npm:0.83.7" @@ -12358,6 +12381,18 @@ __metadata: languageName: node linkType: hard +"metro-cache@npm:0.84.4": + version: 0.84.4 + resolution: "metro-cache@npm:0.84.4" + dependencies: + exponential-backoff: "npm:^3.1.1" + flow-enums-runtime: "npm:^0.0.6" + https-proxy-agent: "npm:^7.0.5" + metro-core: "npm:0.84.4" + checksum: 10c0/3bf7f3a1f85b4f1af05f4b2c71c78e56fd3262d967ee43f02e9ff6820254063af33a70b6549e3dc5e993a6a0b9df92e9279632ad9a8b1cde2577342f93df45eb + languageName: node + linkType: hard + "metro-config@npm:0.83.7, metro-config@npm:^0.83.3": version: 0.83.7 resolution: "metro-config@npm:0.83.7" @@ -12390,6 +12425,22 @@ __metadata: languageName: node linkType: hard +"metro-config@npm:0.84.4, metro-config@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-config@npm:0.84.4" + dependencies: + connect: "npm:^3.6.5" + flow-enums-runtime: "npm:^0.0.6" + jest-validate: "npm:^29.7.0" + metro: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-core: "npm:0.84.4" + metro-runtime: "npm:0.84.4" + yaml: "npm:^2.6.1" + checksum: 10c0/f8aaf7d8cff9b486353b62f4746b0a70f99749bd4061f5ae847524aaedcd9c5a34bf176cbbe12fb33e771e8ed3c1496654b2578fa5ba8b9e4f856f0589744d98 + languageName: node + linkType: hard + "metro-core@npm:0.83.7": version: 0.83.7 resolution: "metro-core@npm:0.83.7" @@ -12401,7 +12452,7 @@ __metadata: languageName: node linkType: hard -"metro-core@npm:0.84.0, metro-core@npm:^0.84.0": +"metro-core@npm:0.84.0": version: 0.84.0 resolution: "metro-core@npm:0.84.0" dependencies: @@ -12412,6 +12463,17 @@ __metadata: languageName: node linkType: hard +"metro-core@npm:0.84.4, metro-core@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-core@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + lodash.throttle: "npm:^4.1.1" + metro-resolver: "npm:0.84.4" + checksum: 10c0/19d859de16b5e082c9c31bed981c579a4e6d31a626c7829b725df9ae0ffb755d0ef7809ba9f8adf22d3921f5ffdd931ed77b21b95ca2ea17895f0c99b3cab831 + languageName: node + linkType: hard + "metro-file-map@npm:0.83.7": version: 0.83.7 resolution: "metro-file-map@npm:0.83.7" @@ -12446,6 +12508,23 @@ __metadata: languageName: node linkType: hard +"metro-file-map@npm:0.84.4": + version: 0.84.4 + resolution: "metro-file-map@npm:0.84.4" + dependencies: + debug: "npm:^4.4.0" + fb-watchman: "npm:^2.0.0" + flow-enums-runtime: "npm:^0.0.6" + graceful-fs: "npm:^4.2.4" + invariant: "npm:^2.2.4" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + nullthrows: "npm:^1.1.1" + walker: "npm:^1.0.7" + checksum: 10c0/09ca829570d1d6dc5beb0534da8a7f2bfcae5415b0974fd5f58b4a05da95dbafdd47f7dc8dedeb11b6562ee9a92c4d918466d02a05cda6e1eaf2c400cbbe6fb4 + languageName: node + linkType: hard + "metro-memory-fs@npm:^0.82.5": version: 0.82.5 resolution: "metro-memory-fs@npm:0.82.5" @@ -12475,6 +12554,16 @@ __metadata: languageName: node linkType: hard +"metro-minify-terser@npm:0.84.4": + version: 0.84.4 + resolution: "metro-minify-terser@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + terser: "npm:^5.15.0" + checksum: 10c0/c9b36c2adb8254c38bdedad9da8bf2b7fae7f45cbd883e590430a5fc9cad808af24dd08a9420925e15733dab886528ad553e3eeb3faffc53d3ad80e7e03e5f6d + languageName: node + linkType: hard + "metro-resolver@npm:0.83.7": version: 0.83.7 resolution: "metro-resolver@npm:0.83.7" @@ -12484,7 +12573,7 @@ __metadata: languageName: node linkType: hard -"metro-resolver@npm:0.84.0, metro-resolver@npm:^0.84.0": +"metro-resolver@npm:0.84.0": version: 0.84.0 resolution: "metro-resolver@npm:0.84.0" dependencies: @@ -12493,6 +12582,15 @@ __metadata: languageName: node linkType: hard +"metro-resolver@npm:0.84.4, metro-resolver@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-resolver@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/468334270598222e15cbee32af51a3b5e1f4fa6869794955b95d1134b28a58594e8e3879e841ccf00bbb5cd86c689a4481714d6c6a464931987d5333d2c55f80 + languageName: node + linkType: hard + "metro-runtime@npm:0.83.7": version: 0.83.7 resolution: "metro-runtime@npm:0.83.7" @@ -12503,7 +12601,7 @@ __metadata: languageName: node linkType: hard -"metro-runtime@npm:0.84.0, metro-runtime@npm:^0.84.0": +"metro-runtime@npm:0.84.0": version: 0.84.0 resolution: "metro-runtime@npm:0.84.0" dependencies: @@ -12513,6 +12611,16 @@ __metadata: languageName: node linkType: hard +"metro-runtime@npm:0.84.4, metro-runtime@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-runtime@npm:0.84.4" + dependencies: + "@babel/runtime": "npm:^7.25.0" + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/e2b2e819027940c6bbd081e5650238d52b6c6d78561cd486b8c10cd1e7fce0213c66fa7f885e37ad5377fcd5726b1c9e473fba6de13938cdf2c966e82968c05f + languageName: node + linkType: hard + "metro-source-map@npm:0.83.7": version: 0.83.7 resolution: "metro-source-map@npm:0.83.7" @@ -12530,7 +12638,7 @@ __metadata: languageName: node linkType: hard -"metro-source-map@npm:0.84.0, metro-source-map@npm:^0.84.0": +"metro-source-map@npm:0.84.0": version: 0.84.0 resolution: "metro-source-map@npm:0.84.0" dependencies: @@ -12547,6 +12655,23 @@ __metadata: languageName: node linkType: hard +"metro-source-map@npm:0.84.4, metro-source-map@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-source-map@npm:0.84.4" + dependencies: + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-symbolicate: "npm:0.84.4" + nullthrows: "npm:^1.1.1" + ob1: "npm:0.84.4" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + checksum: 10c0/39df4524022e07aa4b4d09dd874a9509eb9e2e1e491e80a35099020347ab6be2407851b026452296aad314b0eb7ecf14f9b6bab96bd7c31d47d8b1eb30279aaf + languageName: node + linkType: hard + "metro-symbolicate@npm:0.83.7": version: 0.83.7 resolution: "metro-symbolicate@npm:0.83.7" @@ -12579,6 +12704,22 @@ __metadata: languageName: node linkType: hard +"metro-symbolicate@npm:0.84.4": + version: 0.84.4 + resolution: "metro-symbolicate@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-source-map: "npm:0.84.4" + nullthrows: "npm:^1.1.1" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + bin: + metro-symbolicate: src/index.js + checksum: 10c0/416a9ef694150a8ec708187743b74ab67e0b4fec39c64610b3771b584830117670a62acb9aa824f84a44efbb1cfec07aaf943d1aaf349d977eecf7c72bd8c0bf + languageName: node + linkType: hard + "metro-transform-plugins@npm:0.83.7": version: 0.83.7 resolution: "metro-transform-plugins@npm:0.83.7" @@ -12607,17 +12748,17 @@ __metadata: languageName: node linkType: hard -"metro-transform-plugins@npm:^0.83.3": - version: 0.83.3 - resolution: "metro-transform-plugins@npm:0.83.3" +"metro-transform-plugins@npm:0.84.4, metro-transform-plugins@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-transform-plugins@npm:0.84.4" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.25.0" - "@babel/template": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.3" + "@babel/generator": "npm:^7.29.1" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" flow-enums-runtime: "npm:^0.0.6" nullthrows: "npm:^1.1.1" - checksum: 10c0/df3c6db6a69d4888e1b6aad40d48ffec0c3c3faa38e89c07633432fc107ef12c47d55598904c91aadfe0751c5bcb7ec191f8a5ee70c18d253201150fc617ca37 + checksum: 10c0/7edb0c0d3655e9f5f5fb8bd8221ec297394b8730c959a3245ea81e50da8177ad7782f21696201a0dcb922281efd919e9548d5b819d8338e52d4b130f06333123 languageName: node linkType: hard @@ -12663,6 +12804,27 @@ __metadata: languageName: node linkType: hard +"metro-transform-worker@npm:0.84.4": + version: 0.84.4 + resolution: "metro-transform-worker@npm:0.84.4" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + flow-enums-runtime: "npm:^0.0.6" + metro: "npm:0.84.4" + metro-babel-transformer: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-cache-key: "npm:0.84.4" + metro-minify-terser: "npm:0.84.4" + metro-source-map: "npm:0.84.4" + metro-transform-plugins: "npm:0.84.4" + nullthrows: "npm:^1.1.1" + checksum: 10c0/95924f9bcaf6df931bba2783f440d8fab29909bdde8cecdcc3bc7603e7de71e51728a34288f045694b616c94216d1fc683493b8a470e074c9c8a7f220aa9f9b5 + languageName: node + linkType: hard + "metro@npm:0.83.7, metro@npm:^0.83.3": version: 0.83.7 resolution: "metro@npm:0.83.7" @@ -12762,6 +12924,55 @@ __metadata: languageName: node linkType: hard +"metro@npm:0.84.4, metro@npm:^0.84.3": + version: 0.84.4 + resolution: "metro@npm:0.84.4" + dependencies: + "@babel/code-frame": "npm:^7.29.0" + "@babel/core": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + accepts: "npm:^2.0.0" + ci-info: "npm:^2.0.0" + connect: "npm:^3.6.5" + debug: "npm:^4.4.0" + error-stack-parser: "npm:^2.0.6" + flow-enums-runtime: "npm:^0.0.6" + graceful-fs: "npm:^4.2.4" + hermes-parser: "npm:0.35.0" + image-size: "npm:^1.0.2" + invariant: "npm:^2.2.4" + jest-worker: "npm:^29.7.0" + jsc-safe-url: "npm:^0.2.2" + lodash.throttle: "npm:^4.1.1" + metro-babel-transformer: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-cache-key: "npm:0.84.4" + metro-config: "npm:0.84.4" + metro-core: "npm:0.84.4" + metro-file-map: "npm:0.84.4" + metro-resolver: "npm:0.84.4" + metro-runtime: "npm:0.84.4" + metro-source-map: "npm:0.84.4" + metro-symbolicate: "npm:0.84.4" + metro-transform-plugins: "npm:0.84.4" + metro-transform-worker: "npm:0.84.4" + mime-types: "npm:^3.0.1" + nullthrows: "npm:^1.1.1" + serialize-error: "npm:^2.1.0" + source-map: "npm:^0.5.6" + throat: "npm:^5.0.0" + ws: "npm:^7.5.10" + yargs: "npm:^17.6.2" + bin: + metro: src/cli.js + checksum: 10c0/ff92915119db29cd855274f3789d391cba83c50cb92e22d1e9b8c729e7f6d39495e32540a22ca4c6591eea6a847ade49fcfa5faab01b2300227e3f1fc7df359c + languageName: node + linkType: hard + "micromark-core-commonmark@npm:2.0.3, micromark-core-commonmark@npm:^2.0.0": version: 2.0.3 resolution: "micromark-core-commonmark@npm:2.0.3" @@ -13145,6 +13356,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:3.1.4": + version: 3.1.4 + resolution: "minimatch@npm:3.1.4" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/868aab7e5f52570107eb283f021383be111cfeee0817a615f2a9ffe61fdc8fb86d535b9bf169fb8882261e7cb9da22b4d7b6f8b3402037f63558bab173f82212 + languageName: node + linkType: hard + "minimatch@npm:9.0.9": version: 9.0.9 resolution: "minimatch@npm:9.0.9" @@ -13190,15 +13410,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:~3.0.3": - version: 3.0.8 - resolution: "minimatch@npm:3.0.8" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/72b226f452dcfb5075255f53534cb83fc25565b909e79b9be4fad463d735cb1084827f7013ff41d050e77ee6e474408c6073473edd2fb72c2fd630cfb0acc6ad - languageName: node - linkType: hard - "minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -13731,6 +13942,15 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.84.4": + version: 0.84.4 + resolution: "ob1@npm:0.84.4" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/8bf3a3bdc2b27f1b1b60569c31ff2d9d829025f9a1ce7388b5e810242e48672c8d6b24e5972d6e30aef4d84f6894d12b13d0c6c418460d031da1972b96920bba + languageName: node + linkType: hard + "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -14372,12 +14592,12 @@ __metadata: languageName: node linkType: hard -"prettier-plugin-hermes-parser@npm:0.34.1": - version: 0.34.1 - resolution: "prettier-plugin-hermes-parser@npm:0.34.1" +"prettier-plugin-hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "prettier-plugin-hermes-parser@npm:0.36.0" peerDependencies: prettier: ^3.0.0 - checksum: 10c0/191c46fbb484c7ec8b71ad0bf92b60af578af9711f1dc540eaa6d9e73bc482e35a3cedcee4b3699d54e430a8a26d27bc828e101ae364f7b4a8ff0420a0d8ecf7 + checksum: 10c0/2255009a398c560becd4520b2000318f509b9a062269af11d87e47cbe0896c0a1c7e86dad18cfd101a1ceeefb2af753026f9a80d716a2167a2ba01358c6709a0 languageName: node linkType: hard @@ -14684,16 +14904,16 @@ __metadata: abort-controller: "npm:^3.0.0" anser: "npm:^1.4.9" ansi-regex: "npm:^5.0.0" - babel-plugin-syntax-hermes-parser: "npm:0.33.3" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" base64-js: "npm:^1.5.1" commander: "npm:^12.0.0" flow-enums-runtime: "npm:^0.0.6" glob: "npm:^7.1.1" - hermes-compiler: "npm:250829098.0.6" + hermes-compiler: "npm:250829098.0.12" invariant: "npm:^2.2.4" memoize-one: "npm:^5.0.0" - metro-runtime: "npm:^0.84.0" - metro-source-map: "npm:^0.84.0" + metro-runtime: "npm:^0.84.3" + metro-source-map: "npm:^0.84.3" nullthrows: "npm:^1.1.1" pretty-format: "npm:^29.7.0" promise: "npm:^8.3.0" From 990f74cdab684be131a6262e2f4fffeba91abbda Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 06:32:27 -0700 Subject: [PATCH 25/34] chore: audit 0.86 macOS diff tags Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../react-native/React/CoreModules/RCTRedBox+Internal.h | 6 ++++-- .../React/CoreModules/RCTRedBoxController+Internal.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTRedBox+Internal.h b/packages/react-native/React/CoreModules/RCTRedBox+Internal.h index 8d9025d86f6d..65629d7299d7 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBox+Internal.h @@ -14,9 +14,11 @@ @protocol RCTRedBoxControllerActionDelegate -- (void)redBoxController:(RCTPlatformViewController *)redBoxController // [macOS] +// [macOS +- (void)redBoxController:(RCTPlatformViewController *)redBoxController openStackFrameInEditor:(RCTJSStackFrame *)stackFrame; -- (void)reloadFromRedBoxController:(RCTPlatformViewController *)redBoxController; // [macOS] +- (void)reloadFromRedBoxController:(RCTPlatformViewController *)redBoxController; +// macOS] - (void)loadExtraDataViewController; @end diff --git a/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h b/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h index bd917b713cf8..f693663afd04 100644 --- a/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h +++ b/packages/react-native/React/CoreModules/RCTRedBoxController+Internal.h @@ -14,7 +14,7 @@ #if RCT_DEV_MENU @interface RCTRedBoxController - : RCTPlatformViewController < // [macOS + : RCTPlatformViewController < // [macOS] RCTRedBoxControlling, #if !TARGET_OS_OSX // [macOS UITableViewDelegate, From c70caaa049aae85ee6106323233cc9fba10f5335 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 06:53:28 -0700 Subject: [PATCH 26/34] fix(workspaces): align 0.86 parser tooling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .yarnrc.yml | 5 +- package.json | 6 +- private/react-native-fantom/package.json | 4 +- yarn.lock | 261 +---------------------- 4 files changed, 14 insertions(+), 262 deletions(-) diff --git a/.yarnrc.yml b/.yarnrc.yml index a5b9869f637d..da65d046291a 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -36,10 +36,11 @@ packageExtensions: "hermes-estree": "0.29.1" "@react-native/fantom@*": dependencies: + "istanbul-lib-coverage": "^3.2.0" "jest-docblock": "^29.7.0" "jest-message-util": "^29.7.0" - "metro": "^0.84.0" - "metro-config": "^0.84.0" + "metro": "^0.84.3" + "metro-config": "^0.84.3" "source-map": "^0.6.1" "@typescript-eslint/type-utils@*": dependencies: diff --git a/package.json b/package.json index e17b2a362b21..6b04ab88fb68 100644 --- a/package.json +++ b/package.json @@ -142,11 +142,11 @@ "@yarnpkg/types": "^4.0.1", "eslint-plugin-react-hooks": "^5.2.0", "glob": "^7.1.1", - "hermes-estree": "0.33.3", + "hermes-estree": "0.36.0", "invariant": "^2.2.4", "jsonc-eslint-parser": "^2.3.0", - "metro": "^0.84.0", - "metro-config": "^0.84.0", + "metro": "^0.84.3", + "metro-config": "^0.84.3", "metro-memory-fs": "^0.82.5", "nx": "21.4.1", "yargs": "^17.6.2", diff --git a/private/react-native-fantom/package.json b/private/react-native-fantom/package.json index cdfd46b39f51..96bc31822054 100644 --- a/private/react-native-fantom/package.json +++ b/private/react-native-fantom/package.json @@ -9,8 +9,8 @@ }, "dependencies": { "babel-plugin-istanbul": "^7.0.0", - "metro": "^0.84.0", - "metro-babel-transformer": "^0.83.3" + "metro": "^0.84.3", + "metro-babel-transformer": "^0.84.3" }, "peerDependencies": { "jest": "^29.7.0", diff --git a/yarn.lock b/yarn.lock index c280d0220acf..2081be8c913e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4555,7 +4555,7 @@ __metadata: flow-bin: "npm:^0.312.0" glob: "npm:^7.1.1" hermes-eslint: "npm:0.36.0" - hermes-estree: "npm:0.33.3" + hermes-estree: "npm:0.36.0" hermes-transform: "npm:0.36.0" ini: "npm:^5.0.0" inquirer: "npm:^7.1.0" @@ -4570,9 +4570,9 @@ __metadata: markdownlint-cli2: "npm:^0.22.0" markdownlint-rule-relative-links: "npm:^3.0.0" memfs: "npm:^4.38.2" - metro: "npm:^0.84.0" + metro: "npm:^0.84.3" metro-babel-register: "npm:^0.84.3" - metro-config: "npm:^0.84.0" + metro-config: "npm:^0.84.3" metro-memory-fs: "npm:^0.82.5" metro-transform-plugins: "npm:^0.84.3" micromatch: "npm:^4.0.4" @@ -4876,8 +4876,8 @@ __metadata: resolution: "@react-native/fantom@workspace:private/react-native-fantom" dependencies: babel-plugin-istanbul: "npm:^7.0.0" - metro: "npm:^0.84.0" - metro-babel-transformer: "npm:^0.83.3" + metro: "npm:^0.84.3" + metro-babel-transformer: "npm:^0.84.3" peerDependencies: jest: ^29.7.0 jest-snapshot: ^29.7.0 @@ -9964,13 +9964,6 @@ __metadata: languageName: node linkType: hard -"hermes-estree@npm:0.33.3": - version: 0.33.3 - resolution: "hermes-estree@npm:0.33.3" - checksum: 10c0/4e04e767a706a93c59d64ef3f114075aeb93b08433655d4f11d310f0785c2a74d5b5041b80bc34d22630dece54865dd93a53fde160d48b8369cfef10dbd0520b - languageName: node - linkType: hard - "hermes-estree@npm:0.35.0": version: 0.35.0 resolution: "hermes-estree@npm:0.35.0" @@ -9994,15 +9987,6 @@ __metadata: languageName: node linkType: hard -"hermes-parser@npm:0.33.3": - version: 0.33.3 - resolution: "hermes-parser@npm:0.33.3" - dependencies: - hermes-estree: "npm:0.33.3" - checksum: 10c0/f7d69de54c77321d8481e37a323bbac01d180ec982275ef8925ceaaf7e501fc3062593e84cf5da50852f36daffb34d0f5d6cbbef079fd0125a7b91c1fe84f225 - languageName: node - linkType: hard - "hermes-parser@npm:0.35.0": version: 0.35.0 resolution: "hermes-parser@npm:0.35.0" @@ -12305,19 +12289,7 @@ __metadata: languageName: node linkType: hard -"metro-babel-transformer@npm:0.84.0": - version: 0.84.0 - resolution: "metro-babel-transformer@npm:0.84.0" - dependencies: - "@babel/core": "npm:^7.25.2" - flow-enums-runtime: "npm:^0.0.6" - hermes-parser: "npm:0.33.3" - nullthrows: "npm:^1.1.1" - checksum: 10c0/8446a23594e7a59a3739d14f3011d2193292ded93c074094127f58e87e197199edf6cf6fe798e9f9dc51a30bb6bdb68e5a24f73c8d362e9ff0795110182c3b42 - languageName: node - linkType: hard - -"metro-babel-transformer@npm:0.84.4": +"metro-babel-transformer@npm:0.84.4, metro-babel-transformer@npm:^0.84.3": version: 0.84.4 resolution: "metro-babel-transformer@npm:0.84.4" dependencies: @@ -12339,15 +12311,6 @@ __metadata: languageName: node linkType: hard -"metro-cache-key@npm:0.84.0": - version: 0.84.0 - resolution: "metro-cache-key@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/800b898de31981e974d9d63ac74dc9d81532fe4d1e0b60db9ab61c769a57f12c22743a38cb736365c39782d87c834f08a3cf67eee1d5bdb5dc9abbe269403b83 - languageName: node - linkType: hard - "metro-cache-key@npm:0.84.4": version: 0.84.4 resolution: "metro-cache-key@npm:0.84.4" @@ -12369,18 +12332,6 @@ __metadata: languageName: node linkType: hard -"metro-cache@npm:0.84.0": - version: 0.84.0 - resolution: "metro-cache@npm:0.84.0" - dependencies: - exponential-backoff: "npm:^3.1.1" - flow-enums-runtime: "npm:^0.0.6" - https-proxy-agent: "npm:^7.0.5" - metro-core: "npm:0.84.0" - checksum: 10c0/f3927b945acc3cab191a52f7f9f0d607569d6b49541f5c552a42274347c5afab6d31cf673cff057595e7a3403f83ced76fb2843f736ab40aa213bfe607769185 - languageName: node - linkType: hard - "metro-cache@npm:0.84.4": version: 0.84.4 resolution: "metro-cache@npm:0.84.4" @@ -12409,22 +12360,6 @@ __metadata: languageName: node linkType: hard -"metro-config@npm:0.84.0, metro-config@npm:^0.84.0": - version: 0.84.0 - resolution: "metro-config@npm:0.84.0" - dependencies: - connect: "npm:^3.6.5" - flow-enums-runtime: "npm:^0.0.6" - jest-validate: "npm:^29.7.0" - metro: "npm:0.84.0" - metro-cache: "npm:0.84.0" - metro-core: "npm:0.84.0" - metro-runtime: "npm:0.84.0" - yaml: "npm:^2.6.1" - checksum: 10c0/b6d13b833c157893405fd71c59edd984d4ec591f01de061a7248df220780a88c105b8aedc3efb541df5a0508e2ede9869b996323b9d5fb87b9fca484cd1b0737 - languageName: node - linkType: hard - "metro-config@npm:0.84.4, metro-config@npm:^0.84.3": version: 0.84.4 resolution: "metro-config@npm:0.84.4" @@ -12452,17 +12387,6 @@ __metadata: languageName: node linkType: hard -"metro-core@npm:0.84.0": - version: 0.84.0 - resolution: "metro-core@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - lodash.throttle: "npm:^4.1.1" - metro-resolver: "npm:0.84.0" - checksum: 10c0/2aaf290c8062f625048da32d6fe8fbbeeee67111f7275d557fb9a98a7d970efb617d5f4d7320ec371fb4e814d525504597717764d9ff7690e72092fc14fdd01d - languageName: node - linkType: hard - "metro-core@npm:0.84.4, metro-core@npm:^0.84.3": version: 0.84.4 resolution: "metro-core@npm:0.84.4" @@ -12491,23 +12415,6 @@ __metadata: languageName: node linkType: hard -"metro-file-map@npm:0.84.0": - version: 0.84.0 - resolution: "metro-file-map@npm:0.84.0" - dependencies: - debug: "npm:^4.4.0" - fb-watchman: "npm:^2.0.0" - flow-enums-runtime: "npm:^0.0.6" - graceful-fs: "npm:^4.2.4" - invariant: "npm:^2.2.4" - jest-worker: "npm:^29.7.0" - micromatch: "npm:^4.0.4" - nullthrows: "npm:^1.1.1" - walker: "npm:^1.0.7" - checksum: 10c0/82943c21f155d4a354d6af8c82e0231e5ce74ad7fbd019f68e69200feb1c630b5137ddb60abede57eb3584ff23213c2f31c6863bca3cb4f54284b798120a0967 - languageName: node - linkType: hard - "metro-file-map@npm:0.84.4": version: 0.84.4 resolution: "metro-file-map@npm:0.84.4" @@ -12544,16 +12451,6 @@ __metadata: languageName: node linkType: hard -"metro-minify-terser@npm:0.84.0": - version: 0.84.0 - resolution: "metro-minify-terser@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - terser: "npm:^5.15.0" - checksum: 10c0/6e4504b3b6b8ca34d2ae92c79a1c142422c39d673af3c457f3caa4b61de9192c5df3ca6ba3ee5e809a9d44cc6dcfc990c03206f3c50a49a9493cde22f642a304 - languageName: node - linkType: hard - "metro-minify-terser@npm:0.84.4": version: 0.84.4 resolution: "metro-minify-terser@npm:0.84.4" @@ -12573,15 +12470,6 @@ __metadata: languageName: node linkType: hard -"metro-resolver@npm:0.84.0": - version: 0.84.0 - resolution: "metro-resolver@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/b6bd7abd752829fed467e85e0c5bfcae42cd00cbde4e0ad29bbc1be79b3cbe109012b38992e54b87ea7f6729c18f1bfad3da65d031227728c76ba43ee739984e - languageName: node - linkType: hard - "metro-resolver@npm:0.84.4, metro-resolver@npm:^0.84.3": version: 0.84.4 resolution: "metro-resolver@npm:0.84.4" @@ -12601,16 +12489,6 @@ __metadata: languageName: node linkType: hard -"metro-runtime@npm:0.84.0": - version: 0.84.0 - resolution: "metro-runtime@npm:0.84.0" - dependencies: - "@babel/runtime": "npm:^7.25.0" - flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/a995c45d09fbcff56c8053efecbfb93503822a8c636387444f8104382b0447694a44389703b9545b06c6ce1c1f1b6b515ea47cf9f6cf6da5e92858e2000fb9f5 - languageName: node - linkType: hard - "metro-runtime@npm:0.84.4, metro-runtime@npm:^0.84.3": version: 0.84.4 resolution: "metro-runtime@npm:0.84.4" @@ -12638,23 +12516,6 @@ __metadata: languageName: node linkType: hard -"metro-source-map@npm:0.84.0": - version: 0.84.0 - resolution: "metro-source-map@npm:0.84.0" - dependencies: - "@babel/traverse": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" - flow-enums-runtime: "npm:^0.0.6" - invariant: "npm:^2.2.4" - metro-symbolicate: "npm:0.84.0" - nullthrows: "npm:^1.1.1" - ob1: "npm:0.84.0" - source-map: "npm:^0.5.6" - vlq: "npm:^1.0.0" - checksum: 10c0/49cf3a233380eb08f0e63482e36fce37c789eec0f9657450cfdefba6eadb1e851d970ef73fc81d63038963762dfd14e7b5f576f2a10b922d9f6c39c886cedcf0 - languageName: node - linkType: hard - "metro-source-map@npm:0.84.4, metro-source-map@npm:^0.84.3": version: 0.84.4 resolution: "metro-source-map@npm:0.84.4" @@ -12688,22 +12549,6 @@ __metadata: languageName: node linkType: hard -"metro-symbolicate@npm:0.84.0": - version: 0.84.0 - resolution: "metro-symbolicate@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - invariant: "npm:^2.2.4" - metro-source-map: "npm:0.84.0" - nullthrows: "npm:^1.1.1" - source-map: "npm:^0.5.6" - vlq: "npm:^1.0.0" - bin: - metro-symbolicate: src/index.js - checksum: 10c0/e6bb09f0ad55d037516fc593495e559ce705f6686b150183b882a156c10cee363258c81d16af1c47de9a14be96c5eb869301988a5846807c5f6dba0b698994eb - languageName: node - linkType: hard - "metro-symbolicate@npm:0.84.4": version: 0.84.4 resolution: "metro-symbolicate@npm:0.84.4" @@ -12734,20 +12579,6 @@ __metadata: languageName: node linkType: hard -"metro-transform-plugins@npm:0.84.0": - version: 0.84.0 - resolution: "metro-transform-plugins@npm:0.84.0" - dependencies: - "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.29.1" - "@babel/template": "npm:^7.28.6" - "@babel/traverse": "npm:^7.29.0" - flow-enums-runtime: "npm:^0.0.6" - nullthrows: "npm:^1.1.1" - checksum: 10c0/a337a36033dda93acc82c50862d7dc4e70d485ae765d15884e19202826d734eff4f73aabc6e9b25d969ce910b0117371cb49924818506053af9f9d95a3ea09fe - languageName: node - linkType: hard - "metro-transform-plugins@npm:0.84.4, metro-transform-plugins@npm:^0.84.3": version: 0.84.4 resolution: "metro-transform-plugins@npm:0.84.4" @@ -12783,27 +12614,6 @@ __metadata: languageName: node linkType: hard -"metro-transform-worker@npm:0.84.0": - version: 0.84.0 - resolution: "metro-transform-worker@npm:0.84.0" - dependencies: - "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.29.1" - "@babel/parser": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" - flow-enums-runtime: "npm:^0.0.6" - metro: "npm:0.84.0" - metro-babel-transformer: "npm:0.84.0" - metro-cache: "npm:0.84.0" - metro-cache-key: "npm:0.84.0" - metro-minify-terser: "npm:0.84.0" - metro-source-map: "npm:0.84.0" - metro-transform-plugins: "npm:0.84.0" - nullthrows: "npm:^1.1.1" - checksum: 10c0/c06619dc2f618d9366cb61b9f072dc1f939447c9bf0328d439c3e806772730b24557169ae60c46510332d2c4ef369e0a9d83891af3240dda7bfa3754a36387d3 - languageName: node - linkType: hard - "metro-transform-worker@npm:0.84.4": version: 0.84.4 resolution: "metro-transform-worker@npm:0.84.4" @@ -12874,56 +12684,6 @@ __metadata: languageName: node linkType: hard -"metro@npm:0.84.0, metro@npm:^0.84.0": - version: 0.84.0 - resolution: "metro@npm:0.84.0" - dependencies: - "@babel/code-frame": "npm:^7.29.0" - "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.29.1" - "@babel/parser": "npm:^7.29.0" - "@babel/template": "npm:^7.28.6" - "@babel/traverse": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" - accepts: "npm:^2.0.0" - chalk: "npm:^4.0.0" - ci-info: "npm:^2.0.0" - connect: "npm:^3.6.5" - debug: "npm:^4.4.0" - error-stack-parser: "npm:^2.0.6" - flow-enums-runtime: "npm:^0.0.6" - graceful-fs: "npm:^4.2.4" - hermes-parser: "npm:0.33.3" - image-size: "npm:^1.0.2" - invariant: "npm:^2.2.4" - jest-worker: "npm:^29.7.0" - jsc-safe-url: "npm:^0.2.2" - lodash.throttle: "npm:^4.1.1" - metro-babel-transformer: "npm:0.84.0" - metro-cache: "npm:0.84.0" - metro-cache-key: "npm:0.84.0" - metro-config: "npm:0.84.0" - metro-core: "npm:0.84.0" - metro-file-map: "npm:0.84.0" - metro-resolver: "npm:0.84.0" - metro-runtime: "npm:0.84.0" - metro-source-map: "npm:0.84.0" - metro-symbolicate: "npm:0.84.0" - metro-transform-plugins: "npm:0.84.0" - metro-transform-worker: "npm:0.84.0" - mime-types: "npm:^3.0.1" - nullthrows: "npm:^1.1.1" - serialize-error: "npm:^2.1.0" - source-map: "npm:^0.5.6" - throat: "npm:^5.0.0" - ws: "npm:^7.5.10" - yargs: "npm:^17.6.2" - bin: - metro: src/cli.js - checksum: 10c0/24def0672ad99a4d5865ff881af53580ed25f413689e142f206d72b9894b2493f394afb006cc23e593ddb9a50b71c198c495b11b53329cdf4690389d817aea23 - languageName: node - linkType: hard - "metro@npm:0.84.4, metro@npm:^0.84.3": version: 0.84.4 resolution: "metro@npm:0.84.4" @@ -13933,15 +13693,6 @@ __metadata: languageName: node linkType: hard -"ob1@npm:0.84.0": - version: 0.84.0 - resolution: "ob1@npm:0.84.0" - dependencies: - flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/4c8da63af5d69d172b2d12df67d2f5dbbb13c74ca940811941df9b92fbf62350f2a50cd00f3e5ef26dd902b2203c4e6c31a9e146e6ed267b13a1da5b32fe25b5 - languageName: node - linkType: hard - "ob1@npm:0.84.4": version: 0.84.4 resolution: "ob1@npm:0.84.4" From 255187d6d450f96eb9520d8de8fadcb2a236d1e3 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 07:23:27 -0700 Subject: [PATCH 27/34] fix(macos): port 0.86 native ownership Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../RCTBackedTextInputDelegateAdapter.mm | 232 ++++++++++++++++-- .../ReactCommon/React-Fabric.podspec | 11 +- 2 files changed, 222 insertions(+), 21 deletions(-) diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm index 9633ab9947a5..5ce7f0eca764 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm +++ b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.mm @@ -6,11 +6,17 @@ */ #import +// [macOS +#import "RCTBackedTextInputViewProtocol.h" +#import "RCTBackedTextInputDelegate.h" +#import +// macOS] #pragma mark - RCTBackedTextFieldDelegateAdapter (for UITextField) static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingContext; +#if !TARGET_OS_OSX // [macOS] @interface RCTBackedTextFieldDelegateAdapter () < UITextFieldDelegate #if !TARGET_OS_TV @@ -18,12 +24,19 @@ @interface RCTBackedTextFieldDelegateAdapter () < UITextDropDelegate #endif > +#else // [macOS +@interface RCTBackedTextFieldDelegateAdapter () +#endif // macOS] @end @implementation RCTBackedTextFieldDelegateAdapter { __weak UITextField *_backedTextInputView; BOOL _textDidChangeIsComing; +#if !TARGET_OS_OSX // [macOS] UITextRange *_previousSelectedTextRange; +#else // [macOS + NSRange _previousSelectedTextRange; +#endif // macOS] } - (instancetype)initWithTextField:(UITextField *)backedTextInputView @@ -31,16 +44,18 @@ - (instancetype)initWithTextField:(UITextField * if (self = [super init]) { _backedTextInputView = backedTextInputView; backedTextInputView.delegate = self; -#if !TARGET_OS_TV +#if !TARGET_OS_OSX && !TARGET_OS_TV // [macOS] backedTextInputView.textDropDelegate = self; -#endif +#endif // [macOS] +#if !TARGET_OS_OSX // [macOS] [_backedTextInputView addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged]; [_backedTextInputView addTarget:self action:@selector(textFieldDidEndEditingOnExit) forControlEvents:UIControlEventEditingDidEndOnExit]; +#endif // [macOS] } return self; @@ -48,8 +63,10 @@ - (instancetype)initWithTextField:(UITextField * - (void)dealloc { +#if !TARGET_OS_OSX // [macOS] [_backedTextInputView removeTarget:self action:nil forControlEvents:UIControlEventEditingChanged]; [_backedTextInputView removeTarget:self action:nil forControlEvents:UIControlEventEditingDidEndOnExit]; +#endif // [macOS] } #pragma mark - UITextFieldDelegate @@ -109,12 +126,14 @@ - (BOOL)textField:(__unused UITextField *)textField [attributedString replaceCharactersInRange:range withString:newText]; [_backedTextInputView setAttributedText:[attributedString copy]]; +#if !TARGET_OS_OSX // [macOS] // Setting selection to the end of the replaced text. UITextPosition *position = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument offset:(range.location + newText.length)]; [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; +#endif // [macOS] [self textFieldDidChange]; return NO; @@ -155,7 +174,11 @@ - (BOOL)keyboardInputShouldDelete:(__unused UITextField *)textField #pragma mark - Public Interface +#if !TARGET_OS_OSX // [macOS] - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange +#else // [macOS +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange +#endif // macOS] { _previousSelectedTextRange = textRange; } @@ -169,16 +192,15 @@ - (void)selectedTextRangeWasSet - (void)textFieldProbablyDidChangeSelection { - if ([_backedTextInputView.selectedTextRange isEqual:_previousSelectedTextRange]) { + if (RCTTextSelectionEqual([_backedTextInputView selectedTextRange], _previousSelectedTextRange)) { // [macOS] return; } - _previousSelectedTextRange = _backedTextInputView.selectedTextRange; + _previousSelectedTextRange = [_backedTextInputView selectedTextRange]; // [macOS] setter not defined for mac [_backedTextInputView.textInputDelegate textInputDidChangeSelection]; } -#if !TARGET_OS_TV - +#if !TARGET_OS_OSX && !TARGET_OS_TV // [macOS] #pragma mark - UITextDropDelegate - (UITextDropEditability)textDroppableView:(UIView *)textDroppableView @@ -214,13 +236,90 @@ - (bool)_shouldAcceptDrop:(id)drop return true; } } +#endif // [macOS] + +#if TARGET_OS_OSX // [macOS + +#pragma mark - NSTextFieldDelegate + +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor +{ + return [self textFieldShouldEndEditing:_backedTextInputView]; +} + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector +{ + id textInputDelegate = [_backedTextInputView textInputDelegate]; + BOOL commandHandled = NO; + // enter/return + if (commandSelector == @selector(insertNewline:) || commandSelector == @selector(insertNewlineIgnoringFieldEditor:)) { + [self textFieldDidEndEditingOnExit]; + if ([textInputDelegate textInputShouldSubmitOnReturn]) { + [[_backedTextInputView window] makeFirstResponder:nil]; + } + commandHandled = YES; + //backspace + } else if (commandSelector == @selector(deleteBackward:)) { + if (textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteBackward:_backedTextInputView]) { + commandHandled = YES; + } else { + [self keyboardInputShouldDelete:_backedTextInputView]; + } + //deleteForward + } else if (commandSelector == @selector(deleteForward:)) { + id textInputDelegate = [_backedTextInputView textInputDelegate]; + if (textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteForward:_backedTextInputView]) { + commandHandled = YES; + } else { + [self keyboardInputShouldDelete:_backedTextInputView]; + } + //paste + } else if (commandSelector == @selector(paste:)) { + id textInputDelegate = [_backedTextInputView textInputDelegate]; + if (textInputDelegate != nil && ![textInputDelegate textInputShouldHandlePaste:_backedTextInputView]) { + commandHandled = YES; + } else { + _backedTextInputView.textWasPasted = YES; + } + //escape + } else if (commandSelector == @selector(cancelOperation:)) { + [textInputDelegate textInputDidCancel]; + if (![textInputDelegate hasKeyDownEventOrKeyUpEvent:@"Escape"]) { + [[_backedTextInputView window] makeFirstResponder:nil]; + } + commandHandled = YES; +} + + return commandHandled; +} + +- (void)textFieldBeginEditing:(NSTextField *)textField +{ + [self textFieldDidBeginEditing:_backedTextInputView]; +} + +- (void)textFieldDidChange:(NSTextField *)textField +{ + [self textFieldDidChange]; +} + +- (void)textFieldEndEditing:(NSTextField *)textField +{ + [self textFieldDidEndEditing:_backedTextInputView]; +} + +- (void)textFieldDidChangeSelection:(NSTextField *)textField +{ + [self selectedTextRangeWasSet]; +} +#endif // macOS] -#endif @end #pragma mark - RCTBackedTextViewDelegateAdapter (for UITextView) +#if !TARGET_OS_OSX // [macOS] @interface RCTBackedTextViewDelegateAdapter () < UITextViewDelegate #if !TARGET_OS_TV @@ -228,14 +327,26 @@ @interface RCTBackedTextViewDelegateAdapter () < UITextDropDelegate #endif > +#else // [macOS +@interface RCTBackedTextViewDelegateAdapter () +#endif // macOS] @end @implementation RCTBackedTextViewDelegateAdapter { +#if !TARGET_OS_OSX // [macOS] __weak UITextView *_backedTextInputView; +#else // [macOS + __unsafe_unretained UITextView *_backedTextInputView; +#endif // macOS] NSAttributedString *_lastStringStateWasUpdatedWith; BOOL _ignoreNextTextInputCall; BOOL _textDidChangeIsComing; +#if !TARGET_OS_OSX // [macOS] UITextRange *_previousSelectedTextRange; +#else // [macOS + NSRange _previousSelectedTextRange; + NSUndoManager *_undoManager; +#endif // macOS] } - (instancetype)initWithTextView:(UITextView *)backedTextInputView @@ -243,9 +354,9 @@ - (instancetype)initWithTextView:(UITextView *)b if (self = [super init]) { _backedTextInputView = backedTextInputView; backedTextInputView.delegate = self; -#if !TARGET_OS_TV +#if !TARGET_OS_OSX && !TARGET_OS_TV // [macOS] backedTextInputView.textDropDelegate = self; -#endif +#endif // [macOS] } return self; @@ -282,6 +393,7 @@ - (void)textViewDidEndEditing:(__unused UITextView *)textView - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { +#if !TARGET_OS_OSX // [macOS] // Custom implementation of `textInputShouldReturn` and `textInputDidReturn` pair for `UITextView`. if (!_backedTextInputView.textWasPasted && [text isEqualToString:@"\n"]) { const BOOL shouldSubmit = [_backedTextInputView.textInputDelegate textInputShouldSubmitOnReturn]; @@ -294,6 +406,7 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang return NO; } } +#endif // [macOS] NSString *newText = [_backedTextInputView.textInputDelegate textInputShouldChangeText:text inRange:range]; @@ -301,6 +414,11 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang return NO; } + if ([newText isEqualToString:text]) { + _textDidChangeIsComing = YES; + return YES; + } + // Clamp the range to the current text length to avoid NSRangeException. NSUInteger textLength = _backedTextInputView.attributedText.length; if (range.location > textLength) { @@ -308,20 +426,19 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang } if (range.location + range.length > textLength) { range = NSMakeRange(range.location, textLength - range.location); - } else if ([newText isEqualToString:text]) { - _textDidChangeIsComing = YES; - return YES; } NSMutableAttributedString *attributedString = [_backedTextInputView.attributedText mutableCopy]; [attributedString replaceCharactersInRange:range withString:newText]; [_backedTextInputView setAttributedText:[attributedString copy]]; +#if !TARGET_OS_OSX // [macOS] // Setting selection to the end of the replaced text. UITextPosition *position = [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument offset:(range.location + newText.length)]; [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; +#endif // [macOS] [self textViewDidChange:_backedTextInputView]; @@ -338,28 +455,106 @@ - (void)textViewDidChange:(__unused UITextView *)textView [_backedTextInputView.textInputDelegate textInputDidChange]; } +#if !TARGET_OS_OSX // [macOS] + - (void)textViewDidChangeSelection:(__unused UITextView *)textView { if (_lastStringStateWasUpdatedWith && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { [self textViewDidChange:_backedTextInputView]; - _ignoreNextTextInputCall = YES; + + if (![_backedTextInputView isGhostTextChanging]) { // [macOS] + _ignoreNextTextInputCall = YES; + } // [macOS] } _lastStringStateWasUpdatedWith = _backedTextInputView.attributedText; [self textViewProbablyDidChangeSelection]; } +#endif // [macOS] + #pragma mark - UIScrollViewDelegate -- (void)scrollViewDidScroll:(UIScrollView *)scrollView +- (void)scrollViewDidScroll:(RCTUIScrollView *)scrollView // [macOS] { if ([_backedTextInputView.textInputDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) { [_backedTextInputView.textInputDelegate scrollViewDidScroll:scrollView]; } } +#if TARGET_OS_OSX // [macOS + +#pragma mark - NSTextViewDelegate + +- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(nullable NSString *)replacementString +{ + return [self textView:textView shouldChangeTextInRange:affectedCharRange replacementText:replacementString]; +} + +- (void)textViewDidChangeSelection:(NSNotification *)notification +{ + [self textViewProbablyDidChangeSelection]; +} + +- (void)textDidBeginEditing:(NSNotification *)notification +{ + [self textViewDidBeginEditing:_backedTextInputView]; +} + +- (void)textDidChange:(NSNotification *)notification +{ + [self textViewDidChange:_backedTextInputView]; +} + +- (void)textDidEndEditing:(NSNotification *)notification +{ + [self textViewDidEndEditing:_backedTextInputView]; +} + +- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector +{ + BOOL commandHandled = NO; + id textInputDelegate = [_backedTextInputView textInputDelegate]; + // enter/return + if ((commandSelector == @selector(insertNewline:) || commandSelector == @selector(insertNewlineIgnoringFieldEditor:))) { + if ([textInputDelegate textInputShouldSubmitOnReturn]) { + [_backedTextInputView.window makeFirstResponder:nil]; + commandHandled = YES; + } + //backspace + } else if (commandSelector == @selector(deleteBackward:)) { + commandHandled = textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteBackward:_backedTextInputView]; + //deleteForward + } else if (commandSelector == @selector(deleteForward:)) { + commandHandled = textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteForward:_backedTextInputView]; + //escape + } else if (commandSelector == @selector(cancelOperation:)) { + [textInputDelegate textInputDidCancel]; + if (![textInputDelegate hasKeyDownEventOrKeyUpEvent:@"Escape"]) { + [[_backedTextInputView window] makeFirstResponder:nil]; + } + commandHandled = YES; + + } + + return commandHandled; +} + +- (NSUndoManager *)undoManagerForTextView:(NSTextView *)textView { + if (!_undoManager) { + _undoManager = [NSUndoManager new]; + } + return _undoManager; +} + +#endif // macOS] + #pragma mark - Public Interface +#if !TARGET_OS_OSX // [macOS] - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange +#else // [macOS +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange +#endif // macOS] { _previousSelectedTextRange = textRange; } @@ -368,16 +563,15 @@ - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)tex - (void)textViewProbablyDidChangeSelection { - if ([_backedTextInputView.selectedTextRange isEqual:_previousSelectedTextRange]) { + if (RCTTextSelectionEqual([_backedTextInputView selectedTextRange], _previousSelectedTextRange)) { // [macOS] return; } - _previousSelectedTextRange = _backedTextInputView.selectedTextRange; + _previousSelectedTextRange = [_backedTextInputView selectedTextRange]; // [macOS] [_backedTextInputView.textInputDelegate textInputDidChangeSelection]; } -#if !TARGET_OS_TV - +#if !TARGET_OS_OSX && !TARGET_OS_TV // [macOS] #pragma mark - UITextDropDelegate - (UITextDropEditability)textDroppableView:(UIView *)textDroppableView @@ -413,7 +607,7 @@ - (bool)_shouldAcceptDrop:(id)drop return true; } } +#endif // [macOS] -#endif @end diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index 61de53ace5d2..bf8e0cd0bdc0 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -133,8 +133,15 @@ Pod::Spec.new do |s| ss.subspec "view" do |sss| sss.dependency "React-renderercss" sss.dependency "Yoga" - sss.source_files = "react/renderer/components/view/**/*.{m,mm,cpp,h}" # [macOS] - sss.exclude_files = "react/renderer/components/view/tests", "react/renderer/components/view/platform/android", "react/renderer/components/view/platform/windows" # [macOS] + # [macOS + sss.source_files = "react/renderer/components/view/**/*.{m,mm,cpp,h}" + sss.exclude_files = "react/renderer/components/view/tests", "react/renderer/components/view/platform/android", "react/renderer/components/view/platform/windows" + sss.osx.exclude_files = "react/renderer/components/view/platform/cxx/**/*" + non_macos_platform_files = "react/renderer/components/view/platform/macos/**/*" + sss.ios.exclude_files = non_macos_platform_files + sss.tvos.exclude_files = non_macos_platform_files + sss.visionos.exclude_files = non_macos_platform_files + # macOS] sss.header_dir = "react/renderer/components/view" end From 188ec64b1252b2f3f2043df1a76345edcf3327cb Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 07:29:43 -0700 Subject: [PATCH 28/34] fix(codegen): transpile absolute source paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/react-native-codegen/scripts/build.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-native-codegen/scripts/build.js b/packages/react-native-codegen/scripts/build.js index 5b0d668fd56d..d4b2ca01fd0c 100644 --- a/packages/react-native-codegen/scripts/build.js +++ b/packages/react-native-codegen/scripts/build.js @@ -60,17 +60,18 @@ function getBuildPath(file, buildFolder) { async function buildFile(file, silent) { const destPath = getBuildPath(file, BUILD_DIR); + const relativeFile = path.relative(PACKAGE_DIR, file); fs.mkdirSync(path.dirname(destPath), {recursive: true}); - if (micromatch.isMatch(file, IGNORE_PATTERN)) { + if (micromatch.isMatch(relativeFile, IGNORE_PATTERN)) { silent || process.stdout.write( styleText('dim', ' \u2022 ') + path.relative(PACKAGE_DIR, file) + ' (ignore)\n', ); - } else if (!micromatch.isMatch(file, JS_FILES_PATTERN)) { + } else if (!micromatch.isMatch(relativeFile, JS_FILES_PATTERN)) { fs.createReadStream(file).pipe(fs.createWriteStream(destPath)); silent || process.stdout.write( From cc3edf1f8f2232ae1cccc451aa28e907455686c9 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 07:32:22 -0700 Subject: [PATCH 29/34] chore: audit 0.86 follow-up diff tags Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/react-native-codegen/scripts/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-codegen/scripts/build.js b/packages/react-native-codegen/scripts/build.js index d4b2ca01fd0c..2ae46a672817 100644 --- a/packages/react-native-codegen/scripts/build.js +++ b/packages/react-native-codegen/scripts/build.js @@ -60,7 +60,7 @@ function getBuildPath(file, buildFolder) { async function buildFile(file, silent) { const destPath = getBuildPath(file, BUILD_DIR); - const relativeFile = path.relative(PACKAGE_DIR, file); + const relativeFile = path.relative(PACKAGE_DIR, file); // [macOS] fs.mkdirSync(path.dirname(destPath), {recursive: true}); From f1ff0928d9ac2b0567106455c7c40f7647bc1e69 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 09:04:34 -0700 Subject: [PATCH 30/34] fix(swiftpm): single host source ownership for 0.86 Fabric Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/react-native/Package.swift | 17 +++------- .../ReactCommon/React-Fabric.podspec | 31 +++++++++---------- .../RCTLegacyViewManagerInteropCoordinator.mm | 8 ++--- .../components/view/HostPlatformTouch.h | 20 ++++++++++++ .../view/HostPlatformViewEventEmitter.h | 20 ++++++++++++ .../components/view/HostPlatformViewProps.h | 20 ++++++++++++ .../view/HostPlatformViewTraitsInitializer.h | 20 ++++++++++++ .../react/renderer/components/view/KeyEvent.h | 12 +++++++ .../renderer/components/view/MouseEvent.h | 12 +++++++ .../view/HostPlatformViewEventEmitter.cpp | 12 ++++++- .../components/view/HostPlatformViewProps.cpp | 12 +++++++ 11 files changed, 151 insertions(+), 33 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformTouch.h create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewEventEmitter.h create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewProps.h create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewTraitsInitializer.h create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/KeyEvent.h create mode 100644 packages/react-native/ReactCommon/react/renderer/components/view/MouseEvent.h diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index 9f4ab004cb2d..2b2ea8a7550a 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -444,15 +444,6 @@ let reactCore = RNTarget( ) /// React-Fabric.podspec -// [macOS -#if os(macOS) -let reactFabricViewPlatformSources = ["components/view/platform/macos"] -let reactFabricViewPlatformExcludes = ["components/view/platform/cxx"] -#else -let reactFabricViewPlatformSources = ["components/view/platform/cxx"] -let reactFabricViewPlatformExcludes = ["components/view/platform/macos"] -#endif -// macOS] let reactFabric = RNTarget( name: .reactFabric, path: "ReactCommon/react/renderer", @@ -463,8 +454,10 @@ let reactFabric = RNTarget( "core/tests", "components/view/tests", "components/view/platform/android", + "components/view/platform/tvos", // [macOS] "components/view/platform/windows", - // "components/view/platform/macos", // [macOS] + "components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h", // [macOS] + "components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm", // [macOS] "components/scrollview/tests", "components/scrollview/platform/android", "mounting/tests", @@ -488,9 +481,9 @@ let reactFabric = RNTarget( "components/unimplementedview", "components/virtualview", "components/root/tests", - ] + reactFabricViewPlatformExcludes, // [macOS] + ], // [macOS] dependencies: [.reactNativeDependencies, .reactJsiExecutor, .rctTypesafety, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .reactRendererDebug, .reactGraphics, .yoga, .reactJsInspectorTracing], - sources: ["animated", "animationbackend", "animations", "attributedstring", "core", "componentregistry", "componentregistry/native", "components/root", "components/view", "components/scrollview", "components/scrollview/platform/cxx", "components/scrollview/platform/ios", "components/legacyviewmanagerinterop", "components/legacyviewmanagerinterop/platform/ios", "dom", "scheduler", "mounting", "observers/events", "observers/intersection", "observers/mutation", "telemetry", "consistency", "leakchecker", "uimanager", "uimanager/consistency", "viewtransition"] + reactFabricViewPlatformSources // [macOS] + sources: ["animated", "animationbackend", "animations", "attributedstring", "core", "componentregistry", "componentregistry/native", "components/root", "components/view", "components/scrollview", "components/scrollview/platform/cxx", "components/scrollview/platform/ios", "components/legacyviewmanagerinterop", "components/legacyviewmanagerinterop/platform/ios", "dom", "scheduler", "mounting", "observers/events", "observers/intersection", "observers/mutation", "telemetry", "consistency", "leakchecker", "uimanager", "uimanager/consistency", "viewtransition"] // [macOS] ) let reactFabricInputAccessory = RNTarget( diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index bf8e0cd0bdc0..c5f917f30877 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -134,15 +134,21 @@ Pod::Spec.new do |s| sss.dependency "React-renderercss" sss.dependency "Yoga" # [macOS - sss.source_files = "react/renderer/components/view/**/*.{m,mm,cpp,h}" - sss.exclude_files = "react/renderer/components/view/tests", "react/renderer/components/view/platform/android", "react/renderer/components/view/platform/windows" - sss.osx.exclude_files = "react/renderer/components/view/platform/cxx/**/*" - non_macos_platform_files = "react/renderer/components/view/platform/macos/**/*" - sss.ios.exclude_files = non_macos_platform_files - sss.tvos.exclude_files = non_macos_platform_files - sss.visionos.exclude_files = non_macos_platform_files + common_view_sources = "react/renderer/components/view/*.{m,mm,cpp,h}" + common_view_headers = "react/renderer/components/view/*.{h}" + cxx_view_sources = "react/renderer/components/view/platform/cxx/**/*.{m,mm,cpp,h}" + macos_view_sources = "react/renderer/components/view/platform/macos/**/*.{m,mm,cpp,h}" + sss.source_files = podspec_sources( + [common_view_sources, cxx_view_sources], + common_view_headers, + ) + sss.osx.source_files = podspec_sources( + [common_view_sources, macos_view_sources], + common_view_headers, + ) # macOS] sss.header_dir = "react/renderer/components/view" + sss.header_mappings_dir = "react/renderer/components/view" # [macOS] end ss.subspec "scrollview" do |sss| @@ -153,19 +159,12 @@ Pod::Spec.new do |s| ss.subspec "legacyviewmanagerinterop" do |sss| sss.source_files = podspec_sources("react/renderer/components/legacyviewmanagerinterop/**/*.{m,mm,cpp,h}", "react/renderer/components/legacyviewmanagerinterop/**/*.{h}") - sss.exclude_files = "react/renderer/components/legacyviewmanagerinterop/tests" # [macOS - sss.osx.exclude_files = [ - "react/renderer/components/legacyviewmanagerinterop/platform/ios/**/RCTLegacyViewManagerInteropCoordinator.h", - "react/renderer/components/legacyviewmanagerinterop/platform/ios/**/RCTLegacyViewManagerInteropCoordinator.mm", - ] - platform_coordinator_files = [ + sss.exclude_files = [ + "react/renderer/components/legacyviewmanagerinterop/tests", "react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h", "react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm", ] - sss.ios.exclude_files = platform_coordinator_files - sss.tvos.exclude_files = platform_coordinator_files - sss.visionos.exclude_files = platform_coordinator_files # macOS] sss.header_dir = "react/renderer/components/legacyviewmanagerinterop" end diff --git a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/platform/ios/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/platform/ios/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index ce72f2e635d4..63b8c1e577b0 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/platform/ios/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/platform/ios/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -89,13 +89,13 @@ - (void)removeObserveForTag:(NSInteger)tag [_eventInterceptors removeObjectForKey:[NSNumber numberWithInteger:tag]]; } -- (UIView *)createPaperViewWithTag:(NSInteger)tag +- (RCTPlatformView *)createPaperViewWithTag:(NSInteger)tag // [macOS] { [_bridgelessInteropData attachInteropAPIsToModule:(id)_componentData.manager]; return [_componentData createViewWithTag:[NSNumber numberWithInteger:tag] rootTag:NULL]; } -- (void)setProps:(NSDictionary *)props forView:(UIView *)view +- (void)setProps:(NSDictionary *)props forView:(RCTPlatformView *)view // [macOS] { [_componentData setProps:props forView:view]; @@ -112,7 +112,7 @@ - (NSString *)componentViewName - (void)handleCommand:(NSString *)commandName args:(NSArray *)args reactTag:(NSInteger)tag - paperView:(nonnull UIView *)paperView + paperView:(nonnull RCTPlatformView *)paperView // [macOS] { Class managerClass = _componentData.managerClass; [self _lookupModuleMethodsIfNecessary]; @@ -163,7 +163,7 @@ - (void)_handleCommandsOnBridgeless:(id)method withArgs:(NSArra [componentViewManager setValue:_bridgeProxy forKey:@"bridge"]; [self->_bridgeProxy.uiManager - addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { + addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { // [macOS] [method invokeWithBridge:nil module:componentViewManager arguments:newArgs]; }]; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformTouch.h b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformTouch.h new file mode 100644 index 000000000000..d4a92185753a --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformTouch.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX +#include +#else +#include +#endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewEventEmitter.h b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewEventEmitter.h new file mode 100644 index 000000000000..886e75468606 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewEventEmitter.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX +#include +#else +#include +#endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewProps.h new file mode 100644 index 000000000000..e1ff9377238b --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewProps.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX +#include +#else +#include +#endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewTraitsInitializer.h b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewTraitsInitializer.h new file mode 100644 index 000000000000..4d2a7396eb38 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/HostPlatformViewTraitsInitializer.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX +#include +#else +#include +#endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/KeyEvent.h b/packages/react-native/ReactCommon/react/renderer/components/view/KeyEvent.h new file mode 100644 index 000000000000..3bb5c34a7047 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/KeyEvent.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#include diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/MouseEvent.h b/packages/react-native/ReactCommon/react/renderer/components/view/MouseEvent.h new file mode 100644 index 000000000000..4a3a5f61fdea --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/view/MouseEvent.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// [macOS] + +#pragma once + +#include diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp index 5b8b18d67be7..85f51c352c3c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp @@ -5,7 +5,13 @@ * LICENSE file in the root directory of this source tree. */ - // [macOS] +// [macOS + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX #include #include @@ -211,3 +217,7 @@ void HostPlatformViewEventEmitter::onDrop(const DragEvent& dragEvent) const { } } // namespace facebook::react + +#endif + +// macOS] diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp index 4190d7a57e13..5223a6977b08 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp @@ -5,6 +5,14 @@ * LICENSE file in the root directory of this source tree. */ +// [macOS + +#if defined(__APPLE__) +#include +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX + #include "HostPlatformViewProps.h" #include @@ -160,3 +168,7 @@ void HostPlatformViewProps::setProp( } // namespace facebook::react + +#endif + +// macOS] From 176bcfad1ffb977883511fce4a6d0316f3bd85bb Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 09:04:34 -0700 Subject: [PATCH 31/34] fix(macos): guard 0.86 frame timings observer Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DevSupport/RCTFrameTimingsObserver.mm | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/react-native/React/DevSupport/RCTFrameTimingsObserver.mm b/packages/react-native/React/DevSupport/RCTFrameTimingsObserver.mm index ec9a0ae01fba..597e8879bbde 100644 --- a/packages/react-native/React/DevSupport/RCTFrameTimingsObserver.mm +++ b/packages/react-native/React/DevSupport/RCTFrameTimingsObserver.mm @@ -7,6 +7,9 @@ #import "RCTFrameTimingsObserver.h" +#import // [macOS] + +#if !TARGET_OS_OSX // [macOS] #import #import @@ -296,3 +299,25 @@ - (UIWindow *)_getKeyWindow } @end + +#else // [macOS + +@implementation RCTFrameTimingsObserver + +- (instancetype)initWithScreenshotsEnabled:(__unused BOOL)screenshotsEnabled + callback:(__unused RCTFrameTimingCallback)callback +{ + return [super init]; +} + +- (void)start +{ +} + +- (void)stop +{ +} + +@end + +#endif // macOS] From 4d33dd6c6ec82d8f3e8ec8bd3c5dc2776d1f63f5 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 09:08:57 -0700 Subject: [PATCH 32/34] fix(macos): hide 0.86 loading window with AppKit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../react-native/React/CoreModules/RCTDevLoadingView.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/CoreModules/RCTDevLoadingView.mm b/packages/react-native/React/CoreModules/RCTDevLoadingView.mm index 41b19ea46013..732641ac1d9a 100644 --- a/packages/react-native/React/CoreModules/RCTDevLoadingView.mm +++ b/packages/react-native/React/CoreModules/RCTDevLoadingView.mm @@ -68,11 +68,15 @@ - (void)dealloc { [self clearInitialMessageDelay]; [[NSNotificationCenter defaultCenter] removeObserver:self]; - UIWindow *window = _window; + RCTPlatformWindow *window = _window; // [macOS] _window = nil; if (window) { RCTExecuteOnMainQueue(^{ +#if !TARGET_OS_OSX // [macOS] window.hidden = YES; +#else // [macOS + [window orderOut:nil]; +#endif // macOS] }); } } From 720125bfe2a5ed265577e903afd43900c31c0621 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 10:46:06 -0700 Subject: [PATCH 33/34] fix(hermes): default production resolver to Hermes V1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/scripts/resolve-hermes.mts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/resolve-hermes.mts b/.github/scripts/resolve-hermes.mts index 77fb5d9a1abe..f8986b8b81dd 100644 --- a/.github/scripts/resolve-hermes.mts +++ b/.github/scripts/resolve-hermes.mts @@ -31,7 +31,7 @@ function setActionOutput(key: string, value: string) { * Reads the Hermes artifact version from * packages/react-native/sdks/hermes-engine/version.properties. * - * Returns HERMES_V1_VERSION_NAME when RCT_HERMES_V1_ENABLED=1, otherwise + * [macOS] Returns HERMES_V1_VERSION_NAME unless RCT_HERMES_V1_ENABLED=0, otherwise * HERMES_VERSION_NAME. Returns null if the file or the key is missing. */ function resolveHermesArtifactVersion(): string | null { @@ -48,7 +48,7 @@ function resolveHermesArtifactVersion(): string | null { } } const key = - process.env.RCT_HERMES_V1_ENABLED === '1' + process.env.RCT_HERMES_V1_ENABLED !== '0' // [macOS] ? 'HERMES_V1_VERSION_NAME' : 'HERMES_VERSION_NAME'; const version = props[key]; @@ -60,12 +60,12 @@ function resolveHermesArtifactVersion(): string | null { /** * Reads the pinned Hermes tag from packages/react-native/sdks/.hermesversion - * (or .hermesv1version when RCT_HERMES_V1_ENABLED=1). The value is a git tag in + * [macOS] Uses .hermesv1version unless RCT_HERMES_V1_ENABLED=0. The value is a git tag in * facebook/hermes. Returns null if the file is missing or empty. */ function resolveHermesTag(): string | null { const tagFile = - process.env.RCT_HERMES_V1_ENABLED === '1' + process.env.RCT_HERMES_V1_ENABLED !== '0' // [macOS] ? '.hermesv1version' : '.hermesversion'; const tagPath = path.resolve( From c40812f21157d5b5e2854d2fcecafa42430d5a7c Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 17 Jul 2026 12:01:43 -0700 Subject: [PATCH 34/34] chore: regenerate 0.86 pod artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/rn-tester/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 1bb0787274e9..56b8d8fb30db 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -2941,7 +2941,7 @@ SPEC CHECKSUMS: React-debug: 80b1b69ee446eba705618c97f6ca38c53303f7a2 React-defaultsnativemodule: 85e802d41b9a1e7f3533fd7c9debc0ece83bbc92 React-domnativemodule: 9868061942d183333b6e2dae39975f3e97642644 - React-Fabric: ae13370e1867bd312a11948fa0e888c999cd1266 + React-Fabric: 2ce532860852fd97817f2b6b2a9d29f68f647ea4 React-FabricComponents: 13abab452d256d4740b93631eee7e56e5ed21290 React-FabricImage: 068487d08af703a7a161eeb7bb38a7057a421fb8 React-featureflags: 276640cbd7120a351c79bceda3ec413a08c66fd7