mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
70
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b399f4a8 | ||
|
|
dc9db01665 | ||
|
|
91df5a4a0f | ||
|
|
4da586ead9 | ||
|
|
de3c1ee097 | ||
|
|
e1a1cead43 | ||
|
|
7211119d23 | ||
|
|
6ab7b70241 | ||
|
|
af384a914a | ||
|
|
566d30f88a | ||
|
|
4afc47a588 | ||
|
|
2cd48ef351 | ||
|
|
65cdd5b82c | ||
|
|
66a2f1d312 | ||
|
|
79d55542aa | ||
|
|
f8fc90e754 | ||
|
|
ec4ead59e1 | ||
|
|
fd273f83e0 | ||
|
|
ecd138e267 | ||
|
|
b91a449e7f | ||
|
|
6ba7cb3102 | ||
|
|
e5dd7d68bf | ||
|
|
a12036f7be | ||
|
|
3e46cb384c | ||
|
|
667ba76aef | ||
|
|
9a3958a619 | ||
|
|
4d90f84869 | ||
|
|
3a68f14ac2 | ||
|
|
b98846c2e3 | ||
|
|
13b93cfdda | ||
|
|
3a01a0c9c5 | ||
|
|
e3f8c63698 | ||
|
|
d79dc48abd | ||
|
|
8bd1f3fbfa | ||
|
|
2d9933e616 | ||
|
|
ec0dbb729d | ||
|
|
50e38cc9f1 | ||
|
|
a85955a446 | ||
|
|
758892a7d8 | ||
|
|
1108b4d97b | ||
|
|
0dcbebd285 | ||
|
|
8850736188 | ||
|
|
e797135247 | ||
|
|
41265bac6b | ||
|
|
7cb9fa9737 | ||
|
|
9f73edee2d | ||
|
|
bfca7cfe7a | ||
|
|
0df59d4f03 | ||
|
|
0d664100bb | ||
|
|
e3f03269c5 | ||
|
|
cc6f75bb75 | ||
|
|
5b53c07329 | ||
|
|
eb3977eccf | ||
|
|
08e8f6adfd | ||
|
|
dacada7750 | ||
|
|
6fc500ee99 | ||
|
|
0fc2e954be | ||
|
|
e8f8ee3c0f | ||
|
|
e51c32d7cf | ||
|
|
97622a5caa | ||
|
|
c19fd11ccf | ||
|
|
7090b790c6 | ||
|
|
7a265d2406 | ||
|
|
19813642f1 | ||
|
|
f5595c716b | ||
|
|
4c0cd72208 | ||
|
|
9792d5e2d1 | ||
|
|
66ffa5ee4d | ||
|
|
66df63d3b5 | ||
|
|
63f8c3dcd6 |
+1
-1
@@ -90,4 +90,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.250.0
|
||||
^0.251.1
|
||||
|
||||
@@ -21,7 +21,7 @@ module.exports.sleep = sleep;
|
||||
|
||||
async function getNpmPackageInfo(pkg, versionOrTag) {
|
||||
return fetch(`https://registry.npmjs.org/${pkg}/${versionOrTag}`).then(resp =>
|
||||
res.json(),
|
||||
resp.json(),
|
||||
);
|
||||
}
|
||||
module.exports.getNpmPackageInfo = getNpmPackageInfo;
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## v0.76.1
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **runtime** Made AppRegistry callable from Native code in Bridgeless (fixes headless tasks) ([f3fee67c54](https://github.com/facebook/react-native/commit/f3fee67c547ace5e08bc9e119bcb80e0f3454d30) by [@robik](https://github.com/robik))
|
||||
- **runtime** Add jsBundleFile to DefaultReactNativeHost.kt ([e56bd89ef](https://github.com/facebook/react-native/commit/e56bd89eff72f1d38d2940b9dbd8f7b97037dc22))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **codegen** Do not generate the ComponentCls function in the RCTThirdPartyFabricComponentsProvider for components deined in the app. ([dc7e9e2d83](https://github.com/facebook/react-native/commit/dc7e9e2d83f5af3d2a970521af4fcc7f86d2168d) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **infra** Generated NODE_BINARY in .xcode.env.local now supports paths with a space ([eeaa3ff458](https://github.com/facebook/react-native/commit/eeaa3ff458a3e5c902075bb45161d6ccde31fe53) by [@blakef](https://github.com/blakef))
|
||||
- **infra** Pin Xcodeproj to < 1.26.0 ([e8776240b41](https://github.com/facebook/react-native/commit/e8776240b41a3147e95713299b69c813be60b8e8))
|
||||
- **runtime** Fixed bug where background colors would sometimes animate when changing on Views ([1d6ac09530](https://github.com/facebook/react-native/commit/1d6ac09530145e6d2bf1e4dd9d40f51542d9e7f6) by [@joevilches](https://github.com/joevilches))
|
||||
- **runtime** Fixes regression of RCTWindowFrameDidChangeNotification not fired ([e271b23fad](https://github.com/facebook/react-native/commit/e271b23fad89e1e7da2e91a8f1e5cbb295406c74) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
|
||||
## v0.76.0
|
||||
|
||||
### Breaking
|
||||
|
||||
+2
-3
@@ -14,8 +14,7 @@
|
||||
"flow": "flow",
|
||||
"format-check": "prettier --list-different \"./**/*.{js,md,yml,ts,tsx}\"",
|
||||
"format": "npm run prettier && npm run clang-format",
|
||||
"featureflags-check": "cd packages/react-native && yarn featureflags-check",
|
||||
"featureflags-update": "cd packages/react-native && yarn featureflags-update",
|
||||
"featureflags": "cd packages/react-native && yarn featureflags",
|
||||
"lint-ci": "./scripts/circleci/analyze_code.sh && yarn shellcheck",
|
||||
"lint-java": "node ./scripts/lint-java.js",
|
||||
"lint": "eslint .",
|
||||
@@ -78,7 +77,7 @@
|
||||
"eslint-plugin-redundant-undefined": "^0.4.0",
|
||||
"eslint-plugin-relay": "^1.8.3",
|
||||
"flow-api-translator": "0.24.0",
|
||||
"flow-bin": "^0.250.0",
|
||||
"flow-bin": "^0.251.1",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-eslint": "0.24.0",
|
||||
"hermes-transform": "0.24.0",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"nullthrows": "^1.1.1",
|
||||
"open": "^7.0.3",
|
||||
"selfsigned": "^2.4.1",
|
||||
"serve-static": "^1.13.1",
|
||||
"serve-static": "^1.16.2",
|
||||
"ws": "^6.2.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -39,7 +39,7 @@ module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.js'],
|
||||
parser: 'hermes-eslint',
|
||||
parser: '@babel/eslint-parser',
|
||||
plugins: ['ft-flow'],
|
||||
rules: {
|
||||
// Flow Plugin
|
||||
@@ -51,7 +51,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
files: ['*.jsx'],
|
||||
parser: 'hermes-eslint',
|
||||
parser: '@babel/eslint-parser',
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"@react-native/eslint-plugin": "0.77.0-main",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
@@ -30,8 +31,7 @@
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"hermes-eslint": "0.24.0"
|
||||
"eslint-plugin-react-native": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=8",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp = "8.7.1"
|
||||
agp = "8.7.2"
|
||||
gson = "2.8.9"
|
||||
guava = "31.0.1-jre"
|
||||
javapoet = "1.13.0"
|
||||
|
||||
+2
-5
@@ -53,11 +53,8 @@ internal object NdkConfiguratorUtils {
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_STL") }) {
|
||||
cmakeArgs.add("-DANDROID_STL=c++_shared")
|
||||
}
|
||||
// Due to the new NDK toolchain file, the C++ flags gets overridden between compilation
|
||||
// units. This is causing some libraries to don't be compiled with -DANDROID and other
|
||||
// crucial flags. This can be revisited once we bump to NDK 25/26
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE") }) {
|
||||
cmakeArgs.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON")
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES") }) {
|
||||
cmakeArgs.add("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
|
||||
}
|
||||
|
||||
val architectures = project.getReactNativeArchitectures()
|
||||
|
||||
@@ -387,15 +387,6 @@ const LOG_LEVELS = {
|
||||
warn: 2,
|
||||
error: 3,
|
||||
};
|
||||
const INSPECTOR_LEVELS = [];
|
||||
INSPECTOR_LEVELS[LOG_LEVELS.trace] = 'debug';
|
||||
INSPECTOR_LEVELS[LOG_LEVELS.info] = 'log';
|
||||
INSPECTOR_LEVELS[LOG_LEVELS.warn] = 'warning';
|
||||
INSPECTOR_LEVELS[LOG_LEVELS.error] = 'error';
|
||||
|
||||
// Strip the inner function in getNativeLogFunction(), if in dev also
|
||||
// strip method printing to originalConsole.
|
||||
const INSPECTOR_FRAMES_TO_SKIP = __DEV__ ? 2 : 1;
|
||||
|
||||
function getNativeLogFunction(level) {
|
||||
return function () {
|
||||
@@ -429,14 +420,6 @@ function getNativeLogFunction(level) {
|
||||
// (Note: Logic duplicated in ExceptionsManager.js.)
|
||||
logLevel = LOG_LEVELS.warn;
|
||||
}
|
||||
if (global.__inspectorLog) {
|
||||
global.__inspectorLog(
|
||||
INSPECTOR_LEVELS[logLevel],
|
||||
str,
|
||||
[].slice.call(arguments),
|
||||
INSPECTOR_FRAMES_TO_SKIP,
|
||||
);
|
||||
}
|
||||
if (groupStack.length) {
|
||||
str = groupFormat('', str);
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@ import * as React from 'react';
|
||||
|
||||
export default (createAnimatedComponent(FlatList): AnimatedComponentType<
|
||||
React.ElementConfig<typeof FlatList>,
|
||||
React.ElementRef<typeof FlatList>,
|
||||
FlatList<mixed>,
|
||||
>);
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RCTArchConfiguratorProtocol.h"
|
||||
#import "RCTRootViewFactory.h"
|
||||
#import "RCTUIConfiguratorProtocol.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@protocol RCTBridgeDelegate;
|
||||
@@ -55,7 +57,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
* - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
||||
*/
|
||||
@interface RCTAppDelegate : UIResponder <UIApplicationDelegate, UISceneDelegate, RCTBridgeDelegate>
|
||||
@interface RCTAppDelegate : UIResponder <
|
||||
UIApplicationDelegate,
|
||||
UISceneDelegate,
|
||||
RCTBridgeDelegate,
|
||||
RCTUIConfiguratorProtocol,
|
||||
RCTArchConfiguratorProtocol>
|
||||
|
||||
/// The window object, used to render the UViewControllers
|
||||
@property (nonatomic, strong, nonnull) UIWindow *window;
|
||||
@@ -96,48 +103,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
initProps:(NSDictionary *)initProps;
|
||||
/**
|
||||
* This method can be used to customize the rootView that is passed to React Native.
|
||||
* A typical example is to override this method in the AppDelegate to change the background color.
|
||||
* To achieve this, add in your `AppDelegate.mm`:
|
||||
* ```
|
||||
* - (void)customizeRootView:(RCTRootView *)rootView
|
||||
* {
|
||||
* rootView.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traitCollection) {
|
||||
* if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleDark) {
|
||||
* return [UIColor blackColor];
|
||||
* } else {
|
||||
* return [UIColor whiteColor];
|
||||
* }
|
||||
* }];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @parameter: rootView - The root view to customize.
|
||||
*/
|
||||
- (void)customizeRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* It creates the RootViewController.
|
||||
* By default, it creates a new instance of a `UIViewController`.
|
||||
* You can override it to provide your own initial ViewController.
|
||||
*
|
||||
* @return: an instance of `UIViewController`.
|
||||
*/
|
||||
- (UIViewController *)createRootViewController;
|
||||
|
||||
/**
|
||||
* It assigns the rootView to the rootViewController
|
||||
* By default, it assigns the rootView to the view property of the rootViewController
|
||||
* If you are not using a simple UIViewController, then there could be other methods to use to setup the rootView.
|
||||
* For example: UISplitViewController requires `setViewController(_:for:)`
|
||||
*/
|
||||
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
|
||||
|
||||
/**
|
||||
* The default `RCTColorSpace` for the app. It defaults to `RCTColorSpaceSRGB`.
|
||||
*/
|
||||
@property (nonatomic, readonly) RCTColorSpace defaultColorSpace;
|
||||
|
||||
/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
|
||||
/// new renderer. The Component Descriptor is a string which represent the name used in JS to refer to the native
|
||||
@@ -147,27 +112,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @return a dictionary that associate a component for the new renderer with his descriptor.
|
||||
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents;
|
||||
|
||||
/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off.
|
||||
///
|
||||
/// @note: This is required to be rendering on Fabric (i.e. on the New Architecture).
|
||||
/// @return: `true` if the Turbo Native Module are enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)turboModuleEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether the App will use the Fabric renderer of the New Architecture or not.
|
||||
///
|
||||
/// @return: `true` if the Fabric Renderer is enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)fabricEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether React Native's new initialization layer is enabled.
|
||||
///
|
||||
/// @return: `true` if the new initialization layer is enabled. Otherwise returns `false`.
|
||||
- (BOOL)bridgelessEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether React Native uses new Architecture.
|
||||
///
|
||||
/// @return: `true` if the new architecture is enabled. Otherwise returns `false`.
|
||||
- (BOOL)newArchEnabled;
|
||||
|
||||
/// Return the bundle URL for the main bundle.
|
||||
- (NSURL *__nullable)bundleURL;
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#endif
|
||||
#import <react/nativemodule/defaults/DefaultTurboModules.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider, RCTHostDelegate>
|
||||
@end
|
||||
|
||||
@@ -179,18 +181,6 @@
|
||||
{
|
||||
}
|
||||
|
||||
- (void)host:(RCTHost *)host
|
||||
didReceiveJSErrorStack:(NSArray<NSDictionary<NSString *, id> *> *)stack
|
||||
message:(NSString *)message
|
||||
originalMessage:(NSString *_Nullable)originalMessage
|
||||
name:(NSString *_Nullable)name
|
||||
componentStack:(NSString *_Nullable)componentStack
|
||||
exceptionId:(NSUInteger)exceptionId
|
||||
isFatal:(BOOL)isFatal
|
||||
extraData:(NSDictionary<NSString *, id> *)extraData
|
||||
{
|
||||
}
|
||||
|
||||
#pragma mark - Bridge and Bridge Adapter properties
|
||||
|
||||
- (RCTBridge *)bridge
|
||||
@@ -224,15 +214,14 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
- (std::shared_ptr<TurboModule>)getTurboModule:(const std::string &)name
|
||||
jsInvoker:(std::shared_ptr<CallInvoker>)jsInvoker
|
||||
{
|
||||
return facebook::react::DefaultTurboModules::getTurboModule(name, jsInvoker);
|
||||
return DefaultTurboModules::getTurboModule(name, jsInvoker);
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
||||
initParams:
|
||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
- (std::shared_ptr<TurboModule>)getTurboModule:(const std::string &)name
|
||||
initParams:(const ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -306,7 +295,7 @@
|
||||
|
||||
#pragma mark - Feature Flags
|
||||
|
||||
class RCTAppDelegateBridgelessFeatureFlags : public facebook::react::ReactNativeFeatureFlagsDefaults {
|
||||
class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefaults {
|
||||
public:
|
||||
bool enableBridgelessArchitecture() override
|
||||
{
|
||||
@@ -320,12 +309,16 @@ class RCTAppDelegateBridgelessFeatureFlags : public facebook::react::ReactNative
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool useNativeViewConfigsInBridgelessMode() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
- (void)_setUpFeatureFlags
|
||||
{
|
||||
if ([self bridgelessEnabled]) {
|
||||
facebook::react::ReactNativeFeatureFlags::override(std::make_unique<RCTAppDelegateBridgelessFeatureFlags>());
|
||||
ReactNativeFeatureFlags::override(std::make_unique<RCTAppDelegateBridgelessFeatureFlags>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,9 @@ RCTAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary
|
||||
id<RCTSurfaceProtocol> surface = [[RCTFabricSurface alloc] initWithBridge:bridge
|
||||
moduleName:moduleName
|
||||
initialProperties:initialProperties];
|
||||
return [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
|
||||
UIView *rootView = [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
|
||||
[surface start];
|
||||
return rootView;
|
||||
}
|
||||
return [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol RCTArchConfiguratorProtocol
|
||||
/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off.
|
||||
///
|
||||
/// @note: This is required to be rendering on Fabric (i.e. on the New Architecture).
|
||||
/// @return: `true` if the Turbo Native Module are enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)turboModuleEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether the App will use the Fabric renderer of the New Architecture or not.
|
||||
///
|
||||
/// @return: `true` if the Fabric Renderer is enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)fabricEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether React Native's new initialization layer is enabled.
|
||||
///
|
||||
/// @return: `true` if the new initialization layer is enabled. Otherwise returns `false`.
|
||||
- (BOOL)bridgelessEnabled __attribute__((deprecated("Use newArchEnabled instead")));
|
||||
|
||||
/// This method controls whether React Native uses new Architecture.
|
||||
///
|
||||
/// @return: `true` if the new architecture is enabled. Otherwise returns `false`.
|
||||
- (BOOL)newArchEnabled;
|
||||
@end
|
||||
@@ -161,12 +161,9 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
initialProperties:(NSDictionary *)initialProperties
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSDictionary *initProps = updateInitialProps(initialProperties, self->_configuration.fabricEnabled);
|
||||
|
||||
if (self->_configuration.bridgelessEnabled) {
|
||||
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
|
||||
RCTSetUseNativeViewConfigsInBridgelessMode(self->_configuration.fabricEnabled);
|
||||
NSDictionary *initProps = updateInitialProps(initialProperties, _configuration.fabricEnabled);
|
||||
|
||||
if (_configuration.bridgelessEnabled) {
|
||||
// Enable TurboModule interop by default in Bridgeless mode
|
||||
RCTEnableTurboModuleInterop(YES);
|
||||
RCTEnableTurboModuleInteropBridgeProxy(YES);
|
||||
@@ -175,9 +172,8 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
|
||||
RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];
|
||||
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
|
||||
initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView =
|
||||
[[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
|
||||
|
||||
surfaceHostingProxyRootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
if (self->_configuration.customizeRootView != nil) {
|
||||
@@ -190,8 +186,8 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
[self createBridgeAdapterIfNeeded];
|
||||
|
||||
UIView *rootView;
|
||||
if (self->_configuration.createRootViewWithBridge != nil) {
|
||||
rootView = self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
|
||||
if (_configuration.createRootViewWithBridge != nil) {
|
||||
rootView = _configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
|
||||
} else {
|
||||
rootView = [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTRootView;
|
||||
|
||||
@protocol RCTUIConfiguratorProtocol
|
||||
/**
|
||||
* The default `RCTColorSpace` for the app. It defaults to `RCTColorSpaceSRGB`.
|
||||
*/
|
||||
- (RCTColorSpace)defaultColorSpace;
|
||||
|
||||
/**
|
||||
* This method can be used to customize the rootView that is passed to React Native.
|
||||
* A typical example is to override this method in the AppDelegate to change the background color.
|
||||
* To achieve this, add in your `AppDelegate.mm`:
|
||||
* ```
|
||||
* - (void)customizeRootView:(RCTRootView *)rootView
|
||||
* {
|
||||
* rootView.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traitCollection) {
|
||||
* if ([traitCollection userInterfaceStyle] == UIUserInterfaceStyleDark) {
|
||||
* return [UIColor blackColor];
|
||||
* } else {
|
||||
* return [UIColor whiteColor];
|
||||
* }
|
||||
* }];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @parameter: rootView - The root view to customize.
|
||||
*/
|
||||
- (void)customizeRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* It creates the RootViewController.
|
||||
* By default, it creates a new instance of a `UIViewController`.
|
||||
* You can override it to provide your own initial ViewController.
|
||||
*
|
||||
* @return: an instance of `UIViewController`.
|
||||
*/
|
||||
- (UIViewController *)createRootViewController;
|
||||
|
||||
/**
|
||||
* It assigns the rootView to the rootViewController
|
||||
* By default, it assigns the rootView to the view property of the rootViewController
|
||||
* If you are not using a simple UIViewController, then there could be other methods to use to setup the rootView.
|
||||
* For example: UISplitViewController requires `setViewController(_:for:)`
|
||||
*/
|
||||
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
|
||||
@end
|
||||
+25
-11
@@ -142,7 +142,11 @@ let inExceptionHandler = false;
|
||||
*/
|
||||
function handleException(e: mixed, isFatal: boolean) {
|
||||
// TODO(T196834299): We should really use a c++ turbomodule for this
|
||||
if (!global.RN$handleException || !global.RN$handleException(e, isFatal)) {
|
||||
const reportToConsole = true;
|
||||
if (
|
||||
!global.RN$handleException ||
|
||||
!global.RN$handleException(e, isFatal, reportToConsole)
|
||||
) {
|
||||
let error: Error;
|
||||
if (e instanceof Error) {
|
||||
error = e;
|
||||
@@ -158,7 +162,7 @@ function handleException(e: mixed, isFatal: boolean) {
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
reportException(error, isFatal, /*reportToConsole*/ true);
|
||||
reportException(error, isFatal, reportToConsole);
|
||||
} finally {
|
||||
inExceptionHandler = false;
|
||||
}
|
||||
@@ -173,7 +177,10 @@ function reactConsoleErrorHandler(...args) {
|
||||
if (!console.reportErrorsAsExceptions) {
|
||||
return;
|
||||
}
|
||||
if (inExceptionHandler) {
|
||||
if (
|
||||
inExceptionHandler ||
|
||||
(global.RN$inExceptionHandler && global.RN$inExceptionHandler())
|
||||
) {
|
||||
// The fundamental trick here is that are multiple entry point to logging errors:
|
||||
// (see D19743075 for more background)
|
||||
//
|
||||
@@ -227,14 +234,21 @@ function reactConsoleErrorHandler(...args) {
|
||||
error.name = 'console.error';
|
||||
}
|
||||
|
||||
reportException(
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
error,
|
||||
false, // isFatal
|
||||
false, // reportToConsole
|
||||
);
|
||||
const isFatal = false;
|
||||
const reportToConsole = false;
|
||||
if (
|
||||
!global.RN$handleException ||
|
||||
!global.RN$handleException(error, isFatal, reportToConsole)
|
||||
) {
|
||||
reportException(
|
||||
/* $FlowFixMe[class-object-subtyping] added when improving typing for this
|
||||
* parameters */
|
||||
// $FlowFixMe[incompatible-call]
|
||||
error,
|
||||
isFatal,
|
||||
reportToConsole,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+18
-29
@@ -60,18 +60,28 @@ export interface ImagePropsIOS {
|
||||
interface ImagePropsAndroid {
|
||||
/**
|
||||
* The mechanism that should be used to resize the image when the image's dimensions
|
||||
* differ from the image view's dimensions. Defaults to auto.
|
||||
* differ from the image view's dimensions. Defaults to `auto`.
|
||||
*
|
||||
* 'auto': Use heuristics to pick between resize and scale.
|
||||
* - `auto`: Use heuristics to pick between `resize` and `scale`.
|
||||
*
|
||||
* 'resize': A software operation which changes the encoded image in memory before it gets decoded.
|
||||
* This should be used instead of scale when the image is much larger than the view.
|
||||
* - `resize`: A software operation which changes the encoded image in memory before it
|
||||
* gets decoded. This should be used instead of `scale` when the image is much larger
|
||||
* than the view.
|
||||
*
|
||||
* 'scale': The image gets drawn downscaled or upscaled. Compared to resize, scale is faster (usually hardware accelerated)
|
||||
* and produces higher quality images. This should be used if the image is smaller than the view.
|
||||
* It should also be used if the image is slightly bigger than the view.
|
||||
* - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is
|
||||
* faster (usually hardware accelerated) and produces higher quality images. This
|
||||
* should be used if the image is smaller than the view. It should also be used if the
|
||||
* image is slightly bigger than the view.
|
||||
*
|
||||
* - `none`: No sampling is performed and the image is displayed in its full resolution. This
|
||||
* should only be used in rare circumstances because it is considered unsafe as Android will
|
||||
* throw a runtime exception when trying to render images that consume too much memory.
|
||||
*
|
||||
* More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html.
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
resizeMethod?: 'auto' | 'resize' | 'scale' | undefined;
|
||||
resizeMethod?: 'auto' | 'resize' | 'scale' | 'none' | undefined;
|
||||
|
||||
/**
|
||||
* Duration of fade in animation in ms. Defaults to 300
|
||||
@@ -193,27 +203,6 @@ export interface ImagePropsBase
|
||||
*/
|
||||
resizeMode?: ImageResizeMode | undefined;
|
||||
|
||||
/**
|
||||
* The mechanism that should be used to resize the image when the image's dimensions
|
||||
* differ from the image view's dimensions. Defaults to `auto`.
|
||||
*
|
||||
* - `auto`: Use heuristics to pick between `resize` and `scale`.
|
||||
*
|
||||
* - `resize`: A software operation which changes the encoded image in memory before it
|
||||
* gets decoded. This should be used instead of `scale` when the image is much larger
|
||||
* than the view.
|
||||
*
|
||||
* - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is
|
||||
* faster (usually hardware accelerated) and produces higher quality images. This
|
||||
* should be used if the image is smaller than the view. It should also be used if the
|
||||
* image is slightly bigger than the view.
|
||||
*
|
||||
* More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html.
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
resizeMethod?: 'auto' | 'resize' | 'scale' | undefined;
|
||||
|
||||
/**
|
||||
* The image source (either a remote URL or a local file resource).
|
||||
*
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ export interface ImageURISource {
|
||||
* to a URL load request, no attempt is made to load the data from the originating source,
|
||||
* and the load is considered to have failed.
|
||||
*
|
||||
* @platform ios
|
||||
* @platform ios (for `force-cache`)
|
||||
*/
|
||||
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached' | undefined;
|
||||
/**
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ export interface ImageURISource {
|
||||
* to a URL load request, no attempt is made to load the data from the originating source,
|
||||
* and the load is considered to have failed.
|
||||
*
|
||||
* @platform ios
|
||||
* @platform ios (for `force-cache`)
|
||||
*/
|
||||
+cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached');
|
||||
|
||||
|
||||
+2
-2
@@ -480,10 +480,10 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
this._checkProps(this.props);
|
||||
}
|
||||
|
||||
_listRef: ?React.ElementRef<typeof VirtualizedList>;
|
||||
_listRef: ?VirtualizedList;
|
||||
_virtualizedListPairs: Array<ViewabilityConfigCallbackPair> = [];
|
||||
|
||||
_captureRef = (ref: ?React.ElementRef<typeof VirtualizedList>) => {
|
||||
_captureRef = (ref: ?VirtualizedList) => {
|
||||
this._listRef = ref;
|
||||
};
|
||||
|
||||
|
||||
+26
-17
@@ -37,6 +37,8 @@ const COLORS = {
|
||||
'ansi-bright-white': 'rgb(247, 247, 247)',
|
||||
};
|
||||
|
||||
const LRM = '\u200E'; // Left-to-Right Mark
|
||||
|
||||
export default function Ansi({
|
||||
text,
|
||||
style,
|
||||
@@ -80,25 +82,28 @@ export default function Ansi({
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.container}>
|
||||
{parsedLines.map((items, i) => (
|
||||
<View style={styles.line} key={i}>
|
||||
{items.map((bundle, key) => {
|
||||
const textStyle =
|
||||
bundle.fg && COLORS[bundle.fg]
|
||||
? {
|
||||
backgroundColor: bundle.bg && COLORS[bundle.bg],
|
||||
color: bundle.fg && COLORS[bundle.fg],
|
||||
}
|
||||
: {
|
||||
backgroundColor: bundle.bg && COLORS[bundle.bg],
|
||||
};
|
||||
return (
|
||||
<Text style={[style, textStyle]} key={key}>
|
||||
{getText(bundle.content, key)}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
<Text>
|
||||
{LRM}
|
||||
{items.map((bundle, key) => {
|
||||
const textStyle =
|
||||
bundle.fg && COLORS[bundle.fg]
|
||||
? {
|
||||
backgroundColor: bundle.bg && COLORS[bundle.bg],
|
||||
color: bundle.fg && COLORS[bundle.fg],
|
||||
}
|
||||
: {
|
||||
backgroundColor: bundle.bg && COLORS[bundle.bg],
|
||||
};
|
||||
return (
|
||||
<Text style={[style, textStyle]} key={key}>
|
||||
{getText(bundle.content, key)}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
@@ -106,6 +111,10 @@ export default function Ansi({
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
minWidth: '100%',
|
||||
direction: 'ltr',
|
||||
},
|
||||
line: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
|
||||
@@ -59,7 +59,9 @@ function LogBoxInspectorCodeFrame(props: Props): React.Node {
|
||||
<LogBoxInspectorSection heading="Source" action={<AppInfo />}>
|
||||
<View style={styles.box}>
|
||||
<View style={styles.frame}>
|
||||
<ScrollView horizontal>
|
||||
<ScrollView
|
||||
horizontal
|
||||
contentContainerStyle={styles.contentContainer}>
|
||||
<AnsiHighlight style={styles.content} text={codeFrame.content} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
@@ -138,6 +140,9 @@ const styles = StyleSheet.create({
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
contentContainer: {
|
||||
minWidth: '100%',
|
||||
},
|
||||
content: {
|
||||
color: LogBoxStyle.getTextColor(1),
|
||||
fontSize: 12,
|
||||
|
||||
+10
@@ -26,6 +26,11 @@ exports[`LogBoxInspectorCodeFrame should render a code frame 1`] = `
|
||||
}
|
||||
>
|
||||
<ScrollView
|
||||
contentContainerStyle={
|
||||
Object {
|
||||
"minWidth": "100%",
|
||||
}
|
||||
}
|
||||
horizontal={true}
|
||||
>
|
||||
<Ansi
|
||||
@@ -108,6 +113,11 @@ exports[`LogBoxInspectorCodeFrame should render a code frame without a location
|
||||
}
|
||||
>
|
||||
<ScrollView
|
||||
contentContainerStyle={
|
||||
Object {
|
||||
"minWidth": "100%",
|
||||
}
|
||||
}
|
||||
horizontal={true}
|
||||
>
|
||||
<Ansi
|
||||
|
||||
@@ -36,6 +36,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
||||
{
|
||||
if (![self isDescendantOfView:scrollView]) {
|
||||
// View is outside scroll view
|
||||
scrollView.firstResponderViewOutsideScrollView = self.backedTextInputView;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -683,7 +683,7 @@ exports[`public API should not change unintentionally Libraries/Animated/bezier.
|
||||
exports[`public API should not change unintentionally Libraries/Animated/components/AnimatedFlatList.js 1`] = `
|
||||
"declare export default AnimatedComponentType<
|
||||
React.ElementConfig<typeof FlatList>,
|
||||
React.ElementRef<typeof FlatList>,
|
||||
FlatList<mixed>,
|
||||
>;
|
||||
"
|
||||
`;
|
||||
@@ -5725,7 +5725,7 @@ declare class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
setNativeProps(props: { [string]: mixed, ... }): void;
|
||||
constructor(props: Props<ItemT>): void;
|
||||
componentDidUpdate(prevProps: Props<ItemT>): void;
|
||||
_listRef: ?React.ElementRef<typeof VirtualizedList>;
|
||||
_listRef: ?VirtualizedList;
|
||||
_virtualizedListPairs: Array<ViewabilityConfigCallbackPair>;
|
||||
_captureRef: $FlowFixMe;
|
||||
_checkProps(props: Props<ItemT>): void;
|
||||
|
||||
@@ -18,6 +18,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTBridgeProxy : NSProxy
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
|
||||
@@ -41,6 +41,8 @@ using namespace facebook;
|
||||
void *_runtime;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
@@ -61,8 +63,10 @@ using namespace facebook;
|
||||
_runtime = runtime;
|
||||
_launchOptions = [launchOptions copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue
|
||||
{
|
||||
@@ -414,6 +418,10 @@ using namespace facebook;
|
||||
RCTViewRegistry *_viewRegistry;
|
||||
NSMutableDictionary<NSNumber *, UIView *> *_legacyViewRegistry;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
||||
|
||||
- (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
|
||||
{
|
||||
self = [super self];
|
||||
@@ -424,6 +432,8 @@ using namespace facebook;
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/**
|
||||
* RCTViewRegistry
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
std::shared_ptr<facebook::react::CallInvoker> _callInvoker;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithCallInvoker:nullptr];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCallInvoker:(std::shared_ptr<facebook::react::CallInvoker>)callInvoker
|
||||
{
|
||||
if (self = [super init]) {
|
||||
|
||||
@@ -50,15 +50,3 @@ RCT_EXTERN NSString *const RCTDidInitializeModuleNotification;
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTGetDispatchW3CPointerEvents(void);
|
||||
RCT_EXTERN void RCTSetDispatchW3CPointerEvents(BOOL value);
|
||||
|
||||
/*
|
||||
* Memory Pressure Unloading Level
|
||||
*/
|
||||
RCT_EXTERN int RCTGetMemoryPressureUnloadLevel(void);
|
||||
RCT_EXTERN void RCTSetMemoryPressureUnloadLevel(int value);
|
||||
|
||||
/*
|
||||
* Use native view configs in bridgeless mode
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void);
|
||||
RCT_EXTERN void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value);
|
||||
|
||||
@@ -38,34 +38,3 @@ void RCTSetDispatchW3CPointerEvents(BOOL value)
|
||||
{
|
||||
RCTDispatchW3CPointerEvents = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory Pressure Unloading Level for experimentation only.
|
||||
* Default is 15, which is TRIM_MEMORY_RUNNING_CRITICAL.
|
||||
*/
|
||||
static int RCTMemoryPressureUnloadLevel = 15;
|
||||
|
||||
int RCTGetMemoryPressureUnloadLevel(void)
|
||||
{
|
||||
return RCTMemoryPressureUnloadLevel;
|
||||
}
|
||||
|
||||
void RCTSetMemoryPressureUnloadLevel(int value)
|
||||
{
|
||||
RCTMemoryPressureUnloadLevel = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use native view configs in bridgeless mode
|
||||
*/
|
||||
static BOOL RCTUseNativeViewConfigsInBridgelessMode = NO;
|
||||
|
||||
BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void)
|
||||
{
|
||||
return RCTUseNativeViewConfigsInBridgelessMode;
|
||||
}
|
||||
|
||||
void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value)
|
||||
{
|
||||
RCTUseNativeViewConfigsInBridgelessMode = value;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ typedef id<RCTBridgeModule> (^RCTBridgeModuleProvider)(void);
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
viewRegistry_DEPRECATED:(RCTViewRegistry *)viewRegistry_DEPRECATED
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules;
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithModuleInstance:(id<RCTBridgeModule>)instance
|
||||
bridge:(RCTBridge *)bridge
|
||||
|
||||
@@ -53,8 +53,21 @@ int32_t getUniqueId()
|
||||
@synthesize instance = _instance;
|
||||
@synthesize methodQueue = _methodQueue;
|
||||
|
||||
- (void)setUp
|
||||
- (void)_setUpWithBridge:(RCTBridge *)bridge
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
viewRegistry_DEPRECATED:(RCTViewRegistry *)viewRegistry_DEPRECATED
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules
|
||||
{
|
||||
// start with the ivars
|
||||
{
|
||||
_bridge = bridge;
|
||||
_moduleRegistry = moduleRegistry;
|
||||
_viewRegistry_DEPRECATED = viewRegistry_DEPRECATED;
|
||||
_bundleManager = bundleManager;
|
||||
_callableJSModules = callableJSModules;
|
||||
}
|
||||
|
||||
_implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)];
|
||||
|
||||
// If a module overrides `constantsToExport` and doesn't implement `requiresMainQueueSetup`, then we must assume
|
||||
@@ -86,35 +99,17 @@ int32_t getUniqueId()
|
||||
viewRegistry_DEPRECATED:(RCTViewRegistry *)viewRegistry_DEPRECATED
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules
|
||||
{
|
||||
return [self _initWithModuleClass:moduleClass
|
||||
moduleProvider:^id<RCTBridgeModule> {
|
||||
return [moduleClass new];
|
||||
}
|
||||
bridge:bridge
|
||||
moduleRegistry:moduleRegistry
|
||||
viewRegistry_DEPRECATED:viewRegistry_DEPRECATED
|
||||
bundleManager:bundleManager
|
||||
callableJSModules:callableJSModules];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithModuleClass:(Class)moduleClass
|
||||
moduleProvider:(RCTBridgeModuleProvider)moduleProvider
|
||||
bridge:(RCTBridge *)bridge
|
||||
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
|
||||
viewRegistry_DEPRECATED:(RCTViewRegistry *)viewRegistry_DEPRECATED
|
||||
bundleManager:(RCTBundleManager *)bundleManager
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_moduleClass = moduleClass;
|
||||
_moduleProvider = [moduleProvider copy];
|
||||
_moduleRegistry = moduleRegistry;
|
||||
_viewRegistry_DEPRECATED = viewRegistry_DEPRECATED;
|
||||
_bundleManager = bundleManager;
|
||||
_callableJSModules = callableJSModules;
|
||||
[self setUp];
|
||||
_moduleProvider = [^id<RCTBridgeModule> {
|
||||
return [moduleClass new];
|
||||
} copy];
|
||||
[self _setUpWithBridge:bridge
|
||||
moduleRegistry:moduleRegistry
|
||||
viewRegistry_DEPRECATED:viewRegistry_DEPRECATED
|
||||
bundleManager:bundleManager
|
||||
callableJSModules:callableJSModules];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -127,14 +122,13 @@ int32_t getUniqueId()
|
||||
callableJSModules:(RCTCallableJSModules *)callableJSModules
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_instance = instance;
|
||||
_moduleClass = [instance class];
|
||||
_moduleRegistry = moduleRegistry;
|
||||
_viewRegistry_DEPRECATED = viewRegistry_DEPRECATED;
|
||||
_bundleManager = bundleManager;
|
||||
_callableJSModules = callableJSModules;
|
||||
[self setUp];
|
||||
[self _setUpWithBridge:bridge
|
||||
moduleRegistry:moduleRegistry
|
||||
viewRegistry_DEPRECATED:viewRegistry_DEPRECATED
|
||||
bundleManager:bundleManager
|
||||
callableJSModules:callableJSModules];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+7
@@ -39,6 +39,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration;
|
||||
@property (nonatomic, assign) CGSize minimumSize;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
- (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface
|
||||
sizeMeasureMode:(RCTSurfaceSizeMeasureMode)sizeMeasureMode NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)cancelTouches;
|
||||
|
||||
+2
-5
@@ -53,11 +53,8 @@ static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSiz
|
||||
|
||||
- (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface
|
||||
{
|
||||
if (self = [super initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]) {
|
||||
[surface start];
|
||||
}
|
||||
return self;
|
||||
return [super initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
|
||||
|
||||
@@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)reportJsException:(nullable NSString *)message
|
||||
stack:(nullable NSArray<NSDictionary *> *)stack
|
||||
exceptionId:(double)exceptionId
|
||||
isFatal:(bool)isFatal;
|
||||
isFatal:(bool)isFatal __attribute__((deprecated));
|
||||
|
||||
@property (nonatomic, weak) id<RCTExceptionsManagerDelegate> delegate;
|
||||
|
||||
|
||||
@@ -371,7 +371,8 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
// in case if some other tread resets it.
|
||||
auto reactInstance = _reactInstance;
|
||||
if (reactInstance) {
|
||||
int unloadLevel = RCTGetMemoryPressureUnloadLevel();
|
||||
// Memory Pressure Unloading Level 15 represents TRIM_MEMORY_RUNNING_CRITICAL.
|
||||
int unloadLevel = 15;
|
||||
reactInstance->handleMemoryPressure(unloadLevel);
|
||||
}
|
||||
}
|
||||
|
||||
-7
@@ -100,13 +100,6 @@ using namespace facebook::react;
|
||||
const auto &imageRequest = _state->getData().getImageRequest();
|
||||
auto &observerCoordinator = imageRequest.getObserverCoordinator();
|
||||
observerCoordinator.removeObserver(_imageResponseObserverProxy);
|
||||
// Cancelling image request because we are no longer observing it.
|
||||
// This is not 100% correct place to do this because we may want to
|
||||
// re-create RCTImageComponentView with the same image and if it
|
||||
// was cancelled before downloaded, download is not resumed.
|
||||
// This will only become issue if we decouple life cycle of a
|
||||
// ShadowNode from ComponentView, which is not something we do now.
|
||||
imageRequest.cancel();
|
||||
}
|
||||
|
||||
_state = state;
|
||||
|
||||
+3
@@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
|
||||
@property (nonatomic, assign) CGRect firstResponderFocus;
|
||||
|
||||
/** newly-activated text input outside of the scroll view */
|
||||
@property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
|
||||
|
||||
/*
|
||||
* Returns the subview of the scroll view that the component uses to mount all subcomponents into. That's useful to
|
||||
* separate component views from auxiliary views to be able to reliably implement pull-to-refresh- and RTL-related
|
||||
|
||||
+17
-13
@@ -182,6 +182,7 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
|
||||
UIViewAnimationCurve curve =
|
||||
(UIViewAnimationCurve)[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
|
||||
CGRect keyboardEndFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
CGRect keyboardBeginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
|
||||
|
||||
CGPoint absoluteViewOrigin = [self convertPoint:self.bounds.origin toView:nil];
|
||||
CGFloat scrollViewLowerY = isInverted ? absoluteViewOrigin.y : absoluteViewOrigin.y + self.bounds.size.height;
|
||||
@@ -203,21 +204,24 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
|
||||
from:self
|
||||
forEvent:nil]) {
|
||||
if (CGRectEqualToRect(_firstResponderFocus, CGRectNull)) {
|
||||
// Text input view is outside of the scroll view.
|
||||
return;
|
||||
}
|
||||
UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
|
||||
if (inputAccessoryView) {
|
||||
// Text input view is within the inputAccessoryView.
|
||||
contentDiff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y;
|
||||
}
|
||||
} else {
|
||||
CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
|
||||
|
||||
CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
|
||||
if (CGRectIsNull(viewIntersection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CGRectIsNull(viewIntersection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inner text field focused
|
||||
CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
|
||||
if (focusEnd > keyboardEndFrame.origin.y) {
|
||||
// Text field active region is below visible area with keyboard - update diff to bring into view
|
||||
contentDiff = keyboardEndFrame.origin.y - focusEnd;
|
||||
// Inner text field focused
|
||||
CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
|
||||
if (focusEnd > keyboardEndFrame.origin.y) {
|
||||
// Text field active region is below visible area with keyboard - update diff to bring into view
|
||||
contentDiff = keyboardEndFrame.origin.y - focusEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -126,6 +126,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
|
||||
{
|
||||
if (![self isDescendantOfView:scrollView.scrollView] || !_backedTextInputView.isFirstResponder) {
|
||||
// View is outside scroll view or it's not a first responder.
|
||||
scrollView.firstResponderViewOutsideScrollView = _backedTextInputView;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,9 +114,6 @@ class LayoutAnimationDelegateProxy : public LayoutAnimationStatusDelegate, publi
|
||||
- (instancetype)initWithToolbox:(SchedulerToolbox)toolbox
|
||||
{
|
||||
if (self = [super init]) {
|
||||
auto reactNativeConfig =
|
||||
toolbox.contextContainer->at<std::shared_ptr<const ReactNativeConfig>>("ReactNativeConfig");
|
||||
|
||||
_delegateProxy = std::make_unique<SchedulerDelegateProxy>((__bridge void *)self);
|
||||
|
||||
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnIOS()) {
|
||||
|
||||
@@ -620,22 +620,22 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
|
||||
if (eventEmitter != nil) {
|
||||
switch (eventType) {
|
||||
case RCTPointerEventTypeStart: {
|
||||
eventEmitter->onPointerDown(pointerEvent);
|
||||
eventEmitter->onPointerDown(std::move(pointerEvent));
|
||||
break;
|
||||
}
|
||||
case RCTPointerEventTypeMove: {
|
||||
eventEmitter->onPointerMove(pointerEvent);
|
||||
eventEmitter->onPointerMove(std::move(pointerEvent));
|
||||
break;
|
||||
}
|
||||
case RCTPointerEventTypeEnd: {
|
||||
eventEmitter->onPointerUp(pointerEvent);
|
||||
if (pointerEvent.isPrimary && pointerEvent.button == 0 && IsPointerWithinInitialTree(activePointer)) {
|
||||
eventEmitter->onClick(pointerEvent);
|
||||
eventEmitter->onClick(std::move(pointerEvent));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RCTPointerEventTypeCancel: {
|
||||
eventEmitter->onPointerCancel(pointerEvent);
|
||||
eventEmitter->onPointerCancel(std::move(pointerEvent));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -761,10 +761,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
|
||||
if (eventEmitter != nil) {
|
||||
switch (recognizer.state) {
|
||||
case UIGestureRecognizerStateEnded:
|
||||
eventEmitter->onPointerLeave(event);
|
||||
eventEmitter->onPointerLeave(std::move(event));
|
||||
break;
|
||||
default:
|
||||
eventEmitter->onPointerMove(event);
|
||||
eventEmitter->onPointerMove(std::move(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#import <React/RCTSurfaceView.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
#import <react/renderer/componentregistry/ComponentDescriptorFactory.h>
|
||||
#import <react/renderer/components/text/BaseTextProps.h>
|
||||
@@ -226,8 +225,6 @@ using namespace facebook::react;
|
||||
|
||||
- (RCTScheduler *)_createScheduler
|
||||
{
|
||||
auto reactNativeConfig = _contextContainer->at<std::shared_ptr<const ReactNativeConfig>>("ReactNativeConfig");
|
||||
|
||||
auto componentRegistryFactory =
|
||||
[factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)](
|
||||
const EventDispatcher::Weak &eventDispatcher, const ContextContainer::Shared &contextContainer) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
/*
|
||||
* Copyright (c) 2015 Daniel Kirchner
|
||||
*
|
||||
@@ -27,10 +28,6 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// magic constants :-)
|
||||
static const uint64_t PRIME1 = 11400714785074694791ULL;
|
||||
static const uint64_t PRIME2 = 14029467366897019727ULL;
|
||||
@@ -106,13 +103,18 @@ NS_INLINE uint64_t h32bytes(const char *p, uint64_t len, const uint64_t seed)
|
||||
return h32bytes_compute(p, len, seed + PRIME1 + PRIME2, seed + PRIME2, seed, seed - PRIME1);
|
||||
}
|
||||
|
||||
/*
|
||||
Primary APIs
|
||||
*/
|
||||
|
||||
NS_INLINE uint64_t meta_xxhash64(const char *p, uint64_t len, uint64_t seed)
|
||||
{
|
||||
return finalize((len >= 32 ? h32bytes(p, len, seed) : seed + PRIME5) + len, p + (len & ~0x1FULL), len & 0x1FULL);
|
||||
}
|
||||
|
||||
NS_INLINE uint64_t FBxxHash64(const char *p, uint64_t len, uint64_t seed)
|
||||
{
|
||||
return finalize((len >= 32 ? h32bytes(p, len, seed) : seed + PRIME5) + len, p + (len & ~0x1F), len & 0x1F);
|
||||
return meta_xxhash64(p, len, seed);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
+4
-3
@@ -9,6 +9,7 @@
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <React/RCTSurfacePresenterStub.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge+Private.h"
|
||||
@@ -1154,10 +1155,10 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
||||
void (^mountingBlock)(void) = ^{
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
|
||||
@try {
|
||||
for (RCTViewManagerUIBlock block in previousPendingUIBlocks) {
|
||||
@@ -1438,7 +1439,7 @@ NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
|
||||
// lazifyViewManagerConfig function in JS. This fuction uses NativeModules global object that is not available in the
|
||||
// New Architecture. To make native view configs work in the New Architecture we will populate these properties in
|
||||
// native.
|
||||
if (RCTGetUseNativeViewConfigsInBridgelessMode()) {
|
||||
if (facebook::react::ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode()) {
|
||||
moduleConstants[@"Commands"] = viewConfig[@"Commands"];
|
||||
moduleConstants[@"Constants"] = viewConfig[@"Constants"];
|
||||
}
|
||||
@@ -19,7 +19,7 @@ BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets)
|
||||
|
||||
BOOL RCTCornerRadiiAreEqualAndSymmetrical(RCTCornerRadii cornerRadii)
|
||||
{
|
||||
return cornerRadii.topLeftHorizontal == cornerRadii.topLeftHorizontal &&
|
||||
return cornerRadii.topLeftHorizontal == cornerRadii.topLeftVertical &&
|
||||
cornerRadii.topRightHorizontal == cornerRadii.topRightVertical &&
|
||||
cornerRadii.bottomLeftHorizontal == cornerRadii.bottomLeftVertical &&
|
||||
cornerRadii.bottomRightHorizontal == cornerRadii.bottomRightVertical &&
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@
|
||||
#import "RCTComponentData.h"
|
||||
|
||||
#import <objc/message.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
@@ -288,9 +289,9 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
|
||||
case _value: { \
|
||||
__block BOOL setDefaultValue = NO; \
|
||||
__block _type defaultValue; \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
_type (*get)(id, SEL) = (typeof(get))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
_type (*convert)(id, SEL, id) = (__typeof(convert))objc_msgSend; \
|
||||
_type (*get)(id, SEL) = (__typeof(get))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (__typeof(set))objc_msgSend; \
|
||||
setterBlock = ^(id target, id json) { \
|
||||
if (json) { \
|
||||
if (!setDefaultValue && target) { \
|
||||
@@ -522,7 +523,7 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
|
||||
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : RCTViewManagerModuleNameForClass(superClass),
|
||||
}];
|
||||
|
||||
if (RCTGetUseNativeViewConfigsInBridgelessMode()) {
|
||||
if (facebook::react::ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode()) {
|
||||
result[@"Commands"] = [self commandsForViewMangerClass:managerClass methods:methods methodCount:count];
|
||||
result[@"Constants"] = [self constantsForViewMangerClass:managerClass];
|
||||
}
|
||||
@@ -50,6 +50,8 @@
|
||||
@property (nonatomic, assign) BOOL inverted;
|
||||
/** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
|
||||
@property (nonatomic, assign) CGRect firstResponderFocus;
|
||||
/** newly-activated text input outside of the scroll view */
|
||||
@property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
|
||||
|
||||
// NOTE: currently these event props are only declared so we can export the
|
||||
// event names to JS - we don't call the blocks directly because scroll events
|
||||
|
||||
@@ -338,6 +338,12 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
|
||||
if (!didFocusExternalTextField && focusEnd > endFrame.origin.y) {
|
||||
// Text field active region is below visible area with keyboard - update diff to bring into view
|
||||
contentDiff = endFrame.origin.y - focusEnd;
|
||||
} else {
|
||||
UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
|
||||
if (inputAccessoryView) {
|
||||
// Text input view is within the inputAccessoryView.
|
||||
contentDiff = endFrame.origin.y - beginFrame.origin.y;
|
||||
}
|
||||
}
|
||||
} else if (endFrame.origin.y <= beginFrame.origin.y) {
|
||||
// Keyboard opened for other reason
|
||||
|
||||
@@ -3377,14 +3377,26 @@ public final class com/facebook/react/modules/fresco/FrescoModule$Companion {
|
||||
public final fun hasBeenInitialized ()Z
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/fresco/ImageCacheControl : java/lang/Enum {
|
||||
public static final field DEFAULT Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
public static final field ONLY_IF_CACHED Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
public static final field RELOAD Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
public static fun values ()[Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/fresco/ReactNetworkImageRequest : com/facebook/imagepipeline/request/ImageRequest {
|
||||
public static final field Companion Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest$Companion;
|
||||
public synthetic fun <init> (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public synthetic fun <init> (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/modules/fresco/ImageCacheControl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public static final fun fromBuilderWithHeaders (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest;
|
||||
public static final fun fromBuilderWithHeaders (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/modules/fresco/ImageCacheControl;)Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/fresco/ReactNetworkImageRequest$Companion {
|
||||
public final fun fromBuilderWithHeaders (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest;
|
||||
public final fun fromBuilderWithHeaders (Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/modules/fresco/ImageCacheControl;)Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest;
|
||||
public static synthetic fun fromBuilderWithHeaders$default (Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest$Companion;Lcom/facebook/imagepipeline/request/ImageRequestBuilder;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/modules/fresco/ImageCacheControl;ILjava/lang/Object;)Lcom/facebook/react/modules/fresco/ReactNetworkImageRequest;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/fresco/SystraceRequestListener : com/facebook/imagepipeline/listener/BaseRequestListener {
|
||||
@@ -6707,8 +6719,10 @@ public class com/facebook/react/views/imagehelper/ImageSource {
|
||||
public fun <init> (Landroid/content/Context;Ljava/lang/String;)V
|
||||
public fun <init> (Landroid/content/Context;Ljava/lang/String;D)V
|
||||
public fun <init> (Landroid/content/Context;Ljava/lang/String;DD)V
|
||||
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;DDILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun <init> (Landroid/content/Context;Ljava/lang/String;DDLcom/facebook/react/modules/fresco/ImageCacheControl;)V
|
||||
public synthetic fun <init> (Landroid/content/Context;Ljava/lang/String;DDLcom/facebook/react/modules/fresco/ImageCacheControl;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCacheControl ()Lcom/facebook/react/modules/fresco/ImageCacheControl;
|
||||
public final fun getSize ()D
|
||||
public final fun getSource ()Ljava/lang/String;
|
||||
public static final fun getTransparentBitmapImageSource (Landroid/content/Context;)Lcom/facebook/react/views/imagehelper/ImageSource;
|
||||
@@ -6959,6 +6973,9 @@ public final class com/facebook/react/views/scroll/ReactHorizontalScrollContaine
|
||||
public static final field Companion Lcom/facebook/react/views/scroll/ReactHorizontalScrollContainerViewManager$Companion;
|
||||
public static final field REACT_CLASS Ljava/lang/String;
|
||||
public fun <init> ()V
|
||||
public synthetic fun createViewInstance (ILcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Landroid/view/View;
|
||||
public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View;
|
||||
public fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/view/ReactViewGroup;
|
||||
public fun getName ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
|
||||
@@ -533,9 +533,7 @@ android {
|
||||
"-DREACT_BUILD_DIR=$buildDir",
|
||||
"-DANDROID_STL=c++_shared",
|
||||
"-DANDROID_TOOLCHAIN=clang",
|
||||
// Due to https://github.com/android/ndk/issues/1693 we're losing Android
|
||||
// specific compilation flags. This can be removed once we moved to NDK 25/26
|
||||
"-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON")
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
|
||||
|
||||
targets(
|
||||
"reactnative",
|
||||
|
||||
@@ -41,7 +41,7 @@ if (PROJECT_ROOT_DIR)
|
||||
endif ()
|
||||
|
||||
file(GLOB input_SRC CONFIGURE_DEPENDS
|
||||
*.cpp
|
||||
${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
|
||||
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
|
||||
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
|
||||
|
||||
@@ -228,6 +228,7 @@ android {
|
||||
"-DHERMES_IS_ANDROID=True",
|
||||
"-DANDROID_STL=c++_shared",
|
||||
"-DANDROID_PIE=True",
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
|
||||
"-DIMPORT_HERMESC=${File(hermesBuildDir, "ImportHermesc.cmake").toString()}",
|
||||
"-DJSI_DIR=${jsiDir}",
|
||||
"-DHERMES_SLOW_DEBUG=False",
|
||||
@@ -235,10 +236,7 @@ android {
|
||||
"-DHERMES_RELEASE_VERSION=for RN ${version}",
|
||||
// We intentionally build Hermes with Intl support only. This is to simplify
|
||||
// the build setup and to avoid overcomplicating the build-type matrix.
|
||||
"-DHERMES_ENABLE_INTL=True",
|
||||
// Due to https://github.com/android/ndk/issues/1693 we're losing Android
|
||||
// specific compilation flags. This can be removed once we moved to NDK 25/26
|
||||
"-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON")
|
||||
"-DHERMES_ENABLE_INTL=True")
|
||||
|
||||
targets("libhermes")
|
||||
}
|
||||
@@ -262,11 +260,6 @@ android {
|
||||
// This has the (unlucky) side effect of letting AGP call the build
|
||||
// tasks `configureCMakeRelease` while is actually building the debug flavor.
|
||||
arguments("-DCMAKE_BUILD_TYPE=Release")
|
||||
// Adding -O3 to handle the issue here:
|
||||
// https://github.com/android/ndk/issues/1740#issuecomment-1198438260
|
||||
// The old NDK toolchain is not passing -O3 correctly for release CMake builds. This is
|
||||
// fixed in NDK 25 and can be removed once we're there.
|
||||
cppFlags("-O3")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,6 +323,11 @@ afterEvaluate {
|
||||
tasks.getByName("preBuild").dependsOn(prepareHeadersForPrefab)
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.compilerArgs.add("-Xlint:deprecation,unchecked")
|
||||
options.compilerArgs.add("-Werror")
|
||||
}
|
||||
|
||||
/* Publishing Configuration */
|
||||
apply(from = "../publish.gradle")
|
||||
|
||||
|
||||
-30
@@ -1,30 +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.debug.debugoverlay.model;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/** Tag for a debug overlay log message. Name must be unique. */
|
||||
@Immutable
|
||||
public class DebugOverlayTag {
|
||||
|
||||
/** Name of tag. */
|
||||
public final String name;
|
||||
|
||||
/** Description to display in settings. */
|
||||
public final String description;
|
||||
|
||||
/** Color for tag display. */
|
||||
public final int color;
|
||||
|
||||
public DebugOverlayTag(String name, String description, int color) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.debug.debugoverlay.model
|
||||
|
||||
/**
|
||||
* Tag for a debug overlay log message. Name must be unique.
|
||||
*
|
||||
* @param name Name of tag.
|
||||
* @param description Description to display in settings.
|
||||
* @param color Color for tag display.
|
||||
*/
|
||||
public class DebugOverlayTag(
|
||||
public val name: String,
|
||||
public val description: String,
|
||||
public val color: Int,
|
||||
)
|
||||
-38
@@ -1,38 +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.debug.tags;
|
||||
|
||||
import android.graphics.Color;
|
||||
import com.facebook.debug.debugoverlay.model.DebugOverlayTag;
|
||||
|
||||
/** Category for debug overlays. */
|
||||
public class ReactDebugOverlayTags {
|
||||
|
||||
public static final DebugOverlayTag PERFORMANCE =
|
||||
new DebugOverlayTag("Performance", "Markers for Performance", Color.GREEN);
|
||||
public static final DebugOverlayTag NAVIGATION =
|
||||
new DebugOverlayTag("Navigation", "Tag for navigation", Color.rgb(0x9C, 0x27, 0xB0));
|
||||
public static final DebugOverlayTag RN_CORE =
|
||||
new DebugOverlayTag("RN Core", "Tag for React Native Core", Color.BLACK);
|
||||
public static final DebugOverlayTag BRIDGE_CALLS =
|
||||
new DebugOverlayTag(
|
||||
"Bridge Calls", "JS to Java calls (warning: this is spammy)", Color.MAGENTA);
|
||||
public static final DebugOverlayTag NATIVE_MODULE =
|
||||
new DebugOverlayTag("Native Module", "Native Module init", Color.rgb(0x80, 0x00, 0x80));
|
||||
public static final DebugOverlayTag UI_MANAGER =
|
||||
new DebugOverlayTag(
|
||||
"UI Manager",
|
||||
"UI Manager View Operations (requires restart\nwarning: this is spammy)",
|
||||
Color.CYAN);
|
||||
public static final DebugOverlayTag FABRIC_UI_MANAGER =
|
||||
new DebugOverlayTag("FabricUIManager", "Fabric UI Manager View Operations", Color.CYAN);
|
||||
public static final DebugOverlayTag FABRIC_RECONCILER =
|
||||
new DebugOverlayTag("FabricReconciler", "Reconciler for Fabric", Color.CYAN);
|
||||
public static final DebugOverlayTag RELAY =
|
||||
new DebugOverlayTag("Relay", "including prefetching", Color.rgb(0xFF, 0x99, 0x00));
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.debug.tags
|
||||
|
||||
import android.graphics.Color
|
||||
import com.facebook.debug.debugoverlay.model.DebugOverlayTag
|
||||
|
||||
/** Category for debug overlays. */
|
||||
public object ReactDebugOverlayTags {
|
||||
@JvmField
|
||||
public val PERFORMANCE: DebugOverlayTag =
|
||||
DebugOverlayTag("Performance", "Markers for Performance", Color.GREEN)
|
||||
@JvmField
|
||||
public val NAVIGATION: DebugOverlayTag =
|
||||
DebugOverlayTag("Navigation", "Tag for navigation", Color.rgb(0x9C, 0x27, 0xB0))
|
||||
@JvmField
|
||||
public val RN_CORE: DebugOverlayTag =
|
||||
DebugOverlayTag("RN Core", "Tag for React Native Core", Color.BLACK)
|
||||
@JvmField
|
||||
public val BRIDGE_CALLS: DebugOverlayTag =
|
||||
DebugOverlayTag("Bridge Calls", "JS to Java calls (warning: this is spammy)", Color.MAGENTA)
|
||||
@JvmField
|
||||
public val NATIVE_MODULE: DebugOverlayTag =
|
||||
DebugOverlayTag("Native Module", "Native Module init", Color.rgb(0x80, 0x00, 0x80))
|
||||
@JvmField
|
||||
public val UI_MANAGER: DebugOverlayTag =
|
||||
DebugOverlayTag(
|
||||
"UI Manager",
|
||||
"UI Manager View Operations (requires restart\nwarning: this is spammy)",
|
||||
Color.CYAN)
|
||||
@JvmField
|
||||
public val FABRIC_UI_MANAGER: DebugOverlayTag =
|
||||
DebugOverlayTag("FabricUIManager", "Fabric UI Manager View Operations", Color.CYAN)
|
||||
@JvmField
|
||||
public val FABRIC_RECONCILER: DebugOverlayTag =
|
||||
DebugOverlayTag("FabricReconciler", "Reconciler for Fabric", Color.CYAN)
|
||||
@JvmField
|
||||
public val RELAY: DebugOverlayTag =
|
||||
DebugOverlayTag("Relay", "including prefetching", Color.rgb(0xFF, 0x99, 0x00))
|
||||
}
|
||||
+5
-1
@@ -319,7 +319,11 @@ public class ReactDelegate {
|
||||
@Nullable
|
||||
public ReactRootView getReactRootView() {
|
||||
if (ReactNativeFeatureFlags.enableBridgelessArchitecture()) {
|
||||
return (ReactRootView) mReactSurface.getView();
|
||||
if (mReactSurface != null) {
|
||||
return (ReactRootView) mReactSurface.getView();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return mReactRootView;
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.react.devsupport">
|
||||
<application>
|
||||
<!-- This activity will be removed for release builds -->
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"
|
||||
android:exported="false"
|
||||
/>
|
||||
</application>
|
||||
</manifest>
|
||||
+8
@@ -593,6 +593,14 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
||||
// for this manager
|
||||
resetCurrentContext(null);
|
||||
}
|
||||
|
||||
// If some JNI types (e.g. jni::HybridClass) are used in JSI (e.g. jsi::HostObject), they might
|
||||
// not be immediately deleted on an app refresh as both Java and JavaScript are
|
||||
// garbage-collected languages and the memory might float around for a while. For C++
|
||||
// developers, this will be hard to debug as destructors might be called at a later point, so in
|
||||
// this case we trigger a Java GC to maybe eagerly collect such objects when the app
|
||||
// reloads.
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-1
@@ -144,7 +144,12 @@ class RedBoxDialogSurfaceDelegate implements SurfaceDelegate {
|
||||
public void hide() {
|
||||
// dismiss redbox if exists
|
||||
if (mDialog != null) {
|
||||
mDialog.dismiss();
|
||||
try {
|
||||
mDialog.dismiss();
|
||||
} catch (IllegalArgumentException e) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG, "RedBoxDialogSurfaceDelegate: error while dismissing dialog: ", e);
|
||||
}
|
||||
destroyContentView();
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
+8
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<64521c5f18fcaf7092590a2629bb8418>>
|
||||
* @generated SignedSource<<b7f79cb934f8bf2519439339fc7562d3>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -256,6 +256,12 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = accessor.traceTurboModulePromiseRejectionsOnAndroid()
|
||||
|
||||
/**
|
||||
* In Bridgeless mode, use the always available javascript error reporting pipeline.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun useAlwaysAvailableJSErrorHandling(): Boolean = accessor.useAlwaysAvailableJSErrorHandling()
|
||||
|
||||
/**
|
||||
* Should this application enable the Fabric Interop Layer for Android? If yes, the application will behave so that it can accept non-Fabric components and render them on Fabric. This toggle is controlling extra logic such as custom event dispatching that are needed for the Fabric Interop Layer to work correctly.
|
||||
*/
|
||||
|
||||
+12
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<8ed11f58c6671703e4021a91256d315d>>
|
||||
* @generated SignedSource<<1fa2a94ddb1563952c457e098f0deed7>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -58,6 +58,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
|
||||
private var loadVectorDrawablesOnImagesCache: Boolean? = null
|
||||
private var setAndroidLayoutDirectionCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useFabricInteropCache: Boolean? = null
|
||||
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
|
||||
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
|
||||
@@ -409,6 +410,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useAlwaysAvailableJSErrorHandling(): Boolean {
|
||||
var cached = useAlwaysAvailableJSErrorHandlingCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.useAlwaysAvailableJSErrorHandling()
|
||||
useAlwaysAvailableJSErrorHandlingCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useFabricInterop(): Boolean {
|
||||
var cached = useFabricInteropCache
|
||||
if (cached == null) {
|
||||
|
||||
+4
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<6f5d74e7ec13afa88fbd088182f0ba2b>>
|
||||
* @generated SignedSource<<fe7f93d5f6aeba9007d892074aac5b3a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -104,6 +104,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useImmediateExecutorInAndroidBridgeless(): Boolean
|
||||
|
||||
+5
-3
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<e1868b062c62107e77fe2897a0681e6e>>
|
||||
* @generated SignedSource<<09dd6fb517b7b965c5cc554a075e5590>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -35,7 +35,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun enableAlignItemsBaselineOnFabricIOS(): Boolean = true
|
||||
|
||||
override fun enableAndroidLineHeightCentering(): Boolean = false
|
||||
override fun enableAndroidLineHeightCentering(): Boolean = true
|
||||
|
||||
override fun enableBridgelessArchitecture(): Boolean = false
|
||||
|
||||
@@ -99,6 +99,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
|
||||
|
||||
override fun useAlwaysAvailableJSErrorHandling(): Boolean = false
|
||||
|
||||
override fun useFabricInterop(): Boolean = false
|
||||
|
||||
override fun useImmediateExecutorInAndroidBridgeless(): Boolean = false
|
||||
|
||||
+13
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<c6e809489f71a930ad05d83a4ef37cde>>
|
||||
* @generated SignedSource<<7e78a7ec91d557297f869b964cc91eac>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -62,6 +62,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var loadVectorDrawablesOnImagesCache: Boolean? = null
|
||||
private var setAndroidLayoutDirectionCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useFabricInteropCache: Boolean? = null
|
||||
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
|
||||
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
|
||||
@@ -451,6 +452,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useAlwaysAvailableJSErrorHandling(): Boolean {
|
||||
var cached = useAlwaysAvailableJSErrorHandlingCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.useAlwaysAvailableJSErrorHandling()
|
||||
accessedFeatureFlags.add("useAlwaysAvailableJSErrorHandling")
|
||||
useAlwaysAvailableJSErrorHandlingCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useFabricInterop(): Boolean {
|
||||
var cached = useFabricInteropCache
|
||||
if (cached == null) {
|
||||
|
||||
+4
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<4c0f737c0dfaa5095d46defe1f67bddf>>
|
||||
* @generated SignedSource<<0c6450697814b66ae910d66f278213ef>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
package com.facebook.react.internal.featureflags
|
||||
@@ -99,6 +99,8 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean
|
||||
|
||||
@DoNotStrip public fun useFabricInterop(): Boolean
|
||||
|
||||
@DoNotStrip public fun useImmediateExecutorInAndroidBridgeless(): Boolean
|
||||
|
||||
+25
@@ -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.react.modules.fresco
|
||||
|
||||
public enum class ImageCacheControl {
|
||||
/** Uses OkHttp's default cache control policy with no store. */
|
||||
DEFAULT,
|
||||
/**
|
||||
* The data for the URL will be loaded from the originating source. No existing cache data should
|
||||
* be used to satisfy a URL load request.
|
||||
*/
|
||||
RELOAD,
|
||||
/**
|
||||
* The existing cache data will be used to satisfy a request, regardless of its age or expiration
|
||||
* date. If there is no existing data in the cache corresponding to a URL load request, no attempt
|
||||
* is made to load the data from the originating source, and the load is considered to have
|
||||
* failed.
|
||||
*/
|
||||
ONLY_IF_CACHED,
|
||||
}
|
||||
+6
-3
@@ -16,16 +16,19 @@ public class ReactNetworkImageRequest
|
||||
private constructor(
|
||||
builder: ImageRequestBuilder,
|
||||
/** Headers for the request */
|
||||
internal val headers: ReadableMap?
|
||||
internal val headers: ReadableMap?,
|
||||
internal val cacheControl: ImageCacheControl,
|
||||
) : ImageRequest(builder) {
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
public fun fromBuilderWithHeaders(
|
||||
builder: ImageRequestBuilder,
|
||||
headers: ReadableMap?
|
||||
headers: ReadableMap?,
|
||||
cacheControl: ImageCacheControl = ImageCacheControl.DEFAULT,
|
||||
): ReactNetworkImageRequest {
|
||||
return ReactNetworkImageRequest(builder, headers)
|
||||
return ReactNetworkImageRequest(builder, headers, cacheControl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -35,22 +35,30 @@ internal class ReactOkHttpNetworkFetcher(private val okHttpClient: OkHttpClient)
|
||||
fetchState.submitTime = SystemClock.elapsedRealtime()
|
||||
val uri = fetchState.uri
|
||||
var requestHeaders: Map<String, String>? = null
|
||||
val cacheControlBuilder = CacheControl.Builder().noStore()
|
||||
if (fetchState.context.imageRequest is ReactNetworkImageRequest) {
|
||||
val networkImageRequest = fetchState.context.imageRequest as ReactNetworkImageRequest
|
||||
requestHeaders = getHeaders(networkImageRequest.headers)
|
||||
when (networkImageRequest.cacheControl) {
|
||||
ImageCacheControl.RELOAD -> {
|
||||
cacheControlBuilder.noCache()
|
||||
}
|
||||
ImageCacheControl.ONLY_IF_CACHED -> {
|
||||
cacheControlBuilder.onlyIfCached()
|
||||
}
|
||||
ImageCacheControl.DEFAULT -> {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
}
|
||||
val headers = OkHttpCompat.getHeadersFromMap(requestHeaders)
|
||||
val request =
|
||||
Request.Builder()
|
||||
.cacheControl(CacheControl.Builder().noStore().build())
|
||||
.cacheControl(cacheControlBuilder.build())
|
||||
.url(uri.toString())
|
||||
.headers(headers)
|
||||
.get()
|
||||
.build()
|
||||
fetchWithRequest(fetchState, callback, request)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TAG = "ReactOkHttpNetworkFetcher"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -546,8 +546,7 @@ public class ReactHostImpl implements ReactHost {
|
||||
* Entrypoint to destroy the ReactInstance. If the ReactInstance is reloading, will wait until
|
||||
* reload is finished, before destroying.
|
||||
*
|
||||
* @param reason {@link String} describing why ReactHost is being destroyed (e.g. memmory
|
||||
* pressure)
|
||||
* @param reason {@link String} describing why ReactHost is being destroyed (e.g. memory pressure)
|
||||
* @param ex {@link Exception} exception that caused the trigger to destroy ReactHost (or null)
|
||||
* This exception will be used to log properly the cause of destroy operation.
|
||||
* @return A task that completes when React Native gets destroyed.
|
||||
|
||||
+7
@@ -143,6 +143,9 @@ public class FabricEventDispatcher implements EventDispatcher, LifecycleEventLis
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
scheduleDispatchOfBatchedEvents();
|
||||
if (!ReactNativeFeatureFlags.useOptimizedEventBatchingOnAndroid()) {
|
||||
mCurrentFrameCallback.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -216,6 +219,10 @@ public class FabricEventDispatcher implements EventDispatcher, LifecycleEventLis
|
||||
mShouldStop = true;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mShouldStop = false;
|
||||
}
|
||||
|
||||
public void maybeDispatchBatchedEvents() {
|
||||
if (!mIsDispatchScheduled) {
|
||||
mIsDispatchScheduled = true;
|
||||
|
||||
+36
-4
@@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import com.facebook.common.references.CloseableReference
|
||||
import com.facebook.common.util.UriUtil
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder
|
||||
import com.facebook.drawee.controller.ControllerListener
|
||||
import com.facebook.drawee.controller.ForwardingControllerListener
|
||||
@@ -40,6 +41,7 @@ import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.facebook.imagepipeline.postprocessors.IterativeBoxBlurPostProcessor
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor
|
||||
import com.facebook.imagepipeline.request.ImageRequest
|
||||
import com.facebook.imagepipeline.request.ImageRequest.RequestLevel
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import com.facebook.imagepipeline.request.Postprocessor
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
@@ -49,6 +51,7 @@ import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
||||
import com.facebook.react.common.annotations.VisibleForTesting
|
||||
import com.facebook.react.common.build.ReactBuildConfig
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.modules.fresco.ImageCacheControl
|
||||
import com.facebook.react.modules.fresco.ReactNetworkImageRequest
|
||||
import com.facebook.react.uimanager.BackgroundStyleApplicator
|
||||
import com.facebook.react.uimanager.LengthPercentage
|
||||
@@ -268,7 +271,8 @@ public class ReactImageView(
|
||||
} else if (sources.size() == 1) {
|
||||
// Optimize for the case where we have just one uri, case in which we don't need the sizes
|
||||
val source = sources.getMap(0)
|
||||
var imageSource = ImageSource(context, source.getString("uri"))
|
||||
val cacheControl = computeCacheControl(source.getString("cache"))
|
||||
var imageSource = ImageSource(context, source.getString("uri"), cacheControl = cacheControl)
|
||||
if (Uri.EMPTY == imageSource.uri) {
|
||||
warnImageSource(source.getString("uri"))
|
||||
imageSource = getTransparentBitmapImageSource(context)
|
||||
@@ -277,12 +281,14 @@ public class ReactImageView(
|
||||
} else {
|
||||
for (idx in 0 until sources.size()) {
|
||||
val source = sources.getMap(idx)
|
||||
val cacheControl = computeCacheControl(source.getString("cache"))
|
||||
var imageSource =
|
||||
ImageSource(
|
||||
context,
|
||||
source.getString("uri"),
|
||||
source.getDouble("width"),
|
||||
source.getDouble("height"))
|
||||
source.getDouble("height"),
|
||||
cacheControl)
|
||||
if (Uri.EMPTY == imageSource.uri) {
|
||||
warnImageSource(source.getString("uri"))
|
||||
imageSource = getTransparentBitmapImageSource(context)
|
||||
@@ -301,6 +307,23 @@ public class ReactImageView(
|
||||
isDirty = true
|
||||
}
|
||||
|
||||
private fun computeCacheControl(cacheControl: String?): ImageCacheControl {
|
||||
return when (cacheControl) {
|
||||
null,
|
||||
"default" -> ImageCacheControl.DEFAULT
|
||||
"reload" -> ImageCacheControl.RELOAD
|
||||
"only-if-cached" -> ImageCacheControl.ONLY_IF_CACHED
|
||||
else -> ImageCacheControl.DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeRequestLevel(cacheControl: ImageCacheControl): RequestLevel {
|
||||
return when (cacheControl) {
|
||||
ImageCacheControl.ONLY_IF_CACHED -> RequestLevel.DISK_CACHE
|
||||
else -> RequestLevel.FULL_FETCH
|
||||
}
|
||||
}
|
||||
|
||||
public fun setDefaultSource(name: String?) {
|
||||
val newDefaultDrawable = instance.getResourceDrawable(context, name)
|
||||
if (defaultImageDrawable != newDefaultDrawable) {
|
||||
@@ -409,7 +432,10 @@ public class ReactImageView(
|
||||
}
|
||||
|
||||
private fun maybeUpdateViewFromRequest(doResize: Boolean) {
|
||||
val uri = this.imageSource?.uri ?: return
|
||||
val imageSource = this.imageSource ?: return
|
||||
val uri = imageSource.uri
|
||||
val cacheControl = imageSource.cacheControl
|
||||
val requestLevel = computeRequestLevel(cacheControl)
|
||||
|
||||
val postprocessorList = mutableListOf<Postprocessor>()
|
||||
iterativeBoxBlurPostProcessor?.let { postprocessorList.add(it) }
|
||||
@@ -418,19 +444,25 @@ public class ReactImageView(
|
||||
|
||||
val resizeOptions = if (doResize) resizeOptions else null
|
||||
|
||||
if (cacheControl == ImageCacheControl.RELOAD) {
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
imagePipeline.evictFromCache(uri)
|
||||
}
|
||||
|
||||
val imageRequestBuilder =
|
||||
ImageRequestBuilder.newBuilderWithSource(uri)
|
||||
.setPostprocessor(postprocessor)
|
||||
.setResizeOptions(resizeOptions)
|
||||
.setAutoRotateEnabled(true)
|
||||
.setProgressiveRenderingEnabled(progressiveRenderingEnabled)
|
||||
.setLowestPermittedRequestLevel(requestLevel)
|
||||
|
||||
if (resizeMethod == ImageResizeMethod.NONE) {
|
||||
imageRequestBuilder.setDownsampleOverride(DownsampleMode.NEVER)
|
||||
}
|
||||
|
||||
val imageRequest: ImageRequest =
|
||||
ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers)
|
||||
ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers, cacheControl)
|
||||
|
||||
globalImageLoadListener?.onLoadAttempt(uri)
|
||||
|
||||
|
||||
+7
-4
@@ -9,6 +9,7 @@ package com.facebook.react.views.imagehelper
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.facebook.react.modules.fresco.ImageCacheControl
|
||||
import java.util.Objects
|
||||
|
||||
/** Class describing an image source (network URI or resource) and size. */
|
||||
@@ -19,7 +20,8 @@ constructor(
|
||||
/** Get the source of this image, as it was passed to the constructor. */
|
||||
public val source: String?,
|
||||
width: Double = 0.0,
|
||||
height: Double = 0.0
|
||||
height: Double = 0.0,
|
||||
public val cacheControl: ImageCacheControl = ImageCacheControl.DEFAULT,
|
||||
) {
|
||||
|
||||
/** Get the URI for this image - can be either a parsed network URI or a resource URI. */
|
||||
@@ -45,10 +47,11 @@ constructor(
|
||||
return java.lang.Double.compare(that.size, size) == 0 &&
|
||||
isResource == that.isResource &&
|
||||
uri == that.uri &&
|
||||
source == that.source
|
||||
source == that.source &&
|
||||
cacheControl == that.cacheControl
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(uri, source, size, isResource)
|
||||
override fun hashCode(): Int = Objects.hash(uri, source, size, isResource, cacheControl)
|
||||
|
||||
private fun computeUri(context: Context): Uri =
|
||||
try {
|
||||
@@ -70,6 +73,6 @@ constructor(
|
||||
|
||||
@JvmStatic
|
||||
public fun getTransparentBitmapImageSource(context: Context): ImageSource =
|
||||
ImageSource(context, TRANSPARENT_BITMAP_URI)
|
||||
ImageSource(context, TRANSPARENT_BITMAP_URI, cacheControl = ImageCacheControl.DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -8,6 +8,7 @@
|
||||
package com.facebook.react.views.imagehelper
|
||||
|
||||
import com.facebook.imagepipeline.core.ImagePipelineFactory
|
||||
import com.facebook.react.modules.fresco.ImageCacheControl
|
||||
|
||||
/** Helper class for dealing with multisource images. */
|
||||
public object MultiSourceHelper {
|
||||
@@ -62,7 +63,10 @@ public object MultiSourceHelper {
|
||||
best = source
|
||||
}
|
||||
if (precision < bestCachePrecision &&
|
||||
source.cacheControl != ImageCacheControl.RELOAD &&
|
||||
(imagePipeline.isInBitmapMemoryCache(source.uri) ||
|
||||
// TODO: T206445115 isInDiskCacheSync is a blocking operation, we should move this to
|
||||
// a separate thread
|
||||
imagePipeline.isInDiskCacheSync(source.uri))) {
|
||||
bestCachePrecision = precision
|
||||
bestCached = source
|
||||
|
||||
+8
-1
@@ -300,7 +300,14 @@ public class ReactModalHostView(context: ThemedReactContext) :
|
||||
* changed. This has the pleasant side-effect of us not having to preface all Modals with "top:
|
||||
* statusBarHeight", since that margin will be included in the FrameLayout.
|
||||
*/
|
||||
get() = FrameLayout(context).apply { addView(dialogRootViewGroup) }
|
||||
get() =
|
||||
FrameLayout(context).apply {
|
||||
addView(dialogRootViewGroup)
|
||||
if (!statusBarTranslucent) {
|
||||
// this is needed to prevent content hiding behind systems bars < API 30
|
||||
this.fitsSystemWindows = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* updateProperties will update the properties that do not require us to recreate the dialog
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.scroll
|
||||
|
||||
import android.content.Context
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil
|
||||
import com.facebook.react.views.view.ReactViewGroup
|
||||
|
||||
/**
|
||||
* Used by legacy/Paper renderer to perform offsetting of scroll content when the app-wide layout
|
||||
* direction is RTL. Contextually set layout direction is not respected by legacy renderer.
|
||||
*/
|
||||
internal class ReactHorizontalScrollContainerLegacyView(context: Context) :
|
||||
ReactViewGroup(context) {
|
||||
private val isRTL: Boolean = I18nUtil.instance.isRTL(context)
|
||||
|
||||
override fun setRemoveClippedSubviews(removeClippedSubviews: Boolean) {
|
||||
// removeClippedSubviews logic may read metrics before the offsetting we do in onLayout() and is
|
||||
// such unsafe
|
||||
if (isRTL) {
|
||||
super.setRemoveClippedSubviews(false)
|
||||
return
|
||||
}
|
||||
|
||||
super.setRemoveClippedSubviews(removeClippedSubviews)
|
||||
}
|
||||
|
||||
protected override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
if (isRTL) {
|
||||
// When the layout direction is RTL, we expect Yoga to give us a layout
|
||||
// that extends off the screen to the left so we re-center it with left=0
|
||||
val newLeft = 0
|
||||
val width = right - left
|
||||
val newRight = newLeft + width
|
||||
setLeft(newLeft)
|
||||
setTop(top)
|
||||
setRight(newRight)
|
||||
setBottom(bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -8,6 +8,12 @@
|
||||
package com.facebook.react.views.scroll
|
||||
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap
|
||||
import com.facebook.react.uimanager.StateWrapper
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.common.UIManagerType
|
||||
import com.facebook.react.uimanager.common.ViewUtil
|
||||
import com.facebook.react.views.view.ReactViewGroup
|
||||
import com.facebook.react.views.view.ReactViewManager
|
||||
|
||||
/** View manager for {@link ReactHorizontalScrollContainerView} components. */
|
||||
@@ -15,7 +21,28 @@ import com.facebook.react.views.view.ReactViewManager
|
||||
public class ReactHorizontalScrollContainerViewManager : ReactViewManager() {
|
||||
override public fun getName(): String = REACT_CLASS
|
||||
|
||||
protected override fun createViewInstance(
|
||||
reactTag: Int,
|
||||
context: ThemedReactContext,
|
||||
initialProps: ReactStylesDiffMap?,
|
||||
stateWrapper: StateWrapper?
|
||||
): ReactViewGroup {
|
||||
check(uiManagerType == null)
|
||||
uiManagerType = ViewUtil.getUIManagerType(reactTag)
|
||||
val view = super.createViewInstance(reactTag, context, initialProps, stateWrapper)
|
||||
uiManagerType = null
|
||||
return view
|
||||
}
|
||||
|
||||
public override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
|
||||
return when (checkNotNull(uiManagerType)) {
|
||||
UIManagerType.FABRIC -> ReactViewGroup(context)
|
||||
else -> ReactHorizontalScrollContainerLegacyView(context)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public const val REACT_CLASS: String = "AndroidHorizontalScrollContentView"
|
||||
private @UIManagerType var uiManagerType: Int? = null
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -21,6 +21,7 @@ import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
import com.facebook.react.uimanager.IllegalViewOperationException;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
|
||||
@@ -34,6 +35,7 @@ import com.facebook.react.views.text.internal.ReactTextInlineImageShadowNode;
|
||||
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomStyleSpan;
|
||||
import com.facebook.react.views.text.internal.span.LegacyLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactAbsoluteSizeSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactBackgroundColorSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactClickableSpan;
|
||||
@@ -229,7 +231,11 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
|
||||
if (!Float.isNaN(effectiveLineHeight)
|
||||
&& (parentTextAttributes == null
|
||||
|| parentTextAttributes.getEffectiveLineHeight() != effectiveLineHeight)) {
|
||||
ops.add(new SetSpanOperation(start, end, new CustomLineHeightSpan(effectiveLineHeight)));
|
||||
if (ReactNativeFeatureFlags.enableAndroidLineHeightCentering()) {
|
||||
ops.add(new SetSpanOperation(start, end, new CustomLineHeightSpan(effectiveLineHeight)));
|
||||
} else {
|
||||
ops.add(new SetSpanOperation(start, end, new LegacyLineHeightSpan(effectiveLineHeight)));
|
||||
}
|
||||
}
|
||||
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textShadowNode.getReactTag())));
|
||||
}
|
||||
|
||||
+4
@@ -270,6 +270,10 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode {
|
||||
.setBreakStrategy(mTextBreakStrategy)
|
||||
.setHyphenationFrequency(mHyphenationFrequency);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setJustificationMode(mJustificationMode);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
builder.setUseLineSpacingFromFallbacks(true);
|
||||
}
|
||||
|
||||
+2
-1
@@ -380,8 +380,9 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
|
||||
getBreakStrategy(),
|
||||
getHyphenationFrequency(),
|
||||
// always passing ALIGN_NORMAL here should be fine, since this method doesn't depend on
|
||||
// how exacly lines are aligned, just their width
|
||||
// how exactly lines are aligned, just their width
|
||||
Layout.Alignment.ALIGN_NORMAL,
|
||||
(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) ? -1 : getJustificationMode(),
|
||||
getPaint());
|
||||
setText(getSpanned());
|
||||
}
|
||||
|
||||
+72
-26
@@ -32,12 +32,14 @@ import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole;
|
||||
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role;
|
||||
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomStyleSpan;
|
||||
import com.facebook.react.views.text.internal.span.LegacyLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactAbsoluteSizeSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactBackgroundColorSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactClickableSpan;
|
||||
@@ -146,13 +148,41 @@ public class TextLayoutManager {
|
||||
== LayoutDirection.RTL;
|
||||
}
|
||||
|
||||
private static Layout.Alignment getTextAlignment(MapBuffer attributedString, Spannable spanned) {
|
||||
@Nullable
|
||||
private static String getTextAlignmentAttr(MapBuffer attributedString) {
|
||||
// TODO: Don't read AS_KEY_FRAGMENTS, which may be expensive, and is not present when using
|
||||
// cached Spannable
|
||||
if (!attributedString.contains(AS_KEY_FRAGMENTS)) {
|
||||
return Layout.Alignment.ALIGN_NORMAL;
|
||||
return null;
|
||||
}
|
||||
|
||||
MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS);
|
||||
if (fragments.getCount() != 0) {
|
||||
MapBuffer fragment = fragments.getMapBuffer(0);
|
||||
MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES);
|
||||
|
||||
if (textAttributes.contains(TextAttributeProps.TA_KEY_ALIGNMENT)) {
|
||||
return textAttributes.getString(TextAttributeProps.TA_KEY_ALIGNMENT);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getTextJustificationMode(@Nullable String alignmentAttr) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (alignmentAttr != null && alignmentAttr.equals("justified")) {
|
||||
return Layout.JUSTIFICATION_MODE_INTER_WORD;
|
||||
}
|
||||
|
||||
return Layout.JUSTIFICATION_MODE_NONE;
|
||||
}
|
||||
|
||||
private static Layout.Alignment getTextAlignment(
|
||||
MapBuffer attributedString, Spannable spanned, @Nullable String alignmentAttr) {
|
||||
// Android will align text based on the script, so normal and opposite alignment needs to be
|
||||
// swapped when the directions of paragraph and script don't match.
|
||||
// I.e. paragraph is LTR but script is RTL, text needs to be aligned to the left, which means
|
||||
@@ -165,23 +195,15 @@ public class TextLayoutManager {
|
||||
Layout.Alignment alignment =
|
||||
swapNormalAndOpposite ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL;
|
||||
|
||||
MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS);
|
||||
if (fragments.getCount() != 0) {
|
||||
MapBuffer fragment = fragments.getMapBuffer(0);
|
||||
MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES);
|
||||
if (alignmentAttr == null) {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
if (textAttributes.contains(TextAttributeProps.TA_KEY_ALIGNMENT)) {
|
||||
String alignmentAttr = textAttributes.getString(TextAttributeProps.TA_KEY_ALIGNMENT);
|
||||
|
||||
if (alignmentAttr.equals("center")) {
|
||||
alignment = Layout.Alignment.ALIGN_CENTER;
|
||||
} else if (alignmentAttr.equals("right")) {
|
||||
alignment =
|
||||
swapNormalAndOpposite
|
||||
? Layout.Alignment.ALIGN_NORMAL
|
||||
: Layout.Alignment.ALIGN_OPPOSITE;
|
||||
}
|
||||
}
|
||||
if (alignmentAttr.equals("center")) {
|
||||
alignment = Layout.Alignment.ALIGN_CENTER;
|
||||
} else if (alignmentAttr.equals("right")) {
|
||||
alignment =
|
||||
swapNormalAndOpposite ? Layout.Alignment.ALIGN_NORMAL : Layout.Alignment.ALIGN_OPPOSITE;
|
||||
}
|
||||
|
||||
return alignment;
|
||||
@@ -190,7 +212,8 @@ public class TextLayoutManager {
|
||||
public static int getTextGravity(
|
||||
MapBuffer attributedString, Spannable spanned, int defaultValue) {
|
||||
int gravity = defaultValue;
|
||||
Layout.Alignment alignment = getTextAlignment(attributedString, spanned);
|
||||
@Nullable String alignmentAttr = getTextAlignmentAttr(attributedString);
|
||||
Layout.Alignment alignment = getTextAlignment(attributedString, spanned, alignmentAttr);
|
||||
|
||||
// depending on whether the script is LTR or RTL, ALIGN_NORMAL and ALIGN_OPPOSITE may mean
|
||||
// different things
|
||||
@@ -296,9 +319,15 @@ public class TextLayoutManager {
|
||||
textAttributes.mTextShadowColor)));
|
||||
}
|
||||
if (!Float.isNaN(textAttributes.getEffectiveLineHeight())) {
|
||||
ops.add(
|
||||
new SetSpanOperation(
|
||||
start, end, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight())));
|
||||
if (ReactNativeFeatureFlags.enableAndroidLineHeightCentering()) {
|
||||
ops.add(
|
||||
new SetSpanOperation(
|
||||
start, end, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight())));
|
||||
} else {
|
||||
ops.add(
|
||||
new SetSpanOperation(
|
||||
start, end, new LegacyLineHeightSpan(textAttributes.getEffectiveLineHeight())));
|
||||
}
|
||||
}
|
||||
|
||||
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(reactTag)));
|
||||
@@ -363,6 +392,7 @@ public class TextLayoutManager {
|
||||
int textBreakStrategy,
|
||||
int hyphenationFrequency,
|
||||
Layout.Alignment alignment,
|
||||
int justificationMode,
|
||||
TextPaint paint) {
|
||||
Layout layout;
|
||||
|
||||
@@ -382,7 +412,7 @@ public class TextLayoutManager {
|
||||
}
|
||||
|
||||
int hintWidth = (int) Math.ceil(desiredWidth);
|
||||
layout =
|
||||
StaticLayout.Builder builder =
|
||||
StaticLayout.Builder.obtain(text, 0, spanLength, paint, hintWidth)
|
||||
.setAlignment(alignment)
|
||||
.setLineSpacing(0.f, 1.f)
|
||||
@@ -390,8 +420,13 @@ public class TextLayoutManager {
|
||||
.setBreakStrategy(textBreakStrategy)
|
||||
.setHyphenationFrequency(hyphenationFrequency)
|
||||
.setTextDirection(
|
||||
isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR)
|
||||
.build();
|
||||
isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
builder.setUseLineSpacingFromFallbacks(true);
|
||||
}
|
||||
|
||||
layout = builder.build();
|
||||
|
||||
} else if (boring != null && (unconstrainedWidth || boring.width <= width)) {
|
||||
int boringLayoutWidth = boring.width;
|
||||
@@ -420,6 +455,10 @@ public class TextLayoutManager {
|
||||
.setTextDirection(
|
||||
isScriptRTL ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setJustificationMode(justificationMode);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
builder.setUseLineSpacingFromFallbacks(true);
|
||||
}
|
||||
@@ -506,7 +545,9 @@ public class TextLayoutManager {
|
||||
? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES)
|
||||
: ReactConstants.UNSET;
|
||||
|
||||
Layout.Alignment alignment = getTextAlignment(attributedString, text);
|
||||
@Nullable String alignmentAttr = getTextAlignmentAttr(attributedString);
|
||||
Layout.Alignment alignment = getTextAlignment(attributedString, text, alignmentAttr);
|
||||
int justificationMode = getTextJustificationMode(alignmentAttr);
|
||||
|
||||
if (adjustFontSizeToFit) {
|
||||
double minimumFontSize =
|
||||
@@ -526,6 +567,7 @@ public class TextLayoutManager {
|
||||
textBreakStrategy,
|
||||
hyphenationFrequency,
|
||||
alignment,
|
||||
justificationMode,
|
||||
paint);
|
||||
}
|
||||
|
||||
@@ -538,6 +580,7 @@ public class TextLayoutManager {
|
||||
textBreakStrategy,
|
||||
hyphenationFrequency,
|
||||
alignment,
|
||||
justificationMode,
|
||||
paint);
|
||||
}
|
||||
|
||||
@@ -553,6 +596,7 @@ public class TextLayoutManager {
|
||||
int textBreakStrategy,
|
||||
int hyphenationFrequency,
|
||||
Layout.Alignment alignment,
|
||||
int justificationMode,
|
||||
TextPaint paint) {
|
||||
BoringLayout.Metrics boring = BoringLayout.isBoring(text, paint);
|
||||
Layout layout =
|
||||
@@ -565,6 +609,7 @@ public class TextLayoutManager {
|
||||
textBreakStrategy,
|
||||
hyphenationFrequency,
|
||||
alignment,
|
||||
justificationMode,
|
||||
paint);
|
||||
|
||||
// Minimum font size is 4pts to match the iOS implementation.
|
||||
@@ -613,6 +658,7 @@ public class TextLayoutManager {
|
||||
textBreakStrategy,
|
||||
hyphenationFrequency,
|
||||
alignment,
|
||||
justificationMode,
|
||||
paint);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-85
@@ -9,105 +9,48 @@ package com.facebook.react.views.text.internal.span
|
||||
|
||||
import android.graphics.Paint.FontMetricsInt
|
||||
import android.text.style.LineHeightSpan
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* We use a custom [LineHeightSpan], because `lineSpacingExtra` is broken. Details here:
|
||||
* https://github.com/facebook/react-native/issues/7546
|
||||
* Implements a [LineHeightSpan] which follows web-like behavior for line height, unlike
|
||||
* LineHeightSpan.Standard which only effects space between the baselines of adjacent line boxes
|
||||
* (does not impact space before the first line or after the last).
|
||||
*/
|
||||
public class CustomLineHeightSpan(height: Float) : LineHeightSpan, ReactSpan {
|
||||
public val lineHeight: Int = ceil(height.toDouble()).toInt()
|
||||
|
||||
private fun chooseCenteredHeight(fm: FontMetricsInt) {
|
||||
if (fm.descent > lineHeight) {
|
||||
// Show as much descent as possible
|
||||
fm.descent = lineHeight
|
||||
fm.bottom = 0
|
||||
fm.top = 0
|
||||
fm.ascent = 0
|
||||
} else if (-fm.ascent + fm.descent > lineHeight) {
|
||||
// Calculate the amount we are over, and split the adjustment between descent and ascent
|
||||
val difference = -(lineHeight + fm.ascent - fm.descent) / 2
|
||||
val remainder = difference % 2
|
||||
fm.ascent = fm.ascent + difference
|
||||
fm.descent = fm.descent - difference - remainder
|
||||
fm.top = fm.ascent
|
||||
fm.bottom = fm.descent
|
||||
} else if (-fm.top + fm.bottom > lineHeight) {
|
||||
// Calculate the amount we are over, and split the adjustment between top and bottom
|
||||
|
||||
val excess = ((-fm.top + fm.bottom) - lineHeight) / 2
|
||||
|
||||
fm.top += excess
|
||||
fm.bottom -= excess
|
||||
} else {
|
||||
// Show proportionally additional ascent / top & descent / bottom
|
||||
val additional = lineHeight - (-fm.top + fm.bottom)
|
||||
|
||||
// Round up for the negative values and down for the positive values (arbitrary choice)
|
||||
// So that bottom - top equals additional even if it's an odd number.
|
||||
val top = (fm.top - ceil(additional / 2.0f)).toInt()
|
||||
val bottom = (fm.bottom + floor(additional / 2.0f)).toInt()
|
||||
|
||||
fm.top = top
|
||||
fm.ascent = top
|
||||
fm.descent = bottom
|
||||
fm.bottom = bottom
|
||||
}
|
||||
}
|
||||
|
||||
private fun chooseOriginalHeight(fm: FontMetricsInt) {
|
||||
// This is more complicated that I wanted it to be. You can find a good explanation of what the
|
||||
// FontMetrics mean here: http://stackoverflow.com/questions/27631736.
|
||||
// The general solution is that if there's not enough height to show the full line height, we
|
||||
// will prioritize in this order: descent, ascent, bottom, top
|
||||
|
||||
if (fm.descent > lineHeight) {
|
||||
// Show as much descent as possible
|
||||
fm.descent = min(lineHeight.toDouble(), fm.descent.toDouble()).toInt()
|
||||
fm.bottom = fm.descent
|
||||
fm.ascent = 0
|
||||
fm.top = fm.ascent
|
||||
} else if (-fm.ascent + fm.descent > lineHeight) {
|
||||
// Show all descent, and as much ascent as possible
|
||||
fm.bottom = fm.descent
|
||||
fm.ascent = -lineHeight + fm.descent
|
||||
fm.top = fm.ascent
|
||||
} else if (-fm.ascent + fm.bottom > lineHeight) {
|
||||
// Show all ascent, descent, as much bottom as possible
|
||||
fm.top = fm.ascent
|
||||
fm.bottom = fm.ascent + lineHeight
|
||||
} else if (-fm.top + fm.bottom > lineHeight) {
|
||||
// Show all ascent, descent, bottom, as much top as possible
|
||||
fm.top = fm.bottom - lineHeight
|
||||
} else {
|
||||
// Show proportionally additional ascent / top & descent / bottom
|
||||
val additional = lineHeight - (-fm.top + fm.bottom)
|
||||
|
||||
// Round up for the negative values and down for the positive values (arbitrary choice)
|
||||
// So that bottom - top equals additional even if it's an odd number.
|
||||
val top = (fm.top - ceil(additional / 2.0f)).toInt()
|
||||
val bottom = (fm.bottom + floor(additional / 2.0f)).toInt()
|
||||
|
||||
fm.top = top
|
||||
fm.ascent = top
|
||||
fm.descent = bottom
|
||||
fm.bottom = bottom
|
||||
}
|
||||
}
|
||||
|
||||
public override fun chooseHeight(
|
||||
text: CharSequence?,
|
||||
text: CharSequence,
|
||||
start: Int,
|
||||
end: Int,
|
||||
spanstartv: Int,
|
||||
v: Int,
|
||||
fm: FontMetricsInt,
|
||||
) {
|
||||
if (ReactNativeFeatureFlags.enableAndroidLineHeightCentering()) chooseCenteredHeight(fm)
|
||||
else chooseOriginalHeight(fm)
|
||||
// https://www.w3.org/TR/css-inline-3/#inline-height
|
||||
// When its computed line-height is not normal, its layout bounds are derived solely from
|
||||
// metrics of its first available font (ignoring glyphs from other fonts), and leading is used
|
||||
// to adjust the effective A and D to add up to the used line-height. Calculate the leading L as
|
||||
// L = line-height - (A + D). Half the leading (its half-leading) is added above A of the first
|
||||
// available font, and the other half below D of the first available font, giving an effective
|
||||
// ascent above the baseline of A′ = A + L/2, and an effective descent of D′ = D + L/2. However,
|
||||
// if line-fit-edge is not leading and this is not the root inline box, if the half-leading is
|
||||
// positive, treat it as zero. The layout bounds exactly encloses this effective A′ and D′.
|
||||
|
||||
val leading = lineHeight - ((-fm.ascent) + fm.descent)
|
||||
fm.ascent -= ceil(leading / 2.0f).toInt()
|
||||
fm.descent += floor(leading / 2.0f).toInt()
|
||||
|
||||
// The top of the first line, and the bottom of the last line, may influence bounds of the
|
||||
// paragraph, so we match them to the text ascent/descent. It is otherwise desirable to allow
|
||||
// line boxes to overlap (to allow too large glyphs to be drawn outside them), so we do not
|
||||
// adjust the top/bottom of interior line-boxes.
|
||||
if (start == 0) {
|
||||
fm.top = fm.ascent
|
||||
}
|
||||
if (end == text.length) {
|
||||
fm.bottom = fm.descent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.text.internal.span
|
||||
|
||||
import android.graphics.Paint.FontMetricsInt
|
||||
import android.text.style.LineHeightSpan
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* We use a custom [LineHeightSpan], because `lineSpacingExtra` is broken. Details here:
|
||||
* https://github.com/facebook/react-native/issues/7546
|
||||
*/
|
||||
public class LegacyLineHeightSpan(height: Float) : LineHeightSpan, ReactSpan {
|
||||
public val lineHeight: Int = ceil(height.toDouble()).toInt()
|
||||
|
||||
public override fun chooseHeight(
|
||||
text: CharSequence?,
|
||||
start: Int,
|
||||
end: Int,
|
||||
spanstartv: Int,
|
||||
v: Int,
|
||||
fm: FontMetricsInt,
|
||||
) {
|
||||
// This is more complicated that I wanted it to be. You can find a good explanation of what the
|
||||
// FontMetrics mean here: http://stackoverflow.com/questions/27631736.
|
||||
// The general solution is that if there's not enough height to show the full line height, we
|
||||
// will prioritize in this order: descent, ascent, bottom, top
|
||||
|
||||
if (fm.descent > lineHeight) {
|
||||
// Show as much descent as possible
|
||||
fm.descent = min(lineHeight.toDouble(), fm.descent.toDouble()).toInt()
|
||||
fm.bottom = fm.descent
|
||||
fm.ascent = 0
|
||||
fm.top = fm.ascent
|
||||
} else if (-fm.ascent + fm.descent > lineHeight) {
|
||||
// Show all descent, and as much ascent as possible
|
||||
fm.bottom = fm.descent
|
||||
fm.ascent = -lineHeight + fm.descent
|
||||
fm.top = fm.ascent
|
||||
} else if (-fm.ascent + fm.bottom > lineHeight) {
|
||||
// Show all ascent, descent, as much bottom as possible
|
||||
fm.top = fm.ascent
|
||||
fm.bottom = fm.ascent + lineHeight
|
||||
} else if (-fm.top + fm.bottom > lineHeight) {
|
||||
// Show all ascent, descent, bottom, as much top as possible
|
||||
fm.top = fm.bottom - lineHeight
|
||||
} else {
|
||||
// Show proportionally additional ascent / top & descent / bottom
|
||||
val additional = lineHeight - (-fm.top + fm.bottom)
|
||||
|
||||
// Round up for the negative values and down for the positive values (arbitrary choice)
|
||||
// So that bottom - top equals additional even if it's an odd number.
|
||||
val top = (fm.top - ceil(additional / 2.0f)).toInt()
|
||||
val bottom = (fm.bottom + floor(additional / 2.0f)).toInt()
|
||||
|
||||
fm.top = top
|
||||
fm.ascent = top
|
||||
fm.descent = bottom
|
||||
fm.bottom = bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -50,6 +50,7 @@ import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactSoftExceptionLogger;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
|
||||
import com.facebook.react.uimanager.BackgroundStyleApplicator;
|
||||
import com.facebook.react.uimanager.LengthPercentage;
|
||||
import com.facebook.react.uimanager.LengthPercentageType;
|
||||
@@ -71,6 +72,7 @@ import com.facebook.react.views.text.TextLayoutManager;
|
||||
import com.facebook.react.views.text.internal.span.CustomLetterSpacingSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.CustomStyleSpan;
|
||||
import com.facebook.react.views.text.internal.span.LegacyLineHeightSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactAbsoluteSizeSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactBackgroundColorSpan;
|
||||
import com.facebook.react.views.text.internal.span.ReactForegroundColorSpan;
|
||||
@@ -866,7 +868,13 @@ public class ReactEditText extends AppCompatEditText {
|
||||
|
||||
float lineHeight = mTextAttributes.getEffectiveLineHeight();
|
||||
if (!Float.isNaN(lineHeight)) {
|
||||
workingText.setSpan(new CustomLineHeightSpan(lineHeight), 0, workingText.length(), spanFlags);
|
||||
if (ReactNativeFeatureFlags.enableAndroidLineHeightCentering()) {
|
||||
workingText.setSpan(
|
||||
new CustomLineHeightSpan(lineHeight), 0, workingText.length(), spanFlags);
|
||||
} else {
|
||||
workingText.setSpan(
|
||||
new LegacyLineHeightSpan(lineHeight), 0, workingText.length(), spanFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -495,7 +495,8 @@ void FabricMountingManager::executeMount(
|
||||
? allocatedViewsIterator->second
|
||||
: defaultAllocatedViews;
|
||||
if (allocatedViewsIterator == allocatedViewRegistry_.end()) {
|
||||
LOG(ERROR) << "Executing commit after surface was stopped!";
|
||||
LOG(ERROR) << "Executing commit after surface " << surfaceId
|
||||
<< " was stopped!";
|
||||
}
|
||||
|
||||
for (const auto& mutation : mutations) {
|
||||
@@ -529,7 +530,7 @@ void FabricMountingManager::executeMount(
|
||||
(maintainMutationOrder ? cppCommonMountItems : cppDeleteMountItems)
|
||||
.push_back(CppMountItem::DeleteMountItem(oldChildShadowView));
|
||||
if (allocatedViewTags.erase(oldChildShadowView.tag) != 1) {
|
||||
LOG(ERROR) << "Emitting delete for unallocated view. "
|
||||
LOG(ERROR) << "Emitting delete for unallocated view "
|
||||
<< oldChildShadowView.tag;
|
||||
}
|
||||
break;
|
||||
@@ -537,7 +538,7 @@ void FabricMountingManager::executeMount(
|
||||
case ShadowViewMutation::Update: {
|
||||
if (!isVirtual) {
|
||||
if (!allocatedViewTags.contains(newChildShadowView.tag)) {
|
||||
LOG(FATAL) << "Emitting update for unallocated view. "
|
||||
LOG(ERROR) << "Emitting update for unallocated view "
|
||||
<< newChildShadowView.tag;
|
||||
}
|
||||
|
||||
@@ -605,7 +606,7 @@ void FabricMountingManager::executeMount(
|
||||
bool shouldCreateView =
|
||||
!allocatedViewTags.contains(newChildShadowView.tag);
|
||||
if (shouldCreateView) {
|
||||
LOG(ERROR) << "Emitting insert for unallocated view. "
|
||||
LOG(ERROR) << "Emitting insert for unallocated view "
|
||||
<< newChildShadowView.tag;
|
||||
(maintainMutationOrder ? cppCommonMountItems
|
||||
: cppUpdatePropsMountItems)
|
||||
|
||||
+16
-25
@@ -157,6 +157,11 @@ void FabricUIManagerBinding::startSurfaceWithSurfaceHandler(
|
||||
}
|
||||
scheduler->registerSurface(surfaceHandler);
|
||||
|
||||
auto mountingManager = getMountingManager("startSurfaceWithSurfaceHandler");
|
||||
if (mountingManager != nullptr) {
|
||||
mountingManager->onSurfaceStart(surfaceId);
|
||||
}
|
||||
|
||||
surfaceHandler.start();
|
||||
|
||||
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid()) {
|
||||
@@ -167,14 +172,8 @@ void FabricUIManagerBinding::startSurfaceWithSurfaceHandler(
|
||||
{
|
||||
std::unique_lock lock(surfaceHandlerRegistryMutex_);
|
||||
surfaceHandlerRegistry_.emplace(
|
||||
surfaceHandler.getSurfaceId(), jni::make_weak(surfaceHandlerBinding));
|
||||
surfaceId, jni::make_weak(surfaceHandlerBinding));
|
||||
}
|
||||
|
||||
auto mountingManager = getMountingManager("startSurfaceWithSurfaceHandler");
|
||||
if (!mountingManager) {
|
||||
return;
|
||||
}
|
||||
mountingManager->onSurfaceStart(surfaceHandler.getSurfaceId());
|
||||
}
|
||||
|
||||
// Used by non-bridgeless+Fabric
|
||||
@@ -206,6 +205,11 @@ void FabricUIManagerBinding::startSurface(
|
||||
|
||||
scheduler->registerSurface(surfaceHandler);
|
||||
|
||||
auto mountingManager = getMountingManager("startSurface");
|
||||
if (mountingManager != nullptr) {
|
||||
mountingManager->onSurfaceStart(surfaceId);
|
||||
}
|
||||
|
||||
surfaceHandler.start();
|
||||
|
||||
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid()) {
|
||||
@@ -214,17 +218,9 @@ void FabricUIManagerBinding::startSurface(
|
||||
}
|
||||
|
||||
{
|
||||
SystraceSection s2("FabricUIManagerBinding::startSurface::surfaceId::lock");
|
||||
std::unique_lock lock(surfaceHandlerRegistryMutex_);
|
||||
SystraceSection s3("FabricUIManagerBinding::startSurface::surfaceId");
|
||||
surfaceHandlerRegistry_.emplace(surfaceId, std::move(surfaceHandler));
|
||||
}
|
||||
|
||||
auto mountingManager = getMountingManager("startSurface");
|
||||
if (!mountingManager) {
|
||||
return;
|
||||
}
|
||||
mountingManager->onSurfaceStart(surfaceId);
|
||||
}
|
||||
|
||||
// Used by non-bridgeless+Fabric
|
||||
@@ -278,6 +274,11 @@ void FabricUIManagerBinding::startSurfaceWithConstraints(
|
||||
|
||||
scheduler->registerSurface(surfaceHandler);
|
||||
|
||||
auto mountingManager = getMountingManager("startSurfaceWithConstraints");
|
||||
if (mountingManager != nullptr) {
|
||||
mountingManager->onSurfaceStart(surfaceId);
|
||||
}
|
||||
|
||||
surfaceHandler.start();
|
||||
|
||||
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid()) {
|
||||
@@ -286,19 +287,9 @@ void FabricUIManagerBinding::startSurfaceWithConstraints(
|
||||
}
|
||||
|
||||
{
|
||||
SystraceSection s2(
|
||||
"FabricUIManagerBinding::startSurfaceWithConstraints::surfaceId::lock");
|
||||
std::unique_lock lock(surfaceHandlerRegistryMutex_);
|
||||
SystraceSection s3(
|
||||
"FabricUIManagerBinding::startSurfaceWithConstraints::surfaceId");
|
||||
surfaceHandlerRegistry_.emplace(surfaceId, std::move(surfaceHandler));
|
||||
}
|
||||
|
||||
auto mountingManager = getMountingManager("startSurfaceWithConstraints");
|
||||
if (!mountingManager) {
|
||||
return;
|
||||
}
|
||||
mountingManager->onSurfaceStart(surfaceId);
|
||||
}
|
||||
|
||||
// Used by non-bridgeless+Fabric
|
||||
|
||||
+16
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<e51d7efeeff83508fa5f1a7a46ee859d>>
|
||||
* @generated SignedSource<<869d22f5b2b35741b171a7257e91f114>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
#include "JReactNativeFeatureFlagsCxxInterop.h"
|
||||
@@ -267,6 +267,12 @@ class ReactNativeFeatureFlagsProviderHolder
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool useAlwaysAvailableJSErrorHandling() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useAlwaysAvailableJSErrorHandling");
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool useFabricInterop() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useFabricInterop");
|
||||
@@ -509,6 +515,11 @@ bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndr
|
||||
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::useFabricInterop();
|
||||
@@ -694,6 +705,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
|
||||
makeNativeMethod(
|
||||
"traceTurboModulePromiseRejectionsOnAndroid",
|
||||
JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid),
|
||||
makeNativeMethod(
|
||||
"useAlwaysAvailableJSErrorHandling",
|
||||
JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling),
|
||||
makeNativeMethod(
|
||||
"useFabricInterop",
|
||||
JReactNativeFeatureFlagsCxxInterop::useFabricInterop),
|
||||
|
||||
+5
-2
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<161df10ef8d0b49dce49c9d0e9354279>>
|
||||
* @generated SignedSource<<344f9c1de8883c4b72c124dd626f6489>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -144,6 +144,9 @@ class JReactNativeFeatureFlagsCxxInterop
|
||||
static bool traceTurboModulePromiseRejectionsOnAndroid(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool useAlwaysAvailableJSErrorHandling(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool useFabricInterop(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.text
|
||||
|
||||
import android.graphics.Paint
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
|
||||
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class CustomLineHeightSpanTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
ReactNativeFeatureFlagsForTests.setUp()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun evenLineHeightShouldIncreaseAllMetricsProportionally() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(22f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.top).isEqualTo(-11)
|
||||
assertThat(fm.ascent).isEqualTo(-11)
|
||||
assertThat(fm.descent).isEqualTo(11)
|
||||
assertThat(fm.bottom).isEqualTo(11)
|
||||
assertThat(fm.bottom - fm.top).isEqualTo(22)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oddLineHeightShouldAlsoWork() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(23f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.descent - fm.ascent).isEqualTo(23)
|
||||
assertThat(fm.bottom - fm.top).isEqualTo(23)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReduceTopFirst() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(19f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.top).isEqualTo(-9)
|
||||
assertThat(fm.ascent).isEqualTo(-5)
|
||||
assertThat(fm.descent).isEqualTo(5)
|
||||
assertThat(fm.bottom).isEqualTo(10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReduceBottomSecond() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(14f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.top).isEqualTo(-5)
|
||||
assertThat(fm.ascent).isEqualTo(-5)
|
||||
assertThat(fm.descent).isEqualTo(5)
|
||||
assertThat(fm.bottom).isEqualTo(9)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReduceAscentThird() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(9f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.top).isEqualTo(-4)
|
||||
assertThat(fm.ascent).isEqualTo(-4)
|
||||
assertThat(fm.descent).isEqualTo(5)
|
||||
assertThat(fm.bottom).isEqualTo(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReduceDescentLast() {
|
||||
val customLineHeightSpan = CustomLineHeightSpan(4f)
|
||||
val fm =
|
||||
Paint.FontMetricsInt().apply {
|
||||
top = -10
|
||||
ascent = -5
|
||||
descent = 5
|
||||
bottom = 10
|
||||
}
|
||||
customLineHeightSpan.chooseHeight("Hi", 0, 2, 0, 0, fm)
|
||||
assertThat(fm.top).isEqualTo(0)
|
||||
assertThat(fm.ascent).isEqualTo(0)
|
||||
assertThat(fm.descent).isEqualTo(4)
|
||||
assertThat(fm.bottom).isEqualTo(4)
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,22 @@ jsi::Object wrapInErrorIfNecessary(
|
||||
: Error.callAsConstructor(runtime, value).getObject(runtime);
|
||||
return error;
|
||||
}
|
||||
|
||||
class SetFalseOnDestruct {
|
||||
std::shared_ptr<bool> _value;
|
||||
|
||||
public:
|
||||
SetFalseOnDestruct(const SetFalseOnDestruct&) = delete;
|
||||
SetFalseOnDestruct& operator=(const SetFalseOnDestruct&) = delete;
|
||||
SetFalseOnDestruct(SetFalseOnDestruct&&) = delete;
|
||||
SetFalseOnDestruct& operator=(SetFalseOnDestruct&&) = delete;
|
||||
explicit SetFalseOnDestruct(std::shared_ptr<bool> value)
|
||||
: _value(std::move(value)) {}
|
||||
~SetFalseOnDestruct() {
|
||||
*_value = false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace facebook::react {
|
||||
@@ -162,7 +178,7 @@ std::ostream& operator<<(
|
||||
|
||||
JsErrorHandler::JsErrorHandler(JsErrorHandler::OnJsError onJsError)
|
||||
: _onJsError(std::move(onJsError)),
|
||||
_hasHandledFatalError(false){
|
||||
_inErrorHandler(std::make_shared<bool>(false)){
|
||||
|
||||
};
|
||||
|
||||
@@ -171,7 +187,8 @@ JsErrorHandler::~JsErrorHandler() {}
|
||||
void JsErrorHandler::handleError(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal) {
|
||||
bool isFatal,
|
||||
bool logToConsole) {
|
||||
// TODO: Current error parsing works and is stable. Can investigate using
|
||||
// REGEX_HERMES to get additional Hermes data, though it requires JS setup
|
||||
if (_isRuntimeReady) {
|
||||
@@ -191,13 +208,17 @@ void JsErrorHandler::handleError(
|
||||
}
|
||||
}
|
||||
|
||||
emitError(runtime, error, isFatal);
|
||||
handleErrorWithCppPipeline(runtime, error, isFatal, logToConsole);
|
||||
}
|
||||
|
||||
void JsErrorHandler::emitError(
|
||||
void JsErrorHandler::handleErrorWithCppPipeline(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal) {
|
||||
bool isFatal,
|
||||
bool logToConsole) {
|
||||
*_inErrorHandler = true;
|
||||
SetFalseOnDestruct temp{_inErrorHandler};
|
||||
|
||||
auto message = error.getMessage();
|
||||
auto errorObj = wrapInErrorIfNecessary(runtime, error.value());
|
||||
auto componentStackValue = errorObj.getProperty(runtime, "componentStack");
|
||||
@@ -278,6 +299,14 @@ void JsErrorHandler::emitError(
|
||||
isTruthy(runtime, errorObj.getProperty(runtime, "isComponentError"));
|
||||
data.setProperty(runtime, "isComponentError", isComponentError);
|
||||
|
||||
if (logToConsole) {
|
||||
auto console = runtime.global().getPropertyAsObject(runtime, "console");
|
||||
auto errorFn = console.getPropertyAsFunction(runtime, "error");
|
||||
auto finalMessage =
|
||||
jsi::String::createFromUtf8(runtime, parsedError.message);
|
||||
errorFn.callWithThis(runtime, console, finalMessage);
|
||||
}
|
||||
|
||||
std::shared_ptr<bool> shouldPreventDefault = std::make_shared<bool>(false);
|
||||
auto preventDefault = jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
@@ -330,4 +359,8 @@ void JsErrorHandler::notifyOfFatalError() {
|
||||
_hasHandledFatalError = true;
|
||||
}
|
||||
|
||||
bool JsErrorHandler::inErrorHandler() {
|
||||
return *_inErrorHandler;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -43,13 +43,18 @@ class JsErrorHandler {
|
||||
explicit JsErrorHandler(OnJsError onJsError);
|
||||
~JsErrorHandler();
|
||||
|
||||
void handleError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal);
|
||||
void handleError(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal,
|
||||
bool logToConsole = true);
|
||||
bool hasHandledFatalError();
|
||||
void registerErrorListener(
|
||||
const std::function<void(jsi::Runtime&, jsi::Value)>& listener);
|
||||
void setRuntimeReady();
|
||||
bool isRuntimeReady();
|
||||
void notifyOfFatalError();
|
||||
bool inErrorHandler();
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -60,11 +65,16 @@ class JsErrorHandler {
|
||||
* teardown get reported properly.
|
||||
**/
|
||||
OnJsError _onJsError;
|
||||
bool _hasHandledFatalError;
|
||||
bool _hasHandledFatalError{};
|
||||
bool _isRuntimeReady{};
|
||||
std::shared_ptr<bool> _inErrorHandler;
|
||||
std::vector<std::function<void(jsi::Runtime&, jsi::Value)>> _errorListeners;
|
||||
|
||||
void emitError(jsi::Runtime& runtime, jsi::JSError& error, bool isFatal);
|
||||
void handleErrorWithCppPipeline(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::JSError& error,
|
||||
bool isFatal,
|
||||
bool logToConsole);
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -225,6 +225,13 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
|
||||
return plain_.utf8(s);
|
||||
}
|
||||
|
||||
std::u16string utf16(const String& str) override {
|
||||
return plain_.utf16(str);
|
||||
}
|
||||
std::u16string utf16(const PropNameID& sym) override {
|
||||
return plain_.utf16(sym);
|
||||
}
|
||||
|
||||
Object createObject() override {
|
||||
return plain_.createObject();
|
||||
};
|
||||
@@ -674,6 +681,15 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
|
||||
return RD::utf8(s);
|
||||
}
|
||||
|
||||
std::u16string utf16(const String& str) override {
|
||||
Around around{with_};
|
||||
return RD::utf16(str);
|
||||
}
|
||||
std::u16string utf16(const PropNameID& sym) override {
|
||||
Around around{with_};
|
||||
return RD::utf16(sym);
|
||||
}
|
||||
|
||||
Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override {
|
||||
Around around{with_};
|
||||
return RD::createValueFromJsonUtf8(json, length);
|
||||
|
||||
@@ -62,6 +62,107 @@ Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) {
|
||||
return f.call(runtime, arg);
|
||||
}
|
||||
|
||||
// Given a sequence of UTF8 encoded bytes, advance the input to past where a
|
||||
// 32-bit unicode codepoint as been decoded and return the codepoint. If the
|
||||
// UTF8 encoding is invalid, then return the value with the unicode replacement
|
||||
// character (U+FFFD). This decoder also relies on zero termination at end of
|
||||
// the input for bound checks.
|
||||
// \param input char pointer pointing to the current character
|
||||
// \return Unicode codepoint
|
||||
uint32_t decodeUTF8(const char*& input) {
|
||||
uint32_t ch = (unsigned char)input[0];
|
||||
if (ch <= 0x7f) {
|
||||
input += 1;
|
||||
return ch;
|
||||
}
|
||||
uint32_t ret;
|
||||
constexpr uint32_t replacementCharacter = 0xFFFD;
|
||||
if ((ch & 0xE0) == 0xC0) {
|
||||
uint32_t ch1 = (unsigned char)input[1];
|
||||
if ((ch1 & 0xC0) != 0x80) {
|
||||
input += 1;
|
||||
return replacementCharacter;
|
||||
}
|
||||
ret = ((ch & 0x1F) << 6) | (ch1 & 0x3F);
|
||||
input += 2;
|
||||
if (ret <= 0x7F) {
|
||||
return replacementCharacter;
|
||||
}
|
||||
} else if ((ch & 0xF0) == 0xE0) {
|
||||
uint32_t ch1 = (unsigned char)input[1];
|
||||
if ((ch1 & 0x40) != 0 || (ch1 & 0x80) == 0) {
|
||||
input += 1;
|
||||
return replacementCharacter;
|
||||
}
|
||||
uint32_t ch2 = (unsigned char)input[2];
|
||||
if ((ch2 & 0x40) != 0 || (ch2 & 0x80) == 0) {
|
||||
input += 2;
|
||||
return replacementCharacter;
|
||||
}
|
||||
ret = ((ch & 0x0F) << 12) | ((ch1 & 0x3F) << 6) | (ch2 & 0x3F);
|
||||
input += 3;
|
||||
if (ret <= 0x7FF) {
|
||||
return replacementCharacter;
|
||||
}
|
||||
} else if ((ch & 0xF8) == 0xF0) {
|
||||
uint32_t ch1 = (unsigned char)input[1];
|
||||
if ((ch1 & 0x40) != 0 || (ch1 & 0x80) == 0) {
|
||||
input += 1;
|
||||
return replacementCharacter;
|
||||
}
|
||||
uint32_t ch2 = (unsigned char)input[2];
|
||||
if ((ch2 & 0x40) != 0 || (ch2 & 0x80) == 0) {
|
||||
input += 2;
|
||||
return replacementCharacter;
|
||||
}
|
||||
uint32_t ch3 = (unsigned char)input[3];
|
||||
if ((ch3 & 0x40) != 0 || (ch3 & 0x80) == 0) {
|
||||
input += 3;
|
||||
return replacementCharacter;
|
||||
}
|
||||
ret = ((ch & 0x07) << 18) | ((ch1 & 0x3F) << 12) | ((ch2 & 0x3F) << 6) |
|
||||
(ch3 & 0x3F);
|
||||
input += 4;
|
||||
if (ret <= 0xFFFF) {
|
||||
return replacementCharacter;
|
||||
}
|
||||
if (ret > 0x10FFFF) {
|
||||
return replacementCharacter;
|
||||
}
|
||||
} else {
|
||||
input += 1;
|
||||
return replacementCharacter;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Given a valid 32-bit unicode codepoint, encode it as UTF-16 into the output.
|
||||
void encodeUTF16(std::u16string& out, uint32_t cp) {
|
||||
if (cp < 0x10000) {
|
||||
out.push_back((uint16_t)cp);
|
||||
return;
|
||||
}
|
||||
cp -= 0x10000;
|
||||
uint16_t highSurrogate = 0xD800 + ((cp >> 10) & 0x3FF);
|
||||
out.push_back(highSurrogate);
|
||||
uint16_t lowSurrogate = 0xDC00 + (cp & 0x3FF);
|
||||
out.push_back(lowSurrogate);
|
||||
}
|
||||
|
||||
// Convert the UTF8 encoded string into a UTF16 encoded string. If the
|
||||
// input is not valid UTF8, the replacement character (U+FFFD) is used to
|
||||
// represent the invalid sequence.
|
||||
std::u16string convertUTF8ToUTF16(const std::string& utf8) {
|
||||
std::u16string ret;
|
||||
const char* curr = utf8.data();
|
||||
const char* end = curr + utf8.length();
|
||||
while (curr < end) {
|
||||
auto cp = decodeUTF8(curr);
|
||||
encodeUTF16(ret, cp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Buffer::~Buffer() = default;
|
||||
@@ -147,6 +248,16 @@ Value Runtime::createValueFromJsonUtf8(const uint8_t* json, size_t length) {
|
||||
return parseJson.call(*this, String::createFromUtf8(*this, json, length));
|
||||
}
|
||||
|
||||
std::u16string Runtime::utf16(const PropNameID& sym) {
|
||||
auto utf8Str = utf8(sym);
|
||||
return convertUTF8ToUTF16(utf8Str);
|
||||
}
|
||||
|
||||
std::u16string Runtime::utf16(const String& str) {
|
||||
auto utf8Str = utf8(str);
|
||||
return convertUTF8ToUTF16(utf8Str);
|
||||
}
|
||||
|
||||
Pointer& Pointer::operator=(Pointer&& other) {
|
||||
if (ptr_) {
|
||||
ptr_->invalidate();
|
||||
|
||||
@@ -399,6 +399,9 @@ class JSI_EXPORT Runtime {
|
||||
const jsi::Object& obj,
|
||||
size_t amount) = 0;
|
||||
|
||||
virtual std::u16string utf16(const String& str);
|
||||
virtual std::u16string utf16(const PropNameID& sym);
|
||||
|
||||
// These exist so derived classes can access the private parts of
|
||||
// Value, Symbol, String, and Object, which are all friends of Runtime.
|
||||
template <typename T>
|
||||
@@ -501,6 +504,11 @@ class JSI_EXPORT PropNameID : public Pointer {
|
||||
return runtime.utf8(*this);
|
||||
}
|
||||
|
||||
/// Copies the data in a PropNameID as utf16 into a C++ string.
|
||||
std::u16string utf16(Runtime& runtime) const {
|
||||
return runtime.utf16(*this);
|
||||
}
|
||||
|
||||
static bool compare(
|
||||
Runtime& runtime,
|
||||
const jsi::PropNameID& a,
|
||||
@@ -651,6 +659,11 @@ class JSI_EXPORT String : public Pointer {
|
||||
return runtime.utf8(*this);
|
||||
}
|
||||
|
||||
/// Copies the data in a JS string as utf16 into a C++ string.
|
||||
std::u16string utf16(Runtime& runtime) const {
|
||||
return runtime.utf16(*this);
|
||||
}
|
||||
|
||||
friend class Runtime;
|
||||
friend class Value;
|
||||
};
|
||||
|
||||
@@ -1576,6 +1576,65 @@ TEST_P(JSITest, UTF8ExceptionTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(JSITest, UTF16Test) {
|
||||
// This Runtime Decorator is used to test the conversion from UTF-8 to UTF-16
|
||||
// in the default utf16 method for runtimes that do not provide their own
|
||||
// utf16 implementation.
|
||||
class UTF16RD : public RuntimeDecorator<Runtime, Runtime> {
|
||||
public:
|
||||
UTF16RD(Runtime& rt) : RuntimeDecorator(rt) {}
|
||||
|
||||
std::string utf8(const String&) override {
|
||||
return utf8Str;
|
||||
}
|
||||
|
||||
std::u16string utf16(const String& str) override {
|
||||
return Runtime::utf16(str);
|
||||
}
|
||||
|
||||
std::string utf8Str;
|
||||
};
|
||||
|
||||
UTF16RD rd = UTF16RD(rt);
|
||||
String str = String::createFromUtf8(rd, "placeholder");
|
||||
|
||||
rd.utf8Str = "foobar";
|
||||
EXPECT_EQ(str.utf16(rd), u"foobar");
|
||||
|
||||
rd.utf8Str = "你好";
|
||||
EXPECT_EQ(str.utf16(rd), u"你好");
|
||||
|
||||
rd.utf8Str = "👍";
|
||||
EXPECT_EQ(str.utf16(rd), u"👍");
|
||||
|
||||
rd.utf8Str = "foobar👍你好";
|
||||
EXPECT_EQ(str.utf16(rd), u"foobar👍你好");
|
||||
|
||||
// String ended before second byte of the encoding
|
||||
rd.utf8Str = "\xcf";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD");
|
||||
|
||||
// Third byte should follow the pattern of 0b10xxxxxx
|
||||
rd.utf8Str = "\xef\x8f\x29";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD\u0029");
|
||||
|
||||
// U+2200 should be encoded in 3 bytes as 0xE2 0x88 0x80, not 4 bytes
|
||||
rd.utf8Str = "\xf0\x82\x88\x80";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD");
|
||||
|
||||
// Unicode Max Value is U+10FFFF, U+11FFFF is invalid
|
||||
rd.utf8Str = "\xf4\x9f\xbf\xbf";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD");
|
||||
|
||||
// Missing the third byte of the 3-byte encoding, followed by 'z'
|
||||
rd.utf8Str = "\xe1\xa0\x7a";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD\u007A");
|
||||
|
||||
// First byte is neither ASCII nor a valid continuation byte
|
||||
rd.utf8Str = "\xea\x7a";
|
||||
EXPECT_EQ(str.utf16(rd), u"\uFFFD\u007A");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Runtimes,
|
||||
JSITest,
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
namespace facebook::react {
|
||||
|
||||
bool EmptyReactNativeConfig::getBool(const std::string& param) const {
|
||||
if (param == "react_fabric:enabled_automatic_interop_android") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<0b4373e19b66481d17c0a8e81785df23>>
|
||||
* @generated SignedSource<<f2175cbbe38a66b9aa5c3f0feaa07462>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@
|
||||
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
|
||||
*
|
||||
* To regenerate this code, run the following script from the repo root:
|
||||
* yarn featureflags-update
|
||||
* yarn featureflags --update
|
||||
*/
|
||||
|
||||
#include "ReactNativeFeatureFlags.h"
|
||||
@@ -178,6 +178,10 @@ bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() {
|
||||
return getAccessor().traceTurboModulePromiseRejectionsOnAndroid();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling() {
|
||||
return getAccessor().useAlwaysAvailableJSErrorHandling();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::useFabricInterop() {
|
||||
return getAccessor().useFabricInterop();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user