Compare commits

..
Author SHA1 Message Date
Devmate Bot 811ffc2653 xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/components/safeareaview/SafeAreaViewShadowNode.cpp
Reviewed By: rshest

Differential Revision: D79627030
2025-08-06 04:02:16 -07:00
118 changed files with 925 additions and 3016 deletions
@@ -118,6 +118,7 @@ async function collectResults(discordWebHook) {
} else {
console.log('Discord webhook not set');
}
process.exit(1);
}
// Initialize Firebase client
+211 -31
View File
@@ -1,40 +1,218 @@
# Changelog
## v0.81.0-rc.5
### Fixed
#### Android specific
- **Runtime:** Fixed `ReactHostImpl.nativeModules` always returning an empty list ([2f46a49](https://github.com/facebook/react-native/commit/2f46a49b8d8a11d5cf4342eee83c469b545c6779) by [@lukmccall](https://github.com/lukmccall))
## v0.81.0-rc.4 - Burned
## v0.81.0-rc.3
### Breaking
- Metro to ^0.83.1 ([e247be793c](https://github.com/facebook/react-native/commit/e247be793c70a374955d798d8cbbc6eba58080ec) by [@motiz88](https://github.com/motiz88))
#### Android specific
#### iOS specific
### Added
#### Android specific
#### iOS specific
### Changed
- **Metro:** Metro to ^0.83.1 ([e247be793c](https://github.com/facebook/react-native/commit/e247be793c70a374955d798d8cbbc6eba58080ec) by [@motiz88](https://github.com/motiz88))
#### Android specific
#### iOS specific
### Deprecated
#### Android specific
#### iOS specific
### Removed
#### Android specific
#### iOS specific
### Fixed
#### Android specific
- **rngp:** Fix a race condition with codegen libraries missing sources ([9013a9e666](https://github.com/facebook/react-native/commit/9013a9e66629677c47e1b69703f9fc8f4cbc1c2c) by [@cortinico](https://github.com/cortinico))
- **API:** Make accessors inside HeadlessJsTaskService open again ([7ef57163cb](https://github.com/facebook/react-native/commit/7ef57163cb016317e43e563da7ea181989f6abca) by [@cortinico](https://github.com/cortinico))
## v0.81.0-rc.2
### Changed
- **API:** `NewAppScreen` no longer internally handles device safe area, use optional `safeAreaInsets` prop (aligned in 0.81 template) ([732bd12dc2](https://github.com/facebook/react-native/commit/732bd12dc21460641ef01b23f2eb722f26b060d5) by [@huntie](https://github.com/huntie))
- **Babel:** Added support to `react-native/babel-preset` for a `hermesParserOptions` option, that expects an object that enables overriding `hermes-parser` options. ([0508eddfe6](https://github.com/facebook/react-native/commit/0508eddfe60df60cb3bfa4074ae199bd0e492d5f) by [@yungsters](https://github.com/yungsters))
### Fixed
- Make accessors inside HeadlessJsTaskService open again ([7ef57163cb](https://github.com/facebook/react-native/commit/7ef57163cb016317e43e563da7ea181989f6abca) by [@cortinico](https://github.com/cortinico))
#### iOS specific
- **Podspec:** Fixed issue with RNDeps release/debug switch failing ([4ee2b60a1e](https://github.com/facebook/react-native/commit/4ee2b60a1eacca744d58a7ad336ca9d3714289f6) by [@chrfalch](https://github.com/chrfalch))
- **Podspec:** Fixed missing script for resolving prebuilt xcframework when switching between release/debug ([2e55241a90](https://github.com/facebook/react-native/commit/2e55241a901b4cd95917de68ce9078928820a208) by [@chrfalch](https://github.com/chrfalch))
### Security
#### Android specific
#### iOS specific
### Unknown
- Release 0.81.0-rc.3 ([0e6009eecf](https://github.com/facebook/react-native/commit/0e6009eecfeac71121a045f0f7e6bae94ffc11b0) by [@react-native-bot](https://github.com/react-native-bot))
- Bump Podfile.lock ([3695258eed](https://github.com/facebook/react-native/commit/3695258eed45fed5fbff5dd6128c88275f91083c) by [@react-native-bot](https://github.com/react-native-bot))
#### Android Unknown
#### iOS Unknown
#### Failed to parse
## v0.81.0-rc.2
### Breaking
#### Android specific
#### iOS specific
### Added
#### Android specific
#### iOS specific
### Changed
- Added support to `react-native/babel-preset` for a `hermesParserOptions` option, that expects an object that enables overriding `hermes-parser` options. ([0508eddfe6](https://github.com/facebook/react-native/commit/0508eddfe60df60cb3bfa4074ae199bd0e492d5f) by [@yungsters](https://github.com/yungsters))
- `NewAppScreen` no longer internally handles device safe area, use optional `safeAreaInsets` prop (aligned in 0.81 template) ([732bd12dc2](https://github.com/facebook/react-native/commit/732bd12dc21460641ef01b23f2eb722f26b060d5) by [@huntie](https://github.com/huntie))
#### Android specific
#### iOS specific
### Deprecated
#### Android specific
#### iOS specific
### Removed
#### Android specific
#### iOS specific
### Fixed
#### Android specific
#### iOS specific
- Fixed issue with RNDeps release/debug switch failing ([4ee2b60a1e](https://github.com/facebook/react-native/commit/4ee2b60a1eacca744d58a7ad336ca9d3714289f6) by [@chrfalch](https://github.com/chrfalch))
- Fixed missing script for resolving prebuilt xcframework when switching between release/debug ([2e55241a90](https://github.com/facebook/react-native/commit/2e55241a901b4cd95917de68ce9078928820a208) by [@chrfalch](https://github.com/chrfalch))
### Security
#### Android specific
#### iOS specific
### Unknown
- Release 0.81.0-rc.2 ([68ef746ec5](https://github.com/facebook/react-native/commit/68ef746ec5dd7d2874d190733e75bb8197034c5a) by [@react-native-bot](https://github.com/react-native-bot))
- Fix E2E test script when the ci flag is not specified ([cdd7f99581](https://github.com/facebook/react-native/commit/cdd7f995813727b630ff38abc8a910a0f8f10b37) by [@cipolleschi](https://github.com/cipolleschi))
- Fix E2E script when using CI artifacts ([d8bf94489a](https://github.com/facebook/react-native/commit/d8bf94489ab7498eba7e5f45d09dd2819fe739c3) by [@cipolleschi](https://github.com/cipolleschi))
- Bump Podfile.lock ([10b63c15b6](https://github.com/facebook/react-native/commit/10b63c15b6faaf54d555ede879f82ac565f030a9) by [@react-native-bot](https://github.com/react-native-bot))
- Release 0.81.0-rc.1 ([b06bb89ddd](https://github.com/facebook/react-native/commit/b06bb89ddd3cebddea4716036a4368b87a65f492) by [@react-native-bot](https://github.com/react-native-bot))
#### Android Unknown
#### iOS Unknown
#### Failed to parse
## v0.81.0-rc.1
@@ -42,27 +220,28 @@
#### iOS specific
- **CocoaPods** Add the `ENTERPRISE_REPOSITORY` env variable to cocoapods infra ([23f3bf9239](https://github.com/facebook/react-native/commit/23f3bf9239a849590f1c72b25732d0090780128c) by [@cipolleschi](https://github.com/cipolleschi))
- **Prebuild:** Add release/debug switch script for React-Core-prebuilt ([42d1a7934c](https://github.com/facebook/react-native/commit/42d1a7934cad4b2c92653e3fa7781c2af8f44df4) by [@chrfalch](https://github.com/chrfalch))
- **Prebuild:** Added support for using USE_FRAMEWORKS with prebuilt React Native Core ([40e45f5366](https://github.com/facebook/react-native/commit/40e45f53661ce80c3a6fbbf07f52dc900afcad52) by [@chrfalch](https://github.com/chrfalch))
- Add release/debug switch script for React-Core-prebuilt ([42d1a7934c](https://github.com/facebook/react-native/commit/42d1a7934cad4b2c92653e3fa7781c2af8f44df4) by [@chrfalch](https://github.com/chrfalch))
- Added support for using USE_FRAMEWORKS with prebuilt React Native Core ([40e45f5366](https://github.com/facebook/react-native/commit/40e45f53661ce80c3a6fbbf07f52dc900afcad52) by [@chrfalch](https://github.com/chrfalch))
- Add the `ENTERPRISE_REPOSITORY` env variable to cocopaods infra ([23f3bf9239](https://github.com/facebook/react-native/commit/23f3bf9239a849590f1c72b25732d0090780128c) by [@cipolleschi](https://github.com/cipolleschi))
### Changed
- **Metro:** Bump Metro to 0.83.0 ([6b9f5d622f](https://github.com/facebook/react-native/commit/6b9f5d622ffbe79da8f4e7b7d8094504a480425e) by [@robhogan](https://github.com/robhogan))
- Bump Metro to 0.83.0 ([6b9f5d622f](https://github.com/facebook/react-native/commit/6b9f5d622ffbe79da8f4e7b7d8094504a480425e) by [@robhogan](https://github.com/robhogan))
#### Android specific
- **Gradle:** Gradle to 8.14.3 ([6892dde363](https://github.com/facebook/react-native/commit/6892dde36373bbef2d0afe535ae818b1a7164f08) by [@cortinico](https://github.com/cortinico))
- **Gradle:** Expose `react_renderer_bridging` headers via prefab ([d1730ff960](https://github.com/facebook/react-native/commit/d1730ff960fcb9a01ee94b9e46e5a9fbb7d73f4a) by [@tomekzaw](https://github.com/tomekzaw))
- **Legacy Arch:** Introduce more deprecation warnings for Legacy Arch classes ([625f69f284](https://github.com/facebook/react-native/commit/625f69f284ddfd9c6beecaa4052a871d092053ef) by [@cortinico](https://github.com/cortinico))
- Gradle to 8.14.3 ([6892dde363](https://github.com/facebook/react-native/commit/6892dde36373bbef2d0afe535ae818b1a7164f08) by [@cortinico](https://github.com/cortinico))
- Expose `react_renderer_bridging` headers via prefab ([d1730ff960](https://github.com/facebook/react-native/commit/d1730ff960fcb9a01ee94b9e46e5a9fbb7d73f4a) by [@tomekzaw](https://github.com/tomekzaw))
- Introduce more deprecation warnings for Legacy Arch classes ([625f69f284](https://github.com/facebook/react-native/commit/625f69f284ddfd9c6beecaa4052a871d092053ef) by [@cortinico](https://github.com/cortinico))
### Fixed
- **Yoga:** Fixed nodes with `display: contents` set being cloned with the wrong owner ([d4b36b0300](https://github.com/facebook/react-native/commit/d4b36b03003eb2de9eaf5b57bb639bae8cc12f20) by [@j-piasecki](https://github.com/j-piasecki))
- Fixed nodes with `display: contents` set being cloned with the wrong owner ([d4b36b0300](https://github.com/facebook/react-native/commit/d4b36b03003eb2de9eaf5b57bb639bae8cc12f20) by [@j-piasecki](https://github.com/j-piasecki))
#### iOS specific
- **Podspec:** Fixed premature return in header file generation from podspec globs ([f2b064c2d4](https://github.com/facebook/react-native/commit/f2b064c2d40c39017ac2a31bf3caf8acef23038c) by [@chrfalch](https://github.com/chrfalch))
- Fixed premature return in header file generation from podspec globs ([f2b064c2d4](https://github.com/facebook/react-native/commit/f2b064c2d40c39017ac2a31bf3caf8acef23038c) by [@chrfalch](https://github.com/chrfalch))
## v0.81.0-rc.0
@@ -1407,3 +1586,4 @@ See [CHANGELOG-0.5x](./CHANGELOG-0.5x.md#v0530)
## v0.52.0
See [CHANGELOG-0.5x](./CHANGELOG-0.5x.md#v0520)
@@ -29,8 +29,6 @@
"lib"
],
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/parser": "^7.25.3",
"glob": "^7.1.1",
"hermes-parser": "0.30.0",
"invariant": "^2.2.4",
@@ -15,7 +15,7 @@ import type {HostInstance} from 'react-native';
import ensureInstance from '../../../src/private/__tests__/utilities/ensureInstance';
import * as Fantom from '@react-native/fantom';
import {createRef} from 'react';
import {createRef, useMemo} from 'react';
import {Animated, View, useAnimatedValue} from 'react-native';
import {allowStyleProp} from 'react-native/Libraries/Animated/NativeAnimatedAllowlist';
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
@@ -747,3 +747,98 @@ test('Animated.sequence', () => {
expect(_isSequenceFinished).toBe(true);
});
test('Props default value is restored when disconnected from animated', () => {
let _animatedOpacity;
const elementRef = createRef<HostInstance>();
function MyApp({
shouldAnimate,
}: $ReadOnly<{
shouldAnimate?: boolean,
}>) {
const animatedOpacity = useAnimatedValue(1, {useNativeDriver: true});
const opacity = useMemo(
() => (shouldAnimate === true ? animatedOpacity : undefined),
[shouldAnimate, animatedOpacity],
);
const scale = useMemo(
() =>
opacity?.interpolate({
inputRange: [0, 1],
outputRange: [0.95, 1],
}) ?? new Animated.Value(1),
[opacity],
);
_animatedOpacity = animatedOpacity;
return (
<Animated.View
ref={elementRef}
style={[
{
opacity,
transform: [{scale}],
height: 100,
width: 100,
},
]}
/>
);
}
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<MyApp shouldAnimate={true} />);
});
const element = ensureInstance(elementRef.current, ReactNativeElement);
expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual(
<rn-view />, // default opacity is 1
);
Fantom.runTask(() => {
Animated.timing(_animatedOpacity, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
});
Fantom.unstable_produceFramesForDuration(500);
Fantom.runWorkLoop();
expect(Fantom.unstable_getDirectManipulationProps(element)).toEqual({
opacity: 0,
transform: [{scale: 0.95}],
});
expect(
root.getRenderedOutput({props: ['opacity', 'transform']}).toJSX(),
).toEqual(<rn-view opacity="0" transform='[{"scale": 0.950000}]' />);
Fantom.runTask(() => {
root.render(<MyApp shouldAnimate={false} />);
});
expect(Fantom.unstable_getDirectManipulationProps(element)).toEqual({
opacity: null,
transform: null,
});
if (ReactNativeFeatureFlags.cxxNativeAnimatedRemoveJsSync()) {
expect(
root.getRenderedOutput({props: ['opacity', 'transform']}).toJSX(),
).toEqual(<rn-view transform='[{"scale": 0.950000}]' />); // TODO: T223344928 scale should be 1
} else {
expect(
root.getRenderedOutput({props: ['opacity', 'transform']}).toJSX(),
).toEqual(<rn-view transform='[{"scale": 1.000000}]' />);
}
Fantom.runWorkLoop();
});
@@ -618,9 +618,6 @@ function InternalTextInput(props: TextInputProps): React.Node {
// so omitting onBlur and onFocus pressability handlers here.
const {onBlur, onFocus, ...eventHandlers} = usePressability(config);
const _accessibilityLabel =
props?.['aria-label'] ?? props?.accessibilityLabel;
let _accessibilityState;
if (
accessibilityState != null ||
@@ -684,7 +681,6 @@ function InternalTextInput(props: TextInputProps): React.Node {
{...otherProps}
{...eventHandlers}
acceptDragAndDropTypes={props.experimental_acceptDragAndDropTypes}
accessibilityLabel={_accessibilityLabel}
accessibilityState={_accessibilityState}
accessible={accessible}
submitBehavior={submitBehavior}
@@ -748,9 +744,8 @@ function InternalTextInput(props: TextInputProps): React.Node {
{...otherProps}
{...colorProps}
{...eventHandlers}
accessibilityLabel={_accessibilityLabel}
accessibilityLabelledBy={_accessibilityLabelledBy}
accessibilityState={_accessibilityState}
accessibilityLabelledBy={_accessibilityLabelledBy}
accessible={accessible}
acceptDragAndDropTypes={props.experimental_acceptDragAndDropTypes}
autoCapitalize={autoCapitalize}
@@ -432,7 +432,6 @@ jest.unmock('../TextInput');
expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityLabel="label"
accessibilityState={
Object {
"busy": true,
@@ -169,460 +169,6 @@ describe('<Image>', () => {
);
});
});
describe('defaultSource', () => {
it('can provide a default image to display', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
defaultSource={require('./img/img1.png')}
source={LOGO_SOURCE}
/>,
);
});
expect(
root.getRenderedOutput({props: ['defaultSource']}).toJSX(),
).toEqual(
<rn-image
defaultSource-type="remote"
defaultSource-uri="file://drawable-mdpi/packages_reactnative_libraries_image___tests___img_img1.png"
/>,
);
});
});
describe('height', () => {
it('provides height for image', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image height={100} source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual(
<rn-image height="100.000000" />,
);
});
});
describe('width', () => {
it('provides width for image', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image width={100} source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual(
<rn-image width="100.000000" />,
);
});
});
describe('loading progress', () => {
(
[
['onError', 'fails to load'],
['onLoadStart', 'start loading'],
['onProgress', 'is loading'],
['onLoad', 'loads successfully'],
['onLoadEnd', 'ends loading'],
] as const
).forEach(([onProp, event]) => {
it(`${onProp} is called when image ${event}`, () => {
const onPropCallback = jest.fn();
const ref = createRef<HostInstance>();
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
ref={ref}
source={LOGO_SOURCE}
onError={() => {
onProp === 'onError' && onPropCallback();
}}
onLoad={() => {
onProp === 'onLoad' && onPropCallback();
}}
onLoadStart={() => {
onProp === 'onLoadStart' && onPropCallback();
}}
onLoadEnd={() => {
onProp === 'onLoadEnd' && onPropCallback();
}}
onProgress={() => {
onProp === 'onProgress' && onPropCallback();
}}
/>,
);
});
expect(onPropCallback).toHaveBeenCalledTimes(0);
const image = ensureInstance(ref.current, ReactNativeElement);
Fantom.dispatchNativeEvent(image, onProp, {});
expect(onPropCallback).toHaveBeenCalledTimes(1);
});
});
});
describe('referrerPolicy', () => {
(
[
'no-referrer',
'no-referrer-when-downgrade',
'origin',
'origin-when-cross-origin',
'same-origin',
'strict-origin',
'strict-origin-when-cross-origin',
'unsafe-url',
] as const
).forEach(referrerPolicy => {
it(`${referrerPolicy} sets correct "Referrer-Policy" header`, () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image referrerPolicy={referrerPolicy} src={LOGO_SOURCE.uri} />,
);
});
expect(
root.getRenderedOutput({props: ['source-header']}).toJSX(),
).toEqual(
<rn-image source-header-Referrer-Policy={referrerPolicy} />,
);
});
});
});
describe('resizeMode', () => {
it('is set to "cover" by default', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['resizeMode']}).toJSX()).toEqual(
<rn-image />,
);
Fantom.runTask(() => {
root.render(<Image resizeMode="cover" source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['resizeMode']}).toJSX()).toEqual(
<rn-image />,
);
});
(['stretch', 'contain', 'repeat', 'center'] as const).forEach(
resizeMode => {
it(`can be set to "${resizeMode}"`, () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image resizeMode={resizeMode} source={LOGO_SOURCE} />,
);
});
expect(
root.getRenderedOutput({props: ['resizeMode']}).toJSX(),
).toEqual(<rn-image resizeMode={resizeMode} />);
});
},
);
});
describe('source', () => {
it('can be set to a local image', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image source={require('./img/img1.png')} />);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-scale="1"
source-size="{1, 1}"
source-type="local"
source-uri="file://drawable-mdpi/packages_reactnative_libraries_image___tests___img_img1.png"
/>,
);
});
it('can be set to a remote image', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
width: 100,
height: 100,
scale: 2,
cache: 'only-if-cached',
method: 'POST',
body: 'name=React+Native',
headers: {
Authorization: 'Basic RandomString',
},
}}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-body="name=React+Native"
source-cache="only-if-cached"
source-header-Authorization="Basic RandomString"
source-method="POST"
source-scale="2"
source-size="{100, 100}"
source-type="remote"
source-uri="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
it('can be set to a list of remote images', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
source={[
{
uri: 'https://reactnative.dev/img/tiny_logo.png',
scale: 1,
headers: {
Authorization: 'Basic RandomString',
},
},
{
uri: 'https://reactnative.dev/img/medium_logo.png',
scale: 2,
cache: 'only-if-cached',
},
{
uri: 'https://reactnative.dev/img/large_logo.png',
scale: 3,
method: 'POST',
},
]}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-1x-header-Authorization="Basic RandomString"
source-1x-scale="1"
source-1x-type="remote"
source-1x-uri="https://reactnative.dev/img/tiny_logo.png"
source-2x-cache="only-if-cached"
source-2x-scale="2"
source-2x-type="remote"
source-2x-uri="https://reactnative.dev/img/medium_logo.png"
source-3x-method="POST"
source-3x-type="remote"
source-3x-uri="https://reactnative.dev/img/large_logo.png"
/>,
);
});
});
describe('src', () => {
it('can be set to a remote image', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image src="https://reactnative.dev/img/tiny_logo.png" />,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-scale="1"
source-type="remote"
source-uri="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
it('takes precedence over `source` prop', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
src="https://reactnative.dev/img/tiny_logo.png"
source={{uri: 'https://reactnative.dev/img/medium_logo.png'}}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-scale="1"
source-type="remote"
source-uri="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
});
describe('srcSet', () => {
it('can be set to a list of remote images', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
srcSet={
'https://reactnative.dev/img/tiny_logo.png 1x, https://reactnative.dev/img/header_logo.svg 2x'
}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-1x-scale="1"
source-1x-type="remote"
source-1x-uri="https://reactnative.dev/img/tiny_logo.png"
source-2x-scale="2"
source-2x-type="remote"
source-2x-uri="https://reactnative.dev/img/header_logo.svg"
/>,
);
});
it('defaults to `1x` descriptor', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
srcSet={
'https://reactnative.dev/img/tiny_logo.png, https://reactnative.dev/img/header_logo.svg 2x'
}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-1x-scale="1"
source-1x-type="remote"
source-1x-uri="https://reactnative.dev/img/tiny_logo.png"
source-2x-scale="2"
source-2x-type="remote"
source-2x-uri="https://reactnative.dev/img/header_logo.svg"
/>,
);
});
it('uses `src` for `1x` descriptor when provided', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
srcSet={
'https://reactnative.dev/img/header_logo.svg 2x, https://reactnative.dev/img/large_logo.svg 3x'
}
src="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-1x-scale="1"
source-1x-type="remote"
source-1x-uri="https://reactnative.dev/img/tiny_logo.png"
source-2x-scale="2"
source-2x-type="remote"
source-2x-uri="https://reactnative.dev/img/header_logo.svg"
source-3x-type="remote"
source-3x-uri="https://reactnative.dev/img/large_logo.svg"
/>,
);
});
});
describe('style', () => {
it('can be set', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<Image
style={{
width: 100,
height: 100,
resizeMode: 'contain',
}}
source={LOGO_SOURCE}
/>,
);
});
expect(root.getRenderedOutput().toJSX()).toEqual(
<rn-image
height="100.000000"
overflow="hidden"
resizeMode="contain"
width="100.000000"
source-scale="1"
source-type="remote"
source-uri="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
});
describe('testID', () => {
it('can be set', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image testID="test" source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['testID']}).toJSX()).toEqual(
<rn-image testID="test" />,
);
});
});
describe('tintColor', () => {
it('can be set', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<Image tintColor="red" source={LOGO_SOURCE} />);
});
expect(root.getRenderedOutput({props: ['tintColor']}).toJSX()).toEqual(
<rn-image tintColor="rgba(255, 0, 0, 1)" />,
);
});
});
});
describe('ref', () => {
@@ -1,128 +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.
*
* @flow strict-local
* @format
*/
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
import type {HostInstance} from 'react-native';
import * as Fantom from '@react-native/fantom';
import * as React from 'react';
import {createRef} from 'react';
import {ImageBackground} from 'react-native';
import ensureInstance from 'react-native/src/private/__tests__/utilities/ensureInstance';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
describe('<ImageBackground>', () => {
describe('props', () => {
describe('ImageProps', () => {
it('can have local source', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<ImageBackground source={require('./img/img1.png')} />);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-scale="1"
source-size="{1, 1}"
source-type="local"
source-uri="file://drawable-mdpi/packages_reactnative_libraries_image___tests___img_img1.png"
/>,
);
});
it('can have remote source', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<ImageBackground
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
width: 100,
height: 100,
scale: 2,
cache: 'only-if-cached',
method: 'POST',
body: 'name=React+Native',
headers: {
Authorization: 'Basic RandomString',
},
}}
/>,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-body="name=React+Native"
source-cache="only-if-cached"
source-header-Authorization="Basic RandomString"
source-method="POST"
source-scale="2"
source-size="{100, 100}"
source-type="remote"
source-uri="https://reactnative.dev/img/tiny_logo.png"
/>,
);
});
it('can have srcSet', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(
<ImageBackground srcSet="https://reactnative.dev/img/tiny_logo.png 1x, https://reactnative.dev/img/header_logo.svg 2x" />,
);
});
expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual(
<rn-image
source-1x-scale="1"
source-1x-type="remote"
source-1x-uri="https://reactnative.dev/img/tiny_logo.png"
source-2x-scale="2"
source-2x-type="remote"
source-2x-uri="https://reactnative.dev/img/header_logo.svg"
/>,
);
});
});
describe('style', () => {
it('can be set', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<ImageBackground style={{width: 100, height: 100}} />);
});
expect(
root.getRenderedOutput({props: ['width|height']}).toJSX(),
).toEqual(<rn-image width="100.000000" height="100.000000" />);
});
});
});
describe('ref', () => {
it('Allows to set a reference to the inner `Image` component', () => {
const elementRef = createRef<HostInstance>();
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<ImageBackground imageRef={elementRef} />);
});
const image = ensureInstance(elementRef.current, ReactNativeElement);
expect(image.tagName).toBe('RN:Image');
});
});
});
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow
* @generated SignedSource<<cf323fc5ca893bab5669c7d321660412>>
* @generated SignedSource<<16b364e89f43b8a47832b0dfb98af11e>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<908f5fb85384725318e261f40e49d9a6>>
* @generated SignedSource<<1dd9e9c3f20e37ae14e485fc6ee3d9e9>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow
* @generated SignedSource<<8f46fdc9267fcc4fdc9e76842fe24066>>
* @generated SignedSource<<e2c46705ed927302dbe9332dafba459d>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<83073425aa3f71ced2c8c51f25a25938>>
* @generated SignedSource<<e8dce0e82b831c91465d04b49fb48ab2>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
@@ -7,7 +7,9 @@
* @noformat
* @nolint
* @flow strict-local
* @generated SignedSource<<52163887de05f1cff05388145cf85b3b>>
* @generated SignedSource<<556d1487de0b9e4a09cbc67dd130a884>>
*
* This file was sync'd from the facebook/react repository.
*/
'use strict';
+242 -516
View File
@@ -14,7 +14,6 @@ import type {GestureResponderEvent} from '../Types/CoreEventTypes';
import type {NativeTextProps} from './TextNativeComponent';
import type {PressRetentionOffset, TextProps} from './TextProps';
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
import * as PressabilityDebug from '../Pressability/PressabilityDebug';
import usePressability from '../Pressability/usePressability';
import flattenStyle from '../StyleSheet/flattenStyle';
@@ -36,495 +35,156 @@ type TextForwardRef = React.ElementRef<
*
* @see https://reactnative.dev/docs/text
*/
let _TextImpl;
if (ReactNativeFeatureFlags.reduceDefaultPropsInText()) {
const TextImplNoDefaultProps: component(
ref?: React.RefSetter<TextForwardRef>,
...props: TextProps
) = ({
ref: forwardedRef,
accessible,
accessibilityLabel,
accessibilityState,
allowFontScaling,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-label': ariaLabel,
'aria-selected': ariaSelected,
children,
ellipsizeMode,
disabled,
id,
nativeID,
numberOfLines,
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
selectable,
selectionColor,
suppressHighlighting,
style,
...restProps
}: {
ref?: React.RefSetter<TextForwardRef>,
...TextProps,
}) => {
const processedProps = restProps as {
...NativeTextProps,
};
const _accessibilityLabel = ariaLabel ?? accessibilityLabel;
let _accessibilityState: ?TextProps['accessibilityState'] =
accessibilityState;
if (
ariaBusy != null ||
ariaChecked != null ||
ariaDisabled != null ||
ariaExpanded != null ||
ariaSelected != null
) {
if (_accessibilityState != null) {
_accessibilityState = {
busy: ariaBusy ?? _accessibilityState.busy,
checked: ariaChecked ?? _accessibilityState.checked,
disabled: ariaDisabled ?? _accessibilityState.disabled,
expanded: ariaExpanded ?? _accessibilityState.expanded,
selected: ariaSelected ?? _accessibilityState.selected,
};
} else {
_accessibilityState = {
busy: ariaBusy,
checked: ariaChecked,
disabled: ariaDisabled,
expanded: ariaExpanded,
selected: ariaSelected,
};
}
}
const TextImpl: component(
ref?: React.RefSetter<TextForwardRef>,
...props: TextProps
) = ({
ref: forwardedRef,
accessible,
accessibilityLabel,
accessibilityState,
allowFontScaling,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-label': ariaLabel,
'aria-selected': ariaSelected,
children,
ellipsizeMode,
disabled,
id,
nativeID,
numberOfLines,
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
selectable,
selectionColor,
suppressHighlighting,
style,
...restProps
}: {
ref?: React.RefSetter<TextForwardRef>,
...TextProps,
}) => {
const _accessibilityLabel = ariaLabel ?? accessibilityLabel;
const _accessibilityStateDisabled = _accessibilityState?.disabled;
const _disabled = disabled ?? _accessibilityStateDisabled;
// If the disabled prop and accessibilityState.disabled are out of sync but not both in
// falsy states we need to update the accessibilityState object to use the disabled prop.
if (
_accessibilityState != null &&
_disabled !== _accessibilityStateDisabled &&
((_disabled != null && _disabled !== false) ||
(_accessibilityStateDisabled != null &&
_accessibilityStateDisabled !== false))
) {
_accessibilityState.disabled = _disabled;
}
const _accessible = Platform.select({
ios: accessible !== false,
android:
accessible == null
? onPress != null || onLongPress != null
: accessible,
default: accessible,
});
const isPressable =
(onPress != null ||
onLongPress != null ||
onStartShouldSetResponder != null) &&
_disabled !== true;
// TODO: Move this processing to the view configuration.
const _selectionColor =
selectionColor != null ? processColor(selectionColor) : undefined;
let _style = style;
if (__DEV__) {
if (PressabilityDebug.isEnabled() && onPress != null) {
_style = [style, {color: 'magenta'}];
}
}
let _numberOfLines = numberOfLines;
if (_numberOfLines != null && !(_numberOfLines >= 0)) {
if (__DEV__) {
console.error(
`'numberOfLines' in <Text> must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`,
);
}
_numberOfLines = 0;
}
let _selectable = selectable;
let processedStyle = flattenStyle<TextStyleProp>(_style);
if (processedStyle != null) {
let overrides: ?{...TextStyleInternal} = null;
if (typeof processedStyle.fontWeight === 'number') {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.fontWeight =
// $FlowFixMe[incompatible-cast]
(String(processedStyle.fontWeight): TextStyleInternal['fontWeight']);
}
if (processedStyle.userSelect != null) {
_selectable = userSelectToSelectableMap[processedStyle.userSelect];
overrides = overrides || ({}: {...TextStyleInternal});
overrides.userSelect = undefined;
}
if (processedStyle.verticalAlign != null) {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.textAlignVertical =
verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign];
overrides.verticalAlign = undefined;
}
if (overrides != null) {
// $FlowFixMe[incompatible-type]
_style = [_style, overrides];
}
}
const _nativeID = id ?? nativeID;
if (_accessibilityLabel !== undefined) {
processedProps.accessibilityLabel = _accessibilityLabel;
}
if (_accessibilityState !== undefined) {
processedProps.accessibilityState = _accessibilityState;
}
if (_nativeID !== undefined) {
processedProps.nativeID = _nativeID;
}
if (_numberOfLines !== undefined) {
processedProps.numberOfLines = _numberOfLines;
}
if (_selectable !== undefined) {
processedProps.selectable = _selectable;
}
if (_style !== undefined) {
processedProps.style = _style;
}
if (_selectionColor !== undefined) {
processedProps.selectionColor = _selectionColor;
}
let textPressabilityProps: ?TextPressabilityProps;
if (isPressable) {
textPressabilityProps = {
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
suppressHighlighting,
let _accessibilityState: ?TextProps['accessibilityState'] =
accessibilityState;
if (
ariaBusy != null ||
ariaChecked != null ||
ariaDisabled != null ||
ariaExpanded != null ||
ariaSelected != null
) {
if (_accessibilityState != null) {
_accessibilityState = {
busy: ariaBusy ?? _accessibilityState.busy,
checked: ariaChecked ?? _accessibilityState.checked,
disabled: ariaDisabled ?? _accessibilityState.disabled,
expanded: ariaExpanded ?? _accessibilityState.expanded,
selected: ariaSelected ?? _accessibilityState.selected,
};
} else {
_accessibilityState = {
busy: ariaBusy,
checked: ariaChecked,
disabled: ariaDisabled,
expanded: ariaExpanded,
selected: ariaSelected,
};
}
}
const hasTextAncestor = useContext(TextAncestorContext);
if (hasTextAncestor) {
processedProps.disabled = disabled;
processedProps.children = children;
if (isPressable) {
return (
<NativePressableVirtualText
ref={forwardedRef}
textProps={processedProps}
textPressabilityProps={textPressabilityProps ?? {}}
/>
);
}
return <NativeVirtualText {...processedProps} ref={forwardedRef} />;
const _accessibilityStateDisabled = _accessibilityState?.disabled;
const _disabled = disabled ?? _accessibilityStateDisabled;
const isPressable =
(onPress != null ||
onLongPress != null ||
onStartShouldSetResponder != null) &&
_disabled !== true;
// TODO: Move this processing to the view configuration.
const _selectionColor =
selectionColor != null ? processColor(selectionColor) : undefined;
let _style = style;
if (__DEV__) {
if (PressabilityDebug.isEnabled() && onPress != null) {
_style = [style, {color: 'magenta'}];
}
}
let nativeText = null;
processedProps.accessible = _accessible;
processedProps.allowFontScaling = allowFontScaling !== false;
processedProps.disabled = _disabled;
processedProps.ellipsizeMode = ellipsizeMode ?? 'tail';
processedProps.children = children;
if (isPressable) {
nativeText = (
<NativePressableText
ref={forwardedRef}
textProps={processedProps}
textPressabilityProps={textPressabilityProps ?? {}}
/>
);
} else {
nativeText = <NativeText {...processedProps} ref={forwardedRef} />;
}
if (children == null) {
return nativeText;
}
// If the children do not contain a JSX element it would not be possible to have a
// nested `Text` component so we can skip adding the `TextAncestorContext` context wrapper
// which has a performance overhead. Since we do this for performance reasons we need
// to keep the check simple to avoid regressing overall perf. For this reason the
// `children.length` constant is set to `3`, this should be a reasonable tradeoff
// to capture the majority of `Text` uses but also not make this check too expensive.
if (Array.isArray(children) && children.length <= 3) {
let hasNonTextChild = false;
for (let child of children) {
if (child != null && typeof child === 'object') {
hasNonTextChild = true;
break;
}
}
if (!hasNonTextChild) {
return nativeText;
}
} else if (typeof children !== 'object') {
return nativeText;
}
return <TextAncestorContext value={true}>{nativeText}</TextAncestorContext>;
};
_TextImpl = TextImplNoDefaultProps;
} else {
const TextImplLegacy: component(
ref?: React.RefSetter<TextForwardRef>,
...props: TextProps
) = ({
ref: forwardedRef,
accessible,
accessibilityLabel,
accessibilityState,
allowFontScaling,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-label': ariaLabel,
'aria-selected': ariaSelected,
children,
ellipsizeMode,
disabled,
id,
nativeID,
numberOfLines,
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
selectable,
selectionColor,
suppressHighlighting,
style,
...restProps
}: {
ref?: React.RefSetter<TextForwardRef>,
...TextProps,
}) => {
const _accessibilityLabel = ariaLabel ?? accessibilityLabel;
let _accessibilityState: ?TextProps['accessibilityState'] =
accessibilityState;
if (
ariaBusy != null ||
ariaChecked != null ||
ariaDisabled != null ||
ariaExpanded != null ||
ariaSelected != null
) {
if (_accessibilityState != null) {
_accessibilityState = {
busy: ariaBusy ?? _accessibilityState.busy,
checked: ariaChecked ?? _accessibilityState.checked,
disabled: ariaDisabled ?? _accessibilityState.disabled,
expanded: ariaExpanded ?? _accessibilityState.expanded,
selected: ariaSelected ?? _accessibilityState.selected,
};
} else {
_accessibilityState = {
busy: ariaBusy,
checked: ariaChecked,
disabled: ariaDisabled,
expanded: ariaExpanded,
selected: ariaSelected,
};
}
}
const _accessibilityStateDisabled = _accessibilityState?.disabled;
const _disabled = disabled ?? _accessibilityStateDisabled;
const isPressable =
(onPress != null ||
onLongPress != null ||
onStartShouldSetResponder != null) &&
_disabled !== true;
// TODO: Move this processing to the view configuration.
const _selectionColor =
selectionColor != null ? processColor(selectionColor) : undefined;
let _style = style;
let _numberOfLines = numberOfLines;
if (_numberOfLines != null && !(_numberOfLines >= 0)) {
if (__DEV__) {
if (PressabilityDebug.isEnabled() && onPress != null) {
_style = [style, {color: 'magenta'}];
}
}
let _numberOfLines = numberOfLines;
if (_numberOfLines != null && !(_numberOfLines >= 0)) {
if (__DEV__) {
console.error(
`'numberOfLines' in <Text> must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`,
);
}
_numberOfLines = 0;
}
let _selectable = selectable;
let processedStyle = flattenStyle<TextStyleProp>(_style);
if (processedStyle != null) {
let overrides: ?{...TextStyleInternal} = null;
if (typeof processedStyle.fontWeight === 'number') {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.fontWeight =
// $FlowFixMe[incompatible-cast]
(processedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']);
}
if (processedStyle.userSelect != null) {
_selectable = userSelectToSelectableMap[processedStyle.userSelect];
overrides = overrides || ({}: {...TextStyleInternal});
overrides.userSelect = undefined;
}
if (processedStyle.verticalAlign != null) {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.textAlignVertical =
verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign];
overrides.verticalAlign = undefined;
}
if (overrides != null) {
// $FlowFixMe[incompatible-type]
_style = [_style, overrides];
}
}
const _nativeID = id ?? nativeID;
const hasTextAncestor = useContext(TextAncestorContext);
if (hasTextAncestor) {
if (isPressable) {
return (
<NativePressableVirtualText
ref={forwardedRef}
textProps={{
...restProps,
accessibilityLabel: _accessibilityLabel,
accessibilityState: _accessibilityState,
nativeID: _nativeID,
numberOfLines: _numberOfLines,
selectable: _selectable,
selectionColor: _selectionColor,
style: _style,
disabled: disabled,
children,
}}
textPressabilityProps={{
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
suppressHighlighting,
}}
/>
);
}
return (
<NativeVirtualText
{...restProps}
accessibilityLabel={_accessibilityLabel}
accessibilityState={_accessibilityState}
nativeID={_nativeID}
numberOfLines={_numberOfLines}
ref={forwardedRef}
selectable={_selectable}
selectionColor={_selectionColor}
style={_style}
disabled={disabled}>
{children}
</NativeVirtualText>
console.error(
`'numberOfLines' in <Text> must be a non-negative number, received: ${_numberOfLines}. The value will be set to 0.`,
);
}
_numberOfLines = 0;
}
// If the disabled prop and accessibilityState.disabled are out of sync but not both in
// falsy states we need to update the accessibilityState object to use the disabled prop.
if (
_disabled !== _accessibilityStateDisabled &&
((_disabled != null && _disabled !== false) ||
(_accessibilityStateDisabled != null &&
_accessibilityStateDisabled !== false))
) {
_accessibilityState = {..._accessibilityState, disabled: _disabled};
let _selectable = selectable;
let processedStyle = flattenStyle<TextStyleProp>(_style);
if (processedStyle != null) {
let overrides: ?{...TextStyleInternal} = null;
if (typeof processedStyle.fontWeight === 'number') {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.fontWeight =
// $FlowFixMe[incompatible-cast]
(processedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']);
}
const _accessible = Platform.select({
ios: accessible !== false,
android:
accessible == null
? onPress != null || onLongPress != null
: accessible,
default: accessible,
});
if (processedStyle.userSelect != null) {
_selectable = userSelectToSelectableMap[processedStyle.userSelect];
overrides = overrides || ({}: {...TextStyleInternal});
overrides.userSelect = undefined;
}
let nativeText = null;
if (processedStyle.verticalAlign != null) {
overrides = overrides || ({}: {...TextStyleInternal});
overrides.textAlignVertical =
verticalAlignToTextAlignVerticalMap[processedStyle.verticalAlign];
overrides.verticalAlign = undefined;
}
if (overrides != null) {
// $FlowFixMe[incompatible-type]
_style = [_style, overrides];
}
}
const _nativeID = id ?? nativeID;
const hasTextAncestor = useContext(TextAncestorContext);
if (hasTextAncestor) {
if (isPressable) {
nativeText = (
<NativePressableText
return (
<NativePressableVirtualText
ref={forwardedRef}
textProps={{
...restProps,
accessibilityLabel: _accessibilityLabel,
accessibilityState: _accessibilityState,
accessible: _accessible,
allowFontScaling: allowFontScaling !== false,
disabled: _disabled,
ellipsizeMode: ellipsizeMode ?? 'tail',
nativeID: _nativeID,
numberOfLines: _numberOfLines,
selectable: _selectable,
selectionColor: _selectionColor,
style: _style,
disabled: disabled,
children,
}}
textPressabilityProps={{
@@ -543,61 +203,127 @@ if (ReactNativeFeatureFlags.reduceDefaultPropsInText()) {
}}
/>
);
} else {
nativeText = (
<NativeText
{...restProps}
accessibilityLabel={_accessibilityLabel}
accessibilityState={_accessibilityState}
accessible={_accessible}
allowFontScaling={allowFontScaling !== false}
disabled={_disabled}
ellipsizeMode={ellipsizeMode ?? 'tail'}
nativeID={_nativeID}
numberOfLines={_numberOfLines}
ref={forwardedRef}
selectable={_selectable}
selectionColor={_selectionColor}
style={_style}>
{children}
</NativeText>
);
}
if (children == null) {
return (
<NativeVirtualText
{...restProps}
accessibilityLabel={_accessibilityLabel}
accessibilityState={_accessibilityState}
nativeID={_nativeID}
numberOfLines={_numberOfLines}
ref={forwardedRef}
selectable={_selectable}
selectionColor={_selectionColor}
style={_style}
disabled={disabled}>
{children}
</NativeVirtualText>
);
}
// If the disabled prop and accessibilityState.disabled are out of sync but not both in
// falsy states we need to update the accessibilityState object to use the disabled prop.
if (
_disabled !== _accessibilityStateDisabled &&
((_disabled != null && _disabled !== false) ||
(_accessibilityStateDisabled != null &&
_accessibilityStateDisabled !== false))
) {
_accessibilityState = {..._accessibilityState, disabled: _disabled};
}
const _accessible = Platform.select({
ios: accessible !== false,
android:
accessible == null ? onPress != null || onLongPress != null : accessible,
default: accessible,
});
let nativeText = null;
if (isPressable) {
nativeText = (
<NativePressableText
ref={forwardedRef}
textProps={{
...restProps,
accessibilityLabel: _accessibilityLabel,
accessibilityState: _accessibilityState,
accessible: _accessible,
allowFontScaling: allowFontScaling !== false,
disabled: _disabled,
ellipsizeMode: ellipsizeMode ?? 'tail',
nativeID: _nativeID,
numberOfLines: _numberOfLines,
selectable: _selectable,
selectionColor: _selectionColor,
style: _style,
children,
}}
textPressabilityProps={{
onLongPress,
onPress,
onPressIn,
onPressOut,
onResponderGrant,
onResponderMove,
onResponderRelease,
onResponderTerminate,
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
suppressHighlighting,
}}
/>
);
} else {
nativeText = (
<NativeText
{...restProps}
accessibilityLabel={_accessibilityLabel}
accessibilityState={_accessibilityState}
accessible={_accessible}
allowFontScaling={allowFontScaling !== false}
disabled={_disabled}
ellipsizeMode={ellipsizeMode ?? 'tail'}
nativeID={_nativeID}
numberOfLines={_numberOfLines}
ref={forwardedRef}
selectable={_selectable}
selectionColor={_selectionColor}
style={_style}>
{children}
</NativeText>
);
}
if (children == null) {
return nativeText;
}
// If the children do not contain a JSX element it would not be possible to have a
// nested `Text` component so we can skip adding the `TextAncestorContext` context wrapper
// which has a performance overhead. Since we do this for performance reasons we need
// to keep the check simple to avoid regressing overall perf. For this reason the
// `children.length` constant is set to `3`, this should be a reasonable tradeoff
// to capture the majority of `Text` uses but also not make this check too expensive.
if (Array.isArray(children) && children.length <= 3) {
let hasNonTextChild = false;
for (let child of children) {
if (child != null && typeof child === 'object') {
hasNonTextChild = true;
break;
}
}
if (!hasNonTextChild) {
return nativeText;
}
} else if (typeof children !== 'object') {
return nativeText;
}
// If the children do not contain a JSX element it would not be possible to have a
// nested `Text` component so we can skip adding the `TextAncestorContext` context wrapper
// which has a performance overhead. Since we do this for performance reasons we need
// to keep the check simple to avoid regressing overall perf. For this reason the
// `children.length` constant is set to `3`, this should be a reasonable tradeoff
// to capture the majority of `Text` uses but also not make this check too expensive.
if (Array.isArray(children) && children.length <= 3) {
let hasNonTextChild = false;
for (let child of children) {
if (child != null && typeof child === 'object') {
hasNonTextChild = true;
break;
}
}
if (!hasNonTextChild) {
return nativeText;
}
} else if (typeof children !== 'object') {
return nativeText;
}
return <TextAncestorContext value={true}>{nativeText}</TextAncestorContext>;
};
_TextImpl = TextImplLegacy;
}
const TextImpl: component(
ref?: React.RefSetter<TextForwardRef>,
...props: TextProps
) = _TextImpl;
return <TextAncestorContext value={true}>{nativeText}</TextAncestorContext>;
};
TextImpl.displayName = 'Text';
@@ -128,6 +128,7 @@ const HMRClient: HMRClientNativeInterface = {
JSON.stringify({
type: 'log',
level,
mode: global.RN$Bridgeless === true ? 'NOBRIDGE' : 'BRIDGE',
data: data.map(item =>
typeof item === 'string'
? item
@@ -96,8 +96,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
@interface RCTModalHostViewComponentView () <RCTFabricModalHostViewControllerDelegate>
@property (nonatomic, weak) UIView *accessibilityFocusedView;
@end
@implementation RCTModalHostViewComponentView {
@@ -150,7 +148,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
{
BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window;
if (shouldBePresented) {
[self saveAccessibilityFocusedView];
self.viewController.presentationController.delegate = self;
_isPresented = YES;
@@ -182,8 +179,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
if (eventEmitter) {
eventEmitter->onDismiss(ModalHostViewEventEmitter::OnDismiss{});
}
[self restoreAccessibilityFocusedView];
}];
}
}
@@ -212,23 +207,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
[self ensurePresentedOnlyIfNeeded];
}
- (void)saveAccessibilityFocusedView
{
id focusedElement = UIAccessibilityFocusedElement(nil);
if (focusedElement && [focusedElement isKindOfClass:[UIView class]]) {
self.accessibilityFocusedView = (UIView *)focusedElement;
}
}
- (void)restoreAccessibilityFocusedView
{
id viewToFocus = self.accessibilityFocusedView;
if (viewToFocus) {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, viewToFocus);
self.accessibilityFocusedView = nil;
}
}
#pragma mark - RCTFabricModalHostViewControllerDelegate
- (void)boundsDidChange:(CGRect)newBounds
@@ -3348,10 +3348,8 @@ public final class com/facebook/react/uimanager/DisplayMetricsHolder {
public static final fun getDisplayMetricsWritableMap (D)Lcom/facebook/react/bridge/WritableMap;
public static final fun getScreenDisplayMetrics ()Landroid/util/DisplayMetrics;
public static final fun getWindowDisplayMetrics ()Landroid/util/DisplayMetrics;
public static final fun initScreenDisplayMetrics (Landroid/content/Context;)V
public static final fun initScreenDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
public static final fun initWindowDisplayMetrics (Landroid/content/Context;)V
public static final fun initWindowDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
public static final fun initDisplayMetrics (Landroid/content/Context;)V
public static final fun initDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
public static final fun setScreenDisplayMetrics (Landroid/util/DisplayMetrics;)V
public static final fun setWindowDisplayMetrics (Landroid/util/DisplayMetrics;)V
}
@@ -630,7 +630,6 @@ dependencies {
api(libs.androidx.autofill)
api(libs.androidx.swiperefreshlayout)
api(libs.androidx.tracing)
api(libs.androidx.window)
api(libs.fbjni)
api(libs.fresco)
@@ -229,9 +229,6 @@ public class ReactInstanceManager {
return new ReactInstanceManagerBuilder();
}
/**
* @noinspection deprecation
*/
/* package */ ReactInstanceManager(
Context applicationContext,
@Nullable Activity currentActivity,
@@ -262,11 +259,7 @@ public class ReactInstanceManager {
FLog.d(TAG, "ReactInstanceManager.ctor()");
initializeSoLoaderIfNecessary(applicationContext);
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(applicationContext);
if (currentActivity != null) {
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
}
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
// See {@code ReactInstanceManagerBuilder} for description of all flags here.
mApplicationContext = applicationContext;
@@ -931,13 +924,6 @@ public class ReactInstanceManager {
ReactContext currentReactContext = getCurrentReactContext();
if (currentReactContext != null) {
DisplayMetricsHolder.initScreenDisplayMetrics(currentReactContext);
Activity currentActivity = currentReactContext.getCurrentActivity();
if (currentActivity != null) {
DisplayMetricsHolder.initWindowDisplayMetrics(currentActivity);
}
AppearanceModule appearanceModule =
currentReactContext.getNativeModule(AppearanceModule.class);
@@ -35,8 +35,7 @@ import java.util.List;
*
* @deprecated This class will be replaced by com.facebook.react.ReactHost in the New Architecture.
*/
@Deprecated(
since = "This class is part of Legacy Architecture and will be removed in a future release")
@Deprecated
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Nullsafe(Nullsafe.Mode.LOCAL)
public abstract class ReactNativeHost {
@@ -136,8 +136,9 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
setRootViewTag(ReactRootViewTagGenerator.getNextRootViewTag());
setClipChildren(false);
DisplayMetricsHolder.initScreenDisplayMetrics(getContext());
DisplayMetricsHolder.initWindowDisplayMetrics(getContext());
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
}
}
@Override
@@ -882,8 +883,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
private int mDeviceRotation = 0;
/* package */ CustomGlobalLayoutListener() {
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(getContext());
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(getContext());
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
mVisibleViewArea = new Rect();
mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
}
@@ -1006,8 +1006,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
return;
}
mDeviceRotation = rotation;
DisplayMetricsHolder.initScreenDisplayMetrics(getContext());
DisplayMetricsHolder.initWindowDisplayMetrics(getContext());
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
emitOrientationChanged(rotation);
}
@@ -7,13 +7,13 @@
package com.facebook.react.bridge
import com.facebook.react.common.annotations.internal.InteropLegacyArchitecture
import com.facebook.react.common.annotations.internal.LegacyArchitecture
/**
* This interface includes the methods needed to use a running JS instance, without specifying any
* of the bridge-specific initialization or lifecycle management.
*/
@InteropLegacyArchitecture
@LegacyArchitecture
public interface JSInstance {
public fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface)
@@ -25,11 +25,6 @@ import com.facebook.react.packagerconnection.RequestHandler
*/
internal class DefaultDevSupportManagerFactory : DevSupportManagerFactory {
@Deprecated(
"Use the other create() method with useDevSupport parameter for New Architecture. This method will be removed in a future release.",
replaceWith =
ReplaceWith(
"create(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, redBoxHandler, devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, surfaceDelegateFactory, devLoadingViewManager, pausedInDebuggerOverlayManager)"))
override fun create(
applicationContext: Context,
reactInstanceManagerHelper: ReactInstanceDevHelper,
@@ -22,12 +22,6 @@ public interface DevSupportManagerFactory {
* Factory used by the Old Architecture flow to create a [DevSupportManager] and a
* [BridgeDevSupportManager]
*/
@Deprecated(
message =
"Use the other create() method with useDevSupport parameter for New Architecture. This method will be removed in a future release.",
replaceWith =
ReplaceWith(
"create(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, redBoxHandler, devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, surfaceDelegateFactory, devLoadingViewManager, pausedInDebuggerOverlayManager)"))
public fun create(
applicationContext: Context,
reactInstanceManagerHelper: ReactInstanceDevHelper,
@@ -15,8 +15,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.DisplayMetricsHolder.getDisplayMetricsWritableMap
import com.facebook.react.uimanager.DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized
import com.facebook.react.uimanager.DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized
import com.facebook.react.uimanager.DisplayMetricsHolder.initDisplayMetricsIfNotInitialized
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
/** Module that exposes Android Constants to JS. */
@@ -27,8 +26,7 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
private var previousDisplayMetrics: ReadableMap? = null
init {
initScreenDisplayMetricsIfNotInitialized(reactContext)
reactContext.currentActivity?.let { initWindowDisplayMetricsIfNotInitialized(it) }
initDisplayMetricsIfNotInitialized(reactContext)
reactContext.addLifecycleEventListener(this)
}
@@ -8,7 +8,6 @@
package com.facebook.react.modules.network
import com.facebook.proguard.annotations.DoNotStripAny
import com.facebook.soloader.SoLoader
/**
* [Experimental] An interface for reporting network events to the modern debugger server and Web
@@ -20,10 +19,6 @@ import com.facebook.soloader.SoLoader
*/
@DoNotStripAny
internal object InspectorNetworkReporter {
init {
SoLoader.loadLibrary("react_devsupportjni")
}
@JvmStatic external fun isDebuggingEnabled(): Boolean
/**
@@ -625,8 +625,9 @@ public class ReactHostImpl(
override fun onConfigurationChanged(context: Context) {
val currentReactContext = this.currentReactContext
if (currentReactContext != null) {
DisplayMetricsHolder.initScreenDisplayMetrics(currentReactContext)
currentReactContext.currentActivity?.let { DisplayMetricsHolder.initWindowDisplayMetrics(it) }
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
DisplayMetricsHolder.initDisplayMetrics(currentReactContext)
}
val appearanceModule = currentReactContext.getNativeModule(AppearanceModule::class.java)
appearanceModule?.onConfigurationChanged(context)
@@ -917,7 +918,6 @@ public class ReactHostImpl(
val instance =
ReactInstance(
reactContext,
currentActivity,
reactHostDelegate,
componentFactory,
devSupportManager,
@@ -7,7 +7,6 @@
package com.facebook.react.runtime
import android.app.Activity
import android.content.res.AssetManager
import android.view.View
import com.facebook.common.logging.FLog
@@ -89,7 +88,6 @@ import kotlin.jvm.JvmStatic
@UnstableReactNativeAPI
internal class ReactInstance(
private val context: BridgelessReactContext,
private val activity: Activity?,
delegate: ReactHostDelegate,
componentFactory: ComponentFactory,
devSupportManager: DevSupportManager,
@@ -242,8 +240,7 @@ internal class ReactInstance(
FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)
// Misc initialization that needs to be done before Fabric init
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(context)
activity?.let { DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(it) }
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
val binding = FabricUIManagerBinding()
binding.register(
@@ -13,20 +13,16 @@ import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.window.layout.WindowMetricsCalculator
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
/**
* Holds an instance of the current DisplayMetrics so we don't have to thread it through all the
* classes that need it.
*/
public object DisplayMetricsHolder {
private const val SCREEN_INITIALIZATION_MISSING_MESSAGE =
"DisplayMetricsHolder must be initialized with initScreenDisplayMetricsIfNotInitialized or initScreenDisplayMetrics"
private const val WINDOW_INITIALIZATION_MISSING_MESSAGE =
"DisplayMetricsHolder must be initialized with initWindowDisplayMetricsIfNotInitialized or initWindowDisplayMetrics"
private const val INITIALIZATION_MISSING_MESSAGE =
"DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or initDisplayMetrics"
@JvmStatic private var windowDisplayMetrics: DisplayMetrics? = null
@JvmStatic private var screenDisplayMetrics: DisplayMetrics? = null
@@ -34,7 +30,7 @@ public object DisplayMetricsHolder {
/** The metrics of the window associated to the Context used to initialize ReactNative */
@JvmStatic
public fun getWindowDisplayMetrics(): DisplayMetrics {
checkNotNull(windowDisplayMetrics) { WINDOW_INITIALIZATION_MISSING_MESSAGE }
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
return windowDisplayMetrics as DisplayMetrics
}
@@ -46,7 +42,7 @@ public object DisplayMetricsHolder {
/** Screen metrics returns the metrics of the default screen on the device. */
@JvmStatic
public fun getScreenDisplayMetrics(): DisplayMetrics {
checkNotNull(screenDisplayMetrics) { SCREEN_INITIALIZATION_MISSING_MESSAGE }
checkNotNull(screenDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
return screenDisplayMetrics as DisplayMetrics
}
@@ -56,58 +52,33 @@ public object DisplayMetricsHolder {
}
@JvmStatic
public fun initScreenDisplayMetricsIfNotInitialized(context: Context) {
if (screenDisplayMetrics == null) {
initScreenDisplayMetrics(context)
public fun initDisplayMetricsIfNotInitialized(context: Context) {
if (screenDisplayMetrics != null) {
return
}
initDisplayMetrics(context)
}
@JvmStatic
public fun initWindowDisplayMetricsIfNotInitialized(context: Context) {
if (windowDisplayMetrics == null) {
initWindowDisplayMetrics(context)
}
}
@JvmStatic
public fun initScreenDisplayMetrics(context: Context) {
val displayMetrics = DisplayMetrics()
displayMetrics.setTo(context.resources.displayMetrics)
public fun initDisplayMetrics(context: Context) {
val displayMetrics = context.resources.displayMetrics
windowDisplayMetrics = displayMetrics
val screenDisplayMetrics = DisplayMetrics()
screenDisplayMetrics.setTo(displayMetrics)
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
// Get the real display metrics if we are using API level 17 or higher.
// The real metrics include system decor elements (e.g. soft menu bar).
//
// See:
// http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(displayMetrics)
screenDisplayMetrics = displayMetrics
}
/*
* NOTE: Unlike [initScreenDisplayMetrics], this method needs a UiContext (Activity of
* InputMethodService) else WindowMetircsCalculator will throw an exception.
*/
@JvmStatic
public fun initWindowDisplayMetrics(context: Context) {
val displayMetrics = DisplayMetrics()
displayMetrics.setTo(context.resources.displayMetrics)
if (isEdgeToEdgeFeatureFlagOn) {
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context).let { windowMetrics
->
displayMetrics.widthPixels = windowMetrics.bounds.width()
displayMetrics.heightPixels = windowMetrics.bounds.height()
}
}
windowDisplayMetrics = displayMetrics
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(screenDisplayMetrics)
DisplayMetricsHolder.screenDisplayMetrics = screenDisplayMetrics
}
@JvmStatic
public fun getDisplayMetricsWritableMap(fontScale: Double): WritableMap {
checkNotNull(windowDisplayMetrics) { WINDOW_INITIALIZATION_MISSING_MESSAGE }
checkNotNull(screenDisplayMetrics) { SCREEN_INITIALIZATION_MISSING_MESSAGE }
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
checkNotNull(screenDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
return WritableNativeMap().apply {
putMap(
@@ -20,7 +20,7 @@ public object PixelUtil {
}
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, value, DisplayMetricsHolder.getScreenDisplayMetrics())
TypedValue.COMPLEX_UNIT_DIP, value, DisplayMetricsHolder.getWindowDisplayMetrics())
}
/** Convert from DIP to PX */
@@ -37,7 +37,7 @@ public object PixelUtil {
return Float.NaN
}
val displayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics()
val displayMetrics = DisplayMetricsHolder.getWindowDisplayMetrics()
val scaledValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, displayMetrics)
if (maxFontScale >= 1) {
@@ -60,13 +60,13 @@ public object PixelUtil {
return Float.NaN
}
return value / DisplayMetricsHolder.getScreenDisplayMetrics().density
return value / DisplayMetricsHolder.getWindowDisplayMetrics().density
}
/** @return [Float] that represents the density of the display metrics for device screen. */
@JvmStatic
public fun getDisplayMetricDensity(): Float =
DisplayMetricsHolder.getScreenDisplayMetrics().density
DisplayMetricsHolder.getWindowDisplayMetrics().density
/* Kotlin extensions */
public fun Int.dpToPx(): Float = toPixelFromDIP(this.toFloat())
@@ -12,7 +12,6 @@ import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_M
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import static com.facebook.react.uimanager.common.UIManagerType.LEGACY;
import android.app.Activity;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.view.View;
@@ -127,11 +126,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
ViewManagerResolver viewManagerResolver,
int minTimeLeftInFrameForNonBatchedOperationMs) {
super(reactContext);
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext);
Activity currentActivity = reactContext.getCurrentActivity();
if (currentActivity != null) {
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
}
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mEventDispatcher = new EventDispatcherImpl(reactContext);
mModuleConstants = createConstants(viewManagerResolver);
mCustomDirectEvents = UIManagerModuleConstants.directEventTypeConstants;
@@ -151,11 +146,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
List<ViewManager> viewManagersList,
int minTimeLeftInFrameForNonBatchedOperationMs) {
super(reactContext);
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext);
Activity currentActivity = reactContext.getCurrentActivity();
if (currentActivity != null) {
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
}
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mEventDispatcher = new EventDispatcherImpl(reactContext);
mCustomDirectEvents = MapBuilder.newHashMap();
mModuleConstants = createConstants(viewManagersList, null, mCustomDirectEvents);
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -29,9 +27,6 @@ import com.facebook.react.uimanager.IllegalViewOperationException
* order to animate layout when a valid configuration has been supplied by the application.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal abstract class AbstractLayoutAnimation {
var interpolator: Interpolator? = null
var delayMs: Int = 0
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -17,9 +15,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
* creation.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal enum class AnimatedPropertyType {
OPACITY,
SCALE_X,
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -19,9 +17,6 @@ import com.facebook.react.uimanager.IllegalViewOperationException
/** Class responsible for default layout animation, i.e animation of view creation and deletion. */
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal abstract class BaseLayoutAnimation : AbstractLayoutAnimation() {
abstract fun isReverse(): Boolean
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -16,9 +14,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
* Enum representing the different interpolators that can be used in layout animation configuration.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal enum class InterpolatorType {
LINEAR,
EASE_IN,
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.util.SparseArray
@@ -31,9 +29,6 @@ import javax.annotation.concurrent.NotThreadSafe
*/
@NotThreadSafe
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
public open class LayoutAnimationController {
private val layoutCreateAnimation: AbstractLayoutAnimation = LayoutCreateAnimation()
private val layoutUpdateAnimation: AbstractLayoutAnimation = LayoutUpdateAnimation()
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -14,9 +12,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
/** Listener invoked when a layout animation has completed. */
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
public fun interface LayoutAnimationListener {
public fun onAnimationEnd()
}
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -17,9 +15,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
* Enum representing the different animation type that can be specified in layout animation config.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal enum class LayoutAnimationType {
CREATE,
UPDATE,
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -18,9 +16,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
* config was supplied for the layout animation of DELETE type.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal class LayoutDeleteAnimation : BaseLayoutAnimation() {
override fun isReverse(): Boolean = true
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import com.facebook.react.common.annotations.internal.LegacyArchitecture
@@ -14,9 +12,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
/** Interface for an animation type that takes care of updating the view layout. */
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal interface LayoutHandlingAnimation {
/**
* Notifies the animation of a layout update in case one occurs during the animation. This avoids
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -21,9 +19,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
* was supplied for the layout animation of UPDATE type.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal class LayoutUpdateAnimation : AbstractLayoutAnimation() {
override fun isValid(): Boolean = durationMs > 0
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -23,9 +21,6 @@ import java.lang.ref.WeakReference
* optimize rendering performances.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal class OpacityAnimation(view: View, private val startOpacity: Float, endOpacity: Float) :
Animation() {
private val viewRef = WeakReference(view)
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -24,9 +22,6 @@ import java.lang.ref.WeakReference
* ScaleAnimation and TranslateAnimation.
*/
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal class PositionAndSizeAnimation(view: View, x: Int, y: Int, width: Int, height: Int) :
Animation(), LayoutHandlingAnimation {
private val viewRef = WeakReference(view)
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.animation.Interpolator
@@ -21,9 +19,6 @@ import kotlin.math.sin
/** Simple spring interpolator */
// TODO(7613736): Improve spring interpolator with friction and damping variable support
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
@Deprecated(
message = "This class is part of Legacy Architecture and will be removed in a future release",
level = DeprecationLevel.WARNING)
internal class SimpleSpringInterpolator @JvmOverloads constructor(springDamping: Float = FACTOR) :
Interpolator {
private val _springDamping: Float = springDamping
@@ -50,32 +50,28 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean {
internal class VirtualViewContainerState {
private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
private val detectWindowFocus = ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()
private val virtualViews: MutableSet<VirtualView> = mutableSetOf()
private val emptyRect: Rect = Rect()
private val visibleRect: Rect = Rect()
private val prerenderRect: Rect = Rect()
private val onWindowFocusChangeListener =
if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) {
ViewTreeObserver.OnWindowFocusChangeListener {
debugLog("onWindowFocusChanged")
updateModes()
}
} else {
null
ViewTreeObserver.OnWindowFocusChangeListener {
debugLog("onWindowFocusChanged")
updateModes()
}
private val scrollView: ViewGroup
constructor(scrollView: ViewGroup) {
this.scrollView = scrollView
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
scrollView.viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
public fun cleanup() {
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
scrollView.viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
@@ -119,7 +115,7 @@ internal class VirtualViewContainerState {
rect.isEmpty -> {}
rectsOverlap(rect, visibleRect) -> {
thresholdRect = visibleRect
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
if (scrollView.hasWindowFocus()) {
mode = VirtualViewMode.Visible
} else {
@@ -40,14 +40,11 @@ public class ReactVirtualView(context: Context) :
internal var modeChangeEmitter: VirtualViewModeChangeEmitter? = null
internal var prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
internal val debugLogEnabled: Boolean = ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()
internal val detectWindowFocus = ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()
private val onWindowFocusChangeListener =
if (ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()) {
ViewTreeObserver.OnWindowFocusChangeListener {
dispatchOnModeChangeIfNeeded(checkRectChange = false)
}
} else {
null
ViewTreeObserver.OnWindowFocusChangeListener {
dispatchOnModeChangeIfNeeded(checkRectChange = false)
}
private var parentScrollView: View? = null
@@ -93,7 +90,7 @@ public class ReactVirtualView(context: Context) :
ReactScrollViewHelper.addLayoutChangeListener(this)
}
debugLog("onAttachedToWindow")
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
dispatchOnModeChangeIfNeeded(checkRectChange = false)
@@ -103,7 +100,7 @@ public class ReactVirtualView(context: Context) :
super.onDetachedFromWindow()
ReactScrollViewHelper.removeScrollListener(this)
ReactScrollViewHelper.removeLayoutChangeListener(this)
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
cleanupLayoutListeners()
@@ -205,7 +202,7 @@ public class ReactVirtualView(context: Context) :
val newMode: VirtualViewMode
if (rectsOverlap(targetRect, thresholdRect)) {
if (onWindowFocusChangeListener != null) {
if (detectWindowFocus) {
if (hasWindowFocus()) {
newMode = VirtualViewMode.Visible
} else {
@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga;
public class YogaConstants {
public static final float UNDEFINED = Float.NaN;
public static boolean isUndefined(float value) {
return Float.compare(value, UNDEFINED) == 0;
}
public static boolean isUndefined(YogaValue value) {
return value.unit == YogaUnit.UNDEFINED;
}
public static float getUndefined() {
return UNDEFINED;
}
}
@@ -1,18 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga
public object YogaConstants {
@JvmField public val UNDEFINED: Float = Float.NaN
@JvmStatic public fun isUndefined(value: Float): Boolean = value.compareTo(UNDEFINED) == 0
@JvmStatic public fun isUndefined(value: YogaValue): Boolean = value.unit == YogaUnit.UNDEFINED
@JvmStatic public fun getUndefined(): Float = UNDEFINED
}
@@ -29,6 +29,9 @@ if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)
# Make sure every shared lib includes a .note.gnu.build-id header
add_link_options(-Wl,--build-id)
function(add_react_android_subdir relative_path)
add_subdirectory(${REACT_ANDROID_DIR}/${relative_path} ReactAndroid/${relative_path})
endfunction()
@@ -19,7 +19,6 @@ target_include_directories(react_devsupportjni PUBLIC .)
target_link_libraries(react_devsupportjni
fbjni
jsinspector
jsinspector_network)
jsinspector)
target_compile_reactnative_options(react_devsupportjni PRIVATE)
@@ -8,7 +8,6 @@
#include "JCxxInspectorPackagerConnection.h"
#include "JCxxInspectorPackagerConnectionWebSocketDelegate.h"
#include "JInspectorFlags.h"
#include "JInspectorNetworkReporter.h"
#include <fbjni/fbjni.h>
@@ -19,7 +18,5 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*unused*/) {
facebook::react::jsinspector_modern::
JCxxInspectorPackagerConnectionWebSocketDelegate::registerNatives();
facebook::react::jsinspector_modern::JInspectorFlags::registerNatives();
facebook::react::jsinspector_modern::JInspectorNetworkReporter::
registerNatives();
});
}
@@ -53,6 +53,7 @@ add_library(
OBJECT
CatalystInstanceImpl.cpp
InspectorNetworkRequestListener.cpp
InspectorNetworkReporter.cpp
JExecutor.cpp
JInspector.cpp
JMessageQueueThread.cpp
@@ -81,8 +82,10 @@ target_link_libraries(reactnativejni
fbjni
folly_runtime
glog_init
jsinspector_network
logger
react_cxxreact
react_featureflags
react_renderer_runtimescheduler
reactnativejni_common
runtimeexecutor
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
#include "JInspectorNetworkReporter.h"
#include "InspectorNetworkReporter.h"
#include <jsinspector-modern/network/NetworkReporter.h>
@@ -16,8 +16,9 @@
#endif
using namespace facebook::jni;
using namespace facebook::react::jsinspector_modern;
namespace facebook::react::jsinspector_modern {
namespace facebook::react {
namespace {
@@ -58,12 +59,12 @@ static std::unordered_map<int, std::string> responseBuffers;
#endif
/* static */ jboolean JInspectorNetworkReporter::isDebuggingEnabled(
/* static */ jboolean InspectorNetworkReporter::isDebuggingEnabled(
jni::alias_ref<jclass> /*unused*/) {
return NetworkReporter::getInstance().isDebuggingEnabled();
}
/* static */ void JInspectorNetworkReporter::reportRequestStart(
/* static */ void InspectorNetworkReporter::reportRequestStart(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jni::alias_ref<jstring> requestUrl,
@@ -81,7 +82,7 @@ static std::unordered_map<int, std::string> responseBuffers;
std::to_string(requestId), requestInfo, encodedDataLength, std::nullopt);
}
/* static */ void JInspectorNetworkReporter::reportConnectionTiming(
/* static */ void InspectorNetworkReporter::reportConnectionTiming(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jni::alias_ref<jni::JMap<jstring, jstring>> headers) {
@@ -89,7 +90,7 @@ static std::unordered_map<int, std::string> responseBuffers;
std::to_string(requestId), convertJavaMapToHeaders(headers));
}
/* static */ void JInspectorNetworkReporter::reportResponseStart(
/* static */ void InspectorNetworkReporter::reportResponseStart(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jni::alias_ref<jstring> requestUrl,
@@ -107,7 +108,7 @@ static std::unordered_map<int, std::string> responseBuffers;
static_cast<std::int64_t>(encodedDataLength));
}
/* static */ void JInspectorNetworkReporter::reportDataReceivedImpl(
/* static */ void InspectorNetworkReporter::reportDataReceivedImpl(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jint dataLength) {
@@ -115,7 +116,7 @@ static std::unordered_map<int, std::string> responseBuffers;
std::to_string(requestId), dataLength, std::nullopt);
}
/* static */ void JInspectorNetworkReporter::reportResponseEnd(
/* static */ void InspectorNetworkReporter::reportResponseEnd(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jlong encodedDataLength) {
@@ -133,7 +134,7 @@ static std::unordered_map<int, std::string> responseBuffers;
#endif
}
/* static */ void JInspectorNetworkReporter::reportRequestFailed(
/* static */ void InspectorNetworkReporter::reportRequestFailed(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jboolean cancelled) {
@@ -141,7 +142,7 @@ static std::unordered_map<int, std::string> responseBuffers;
std::to_string(requestId), cancelled);
}
/* static */ void JInspectorNetworkReporter::maybeStoreResponseBodyImpl(
/* static */ void InspectorNetworkReporter::maybeStoreResponseBodyImpl(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jni::alias_ref<jstring> body,
@@ -159,7 +160,7 @@ static std::unordered_map<int, std::string> responseBuffers;
}
/* static */ void
JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl(
InspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl(
jni::alias_ref<jclass> /*unused*/,
jint requestId,
jni::alias_ref<jstring> data) {
@@ -175,33 +176,31 @@ JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl(
#endif
}
/* static */ void JInspectorNetworkReporter::registerNatives() {
/* static */ void InspectorNetworkReporter::registerNatives() {
javaClassLocal()->registerNatives({
makeNativeMethod(
"isDebuggingEnabled", JInspectorNetworkReporter::isDebuggingEnabled),
"isDebuggingEnabled", InspectorNetworkReporter::isDebuggingEnabled),
makeNativeMethod(
"reportRequestStart", JInspectorNetworkReporter::reportRequestStart),
"reportRequestStart", InspectorNetworkReporter::reportRequestStart),
makeNativeMethod(
"reportResponseStart",
JInspectorNetworkReporter::reportResponseStart),
"reportResponseStart", InspectorNetworkReporter::reportResponseStart),
makeNativeMethod(
"reportConnectionTiming",
JInspectorNetworkReporter::reportConnectionTiming),
InspectorNetworkReporter::reportConnectionTiming),
makeNativeMethod(
"reportDataReceivedImpl",
JInspectorNetworkReporter::reportDataReceivedImpl),
InspectorNetworkReporter::reportDataReceivedImpl),
makeNativeMethod(
"reportResponseEnd", JInspectorNetworkReporter::reportResponseEnd),
"reportResponseEnd", InspectorNetworkReporter::reportResponseEnd),
makeNativeMethod(
"reportRequestFailed",
JInspectorNetworkReporter::reportRequestFailed),
"reportRequestFailed", InspectorNetworkReporter::reportRequestFailed),
makeNativeMethod(
"maybeStoreResponseBodyImpl",
JInspectorNetworkReporter::maybeStoreResponseBodyImpl),
InspectorNetworkReporter::maybeStoreResponseBodyImpl),
makeNativeMethod(
"maybeStoreResponseBodyIncrementalImpl",
JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl),
InspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl),
});
}
} // namespace facebook::react::jsinspector_modern
} // namespace facebook::react
@@ -9,10 +9,10 @@
#include <fbjni/fbjni.h>
namespace facebook::react::jsinspector_modern {
namespace facebook::react {
class JInspectorNetworkReporter
: public jni::HybridClass<JInspectorNetworkReporter> {
class InspectorNetworkReporter
: public jni::HybridClass<InspectorNetworkReporter> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/modules/network/InspectorNetworkReporter;";
@@ -70,7 +70,7 @@ class JInspectorNetworkReporter
static void registerNatives();
private:
JInspectorNetworkReporter() = delete;
InspectorNetworkReporter() = delete;
};
} // namespace facebook::react::jsinspector_modern
} // namespace facebook::react
@@ -12,6 +12,7 @@
#include "CatalystInstanceImpl.h"
#include "CxxModuleWrapperBase.h"
#include "InspectorNetworkReporter.h"
#include "InspectorNetworkRequestListener.h"
#include "JInspector.h"
#include "JavaScriptExecutorHolder.h"
@@ -44,6 +45,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JInspector::registerNatives();
ReactInstanceManagerInspectorTarget::registerNatives();
InspectorNetworkRequestListener::registerNatives();
InspectorNetworkReporter::registerNatives();
});
}
@@ -127,9 +127,6 @@ void JReactHostInspectorTarget::onSetPausedInDebuggerMessage(
}
}
void JReactHostInspectorTarget::unstable_onPerfMonitorUpdate(
const PerfMonitorUpdateRequest& /* unused */) {}
void JReactHostInspectorTarget::loadNetworkResource(
const jsinspector_modern::LoadNetworkResourceRequest& params,
jsinspector_modern::ScopedExecutor<
@@ -84,8 +84,6 @@ class JReactHostInspectorTarget
void onReload(const PageReloadRequest& request) override;
void onSetPausedInDebuggerMessage(
const OverlaySetPausedInDebuggerMessageRequest&) override;
void unstable_onPerfMonitorUpdate(
const PerfMonitorUpdateRequest& /* unused */) override;
void loadNetworkResource(
const jsinspector_modern::LoadNetworkResourceRequest& params,
jsinspector_modern::ScopedExecutor<
@@ -71,8 +71,7 @@ class RootViewTest {
reactContext = spy(BridgeReactContext(RuntimeEnvironment.getApplication()))
reactContext.initializeWithInstance(catalystInstanceMock)
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext)
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(reactContext)
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext)
val uiManagerModuleMock: UIManagerModule = mock()
whenever(catalystInstanceMock.getNativeModule(UIManagerModule::class.java))
.thenReturn(uiManagerModuleMock)
@@ -11,18 +11,12 @@
package com.facebook.react.bridge
import android.app.Application
import com.facebook.react.bridge.queue.MessageQueueThreadSpec
import com.facebook.react.bridge.queue.ReactQueueConfiguration
import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.runtime.BridgelessReactContext
import com.facebook.react.runtime.ReactHostImpl
import com.facebook.react.runtime.internal.bolts.Task
import com.facebook.react.uimanager.UIManagerModule
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.robolectric.RuntimeEnvironment
@@ -55,20 +49,4 @@ object ReactTestHelper {
whenever(reactInstance.isDestroyed).thenReturn(false)
return reactInstance
}
@OptIn(UnstableReactNativeAPI::class)
fun createTestReactApplicationContext(application: Application): ReactApplicationContext {
val reactHost =
spy(
ReactHostImpl(
RuntimeEnvironment.getApplication(),
mock(),
mock(),
Task.Companion.IMMEDIATE_EXECUTOR,
Task.Companion.IMMEDIATE_EXECUTOR,
false /* allowPackagerServerAccess */,
false /* useDevSupport */,
))
return BridgelessReactContext(application, reactHost)
}
}
@@ -467,7 +467,7 @@ class TouchEventDispatchTest {
metrics.xdpi = 1f
metrics.ydpi = 1f
metrics.density = 1f
DisplayMetricsHolder.setScreenDisplayMetrics(metrics)
DisplayMetricsHolder.setWindowDisplayMetrics(metrics)
val reactContext = ReactTestHelper.createCatalystContextForTest()
@@ -5,45 +5,36 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.modules.clipboard
import android.annotation.SuppressLint
import android.content.ClipboardManager
import android.content.Context
import com.facebook.react.bridge.ReactTestHelper.createTestReactApplicationContext
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
import com.facebook.testutils.shadows.ShadowSoLoader
import com.facebook.react.bridge.BridgeReactContext
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@Suppress("DEPRECATION")
@SuppressLint("ClipboardManager", "DeprecatedClass")
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowSoLoader::class])
class ClipboardModuleTest {
private lateinit var clipboardModule: ClipboardModule
private lateinit var clipboardManager: ClipboardManager
@Before
fun setUp() {
ReactNativeFeatureFlagsForTests.setUp()
clipboardModule =
ClipboardModule(createTestReactApplicationContext(RuntimeEnvironment.getApplication()))
clipboardModule = ClipboardModule(BridgeReactContext(RuntimeEnvironment.getApplication()))
clipboardManager =
RuntimeEnvironment.getApplication().getSystemService(Context.CLIPBOARD_SERVICE)
as ClipboardManager
}
@After
fun tearDown() {
ReactNativeFeatureFlags.dangerouslyReset()
}
@Suppress("DEPRECATION")
@Test
fun testSetString() {
clipboardModule.setString(TEST_CONTENT)
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import java.util.Locale
@@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
@file:Suppress("DEPRECATION")
package com.facebook.react.uimanager.layoutanimation
import android.view.View
@@ -25,7 +25,7 @@ class ColorStopTest {
fun setUp() {
val metrics = DisplayMetrics()
metrics.density = 1f
DisplayMetricsHolder.setScreenDisplayMetrics(metrics)
DisplayMetricsHolder.setWindowDisplayMetrics(metrics)
}
@Test
@@ -75,14 +75,14 @@ class ReactImagePropertyTest {
context.initializeWithInstance(catalystInstanceMock)
themeContext = ThemedReactContext(context, context, null, -1)
Fresco.initialize(context)
DisplayMetricsHolder.setScreenDisplayMetrics(DisplayMetrics())
DisplayMetricsHolder.setWindowDisplayMetrics(DisplayMetrics())
ReactNativeFeatureFlagsForTests.setUp()
}
@After
fun teardown() {
DisplayMetricsHolder.setScreenDisplayMetrics(null)
DisplayMetricsHolder.setWindowDisplayMetrics(null)
rnLog.close()
flogMock.close()
}
@@ -65,7 +65,7 @@ class ReactTextInputPropertyTest {
context.initializeWithInstance(catalystInstanceMock)
themedContext = ThemedReactContext(context, context.baseContext, null, ID_NULL)
manager = ReactTextInputManager()
DisplayMetricsHolder.setScreenDisplayMetrics(DisplayMetrics())
DisplayMetricsHolder.setWindowDisplayMetrics(DisplayMetrics())
view = manager.createViewInstance(themedContext)
}
@@ -47,7 +47,7 @@ class ReactVirtualViewTest {
val displayMetricsHolder = mockStatic(DisplayMetricsHolder::class.java)
displayMetricsHolder
.`when`<DisplayMetrics> { DisplayMetricsHolder.getScreenDisplayMetrics() }
.`when`<DisplayMetrics> { DisplayMetricsHolder.getWindowDisplayMetrics() }
.thenAnswer { DisplayMetrics().apply { density = 1f } }
}
@@ -47,8 +47,7 @@ class HostAgent::Impl final {
hostMetadata_(std::move(hostMetadata)),
sessionState_(sessionState),
networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))),
tracingAgent_(
TracingAgent(frontendChannel, sessionState, targetController)) {}
tracingAgent_(TracingAgent(frontendChannel, sessionState)) {}
~Impl() {
if (isPausedInDebuggerOverlayVisible_) {
@@ -311,6 +310,8 @@ class HostAgent::Impl final {
}
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> instanceAgent) {
tracingAgent_.setCurrentInstanceAgent(instanceAgent);
auto previousInstanceAgent = std::move(instanceAgent_);
instanceAgent_ = std::move(instanceAgent);
@@ -461,17 +462,4 @@ void HostAgent::setCurrentInstanceAgent(
impl_->setCurrentInstanceAgent(std::move(instanceAgent));
}
#pragma mark - Tracing
HostTracingAgent::HostTracingAgent(tracing::TraceRecordingState& state)
: tracing::TargetTracingAgent(state) {}
void HostTracingAgent::setTracedInstance(InstanceTarget* instanceTarget) {
if (instanceTarget != nullptr) {
instanceTracingAgent_ = instanceTarget->createTracingAgent(state_);
} else {
instanceTracingAgent_ = nullptr;
}
}
} // namespace facebook::react::jsinspector_modern
@@ -12,7 +12,6 @@
#include <jsinspector-modern/InspectorInterfaces.h>
#include <jsinspector-modern/InstanceAgent.h>
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/TargetTracingAgent.h>
namespace facebook::react::jsinspector_modern {
@@ -76,25 +75,4 @@ class HostAgent final {
std::unique_ptr<Impl> impl_;
};
#pragma mark - Tracing
/**
* An Agent that handles Tracing events for a particular InstanceTarget.
*
* Lifetime of this agent is bound to the lifetime of the Tracing session -
* HostTargetTraceRecording.
*/
class HostTracingAgent : tracing::TargetTracingAgent {
public:
explicit HostTracingAgent(tracing::TraceRecordingState& state);
/**
* Registers the InstanceTarget with this tracing agent.
*/
void setTracedInstance(InstanceTarget* instanceTarget);
private:
std::shared_ptr<InstanceTracingAgent> instanceTracingAgent_{nullptr};
};
} // namespace facebook::react::jsinspector_modern
@@ -7,7 +7,6 @@
#include "HostTarget.h"
#include "HostAgent.h"
#include "HostTargetTraceRecording.h"
#include "InspectorInterfaces.h"
#include "InspectorUtilities.h"
#include "InstanceTarget.h"
@@ -146,53 +145,11 @@ class HostCommandSender {
std::unique_ptr<ILocalConnection> connection_;
};
/**
* Enables the caller to install and subscribe to a named CDP runtime binding
* on the HostTarget via a callback. Note: Per CDP spec, this does not need to
* check if the `Runtime` domain is enabled.
*/
class HostRuntimeBinding {
public:
explicit HostRuntimeBinding(
HostTarget& target,
std::string name,
std::function<void(std::string)> callback)
: connection_(target.connect(std::make_unique<CallbackRemoteConnection>(
[callback = std::move(callback)](const std::string& message) {
auto parsedMessage = folly::parseJson(message);
// Ignore initial Runtime.addBinding response
if (parsedMessage["id"] == 0 &&
parsedMessage["result"].isObject() &&
parsedMessage["result"].empty()) {
return;
}
// Assert that we only intercept bindingCalled responses
assert(
parsedMessage["method"].asString() ==
"Runtime.bindingCalled");
callback(parsedMessage["params"]["payload"].asString());
}))) {
// Install runtime binding
connection_->sendMessage(cdp::jsonRequest(
0,
"Runtime.addBinding",
folly::dynamic::object("name", std::move(name))));
}
private:
std::unique_ptr<ILocalConnection> connection_;
};
std::shared_ptr<HostTarget> HostTarget::create(
HostTargetDelegate& delegate,
VoidExecutor executor) {
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
hostTarget->setExecutor(std::move(executor));
if (InspectorFlags::getInstance().getPerfMonitorV2Enabled()) {
hostTarget->installPerfMetricsBinding();
}
return hostTarget;
}
@@ -222,8 +179,6 @@ HostTarget::~HostTarget() {
assert(
sessions_.empty() &&
"HostTargetSession objects must be destroyed before their HostTarget. Did you call getInspectorInstance().removePage()?");
// Trace Recording object (traceRecording_) doesn't create an actual session,
// so we don't need to reset it explicitly here.
}
HostTargetDelegate::~HostTargetDelegate() = default;
@@ -236,13 +191,6 @@ InstanceTarget& HostTarget::registerInstance(InstanceTargetDelegate& delegate) {
[currentInstance = &*currentInstance_](HostTargetSession& session) {
session.setCurrentInstance(currentInstance);
});
if (traceRecording_) {
// Registers the Instance for tracing, if a Trace is currently being
// recorded.
traceRecording_->setTracedInstance(currentInstance_.get());
}
return *currentInstance_;
}
@@ -252,13 +200,6 @@ void HostTarget::unregisterInstance(InstanceTarget& instance) {
"Invalid unregistration");
sessions_.forEach(
[](HostTargetSession& session) { session.setCurrentInstance(nullptr); });
if (traceRecording_) {
// Unregisters the Instance for tracing, if a Trace is currently being
// recorded.
traceRecording_->setTracedInstance(nullptr);
}
currentInstance_.reset();
}
@@ -271,19 +212,6 @@ void HostTarget::sendCommand(HostCommand command) {
});
}
void HostTarget::installPerfMetricsBinding() {
perfMetricsBinding_ = std::make_unique<HostRuntimeBinding>(
*this, // Used immediately
"__chromium_devtools_metrics_reporter",
[this](const std::string& message) {
auto payload = folly::parseJson(message);
HostTargetDelegate::PerfMonitorUpdateRequest request{
.interactionName = payload["eventName"].asString(),
.durationMs = static_cast<uint16_t>(payload["duration"].asInt())};
delegate_.unstable_onPerfMonitorUpdate(request);
});
}
HostTargetController::HostTargetController(HostTarget& target)
: target_(target) {}
@@ -18,8 +18,6 @@
#include <optional>
#include <string>
#include <jsinspector-modern/tracing/TracingMode.h>
#ifndef JSINSPECTOR_EXPORT
#ifdef _MSC_VER
#ifdef CREATE_SHARED_LIBRARY
@@ -36,11 +34,8 @@ namespace facebook::react::jsinspector_modern {
class HostTargetSession;
class HostAgent;
class HostTracingAgent;
class HostCommandSender;
class HostRuntimeBinding;
class HostTarget;
class HostTargetTraceRecording;
struct HostTargetMetadata {
std::optional<std::string> appDisplayName;
@@ -98,11 +93,6 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
}
};
struct PerfMonitorUpdateRequest {
std::string interactionName;
uint16_t durationMs;
};
virtual ~HostTargetDelegate() override;
/**
@@ -131,13 +121,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
@@ -183,19 +166,6 @@ class HostTargetController final {
*/
bool decrementPauseOverlayCounter();
/**
* Starts trace recording for this HostTarget.
*
* \param mode In which mode to start the trace recording.
* \return false if already tracing, true otherwise.
*/
bool startTracing(tracing::Mode mode);
/**
* Stops previously started trace recording.
*/
tracing::TraceRecordingState stopTracing();
private:
HostTarget& target_;
size_t pauseOverlayCounter_{0};
@@ -267,29 +237,6 @@ class JSINSPECTOR_EXPORT HostTarget
*/
void sendCommand(HostCommand command);
/**
* Creates a new HostTracingAgent.
* This Agent is not owned by the HostTarget. The Agent will be destroyed at
* the end of the tracing session.
*
* \param state A reference to the state of the active trace recording.
*/
std::shared_ptr<HostTracingAgent> createTracingAgent(
tracing::TraceRecordingState& state);
/**
* Starts trace recording for this HostTarget.
*
* \param mode In which mode to start the trace recording.
* \return false if already tracing, true otherwise.
*/
bool startTracing(tracing::Mode mode);
/**
* Stops previously started trace recording.
*/
tracing::TraceRecordingState stopTracing();
private:
/**
* Constructs a new HostTarget.
@@ -309,15 +256,6 @@ class JSINSPECTOR_EXPORT HostTarget
std::shared_ptr<ExecutionContextManager> executionContextManager_;
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
std::unique_ptr<HostCommandSender> commandSender_;
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
/**
* Current pending trace recording, which encapsulates the configuration of
* the tracing session and the state.
*
* Should only be allocated when there is an active tracing session.
*/
std::unique_ptr<HostTargetTraceRecording> traceRecording_{nullptr};
inline HostTargetDelegate& getDelegate() {
return delegate_;
@@ -327,13 +265,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).
@@ -1,54 +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 "HostTargetTraceRecording.h"
#include "HostTarget.h"
namespace facebook::react::jsinspector_modern {
HostTargetTraceRecording::HostTargetTraceRecording(
tracing::Mode tracingMode,
HostTarget& hostTarget)
: tracingMode_(tracingMode), hostTarget_(hostTarget) {}
void HostTargetTraceRecording::setTracedInstance(
InstanceTarget* instanceTarget) {
// If HostTracingAgent is allocated, it means that there is an active tracing
// recording session.
if (hostTracingAgent_ != nullptr) {
hostTracingAgent_->setTracedInstance(instanceTarget);
}
}
void HostTargetTraceRecording::start() {
assert(
hostTracingAgent_ == nullptr &&
"Tracing Agent for the HostTarget was already initialized.");
state_ = tracing::TraceRecordingState{
.mode = tracingMode_,
.startTime = HighResTimeStamp::now(),
};
hostTracingAgent_ = hostTarget_.createTracingAgent(*state_);
}
tracing::TraceRecordingState HostTargetTraceRecording::stop() {
assert(
hostTracingAgent_ != nullptr &&
"TracingAgent for the HostTarget has not been initialized.");
hostTracingAgent_.reset();
assert(
state_.has_value() &&
"The state for this tracing session has not been initialized.");
auto state = std::move(*state_);
state_.reset();
return state;
}
} // namespace facebook::react::jsinspector_modern
@@ -1,86 +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 "HostAgent.h"
#include "HostTarget.h"
#include "InstanceTarget.h"
#include <jsinspector-modern/tracing/TraceRecordingState.h>
#include <optional>
namespace facebook::react::jsinspector_modern {
/**
* A local representation of the Tracing "session".
*
* Owned by the HostTarget and should only be allocated during an active
* recording.
*
* Owns all allocated Tracing Agents. A single Target can have a single active
* Tracing Agent, but only as a std::weak_ptr.
*/
class HostTargetTraceRecording {
public:
explicit HostTargetTraceRecording(
tracing::Mode tracingMode,
HostTarget& hostTarget);
inline bool isBackgroundInitiated() const {
return tracingMode_ == tracing::Mode::Background;
}
inline bool isUserInitiated() const {
return tracingMode_ == tracing::Mode::CDP;
}
/**
* Updates the current traced Instance for this recording.
*/
void setTracedInstance(InstanceTarget* instanceTarget);
/**
* Starts the recording.
*
* Will allocate all Tracing Agents for all currently registered Targets.
*/
void start();
/**
* Stops the recording and drops the recording state.
*
* Will deallocate all Tracing Agents.
*/
tracing::TraceRecordingState stop();
private:
/**
* The mode in which this trace recording was initialized.
*/
tracing::Mode tracingMode_;
/**
* The Host for which this Trace Recording is going to happen.
*/
HostTarget& hostTarget_;
/**
* The state of the current Trace Recording.
* Only allocated if the recording is enabled.
*/
std::optional<tracing::TraceRecordingState> state_;
/**
* The TracingAgent of the targeted Host.
* Only allocated if the recording is enabled.
*/
std::shared_ptr<HostTracingAgent> hostTracingAgent_;
};
} // namespace facebook::react::jsinspector_modern
@@ -1,55 +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 "HostTarget.h"
#include "HostTargetTraceRecording.h"
namespace facebook::react::jsinspector_modern {
bool HostTargetController::startTracing(tracing::Mode tracingMode) {
return target_.startTracing(tracingMode);
}
tracing::TraceRecordingState HostTargetController::stopTracing() {
return target_.stopTracing();
}
std::shared_ptr<HostTracingAgent> HostTarget::createTracingAgent(
tracing::TraceRecordingState& state) {
auto agent = std::make_shared<HostTracingAgent>(state);
agent->setTracedInstance(currentInstance_.get());
return agent;
}
bool HostTarget::startTracing(tracing::Mode tracingMode) {
if (traceRecording_ != nullptr) {
if (traceRecording_->isBackgroundInitiated() &&
tracingMode == tracing::Mode::CDP) {
traceRecording_.reset();
} else {
return false;
}
}
traceRecording_ =
std::make_unique<HostTargetTraceRecording>(tracingMode, *this);
traceRecording_->setTracedInstance(currentInstance_.get());
traceRecording_->start();
return true;
}
tracing::TraceRecordingState HostTarget::stopTracing() {
assert(traceRecording_ != nullptr && "No tracing in progress");
auto state = traceRecording_->stop();
traceRecording_.reset();
return state;
}
} // namespace facebook::react::jsinspector_modern
@@ -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;
};
@@ -24,14 +24,6 @@ void CallbackLocalConnection::disconnect() {
handler_ = nullptr;
}
CallbackRemoteConnection::CallbackRemoteConnection(
std::function<void(std::string)> handler)
: handler_(std::move(handler)) {}
void CallbackRemoteConnection::onMessage(std::string message) {
handler_(std::move(message));
}
RAIIRemoteConnection::RAIIRemoteConnection(
std::unique_ptr<IRemoteConnection> remote)
: remote_(std::move(remote)) {}
@@ -33,25 +33,6 @@ class CallbackLocalConnection : public ILocalConnection {
std::function<void(std::string)> handler_;
};
/**
* Wraps a callback function in IRemoteConnection.
*/
class CallbackRemoteConnection : public IRemoteConnection {
public:
/**
* Creates a new Connection that uses the given callback to receive messages
* from the backend.
*/
explicit CallbackRemoteConnection(std::function<void(std::string)> handler);
void onMessage(std::string message) override;
void onDisconnect() override {}
private:
std::function<void(std::string)> handler_;
};
/**
* Wraps an IRemoteConnection in a simpler interface that calls `onDisconnect`
* implicitly upon destruction.
@@ -9,21 +9,11 @@
#include "RuntimeTarget.h"
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/PerformanceTracer.h>
#include <utility>
namespace facebook::react::jsinspector_modern {
namespace {
// The size of the timeline for the trace recording that happened in the
// background.
constexpr HighResDuration kBackgroundTracePerformanceTracerWindowSize =
HighResDuration::fromMilliseconds(20000);
} // namespace
InstanceAgent::InstanceAgent(
FrontendChannel frontendChannel,
InstanceTarget& target,
@@ -165,34 +155,25 @@ void InstanceAgent::maybeSendPendingConsoleMessages() {
}
}
#pragma mark - Tracing
InstanceTracingAgent::InstanceTracingAgent(tracing::TraceRecordingState& state)
: tracing::TargetTracingAgent(state) {
auto& performanceTracer = tracing::PerformanceTracer::getInstance();
if (state.mode == tracing::Mode::Background) {
performanceTracer.startTracing(kBackgroundTracePerformanceTracerWindowSize);
} else {
performanceTracer.startTracing();
void InstanceAgent::startTracing() {
if (runtimeAgent_) {
runtimeAgent_->enableSamplingProfiler();
}
}
InstanceTracingAgent::~InstanceTracingAgent() {
auto& performanceTracer = tracing::PerformanceTracer::getInstance();
auto performanceTraceEvents = performanceTracer.stopTracing();
if (performanceTraceEvents) {
state_.instanceTracingProfiles.emplace_back(tracing::InstanceTracingProfile{
.performanceTraceEvents = std::move(*performanceTraceEvents),
});
void InstanceAgent::stopTracing() {
if (runtimeAgent_) {
runtimeAgent_->disableSamplingProfiler();
}
}
void InstanceTracingAgent::setTracedRuntime(RuntimeTarget* runtimeTarget) {
if (runtimeTarget != nullptr) {
runtimeTracingAgent_ = runtimeTarget->createTracingAgent(state_);
} else {
runtimeTracingAgent_ = nullptr;
}
tracing::InstanceTracingProfile InstanceAgent::collectTracingProfile() {
tracing::RuntimeSamplingProfile runtimeSamplingProfile =
runtimeAgent_->collectSamplingProfile();
return tracing::InstanceTracingProfile{
.runtimeSamplingProfile = std::move(runtimeSamplingProfile),
};
}
} // namespace facebook::react::jsinspector_modern
@@ -15,7 +15,6 @@
#include <jsinspector-modern/RuntimeAgent.h>
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/InstanceTracingProfile.h>
#include <jsinspector-modern/tracing/TargetTracingAgent.h>
#include <functional>
@@ -60,6 +59,23 @@ class InstanceAgent final {
*/
void sendConsoleMessage(SimpleConsoleMessage message);
/**
* Notify Instance about started Tracing session. Should be initiated by
* TracingAgent on Tracing.start CDP method.
*/
void startTracing();
/**
* Notify Instance about stopped Tracing session. Should be initiated by
* TracingAgent on Tracing.end CDP method.
*/
void stopTracing();
/**
* Return recorded profile for the previous tracing session.
*/
tracing::InstanceTracingProfile collectTracingProfile();
private:
void maybeSendExecutionContextCreatedNotification();
void sendConsoleMessageImmediately(SimpleConsoleMessage message);
@@ -71,27 +87,4 @@ class InstanceAgent final {
SessionState& sessionState_;
};
#pragma mark - Tracing
/**
* An Agent that handles Tracing events for a particular InstanceTarget.
*
* Lifetime of this agent is bound to the lifetime of the Tracing session -
* HostTargetTraceRecording and to the lifetime of the InstanceTarget.
*/
class InstanceTracingAgent : tracing::TargetTracingAgent {
public:
explicit InstanceTracingAgent(tracing::TraceRecordingState& state);
~InstanceTracingAgent();
/**
* Registers the RuntimeTarget with this tracing agent.
*/
void setTracedRuntime(RuntimeTarget* runtimeTarget);
private:
std::shared_ptr<RuntimeTracingAgent> runtimeTracingAgent_;
};
} // namespace facebook::react::jsinspector_modern
@@ -44,25 +44,12 @@ std::shared_ptr<InstanceAgent> InstanceTarget::createAgent(
return instanceAgent;
}
std::shared_ptr<InstanceTracingAgent> InstanceTarget::createTracingAgent(
tracing::TraceRecordingState& state) {
auto agent = std::make_shared<InstanceTracingAgent>(state);
agent->setTracedRuntime(currentRuntime_.get());
tracingAgent_ = agent;
return agent;
}
InstanceTarget::~InstanceTarget() {
// Agents are owned by the session, not by InstanceTarget, but
// they hold an InstanceTarget& that we must guarantee is valid.
assert(
agents_.empty() &&
"InstanceAgent objects must be destroyed before their InstanceTarget. Did you call HostTarget::unregisterInstance()?");
// Tracing Agents are owned by the HostTargetTraceRecording.
assert(
tracingAgent_.expired() &&
"InstanceTracingAgent must be destroyed before their InstanceTarget. Did you call HostTarget::unregisterInstance()?");
}
RuntimeTarget& InstanceTarget::registerRuntime(
@@ -82,13 +69,6 @@ RuntimeTarget& InstanceTarget::registerRuntime(
agents_.forEach([currentRuntime = &*currentRuntime_](InstanceAgent& agent) {
agent.setCurrentRuntime(currentRuntime);
});
if (auto tracingAgent = tracingAgent_.lock()) {
// Registers the Runtime for tracing, if a Trace is currently being
// recorded.
tracingAgent->setTracedRuntime(currentRuntime_.get());
}
return *currentRuntime_;
}
@@ -98,13 +78,6 @@ void InstanceTarget::unregisterRuntime(RuntimeTarget& Runtime) {
"Invalid unregistration");
agents_.forEach(
[](InstanceAgent& agent) { agent.setCurrentRuntime(nullptr); });
if (auto tracingAgent = tracingAgent_.lock()) {
// Unregisters the Runtime for tracing, if a Trace is currently being
// recorded.
tracingAgent->setTracedRuntime(nullptr);
}
currentRuntime_.reset();
}
@@ -15,15 +15,12 @@
#include <jsinspector-modern/InspectorInterfaces.h>
#include <jsinspector-modern/RuntimeAgent.h>
#include <jsinspector-modern/tracing/TraceRecordingState.h>
#include <memory>
namespace facebook::react::jsinspector_modern {
class InstanceAgent;
class InstanceTracingAgent;
class HostTargetTraceRecording;
/**
* Receives events from an InstanceTarget. This is a shared interface that
@@ -71,18 +68,6 @@ class InstanceTarget : public EnableExecutorFromThis<InstanceTarget> {
const FrontendChannel& channel,
SessionState& sessionState);
/**
* Creates a new InstanceTracingAgent.
* This Agent is not owned by the InstanceTarget. The Agent will be destroyed
* either before the InstanceTarget is destroyed, as part of the
* InstanceTarget unregistration in HostTarget, or at the end of the tracing
* session.
*
* \param state A reference to the state of the active trace recording.
*/
std::shared_ptr<InstanceTracingAgent> createTracingAgent(
tracing::TraceRecordingState& state);
/**
* Registers a JS runtime with this InstanceTarget. \returns a reference to
* the created RuntimeTarget, which is owned by the \c InstanceTarget. All the
@@ -118,13 +103,6 @@ class InstanceTarget : public EnableExecutorFromThis<InstanceTarget> {
std::shared_ptr<RuntimeTarget> currentRuntime_{nullptr};
WeakList<InstanceAgent> agents_;
std::shared_ptr<ExecutionContextManager> executionContextManager_;
/**
* This TracingAgent is owned by the HostTracingAgent, both are bound to
* the lifetime of their corresponding targets and the lifetime of the tracing
* session - HostTargetTraceRecording.
*/
std::weak_ptr<InstanceTracingAgent> tracingAgent_;
};
} // namespace facebook::react::jsinspector_modern
@@ -128,24 +128,4 @@ tracing::RuntimeSamplingProfile RuntimeAgent::collectSamplingProfile() {
return targetController_.collectSamplingProfile();
}
#pragma mark - Tracing
RuntimeTracingAgent::RuntimeTracingAgent(
tracing::TraceRecordingState& state,
RuntimeTargetController& targetController)
: tracing::TargetTracingAgent(state), targetController_(targetController) {
if (state.mode == tracing::Mode::CDP) {
targetController_.enableSamplingProfiler();
}
}
RuntimeTracingAgent::~RuntimeTracingAgent() {
if (state_.mode == tracing::Mode::CDP) {
targetController_.disableSamplingProfiler();
auto profile = targetController_.collectSamplingProfile();
state_.runtimeSamplingProfiles.emplace_back(std::move(profile));
}
}
} // namespace facebook::react::jsinspector_modern
@@ -13,8 +13,6 @@
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/RuntimeSamplingProfile.h>
#include <jsinspector-modern/tracing/TargetTracingAgent.h>
#include <jsinspector-modern/tracing/TraceRecordingState.h>
namespace facebook::react::jsinspector_modern {
@@ -108,24 +106,4 @@ class RuntimeAgent final {
const ExecutionContextDescription executionContextDescription_;
};
#pragma mark - Tracing
/**
* An Agent that handles Tracing events for a particular RuntimeTarget.
*
* Lifetime of this agent is bound to the lifetime of the Tracing session -
* HostTargetTraceRecording and to the lifetime of the RuntimeTarget.
*/
class RuntimeTracingAgent : tracing::TargetTracingAgent {
public:
explicit RuntimeTracingAgent(
tracing::TraceRecordingState& state,
RuntimeTargetController& targetController);
~RuntimeTracingAgent();
private:
RuntimeTargetController& targetController_;
};
} // namespace facebook::react::jsinspector_modern
@@ -77,24 +77,12 @@ std::shared_ptr<RuntimeAgent> RuntimeTarget::createAgent(
return runtimeAgent;
}
std::shared_ptr<RuntimeTracingAgent> RuntimeTarget::createTracingAgent(
tracing::TraceRecordingState& state) {
auto agent = std::make_shared<RuntimeTracingAgent>(state, controller_);
tracingAgent_ = agent;
return agent;
}
RuntimeTarget::~RuntimeTarget() {
// Agents are owned by the session, not by RuntimeTarget, but
// they hold a RuntimeTarget& that we must guarantee is valid.
assert(
agents_.empty() &&
"RuntimeAgent objects must be destroyed before their RuntimeTarget. Did you call InstanceTarget::unregisterRuntime()?");
// Tracing Agents are owned by the HostTargetTraceRecording.
assert(
tracingAgent_.expired() &&
"RuntimeTracingAgent must be destroyed before their InstanceTarget. Did you call InstanceTarget::unregisterRuntime()?");
}
void RuntimeTarget::installBindingHandler(const std::string& bindingName) {
@@ -17,7 +17,6 @@
#include <ReactCommon/RuntimeExecutor.h>
#include <jsinspector-modern/tracing/RuntimeSamplingProfile.h>
#include <jsinspector-modern/tracing/TraceRecordingState.h>
#include <memory>
@@ -36,7 +35,6 @@
namespace facebook::react::jsinspector_modern {
class RuntimeAgent;
class RuntimeTracingAgent;
class RuntimeAgentDelegate;
class RuntimeTarget;
struct SessionState;
@@ -204,17 +202,6 @@ class JSINSPECTOR_EXPORT RuntimeTarget
const FrontendChannel& channel,
SessionState& sessionState);
/**
* Creates a new RuntimeTracingAgent.
* This Agent is not owned by the RuntimeTarget. The Agent will be destroyed
* either before the RuntimeTarget is destroyed, as part of the RuntimeTarget
* unregistration in InstanceTarget, or at the end of the tracing session.
*
* \param state A reference to the state of the active trace recording.
*/
std::shared_ptr<RuntimeTracingAgent> createTracingAgent(
tracing::TraceRecordingState& state);
/**
* Start sampling profiler for a particular JavaScript runtime.
*/
@@ -259,13 +246,6 @@ class JSINSPECTOR_EXPORT RuntimeTarget
WeakList<RuntimeAgent> agents_;
RuntimeTargetController controller_{*this};
/**
* This TracingAgent is owned by the InstanceTracingAgent, both are bound to
* the lifetime of their corresponding targets and the lifetime of the tracing
* session - HostTargetTraceRecording.
*/
std::weak_ptr<RuntimeTracingAgent> tracingAgent_;
/**
* Adds a function with the given name on the runtime's global object, that
* when called will send a Runtime.bindingCalled event through all connected
@@ -25,12 +25,6 @@ struct SessionState {
bool isReactNativeApplicationDomainEnabled{false};
bool isRuntimeDomainEnabled{false};
/**
* Whether the Trace Recording was initialized via CDP Tracing.start method
* and not finished yet with Tracing.stop.
*/
bool hasPendingTraceRecording{false};
/**
* A map from binding names (registered during this session using @cdp
* Runtime.addBinding) to execution context selectors.
@@ -10,8 +10,6 @@
#include <jsinspector-modern/tracing/PerformanceTracer.h>
#include <jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h>
#include <jsinspector-modern/tracing/TraceEventSerializer.h>
#include <jsinspector-modern/tracing/TraceRecordingStateSerializer.h>
#include <jsinspector-modern/tracing/TracingMode.h>
namespace facebook::react::jsinspector_modern {
@@ -26,30 +24,39 @@ const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000;
/**
* The maximum number of ProfileChunk trace events
* that will be sent in a single CDP Tracing.dataCollected message.
* TODO(T219394401): Increase the size once we manage the queue on OkHTTP
side
* TODO(T219394401): Increase the size once we manage the queue on OkHTTP side
* properly and avoid WebSocket disconnections when sending a message larger
* than 16MB.
*/
const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1;
void serializeTraceEventsInChunks(
std::vector<tracing::TraceEvent>&& traceEvents,
uint16_t chunkSize,
const std::function<void(folly::dynamic&& eventsChunk)>& resultCallback) {
auto serializedTraceEvents = folly::dynamic::array();
for (auto&& traceEvent : traceEvents) {
// Emit trace events
serializedTraceEvents.push_back(
tracing::TraceEventSerializer::serialize(std::move(traceEvent)));
if (serializedTraceEvents.size() == chunkSize) {
resultCallback(std::move(serializedTraceEvents));
serializedTraceEvents = folly::dynamic::array();
}
}
if (!serializedTraceEvents.empty()) {
resultCallback(std::move(serializedTraceEvents));
}
}
} // namespace
TracingAgent::TracingAgent(
FrontendChannel frontendChannel,
SessionState& sessionState,
HostTargetController& hostTargetController)
const SessionState& sessionState)
: frontendChannel_(std::move(frontendChannel)),
sessionState_(sessionState),
hostTargetController_(hostTargetController) {}
TracingAgent::~TracingAgent() {
// Agents are owned by the session. If the agent is destroyed, it means that
// the session was destroyed. We should stop pending recording.
if (sessionState_.hasPendingTraceRecording) {
hostTargetController_.stopTracing();
}
}
sessionState_(sessionState) {}
bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
if (req.method == "Tracing.start") {
@@ -62,27 +69,57 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
return true;
}
bool didNotHaveAlreadyRunningRecording =
hostTargetController_.startTracing(tracing::Mode::CDP);
if (!didNotHaveAlreadyRunningRecording) {
if (!instanceAgent_) {
frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::InvalidRequest,
"Tracing has already been started"));
cdp::ErrorCode::InternalError,
"Couldn't find instance available for Tracing"));
return true;
}
sessionState_.hasPendingTraceRecording = true;
bool correctlyStartedPerformanceTracer =
tracing::PerformanceTracer::getInstance().startTracing();
if (!correctlyStartedPerformanceTracer) {
frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::InternalError,
"Tracing session already started"));
return true;
}
instanceAgent_->startTracing();
instanceTracingStartTimestamp_ = HighResTimeStamp::now();
frontendChannel_(cdp::jsonResult(req.id));
return true;
} else if (req.method == "Tracing.end") {
// @cdp Tracing.end support is experimental.
auto state = hostTargetController_.stopTracing();
if (!instanceAgent_) {
frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::InternalError,
"Couldn't find instance available for Tracing"));
return true;
}
instanceAgent_->stopTracing();
tracing::PerformanceTracer& performanceTracer =
tracing::PerformanceTracer::getInstance();
auto collectedEvents = performanceTracer.stopTracing();
if (!collectedEvents) {
frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::InternalError,
"Tracing session not started"));
return true;
}
sessionState_.hasPendingTraceRecording = false;
// Send response to Tracing.end request.
frontendChannel_(cdp::jsonResult(req.id));
@@ -91,10 +128,19 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
"Tracing.dataCollected",
folly::dynamic::object("value", std::move(eventsChunk))));
};
tracing::TraceRecordingStateSerializer::emitAsDataCollectedChunks(
std::move(state),
dataCollectedCallback,
serializeTraceEventsInChunks(
std::move(*collectedEvents),
TRACE_EVENT_CHUNK_SIZE,
dataCollectedCallback);
auto tracingProfile = instanceAgent_->collectTracingProfile();
tracing::IdGenerator profileIdGenerator;
tracing::RuntimeSamplingProfileTraceEventSerializer::serializeAndDispatch(
std::move(tracingProfile.runtimeSamplingProfile),
profileIdGenerator,
instanceTracingStartTimestamp_,
dataCollectedCallback,
PROFILE_TRACE_EVENT_CHUNK_SIZE);
frontendChannel_(cdp::jsonNotification(
@@ -107,4 +153,9 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
return false;
}
void TracingAgent::setCurrentInstanceAgent(
std::shared_ptr<InstanceAgent> instanceAgent) {
instanceAgent_ = std::move(instanceAgent);
}
} // namespace facebook::react::jsinspector_modern
@@ -7,8 +7,8 @@
#pragma once
#include "HostTarget.h"
#include "InspectorInterfaces.h"
#include "InstanceAgent.h"
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/Timing.h>
@@ -27,10 +27,7 @@ class TracingAgent {
*/
TracingAgent(
FrontendChannel frontendChannel,
SessionState& sessionState,
HostTargetController& hostTargetController);
~TracingAgent();
const SessionState& sessionState);
/**
* Handle a CDP request. The response will be sent over the provided
@@ -39,15 +36,33 @@ class TracingAgent {
*/
bool handleRequest(const cdp::PreparsedRequest& req);
/**
* Replace the current InstanceAgent with the given one.
* \param agent The new InstanceAgent. May be null to signify that there is
* currently no active instance.
*/
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent);
private:
/**
* A channel used to send responses and events to the frontend.
*/
FrontendChannel frontendChannel_;
SessionState& sessionState_;
/**
* Current InstanceAgent. May be null to signify that there is
* currently no active instance.
*/
std::shared_ptr<InstanceAgent> instanceAgent_;
HostTargetController& hostTargetController_;
/**
* Timestamp of when we started tracing of an Instance, will be used as a
* a start of JavaScript samples recording and as a time origin for the events
* in this trace.
*/
HighResTimeStamp instanceTracingStartTimestamp_;
const SessionState& sessionState_;
};
} // namespace facebook::react::jsinspector_modern
@@ -7,12 +7,13 @@
#pragma once
#include "TraceEvent.h"
#include "RuntimeSamplingProfile.h"
namespace facebook::react::jsinspector_modern::tracing {
struct InstanceTracingProfile {
std::vector<TraceEvent> performanceTraceEvents;
public:
RuntimeSamplingProfile runtimeSamplingProfile;
};
} // namespace facebook::react::jsinspector_modern::tracing
@@ -77,6 +77,7 @@ std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
tracingAtomic_ = false;
performanceMeasureCount_ = 0;
currentTraceMaxDuration_ = std::nullopt;
events = collectEventsAndClearBuffers(currentTraceEndTime);
}
@@ -113,7 +114,6 @@ std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
.tid = oscompat::getCurrentThreadId(),
});
currentTraceMaxDuration_ = std::nullopt;
return events;
}
@@ -1,33 +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 "TraceRecordingState.h"
namespace facebook::react::jsinspector_modern::tracing {
/**
* An interface for a tracing agent of a target.
* Tracing Agents are only allocated during an active tracing session.
*
* Construction of a TracingAgent means that either the recording has just
* started or the target was just created during an active recording.
* Destruction of a TracingAgent means that either the recording has stopped or
* the target is about to be destroyed.
*/
class TargetTracingAgent {
public:
explicit TargetTracingAgent(TraceRecordingState& state) : state_(state) {
(void)state_;
}
protected:
TraceRecordingState& state_;
};
} // namespace facebook::react::jsinspector_modern::tracing
@@ -1,39 +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 "InstanceTracingProfile.h"
#include "RuntimeSamplingProfile.h"
#include "TracingMode.h"
#include <oscompat/OSCompat.h>
#include <react/timing/primitives.h>
#include <vector>
namespace facebook::react::jsinspector_modern::tracing {
struct TraceRecordingState {
// The mode of this Trace Recording.
tracing::Mode mode;
// The ID of the OS-level process that this Trace Recording is associated
// with.
ProcessId processId = oscompat::getCurrentProcessId();
// The timestamp at which this Trace Recording started.
HighResTimeStamp startTime;
// All captured Runtime Sampling Profiles during this Trace Recording.
std::vector<RuntimeSamplingProfile> runtimeSamplingProfiles{};
// All captures Instance Tracing Profiles during this Trace Recording.
std::vector<InstanceTracingProfile> instanceTracingProfiles{};
};
} // namespace facebook::react::jsinspector_modern::tracing
@@ -1,68 +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 "TraceRecordingStateSerializer.h"
#include "RuntimeSamplingProfileTraceEventSerializer.h"
#include "TraceEventSerializer.h"
namespace facebook::react::jsinspector_modern::tracing {
namespace {
folly::dynamic generateNewChunk(uint16_t chunkSize) {
folly::dynamic chunk = folly::dynamic::array();
chunk.reserve(chunkSize);
return chunk;
}
} // namespace
/* static */ void TraceRecordingStateSerializer::emitAsDataCollectedChunks(
TraceRecordingState&& recording,
const std::function<void(folly::dynamic&&)>& chunkCallback,
uint16_t performanceTraceEventsChunkSize,
uint16_t profileTraceEventsChunkSize) {
auto instancesProfiles = std::move(recording.instanceTracingProfiles);
IdGenerator profileIdGenerator;
for (auto& instanceProfile : instancesProfiles) {
emitPerformanceTraceEvents(
std::move(instanceProfile.performanceTraceEvents),
chunkCallback,
performanceTraceEventsChunkSize);
}
RuntimeSamplingProfileTraceEventSerializer::serializeAndDispatch(
std::move(recording.runtimeSamplingProfiles),
profileIdGenerator,
recording.startTime,
chunkCallback,
profileTraceEventsChunkSize);
}
/* static */ void TraceRecordingStateSerializer::emitPerformanceTraceEvents(
std::vector<TraceEvent>&& events,
const std::function<void(folly::dynamic&&)>& chunkCallback,
uint16_t chunkSize) {
folly::dynamic chunk = generateNewChunk(chunkSize);
for (auto& event : events) {
if (chunk.size() == chunkSize) {
chunkCallback(std::move(chunk));
chunk = generateNewChunk(chunkSize);
}
chunk.push_back(TraceEventSerializer::serialize(std::move(event)));
}
if (!chunk.empty()) {
chunkCallback(std::move(chunk));
}
}
} // namespace facebook::react::jsinspector_modern::tracing
@@ -1,42 +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 "TraceEvent.h"
#include "TraceRecordingState.h"
#include <folly/dynamic.h>
#include <vector>
namespace facebook::react::jsinspector_modern::tracing {
/**
* A serializer for TraceRecordingState that can be used for tranforming the
* recording into sequence of serialized Trace Events.
*/
class TraceRecordingStateSerializer {
public:
/**
* Transforms the recording into a sequence of serialized Trace Events, which
* is split in chunks of sizes \p performanceTraceEventsChunkSize or
* \p profileTraceEventsChunkSize, depending on type, and sent with \p
* chunkCallback.
*/
static void emitAsDataCollectedChunks(
TraceRecordingState&& recording,
const std::function<void(folly::dynamic&& chunk)>& chunkCallback,
uint16_t performanceTraceEventsChunkSize,
uint16_t profileTraceEventsChunkSize);
static void emitPerformanceTraceEvents(
std::vector<TraceEvent>&& events,
const std::function<void(folly::dynamic&& chunk)>& chunkCallback,
uint16_t chunkSize);
};
} // namespace facebook::react::jsinspector_modern::tracing
@@ -1,17 +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
namespace facebook::react::jsinspector_modern::tracing {
enum class Mode {
CDP, // Initiated by the user via Chrome DevTools Frontend.
Background, // Initiated by the host, doesn't require active CDP session.
};
}
@@ -292,7 +292,7 @@ SharedDebugStringConvertibleList ImageProps::getDebugProps() const {
sourcesList = sources[0].getDebugProps("source");
} else if (sources.size() > 1) {
for (const auto& source : sources) {
std::string sourceName = "source-" + react::toString(source.scale) + "x";
std::string sourceName = "source@" + react::toString(source.scale) + "x";
auto debugProps = source.getDebugProps(sourceName);
sourcesList.insert(
sourcesList.end(), debugProps.begin(), debugProps.end());
@@ -304,32 +304,8 @@ SharedDebugStringConvertibleList ImageProps::getDebugProps() const {
SharedDebugStringConvertibleList{
debugStringConvertibleItem(
"blurRadius", blurRadius, imageProps.blurRadius),
debugStringConvertibleItem(
"resizeMode",
toString(resizeMode),
toString(imageProps.resizeMode)),
debugStringConvertibleItem(
"tintColor", toString(tintColor), toString(imageProps.tintColor)),
};
}
inline std::string toString(ImageResizeMode resizeMode) {
switch (resizeMode) {
case ImageResizeMode::Cover:
return "cover";
case ImageResizeMode::Contain:
return "contain";
case ImageResizeMode::Stretch:
return "stretch";
case ImageResizeMode::Center:
return "center";
case ImageResizeMode::Repeat:
return "repeat";
case ImageResizeMode::None:
return "none";
}
}
#endif
} // namespace facebook::react
@@ -34,7 +34,7 @@ class ImageProps final : public ViewProps {
ImageSources sources{};
ImageSource defaultSource{};
ImageSource loadingIndicatorSource{};
ImageResizeMode resizeMode{ImageResizeMode::Cover};
ImageResizeMode resizeMode{ImageResizeMode::Stretch};
Float blurRadius{};
EdgeInsets capInsets{};
SharedColor tintColor{};

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