mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
48
Commits
@@ -276,7 +276,8 @@ jobs:
|
||||
NEW_ARCH_ENABLED=1
|
||||
|
||||
export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
|
||||
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
|
||||
# Disable prebuilds for now, as they are causing issues with E2E tests for 0.82-stable branch
|
||||
# export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ matrix.flavor }}.xcframework.tar.gz"
|
||||
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
|
||||
|
||||
xcodebuild \
|
||||
@@ -382,7 +383,8 @@ jobs:
|
||||
runs-on: 8-core-ubuntu
|
||||
needs: [set_release_type]
|
||||
container:
|
||||
image: reactnativecommunity/react-native-android:latest
|
||||
# Version is pinned to v18.0 to unblock `run_fantom_tests` - see https://github.com/react-native-community/docker-android/pull/242#issuecomment-3280029122
|
||||
image: reactnativecommunity/react-native-android:v18.0
|
||||
env:
|
||||
TERM: "dumb"
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## v0.81.3
|
||||
|
||||
### Fixed
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Reverted "Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source" ([537e3ad930](https://github.com/facebook/react-native/commit/537e3ad93041c0cef959f0d20586fe97818900a6) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
||||
|
||||
## v0.81.2
|
||||
|
||||
### Added
|
||||
|
||||
#### Android specific
|
||||
|
||||
- Create a debugOptimized buildType for Android ([5e3edafec6](https://github.com/facebook/react-native/commit/5e3edafec6c69558521061dced7a6bcd046576b0) by [@cortinico](https://github.com/cortinico))
|
||||
- Add `useNativeEqualsInNativeReadableArrayAndroid` and `useNativeTransformHelperAndroid` feature flag to the experimental channel. This should alleviate some of the perf issue users are seeing on Android + Reanimated + NewArch on 0.81([a346096da8](https://github.com/facebook/react-native/commit/a346096da81fccf5fbc82d83bfc128695e6ec3a5) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
### Fixed
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- Fix Node scripts related to prebuilt tarball extraction for paths containing whitespaces ([366f2ad505](https://github.com/facebook/react-native/commit/366f2ad5057ffecc1f5b211f6aae29567ae6b7e5) by [@kitten](https://github.com/kitten))
|
||||
- Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source ([a2eb29e5e7](https://github.com/facebook/react-native/commit/a2eb29e5e7aef8bbdf4c647c8467b5292b013b20) by [@kitten](https://github.com/kitten))
|
||||
|
||||
## v0.82.0-rc.1
|
||||
|
||||
### Added
|
||||
|
||||
@@ -135,6 +135,7 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(project(":packages:react-native:ReactAndroid:hermes-engine"))
|
||||
// TODO: T237406039 update coordinates
|
||||
.using(module("com.facebook.react:hermes-android:0.+"))
|
||||
.because("Users opted to use hermes from nightly")
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
declare module 'electron-store' {
|
||||
declare export type Schema = any;
|
||||
|
||||
declare export type Options = {
|
||||
+name?: string,
|
||||
defaults?: Object,
|
||||
schema?: any,
|
||||
migrations?: any,
|
||||
beforeEachMigration?: any,
|
||||
clearInvalidConfig?: boolean,
|
||||
serialize?: any,
|
||||
deserialize?: any,
|
||||
accessPropertiesByDotNotation?: boolean,
|
||||
watch?: boolean,
|
||||
encryptionKey?: string | Buffer | $ReadOnlyArray<number>,
|
||||
...
|
||||
};
|
||||
|
||||
declare class ElectronStore {
|
||||
constructor(options?: Options): this;
|
||||
get(key: string): any;
|
||||
get(key: string, defaultValue: any): any;
|
||||
set(key: string, value: any): void;
|
||||
set(object: Object): void;
|
||||
has(key: string): boolean;
|
||||
delete(key: string): void;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
declare module.exports: Class<ElectronStore>;
|
||||
}
|
||||
+1
-1
@@ -117,7 +117,7 @@
|
||||
"temp-dir": "^2.0.0",
|
||||
"tinybench": "^4.1.0",
|
||||
"typescript": "5.8.3",
|
||||
"ws": "^6.2.3"
|
||||
"ws": "^7.5.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"eslint-plugin-react-hooks": "6.1.0-canary-12bc60f5-20250613",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@generated SignedSource<<b6a779e724ecce5e727ee674dc20f809>>
|
||||
Git revision: e87564a24cf233c60aaebee8c418ec85724f7214
|
||||
@generated SignedSource<<0b54f75686e4893a5444839bf621a317>>
|
||||
Git revision: 5a792db1225adda206313e9bf751198a1ca7851a
|
||||
Built with --nohooks: false
|
||||
Is local checkout: false
|
||||
Remote URL: https://github.com/facebook/react-native-devtools-frontend
|
||||
|
||||
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -238,7 +238,7 @@ import"../../ui/components/icon_button/icon_button.js";import"../../ui/component
|
||||
<td>${e.method}</td>
|
||||
<td>${e.params?d`<code>${JSON.stringify(e.params)}</code>`:""}</td>
|
||||
<td>
|
||||
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"(pending)"}
|
||||
${e.result?d`<code>${JSON.stringify(e.result)}</code>`:e.error?d`<code>${JSON.stringify(e.error)}</code>`:"id"in e?"(pending)":""}
|
||||
</td>
|
||||
<td data-value=${e.elapsedTime||0}>
|
||||
${"id"in e?e.elapsedTime?U(A.sMs,{PH1:String(e.elapsedTime)}):"(pending)":""}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,71 +1,71 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
// @generated SignedSource<<e93d55b5e28943e44271f5d3738083e0>>
|
||||
// @generated SignedSource<<9df662721aea6e3774a8677e2a6065e4>>
|
||||
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 116060647,
|
||||
"size": 116056584,
|
||||
"hash": "sha256",
|
||||
"digest": "4352f1c9848ca919101ec628bd08b87a72a828d1ab55fa43a02098329fa452fa",
|
||||
"digest": "fac3912f10e3c373c874be6c4696f11e05cbaa754c116db6ea72189afae5efe6",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQM24LW0nPZRk0ypuIG9prz_72YjBJNcVlGSIEpO4zdlLXgw4dFNodH7MKg9eKNTnx7wrVDDBNACrnEPt_OfXOjZyZsV9Oaqu0-vNFRdlEyis4YqmpqGLtz3LvD-9R6fzcWxJI9zrhdPvOvlXP-3Syt1UNITaxXDVqIwAAYpCaAh0oLpupAFCc2yvYw"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPdlYV_w4y_wxd5Rs8c_F-soLMbY1zwXJjD-JM4TkOtOELyxZGyreTb6dTehp_X6e3qVFZUT05ETRs0MbvqYr4mqdb6dkG9DqNQEVv3TBoHwl7TYSygtHJEs3gXTAtMLYEKW9xXODJlVF-9II7fdTdHU7x7Pyf6NR6S3nv7sKUVD-zKpxF50L2TwUQ"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 115930333,
|
||||
"size": 115929093,
|
||||
"hash": "sha256",
|
||||
"digest": "11c7b07942928a6301b07fbf2bc77ce1229b2a52891f23541cdd9858b5250e64",
|
||||
"digest": "06bbaeb62ae2e0081d184eba42b9f15be0d0b3f4901142b08a8a1430ff83e722",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQNDwm7HZRhtNxHqMr1FfSb0afHFGrn1OHxH0gOiggrLrht9QRUgJ3GG5jj7huhQzMRogE-LCMsnxh1ioOZks-YYX4KRt6Kj1-whdWsGFc7lBhPOpk1ssbYFGN1NNyuyFRmH-3nCY3lBC4AmbCUkbDTUeCi9DidCtJeyc73CZJEu7M62rIzxR2yV"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPENbrxrg0RF_TiyxSY_YVYqC1UfRKj_bMfKb3qHUsIhBlya8n3FkbfJqTXTNdqL4riFRulXS5ecXZprtvk_9kao-zY59r3kiTRwobzF0jnjM507_9UOnEHWzG5ZJYzQyOtS3kQmT0HwZabVj9qw38OB9mk-MPVCZIPZay2PRnIThBzpKDoOOP9"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 110891041,
|
||||
"size": 110891603,
|
||||
"hash": "sha256",
|
||||
"digest": "3cbe8b1b3d17e433347f1601435bb9a6cb758528a5c176a66fc52d9977223175",
|
||||
"digest": "eeb9cc1399c0c38c429848dbf622f1b46e88d7d97788dcdc4c30a9fcce37705c",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOmC2cqqSv4OrSJKJroYVg_NE8OE4O73AXqY7wXiYqiWQVkDt0Xnyw3ZeUpQT_Qb0-OoT5F8REKoFrB6eqwat8Ovkyina30peYTTwNUzmwnnGQEg7J0fOHNxLF4dkmU1FagXtsoWgex4dKgsK_VpcMsHj3Vp7diomkYvWBVTf_gPVEseYSN9oKq92qa"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN9bd7nbXi-uF_MooJEiUL2jJVifMqgQxZ236aqbSdiJe5Uzjud5ANf0LDAl8GDVVRShd6x4B-gsZ-qRowH0qJikoGatQIkBgyzp4i8ors52etGOIIwdAxdNIng5Vtp441j2_N__btkJqlHcMMbbqvO8fg9oeYTlhMpOx3MLLWXIlG7ix5IFUDiYChf"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 117766158,
|
||||
"size": 117770388,
|
||||
"hash": "sha256",
|
||||
"digest": "6fb79bc2ba3008401b4c9c128248657b95b98581ccde60f8fadb622163779775",
|
||||
"digest": "a65e446e526502b267cbe6800ae031e4e1b5b0aca21412152a66ffe1d3a29410",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQMMtGn-YGdfLfTVWC8zbQkQx65Asq6iArKt1t__cjZ8UY_s6-sX5XBHr8k1SaexAO21dFZENQVZ1jW_wn_gJ9ENvosQDG1KfWMViKsHli0xRzZ1HVsgPIj_KVXe907QZwwtJf2XhgH0HT8dfH-AQdDcd0_TB5DFUwOsHzhH0nBrHet7YFkbJtPTaA"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN29ukKLZS48QcCU-gms06YFnG-fxxDfrWTup-Z6KoIAP-QEu3sFzrpqeZz4g-jFsh3o-IIoHJsdbu4a2U7ROjPfWGguGJrPK8fdc3iV67Qw1_VAPU13dm0dI1DbSY50ah05wh43jdBG3LGCAYYJ-W8_mZMC3HqM2v-8KVd-DoPT4hOh1s9CCFgEQ"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"size": 125527537,
|
||||
"size": 125526370,
|
||||
"hash": "sha256",
|
||||
"digest": "579a5b0944c51c3b1b541ad5af66c1ffedf93cae2a891ecdf88cb7219fd9b096",
|
||||
"digest": "26b190c0f85249dee91999e020b8fe7ffd5c007458a2103ed3822558861dbe87",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOQ3E8lBXqdVHbDPyVb5AOQMrDWjFrFV8fnLLBsygQvLWdpu6ixyG9PgWdwpi5jM-XcDdCHkhBdhaq-5dwT_tgRWKCAMsEBoAIUk0Xg77mGyHG2VF7bNfQ2qFBMuObrsTmrKy1nJ-UFDDm29pJD4GkFQW5NesiBwndJj8t3B8Ur8cczh_XR8rF5"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQO0mdIJx1eYiSDJNiAZ2soaeECILayWnvOeiey-fpS4ydDTreAMfwL4QZ5GZo4-AsGwOXCSdF3hBMM2ufnvw6BDFg5UEiTc4DALJu7o6YBBG4wlVZbI-2kkaXd7u8RhBQPjmhsdcX3sDmCGmSr3WK9EkTRKStE2lb6ilOn-969h4dYWekwUUCSX"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/debugger-shell",
|
||||
"productName": "React Native DevTools",
|
||||
"version": "0.82.0-main",
|
||||
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
|
||||
"keywords": [
|
||||
@@ -34,6 +35,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "37.2.6",
|
||||
"electron-store": "^8.2.0",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
*/
|
||||
|
||||
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
|
||||
const {BrowserWindow, app, shell, ipcMain} = require('electron') as any;
|
||||
const {BrowserWindow, Menu, app, shell, ipcMain} = require('electron') as any;
|
||||
const Store = require('electron-store');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
|
||||
const appSettings = new Store();
|
||||
const windowMetadata = new WeakMap<
|
||||
typeof BrowserWindow,
|
||||
$ReadOnly<{
|
||||
@@ -53,10 +55,11 @@ function handleLaunchArgs(argv: string[]) {
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
// Create the browser window.
|
||||
frontendWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 600,
|
||||
...(getSavedWindowPosition(windowKey) ?? {
|
||||
width: 1200,
|
||||
height: 600,
|
||||
}),
|
||||
webPreferences: {
|
||||
partition: 'persist:react-native-devtools',
|
||||
preload: require.resolve('./preload.js'),
|
||||
@@ -66,6 +69,8 @@ function handleLaunchArgs(argv: string[]) {
|
||||
});
|
||||
// Auto-hide the Windows/Linux menu bar
|
||||
frontendWindow.setMenuBarVisibility(false);
|
||||
// Observe and update saved window position
|
||||
setupWindowResizeListeners(frontendWindow, windowKey);
|
||||
}
|
||||
|
||||
// Open links in the default browser instead of in new Electron windows.
|
||||
@@ -91,8 +96,68 @@ function handleLaunchArgs(argv: string[]) {
|
||||
frontendWindow.focus();
|
||||
}
|
||||
|
||||
function configureAppMenu() {
|
||||
const template = [
|
||||
...(process.platform === 'darwin' ? [{role: 'appMenu'}] : []),
|
||||
{role: 'fileMenu'},
|
||||
{role: 'editMenu'},
|
||||
{role: 'viewMenu'},
|
||||
{role: 'windowMenu'},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'React Native Website',
|
||||
click: () => shell.openExternal('https://reactnative.dev'),
|
||||
},
|
||||
{
|
||||
label: 'Release Notes',
|
||||
click: () =>
|
||||
shell.openExternal(
|
||||
'https://github.com/facebook/react-native/releases',
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
function getSavedWindowPosition(
|
||||
windowKey: string,
|
||||
): ?{width: number, height: number, x?: number, y?: number} {
|
||||
return appSettings.get('windowArrangements', {})[windowKey];
|
||||
}
|
||||
|
||||
function saveWindowPosition(
|
||||
windowKey: string,
|
||||
position: {x: number, y: number, width: number, height: number},
|
||||
) {
|
||||
const windowArrangements = appSettings.get('windowArrangements', {});
|
||||
windowArrangements[windowKey] = position;
|
||||
appSettings.set('windowArrangements', windowArrangements);
|
||||
}
|
||||
|
||||
function setupWindowResizeListeners(
|
||||
browserWindow: typeof BrowserWindow,
|
||||
windowKey: string,
|
||||
) {
|
||||
const savePosition = () => {
|
||||
if (!browserWindow.isDestroyed()) {
|
||||
const [x, y] = browserWindow.getPosition();
|
||||
const [width, height] = browserWindow.getSize();
|
||||
saveWindowPosition(windowKey, {x, y, width, height});
|
||||
}
|
||||
};
|
||||
browserWindow.on('moved', savePosition);
|
||||
browserWindow.on('resized', savePosition);
|
||||
browserWindow.on('closed', savePosition);
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
handleLaunchArgs(process.argv.slice(app.isPackaged ? 1 : 2));
|
||||
configureAppMenu();
|
||||
|
||||
app.on(
|
||||
'second-instance',
|
||||
|
||||
@@ -16,9 +16,8 @@ const util = require('util');
|
||||
// $FlowFixMe[unclear-type] We have no Flow types for the Electron API.
|
||||
const {app} = require('electron') as any;
|
||||
|
||||
// Set the app name and version early - these are used in --version as well as
|
||||
// in the User-Agent string.
|
||||
app.setName(pkg.name);
|
||||
// Set the application name and version
|
||||
app.setName(pkg.productName ?? pkg.name);
|
||||
app.setVersion(pkg.version + '-' + buildInfo.revision);
|
||||
|
||||
// Handle global command line arguments which don't require a window
|
||||
@@ -31,7 +30,7 @@ const {
|
||||
strict: false,
|
||||
});
|
||||
if (version) {
|
||||
console.log(`${app.getName()} v${app.getVersion()}`);
|
||||
console.log(`${pkg.name} v${app.getVersion()}`);
|
||||
// Not app.quit() - we want to exit immediately without initialising the graphical subsystem.
|
||||
app.exit(0);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"nullthrows": "^1.1.1",
|
||||
"open": "^7.0.3",
|
||||
"serve-static": "^1.16.2",
|
||||
"ws": "^6.2.3"
|
||||
"ws": "^7.5.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.4"
|
||||
|
||||
+7
-7
@@ -55,8 +55,8 @@ class ReactPlugin : Plugin<Project> {
|
||||
project,
|
||||
)
|
||||
|
||||
if (project.rootProject.isHermesV1Enabled != rootExtension.hermesV1Enabled.get()) {
|
||||
rootExtension.hermesV1Enabled.set(project.rootProject.isHermesV1Enabled)
|
||||
if (project.rootProject.isHermesV1Enabled) {
|
||||
rootExtension.hermesV1Enabled.set(true)
|
||||
}
|
||||
|
||||
// App Only Configuration
|
||||
@@ -71,11 +71,11 @@ class ReactPlugin : Plugin<Project> {
|
||||
project.afterEvaluate {
|
||||
val reactNativeDir = extension.reactNativeDir.get().asFile
|
||||
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
|
||||
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
|
||||
val hermesV1Enabled =
|
||||
if (project.rootProject.hasProperty("hermesV1Enabled"))
|
||||
project.rootProject.findProperty("hermesV1Enabled") == "true"
|
||||
else false
|
||||
val hermesVersionPropertiesFile =
|
||||
File(reactNativeDir, "sdks/hermes-engine/version.properties")
|
||||
val versionAndGroupStrings =
|
||||
readVersionAndGroupStrings(propertiesFile, hermesVersionPropertiesFile)
|
||||
val hermesV1Enabled = rootExtension.hermesV1Enabled.get()
|
||||
configureDependencies(project, versionAndGroupStrings, hermesV1Enabled)
|
||||
configureRepositories(project)
|
||||
}
|
||||
|
||||
+3
@@ -14,6 +14,7 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.capitalizeCompat
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
|
||||
import com.facebook.react.utils.ProjectUtils.useThirdPartyJSC
|
||||
import com.facebook.react.utils.detectedCliFile
|
||||
import com.facebook.react.utils.detectedEntryFile
|
||||
@@ -48,6 +49,7 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
|
||||
} else {
|
||||
isHermesEnabledInProject
|
||||
}
|
||||
val isHermesV1Enabled = project.isHermesV1Enabled || rootProject.isHermesV1Enabled
|
||||
val isDebuggableVariant =
|
||||
config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
|
||||
val useThirdPartyJSC = project.useThirdPartyJSC
|
||||
@@ -78,6 +80,7 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
|
||||
task.jsBundleDir.set(jsBundleDir)
|
||||
task.resourcesDir.set(resourcesDir)
|
||||
task.hermesEnabled.set(isHermesEnabledInThisVariant)
|
||||
task.hermesV1Enabled.set(isHermesV1Enabled)
|
||||
task.minifyEnabled.set(!isHermesEnabledInThisVariant)
|
||||
task.devEnabled.set(false)
|
||||
task.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
|
||||
|
||||
+3
-5
@@ -63,6 +63,8 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
|
||||
@get:Input abstract val hermesEnabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val hermesV1Enabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val devEnabled: Property<Boolean>
|
||||
|
||||
@get:Input abstract val extraPackagerArgs: ListProperty<String>
|
||||
@@ -94,12 +96,8 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
runCommand(bundleCommand)
|
||||
|
||||
if (hermesEnabled.get()) {
|
||||
val hermesV1Enabled =
|
||||
if (project.rootProject.hasProperty("hermesV1Enabled"))
|
||||
project.rootProject.findProperty("hermesV1Enabled") == "true"
|
||||
else false
|
||||
val detectedHermesCommand =
|
||||
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled)
|
||||
detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled.get())
|
||||
val bytecodeFile = File("${bundleFile}.hbc")
|
||||
val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
|
||||
val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
|
||||
|
||||
+28
-11
@@ -13,7 +13,7 @@ import com.facebook.react.utils.PropertyUtils.EXCLUSIVE_ENTEPRISE_REPOSITORY
|
||||
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
|
||||
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_VERSION_NAME
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_V1_VERSION_NAME
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
|
||||
@@ -31,6 +31,7 @@ internal object DependencyUtils {
|
||||
internal data class Coordinates(
|
||||
val versionString: String,
|
||||
val hermesVersionString: String,
|
||||
val hermesV1VersionString: String,
|
||||
val reactGroupString: String = DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP,
|
||||
val hermesGroupString: String = DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP,
|
||||
)
|
||||
@@ -113,7 +114,12 @@ internal object DependencyUtils {
|
||||
coordinates: Coordinates,
|
||||
hermesV1Enabled: Boolean = false,
|
||||
) {
|
||||
if (coordinates.versionString.isBlank() || coordinates.hermesVersionString.isBlank()) return
|
||||
if (
|
||||
coordinates.versionString.isBlank() ||
|
||||
(!hermesV1Enabled && coordinates.hermesVersionString.isBlank()) ||
|
||||
(hermesV1Enabled && coordinates.hermesV1VersionString.isBlank())
|
||||
)
|
||||
return
|
||||
project.rootProject.allprojects { eachProject ->
|
||||
eachProject.configurations.all { configuration ->
|
||||
// Here we set a dependencySubstitution for both react-native and hermes-engine as those
|
||||
@@ -133,7 +139,11 @@ internal object DependencyUtils {
|
||||
// Contributors only: The hermes-engine version is forced only if the user has
|
||||
// not opted into using nightlies for local development.
|
||||
configuration.resolutionStrategy.force(
|
||||
"${coordinates.reactGroupString}:hermes-android:${coordinates.versionString}"
|
||||
// TODO: T237406039 update coordinates
|
||||
if (hermesV1Enabled)
|
||||
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
|
||||
else
|
||||
"${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -146,10 +156,11 @@ internal object DependencyUtils {
|
||||
): List<Triple<String, String, String>> {
|
||||
// TODO: T231755027 update coordinates and versioning
|
||||
val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
|
||||
// TODO: T237406039 update coordinates
|
||||
val hermesVersionString =
|
||||
if (hermesV1Enabled)
|
||||
"${coordinates.hermesGroupString}:hermes-android:${coordinates.versionString}"
|
||||
else "${coordinates.reactGroupString}:hermes-android:${coordinates.versionString}"
|
||||
"${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
|
||||
else "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:react-native",
|
||||
@@ -172,6 +183,7 @@ internal object DependencyUtils {
|
||||
"The react-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
// TODO: T237406039 update coordinates
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-android",
|
||||
@@ -183,14 +195,10 @@ internal object DependencyUtils {
|
||||
return dependencySubstitution
|
||||
}
|
||||
|
||||
fun readVersionAndGroupStrings(propertiesFile: File): Coordinates {
|
||||
fun readVersionAndGroupStrings(propertiesFile: File, hermesVersionFile: File): Coordinates {
|
||||
val reactAndroidProperties = Properties()
|
||||
propertiesFile.inputStream().use { reactAndroidProperties.load(it) }
|
||||
val versionStringFromFile = (reactAndroidProperties[INTERNAL_VERSION_NAME] as? String).orEmpty()
|
||||
// TODO: T231755027 update HERMES_VERSION_NAME in gradle.properties to point to the correct
|
||||
// hermes version
|
||||
val hermesVersionStringFromFile =
|
||||
(reactAndroidProperties[INTERNAL_HERMES_VERSION_NAME] as? String).orEmpty()
|
||||
// If on a nightly, we need to fetch the -SNAPSHOT artifact from Sonatype.
|
||||
val versionString =
|
||||
if (versionStringFromFile.startsWith("0.0.0") || "-nightly-" in versionStringFromFile) {
|
||||
@@ -205,9 +213,18 @@ internal object DependencyUtils {
|
||||
val hermesGroupString =
|
||||
reactAndroidProperties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
|
||||
?: DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
|
||||
// TODO: T237406039 read both versions from the same file
|
||||
val hermesVersionProperties = Properties()
|
||||
hermesVersionFile.inputStream().use { hermesVersionProperties.load(it) }
|
||||
|
||||
val hermesVersion = versionString
|
||||
val hermesV1Version =
|
||||
(hermesVersionProperties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
|
||||
|
||||
return Coordinates(
|
||||
versionString,
|
||||
hermesVersionStringFromFile,
|
||||
hermesVersion,
|
||||
hermesV1Version,
|
||||
reactGroupString,
|
||||
hermesGroupString,
|
||||
)
|
||||
|
||||
+1
@@ -153,6 +153,7 @@ internal fun detectOSAwareHermesCommand(
|
||||
|
||||
// 3. If Hermes V1 is enabled, use hermes-compiler from npm, otherwise, if the
|
||||
// react-native contains a pre-built hermesc, use it.
|
||||
// TODO: T237406039 use hermes-compiler from npm for both
|
||||
val hermesCPath = if (hermesV1Enabled) HERMES_COMPILER_NPM_DIR else HERMESC_IN_REACT_NATIVE_DIR
|
||||
val prebuiltHermesPath =
|
||||
hermesCPath
|
||||
|
||||
+6
-2
@@ -79,6 +79,10 @@ object PropertyUtils {
|
||||
|
||||
/** Internal property used to control the version name of React Native */
|
||||
const val INTERNAL_VERSION_NAME = "VERSION_NAME"
|
||||
/** Internal property used to control the version name of Hermes Engine */
|
||||
const val INTERNAL_HERMES_VERSION_NAME = "HERMES_VERSION_NAME"
|
||||
|
||||
/**
|
||||
* Internal property, shared with iOS, used to control the version name of Hermes Engine. This is
|
||||
* stored in sdks/hermes-engine/version.properties
|
||||
*/
|
||||
const val INTERNAL_HERMES_V1_VERSION_NAME = "HERMES_V1_VERSION_NAME"
|
||||
}
|
||||
|
||||
+5
-5
@@ -98,11 +98,11 @@ class PrepareGlogTaskTest {
|
||||
val glogThirdPartyJniPath = tempFolder.newFolder("glogpath/jni")
|
||||
val output = tempFolder.newFolder("output")
|
||||
val task =
|
||||
createTestTask<PrepareGlogTask> {
|
||||
it.glogPath.setFrom(glogpath)
|
||||
it.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
|
||||
it.glogVersion.set("1.0.0")
|
||||
it.outputDir.set(output)
|
||||
createTestTask<PrepareGlogTask> { task ->
|
||||
task.glogPath.setFrom(glogpath)
|
||||
task.glogThirdPartyJniPath.set(glogThirdPartyJniPath)
|
||||
task.glogVersion.set("1.0.0")
|
||||
task.outputDir.set(output)
|
||||
}
|
||||
File(glogpath, "glog-1.0.0/src/glog.h.in").apply {
|
||||
parentFile.mkdirs()
|
||||
|
||||
+271
-26
@@ -271,54 +271,69 @@ class DependencyUtilsTest {
|
||||
.isEqualTo(2)
|
||||
}
|
||||
|
||||
// TODO: T236767053
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withEmptyVersion_doesNothing() {
|
||||
val project = createProject()
|
||||
|
||||
configureDependencies(project, DependencyUtils.Coordinates("", ""))
|
||||
configureDependencies(project, DependencyUtils.Coordinates("", "", ""))
|
||||
|
||||
assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionString_appliesResolutionStrategy() {
|
||||
fun configureDependencies_withVersionString_appliesResolutionStrategy_withClassicHermes() {
|
||||
val project = createProject()
|
||||
|
||||
configureDependencies(project, DependencyUtils.Coordinates("1.2.3", "1.2.3"))
|
||||
configureDependencies(project, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
|
||||
|
||||
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionString_appliesOnAllProjects() {
|
||||
fun configureDependencies_withVersionString_appliesResolutionStrategy_withHermesV1() {
|
||||
val project = createProject()
|
||||
|
||||
configureDependencies(
|
||||
project,
|
||||
DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
|
||||
hermesV1Enabled = true,
|
||||
)
|
||||
|
||||
val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionString_appliesOnAllProjects_withClassicHermes() {
|
||||
val rootProject = ProjectBuilder.builder().build()
|
||||
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
|
||||
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
|
||||
appProject.plugins.apply("com.android.application")
|
||||
libProject.plugins.apply("com.android.library")
|
||||
|
||||
configureDependencies(appProject, DependencyUtils.Coordinates("1.2.3", "1.2.3"))
|
||||
configureDependencies(appProject, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
|
||||
|
||||
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects() {
|
||||
fun configureDependencies_withVersionString_appliesOnAllProjects_withHermesV1() {
|
||||
val rootProject = ProjectBuilder.builder().build()
|
||||
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
|
||||
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
|
||||
@@ -327,25 +342,93 @@ class DependencyUtilsTest {
|
||||
|
||||
configureDependencies(
|
||||
appProject,
|
||||
DependencyUtils.Coordinates("1.2.3", "1.2.3", "io.github.test"),
|
||||
DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
|
||||
hermesV1Enabled = true,
|
||||
)
|
||||
|
||||
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(appForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withClassicHermes() {
|
||||
val rootProject = ProjectBuilder.builder().build()
|
||||
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
|
||||
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
|
||||
appProject.plugins.apply("com.android.application")
|
||||
libProject.plugins.apply("com.android.library")
|
||||
|
||||
configureDependencies(
|
||||
appProject,
|
||||
DependencyUtils.Coordinates(
|
||||
"1.2.3",
|
||||
"4.5.6",
|
||||
"7.8.9",
|
||||
"io.github.test",
|
||||
"io.github.test.hermes",
|
||||
),
|
||||
)
|
||||
|
||||
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly() {
|
||||
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withHermesV1() {
|
||||
val rootProject = ProjectBuilder.builder().build()
|
||||
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
|
||||
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
|
||||
appProject.plugins.apply("com.android.application")
|
||||
libProject.plugins.apply("com.android.library")
|
||||
|
||||
configureDependencies(
|
||||
appProject,
|
||||
DependencyUtils.Coordinates(
|
||||
"1.2.3",
|
||||
"4.5.6",
|
||||
"7.8.9",
|
||||
"io.github.test",
|
||||
"io.github.test.hermes",
|
||||
),
|
||||
hermesV1Enabled = true,
|
||||
)
|
||||
|
||||
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(
|
||||
appForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
|
||||
)
|
||||
.isTrue()
|
||||
assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
.isTrue()
|
||||
assertThat(
|
||||
libForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
|
||||
)
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withClassicHermes() {
|
||||
val dependencySubstitutions =
|
||||
getDependencySubstitutions(DependencyUtils.Coordinates("0.42.0", "0.42.0"))
|
||||
getDependencySubstitutions(DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"))
|
||||
|
||||
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
|
||||
assertThat("com.facebook.react:react-android:0.42.0")
|
||||
@@ -364,10 +447,40 @@ class DependencyUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly() {
|
||||
fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withHermesV1() {
|
||||
val dependencySubstitutions =
|
||||
getDependencySubstitutions(
|
||||
DependencyUtils.Coordinates("0.42.0", "0.42.0", "io.github.test")
|
||||
DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"),
|
||||
hermesV1Enabled = true,
|
||||
)
|
||||
|
||||
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
|
||||
assertThat("com.facebook.react:react-android:0.42.0")
|
||||
.isEqualTo(dependencySubstitutions[0].second)
|
||||
assertThat(
|
||||
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[0].third)
|
||||
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
|
||||
assertThat("com.facebook.hermes:hermes-android:0.43.0")
|
||||
.isEqualTo(dependencySubstitutions[1].second)
|
||||
assertThat(
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[1].third)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withClassicHermes() {
|
||||
val dependencySubstitutions =
|
||||
getDependencySubstitutions(
|
||||
DependencyUtils.Coordinates(
|
||||
"0.42.0",
|
||||
"0.42.0",
|
||||
"0.43.0",
|
||||
"io.github.test",
|
||||
"io.github.test.hermes",
|
||||
)
|
||||
)
|
||||
|
||||
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
|
||||
@@ -392,6 +505,44 @@ class DependencyUtilsTest {
|
||||
.isEqualTo(dependencySubstitutions[3].third)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withHermesV1() {
|
||||
val dependencySubstitutions =
|
||||
getDependencySubstitutions(
|
||||
DependencyUtils.Coordinates(
|
||||
"0.42.0",
|
||||
"0.42.0",
|
||||
"0.43.0",
|
||||
"io.github.test",
|
||||
"io.github.test.hermes",
|
||||
),
|
||||
hermesV1Enabled = true,
|
||||
)
|
||||
|
||||
assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
|
||||
assertThat(
|
||||
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[0].third)
|
||||
assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.43.0")
|
||||
.isEqualTo(dependencySubstitutions[1].second)
|
||||
assertThat(
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
|
||||
)
|
||||
.isEqualTo(dependencySubstitutions[1].third)
|
||||
assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[2].first)
|
||||
assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[2].second)
|
||||
assertThat("The react-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[2].third)
|
||||
assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
|
||||
assertThat("io.github.test.hermes:hermes-android:0.43.0")
|
||||
.isEqualTo(dependencySubstitutions[3].second)
|
||||
assertThat("The hermes-android dependency was modified to use the correct Maven group.")
|
||||
.isEqualTo(dependencySubstitutions[3].third)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readVersionString_withCorrectVersionString_returnsIt() {
|
||||
val propertiesFile =
|
||||
@@ -405,9 +556,25 @@ class DependencyUtilsTest {
|
||||
)
|
||||
}
|
||||
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_V1_VERSION_NAME=1000.0.0
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val versionString = strings.versionString
|
||||
val hermesVersionString = strings.hermesVersionString
|
||||
val hermesV1VersionString = strings.hermesV1VersionString
|
||||
|
||||
assertThat(versionString).isEqualTo("1000.0.0")
|
||||
assertThat(hermesVersionString).isEqualTo("1000.0.0")
|
||||
assertThat(hermesV1VersionString).isEqualTo("1000.0.0")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -417,15 +584,33 @@ class DependencyUtilsTest {
|
||||
writeText(
|
||||
"""
|
||||
VERSION_NAME=0.0.0-20221101-2019-cfe811ab1
|
||||
HERMES_VERSION_NAME=0.12.0-commitly-20221101-2019-cfe811ab1
|
||||
HERMES_V1_VERSION_NAME=250829098.0.0-stable
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_V1_VERSION_NAME=250829098.0.0-stable
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val versionString = strings.versionString
|
||||
val hermesVersionString = strings.hermesVersionString
|
||||
val hermesV1VersionString = strings.hermesV1VersionString
|
||||
|
||||
assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
|
||||
assertThat(hermesVersionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
|
||||
assertThat(hermesV1VersionString).isEqualTo("250829098.0.0-stable")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -440,8 +625,23 @@ class DependencyUtilsTest {
|
||||
)
|
||||
}
|
||||
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val versionString = strings.versionString
|
||||
val hermesVersionString = strings.hermesVersionString
|
||||
val hermesV1VersionString = strings.hermesV1VersionString
|
||||
assertThat(versionString).isEqualTo("")
|
||||
assertThat(hermesVersionString).isEqualTo("")
|
||||
assertThat(hermesV1VersionString).isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -457,8 +657,24 @@ class DependencyUtilsTest {
|
||||
)
|
||||
}
|
||||
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).versionString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val versionString = strings.versionString
|
||||
val hermesVersionString = strings.hermesVersionString
|
||||
val hermesV1VersionString = strings.hermesV1VersionString
|
||||
assertThat(versionString).isEqualTo("")
|
||||
assertThat(hermesVersionString).isEqualTo("")
|
||||
assertThat(hermesV1VersionString).isEqualTo("")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -468,15 +684,30 @@ class DependencyUtilsTest {
|
||||
writeText(
|
||||
"""
|
||||
react.internal.publishingGroup=io.github.test
|
||||
react.internal.hermesPublishingGroup=io.github.test
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
val groupString = readVersionAndGroupStrings(propertiesFile).reactGroupString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(groupString).isEqualTo("io.github.test")
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val reactGroupString = strings.reactGroupString
|
||||
val hermesGroupString = strings.hermesGroupString
|
||||
|
||||
assertThat(reactGroupString).isEqualTo("io.github.test")
|
||||
assertThat(hermesGroupString).isEqualTo("io.github.test")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -491,9 +722,23 @@ class DependencyUtilsTest {
|
||||
)
|
||||
}
|
||||
|
||||
val groupString = readVersionAndGroupStrings(propertiesFile).reactGroupString
|
||||
val hermesVersionFile =
|
||||
tempFolder.newFile("version.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
HERMES_V1_VERSION_NAME=
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(groupString).isEqualTo("com.facebook.react")
|
||||
val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
|
||||
val reactGroupString = strings.reactGroupString
|
||||
val hermesGroupString = strings.hermesGroupString
|
||||
|
||||
assertThat(reactGroupString).isEqualTo("com.facebook.react")
|
||||
assertThat(hermesGroupString).isEqualTo("com.facebook.hermes")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ class PathUtilsTest {
|
||||
@WithOs(OS.MAC)
|
||||
fun detectOSAwareHermesCommand_withHermesV1Enabled() {
|
||||
tempFolder.newFolder("node_modules/hermes-compiler/osx-bin/")
|
||||
val expected = tempFolder.newFile("node_modules/hermes-compiler/osx-bin//hermesc")
|
||||
val expected = tempFolder.newFile("node_modules/hermes-compiler/osx-bin/hermesc")
|
||||
|
||||
assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
|
||||
.isEqualTo(expected.toString())
|
||||
|
||||
@@ -264,7 +264,7 @@ const Switch: component(
|
||||
disabled,
|
||||
onTintColor: trackColorForTrue,
|
||||
style: StyleSheet.compose(
|
||||
{height: 31, width: 51},
|
||||
{alignSelf: 'flex-start' as const},
|
||||
StyleSheet.compose(
|
||||
style,
|
||||
ios_backgroundColor == null
|
||||
|
||||
@@ -29,6 +29,16 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['hotspotUpdate', 'setPressed'],
|
||||
});
|
||||
|
||||
/**
|
||||
* `ViewNativeComponent` is an internal React Native host component, and is
|
||||
* exported to provide lower-level access for libraries.
|
||||
*
|
||||
* @warning `<unstable_NativeView>` provides no semver guarantees and is not
|
||||
* intended to be used in app code. Please use
|
||||
* [`<View>`](https://reactnative.dev/docs/view) instead.
|
||||
*/
|
||||
// Additional note: Our long term plan is to reduce the overhead of the <Text>
|
||||
// and <View> wrappers so that we no longer have any reason to export these APIs.
|
||||
export default ViewNativeComponent;
|
||||
|
||||
export type ViewNativeComponentType = HostComponent<Props>;
|
||||
|
||||
@@ -30,7 +30,8 @@ UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius)
|
||||
}].CGImage;
|
||||
}
|
||||
|
||||
vImage_Buffer buffer1, buffer2;
|
||||
vImage_Buffer buffer1;
|
||||
vImage_Buffer buffer2;
|
||||
buffer1.width = buffer2.width = CGImageGetWidth(imageRef);
|
||||
buffer1.height = buffer2.height = CGImageGetHeight(imageRef);
|
||||
buffer1.rowBytes = buffer2.rowBytes = CGImageGetBytesPerRow(imageRef);
|
||||
|
||||
@@ -212,7 +212,8 @@ BOOL RCTUpscalingRequired(
|
||||
sourceSize.height *= scale;
|
||||
|
||||
// Calculate aspect ratios if needed (don't bother if resizeMode == stretch)
|
||||
CGFloat aspect = 0.0, targetAspect = 0.0;
|
||||
CGFloat aspect = 0.0;
|
||||
CGFloat targetAspect = 0.0;
|
||||
if (resizeMode != RCTResizeModeStretch) {
|
||||
aspect = sourceSize.width / sourceSize.height;
|
||||
targetAspect = destSize.width / destSize.height;
|
||||
|
||||
+5
-1
@@ -35,7 +35,11 @@ export interface SectionBase<ItemT, SectionT = DefaultSectionT> {
|
||||
|
||||
renderItem?: SectionListRenderItem<ItemT, SectionT> | undefined;
|
||||
|
||||
ItemSeparatorComponent?: React.ComponentType<any> | null | undefined;
|
||||
ItemSeparatorComponent?:
|
||||
| React.ComponentType<any>
|
||||
| React.ReactElement
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
keyExtractor?: ((item: ItemT, index: number) => string) | undefined;
|
||||
}
|
||||
|
||||
@@ -90,9 +90,15 @@ uint32_t RCTInterpolateColorInRange(CGFloat value, NSArray<NSNumber *> *inputRan
|
||||
CGFloat inputMin = inputRange[rangeIndex].doubleValue;
|
||||
CGFloat inputMax = inputRange[rangeIndex + 1].doubleValue;
|
||||
|
||||
CGFloat redMin, greenMin, blueMin, alphaMin;
|
||||
CGFloat redMin;
|
||||
CGFloat greenMin;
|
||||
CGFloat blueMin;
|
||||
CGFloat alphaMin;
|
||||
[outputRange[rangeIndex] getRed:&redMin green:&greenMin blue:&blueMin alpha:&alphaMin];
|
||||
CGFloat redMax, greenMax, blueMax, alphaMax;
|
||||
CGFloat redMax;
|
||||
CGFloat greenMax;
|
||||
CGFloat blueMax;
|
||||
CGFloat alphaMax;
|
||||
[outputRange[rangeIndex + 1] getRed:&redMax green:&greenMax blue:&blueMax alpha:&alphaMax];
|
||||
|
||||
return RCTColorFromComponents(
|
||||
|
||||
@@ -65,6 +65,16 @@ const virtualTextViewConfig = {
|
||||
uiViewClassName: 'RCTVirtualText',
|
||||
};
|
||||
|
||||
/**
|
||||
* `NativeText` is an internal React Native host component, and is exported to
|
||||
* provide lower-level access for libraries.
|
||||
*
|
||||
* @warning `<unstable_NativeText>` provides no semver guarantees and is not
|
||||
* intended to be used in app code. Please use
|
||||
* [`<Text>`](https://reactnative.dev/docs/text) instead.
|
||||
*/
|
||||
// Additional note: Our long term plan is to reduce the overhead of the <Text>
|
||||
// and <View> wrappers so that we no longer have any reason to export these APIs.
|
||||
export const NativeText: HostComponent<NativeTextProps> =
|
||||
(createReactNativeComponentClass('RCTText', () =>
|
||||
/* $FlowFixMe[incompatible-type] Natural Inference rollout. See
|
||||
|
||||
@@ -68,6 +68,17 @@ let reactOSCompat = RNTarget(
|
||||
path: "ReactCommon/oscompat"
|
||||
)
|
||||
|
||||
let rctSwiftUI = RNTarget(
|
||||
name: .rctSwiftUI,
|
||||
path: "ReactApple/RCTSwiftUI"
|
||||
)
|
||||
|
||||
let rctSwiftUIWrapper = RNTarget(
|
||||
name: .rctSwiftUIWrapper,
|
||||
path: "ReactApple/RCTSwiftUIWrapper",
|
||||
dependencies: [.rctSwiftUI]
|
||||
)
|
||||
|
||||
// React-rendererconsistency.podspec
|
||||
let reactRendererConsistency = RNTarget(
|
||||
name: .reactRendererConsistency,
|
||||
@@ -439,7 +450,7 @@ let reactFabric = RNTarget(
|
||||
let reactRCTFabric = RNTarget(
|
||||
name: .reactRCTFabric,
|
||||
path: "React/Fabric",
|
||||
dependencies: [.reactNativeDependencies, .reactCore, .reactRCTImage, .yoga, .reactRCTText, .jsi, .reactFabricComponents, .reactGraphics, .reactImageManager, .reactDebug, .reactUtils, .reactPerformanceTimeline, .reactRendererDebug, .reactRendererConsistency, .reactRuntimeScheduler, .reactRCTAnimation, .reactJsInspector, .reactJsInspectorNetwork, .reactJsInspectorTracing, .reactFabric, .reactFabricImage]
|
||||
dependencies: [.reactNativeDependencies, .reactCore, .reactRCTImage, .yoga, .reactRCTText, .jsi, .reactFabricComponents, .reactGraphics, .reactImageManager, .reactDebug, .reactUtils, .reactPerformanceTimeline, .reactRendererDebug, .reactRendererConsistency, .reactRuntimeScheduler, .reactRCTAnimation, .reactJsInspector, .reactJsInspectorNetwork, .reactJsInspectorTracing, .reactFabric, .reactFabricImage, .rctSwiftUIWrapper]
|
||||
)
|
||||
|
||||
/// React-FabricComponents.podspec
|
||||
@@ -579,6 +590,8 @@ let targets = [
|
||||
reactCore,
|
||||
reactCoreRCTWebsocket,
|
||||
reactFabric,
|
||||
rctSwiftUI,
|
||||
rctSwiftUIWrapper,
|
||||
reactRCTFabric,
|
||||
reactFabricComponents,
|
||||
reactFabricImage,
|
||||
@@ -728,6 +741,9 @@ extension String {
|
||||
static let logger = "React-logger"
|
||||
static let mapbuffer = "React-Mapbuffer"
|
||||
|
||||
static let rctSwiftUI = "RCTSwiftUI"
|
||||
static let rctSwiftUIWrapper = "RCTSwiftUIWrapper"
|
||||
|
||||
static let rctDeprecation = "RCT-Deprecation"
|
||||
static let yoga = "Yoga"
|
||||
static let reactUtils = "React-utils"
|
||||
|
||||
@@ -69,6 +69,7 @@ RCT_EXTERN_C_END
|
||||
* will be used as the JS module name. If omitted, the JS module name will
|
||||
* match the Objective-C class name.
|
||||
*/
|
||||
#ifndef RCT_FIT_RM_OLD_RUNTIME
|
||||
#define RCT_EXPORT_MODULE(js_name) \
|
||||
RCT_EXTERN void RCTRegisterModule(Class); \
|
||||
+(NSString *)moduleName \
|
||||
@@ -80,6 +81,17 @@ RCT_EXTERN_C_END
|
||||
RCTRegisterModule(self); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define RCT_EXPORT_MODULE(js_name) \
|
||||
RCT_EXTERN void RCTRegisterModule(Class); \
|
||||
+(NSString *)moduleName \
|
||||
{ \
|
||||
return @ #js_name; \
|
||||
}
|
||||
|
||||
#endif // RCT_FIT_RM_OLD_RUNTIME
|
||||
|
||||
/**
|
||||
* Same as RCT_EXPORT_MODULE, but uses __attribute__((constructor)) for module
|
||||
* registration. Useful for registering swift classes that forbids use of load
|
||||
|
||||
+2
-1
@@ -81,7 +81,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat top = 0, left = 0;
|
||||
CGFloat top = 0;
|
||||
CGFloat left = 0;
|
||||
if (contentSize.width < boundsSize.width) {
|
||||
left = (boundsSize.width - contentSize.width) * 0.5f;
|
||||
}
|
||||
|
||||
+159
-29
@@ -12,6 +12,7 @@
|
||||
#import <objc/runtime.h>
|
||||
#import <ranges>
|
||||
|
||||
#import <RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h>
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBorderDrawing.h>
|
||||
#import <React/RCTBoxShadow.h>
|
||||
@@ -50,6 +51,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
||||
UIView *_containerView;
|
||||
BOOL _useCustomContainerView;
|
||||
NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
|
||||
RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
|
||||
}
|
||||
|
||||
#ifdef RCT_DYNAMIC_FRAMEWORKS
|
||||
@@ -576,6 +578,10 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
||||
auto newTransform = _props->resolveTransform(layoutMetrics);
|
||||
self.layer.transform = RCTCATransform3DFromTransformMatrix(newTransform);
|
||||
}
|
||||
|
||||
if (_swiftUIWrapper != nullptr) {
|
||||
[_swiftUIWrapper updateLayoutWithBounds:self.bounds];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isJSResponder
|
||||
@@ -793,43 +799,95 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
((!_props->boxShadow.empty() || (clipToPaddingBox && nonZeroBorderWidth)) || _props->outlineWidth != 0);
|
||||
}
|
||||
|
||||
// The view that is used as the receiver for all styling (borders, background,
|
||||
// etc.). Most of the time, this is just `self`. When a view has a filter like
|
||||
// `blur` applied, we need to wrap it in a SwiftUI view to render the effect.
|
||||
// In this case, `effectiveContentView` will be the content view inside the
|
||||
// SwiftUI wrapper.
|
||||
- (UIView *)effectiveContentView
|
||||
{
|
||||
if (!ReactNativeFeatureFlags::enableSwiftUIBasedFilters()) {
|
||||
return self;
|
||||
}
|
||||
|
||||
UIView *effectiveContentView = self;
|
||||
|
||||
if (self.styleNeedsSwiftUIContainer) {
|
||||
if (_swiftUIWrapper == nullptr) {
|
||||
_swiftUIWrapper = [RCTSwiftUIContainerViewWrapper new];
|
||||
UIView *swiftUIContentView = [[UIView alloc] init];
|
||||
for (UIView *subview = nullptr in self.subviews) {
|
||||
[swiftUIContentView addSubview:subview];
|
||||
}
|
||||
swiftUIContentView.clipsToBounds = self.clipsToBounds;
|
||||
self.clipsToBounds = NO;
|
||||
swiftUIContentView.layer.mask = self.layer.mask;
|
||||
self.layer.mask = nil;
|
||||
[_swiftUIWrapper updateContentView:swiftUIContentView];
|
||||
[_swiftUIWrapper updateLayoutWithBounds:self.bounds];
|
||||
[self addSubview:_swiftUIWrapper.hostingView];
|
||||
|
||||
[self transferVisualPropertiesFromView:self toView:swiftUIContentView];
|
||||
}
|
||||
|
||||
effectiveContentView = _swiftUIWrapper.contentView;
|
||||
} else {
|
||||
if (_swiftUIWrapper != nullptr) {
|
||||
UIView *swiftUIContentView = _swiftUIWrapper.contentView;
|
||||
for (UIView *subview = nullptr in swiftUIContentView.subviews) {
|
||||
[self addSubview:subview];
|
||||
}
|
||||
self.clipsToBounds = swiftUIContentView.clipsToBounds;
|
||||
self.layer.mask = swiftUIContentView.layer.mask;
|
||||
|
||||
[self transferVisualPropertiesFromView:swiftUIContentView toView:self];
|
||||
|
||||
[_swiftUIWrapper.hostingView removeFromSuperview];
|
||||
_swiftUIWrapper = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveContentView;
|
||||
}
|
||||
|
||||
// This UIView is the UIView that holds all subviews. It is sometimes not self
|
||||
// because we want to render "overflow ink" that extends beyond the bounds of
|
||||
// the view and is not affected by clipping.
|
||||
- (UIView *)currentContainerView
|
||||
{
|
||||
UIView *effectiveContentView = self.effectiveContentView;
|
||||
|
||||
if (_useCustomContainerView) {
|
||||
if (!_containerView) {
|
||||
_containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
|
||||
for (UIView *subview in self.subviews) {
|
||||
for (UIView *subview = nullptr in effectiveContentView.subviews) {
|
||||
[_containerView addSubview:subview];
|
||||
}
|
||||
_containerView.clipsToBounds = self.clipsToBounds;
|
||||
self.clipsToBounds = NO;
|
||||
_containerView.layer.mask = self.layer.mask;
|
||||
self.layer.mask = nil;
|
||||
[self addSubview:_containerView];
|
||||
_containerView.clipsToBounds = effectiveContentView.clipsToBounds;
|
||||
effectiveContentView.clipsToBounds = NO;
|
||||
_containerView.layer.mask = effectiveContentView.layer.mask;
|
||||
effectiveContentView.layer.mask = nil;
|
||||
[effectiveContentView addSubview:_containerView];
|
||||
}
|
||||
|
||||
return _containerView;
|
||||
effectiveContentView = _containerView;
|
||||
} else {
|
||||
if (_containerView) {
|
||||
for (UIView *subview in _containerView.subviews) {
|
||||
[self addSubview:subview];
|
||||
[effectiveContentView addSubview:subview];
|
||||
}
|
||||
self.clipsToBounds = _containerView.clipsToBounds;
|
||||
self.layer.mask = _containerView.layer.mask;
|
||||
effectiveContentView.clipsToBounds = _containerView.clipsToBounds;
|
||||
effectiveContentView.layer.mask = _containerView.layer.mask;
|
||||
[_containerView removeFromSuperview];
|
||||
_containerView = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
return effectiveContentView;
|
||||
}
|
||||
|
||||
- (void)invalidateLayer
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
CALayer *layer = self.effectiveContentView.layer;
|
||||
|
||||
if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) {
|
||||
return;
|
||||
@@ -910,7 +968,7 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
if (!_backgroundColorLayer) {
|
||||
_backgroundColorLayer = [CALayer layer];
|
||||
_backgroundColorLayer.zPosition = BACKGROUND_COLOR_ZPOSITION;
|
||||
[self.layer addSublayer:_backgroundColorLayer];
|
||||
[layer addSublayer:_backgroundColorLayer];
|
||||
}
|
||||
[self shapeLayerToMatchView:_backgroundColorLayer borderMetrics:borderMetrics];
|
||||
_backgroundColorLayer.backgroundColor = backgroundColor.CGColor;
|
||||
@@ -986,31 +1044,43 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
// filter
|
||||
[_filterLayer removeFromSuperlayer];
|
||||
_filterLayer = nil;
|
||||
if (_swiftUIWrapper != nullptr) {
|
||||
[_swiftUIWrapper updateBlurRadius:@(0)];
|
||||
}
|
||||
self.layer.opacity = (float)_props->opacity;
|
||||
if (!_props->filter.empty()) {
|
||||
float multiplicativeBrightness = 1;
|
||||
bool hasBrightnessFilter = false;
|
||||
for (const auto &primitive : _props->filter) {
|
||||
if (std::holds_alternative<Float>(primitive.parameters)) {
|
||||
if (primitive.type == FilterType::Brightness) {
|
||||
multiplicativeBrightness *= std::get<Float>(primitive.parameters);
|
||||
hasBrightnessFilter = true;
|
||||
} else if (primitive.type == FilterType::Opacity) {
|
||||
self.layer.opacity *= std::get<Float>(primitive.parameters);
|
||||
} else if (primitive.type == FilterType::Blur) {
|
||||
if (_swiftUIWrapper != nullptr) {
|
||||
Float blurRadius = std::get<Float>(primitive.parameters);
|
||||
[_swiftUIWrapper updateBlurRadius:@(blurRadius)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_filterLayer = [CALayer layer];
|
||||
[self shapeLayerToMatchView:_filterLayer borderMetrics:borderMetrics];
|
||||
_filterLayer.compositingFilter = @"multiplyBlendMode";
|
||||
_filterLayer.backgroundColor = [UIColor colorWithRed:multiplicativeBrightness
|
||||
green:multiplicativeBrightness
|
||||
blue:multiplicativeBrightness
|
||||
alpha:self.layer.opacity]
|
||||
.CGColor;
|
||||
// So that this layer is always above any potential sublayers this view may
|
||||
// add
|
||||
_filterLayer.zPosition = CGFLOAT_MAX;
|
||||
[self.layer addSublayer:_filterLayer];
|
||||
if (hasBrightnessFilter) {
|
||||
_filterLayer = [CALayer layer];
|
||||
[self shapeLayerToMatchView:_filterLayer borderMetrics:borderMetrics];
|
||||
_filterLayer.compositingFilter = @"multiplyBlendMode";
|
||||
_filterLayer.backgroundColor = [UIColor colorWithRed:multiplicativeBrightness
|
||||
green:multiplicativeBrightness
|
||||
blue:multiplicativeBrightness
|
||||
alpha:self.layer.opacity]
|
||||
.CGColor;
|
||||
// So that this layer is always above any potential sublayers this view may
|
||||
// add
|
||||
_filterLayer.zPosition = CGFLOAT_MAX;
|
||||
[layer addSublayer:_filterLayer];
|
||||
}
|
||||
}
|
||||
|
||||
// background image
|
||||
@@ -1025,7 +1095,7 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
[self shapeLayerToMatchView:backgroundImageLayer borderMetrics:borderMetrics];
|
||||
backgroundImageLayer.masksToBounds = YES;
|
||||
backgroundImageLayer.zPosition = BACKGROUND_COLOR_ZPOSITION;
|
||||
[self.layer addSublayer:backgroundImageLayer];
|
||||
[layer addSublayer:backgroundImageLayer];
|
||||
[_backgroundImageLayers addObject:backgroundImageLayer];
|
||||
} else if (std::holds_alternative<RadialGradient>(backgroundImage)) {
|
||||
const auto &radialGradient = std::get<RadialGradient>(backgroundImage);
|
||||
@@ -1034,7 +1104,7 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
[self shapeLayerToMatchView:backgroundImageLayer borderMetrics:borderMetrics];
|
||||
backgroundImageLayer.masksToBounds = YES;
|
||||
backgroundImageLayer.zPosition = BACKGROUND_COLOR_ZPOSITION;
|
||||
[self.layer addSublayer:backgroundImageLayer];
|
||||
[layer addSublayer:backgroundImageLayer];
|
||||
[_backgroundImageLayers addObject:backgroundImageLayer];
|
||||
}
|
||||
}
|
||||
@@ -1056,7 +1126,7 @@ static RCTBorderStyle RCTBorderStyleFromOutlineStyle(OutlineStyle outlineStyle)
|
||||
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
|
||||
self.layer.bounds.size);
|
||||
shadowLayer.zPosition = _borderLayer.zPosition;
|
||||
[self.layer addSublayer:shadowLayer];
|
||||
[layer addSublayer:shadowLayer];
|
||||
[_boxShadowLayers addObject:shadowLayer];
|
||||
}
|
||||
}
|
||||
@@ -1410,6 +1480,66 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
return RCTNSStringFromString([[self class] componentDescriptorProvider].name);
|
||||
}
|
||||
|
||||
- (BOOL)styleNeedsSwiftUIContainer
|
||||
{
|
||||
if (!_props->filter.empty()) {
|
||||
for (const auto &primitive : _props->filter) {
|
||||
if (primitive.type == FilterType::Blur) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)transferVisualPropertiesFromView:(UIView *)sourceView toView:(UIView *)destinationView
|
||||
{
|
||||
// shadow
|
||||
destinationView.layer.shadowColor = sourceView.layer.shadowColor;
|
||||
sourceView.layer.shadowColor = nil;
|
||||
destinationView.layer.shadowOffset = sourceView.layer.shadowOffset;
|
||||
sourceView.layer.shadowOffset = CGSizeZero;
|
||||
destinationView.layer.shadowOpacity = sourceView.layer.shadowOpacity;
|
||||
sourceView.layer.shadowOpacity = 0;
|
||||
destinationView.layer.shadowRadius = sourceView.layer.shadowRadius;
|
||||
sourceView.layer.shadowRadius = 0;
|
||||
|
||||
// background
|
||||
destinationView.layer.backgroundColor = sourceView.layer.backgroundColor;
|
||||
sourceView.layer.backgroundColor = nil;
|
||||
if (_backgroundColorLayer != nullptr) {
|
||||
[destinationView.layer addSublayer:_backgroundColorLayer];
|
||||
}
|
||||
|
||||
// border
|
||||
destinationView.layer.borderColor = sourceView.layer.borderColor;
|
||||
sourceView.layer.borderColor = nil;
|
||||
destinationView.layer.borderWidth = sourceView.layer.borderWidth;
|
||||
sourceView.layer.borderWidth = 0;
|
||||
|
||||
// corner
|
||||
destinationView.layer.cornerRadius = sourceView.layer.cornerRadius;
|
||||
sourceView.layer.cornerRadius = 0;
|
||||
destinationView.layer.cornerCurve = sourceView.layer.cornerCurve;
|
||||
|
||||
// custom layers
|
||||
if (_borderLayer != nullptr) {
|
||||
[destinationView.layer addSublayer:_borderLayer];
|
||||
}
|
||||
if (_outlineLayer != nullptr) {
|
||||
[destinationView.layer addSublayer:_outlineLayer];
|
||||
}
|
||||
if (_filterLayer != nullptr) {
|
||||
[destinationView.layer addSublayer:_filterLayer];
|
||||
}
|
||||
for (CALayer *layer = nullptr in _backgroundImageLayers) {
|
||||
[destinationView.layer addSublayer:layer];
|
||||
}
|
||||
for (CALayer *layer = nullptr in _boxShadowLayers) {
|
||||
[destinationView.layer addSublayer:layer];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -63,6 +63,7 @@ Pod::Spec.new do |s|
|
||||
s.dependency "Yoga"
|
||||
s.dependency "React-RCTText"
|
||||
s.dependency "React-jsi"
|
||||
s.dependency "RCTSwiftUIWrapper"
|
||||
|
||||
add_dependency(s, "React-FabricImage")
|
||||
add_dependency(s, "React-Fabric", :additional_framework_paths => [
|
||||
|
||||
@@ -80,7 +80,9 @@ static void RCTPathAddEllipticArc(
|
||||
CGFloat endAngle,
|
||||
BOOL clockwise)
|
||||
{
|
||||
CGFloat xScale = 1, yScale = 1, radius = 0;
|
||||
CGFloat xScale = 1;
|
||||
CGFloat yScale = 1;
|
||||
CGFloat radius = 0;
|
||||
if (size.width != 0) {
|
||||
xScale = 1;
|
||||
yScale = size.height / size.width;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTDefines.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
RCT_EXTERN UIFont *__nullable RCTGetLegacyDefaultFont(CGFloat fontSize, UIFontWeight fontWeight);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -17,8 +17,10 @@ typedef CGFloat RCTFontWeight;
|
||||
* provide a different base font, use this override. The font weight supplied to your
|
||||
* handler will be one of "ultralight", "thin", "light", "regular", "medium",
|
||||
* "semibold", "extrabold", "bold", "heavy", or "black".
|
||||
*
|
||||
* @deprecated Use RCTSetDefaultFontResolver
|
||||
*/
|
||||
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler);
|
||||
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler) __attribute__((deprecated));
|
||||
RCT_EXTERN BOOL RCTHasFontHandlerSet(void);
|
||||
RCT_EXTERN RCTFontWeight RCTGetFontWeight(UIFont *font);
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
#import "RCTFont.h"
|
||||
#import "RCTFont+Private.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@@ -122,6 +124,15 @@ static NSString *FontWeightDescriptionFromUIFontWeight(UIFontWeight fontWeight)
|
||||
return @"regular";
|
||||
}
|
||||
|
||||
UIFont *RCTGetLegacyDefaultFont(CGFloat size, UIFontWeight fontWeight)
|
||||
{
|
||||
if (defaultFontHandler != nil) {
|
||||
return defaultFontHandler(size, FontWeightDescriptionFromUIFontWeight(fontWeight));
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight)
|
||||
{
|
||||
static NSCache<NSValue *, UIFont *> *fontCache = [NSCache new];
|
||||
@@ -135,8 +146,8 @@ static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight)
|
||||
NSValue *cacheKey = [[NSValue alloc] initWithBytes:&key objCType:@encode(CacheKey)];
|
||||
UIFont *font = [fontCache objectForKey:cacheKey];
|
||||
|
||||
if (!font) {
|
||||
if (defaultFontHandler) {
|
||||
if (font == nil) {
|
||||
if (defaultFontHandler != nil) {
|
||||
NSString *fontWeightDescription = FontWeightDescriptionFromUIFontWeight(weight);
|
||||
font = defaultFontHandler(size, fontWeightDescription);
|
||||
} else {
|
||||
|
||||
@@ -679,7 +679,8 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat top = 0, left = 0;
|
||||
CGFloat top = 0;
|
||||
CGFloat left = 0;
|
||||
if (contentSize.width < boundsSize.width) {
|
||||
left = (boundsSize.width - contentSize.width) * 0.5f;
|
||||
}
|
||||
|
||||
@@ -5246,6 +5246,7 @@ public class com/facebook/react/viewmanagers/VirtualViewExperimentalManagerDeleg
|
||||
|
||||
public abstract interface class com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface : com/facebook/react/uimanager/ViewManagerWithGeneratedInterface {
|
||||
public abstract fun setInitialHidden (Landroid/view/View;Z)V
|
||||
public abstract fun setRemoveClippedSubviews (Landroid/view/View;Z)V
|
||||
public abstract fun setRenderState (Landroid/view/View;I)V
|
||||
}
|
||||
|
||||
@@ -6725,9 +6726,10 @@ public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtua
|
||||
public fun onLayoutChange (Landroid/view/View;IIIIIIII)V
|
||||
public fun onModeChange (Lcom/facebook/react/views/virtual/VirtualViewMode;Landroid/graphics/Rect;)V
|
||||
public synthetic fun recycleView$xplat_js_react_native_github_packages_react_native_ReactAndroid_src_main_java_com_facebook_react_views_view_viewAndroid ()V
|
||||
public fun updateClippingRect (Ljava/util/Set;)V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface {
|
||||
public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager : com/facebook/react/views/view/ReactClippingViewManager, com/facebook/react/viewmanagers/VirtualViewExperimentalManagerInterface {
|
||||
public static final field Companion Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimentalManager$Companion;
|
||||
public static final field REACT_CLASS Ljava/lang/String;
|
||||
public fun <init> ()V
|
||||
@@ -6739,6 +6741,7 @@ public final class com/facebook/react/views/virtual/viewexperimental/ReactVirtua
|
||||
public fun setInitialHidden (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;Z)V
|
||||
public synthetic fun setNativeId (Landroid/view/View;Ljava/lang/String;)V
|
||||
public fun setNativeId (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;Ljava/lang/String;)V
|
||||
public synthetic fun setRemoveClippedSubviews (Landroid/view/View;Z)V
|
||||
public synthetic fun setRenderState (Landroid/view/View;I)V
|
||||
public fun setRenderState (Lcom/facebook/react/views/virtual/viewexperimental/ReactVirtualViewExperimental;I)V
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
VERSION_NAME=1000.0.0
|
||||
HERMES_VERSION_NAME=1000.0.0
|
||||
react.internal.publishingGroup=com.facebook.react
|
||||
react.internal.hermesPublishingGroup=com.facebook.hermes
|
||||
|
||||
|
||||
+19
-24
@@ -196,11 +196,9 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext) :
|
||||
|
||||
private val nodesManagerRef = AtomicReference<NativeAnimatedNodesManager?>()
|
||||
|
||||
private var batchingControlledByJS = false // TODO T71377544: delete
|
||||
@Volatile private var currentFrameNumber: Long = 0
|
||||
|
||||
@Volatile private var currentFrameNumber: Long = 0 // TODO T71377544: delete
|
||||
|
||||
@Volatile private var currentBatchNumber: Long = 0
|
||||
@Volatile private var currentBatchNumber: Long = 0 // frame number at last operations dispatch
|
||||
|
||||
private var initializedForFabric = false
|
||||
private var initializedForNonFabric = false
|
||||
@@ -282,22 +280,19 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext) :
|
||||
|
||||
var batchNumber = currentBatchNumber - 1
|
||||
|
||||
// TODO T71377544: delete this when the JS method is confirmed safe
|
||||
if (!batchingControlledByJS) {
|
||||
// The problem we're trying to solve here: we could be in the middle of queueing
|
||||
// a batch of related animation operations when Fabric flushes a batch of MountItems.
|
||||
// It's visually bad if we execute half of the animation ops and then wait another frame
|
||||
// (or more) to execute the rest.
|
||||
// See mFrameNumber. If the dispatchedFrameNumber drifts too far - that
|
||||
// is, if no MountItems are scheduled for a while, which can happen if a tree
|
||||
// is committed but there are no changes - bring these counts back in sync and
|
||||
// execute any queued operations. This number is arbitrary, but we want it low
|
||||
// enough that the user shouldn't be able to see this delay in most cases.
|
||||
currentFrameNumber++
|
||||
if ((currentFrameNumber - currentBatchNumber) > 2) {
|
||||
currentBatchNumber = currentFrameNumber
|
||||
batchNumber = currentBatchNumber
|
||||
}
|
||||
// The problem we're trying to solve here: we could be in the middle of queueing
|
||||
// a batch of related animation operations when Fabric flushes a batch of MountItems.
|
||||
// It's visually bad if we execute half of the animation ops and then wait another frame
|
||||
// (or more) to execute the rest.
|
||||
// See mFrameNumber. If the dispatchedFrameNumber drifts too far - that
|
||||
// is, if no MountItems are scheduled for a while, which can happen if a tree
|
||||
// is committed but there are no changes - bring these counts back in sync and
|
||||
// execute any queued operations. This number is arbitrary, but we want it low
|
||||
// enough that the user shouldn't be able to see this delay in most cases.
|
||||
currentFrameNumber++
|
||||
if ((currentFrameNumber - currentBatchNumber) > 2) {
|
||||
currentBatchNumber = currentFrameNumber
|
||||
batchNumber = currentBatchNumber
|
||||
}
|
||||
|
||||
preOperations.executeBatch(batchNumber, nodesManager)
|
||||
@@ -484,14 +479,14 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext) :
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun startOperationBatch() {
|
||||
batchingControlledByJS = true
|
||||
currentBatchNumber++
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun finishOperationBatch() {
|
||||
batchingControlledByJS = false
|
||||
currentBatchNumber++
|
||||
// no-op
|
||||
}
|
||||
|
||||
override fun createAnimatedNode(tagDouble: Double, config: ReadableMap) {
|
||||
|
||||
-4
@@ -774,12 +774,8 @@ public class NativeAnimatedNodesManager(
|
||||
("Looks like animated nodes graph has ${reason}, there are $activeNodesCount but toposort visited only $updatedNodesCount")
|
||||
)
|
||||
if (eventListenerInitializedForFabric && cyclesDetected == 0) {
|
||||
// TODO T71377544: investigate these SoftExceptions and see if we can remove entirely
|
||||
// or fix the root cause
|
||||
ReactSoftExceptionLogger.logSoftException(TAG, ReactNoCrashSoftException(ex))
|
||||
} else if (eventListenerInitializedForFabric) {
|
||||
// TODO T71377544: investigate these SoftExceptions and see if we can remove entirely
|
||||
// or fix the root cause
|
||||
ReactSoftExceptionLogger.logSoftException(TAG, ReactNoCrashSoftException(ex))
|
||||
} else {
|
||||
throw ex
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public abstract class ReactContextBaseJavaModule : BaseJavaModule {
|
||||
* this method whenever you actually need the Activity and make sure to check for `null`.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Deprecated in 0.80.0. Use getReactApplicationContext.getCurrentActivity() instead.",
|
||||
"Deprecated in 0.80.0. Use getReactApplicationContext().getCurrentActivity() instead.",
|
||||
ReplaceWith("reactApplicationContext.currentActivity"),
|
||||
)
|
||||
protected fun getCurrentActivity(): Activity? {
|
||||
|
||||
+7
-3
@@ -387,10 +387,14 @@ public abstract class DevSupportManagerBase(
|
||||
TracingState.ENABLEDINBACKGROUNDMODE ->
|
||||
DevOptionHandler {
|
||||
UiThreadUtil.runOnUiThread {
|
||||
if (reactInstanceDevHelper is PerfMonitorDevHelper)
|
||||
reactInstanceDevHelper.inspectorTarget?.pauseAndAnalyzeBackgroundTrace()
|
||||
if (reactInstanceDevHelper is PerfMonitorDevHelper) {
|
||||
reactInstanceDevHelper.inspectorTarget?.let {
|
||||
if (it.pauseAndAnalyzeBackgroundTrace()) {
|
||||
openDebugger(DebuggerFrontendPanelName.PERFORMANCE.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
openDebugger(DebuggerFrontendPanelName.PERFORMANCE.toString())
|
||||
}
|
||||
TracingState.DISABLED ->
|
||||
DevOptionHandler {
|
||||
|
||||
+5
-2
@@ -17,8 +17,11 @@ internal interface PerfMonitorInspectorTargetBinding {
|
||||
/** Get the current CDP or background performance tracing state. */
|
||||
public fun getTracingState(): TracingState
|
||||
|
||||
/** Attempt to pause the current background performance trace, and open in DevTools. */
|
||||
public fun pauseAndAnalyzeBackgroundTrace()
|
||||
/**
|
||||
* Attempt to pause the current background performance trace, and open in DevTools. Returns true
|
||||
* if there is an active session that can display the trace, false otherwise.
|
||||
*/
|
||||
public fun pauseAndAnalyzeBackgroundTrace(): Boolean
|
||||
|
||||
/** Attempt to start a new background performance trace. */
|
||||
public fun resumeBackgroundTrace()
|
||||
|
||||
+6
-9
@@ -43,29 +43,23 @@ internal class PerfMonitorOverlayManager(
|
||||
fun enable() {
|
||||
enabled = true
|
||||
init()
|
||||
UiThreadUtil.runOnUiThread { view?.show() }
|
||||
}
|
||||
|
||||
/** Disable the Perf Monitor overlay. Will remain hidden when updates are received. */
|
||||
fun disable() {
|
||||
UiThreadUtil.runOnUiThread { view?.hide() }
|
||||
view = null
|
||||
enabled = false
|
||||
}
|
||||
|
||||
/** Reset the Perf Monitor overlay, e.g. after a reload. */
|
||||
fun reset() {
|
||||
UiThreadUtil.runOnUiThread { view?.resetState() }
|
||||
|
||||
// Update with current recording state
|
||||
onRecordingStateChanged(
|
||||
devHelper.inspectorTarget?.getTracingState() ?: TracingState.ENABLEDINCDPMODE
|
||||
)
|
||||
}
|
||||
|
||||
override fun onNewFocusedEvent(data: PerfMonitorUpdateListener.LongTaskEventData) {
|
||||
view?.updateFocusedEvent(data)
|
||||
}
|
||||
|
||||
override fun onRecordingStateChanged(state: TracingState) {
|
||||
tracingState = state
|
||||
view?.updateRecordingState(state)
|
||||
@@ -74,8 +68,11 @@ internal class PerfMonitorOverlayManager(
|
||||
private fun handleRecordingButtonPress() {
|
||||
when (tracingState) {
|
||||
TracingState.ENABLEDINBACKGROUNDMODE -> {
|
||||
devHelper.inspectorTarget?.pauseAndAnalyzeBackgroundTrace()
|
||||
onRequestOpenDevTools()
|
||||
devHelper.inspectorTarget?.let {
|
||||
if (!it.pauseAndAnalyzeBackgroundTrace()) {
|
||||
onRequestOpenDevTools()
|
||||
}
|
||||
}
|
||||
}
|
||||
TracingState.DISABLED -> {
|
||||
devHelper.inspectorTarget?.resumeBackgroundTrace()
|
||||
|
||||
+51
-177
@@ -13,8 +13,6 @@ import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Gravity
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
@@ -26,194 +24,93 @@ import com.facebook.react.R
|
||||
import com.facebook.react.devsupport.interfaces.TracingState
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder
|
||||
import com.facebook.react.uimanager.PixelUtil
|
||||
import java.util.Locale
|
||||
|
||||
internal class PerfMonitorOverlayView(
|
||||
private val context: Context,
|
||||
private val onButtonPress: () -> Unit,
|
||||
) {
|
||||
private var hidden: Boolean = true
|
||||
private var hasEventData: Boolean = false
|
||||
private val metricsDialog: Dialog
|
||||
private val toolbarDialog: Dialog
|
||||
private val tooltipDialog: Dialog
|
||||
private lateinit var buttonLabel: TextView
|
||||
private lateinit var recordingStateLabel: TextView
|
||||
private lateinit var durationLabel: TextView
|
||||
private val dialog: Dialog
|
||||
private lateinit var statusLabel: TextView
|
||||
private lateinit var tooltipLabel: TextView
|
||||
private var ttl: Int = 0
|
||||
private var hideAfterTimeoutHandler: Handler? = null
|
||||
private lateinit var statusIndicator: TextView
|
||||
|
||||
init {
|
||||
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
|
||||
tooltipDialog = createTooltipDialog()
|
||||
metricsDialog = createMetricsDialog()
|
||||
toolbarDialog = createToolbarDialog()
|
||||
dialog = createToolbarDialog()
|
||||
}
|
||||
|
||||
fun show() {
|
||||
toolbarDialog.show()
|
||||
tooltipDialog.show()
|
||||
if (hasEventData) {
|
||||
toolbarDialog.window?.decorView?.post { updateMetricsDialogPosition() }
|
||||
metricsDialog.show()
|
||||
}
|
||||
hidden = false
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
metricsDialog.hide()
|
||||
toolbarDialog.hide()
|
||||
tooltipDialog.hide()
|
||||
hidden = true
|
||||
}
|
||||
|
||||
fun resetState() {
|
||||
hasEventData = false
|
||||
metricsDialog.hide()
|
||||
}
|
||||
|
||||
fun updateFocusedEvent(data: PerfMonitorUpdateListener.LongTaskEventData) {
|
||||
durationLabel.text = String.format(Locale.US, "%d ms", data.durationMs)
|
||||
durationLabel.setTextColor(getDurationHighlightColor(data.responsivenessScore))
|
||||
hasEventData = true
|
||||
ttl = data.ttl
|
||||
|
||||
hideAfterTimeoutHandler?.removeCallbacksAndMessages(null)
|
||||
|
||||
if (!hidden) {
|
||||
metricsDialog.show()
|
||||
|
||||
// Schedule hiding metrics overlay after ttl milliseconds
|
||||
if (ttl > 0) {
|
||||
if (hideAfterTimeoutHandler == null) {
|
||||
hideAfterTimeoutHandler = Handler(Looper.getMainLooper())
|
||||
}
|
||||
hideAfterTimeoutHandler?.postDelayed({ metricsDialog.hide() }, ttl.toLong())
|
||||
}
|
||||
}
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
fun updateRecordingState(state: TracingState) {
|
||||
recordingStateLabel.text =
|
||||
when (state) {
|
||||
TracingState.ENABLEDINBACKGROUNDMODE -> "Profiling: ON"
|
||||
TracingState.DISABLED -> "Profiling: OFF"
|
||||
TracingState.ENABLEDINCDPMODE -> "Profiling: DISABLED"
|
||||
}
|
||||
buttonLabel.text =
|
||||
when (state) {
|
||||
TracingState.ENABLEDINBACKGROUNDMODE -> "Open ↗️"
|
||||
TracingState.DISABLED -> "Start"
|
||||
TracingState.ENABLEDINCDPMODE -> ""
|
||||
}
|
||||
tooltipLabel.text =
|
||||
when (state) {
|
||||
TracingState.ENABLEDINBACKGROUNDMODE -> "Dev Menu > Finish performance trace"
|
||||
TracingState.DISABLED -> "Dev Menu > Start performance trace"
|
||||
TracingState.ENABLEDINCDPMODE -> ""
|
||||
}
|
||||
if (state == TracingState.ENABLEDINCDPMODE) {
|
||||
tooltipDialog.hide()
|
||||
dialog.hide()
|
||||
return
|
||||
}
|
||||
|
||||
if (state == TracingState.ENABLEDINBACKGROUNDMODE) {
|
||||
(statusIndicator.background as GradientDrawable).setColor(Color.RED)
|
||||
statusLabel.text = "Background Profiling Active"
|
||||
tooltipLabel.text = "Press ☰ to open"
|
||||
} else {
|
||||
tooltipDialog.show()
|
||||
(statusIndicator.background as GradientDrawable).setColor(Color.GRAY)
|
||||
statusLabel.text = "Background Profiling Stopped"
|
||||
tooltipLabel.text = "Press ☰ to restart"
|
||||
}
|
||||
|
||||
toolbarDialog.window?.decorView?.post { updateMetricsDialogPosition() }
|
||||
}
|
||||
|
||||
private fun createMetricsDialog(): Dialog {
|
||||
val containerLayout = createInnerLayout()
|
||||
val longTaskLabel =
|
||||
TextView(context).apply {
|
||||
textSize = TEXT_SIZE_PRIMARY
|
||||
text = "Long Task"
|
||||
setTextColor(Color.WHITE)
|
||||
typeface = TYPEFACE_BOLD
|
||||
}
|
||||
durationLabel =
|
||||
TextView(context).apply {
|
||||
textSize = TEXT_SIZE_PRIMARY
|
||||
setTextColor(COLOR_TEXT_GREEN)
|
||||
typeface = TYPEFACE_BOLD
|
||||
}
|
||||
containerLayout.addView(longTaskLabel)
|
||||
containerLayout.addView(durationLabel)
|
||||
|
||||
val dialog =
|
||||
createAnchoredDialog(getMetricsDialogOffsetX(), dpToPx(16f)).apply {
|
||||
setContentView(containerLayout)
|
||||
}
|
||||
dialog.window?.apply {
|
||||
attributes =
|
||||
attributes?.apply {
|
||||
flags =
|
||||
flags or
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
}
|
||||
}
|
||||
|
||||
return dialog
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun createToolbarDialog(): Dialog {
|
||||
val buttonInner = createInnerLayout()
|
||||
recordingStateLabel =
|
||||
statusIndicator =
|
||||
TextView(context).apply {
|
||||
width = dpToPx(12f).toInt()
|
||||
height = dpToPx(12f).toInt()
|
||||
background =
|
||||
GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(Color.RED)
|
||||
}
|
||||
}
|
||||
|
||||
val textContainer =
|
||||
LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams =
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
}
|
||||
statusLabel =
|
||||
TextView(context).apply {
|
||||
textSize = TEXT_SIZE_PRIMARY
|
||||
setTextColor(Color.WHITE)
|
||||
typeface = TYPEFACE_BOLD
|
||||
}
|
||||
buttonInner.addView(recordingStateLabel)
|
||||
buttonLabel =
|
||||
TextView(context).apply {
|
||||
textSize = TEXT_SIZE_PRIMARY
|
||||
setTextColor(COLOR_TEXT_BLUE)
|
||||
typeface = TYPEFACE_BOLD
|
||||
}
|
||||
buttonInner.addView(buttonLabel)
|
||||
val buttonView =
|
||||
LinearLayout(context).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(
|
||||
dpToPx(8f).toInt(),
|
||||
dpToPx(16f).toInt(),
|
||||
dpToPx(16f).toInt(),
|
||||
dpToPx(8f).toInt(),
|
||||
)
|
||||
addView(buttonInner)
|
||||
setOnClickListener { onButtonPress() }
|
||||
}
|
||||
|
||||
val dialog = createAnchoredDialog(dpToPx(0f), dpToPx(0f)).apply { setContentView(buttonView) }
|
||||
dialog.window?.apply {
|
||||
attributes =
|
||||
attributes?.apply { flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE }
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun createTooltipDialog(): Dialog {
|
||||
val containerLayout = createInnerLayout()
|
||||
tooltipLabel =
|
||||
TextView(context).apply {
|
||||
textSize = TEXT_SIZE_ACCESSORY
|
||||
setTextColor(Color.WHITE)
|
||||
typeface = TYPEFACE_BOLD
|
||||
}
|
||||
containerLayout.addView(tooltipLabel)
|
||||
textContainer.addView(statusLabel)
|
||||
textContainer.addView(tooltipLabel)
|
||||
|
||||
val containerLayout = createInnerLayout()
|
||||
containerLayout.setOnClickListener { onButtonPress() }
|
||||
containerLayout.addView(statusIndicator)
|
||||
containerLayout.addView(textContainer)
|
||||
|
||||
val dialog =
|
||||
createAnchoredDialog(dpToPx(16f), dpToPx(52f)).apply { setContentView(containerLayout) }
|
||||
createAnchoredDialog(dpToPx(12f), dpToPx(12f)).apply { setContentView(containerLayout) }
|
||||
dialog.window?.apply {
|
||||
attributes =
|
||||
attributes?.apply {
|
||||
flags =
|
||||
flags or
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
}
|
||||
attributes?.apply { flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE }
|
||||
}
|
||||
|
||||
return dialog
|
||||
@@ -278,35 +175,12 @@ internal class PerfMonitorOverlayView(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMetricsDialogOffsetX(): Float {
|
||||
val toolbarWidth = toolbarDialog?.window?.decorView?.width ?: 0
|
||||
return toolbarWidth.toFloat()
|
||||
}
|
||||
|
||||
private fun updateMetricsDialogPosition() {
|
||||
metricsDialog?.window?.apply {
|
||||
attributes = attributes?.apply { x = getMetricsDialogOffsetX().toInt() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDurationHighlightColor(responsivenessScore: Int): Int {
|
||||
return when (responsivenessScore) {
|
||||
2 -> COLOR_TEXT_RED
|
||||
1 -> COLOR_TEXT_YELLOW
|
||||
else -> COLOR_TEXT_GREEN
|
||||
}
|
||||
}
|
||||
|
||||
private fun dpToPx(dp: Float): Float = PixelUtil.toPixelFromDIP(dp)
|
||||
|
||||
companion object {
|
||||
private val COLOR_TEXT_GREEN = Color.parseColor("#4AEB2F")
|
||||
private val COLOR_TEXT_YELLOW = Color.parseColor("#FFAA00")
|
||||
private val COLOR_TEXT_RED = Color.parseColor("#FF0000")
|
||||
private val COLOR_TEXT_BLUE = Color.parseColor("#00B0FF")
|
||||
private val COLOR_OVERLAY_BORDER = Color.parseColor("#6C6C6C")
|
||||
private val TEXT_SIZE_PRIMARY = 13f
|
||||
private val TEXT_SIZE_ACCESSORY = 9f
|
||||
private val TEXT_SIZE_PRIMARY = 12f
|
||||
private val TEXT_SIZE_ACCESSORY = 10f
|
||||
private val TYPEFACE_BOLD = Typeface.create("sans-serif", Typeface.BOLD)
|
||||
}
|
||||
}
|
||||
|
||||
-9
@@ -10,15 +10,6 @@ import com.facebook.react.devsupport.interfaces.TracingState
|
||||
|
||||
/** [Experimental] An interface for subscribing to updates for the V2 Perf Monitor. */
|
||||
internal interface PerfMonitorUpdateListener {
|
||||
data class LongTaskEventData(
|
||||
val durationMs: Int,
|
||||
val responsivenessScore: Int,
|
||||
val ttl: Int,
|
||||
)
|
||||
|
||||
/** Called when a new active performance event should be displayed. */
|
||||
fun onNewFocusedEvent(data: LongTaskEventData)
|
||||
|
||||
/** Called when the recording state of the background performance trace has changed. */
|
||||
fun onRecordingStateChanged(state: TracingState)
|
||||
}
|
||||
|
||||
+11
-6
@@ -99,7 +99,6 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -738,11 +737,17 @@ public class FabricUIManager
|
||||
* vertical insets.
|
||||
*/
|
||||
private long getEncodedScreenSizeWithoutVerticalInsets(int surfaceId) {
|
||||
SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
|
||||
Objects.requireNonNull(surfaceMountingManager);
|
||||
ThemedReactContext context = Objects.requireNonNull(surfaceMountingManager.getContext());
|
||||
return DisplayMetricsHolder.getEncodedScreenSizeWithoutVerticalInsets(
|
||||
context.getCurrentActivity());
|
||||
ThemedReactContext context =
|
||||
mMountingManager
|
||||
.getSurfaceManagerEnforced(surfaceId, "getEncodedScreenSizeWithoutVerticalInsets")
|
||||
.getContext();
|
||||
if (context == null) {
|
||||
FLog.w(TAG, "Couldn't get context from SurfaceMountingManager for surfaceId %d", surfaceId);
|
||||
return 0;
|
||||
} else {
|
||||
return DisplayMetricsHolder.getEncodedScreenSizeWithoutVerticalInsets(
|
||||
context.getCurrentActivity());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-1
@@ -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<<8300eb10fb4906468cd5f04afd8e16cd>>
|
||||
* @generated SignedSource<<f089964c958dfcac00f83c3371ccbefc>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -90,6 +90,12 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun enableAccumulatedUpdatesInRawPropsAndroid(): Boolean = accessor.enableAccumulatedUpdatesInRawPropsAndroid()
|
||||
|
||||
/**
|
||||
* Enables linear text rendering on Android wherever subpixel text rendering is enabled
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableAndroidLinearText(): Boolean = accessor.enableAndroidLinearText()
|
||||
|
||||
/**
|
||||
* Enables various optimizations throughout the path of measuring text on Android.
|
||||
*/
|
||||
@@ -246,6 +252,12 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun enableResourceTimingAPI(): Boolean = accessor.enableResourceTimingAPI()
|
||||
|
||||
/**
|
||||
* When enabled, it will use SwiftUI for filter effects like blur on iOS.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableSwiftUIBasedFilters(): Boolean = accessor.enableSwiftUIBasedFilters()
|
||||
|
||||
/**
|
||||
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
|
||||
*/
|
||||
|
||||
+21
-1
@@ -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<<ea5133af611c4d1e7e70155a3a74492d>>
|
||||
* @generated SignedSource<<912dec895495052328e7e52b94a6738a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -30,6 +30,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var disableTextLayoutManagerCacheAndroidCache: Boolean? = null
|
||||
private var enableAccessibilityOrderCache: Boolean? = null
|
||||
private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null
|
||||
private var enableAndroidLinearTextCache: Boolean? = null
|
||||
private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null
|
||||
private var enableBridgelessArchitectureCache: Boolean? = null
|
||||
private var enableCppPropsIteratorSetterCache: Boolean? = null
|
||||
@@ -56,6 +57,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var enablePreparedTextLayoutCache: Boolean? = null
|
||||
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
|
||||
private var enableResourceTimingAPICache: Boolean? = null
|
||||
private var enableSwiftUIBasedFiltersCache: Boolean? = null
|
||||
private var enableViewCullingCache: Boolean? = null
|
||||
private var enableViewRecyclingCache: Boolean? = null
|
||||
private var enableViewRecyclingForImageCache: Boolean? = null
|
||||
@@ -182,6 +184,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableAndroidLinearText(): Boolean {
|
||||
var cached = enableAndroidLinearTextCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.enableAndroidLinearText()
|
||||
enableAndroidLinearTextCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableAndroidTextMeasurementOptimizations(): Boolean {
|
||||
var cached = enableAndroidTextMeasurementOptimizationsCache
|
||||
if (cached == null) {
|
||||
@@ -416,6 +427,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableSwiftUIBasedFilters(): Boolean {
|
||||
var cached = enableSwiftUIBasedFiltersCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.enableSwiftUIBasedFilters()
|
||||
enableSwiftUIBasedFiltersCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewCulling(): Boolean {
|
||||
var cached = enableViewCullingCache
|
||||
if (cached == null) {
|
||||
|
||||
+5
-1
@@ -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<<cf5b9f099846d4f954304905f77ebe17>>
|
||||
* @generated SignedSource<<ba1e53d93b9fdaf298a034543bc44a57>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -48,6 +48,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableAccumulatedUpdatesInRawPropsAndroid(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableAndroidLinearText(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableAndroidTextMeasurementOptimizations(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableBridgelessArchitecture(): Boolean
|
||||
@@ -100,6 +102,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableResourceTimingAPI(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableSwiftUIBasedFilters(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewCulling(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewRecycling(): Boolean
|
||||
|
||||
+5
-1
@@ -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<<305496047314dc97352a6e0dad0bba54>>
|
||||
* @generated SignedSource<<7601cbcde75ff19ef0fe67f6faef2549>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun enableAccumulatedUpdatesInRawPropsAndroid(): Boolean = false
|
||||
|
||||
override fun enableAndroidLinearText(): Boolean = false
|
||||
|
||||
override fun enableAndroidTextMeasurementOptimizations(): Boolean = false
|
||||
|
||||
override fun enableBridgelessArchitecture(): Boolean = false
|
||||
@@ -95,6 +97,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun enableResourceTimingAPI(): Boolean = false
|
||||
|
||||
override fun enableSwiftUIBasedFilters(): Boolean = false
|
||||
|
||||
override fun enableViewCulling(): Boolean = false
|
||||
|
||||
override fun enableViewRecycling(): Boolean = false
|
||||
|
||||
+23
-1
@@ -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<<f3798eada96337cf98715fa580823b30>>
|
||||
* @generated SignedSource<<3a13f8e35423b634ed4ef46fbac3c1e9>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -34,6 +34,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var disableTextLayoutManagerCacheAndroidCache: Boolean? = null
|
||||
private var enableAccessibilityOrderCache: Boolean? = null
|
||||
private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null
|
||||
private var enableAndroidLinearTextCache: Boolean? = null
|
||||
private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null
|
||||
private var enableBridgelessArchitectureCache: Boolean? = null
|
||||
private var enableCppPropsIteratorSetterCache: Boolean? = null
|
||||
@@ -60,6 +61,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var enablePreparedTextLayoutCache: Boolean? = null
|
||||
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
|
||||
private var enableResourceTimingAPICache: Boolean? = null
|
||||
private var enableSwiftUIBasedFiltersCache: Boolean? = null
|
||||
private var enableViewCullingCache: Boolean? = null
|
||||
private var enableViewRecyclingCache: Boolean? = null
|
||||
private var enableViewRecyclingForImageCache: Boolean? = null
|
||||
@@ -196,6 +198,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableAndroidLinearText(): Boolean {
|
||||
var cached = enableAndroidLinearTextCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.enableAndroidLinearText()
|
||||
accessedFeatureFlags.add("enableAndroidLinearText")
|
||||
enableAndroidLinearTextCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableAndroidTextMeasurementOptimizations(): Boolean {
|
||||
var cached = enableAndroidTextMeasurementOptimizationsCache
|
||||
if (cached == null) {
|
||||
@@ -456,6 +468,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableSwiftUIBasedFilters(): Boolean {
|
||||
var cached = enableSwiftUIBasedFiltersCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.enableSwiftUIBasedFilters()
|
||||
accessedFeatureFlags.add("enableSwiftUIBasedFilters")
|
||||
enableSwiftUIBasedFiltersCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewCulling(): Boolean {
|
||||
var cached = enableViewCullingCache
|
||||
if (cached == null) {
|
||||
|
||||
+3
-1
@@ -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<<f82b5905d822f8009c054a9443f86e39>>
|
||||
* @generated SignedSource<<4464982256b8ce543d9235fccbf67054>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -27,6 +27,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android : ReactN
|
||||
|
||||
override fun enableFabricRenderer(): Boolean = true
|
||||
|
||||
override fun enableWebPerformanceAPIsByDefault(): Boolean = true
|
||||
|
||||
override fun useNativeViewConfigsInBridgelessMode(): Boolean = true
|
||||
|
||||
override fun useTurboModuleInterop(): Boolean = true
|
||||
|
||||
+5
-1
@@ -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<<c36e7ac588eb68660f000e7792ec2384>>
|
||||
* @generated SignedSource<<143b568248e68033efaefc3f178ff6db>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,8 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun enableAccumulatedUpdatesInRawPropsAndroid(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableAndroidLinearText(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableAndroidTextMeasurementOptimizations(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableBridgelessArchitecture(): Boolean
|
||||
@@ -95,6 +97,8 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun enableResourceTimingAPI(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableSwiftUIBasedFilters(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewCulling(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewRecycling(): Boolean
|
||||
|
||||
+1
-10
@@ -55,7 +55,7 @@ public class AndroidInfoModule(reactContext: ReactApplicationContext) :
|
||||
constants["ServerHost"] =
|
||||
AndroidInfoHelpers.getServerHost(reactApplicationContext.applicationContext)
|
||||
}
|
||||
constants["isTesting"] = "true" == System.getProperty(IS_TESTING) || isRunningScreenshotTest()
|
||||
constants["isTesting"] = "true" == System.getProperty(IS_TESTING)
|
||||
val isDisableAnimations = System.getProperty(IS_DISABLE_ANIMATIONS)
|
||||
if (isDisableAnimations != null) {
|
||||
constants["isDisableAnimations"] = "true" == isDisableAnimations
|
||||
@@ -71,15 +71,6 @@ public class AndroidInfoModule(reactContext: ReactApplicationContext) :
|
||||
|
||||
override fun invalidate() {}
|
||||
|
||||
private fun isRunningScreenshotTest(): Boolean {
|
||||
return try {
|
||||
Class.forName("com.facebook.testing.react.screenshots.ReactAppScreenshotTestActivity")
|
||||
true
|
||||
} catch (ignored: ClassNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public const val NAME: String = NativePlatformConstantsAndroidSpec.NAME
|
||||
private const val IS_TESTING = "IS_TESTING"
|
||||
|
||||
+5
-15
@@ -37,7 +37,7 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
|
||||
|
||||
external fun startBackgroundTrace(): Boolean
|
||||
|
||||
external fun stopAndStashBackgroundTrace()
|
||||
external fun stopAndMaybeEmitBackgroundTrace(): Boolean
|
||||
|
||||
external fun stopAndDiscardBackgroundTrace()
|
||||
|
||||
@@ -51,11 +51,13 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
|
||||
perfMonitorListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun pauseAndAnalyzeBackgroundTrace() {
|
||||
stopAndStashBackgroundTrace()
|
||||
override fun pauseAndAnalyzeBackgroundTrace(): Boolean {
|
||||
val emitted = stopAndMaybeEmitBackgroundTrace()
|
||||
perfMonitorListeners.forEach { listener ->
|
||||
listener.onRecordingStateChanged(TracingState.DISABLED)
|
||||
}
|
||||
|
||||
return emitted
|
||||
}
|
||||
|
||||
override fun resumeBackgroundTrace() {
|
||||
@@ -65,18 +67,6 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
|
||||
}
|
||||
}
|
||||
|
||||
fun handleNativePerfMonitorMetricUpdate(
|
||||
longTaskDurationMs: Int,
|
||||
responsivenessScore: Int,
|
||||
ttl: Int,
|
||||
) {
|
||||
perfMonitorListeners.forEach { listener ->
|
||||
listener.onNewFocusedEvent(
|
||||
PerfMonitorUpdateListener.LongTaskEventData(longTaskDurationMs, responsivenessScore, ttl)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
mHybridData.resetNative()
|
||||
}
|
||||
|
||||
+18
-2
@@ -245,7 +245,15 @@ public object ReactScrollViewHelper {
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@JvmStatic
|
||||
public fun removeScrollListener(listener: ScrollListener) {
|
||||
scrollListeners.removeIf { it.get() == null || it.get() == listener }
|
||||
// Avoid using removeIf, only available in API 26+
|
||||
val toRemove = ArrayList<WeakReference<ScrollListener>>()
|
||||
for (ref in scrollListeners) {
|
||||
val target = ref.get()
|
||||
if (target == null || target == listener) {
|
||||
toRemove.add(ref)
|
||||
}
|
||||
}
|
||||
scrollListeners.removeAll(toRemove)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -256,7 +264,15 @@ public object ReactScrollViewHelper {
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@JvmStatic
|
||||
public fun removeLayoutChangeListener(listener: LayoutChangeListener) {
|
||||
layoutChangeListeners.removeIf { it.get() == null || it.get() == listener }
|
||||
// Avoid using removeIf, only available in API 26+
|
||||
val toRemove = ArrayList<WeakReference<LayoutChangeListener>>()
|
||||
for (ref in layoutChangeListeners) {
|
||||
val target = ref.get()
|
||||
if (target == null || target == listener) {
|
||||
toRemove.add(ref)
|
||||
}
|
||||
}
|
||||
layoutChangeListeners.removeAll(toRemove)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+26
-8
@@ -50,11 +50,13 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean {
|
||||
internal class VirtualViewContainerState {
|
||||
|
||||
private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
|
||||
private val hysteresisRatio: Double = ReactNativeFeatureFlags.virtualViewHysteresisRatio()
|
||||
|
||||
private val virtualViews: MutableSet<VirtualView> = mutableSetOf()
|
||||
private val emptyRect: Rect = Rect()
|
||||
private val visibleRect: Rect = Rect()
|
||||
private val prerenderRect: Rect = Rect()
|
||||
private val hysteresisRect: Rect = Rect()
|
||||
private val onWindowFocusChangeListener =
|
||||
if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) {
|
||||
ViewTreeObserver.OnWindowFocusChangeListener {
|
||||
@@ -119,11 +121,12 @@ internal class VirtualViewContainerState {
|
||||
(-prerenderRect.height() * prerenderRatio).toInt(),
|
||||
)
|
||||
|
||||
val virtualViewsIt = if (virtualView != null) listOf(virtualView) else virtualViews
|
||||
val virtualViewsIt =
|
||||
if (virtualView != null) listOf(virtualView) else virtualViews.toMutableSet()
|
||||
virtualViewsIt.forEach { vv ->
|
||||
val rect = vv.containerRelativeRect
|
||||
|
||||
var mode = VirtualViewMode.Hidden
|
||||
var mode: VirtualViewMode? = VirtualViewMode.Hidden
|
||||
var thresholdRect = emptyRect
|
||||
when {
|
||||
rectsOverlap(rect, visibleRect) -> {
|
||||
@@ -142,14 +145,29 @@ internal class VirtualViewContainerState {
|
||||
mode = VirtualViewMode.Prerender
|
||||
thresholdRect = prerenderRect
|
||||
}
|
||||
else -> {}
|
||||
else -> {
|
||||
if (hysteresisRatio > 0.0) {
|
||||
hysteresisRect.set(prerenderRect)
|
||||
hysteresisRect.inset(
|
||||
(-visibleRect.width() * hysteresisRatio).toInt(),
|
||||
(-visibleRect.height() * hysteresisRatio).toInt(),
|
||||
)
|
||||
if (rectsOverlap(rect, hysteresisRect)) {
|
||||
mode = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugLog(
|
||||
"updateModes",
|
||||
{ "virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect" },
|
||||
)
|
||||
vv.onModeChange(mode, thresholdRect)
|
||||
if (mode != null) {
|
||||
vv.onModeChange(mode, thresholdRect)
|
||||
debugLog(
|
||||
"updateModes",
|
||||
{
|
||||
"virtualView=${vv.virtualViewID} mode=$mode rect=$rect thresholdRect=$thresholdRect"
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -14,6 +14,7 @@ import android.text.TextPaint
|
||||
import android.text.style.MetricAffectingSpan
|
||||
import com.facebook.react.common.ReactConstants
|
||||
import com.facebook.react.common.assets.ReactFontManager
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.views.text.ReactTypefaceUtils
|
||||
|
||||
/**
|
||||
@@ -73,6 +74,9 @@ internal class CustomStyleSpan(
|
||||
fontFeatureSettings = fontFeatureSettingsParam
|
||||
setTypeface(typeface)
|
||||
isSubpixelText = true
|
||||
if (ReactNativeFeatureFlags.enableAndroidLinearText()) {
|
||||
isLinearText = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-7
@@ -611,17 +611,26 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
|
||||
|
||||
// Match behavior of CustomStyleSpan and enable SUBPIXEL_TEXT_FLAG when setting anything
|
||||
// nonstandard
|
||||
val enableSubpixelText =
|
||||
fontStyle != ReactConstants.UNSET ||
|
||||
fontWeight != ReactConstants.UNSET ||
|
||||
fontFamily != null ||
|
||||
fontFeatureSettings != null
|
||||
paintFlags =
|
||||
if (
|
||||
fontStyle != ReactConstants.UNSET ||
|
||||
fontWeight != ReactConstants.UNSET ||
|
||||
fontFamily != null ||
|
||||
fontFeatureSettings != null
|
||||
) {
|
||||
if (enableSubpixelText) {
|
||||
paintFlags or Paint.SUBPIXEL_TEXT_FLAG
|
||||
} else {
|
||||
paintFlags and (Paint.SUBPIXEL_TEXT_FLAG.inv())
|
||||
paintFlags and Paint.SUBPIXEL_TEXT_FLAG.inv()
|
||||
}
|
||||
|
||||
if (ReactNativeFeatureFlags.enableAndroidLinearText()) {
|
||||
paintFlags =
|
||||
if (enableSubpixelText) {
|
||||
paintFlags or Paint.LINEAR_TEXT_FLAG
|
||||
} else {
|
||||
paintFlags and Paint.LINEAR_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun requestFocusFromJS() {
|
||||
|
||||
+49
-2
@@ -16,9 +16,11 @@ import com.facebook.common.logging.FLog
|
||||
import com.facebook.react.R
|
||||
import com.facebook.react.common.build.ReactBuildConfig
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.uimanager.ReactClippingViewGroup
|
||||
import com.facebook.react.uimanager.ReactRoot
|
||||
import com.facebook.react.views.scroll.VirtualView
|
||||
import com.facebook.react.views.scroll.VirtualViewContainer
|
||||
import com.facebook.react.views.scroll.debugLog
|
||||
import com.facebook.react.views.view.ReactViewGroup
|
||||
import com.facebook.react.views.virtual.VirtualViewMode
|
||||
import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter
|
||||
@@ -34,6 +36,7 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
private var scrollView: VirtualViewContainer? = null
|
||||
|
||||
private val lastContainerRelativeRect: Rect = Rect()
|
||||
private val lastClippingRect: Rect = Rect()
|
||||
override val containerRelativeRect: Rect = Rect()
|
||||
private var offsetX: Int = 0
|
||||
private var offsetY: Int = 0
|
||||
@@ -120,6 +123,7 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
modeChangeEmitter = null
|
||||
hadLayout = false
|
||||
lastContainerRelativeRect.setEmpty()
|
||||
lastClippingRect.setEmpty()
|
||||
containerRelativeRect.setEmpty()
|
||||
}
|
||||
|
||||
@@ -132,7 +136,12 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
modeChangeEmitter ?: return
|
||||
scrollView ?: return
|
||||
|
||||
if (newMode == VirtualViewMode.Visible) {
|
||||
updateClippingRect(null)
|
||||
}
|
||||
|
||||
if (newMode == mode) {
|
||||
debugLog("onModeChange") { "no change $newMode" }
|
||||
return
|
||||
}
|
||||
|
||||
@@ -141,6 +150,10 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
|
||||
debugLog("onModeChange") { "$oldMode->$newMode" }
|
||||
|
||||
if (oldMode == VirtualViewMode.Visible) {
|
||||
updateClippingRect(null)
|
||||
}
|
||||
|
||||
when (newMode) {
|
||||
VirtualViewMode.Visible -> {
|
||||
if (renderState == VirtualViewRenderState.Unknown) {
|
||||
@@ -187,6 +200,37 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We co-opt subview clipping on ReactVirtualView by returning the
|
||||
// clipping rect of the ScrollView. This means we clip the children of ReactVirtualView
|
||||
// when they are out of the viewport, but not ReactVirtualView itself.
|
||||
override fun updateClippingRect(excludedViews: Set<Int>?) {
|
||||
if (!_removeClippedSubviews) {
|
||||
return
|
||||
}
|
||||
|
||||
// If no ScrollView, or ScrollView has disabled removeClippedSubviews, use default behavior
|
||||
if (
|
||||
scrollView == null ||
|
||||
!((scrollView as ReactClippingViewGroup)?.removeClippedSubviews ?: false)
|
||||
) {
|
||||
super.updateClippingRect(excludedViews)
|
||||
return
|
||||
}
|
||||
|
||||
val clippingRect = checkNotNull(clippingRect)
|
||||
|
||||
(scrollView as ReactClippingViewGroup).getClippingRect(clippingRect)
|
||||
clippingRect.intersect(containerRelativeRect)
|
||||
clippingRect.offset(-containerRelativeRect.left, -containerRelativeRect.top)
|
||||
|
||||
if (lastClippingRect == clippingRect) {
|
||||
return
|
||||
}
|
||||
|
||||
updateClippingToRect(clippingRect, excludedViews)
|
||||
lastClippingRect.set(clippingRect)
|
||||
}
|
||||
|
||||
private fun updateParentOffset() {
|
||||
val virtualViewScrollView = scrollView ?: return
|
||||
offsetX = 0
|
||||
@@ -212,8 +256,11 @@ public class ReactVirtualViewExperimental(context: Context) :
|
||||
debugLog("reportRectChangeToContainer") { "no rect change $containerRelativeRect" }
|
||||
return
|
||||
}
|
||||
scrollView?.virtualViewContainerState?.onChange(this)
|
||||
lastContainerRelativeRect.set(containerRelativeRect)
|
||||
|
||||
if (scrollView != null) {
|
||||
scrollView?.virtualViewContainerState?.onChange(this)
|
||||
lastContainerRelativeRect.set(containerRelativeRect)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getScrollView(): VirtualViewContainer? = traverseParentStack(true)
|
||||
|
||||
+2
-2
@@ -13,12 +13,12 @@ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.UIManagerHelper
|
||||
import com.facebook.react.uimanager.ViewGroupManager
|
||||
import com.facebook.react.uimanager.ViewManagerDelegate
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
import com.facebook.react.uimanager.events.EventDispatcher
|
||||
import com.facebook.react.viewmanagers.VirtualViewExperimentalManagerDelegate
|
||||
import com.facebook.react.viewmanagers.VirtualViewExperimentalManagerInterface
|
||||
import com.facebook.react.views.view.ReactClippingViewManager
|
||||
import com.facebook.react.views.virtual.VirtualViewMode
|
||||
import com.facebook.react.views.virtual.VirtualViewModeChangeEmitter
|
||||
import com.facebook.react.views.virtual.VirtualViewModeChangeEvent
|
||||
@@ -26,7 +26,7 @@ import com.facebook.react.views.virtual.VirtualViewRenderState
|
||||
|
||||
@ReactModule(name = ReactVirtualViewExperimentalManager.REACT_CLASS)
|
||||
public class ReactVirtualViewExperimentalManager :
|
||||
ViewGroupManager<ReactVirtualViewExperimental>(),
|
||||
ReactClippingViewManager<ReactVirtualViewExperimental>(),
|
||||
VirtualViewExperimentalManagerInterface<ReactVirtualViewExperimental> {
|
||||
|
||||
private val _delegate = VirtualViewExperimentalManagerDelegate(this)
|
||||
|
||||
+29
-1
@@ -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<<be39ff0cb3b7c5881510b1528c17465a>>
|
||||
* @generated SignedSource<<92c9aa29df580c6d12faa14e60b00e4f>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -99,6 +99,12 @@ class ReactNativeFeatureFlagsJavaProvider
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool enableAndroidLinearText() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableAndroidLinearText");
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool enableAndroidTextMeasurementOptimizations() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableAndroidTextMeasurementOptimizations");
|
||||
@@ -255,6 +261,12 @@ class ReactNativeFeatureFlagsJavaProvider
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool enableSwiftUIBasedFilters() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableSwiftUIBasedFilters");
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool enableViewCulling() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableViewCulling");
|
||||
@@ -519,6 +531,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableAccumulatedUpdatesInRawPropsAndro
|
||||
return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::enableAndroidLinearText(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::enableAndroidLinearText();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::enableAndroidTextMeasurementOptimizations(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::enableAndroidTextMeasurementOptimizations();
|
||||
@@ -649,6 +666,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableResourceTimingAPI(
|
||||
return ReactNativeFeatureFlags::enableResourceTimingAPI();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::enableSwiftUIBasedFilters();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::enableViewCulling(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::enableViewCulling();
|
||||
@@ -885,6 +907,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
|
||||
makeNativeMethod(
|
||||
"enableAccumulatedUpdatesInRawPropsAndroid",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableAccumulatedUpdatesInRawPropsAndroid),
|
||||
makeNativeMethod(
|
||||
"enableAndroidLinearText",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableAndroidLinearText),
|
||||
makeNativeMethod(
|
||||
"enableAndroidTextMeasurementOptimizations",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableAndroidTextMeasurementOptimizations),
|
||||
@@ -963,6 +988,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
|
||||
makeNativeMethod(
|
||||
"enableResourceTimingAPI",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableResourceTimingAPI),
|
||||
makeNativeMethod(
|
||||
"enableSwiftUIBasedFilters",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters),
|
||||
makeNativeMethod(
|
||||
"enableViewCulling",
|
||||
JReactNativeFeatureFlagsCxxInterop::enableViewCulling),
|
||||
|
||||
+7
-1
@@ -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<<8cd0afd7badb6739b4e8f9b0e630677c>>
|
||||
* @generated SignedSource<<124d1378990fdd09a4d97f9103f81a6a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -60,6 +60,9 @@ class JReactNativeFeatureFlagsCxxInterop
|
||||
static bool enableAccumulatedUpdatesInRawPropsAndroid(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool enableAndroidLinearText(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool enableAndroidTextMeasurementOptimizations(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
@@ -138,6 +141,9 @@ class JReactNativeFeatureFlagsCxxInterop
|
||||
static bool enableResourceTimingAPI(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool enableSwiftUIBasedFilters(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool enableViewCulling(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
|
||||
+10
-14
@@ -120,17 +120,6 @@ void JReactHostInspectorTarget::onSetPausedInDebuggerMessage(
|
||||
}
|
||||
}
|
||||
|
||||
void JReactHostInspectorTarget::unstable_onPerfMonitorUpdate(
|
||||
const PerfMonitorUpdateRequest& request) {
|
||||
static auto method = javaClassStatic()->getMethod<void(jint, jint, jint)>(
|
||||
"handleNativePerfMonitorMetricUpdate");
|
||||
method(
|
||||
jobj_,
|
||||
request.activeInteraction.duration,
|
||||
static_cast<jint>(request.activeInteraction.responsivenessScore),
|
||||
request.activeInteraction.ttl);
|
||||
}
|
||||
|
||||
void JReactHostInspectorTarget::loadNetworkResource(
|
||||
const jsinspector_modern::LoadNetworkResourceRequest& params,
|
||||
jsinspector_modern::ScopedExecutor<
|
||||
@@ -168,9 +157,16 @@ tracing::TraceRecordingState JReactHostInspectorTarget::stopTracing() {
|
||||
}
|
||||
}
|
||||
|
||||
void JReactHostInspectorTarget::stopAndStashBackgroundTrace() {
|
||||
jboolean JReactHostInspectorTarget::stopAndMaybeEmitBackgroundTrace() {
|
||||
auto capturedTrace = inspectorTarget_->stopTracing();
|
||||
if (inspectorTarget_->hasActiveSessionWithFuseboxClient()) {
|
||||
inspectorTarget_->emitTraceRecordingForFirstFuseboxClient(
|
||||
std::move(capturedTrace));
|
||||
return jboolean(true);
|
||||
}
|
||||
|
||||
stashTraceRecordingState(std::move(capturedTrace));
|
||||
return jboolean(false);
|
||||
}
|
||||
|
||||
void JReactHostInspectorTarget::stopAndDiscardBackgroundTrace() {
|
||||
@@ -199,8 +195,8 @@ void JReactHostInspectorTarget::registerNatives() {
|
||||
"startBackgroundTrace",
|
||||
JReactHostInspectorTarget::startBackgroundTrace),
|
||||
makeNativeMethod(
|
||||
"stopAndStashBackgroundTrace",
|
||||
JReactHostInspectorTarget::stopAndStashBackgroundTrace),
|
||||
"stopAndMaybeEmitBackgroundTrace",
|
||||
JReactHostInspectorTarget::stopAndMaybeEmitBackgroundTrace),
|
||||
makeNativeMethod(
|
||||
"stopAndDiscardBackgroundTrace",
|
||||
JReactHostInspectorTarget::stopAndDiscardBackgroundTrace),
|
||||
|
||||
+14
-13
@@ -82,17 +82,6 @@ class JReactHostInspectorTarget
|
||||
static void registerNatives();
|
||||
void sendDebuggerResumeCommand();
|
||||
|
||||
/**
|
||||
* Starts a background trace recording for this HostTarget.
|
||||
*
|
||||
* \return false if already tracing, true otherwise.
|
||||
*/
|
||||
bool startBackgroundTrace();
|
||||
/**
|
||||
* Stops previously started trace recording and stashes the captured trace,
|
||||
* which will be emitted the next time CDP session is created.
|
||||
*/
|
||||
void stopAndStashBackgroundTrace();
|
||||
/**
|
||||
* Get the state of the background trace: running, stopped, or disabled
|
||||
* Background tracing will be disabled if there is no metro connection or if
|
||||
@@ -101,6 +90,20 @@ class JReactHostInspectorTarget
|
||||
* \return the background trace state
|
||||
*/
|
||||
jint tracingState();
|
||||
/**
|
||||
* Starts a background trace recording for this HostTarget.
|
||||
*
|
||||
* \return false if already tracing, true otherwise.
|
||||
*/
|
||||
bool startBackgroundTrace();
|
||||
/**
|
||||
* Stops previously started trace recording and:
|
||||
* - If there is an active CDP session with Fusebox client enabled, emits the
|
||||
* trace and returns true.
|
||||
* - Otherwise, stashes the captured trace, that will be emitted when the CDP
|
||||
* session is initialized. Returns false.
|
||||
*/
|
||||
jboolean stopAndMaybeEmitBackgroundTrace();
|
||||
/**
|
||||
* Stops previously started trace recording and discards the captured trace.
|
||||
*/
|
||||
@@ -113,8 +116,6 @@ class JReactHostInspectorTarget
|
||||
void onReload(const PageReloadRequest& request) override;
|
||||
void onSetPausedInDebuggerMessage(
|
||||
const OverlaySetPausedInDebuggerMessageRequest& request) override;
|
||||
void unstable_onPerfMonitorUpdate(
|
||||
const jsinspector_modern::PerfMonitorUpdateRequest& request) override;
|
||||
void loadNetworkResource(
|
||||
const jsinspector_modern::LoadNetworkResourceRequest& params,
|
||||
jsinspector_modern::ScopedExecutor<
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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.
|
||||
|
||||
require "json"
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
|
||||
version = package['version']
|
||||
|
||||
source = { :git => 'https://github.com/facebook/react-native.git' }
|
||||
if version == '1000.0.0'
|
||||
# This is an unpublished version, use the latest commit hash of the react-native repo, which we're presumably in.
|
||||
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
|
||||
else
|
||||
source[:tag] = "v#{version}"
|
||||
end
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "RCTSwiftUI"
|
||||
s.version = version
|
||||
s.summary = "Swift utilities for React Native."
|
||||
s.homepage = "https://reactnative.dev/"
|
||||
s.license = package["license"]
|
||||
s.author = "Meta Platforms, Inc. and its affiliates"
|
||||
s.platforms = min_supported_versions
|
||||
s.source = source
|
||||
s.source_files = "*.{h,m,swift}"
|
||||
s.public_header_files = "*.h"
|
||||
s.module_name = "RCTSwiftUI"
|
||||
s.header_dir = "RCTSwiftUI"
|
||||
|
||||
# Swift-specific configuration
|
||||
s.pod_target_xcconfig = {
|
||||
"SWIFT_VERSION" => "5.0",
|
||||
"DEFINES_MODULE" => "YES",
|
||||
}
|
||||
|
||||
end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
@MainActor @objc public class RCTSwiftUIContainerView: NSObject {
|
||||
private var containerViewModel = ContainerViewModel()
|
||||
private var hostingController: UIHostingController<SwiftUIContainerView>?
|
||||
|
||||
@objc public override init() {
|
||||
super.init()
|
||||
hostingController = UIHostingController(rootView: SwiftUIContainerView(viewModel: containerViewModel))
|
||||
guard let view = hostingController?.view else {
|
||||
return
|
||||
}
|
||||
view.backgroundColor = .clear
|
||||
}
|
||||
|
||||
@objc public func updateContentView(_ view: UIView) {
|
||||
containerViewModel.contentView = view
|
||||
}
|
||||
|
||||
@objc public func hostingView() -> UIView? {
|
||||
return hostingController?.view
|
||||
}
|
||||
|
||||
@objc public func contentView() -> UIView? {
|
||||
return containerViewModel.contentView
|
||||
}
|
||||
|
||||
@objc public func updateBlurRadius(_ radius: NSNumber) {
|
||||
let blurRadius = CGFloat(radius.floatValue)
|
||||
containerViewModel.blurRadius = blurRadius
|
||||
}
|
||||
|
||||
@objc public func updateLayout(withBounds bounds: CGRect) {
|
||||
hostingController?.view.frame = bounds
|
||||
containerViewModel.contentView?.frame = bounds
|
||||
}
|
||||
|
||||
@objc public func resetStyles() {
|
||||
containerViewModel.blurRadius = 0
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerViewModel: ObservableObject {
|
||||
@Published var blurRadius: CGFloat = 0
|
||||
@Published var contentView: UIView?
|
||||
}
|
||||
|
||||
struct SwiftUIContainerView: View {
|
||||
@ObservedObject var viewModel: ContainerViewModel
|
||||
|
||||
var body: some View {
|
||||
if let contentView = viewModel.contentView {
|
||||
UIViewWrapper(view: contentView)
|
||||
.blur(radius: viewModel.blurRadius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UIViewWrapper: UIViewRepresentable {
|
||||
let view: UIView
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTSwiftUIContainerViewWrapper : NSObject
|
||||
|
||||
- (UIView *_Nullable)contentView;
|
||||
- (void)updateBlurRadius:(NSNumber *)radius;
|
||||
- (void)updateContentView:(UIView *)view;
|
||||
- (UIView *_Nullable)hostingView;
|
||||
- (void)resetStyles;
|
||||
- (void)updateLayoutWithBounds:(CGRect)bounds;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTSwiftUIContainerViewWrapper.h"
|
||||
|
||||
@import RCTSwiftUI;
|
||||
|
||||
@interface RCTSwiftUIContainerViewWrapper ()
|
||||
@property (nonatomic, strong) RCTSwiftUIContainerView *swiftContainerView;
|
||||
@end
|
||||
|
||||
@implementation RCTSwiftUIContainerViewWrapper
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_swiftContainerView = [RCTSwiftUIContainerView new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView *_Nullable)contentView
|
||||
{
|
||||
return [self.swiftContainerView contentView];
|
||||
}
|
||||
|
||||
- (UIView *_Nullable)hostingView
|
||||
{
|
||||
return [self.swiftContainerView hostingView];
|
||||
}
|
||||
|
||||
- (void)resetStyles
|
||||
{
|
||||
[self.swiftContainerView resetStyles];
|
||||
}
|
||||
|
||||
- (void)updateContentView:(UIView *)view
|
||||
{
|
||||
return [self.swiftContainerView updateContentView:view];
|
||||
}
|
||||
|
||||
- (void)updateBlurRadius:(NSNumber *)radius
|
||||
{
|
||||
[self.swiftContainerView updateBlurRadius:radius];
|
||||
}
|
||||
|
||||
- (void)updateLayoutWithBounds:(CGRect)bounds
|
||||
{
|
||||
[self.swiftContainerView updateLayoutWithBounds:bounds];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
require "json"
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
|
||||
version = package['version']
|
||||
|
||||
source = { :git => 'https://github.com/facebook/react-native.git' }
|
||||
if version == '1000.0.0'
|
||||
# This is an unpublished version, use the latest commit hash of the react-native repo, which we're presumably in.
|
||||
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
|
||||
else
|
||||
source[:tag] = "v#{version}"
|
||||
end
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "RCTSwiftUIWrapper"
|
||||
s.version = version
|
||||
s.summary = "Swift utilities for React Native."
|
||||
s.homepage = "https://reactnative.dev/"
|
||||
s.license = package["license"]
|
||||
s.author = "Meta Platforms, Inc. and its affiliates"
|
||||
s.platforms = min_supported_versions
|
||||
s.source = source
|
||||
s.source_files = "*.{h,m}"
|
||||
s.public_header_files = "*.h"
|
||||
s.module_name = "RCTSwiftUIWrapper"
|
||||
s.header_dir = "RCTSwiftUIWrapper"
|
||||
s.dependency "RCTSwiftUI"
|
||||
|
||||
s.pod_target_xcconfig = {
|
||||
"SWIFT_VERSION" => "5.0",
|
||||
}
|
||||
|
||||
end
|
||||
@@ -41,18 +41,14 @@ class HostAgent::Impl final {
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit)
|
||||
VoidExecutor executor)
|
||||
: frontendChannel_(frontendChannel),
|
||||
targetController_(targetController),
|
||||
hostMetadata_(std::move(hostMetadata)),
|
||||
sessionState_(sessionState),
|
||||
networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))),
|
||||
tracingAgent_(TracingAgent(
|
||||
frontendChannel,
|
||||
sessionState,
|
||||
targetController,
|
||||
std::move(traceRecordingToEmit))) {}
|
||||
tracingAgent_(
|
||||
TracingAgent(frontendChannel, sessionState, targetController)) {}
|
||||
|
||||
~Impl() {
|
||||
if (isPausedInDebuggerOverlayVisible_) {
|
||||
@@ -201,6 +197,14 @@ class HostAgent::Impl final {
|
||||
"ReactNativeApplication.metadataUpdated",
|
||||
createHostMetadataPayload(hostMetadata_)));
|
||||
|
||||
auto stashedTraceRecording =
|
||||
targetController_.getDelegate()
|
||||
.unstable_getTraceRecordingThatWillBeEmittedOnInitialization();
|
||||
if (stashedTraceRecording.has_value()) {
|
||||
tracingAgent_.emitExternalTraceRecording(
|
||||
std::move(stashedTraceRecording.value()));
|
||||
}
|
||||
|
||||
return {
|
||||
.isFinishedHandlingRequest = true,
|
||||
.shouldSendOKResponse = true,
|
||||
@@ -336,12 +340,24 @@ class HostAgent::Impl final {
|
||||
}
|
||||
}
|
||||
|
||||
bool hasFuseboxClientConnected() const {
|
||||
return fuseboxClientType_ == FuseboxClientType::Fusebox;
|
||||
}
|
||||
|
||||
void emitExternalTraceRecording(
|
||||
tracing::TraceRecordingState traceRecording) const {
|
||||
assert(
|
||||
hasFuseboxClientConnected() &&
|
||||
"Attempted to emit a trace recording to a non-Fusebox client");
|
||||
tracingAgent_.emitExternalTraceRecording(std::move(traceRecording));
|
||||
}
|
||||
|
||||
private:
|
||||
enum class FuseboxClientType { Unknown, Fusebox, NonFusebox };
|
||||
|
||||
/**
|
||||
* Send a simple Log.entryAdded notification with the given
|
||||
* \param text. You must ensure that the frontend has enabled Log
|
||||
* \param text You must ensure that the frontend has enabled Log
|
||||
* notifications (using Log.enable) prior to calling this function. In Chrome
|
||||
* DevTools, the message will appear in the Console tab along with regular
|
||||
* console messages. The difference between Log.entryAdded and
|
||||
@@ -432,11 +448,15 @@ class HostAgent::Impl final {
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit) {}
|
||||
VoidExecutor executor) {}
|
||||
|
||||
void handleRequest(const cdp::PreparsedRequest& req) {}
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent) {}
|
||||
bool hasFuseboxClientConnected() const {
|
||||
return false;
|
||||
}
|
||||
void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) {
|
||||
}
|
||||
};
|
||||
|
||||
#endif // REACT_NATIVE_DEBUGGER_ENABLED
|
||||
@@ -446,16 +466,14 @@ HostAgent::HostAgent(
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit)
|
||||
VoidExecutor executor)
|
||||
: impl_(std::make_unique<Impl>(
|
||||
*this,
|
||||
frontendChannel,
|
||||
targetController,
|
||||
std::move(hostMetadata),
|
||||
sessionState,
|
||||
std::move(executor),
|
||||
std::move(traceRecordingToEmit))) {}
|
||||
std::move(executor))) {}
|
||||
|
||||
HostAgent::~HostAgent() = default;
|
||||
|
||||
@@ -468,6 +486,15 @@ void HostAgent::setCurrentInstanceAgent(
|
||||
impl_->setCurrentInstanceAgent(std::move(instanceAgent));
|
||||
}
|
||||
|
||||
bool HostAgent::hasFuseboxClientConnected() const {
|
||||
return impl_->hasFuseboxClientConnected();
|
||||
}
|
||||
|
||||
void HostAgent::emitExternalTraceRecording(
|
||||
tracing::TraceRecordingState traceRecording) const {
|
||||
impl_->emitExternalTraceRecording(std::move(traceRecording));
|
||||
}
|
||||
|
||||
#pragma mark - Tracing
|
||||
|
||||
HostTracingAgent::HostTracingAgent(tracing::TraceRecordingState& state)
|
||||
|
||||
@@ -36,16 +36,13 @@ class HostAgent final {
|
||||
* \param hostMetadata Metadata about the host that created this agent.
|
||||
* \param sessionState The state of the session that created this agent.
|
||||
* \param executor A void executor to be used by async-aware handlers.
|
||||
* \param traceRecordingToEmit If set, this is the trace that Host has
|
||||
* requested to display in the Frontend.
|
||||
*/
|
||||
HostAgent(
|
||||
const FrontendChannel& frontendChannel,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit);
|
||||
VoidExecutor executor);
|
||||
|
||||
HostAgent(const HostAgent&) = delete;
|
||||
HostAgent(HostAgent&&) = delete;
|
||||
@@ -69,6 +66,20 @@ class HostAgent final {
|
||||
*/
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent);
|
||||
|
||||
/**
|
||||
* Returns whether this HostAgent is part of the session that has an active
|
||||
* Fusebox client connecte, i.e. with Chrome DevTools Frontend fork for React
|
||||
* Native.
|
||||
*/
|
||||
bool hasFuseboxClientConnected() const;
|
||||
|
||||
/**
|
||||
* Emits the trace recording that was captured externally, not via the
|
||||
* CDP-initiated request.
|
||||
*/
|
||||
void emitExternalTraceRecording(
|
||||
tracing::TraceRecordingState traceRecording) const;
|
||||
|
||||
private:
|
||||
// We use the private implementation idiom to ensure this class has the same
|
||||
// layout regardless of whether REACT_NATIVE_DEBUGGER_ENABLED is defined. The
|
||||
|
||||
@@ -34,8 +34,7 @@ class HostTargetSession {
|
||||
std::unique_ptr<IRemoteConnection> remote,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
VoidExecutor executor,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit)
|
||||
VoidExecutor executor)
|
||||
: remote_(std::make_shared<RAIIRemoteConnection>(std::move(remote))),
|
||||
frontendChannel_(
|
||||
[remoteWeak = std::weak_ptr(remote_)](std::string_view message) {
|
||||
@@ -48,8 +47,7 @@ class HostTargetSession {
|
||||
targetController,
|
||||
std::move(hostMetadata),
|
||||
state_,
|
||||
std::move(executor),
|
||||
std::move(traceRecordingToEmit)) {}
|
||||
std::move(executor)) {}
|
||||
|
||||
/**
|
||||
* Called by CallbackLocalConnection to send a message to this Session's
|
||||
@@ -103,6 +101,19 @@ class HostTargetSession {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ReactNativeApplication CDP domain is enabled.
|
||||
*
|
||||
* Chrome DevTools Frontend enables this domain as a client.
|
||||
*/
|
||||
bool hasFuseboxClient() const {
|
||||
return hostAgent_.hasFuseboxClientConnected();
|
||||
}
|
||||
|
||||
void emitTraceRecording(tracing::TraceRecordingState traceRecording) const {
|
||||
hostAgent_.emitExternalTraceRecording(std::move(traceRecording));
|
||||
}
|
||||
|
||||
private:
|
||||
// Owned by this instance, but shared (weakly) with the frontend channel
|
||||
std::shared_ptr<RAIIRemoteConnection> remote_;
|
||||
@@ -192,9 +203,6 @@ std::shared_ptr<HostTarget> HostTarget::create(
|
||||
VoidExecutor executor) {
|
||||
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
|
||||
hostTarget->setExecutor(std::move(executor));
|
||||
if (InspectorFlags::getInstance().getPerfMonitorV2Enabled()) {
|
||||
hostTarget->installPerfMetricsBinding();
|
||||
}
|
||||
return hostTarget;
|
||||
}
|
||||
|
||||
@@ -208,8 +216,7 @@ std::unique_ptr<ILocalConnection> HostTarget::connect(
|
||||
std::move(connectionToFrontend),
|
||||
controller_,
|
||||
delegate_.getMetadata(),
|
||||
makeVoidExecutor(executorFromThis()),
|
||||
delegate_.unstable_getTraceRecordingThatWillBeEmittedOnInitialization());
|
||||
makeVoidExecutor(executorFromThis()));
|
||||
session->setCurrentInstance(currentInstance_.get());
|
||||
sessions_.insert(std::weak_ptr(session));
|
||||
return std::make_unique<CallbackLocalConnection>(
|
||||
@@ -279,17 +286,6 @@ void HostTarget::sendCommand(HostCommand command) {
|
||||
});
|
||||
}
|
||||
|
||||
void HostTarget::installPerfMetricsBinding() {
|
||||
perfMonitorUpdateHandler_ =
|
||||
std::make_unique<PerfMonitorUpdateHandler>(delegate_);
|
||||
perfMetricsBinding_ = std::make_unique<HostRuntimeBinding>(
|
||||
*this, // Used immediately
|
||||
"__chromium_devtools_metrics_reporter",
|
||||
[this](const std::string& message) {
|
||||
perfMonitorUpdateHandler_->handlePerfMetricsUpdate(message);
|
||||
});
|
||||
}
|
||||
|
||||
HostTargetController::HostTargetController(HostTarget& target)
|
||||
: target_(target) {}
|
||||
|
||||
@@ -361,4 +357,32 @@ folly::dynamic createHostMetadataPayload(const HostTargetMetadata& metadata) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HostTarget::hasActiveSessionWithFuseboxClient() const {
|
||||
bool hasActiveFuseboxSession = false;
|
||||
sessions_.forEach([&](HostTargetSession& session) {
|
||||
hasActiveFuseboxSession |= session.hasFuseboxClient();
|
||||
});
|
||||
return hasActiveFuseboxSession;
|
||||
}
|
||||
|
||||
void HostTarget::emitTraceRecordingForFirstFuseboxClient(
|
||||
tracing::TraceRecordingState traceRecording) const {
|
||||
bool emitted = false;
|
||||
sessions_.forEach([&](HostTargetSession& session) {
|
||||
if (emitted) {
|
||||
/**
|
||||
* TraceRecordingState object is not copiable for performance reasons,
|
||||
* because it could contain large Runtime sampling profile object.
|
||||
*
|
||||
* This approach would not work with multi-client debugger setup.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (session.hasFuseboxClient()) {
|
||||
session.emitTraceRecording(std::move(traceRecording));
|
||||
emitted = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "InspectorInterfaces.h"
|
||||
#include "InstanceTarget.h"
|
||||
#include "NetworkIOAgent.h"
|
||||
#include "PerfMonitorV2.h"
|
||||
#include "ScopedExecutor.h"
|
||||
#include "WeakList.h"
|
||||
|
||||
@@ -128,13 +127,6 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
|
||||
virtual void onSetPausedInDebuggerMessage(
|
||||
const OverlaySetPausedInDebuggerMessageRequest& request) = 0;
|
||||
|
||||
/**
|
||||
* [Experimental] Called when the runtime has new data for the V2 Perf
|
||||
* Monitor overlay. This is called on the inspector thread.
|
||||
*/
|
||||
virtual void unstable_onPerfMonitorUpdate(
|
||||
const PerfMonitorUpdateRequest& /*request*/) {}
|
||||
|
||||
/**
|
||||
* Called by NetworkIOAgent on handling a `Network.loadNetworkResource` CDP
|
||||
* request. Platform implementations should override this to perform a
|
||||
@@ -305,6 +297,21 @@ class JSINSPECTOR_EXPORT HostTarget
|
||||
*/
|
||||
tracing::TracingState tracingState() const;
|
||||
|
||||
/**
|
||||
* Returns whether there is an active session with the Fusebox client, i.e.
|
||||
* with Chrome DevTools Frontend fork for React Native.
|
||||
*/
|
||||
bool hasActiveSessionWithFuseboxClient() const;
|
||||
|
||||
/**
|
||||
* Emits the trace recording for the first active session with the Fusebox
|
||||
* client.
|
||||
*
|
||||
* @see \c hasActiveFrontendSession
|
||||
*/
|
||||
void emitTraceRecordingForFirstFuseboxClient(
|
||||
tracing::TraceRecordingState traceRecording) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Constructs a new HostTarget.
|
||||
@@ -324,7 +331,6 @@ class JSINSPECTOR_EXPORT HostTarget
|
||||
std::shared_ptr<ExecutionContextManager> executionContextManager_;
|
||||
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
|
||||
std::unique_ptr<HostCommandSender> commandSender_;
|
||||
std::unique_ptr<PerfMonitorUpdateHandler> perfMonitorUpdateHandler_;
|
||||
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
|
||||
|
||||
/**
|
||||
@@ -343,13 +349,6 @@ class JSINSPECTOR_EXPORT HostTarget
|
||||
return currentInstance_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a runtime binding subscribing to the Interaction to Next Paint
|
||||
* (INP) live metric, which we broadcast to the V2 Perf Monitor overlay
|
||||
* via \ref HostTargetDelegate::unstable_onPerfMonitorUpdate.
|
||||
*/
|
||||
void installPerfMetricsBinding();
|
||||
|
||||
// Necessary to allow HostAgent to access HostTarget's internals in a
|
||||
// controlled way (i.e. only HostTargetController gets friend access, while
|
||||
// HostAgent itself doesn't).
|
||||
|
||||
@@ -33,10 +33,6 @@ bool InspectorFlags::getNetworkInspectionEnabled() const {
|
||||
return loadFlagsAndAssertUnchanged().networkInspectionEnabled;
|
||||
}
|
||||
|
||||
bool InspectorFlags::getPerfMonitorV2Enabled() const {
|
||||
return loadFlagsAndAssertUnchanged().perfMonitorV2Enabled;
|
||||
}
|
||||
|
||||
void InspectorFlags::dangerouslyResetFlags() {
|
||||
*this = InspectorFlags{};
|
||||
}
|
||||
@@ -63,9 +59,6 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
|
||||
.networkInspectionEnabled =
|
||||
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
|
||||
ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(),
|
||||
.perfMonitorV2Enabled =
|
||||
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
|
||||
ReactNativeFeatureFlags::perfMonitorV2Enabled(),
|
||||
};
|
||||
|
||||
if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) {
|
||||
|
||||
@@ -35,11 +35,6 @@ class InspectorFlags {
|
||||
*/
|
||||
bool getNetworkInspectionEnabled() const;
|
||||
|
||||
/**
|
||||
* Flag determining if the V2 in-app Performance Monitor is enabled.
|
||||
*/
|
||||
bool getPerfMonitorV2Enabled() const;
|
||||
|
||||
/**
|
||||
* Forcibly disable the main `getFuseboxEnabled()` flag. This should ONLY be
|
||||
* used by `ReactInstanceIntegrationTest`.
|
||||
@@ -57,7 +52,6 @@ class InspectorFlags {
|
||||
bool fuseboxEnabled;
|
||||
bool isProfilingBuild;
|
||||
bool networkInspectionEnabled;
|
||||
bool perfMonitorV2Enabled;
|
||||
bool operator==(const Values&) const = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "PerfMonitorV2.h"
|
||||
#include "HostTarget.h"
|
||||
|
||||
#include <folly/json.h>
|
||||
#include <react/timing/primitives.h>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint16_t MIN_DURATION = 200;
|
||||
constexpr uint16_t DEFAULT_TTL = 4000;
|
||||
constexpr uint16_t BAD_EVENT_TTL = 20000;
|
||||
|
||||
InteractionResponsivenessScore getInteractionScore(uint16_t duration) {
|
||||
constexpr uint16_t GOOD_THRESHOLD = 200;
|
||||
constexpr uint16_t NEEDS_IMPROVEMENT_THRESHOLD = 500;
|
||||
|
||||
if (duration < GOOD_THRESHOLD) {
|
||||
return InteractionResponsivenessScore::Good;
|
||||
} else if (duration < NEEDS_IMPROVEMENT_THRESHOLD) {
|
||||
return InteractionResponsivenessScore::NeedsImprovement;
|
||||
} else {
|
||||
return InteractionResponsivenessScore::Poor;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void PerfMonitorUpdateHandler::handlePerfMetricsUpdate(
|
||||
const std::string& message) {
|
||||
auto payload = folly::parseJson(message);
|
||||
|
||||
if (payload.isObject()) {
|
||||
if (payload["name"] != "__ReactNative__LongTask") {
|
||||
return;
|
||||
}
|
||||
|
||||
auto duration = static_cast<uint16_t>(payload["duration"].asInt());
|
||||
|
||||
if (duration < MIN_DURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto responsivenessScore = getInteractionScore(duration);
|
||||
auto ttl =
|
||||
responsivenessScore == InteractionResponsivenessScore::NeedsImprovement
|
||||
? BAD_EVENT_TTL
|
||||
: DEFAULT_TTL;
|
||||
|
||||
LongTaskPayload newEvent{
|
||||
static_cast<uint16_t>(payload["startTime"].asInt()),
|
||||
duration,
|
||||
responsivenessScore,
|
||||
ttl};
|
||||
|
||||
if (shouldOverrideLastEvent(newEvent)) {
|
||||
lastEvent_ = newEvent;
|
||||
delegate_.unstable_onPerfMonitorUpdate(
|
||||
PerfMonitorUpdateRequest{newEvent});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PerfMonitorUpdateHandler::shouldOverrideLastEvent(
|
||||
const LongTaskPayload& newEvent) {
|
||||
if (!lastEvent_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Override if last event has expired
|
||||
if (HighResTimeStamp::now().toDOMHighResTimeStamp() >
|
||||
lastEvent_->startTime + lastEvent_->ttl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Override if same or greater responsiveness score
|
||||
if (newEvent.responsivenessScore >= lastEvent_->responsivenessScore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
class HostTargetDelegate;
|
||||
|
||||
/**
|
||||
* See https://web.dev/articles/inp#good-score.
|
||||
*/
|
||||
enum class InteractionResponsivenessScore : int32_t {
|
||||
Good = 0,
|
||||
NeedsImprovement = 1,
|
||||
Poor = 2
|
||||
};
|
||||
|
||||
struct LongTaskPayload {
|
||||
uint16_t startTime;
|
||||
uint16_t duration;
|
||||
InteractionResponsivenessScore responsivenessScore;
|
||||
uint16_t ttl;
|
||||
};
|
||||
|
||||
struct PerfMonitorUpdateRequest {
|
||||
LongTaskPayload activeInteraction;
|
||||
};
|
||||
|
||||
/**
|
||||
* [Experimental] Utility to handle performance metrics updates received from
|
||||
* the runtime and forward update events to the V2 Perf Monitor UI.
|
||||
*/
|
||||
class PerfMonitorUpdateHandler {
|
||||
public:
|
||||
explicit PerfMonitorUpdateHandler(HostTargetDelegate& delegate)
|
||||
: delegate_(delegate) {}
|
||||
|
||||
/**
|
||||
* Handle a new "__chromium_devtools_metrics_reporter" message.
|
||||
*/
|
||||
void handlePerfMetricsUpdate(const std::string& message);
|
||||
|
||||
private:
|
||||
HostTargetDelegate& delegate_;
|
||||
std::optional<LongTaskPayload> lastEvent_;
|
||||
|
||||
bool shouldOverrideLastEvent(const LongTaskPayload& newInteraction);
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
@@ -38,17 +38,10 @@ const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1;
|
||||
TracingAgent::TracingAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
SessionState& sessionState,
|
||||
HostTargetController& hostTargetController,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit)
|
||||
HostTargetController& hostTargetController)
|
||||
: frontendChannel_(std::move(frontendChannel)),
|
||||
sessionState_(sessionState),
|
||||
hostTargetController_(hostTargetController) {
|
||||
if (traceRecordingToEmit.has_value()) {
|
||||
frontendChannel_(
|
||||
cdp::jsonNotification("ReactNativeApplication.traceRequested"));
|
||||
emitTraceRecording(std::move(traceRecordingToEmit.value()));
|
||||
}
|
||||
}
|
||||
hostTargetController_(hostTargetController) {}
|
||||
|
||||
TracingAgent::~TracingAgent() {
|
||||
// Agents are owned by the session. If the agent is destroyed, it means that
|
||||
@@ -100,15 +93,22 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TracingAgent::emitExternalTraceRecording(
|
||||
tracing::TraceRecordingState traceRecording) const {
|
||||
frontendChannel_(
|
||||
cdp::jsonNotification("ReactNativeApplication.traceRequested"));
|
||||
emitTraceRecording(std::move(traceRecording));
|
||||
}
|
||||
|
||||
void TracingAgent::emitTraceRecording(
|
||||
tracing::TraceRecordingState state) const {
|
||||
tracing::TraceRecordingState traceRecording) const {
|
||||
auto dataCollectedCallback = [this](folly::dynamic&& eventsChunk) {
|
||||
frontendChannel_(cdp::jsonNotification(
|
||||
"Tracing.dataCollected",
|
||||
folly::dynamic::object("value", std::move(eventsChunk))));
|
||||
};
|
||||
tracing::TraceRecordingStateSerializer::emitAsDataCollectedChunks(
|
||||
std::move(state),
|
||||
std::move(traceRecording),
|
||||
dataCollectedCallback,
|
||||
TRACE_EVENT_CHUNK_SIZE,
|
||||
PROFILE_TRACE_EVENT_CHUNK_SIZE);
|
||||
|
||||
@@ -28,14 +28,11 @@ class TracingAgent {
|
||||
* \param hostTargetController An interface to the HostTarget that this agent
|
||||
* is attached to. The caller is responsible for ensuring that the
|
||||
* HostTargetDelegate and underlying HostTarget both outlive the agent.
|
||||
* \param traceRecordingToEmit If set, this is the trace that Host has
|
||||
* requested to display in the Frontend.
|
||||
*/
|
||||
TracingAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
SessionState& sessionState,
|
||||
HostTargetController& hostTargetController,
|
||||
std::optional<tracing::TraceRecordingState> traceRecordingToEmit);
|
||||
HostTargetController& hostTargetController);
|
||||
|
||||
~TracingAgent();
|
||||
|
||||
@@ -46,6 +43,12 @@ class TracingAgent {
|
||||
*/
|
||||
bool handleRequest(const cdp::PreparsedRequest& req);
|
||||
|
||||
/**
|
||||
* Emits the Trace Recording that was stashed externally by the HostTarget.
|
||||
*/
|
||||
void emitExternalTraceRecording(
|
||||
tracing::TraceRecordingState traceRecording) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* A channel used to send responses and events to the frontend.
|
||||
@@ -60,7 +63,7 @@ class TracingAgent {
|
||||
* Emits the captured Trace Recording state in a series of
|
||||
* Tracing.dataCollected events, followed by a Tracing.tracingComplete event.
|
||||
*/
|
||||
void emitTraceRecording(tracing::TraceRecordingState state) const;
|
||||
void emitTraceRecording(tracing::TraceRecordingState traceRecording) const;
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -49,7 +49,7 @@ std::string jsonResult(RequestId id, const folly::dynamic& result) {
|
||||
}
|
||||
|
||||
std::string jsonNotification(
|
||||
std::string_view method,
|
||||
const std::string& method,
|
||||
std::optional<folly::dynamic> params) {
|
||||
auto dynamicNotification = folly::dynamic::object("method", method);
|
||||
if (params) {
|
||||
@@ -60,7 +60,7 @@ std::string jsonNotification(
|
||||
|
||||
std::string jsonRequest(
|
||||
RequestId id,
|
||||
std::string_view method,
|
||||
const std::string& method,
|
||||
std::optional<folly::dynamic> params) {
|
||||
auto dynamicRequest = folly::dynamic::object("id", id)("method", method);
|
||||
if (params) {
|
||||
|
||||
@@ -118,7 +118,7 @@ std::string jsonResult(
|
||||
* \param params Optional payload object.
|
||||
*/
|
||||
std::string jsonNotification(
|
||||
std::string_view method,
|
||||
const std::string& method,
|
||||
std::optional<folly::dynamic> params = std::nullopt);
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ std::string jsonNotification(
|
||||
*/
|
||||
std::string jsonRequest(
|
||||
RequestId id,
|
||||
std::string_view method,
|
||||
const std::string& method,
|
||||
std::optional<folly::dynamic> params = std::nullopt);
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern::cdp
|
||||
|
||||
@@ -56,7 +56,7 @@ bool NetworkHandler::disable() {
|
||||
}
|
||||
|
||||
enabled_.store(false, std::memory_order_release);
|
||||
requestBodyBuffer_.clear();
|
||||
responseBodyBuffer_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,10 @@ void NetworkHandler::onResponseReceived(
|
||||
}
|
||||
|
||||
auto resourceType = cdp::network::resourceTypeFromMimeType(response.mimeType);
|
||||
resourceTypeMap_.emplace(requestId, resourceType);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resourceTypeMapMutex_);
|
||||
resourceTypeMap_.emplace(requestId, resourceType);
|
||||
}
|
||||
|
||||
auto params = cdp::network::ResponseReceivedParams{
|
||||
.requestId = requestId,
|
||||
@@ -166,23 +169,26 @@ void NetworkHandler::onLoadingFinished(
|
||||
|
||||
void NetworkHandler::onLoadingFailed(
|
||||
const std::string& requestId,
|
||||
bool cancelled) const {
|
||||
bool cancelled) {
|
||||
if (!isEnabledNoSync()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto params = cdp::network::LoadingFailedParams{
|
||||
.requestId = requestId,
|
||||
.timestamp = getCurrentUnixTimestampSeconds(),
|
||||
.type = resourceTypeMap_.find(requestId) != resourceTypeMap_.end()
|
||||
? resourceTypeMap_.at(requestId)
|
||||
: "Other",
|
||||
.errorText = cancelled ? "net::ERR_ABORTED" : "net::ERR_FAILED",
|
||||
.canceled = cancelled,
|
||||
};
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resourceTypeMapMutex_);
|
||||
auto params = cdp::network::LoadingFailedParams{
|
||||
.requestId = requestId,
|
||||
.timestamp = getCurrentUnixTimestampSeconds(),
|
||||
.type = resourceTypeMap_.find(requestId) != resourceTypeMap_.end()
|
||||
? resourceTypeMap_.at(requestId)
|
||||
: "Other",
|
||||
.errorText = cancelled ? "net::ERR_ABORTED" : "net::ERR_FAILED",
|
||||
.canceled = cancelled,
|
||||
};
|
||||
|
||||
frontendChannel_(
|
||||
cdp::jsonNotification("Network.loadingFailed", params.toDynamic()));
|
||||
frontendChannel_(
|
||||
cdp::jsonNotification("Network.loadingFailed", params.toDynamic()));
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHandler::storeResponseBody(
|
||||
@@ -190,13 +196,13 @@ void NetworkHandler::storeResponseBody(
|
||||
std::string_view body,
|
||||
bool base64Encoded) {
|
||||
std::lock_guard<std::mutex> lock(requestBodyMutex_);
|
||||
requestBodyBuffer_.put(requestId, body, base64Encoded);
|
||||
responseBodyBuffer_.put(requestId, body, base64Encoded);
|
||||
}
|
||||
|
||||
std::optional<std::tuple<std::string, bool>> NetworkHandler::getResponseBody(
|
||||
const std::string& requestId) {
|
||||
std::lock_guard<std::mutex> lock(requestBodyMutex_);
|
||||
auto responseBody = requestBodyBuffer_.get(requestId);
|
||||
auto responseBody = responseBodyBuffer_.get(requestId);
|
||||
|
||||
if (responseBody == nullptr) {
|
||||
return std::nullopt;
|
||||
|
||||
@@ -100,7 +100,7 @@ class NetworkHandler {
|
||||
/**
|
||||
* @cdp Network.loadingFailed
|
||||
*/
|
||||
void onLoadingFailed(const std::string& requestId, bool cancelled) const;
|
||||
void onLoadingFailed(const std::string& requestId, bool cancelled);
|
||||
|
||||
/**
|
||||
* Store the fetched response body for a text or image network response.
|
||||
@@ -139,8 +139,9 @@ class NetworkHandler {
|
||||
FrontendChannel frontendChannel_;
|
||||
|
||||
std::map<std::string, std::string> resourceTypeMap_{};
|
||||
std::mutex resourceTypeMapMutex_{};
|
||||
|
||||
BoundedRequestBuffer requestBodyBuffer_{};
|
||||
BoundedRequestBuffer responseBodyBuffer_{};
|
||||
std::mutex requestBodyMutex_;
|
||||
};
|
||||
|
||||
|
||||
+117
-6
@@ -124,7 +124,7 @@ std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
|
||||
}
|
||||
|
||||
void PerformanceTracer::reportMark(
|
||||
const std::string_view& name,
|
||||
const std::string& name,
|
||||
HighResTimeStamp start,
|
||||
folly::dynamic&& detail) {
|
||||
if (!tracingAtomic_) {
|
||||
@@ -137,7 +137,7 @@ void PerformanceTracer::reportMark(
|
||||
}
|
||||
|
||||
enqueueEvent(PerformanceTracerEventMark{
|
||||
.name = std::string(name),
|
||||
.name = name,
|
||||
.start = start,
|
||||
.detail = std::move(detail),
|
||||
.threadId = getCurrentThreadId(),
|
||||
@@ -145,7 +145,7 @@ void PerformanceTracer::reportMark(
|
||||
}
|
||||
|
||||
void PerformanceTracer::reportMeasure(
|
||||
const std::string_view& name,
|
||||
const std::string& name,
|
||||
HighResTimeStamp start,
|
||||
HighResDuration duration,
|
||||
folly::dynamic&& detail) {
|
||||
@@ -159,7 +159,7 @@ void PerformanceTracer::reportMeasure(
|
||||
}
|
||||
|
||||
enqueueEvent(PerformanceTracerEventMeasure{
|
||||
.name = std::string(name),
|
||||
.name = name,
|
||||
.start = start,
|
||||
.duration = duration,
|
||||
.detail = std::move(detail),
|
||||
@@ -168,7 +168,7 @@ void PerformanceTracer::reportMeasure(
|
||||
}
|
||||
|
||||
void PerformanceTracer::reportTimeStamp(
|
||||
std::string name,
|
||||
const std::string& name,
|
||||
std::optional<ConsoleTimeStampEntry> start,
|
||||
std::optional<ConsoleTimeStampEntry> end,
|
||||
std::optional<std::string> trackName,
|
||||
@@ -184,7 +184,7 @@ void PerformanceTracer::reportTimeStamp(
|
||||
}
|
||||
|
||||
enqueueEvent(PerformanceTracerEventTimeStamp{
|
||||
.name = std::move(name),
|
||||
.name = name,
|
||||
.start = std::move(start),
|
||||
.end = std::move(end),
|
||||
.trackName = std::move(trackName),
|
||||
@@ -232,6 +232,50 @@ void PerformanceTracer::reportEventLoopMicrotasks(
|
||||
});
|
||||
}
|
||||
|
||||
void PerformanceTracer::reportResourceTiming(
|
||||
const std::string& requestId,
|
||||
const std::string& url,
|
||||
HighResTimeStamp fetchStart,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
int statusCode,
|
||||
const std::string& requestMethod,
|
||||
const std::string& resourceType) {
|
||||
if (!tracingAtomic_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!tracingAtomic_) {
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueEvent(PerformanceTracerResourceWillSendRequest{
|
||||
.requestId = requestId,
|
||||
.start = fetchStart,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceSendRequest{
|
||||
.requestId = requestId,
|
||||
.url = url,
|
||||
.start = fetchStart,
|
||||
.requestMethod = requestMethod,
|
||||
.resourceType = resourceType,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceReceiveResponse{
|
||||
.requestId = requestId,
|
||||
.start = responseStart,
|
||||
.statusCode = statusCode,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceFinish{
|
||||
.requestId = requestId,
|
||||
.start = responseEnd,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
}
|
||||
|
||||
/* static */ TraceEvent PerformanceTracer::constructRuntimeProfileTraceEvent(
|
||||
RuntimeProfileId profileId,
|
||||
ProcessId processId,
|
||||
@@ -505,6 +549,73 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceWillSendRequest&& event) {
|
||||
folly::dynamic data =
|
||||
folly::dynamic::object("requestId", std::move(event.requestId));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceWillSendRequest",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceSendRequest&& event) {
|
||||
folly::dynamic data =
|
||||
folly::dynamic::object("initiator", folly::dynamic::object())(
|
||||
"renderBlocking", "non_blocking")(
|
||||
"requestId", std::move(event.requestId))(
|
||||
"requestMethod", std::move(event.requestMethod))(
|
||||
"resourceType", std::move(event.resourceType))(
|
||||
"url", std::move(event.url));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceSendRequest",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceReceiveResponse&& event) {
|
||||
folly::dynamic data = folly::dynamic::object("protocol", "h2")(
|
||||
"requestId", std::move(event.requestId))(
|
||||
"statusCode", event.statusCode)(
|
||||
"timing", folly::dynamic::object());
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceReceiveResponse",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceFinish&& event) {
|
||||
folly::dynamic data = folly::dynamic::object("didFail", false)(
|
||||
"requestId", std::move(event.requestId));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceFinish",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
},
|
||||
std::move(event));
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class PerformanceTracer {
|
||||
* See https://w3c.github.io/user-timing/#mark-method.
|
||||
*/
|
||||
void reportMark(
|
||||
const std::string_view& name,
|
||||
const std::string& name,
|
||||
HighResTimeStamp start,
|
||||
folly::dynamic&& detail = nullptr);
|
||||
|
||||
@@ -76,7 +76,7 @@ class PerformanceTracer {
|
||||
* See https://w3c.github.io/user-timing/#measure-method.
|
||||
*/
|
||||
void reportMeasure(
|
||||
const std::string_view& name,
|
||||
const std::string& name,
|
||||
HighResTimeStamp start,
|
||||
HighResDuration duration,
|
||||
folly::dynamic&& detail = nullptr);
|
||||
@@ -89,7 +89,7 @@ class PerformanceTracer {
|
||||
https://developer.chrome.com/docs/devtools/performance/extension#inject_your_data_with_consoletimestamp
|
||||
*/
|
||||
void reportTimeStamp(
|
||||
std::string name,
|
||||
const std::string& name,
|
||||
std::optional<ConsoleTimeStampEntry> start = std::nullopt,
|
||||
std::optional<ConsoleTimeStampEntry> end = std::nullopt,
|
||||
std::optional<std::string> trackName = std::nullopt,
|
||||
@@ -108,6 +108,21 @@ class PerformanceTracer {
|
||||
*/
|
||||
void reportEventLoopMicrotasks(HighResTimeStamp start, HighResTimeStamp end);
|
||||
|
||||
/**
|
||||
* Record a "ResourceSendRequest"/"ResourceFinish" event pair - a labelled
|
||||
* duration in the Performance timeline Network track. If not currently
|
||||
* tracing, this is a no-op.
|
||||
*/
|
||||
void reportResourceTiming(
|
||||
const std::string& requestId,
|
||||
const std::string& url,
|
||||
HighResTimeStamp fetchStart,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
int statusCode,
|
||||
const std::string& requestMethod,
|
||||
const std::string& resourceType);
|
||||
|
||||
/**
|
||||
* Creates "Profile" Trace Event.
|
||||
*
|
||||
@@ -181,12 +196,48 @@ class PerformanceTracer {
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceWillSendRequest {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceSendRequest {
|
||||
std::string requestId;
|
||||
std::string url;
|
||||
HighResTimeStamp start;
|
||||
std::string requestMethod;
|
||||
std::string resourceType;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceFinish {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceReceiveResponse {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
int statusCode;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
using PerformanceTracerEvent = std::variant<
|
||||
PerformanceTracerEventTimeStamp,
|
||||
PerformanceTracerEventEventLoopTask,
|
||||
PerformanceTracerEventEventLoopMicrotask,
|
||||
PerformanceTracerEventMark,
|
||||
PerformanceTracerEventMeasure>;
|
||||
PerformanceTracerEventMeasure,
|
||||
PerformanceTracerResourceWillSendRequest,
|
||||
PerformanceTracerResourceSendRequest,
|
||||
PerformanceTracerResourceReceiveResponse,
|
||||
PerformanceTracerResourceFinish>;
|
||||
|
||||
#pragma mark - Private fields and methods
|
||||
|
||||
|
||||
@@ -49,8 +49,14 @@ struct RuntimeSamplingProfile {
|
||||
/// id of the corresponding script in the VM.
|
||||
uint32_t scriptId;
|
||||
/// name of the function that represents call frame.
|
||||
/// Storing a std::string_view should be considered safe here, beacause
|
||||
/// the lifetime of the string contents are guaranteed as long as the raw
|
||||
// Sampling Profiler object from Hermes is allocated.
|
||||
std::string_view functionName;
|
||||
/// source url of the corresponding script in the VM.
|
||||
/// Storing a std::string_view should be considered safe here, beacause
|
||||
/// the lifetime of the string contents are guaranteed as long as the raw
|
||||
// Sampling Profiler object from Hermes is allocated.
|
||||
std::optional<std::string_view> scriptURL = std::nullopt;
|
||||
/// 0-based line number of the corresponding call frame.
|
||||
std::optional<uint32_t> lineNumber = std::nullopt;
|
||||
|
||||
@@ -55,6 +55,12 @@ struct TraceEvent {
|
||||
/** The ID for the process that output this event. */
|
||||
ProcessId pid;
|
||||
|
||||
/**
|
||||
* The scope of the event, either global (g), process (p), or thread (t).
|
||||
* Only applicable to instant events ("ph": "i").
|
||||
*/
|
||||
std::optional<char> s;
|
||||
|
||||
/** The ID for the thread that output this event. */
|
||||
ThreadId tid;
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace facebook::react::jsinspector_modern::tracing {
|
||||
result["ph"] = std::string(1, event.ph);
|
||||
result["ts"] = highResTimeStampToTracingClockTimeStamp(event.ts);
|
||||
result["pid"] = event.pid;
|
||||
if (event.s.has_value()) {
|
||||
result["s"] = std::string(1, event.s.value());
|
||||
}
|
||||
result["tid"] = event.tid;
|
||||
result["args"] = std::move(event.args);
|
||||
if (event.dur.has_value()) {
|
||||
|
||||
@@ -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<<a4fb74cf8a68181f509794e387bf666d>>
|
||||
* @generated SignedSource<<42bea10b4b62a91dce8fde7674f76ceb>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -66,6 +66,10 @@ bool ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid() {
|
||||
return getAccessor().enableAccumulatedUpdatesInRawPropsAndroid();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::enableAndroidLinearText() {
|
||||
return getAccessor().enableAndroidLinearText();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::enableAndroidTextMeasurementOptimizations() {
|
||||
return getAccessor().enableAndroidTextMeasurementOptimizations();
|
||||
}
|
||||
@@ -170,6 +174,10 @@ bool ReactNativeFeatureFlags::enableResourceTimingAPI() {
|
||||
return getAccessor().enableResourceTimingAPI();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::enableSwiftUIBasedFilters() {
|
||||
return getAccessor().enableSwiftUIBasedFilters();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::enableViewCulling() {
|
||||
return getAccessor().enableViewCulling();
|
||||
}
|
||||
|
||||
@@ -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<<a4a98649c527be3c936c859ddc59cb33>>
|
||||
* @generated SignedSource<<40421ac664927693136a8e3197e3c07c>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -89,6 +89,11 @@ class ReactNativeFeatureFlags {
|
||||
*/
|
||||
RN_EXPORT static bool enableAccumulatedUpdatesInRawPropsAndroid();
|
||||
|
||||
/**
|
||||
* Enables linear text rendering on Android wherever subpixel text rendering is enabled
|
||||
*/
|
||||
RN_EXPORT static bool enableAndroidLinearText();
|
||||
|
||||
/**
|
||||
* Enables various optimizations throughout the path of measuring text on Android.
|
||||
*/
|
||||
@@ -219,6 +224,11 @@ class ReactNativeFeatureFlags {
|
||||
*/
|
||||
RN_EXPORT static bool enableResourceTimingAPI();
|
||||
|
||||
/**
|
||||
* When enabled, it will use SwiftUI for filter effects like blur on iOS.
|
||||
*/
|
||||
RN_EXPORT static bool enableSwiftUIBasedFilters();
|
||||
|
||||
/**
|
||||
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
|
||||
*/
|
||||
|
||||
+98
-62
@@ -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<<91188495ce43a1e1c820cdc3d6302bda>>
|
||||
* @generated SignedSource<<e433f2f36d7a2852aaea2b37f671a7e1>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -209,6 +209,24 @@ bool ReactNativeFeatureFlagsAccessor::enableAccumulatedUpdatesInRawPropsAndroid(
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::enableAndroidLinearText() {
|
||||
auto flagValue = enableAndroidLinearText_.load();
|
||||
|
||||
if (!flagValue.has_value()) {
|
||||
// This block is not exclusive but it is not necessary.
|
||||
// If multiple threads try to initialize the feature flag, we would only
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(10, "enableAndroidLinearText");
|
||||
|
||||
flagValue = currentProvider_->enableAndroidLinearText();
|
||||
enableAndroidLinearText_ = flagValue;
|
||||
}
|
||||
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::enableAndroidTextMeasurementOptimizations() {
|
||||
auto flagValue = enableAndroidTextMeasurementOptimizations_.load();
|
||||
|
||||
@@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableAndroidTextMeasurementOptimizations(
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(10, "enableAndroidTextMeasurementOptimizations");
|
||||
markFlagAsAccessed(11, "enableAndroidTextMeasurementOptimizations");
|
||||
|
||||
flagValue = currentProvider_->enableAndroidTextMeasurementOptimizations();
|
||||
enableAndroidTextMeasurementOptimizations_ = flagValue;
|
||||
@@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableBridgelessArchitecture() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(11, "enableBridgelessArchitecture");
|
||||
markFlagAsAccessed(12, "enableBridgelessArchitecture");
|
||||
|
||||
flagValue = currentProvider_->enableBridgelessArchitecture();
|
||||
enableBridgelessArchitecture_ = flagValue;
|
||||
@@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCppPropsIteratorSetter() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(12, "enableCppPropsIteratorSetter");
|
||||
markFlagAsAccessed(13, "enableCppPropsIteratorSetter");
|
||||
|
||||
flagValue = currentProvider_->enableCppPropsIteratorSetter();
|
||||
enableCppPropsIteratorSetter_ = flagValue;
|
||||
@@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomFocusSearchOnClippedElementsAn
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(13, "enableCustomFocusSearchOnClippedElementsAndroid");
|
||||
markFlagAsAccessed(14, "enableCustomFocusSearchOnClippedElementsAndroid");
|
||||
|
||||
flagValue = currentProvider_->enableCustomFocusSearchOnClippedElementsAndroid();
|
||||
enableCustomFocusSearchOnClippedElementsAndroid_ = flagValue;
|
||||
@@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDestroyShadowTreeRevisionAsync() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(14, "enableDestroyShadowTreeRevisionAsync");
|
||||
markFlagAsAccessed(15, "enableDestroyShadowTreeRevisionAsync");
|
||||
|
||||
flagValue = currentProvider_->enableDestroyShadowTreeRevisionAsync();
|
||||
enableDestroyShadowTreeRevisionAsync_ = flagValue;
|
||||
@@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(15, "enableDoubleMeasurementFixAndroid");
|
||||
markFlagAsAccessed(16, "enableDoubleMeasurementFixAndroid");
|
||||
|
||||
flagValue = currentProvider_->enableDoubleMeasurementFixAndroid();
|
||||
enableDoubleMeasurementFixAndroid_ = flagValue;
|
||||
@@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerMainQueueModulesOnIOS() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(16, "enableEagerMainQueueModulesOnIOS");
|
||||
markFlagAsAccessed(17, "enableEagerMainQueueModulesOnIOS");
|
||||
|
||||
flagValue = currentProvider_->enableEagerMainQueueModulesOnIOS();
|
||||
enableEagerMainQueueModulesOnIOS_ = flagValue;
|
||||
@@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(17, "enableEagerRootViewAttachment");
|
||||
markFlagAsAccessed(18, "enableEagerRootViewAttachment");
|
||||
|
||||
flagValue = currentProvider_->enableEagerRootViewAttachment();
|
||||
enableEagerRootViewAttachment_ = flagValue;
|
||||
@@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(18, "enableFabricLogs");
|
||||
markFlagAsAccessed(19, "enableFabricLogs");
|
||||
|
||||
flagValue = currentProvider_->enableFabricLogs();
|
||||
enableFabricLogs_ = flagValue;
|
||||
@@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRenderer() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(19, "enableFabricRenderer");
|
||||
markFlagAsAccessed(20, "enableFabricRenderer");
|
||||
|
||||
flagValue = currentProvider_->enableFabricRenderer();
|
||||
enableFabricRenderer_ = flagValue;
|
||||
@@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(20, "enableFontScaleChangesUpdatingLayout");
|
||||
markFlagAsAccessed(21, "enableFontScaleChangesUpdatingLayout");
|
||||
|
||||
flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout();
|
||||
enableFontScaleChangesUpdatingLayout_ = flagValue;
|
||||
@@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(21, "enableIOSTextBaselineOffsetPerLine");
|
||||
markFlagAsAccessed(22, "enableIOSTextBaselineOffsetPerLine");
|
||||
|
||||
flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine();
|
||||
enableIOSTextBaselineOffsetPerLine_ = flagValue;
|
||||
@@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(22, "enableIOSViewClipToPaddingBox");
|
||||
markFlagAsAccessed(23, "enableIOSViewClipToPaddingBox");
|
||||
|
||||
flagValue = currentProvider_->enableIOSViewClipToPaddingBox();
|
||||
enableIOSViewClipToPaddingBox_ = flagValue;
|
||||
@@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(23, "enableImagePrefetchingAndroid");
|
||||
markFlagAsAccessed(24, "enableImagePrefetchingAndroid");
|
||||
|
||||
flagValue = currentProvider_->enableImagePrefetchingAndroid();
|
||||
enableImagePrefetchingAndroid_ = flagValue;
|
||||
@@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingOnUiThreadAndroid()
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(24, "enableImagePrefetchingOnUiThreadAndroid");
|
||||
markFlagAsAccessed(25, "enableImagePrefetchingOnUiThreadAndroid");
|
||||
|
||||
flagValue = currentProvider_->enableImagePrefetchingOnUiThreadAndroid();
|
||||
enableImagePrefetchingOnUiThreadAndroid_ = flagValue;
|
||||
@@ -488,7 +506,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(25, "enableImmediateUpdateModeForContentOffsetChanges");
|
||||
markFlagAsAccessed(26, "enableImmediateUpdateModeForContentOffsetChanges");
|
||||
|
||||
flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges();
|
||||
enableImmediateUpdateModeForContentOffsetChanges_ = flagValue;
|
||||
@@ -506,7 +524,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(26, "enableInteropViewManagerClassLookUpOptimizationIOS");
|
||||
markFlagAsAccessed(27, "enableInteropViewManagerClassLookUpOptimizationIOS");
|
||||
|
||||
flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS();
|
||||
enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue;
|
||||
@@ -524,7 +542,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(27, "enableLayoutAnimationsOnAndroid");
|
||||
markFlagAsAccessed(28, "enableLayoutAnimationsOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
|
||||
enableLayoutAnimationsOnAndroid_ = flagValue;
|
||||
@@ -542,7 +560,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(28, "enableLayoutAnimationsOnIOS");
|
||||
markFlagAsAccessed(29, "enableLayoutAnimationsOnIOS");
|
||||
|
||||
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
|
||||
enableLayoutAnimationsOnIOS_ = flagValue;
|
||||
@@ -560,7 +578,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(29, "enableMainQueueCoordinatorOnIOS");
|
||||
markFlagAsAccessed(30, "enableMainQueueCoordinatorOnIOS");
|
||||
|
||||
flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS();
|
||||
enableMainQueueCoordinatorOnIOS_ = flagValue;
|
||||
@@ -578,7 +596,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(30, "enableModuleArgumentNSNullConversionIOS");
|
||||
markFlagAsAccessed(31, "enableModuleArgumentNSNullConversionIOS");
|
||||
|
||||
flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS();
|
||||
enableModuleArgumentNSNullConversionIOS_ = flagValue;
|
||||
@@ -596,7 +614,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(31, "enableNativeCSSParsing");
|
||||
markFlagAsAccessed(32, "enableNativeCSSParsing");
|
||||
|
||||
flagValue = currentProvider_->enableNativeCSSParsing();
|
||||
enableNativeCSSParsing_ = flagValue;
|
||||
@@ -614,7 +632,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(32, "enableNetworkEventReporting");
|
||||
markFlagAsAccessed(33, "enableNetworkEventReporting");
|
||||
|
||||
flagValue = currentProvider_->enableNetworkEventReporting();
|
||||
enableNetworkEventReporting_ = flagValue;
|
||||
@@ -632,7 +650,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(33, "enablePreparedTextLayout");
|
||||
markFlagAsAccessed(34, "enablePreparedTextLayout");
|
||||
|
||||
flagValue = currentProvider_->enablePreparedTextLayout();
|
||||
enablePreparedTextLayout_ = flagValue;
|
||||
@@ -650,7 +668,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(34, "enablePropsUpdateReconciliationAndroid");
|
||||
markFlagAsAccessed(35, "enablePropsUpdateReconciliationAndroid");
|
||||
|
||||
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
|
||||
enablePropsUpdateReconciliationAndroid_ = flagValue;
|
||||
@@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(35, "enableResourceTimingAPI");
|
||||
markFlagAsAccessed(36, "enableResourceTimingAPI");
|
||||
|
||||
flagValue = currentProvider_->enableResourceTimingAPI();
|
||||
enableResourceTimingAPI_ = flagValue;
|
||||
@@ -677,6 +695,24 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() {
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() {
|
||||
auto flagValue = enableSwiftUIBasedFilters_.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(37, "enableSwiftUIBasedFilters");
|
||||
|
||||
flagValue = currentProvider_->enableSwiftUIBasedFilters();
|
||||
enableSwiftUIBasedFilters_ = flagValue;
|
||||
}
|
||||
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
|
||||
auto flagValue = enableViewCulling_.load();
|
||||
|
||||
@@ -686,7 +722,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(36, "enableViewCulling");
|
||||
markFlagAsAccessed(38, "enableViewCulling");
|
||||
|
||||
flagValue = currentProvider_->enableViewCulling();
|
||||
enableViewCulling_ = flagValue;
|
||||
@@ -704,7 +740,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(37, "enableViewRecycling");
|
||||
markFlagAsAccessed(39, "enableViewRecycling");
|
||||
|
||||
flagValue = currentProvider_->enableViewRecycling();
|
||||
enableViewRecycling_ = flagValue;
|
||||
@@ -722,7 +758,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(38, "enableViewRecyclingForImage");
|
||||
markFlagAsAccessed(40, "enableViewRecyclingForImage");
|
||||
|
||||
flagValue = currentProvider_->enableViewRecyclingForImage();
|
||||
enableViewRecyclingForImage_ = flagValue;
|
||||
@@ -740,7 +776,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(39, "enableViewRecyclingForScrollView");
|
||||
markFlagAsAccessed(41, "enableViewRecyclingForScrollView");
|
||||
|
||||
flagValue = currentProvider_->enableViewRecyclingForScrollView();
|
||||
enableViewRecyclingForScrollView_ = flagValue;
|
||||
@@ -758,7 +794,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(40, "enableViewRecyclingForText");
|
||||
markFlagAsAccessed(42, "enableViewRecyclingForText");
|
||||
|
||||
flagValue = currentProvider_->enableViewRecyclingForText();
|
||||
enableViewRecyclingForText_ = flagValue;
|
||||
@@ -776,7 +812,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(41, "enableViewRecyclingForView");
|
||||
markFlagAsAccessed(43, "enableViewRecyclingForView");
|
||||
|
||||
flagValue = currentProvider_->enableViewRecyclingForView();
|
||||
enableViewRecyclingForView_ = flagValue;
|
||||
@@ -794,7 +830,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(42, "enableVirtualViewDebugFeatures");
|
||||
markFlagAsAccessed(44, "enableVirtualViewDebugFeatures");
|
||||
|
||||
flagValue = currentProvider_->enableVirtualViewDebugFeatures();
|
||||
enableVirtualViewDebugFeatures_ = flagValue;
|
||||
@@ -812,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(43, "enableVirtualViewRenderState");
|
||||
markFlagAsAccessed(45, "enableVirtualViewRenderState");
|
||||
|
||||
flagValue = currentProvider_->enableVirtualViewRenderState();
|
||||
enableVirtualViewRenderState_ = flagValue;
|
||||
@@ -830,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(44, "enableVirtualViewWindowFocusDetection");
|
||||
markFlagAsAccessed(46, "enableVirtualViewWindowFocusDetection");
|
||||
|
||||
flagValue = currentProvider_->enableVirtualViewWindowFocusDetection();
|
||||
enableVirtualViewWindowFocusDetection_ = flagValue;
|
||||
@@ -848,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableWebPerformanceAPIsByDefault() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(45, "enableWebPerformanceAPIsByDefault");
|
||||
markFlagAsAccessed(47, "enableWebPerformanceAPIsByDefault");
|
||||
|
||||
flagValue = currentProvider_->enableWebPerformanceAPIsByDefault();
|
||||
enableWebPerformanceAPIsByDefault_ = flagValue;
|
||||
@@ -866,7 +902,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(46, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
|
||||
markFlagAsAccessed(48, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
|
||||
|
||||
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
|
||||
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
|
||||
@@ -884,7 +920,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(47, "fuseboxEnabledRelease");
|
||||
markFlagAsAccessed(49, "fuseboxEnabledRelease");
|
||||
|
||||
flagValue = currentProvider_->fuseboxEnabledRelease();
|
||||
fuseboxEnabledRelease_ = flagValue;
|
||||
@@ -902,7 +938,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(48, "fuseboxNetworkInspectionEnabled");
|
||||
markFlagAsAccessed(50, "fuseboxNetworkInspectionEnabled");
|
||||
|
||||
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
|
||||
fuseboxNetworkInspectionEnabled_ = flagValue;
|
||||
@@ -920,7 +956,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(49, "hideOffscreenVirtualViewsOnIOS");
|
||||
markFlagAsAccessed(51, "hideOffscreenVirtualViewsOnIOS");
|
||||
|
||||
flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS();
|
||||
hideOffscreenVirtualViewsOnIOS_ = flagValue;
|
||||
@@ -938,7 +974,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(50, "overrideBySynchronousMountPropsAtMountingAndroid");
|
||||
markFlagAsAccessed(52, "overrideBySynchronousMountPropsAtMountingAndroid");
|
||||
|
||||
flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid();
|
||||
overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue;
|
||||
@@ -956,7 +992,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(51, "perfMonitorV2Enabled");
|
||||
markFlagAsAccessed(53, "perfMonitorV2Enabled");
|
||||
|
||||
flagValue = currentProvider_->perfMonitorV2Enabled();
|
||||
perfMonitorV2Enabled_ = flagValue;
|
||||
@@ -974,7 +1010,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(52, "preparedTextCacheSize");
|
||||
markFlagAsAccessed(54, "preparedTextCacheSize");
|
||||
|
||||
flagValue = currentProvider_->preparedTextCacheSize();
|
||||
preparedTextCacheSize_ = flagValue;
|
||||
@@ -992,7 +1028,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(53, "preventShadowTreeCommitExhaustion");
|
||||
markFlagAsAccessed(55, "preventShadowTreeCommitExhaustion");
|
||||
|
||||
flagValue = currentProvider_->preventShadowTreeCommitExhaustion();
|
||||
preventShadowTreeCommitExhaustion_ = flagValue;
|
||||
@@ -1010,7 +1046,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(54, "shouldPressibilityUseW3CPointerEventsForHover");
|
||||
markFlagAsAccessed(56, "shouldPressibilityUseW3CPointerEventsForHover");
|
||||
|
||||
flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover();
|
||||
shouldPressibilityUseW3CPointerEventsForHover_ = flagValue;
|
||||
@@ -1028,7 +1064,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(55, "skipActivityIdentityAssertionOnHostPause");
|
||||
markFlagAsAccessed(57, "skipActivityIdentityAssertionOnHostPause");
|
||||
|
||||
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
|
||||
skipActivityIdentityAssertionOnHostPause_ = flagValue;
|
||||
@@ -1046,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(56, "sweepActiveTouchOnChildNativeGesturesAndroid");
|
||||
markFlagAsAccessed(58, "sweepActiveTouchOnChildNativeGesturesAndroid");
|
||||
|
||||
flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid();
|
||||
sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue;
|
||||
@@ -1064,7 +1100,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(57, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
markFlagAsAccessed(59, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
|
||||
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
|
||||
@@ -1082,7 +1118,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(58, "updateRuntimeShadowNodeReferencesOnCommit");
|
||||
markFlagAsAccessed(60, "updateRuntimeShadowNodeReferencesOnCommit");
|
||||
|
||||
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
|
||||
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
|
||||
@@ -1100,7 +1136,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(59, "useAlwaysAvailableJSErrorHandling");
|
||||
markFlagAsAccessed(61, "useAlwaysAvailableJSErrorHandling");
|
||||
|
||||
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
|
||||
useAlwaysAvailableJSErrorHandling_ = flagValue;
|
||||
@@ -1118,7 +1154,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(60, "useFabricInterop");
|
||||
markFlagAsAccessed(62, "useFabricInterop");
|
||||
|
||||
flagValue = currentProvider_->useFabricInterop();
|
||||
useFabricInterop_ = flagValue;
|
||||
@@ -1136,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(61, "useNativeEqualsInNativeReadableArrayAndroid");
|
||||
markFlagAsAccessed(63, "useNativeEqualsInNativeReadableArrayAndroid");
|
||||
|
||||
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
|
||||
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
|
||||
@@ -1154,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(62, "useNativeTransformHelperAndroid");
|
||||
markFlagAsAccessed(64, "useNativeTransformHelperAndroid");
|
||||
|
||||
flagValue = currentProvider_->useNativeTransformHelperAndroid();
|
||||
useNativeTransformHelperAndroid_ = flagValue;
|
||||
@@ -1172,7 +1208,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(63, "useNativeViewConfigsInBridgelessMode");
|
||||
markFlagAsAccessed(65, "useNativeViewConfigsInBridgelessMode");
|
||||
|
||||
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
|
||||
useNativeViewConfigsInBridgelessMode_ = flagValue;
|
||||
@@ -1190,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(64, "useOptimizedEventBatchingOnAndroid");
|
||||
markFlagAsAccessed(66, "useOptimizedEventBatchingOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
|
||||
useOptimizedEventBatchingOnAndroid_ = flagValue;
|
||||
@@ -1208,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(65, "useRawPropsJsiValue");
|
||||
markFlagAsAccessed(67, "useRawPropsJsiValue");
|
||||
|
||||
flagValue = currentProvider_->useRawPropsJsiValue();
|
||||
useRawPropsJsiValue_ = flagValue;
|
||||
@@ -1226,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(66, "useShadowNodeStateOnClone");
|
||||
markFlagAsAccessed(68, "useShadowNodeStateOnClone");
|
||||
|
||||
flagValue = currentProvider_->useShadowNodeStateOnClone();
|
||||
useShadowNodeStateOnClone_ = flagValue;
|
||||
@@ -1244,7 +1280,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(67, "useTurboModuleInterop");
|
||||
markFlagAsAccessed(69, "useTurboModuleInterop");
|
||||
|
||||
flagValue = currentProvider_->useTurboModuleInterop();
|
||||
useTurboModuleInterop_ = flagValue;
|
||||
@@ -1262,7 +1298,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(68, "useTurboModules");
|
||||
markFlagAsAccessed(70, "useTurboModules");
|
||||
|
||||
flagValue = currentProvider_->useTurboModules();
|
||||
useTurboModules_ = flagValue;
|
||||
@@ -1280,7 +1316,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(69, "virtualViewHysteresisRatio");
|
||||
markFlagAsAccessed(71, "virtualViewHysteresisRatio");
|
||||
|
||||
flagValue = currentProvider_->virtualViewHysteresisRatio();
|
||||
virtualViewHysteresisRatio_ = flagValue;
|
||||
@@ -1298,7 +1334,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(70, "virtualViewPrerenderRatio");
|
||||
markFlagAsAccessed(72, "virtualViewPrerenderRatio");
|
||||
|
||||
flagValue = currentProvider_->virtualViewPrerenderRatio();
|
||||
virtualViewPrerenderRatio_ = flagValue;
|
||||
|
||||
+6
-2
@@ -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<<17f7a8577d05b191010622fb58ba27f8>>
|
||||
* @generated SignedSource<<6f05211b44f2be076b37ef9178e5ef43>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -42,6 +42,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
bool disableTextLayoutManagerCacheAndroid();
|
||||
bool enableAccessibilityOrder();
|
||||
bool enableAccumulatedUpdatesInRawPropsAndroid();
|
||||
bool enableAndroidLinearText();
|
||||
bool enableAndroidTextMeasurementOptimizations();
|
||||
bool enableBridgelessArchitecture();
|
||||
bool enableCppPropsIteratorSetter();
|
||||
@@ -68,6 +69,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
bool enablePreparedTextLayout();
|
||||
bool enablePropsUpdateReconciliationAndroid();
|
||||
bool enableResourceTimingAPI();
|
||||
bool enableSwiftUIBasedFilters();
|
||||
bool enableViewCulling();
|
||||
bool enableViewRecycling();
|
||||
bool enableViewRecyclingForImage();
|
||||
@@ -114,7 +116,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
|
||||
bool wasOverridden_;
|
||||
|
||||
std::array<std::atomic<const char*>, 71> accessedFeatureFlags_;
|
||||
std::array<std::atomic<const char*>, 73> accessedFeatureFlags_;
|
||||
|
||||
std::atomic<std::optional<bool>> commonTestFlag_;
|
||||
std::atomic<std::optional<bool>> cdpInteractionMetricsEnabled_;
|
||||
@@ -126,6 +128,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::atomic<std::optional<bool>> disableTextLayoutManagerCacheAndroid_;
|
||||
std::atomic<std::optional<bool>> enableAccessibilityOrder_;
|
||||
std::atomic<std::optional<bool>> enableAccumulatedUpdatesInRawPropsAndroid_;
|
||||
std::atomic<std::optional<bool>> enableAndroidLinearText_;
|
||||
std::atomic<std::optional<bool>> enableAndroidTextMeasurementOptimizations_;
|
||||
std::atomic<std::optional<bool>> enableBridgelessArchitecture_;
|
||||
std::atomic<std::optional<bool>> enableCppPropsIteratorSetter_;
|
||||
@@ -152,6 +155,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::atomic<std::optional<bool>> enablePreparedTextLayout_;
|
||||
std::atomic<std::optional<bool>> enablePropsUpdateReconciliationAndroid_;
|
||||
std::atomic<std::optional<bool>> enableResourceTimingAPI_;
|
||||
std::atomic<std::optional<bool>> enableSwiftUIBasedFilters_;
|
||||
std::atomic<std::optional<bool>> enableViewCulling_;
|
||||
std::atomic<std::optional<bool>> enableViewRecycling_;
|
||||
std::atomic<std::optional<bool>> enableViewRecyclingForImage_;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user