mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
811ffc2653 |
@@ -118,6 +118,7 @@ async function collectResults(discordWebHook) {
|
||||
} else {
|
||||
console.log('Discord webhook not set');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Initialize Firebase client
|
||||
|
||||
+211
-31
@@ -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}
|
||||
|
||||
-1
@@ -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';
|
||||
|
||||
|
||||
+3
-1
@@ -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';
|
||||
|
||||
+3
-1
@@ -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
@@ -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
|
||||
|
||||
-22
@@ -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)
|
||||
|
||||
+1
-15
@@ -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);
|
||||
|
||||
|
||||
+1
-2
@@ -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 {
|
||||
|
||||
+5
-6
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
|
||||
-5
@@ -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,
|
||||
|
||||
-6
@@ -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,
|
||||
|
||||
+2
-4
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -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,
|
||||
|
||||
+1
-4
@@ -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(
|
||||
|
||||
+17
-46
@@ -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(
|
||||
|
||||
+4
-4
@@ -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())
|
||||
|
||||
+2
-11
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+7
-11
@@ -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 {
|
||||
|
||||
+6
-9
@@ -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
|
||||
|
||||
+23
-24
@@ -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
|
||||
+5
-5
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -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<
|
||||
|
||||
-2
@@ -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)
|
||||
|
||||
-22
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()
|
||||
|
||||
|
||||
+7
-16
@@ -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)
|
||||
|
||||
-2
@@ -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
|
||||
|
||||
-2
@@ -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
|
||||
|
||||
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ class ColorStopTest {
|
||||
fun setUp() {
|
||||
val metrics = DisplayMetrics()
|
||||
metrics.density = 1f
|
||||
DisplayMetricsHolder.setScreenDisplayMetrics(metrics)
|
||||
DisplayMetricsHolder.setWindowDisplayMetrics(metrics)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-2
@@ -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()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+3
-2
@@ -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
|
||||
-68
@@ -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
|
||||
-42
@@ -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
Reference in New Issue
Block a user